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