diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /fs/devices.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/devices.c')
-rw-r--r-- | fs/devices.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/fs/devices.c b/fs/devices.c new file mode 100644 index 000000000..e79ea07d5 --- /dev/null +++ b/fs/devices.c @@ -0,0 +1,276 @@ +/* + * linux/fs/devices.c + * + * (C) 1993 Matthias Urlichs -- collected common code and tables. + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <linux/fs.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/ext_fs.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/errno.h> + +struct device_struct { + const char * name; + struct file_operations * fops; +}; + +static struct device_struct chrdevs[MAX_CHRDEV] = { + { NULL, NULL }, +}; + +static struct device_struct blkdevs[MAX_BLKDEV] = { + { NULL, NULL }, +}; + +int get_device_list(char * page) +{ + int i; + int len; + + len = sprintf(page, "Character devices:\n"); + for (i = 0; i < MAX_CHRDEV ; i++) { + if (chrdevs[i].fops) { + len += sprintf(page+len, "%2d %s\n", i, chrdevs[i].name); + } + } + len += sprintf(page+len, "\nBlock devices:\n"); + for (i = 0; i < MAX_BLKDEV ; i++) { + if (blkdevs[i].fops) { + len += sprintf(page+len, "%2d %s\n", i, blkdevs[i].name); + } + } + return len; +} + +struct file_operations * get_blkfops(unsigned int major) +{ + if (major >= MAX_BLKDEV) + return NULL; + return blkdevs[major].fops; +} + +struct file_operations * get_chrfops(unsigned int major) +{ + if (major >= MAX_CHRDEV) + return NULL; + return chrdevs[major].fops; +} + +int register_chrdev(unsigned int major, const char * name, struct file_operations *fops) +{ + if (major == 0) { + for (major = MAX_CHRDEV-1; major > 0; major--) { + if (chrdevs[major].fops == fops) + return major; + } + for (major = MAX_CHRDEV-1; major > 0; major--) { + if (chrdevs[major].fops == NULL) { + chrdevs[major].name = name; + chrdevs[major].fops = fops; + return major; + } + } + return -EBUSY; + } + if (major >= MAX_CHRDEV) + return -EINVAL; + if (chrdevs[major].fops && chrdevs[major].fops != fops) + return -EBUSY; + chrdevs[major].name = name; + chrdevs[major].fops = fops; + return 0; +} + +int register_blkdev(unsigned int major, const char * name, struct file_operations *fops) +{ + if (major == 0) { + for (major = MAX_BLKDEV-1; major > 0; major--) { + if (blkdevs[major].fops == fops) + return major; + } + for (major = MAX_BLKDEV-1; major > 0; major--) { + if (blkdevs[major].fops == NULL) { + blkdevs[major].name = name; + blkdevs[major].fops = fops; + return major; + } + } + return -EBUSY; + } + if (major >= MAX_BLKDEV) + return -EINVAL; + if (blkdevs[major].fops && blkdevs[major].fops != fops) + return -EBUSY; + blkdevs[major].name = name; + blkdevs[major].fops = fops; + return 0; +} + +int unregister_chrdev(unsigned int major, const char * name) +{ + if (major >= MAX_CHRDEV) + return -EINVAL; + if (!chrdevs[major].fops) + return -EINVAL; + if (strcmp(chrdevs[major].name, name)) + return -EINVAL; + chrdevs[major].name = NULL; + chrdevs[major].fops = NULL; + return 0; +} + +int unregister_blkdev(unsigned int major, const char * name) +{ + if (major >= MAX_BLKDEV) + return -EINVAL; + if (!blkdevs[major].fops) + return -EINVAL; + if (strcmp(blkdevs[major].name, name)) + return -EINVAL; + blkdevs[major].name = NULL; + blkdevs[major].fops = NULL; + return 0; +} + +/* + * This routine checks whether a removable media has been changed, + * and invalidates all buffer-cache-entries in that case. This + * is a relatively slow routine, so we have to try to minimize using + * it. Thus it is called only upon a 'mount' or 'open'. This + * is the best way of combining speed and utility, I think. + * People changing diskettes in the middle of an operation deserve + * to loose :-) + */ +int check_disk_change(dev_t dev) +{ + int i; + struct file_operations * fops; + + i = MAJOR(dev); + if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL) + return 0; + if (fops->check_media_change == NULL) + return 0; + if (!fops->check_media_change(dev)) + return 0; + + printk("VFS: Disk change detected on device %d/%d\n", + MAJOR(dev), MINOR(dev)); + for (i=0 ; i<NR_SUPER ; i++) + if (super_blocks[i].s_dev == dev) + put_super(super_blocks[i].s_dev); + invalidate_inodes(dev); + invalidate_buffers(dev); + + if (fops->revalidate) + fops->revalidate(dev); + return 1; +} + +/* + * Called every time a block special file is opened + */ +int blkdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i >= MAX_BLKDEV || !blkdevs[i].fops) + return -ENODEV; + filp->f_op = blkdevs[i].fops; + if (filp->f_op->open) + return filp->f_op->open(inode,filp); + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +struct file_operations def_blk_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + blkdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations blkdev_inode_operations = { + &def_blk_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/* + * Called every time a character special file is opened + */ +int chrdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i >= MAX_CHRDEV || !chrdevs[i].fops) + return -ENODEV; + filp->f_op = chrdevs[i].fops; + if (filp->f_op->open) + return filp->f_op->open(inode,filp); + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +struct file_operations def_chr_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + chrdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations chrdev_inode_operations = { + &def_chr_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; |