root/fs/devices.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_device_list
  2. get_fops
  3. get_blkfops
  4. get_chrfops
  5. register_chrdev
  6. register_blkdev
  7. unregister_chrdev
  8. unregister_blkdev
  9. check_disk_change
  10. blkdev_open
  11. blkdev_release
  12. chrdev_open
  13. kdevname

   1 /*
   2  *  linux/fs/devices.c
   3  *
   4  * (C) 1993 Matthias Urlichs -- collected common code and tables.
   5  * 
   6  *  Copyright (C) 1991, 1992  Linus Torvalds
   7  *
   8  *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
   9  */
  10 
  11 #include <linux/config.h>
  12 #include <linux/fs.h>
  13 #include <linux/major.h>
  14 #include <linux/string.h>
  15 #include <linux/sched.h>
  16 #include <linux/ext_fs.h>
  17 #include <linux/stat.h>
  18 #include <linux/fcntl.h>
  19 #include <linux/errno.h>
  20 #ifdef CONFIG_KERNELD
  21 #include <linux/kerneld.h>
  22 #endif
  23 
  24 struct device_struct {
  25         const char * name;
  26         struct file_operations * fops;
  27 };
  28 
  29 static struct device_struct chrdevs[MAX_CHRDEV] = {
  30         { NULL, NULL },
  31 };
  32 
  33 static struct device_struct blkdevs[MAX_BLKDEV] = {
  34         { NULL, NULL },
  35 };
  36 
  37 int get_device_list(char * page)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39         int i;
  40         int len;
  41 
  42         len = sprintf(page, "Character devices:\n");
  43         for (i = 0; i < MAX_CHRDEV ; i++) {
  44                 if (chrdevs[i].fops) {
  45                         len += sprintf(page+len, "%2d %s\n", i, chrdevs[i].name);
  46                 }
  47         }
  48         len += sprintf(page+len, "\nBlock devices:\n");
  49         for (i = 0; i < MAX_BLKDEV ; i++) {
  50                 if (blkdevs[i].fops) {
  51                         len += sprintf(page+len, "%2d %s\n", i, blkdevs[i].name);
  52                 }
  53         }
  54         return len;
  55 }
  56 /*
  57         Return the function table of a device.
  58         Load the driver if needed.
  59 */
  60 static struct file_operations * get_fops(
     /* [previous][next][first][last][top][bottom][index][help] */
  61         unsigned int major,
  62         unsigned int maxdev,
  63         const char *mangle,             /* String to use to build the module name */
  64         struct device_struct tb[])
  65 {
  66         struct file_operations *ret = NULL;
  67         if (major < maxdev){
  68 #ifdef CONFIG_KERNELD
  69                 /*
  70                  * I do get request for device 0. I have no idea why. It happen
  71                  * at shutdown time for one. Without the following test, the
  72                  * kernel will happily trigger a request_module() which will
  73                  * trigger kerneld and modprobe for nothing (since there
  74                  * is no device with major number == 0. And furthermore
  75                  * it locks the reboot process :-(
  76                  *
  77                  * Jacques Gelinas (jacques@solucorp.qc.ca)
  78                  */
  79                 if (!tb[major].fops && major != 0) {
  80                         char name[20];
  81                         sprintf(name, mangle, major);
  82                         request_module(name);
  83                 }
  84 #endif
  85                 ret = tb[major].fops;
  86         }
  87         return ret;
  88 }
  89 
  90 
  91 /*
  92         Return the function table of a device.
  93         Load the driver if needed.
  94 */
  95 struct file_operations * get_blkfops(unsigned int major)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         return get_fops (major,MAX_BLKDEV,"block-major-%d",blkdevs);
  98 }
  99 
 100 struct file_operations * get_chrfops(unsigned int major)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102         return get_fops (major,MAX_CHRDEV,"char-major-%d",chrdevs);
 103 }
 104 
 105 int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107         if (major == 0) {
 108                 for (major = MAX_CHRDEV-1; major > 0; major--) {
 109                         if (chrdevs[major].fops == NULL) {
 110                                 chrdevs[major].name = name;
 111                                 chrdevs[major].fops = fops;
 112                                 return major;
 113                         }
 114                 }
 115                 return -EBUSY;
 116         }
 117         if (major >= MAX_CHRDEV)
 118                 return -EINVAL;
 119         if (chrdevs[major].fops && chrdevs[major].fops != fops)
 120                 return -EBUSY;
 121         chrdevs[major].name = name;
 122         chrdevs[major].fops = fops;
 123         return 0;
 124 }
 125 
 126 int register_blkdev(unsigned int major, const char * name, struct file_operations *fops)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128         if (major == 0) {
 129                 for (major = MAX_BLKDEV-1; major > 0; major--) {
 130                         if (blkdevs[major].fops == NULL) {
 131                                 blkdevs[major].name = name;
 132                                 blkdevs[major].fops = fops;
 133                                 return major;
 134                         }
 135                 }
 136                 return -EBUSY;
 137         }
 138         if (major >= MAX_BLKDEV)
 139                 return -EINVAL;
 140         if (blkdevs[major].fops && blkdevs[major].fops != fops)
 141                 return -EBUSY;
 142         blkdevs[major].name = name;
 143         blkdevs[major].fops = fops;
 144         return 0;
 145 }
 146 
 147 int unregister_chrdev(unsigned int major, const char * name)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149         if (major >= MAX_CHRDEV)
 150                 return -EINVAL;
 151         if (!chrdevs[major].fops)
 152                 return -EINVAL;
 153         if (strcmp(chrdevs[major].name, name))
 154                 return -EINVAL;
 155         chrdevs[major].name = NULL;
 156         chrdevs[major].fops = NULL;
 157         return 0;
 158 }
 159 
 160 int unregister_blkdev(unsigned int major, const char * name)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162         if (major >= MAX_BLKDEV)
 163                 return -EINVAL;
 164         if (!blkdevs[major].fops)
 165                 return -EINVAL;
 166         if (strcmp(blkdevs[major].name, name))
 167                 return -EINVAL;
 168         blkdevs[major].name = NULL;
 169         blkdevs[major].fops = NULL;
 170         return 0;
 171 }
 172 
 173 /*
 174  * This routine checks whether a removable media has been changed,
 175  * and invalidates all buffer-cache-entries in that case. This
 176  * is a relatively slow routine, so we have to try to minimize using
 177  * it. Thus it is called only upon a 'mount' or 'open'. This
 178  * is the best way of combining speed and utility, I think.
 179  * People changing diskettes in the middle of an operation deserve
 180  * to loose :-)
 181  */
 182 int check_disk_change(kdev_t dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 183 {
 184         int i;
 185         struct file_operations * fops;
 186 
 187         i = MAJOR(dev);
 188         if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
 189                 return 0;
 190         if (fops->check_media_change == NULL)
 191                 return 0;
 192         if (!fops->check_media_change(dev))
 193                 return 0;
 194 
 195         printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
 196                 kdevname(dev));
 197         for (i=0 ; i<NR_SUPER ; i++)
 198                 if (super_blocks[i].s_dev == dev)
 199                         put_super(super_blocks[i].s_dev);
 200         invalidate_inodes(dev);
 201         invalidate_buffers(dev);
 202 
 203         if (fops->revalidate)
 204                 fops->revalidate(dev);
 205         return 1;
 206 }
 207 
 208 /*
 209  * Called every time a block special file is opened
 210  */
 211 int blkdev_open(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213         int ret = -ENODEV;
 214         filp->f_op = get_blkfops(MAJOR(inode->i_rdev));
 215         if (filp->f_op != NULL){
 216                 ret = 0;
 217                 if (filp->f_op->open != NULL)
 218                         ret = filp->f_op->open(inode,filp);
 219         }       
 220         return ret;
 221 }       
 222 
 223 void blkdev_release(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225         struct file_operations *fops = get_blkfops(MAJOR(inode->i_rdev));
 226         if (fops && fops->release)
 227                 fops->release(inode,NULL);
 228 }
 229 
 230 
 231 /*
 232  * Dummy default file-operations: the only thing this does
 233  * is contain the open that then fills in the correct operations
 234  * depending on the special file...
 235  */
 236 struct file_operations def_blk_fops = {
 237         NULL,           /* lseek */
 238         NULL,           /* read */
 239         NULL,           /* write */
 240         NULL,           /* readdir */
 241         NULL,           /* select */
 242         NULL,           /* ioctl */
 243         NULL,           /* mmap */
 244         blkdev_open,    /* open */
 245         NULL,           /* release */
 246 };
 247 
 248 struct inode_operations blkdev_inode_operations = {
 249         &def_blk_fops,          /* default file operations */
 250         NULL,                   /* create */
 251         NULL,                   /* lookup */
 252         NULL,                   /* link */
 253         NULL,                   /* unlink */
 254         NULL,                   /* symlink */
 255         NULL,                   /* mkdir */
 256         NULL,                   /* rmdir */
 257         NULL,                   /* mknod */
 258         NULL,                   /* rename */
 259         NULL,                   /* readlink */
 260         NULL,                   /* follow_link */
 261         NULL,                   /* readpage */
 262         NULL,                   /* writepage */
 263         NULL,                   /* bmap */
 264         NULL,                   /* truncate */
 265         NULL                    /* permission */
 266 };
 267 
 268 /*
 269  * Called every time a character special file is opened
 270  */
 271 int chrdev_open(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273         int ret = -ENODEV;
 274         filp->f_op = get_chrfops(MAJOR(inode->i_rdev));
 275         if (filp->f_op != NULL){
 276                 ret = 0;
 277                 if (filp->f_op->open != NULL)
 278                         ret = filp->f_op->open(inode,filp);
 279         }
 280         return ret;
 281 }
 282 
 283 /*
 284  * Dummy default file-operations: the only thing this does
 285  * is contain the open that then fills in the correct operations
 286  * depending on the special file...
 287  */
 288 struct file_operations def_chr_fops = {
 289         NULL,           /* lseek */
 290         NULL,           /* read */
 291         NULL,           /* write */
 292         NULL,           /* readdir */
 293         NULL,           /* select */
 294         NULL,           /* ioctl */
 295         NULL,           /* mmap */
 296         chrdev_open,    /* open */
 297         NULL,           /* release */
 298 };
 299 
 300 struct inode_operations chrdev_inode_operations = {
 301         &def_chr_fops,          /* default file operations */
 302         NULL,                   /* create */
 303         NULL,                   /* lookup */
 304         NULL,                   /* link */
 305         NULL,                   /* unlink */
 306         NULL,                   /* symlink */
 307         NULL,                   /* mkdir */
 308         NULL,                   /* rmdir */
 309         NULL,                   /* mknod */
 310         NULL,                   /* rename */
 311         NULL,                   /* readlink */
 312         NULL,                   /* follow_link */
 313         NULL,                   /* readpage */
 314         NULL,                   /* writepage */
 315         NULL,                   /* bmap */
 316         NULL,                   /* truncate */
 317         NULL                    /* permission */
 318 };
 319 
 320 /*
 321  * Print device name (in decimal, hexadecimal or symbolic) -
 322  * at present hexadecimal only.
 323  * Note: returns pointer to static data!
 324  */
 325 char * kdevname(kdev_t dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327         static char buffer[32];
 328         sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
 329         return buffer;
 330 }

/* [previous][next][first][last][top][bottom][index][help] */