summaryrefslogtreecommitdiffstats
path: root/fs/devices.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
committer <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
commit1513ff9b7899ab588401c89db0e99903dbf5f886 (patch)
treef69cc81a940a502ea23d664c3ffb2d215a479667 /fs/devices.c
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/devices.c')
-rw-r--r--fs/devices.c276
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 */
+};