linux-bk/fs/devices.c
<<
>>
Prefs
   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 *  (changed to kmod)
  10 */
  11
  12#include <linux/config.h>
  13#include <linux/fs.h>
  14#include <linux/major.h>
  15#include <linux/string.h>
  16#include <linux/time.h>
  17#include <linux/stat.h>
  18#include <linux/fcntl.h>
  19#include <linux/errno.h>
  20#include <linux/module.h>
  21#include <linux/smp_lock.h>
  22#include <linux/devfs_fs_kernel.h>
  23#ifdef CONFIG_KMOD
  24#include <linux/kmod.h>
  25
  26#include <linux/tty.h>
  27
  28/* serial module kmod load support */
  29struct tty_driver *get_tty_driver(kdev_t device);
  30#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
  31#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL)
  32#endif
  33
  34struct device_struct {
  35        const char * name;
  36        struct file_operations * fops;
  37};
  38
  39static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
  40static struct device_struct chrdevs[MAX_CHRDEV];
  41
  42extern int get_blkdev_list(char *);
  43
  44int get_device_list(char * page)
  45{
  46        int i;
  47        int len;
  48
  49        len = sprintf(page, "Character devices:\n");
  50        read_lock(&chrdevs_lock);
  51        for (i = 0; i < MAX_CHRDEV ; i++) {
  52                if (chrdevs[i].fops) {
  53                        len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
  54                }
  55        }
  56        read_unlock(&chrdevs_lock);
  57        len += get_blkdev_list(page+len);
  58        return len;
  59}
  60
  61/*
  62        Return the function table of a device.
  63        Load the driver if needed.
  64        Increment the reference count of module in question.
  65*/
  66static struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
  67{
  68        struct file_operations *ret = NULL;
  69
  70        if (!major || major >= MAX_CHRDEV)
  71                return NULL;
  72
  73        read_lock(&chrdevs_lock);
  74        ret = fops_get(chrdevs[major].fops);
  75        read_unlock(&chrdevs_lock);
  76#ifdef CONFIG_KMOD
  77        if (ret && isa_tty_dev(major)) {
  78                lock_kernel();
  79                if (need_serial(major,minor)) {
  80                        /* Force request_module anyway, but what for? */
  81                        fops_put(ret);
  82                        ret = NULL;
  83                }
  84                unlock_kernel();
  85        }
  86        if (!ret) {
  87                char name[20];
  88                sprintf(name, "char-major-%d", major);
  89                request_module(name);
  90
  91                read_lock(&chrdevs_lock);
  92                ret = fops_get(chrdevs[major].fops);
  93                read_unlock(&chrdevs_lock);
  94        }
  95#endif
  96        return ret;
  97}
  98
  99int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
 100{
 101        if (devfs_only())
 102                return 0;
 103        if (major == 0) {
 104                write_lock(&chrdevs_lock);
 105                for (major = MAX_CHRDEV-1; major > 0; major--) {
 106                        if (chrdevs[major].fops == NULL) {
 107                                chrdevs[major].name = name;
 108                                chrdevs[major].fops = fops;
 109                                write_unlock(&chrdevs_lock);
 110                                return major;
 111                        }
 112                }
 113                write_unlock(&chrdevs_lock);
 114                return -EBUSY;
 115        }
 116        if (major >= MAX_CHRDEV)
 117                return -EINVAL;
 118        write_lock(&chrdevs_lock);
 119        if (chrdevs[major].fops && chrdevs[major].fops != fops) {
 120                write_unlock(&chrdevs_lock);
 121                return -EBUSY;
 122        }
 123        chrdevs[major].name = name;
 124        chrdevs[major].fops = fops;
 125        write_unlock(&chrdevs_lock);
 126        return 0;
 127}
 128
 129int unregister_chrdev(unsigned int major, const char * name)
 130{
 131        if (devfs_only())
 132                return 0;
 133        if (major >= MAX_CHRDEV)
 134                return -EINVAL;
 135        write_lock(&chrdevs_lock);
 136        if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
 137                write_unlock(&chrdevs_lock);
 138                return -EINVAL;
 139        }
 140        chrdevs[major].name = NULL;
 141        chrdevs[major].fops = NULL;
 142        write_unlock(&chrdevs_lock);
 143        return 0;
 144}
 145
 146/*
 147 * Called every time a character special file is opened
 148 */
 149int chrdev_open(struct inode * inode, struct file * filp)
 150{
 151        int ret = -ENODEV;
 152
 153        filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
 154        if (filp->f_op) {
 155                ret = 0;
 156                if (filp->f_op->open != NULL) {
 157                        lock_kernel();
 158                        ret = filp->f_op->open(inode,filp);
 159                        unlock_kernel();
 160                }
 161        }
 162        return ret;
 163}
 164
 165/*
 166 * Dummy default file-operations: the only thing this does
 167 * is contain the open that then fills in the correct operations
 168 * depending on the special file...
 169 */
 170static struct file_operations def_chr_fops = {
 171        .open           = chrdev_open,
 172};
 173
 174/*
 175 * Print device name (in decimal, hexadecimal or symbolic)
 176 * Note: returns pointer to static data!
 177 */
 178const char * kdevname(kdev_t dev)
 179{
 180        static char buffer[32];
 181        sprintf(buffer, "%02x:%02x", major(dev), minor(dev));
 182        return buffer;
 183}
 184
 185const char * cdevname(kdev_t dev)
 186{
 187        static char buffer[32];
 188        const char * name = chrdevs[major(dev)].name;
 189
 190        if (!name)
 191                name = "unknown-char";
 192        sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
 193        return buffer;
 194}
 195  
 196static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
 197{
 198        return -ENXIO;
 199}
 200
 201static struct file_operations bad_sock_fops = {
 202        .open           = sock_no_open
 203};
 204
 205void init_special_inode(struct inode *inode, umode_t mode, int rdev)
 206{
 207        inode->i_mode = mode;
 208        if (S_ISCHR(mode)) {
 209                inode->i_fop = &def_chr_fops;
 210                inode->i_rdev = to_kdev_t(rdev);
 211                inode->i_cdev = cdget(rdev);
 212        } else if (S_ISBLK(mode)) {
 213                inode->i_fop = &def_blk_fops;
 214                inode->i_rdev = to_kdev_t(rdev);
 215        } else if (S_ISFIFO(mode))
 216                inode->i_fop = &def_fifo_fops;
 217        else if (S_ISSOCK(mode))
 218                inode->i_fop = &bad_sock_fops;
 219        else
 220                printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
 221}
 222
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.