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

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