linux/drivers/char/misc.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/char/misc.c
   3 *
   4 * Generic misc open routine by Johan Myreen
   5 *
   6 * Based on code from Linus
   7 *
   8 * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
   9 *   changes incorporated into 0.97pl4
  10 *   by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
  11 *   See busmouse.c for particulars.
  12 *
  13 * Made things a lot mode modular - easy to compile in just one or two
  14 * of the misc drivers, as they are now completely independent. Linus.
  15 *
  16 * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
  17 *
  18 * Fixed a failing symbol register to free the device registration
  19 *              Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
  20 *
  21 * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
  22 *
  23 * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
  24 *
  25 * Handling of mouse minor numbers for kerneld:
  26 *  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
  27 *  adapted by Bjorn Ekwall <bj0rn@blox.se>
  28 *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
  29 *
  30 * Changes for kmod (from kerneld):
  31 *      Cyrus Durgin <cider@speakeasy.org>
  32 *
  33 * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  10-Jan-1998
  34 */
  35
  36#include <linux/module.h>
  37
  38#include <linux/fs.h>
  39#include <linux/errno.h>
  40#include <linux/miscdevice.h>
  41#include <linux/kernel.h>
  42#include <linux/major.h>
  43#include <linux/mutex.h>
  44#include <linux/proc_fs.h>
  45#include <linux/seq_file.h>
  46#include <linux/stat.h>
  47#include <linux/init.h>
  48#include <linux/device.h>
  49#include <linux/tty.h>
  50#include <linux/kmod.h>
  51#include <linux/gfp.h>
  52
  53/*
  54 * Head entry for the doubly linked miscdevice list
  55 */
  56static LIST_HEAD(misc_list);
  57static DEFINE_MUTEX(misc_mtx);
  58
  59/*
  60 * Assigned numbers, used for dynamic minors
  61 */
  62#define DYNAMIC_MINORS 64 /* like dynamic majors */
  63static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
  64
  65#ifdef CONFIG_PROC_FS
  66static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
  67{
  68        mutex_lock(&misc_mtx);
  69        return seq_list_start(&misc_list, *pos);
  70}
  71
  72static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  73{
  74        return seq_list_next(v, &misc_list, pos);
  75}
  76
  77static void misc_seq_stop(struct seq_file *seq, void *v)
  78{
  79        mutex_unlock(&misc_mtx);
  80}
  81
  82static int misc_seq_show(struct seq_file *seq, void *v)
  83{
  84        const struct miscdevice *p = list_entry(v, struct miscdevice, list);
  85
  86        seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
  87        return 0;
  88}
  89
  90
  91static const struct seq_operations misc_seq_ops = {
  92        .start = misc_seq_start,
  93        .next  = misc_seq_next,
  94        .stop  = misc_seq_stop,
  95        .show  = misc_seq_show,
  96};
  97
  98static int misc_seq_open(struct inode *inode, struct file *file)
  99{
 100        return seq_open(file, &misc_seq_ops);
 101}
 102
 103static const struct file_operations misc_proc_fops = {
 104        .owner   = THIS_MODULE,
 105        .open    = misc_seq_open,
 106        .read    = seq_read,
 107        .llseek  = seq_lseek,
 108        .release = seq_release,
 109};
 110#endif
 111
 112static int misc_open(struct inode * inode, struct file * file)
 113{
 114        int minor = iminor(inode);
 115        struct miscdevice *c;
 116        int err = -ENODEV;
 117        const struct file_operations *old_fops, *new_fops = NULL;
 118
 119        mutex_lock(&misc_mtx);
 120        
 121        list_for_each_entry(c, &misc_list, list) {
 122                if (c->minor == minor) {
 123                        new_fops = fops_get(c->fops);           
 124                        break;
 125                }
 126        }
 127                
 128        if (!new_fops) {
 129                mutex_unlock(&misc_mtx);
 130                request_module("char-major-%d-%d", MISC_MAJOR, minor);
 131                mutex_lock(&misc_mtx);
 132
 133                list_for_each_entry(c, &misc_list, list) {
 134                        if (c->minor == minor) {
 135                                new_fops = fops_get(c->fops);
 136                                break;
 137                        }
 138                }
 139                if (!new_fops)
 140                        goto fail;
 141        }
 142
 143        err = 0;
 144        old_fops = file->f_op;
 145        file->f_op = new_fops;
 146        if (file->f_op->open) {
 147                file->private_data = c;
 148                err=file->f_op->open(inode,file);
 149                if (err) {
 150                        fops_put(file->f_op);
 151                        file->f_op = fops_get(old_fops);
 152                }
 153        }
 154        fops_put(old_fops);
 155fail:
 156        mutex_unlock(&misc_mtx);
 157        return err;
 158}
 159
 160static struct class *misc_class;
 161
 162static const struct file_operations misc_fops = {
 163        .owner          = THIS_MODULE,
 164        .open           = misc_open,
 165        .llseek         = noop_llseek,
 166};
 167
 168/**
 169 *      misc_register   -       register a miscellaneous device
 170 *      @misc: device structure
 171 *      
 172 *      Register a miscellaneous device with the kernel. If the minor
 173 *      number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
 174 *      and placed in the minor field of the structure. For other cases
 175 *      the minor number requested is used.
 176 *
 177 *      The structure passed is linked into the kernel and may not be
 178 *      destroyed until it has been unregistered.
 179 *
 180 *      A zero is returned on success and a negative errno code for
 181 *      failure.
 182 */
 183 
 184int misc_register(struct miscdevice * misc)
 185{
 186        dev_t dev;
 187        int err = 0;
 188
 189        INIT_LIST_HEAD(&misc->list);
 190
 191        mutex_lock(&misc_mtx);
 192
 193        if (misc->minor == MISC_DYNAMIC_MINOR) {
 194                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
 195                if (i >= DYNAMIC_MINORS) {
 196                        mutex_unlock(&misc_mtx);
 197                        return -EBUSY;
 198                }
 199                misc->minor = DYNAMIC_MINORS - i - 1;
 200                set_bit(i, misc_minors);
 201        } else {
 202                struct miscdevice *c;
 203
 204                list_for_each_entry(c, &misc_list, list) {
 205                        if (c->minor == misc->minor) {
 206                                mutex_unlock(&misc_mtx);
 207                                return -EBUSY;
 208                        }
 209                }
 210        }
 211
 212        dev = MKDEV(MISC_MAJOR, misc->minor);
 213
 214        misc->this_device = device_create(misc_class, misc->parent, dev,
 215                                          misc, "%s", misc->name);
 216        if (IS_ERR(misc->this_device)) {
 217                int i = DYNAMIC_MINORS - misc->minor - 1;
 218                if (i < DYNAMIC_MINORS && i >= 0)
 219                        clear_bit(i, misc_minors);
 220                err = PTR_ERR(misc->this_device);
 221                goto out;
 222        }
 223
 224        /*
 225         * Add it to the front, so that later devices can "override"
 226         * earlier defaults
 227         */
 228        list_add(&misc->list, &misc_list);
 229 out:
 230        mutex_unlock(&misc_mtx);
 231        return err;
 232}
 233
 234/**
 235 *      misc_deregister - unregister a miscellaneous device
 236 *      @misc: device to unregister
 237 *
 238 *      Unregister a miscellaneous device that was previously
 239 *      successfully registered with misc_register(). Success
 240 *      is indicated by a zero return, a negative errno code
 241 *      indicates an error.
 242 */
 243
 244int misc_deregister(struct miscdevice *misc)
 245{
 246        int i = DYNAMIC_MINORS - misc->minor - 1;
 247
 248        if (WARN_ON(list_empty(&misc->list)))
 249                return -EINVAL;
 250
 251        mutex_lock(&misc_mtx);
 252        list_del(&misc->list);
 253        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
 254        if (i < DYNAMIC_MINORS && i >= 0)
 255                clear_bit(i, misc_minors);
 256        mutex_unlock(&misc_mtx);
 257        return 0;
 258}
 259
 260EXPORT_SYMBOL(misc_register);
 261EXPORT_SYMBOL(misc_deregister);
 262
 263static char *misc_devnode(struct device *dev, umode_t *mode)
 264{
 265        struct miscdevice *c = dev_get_drvdata(dev);
 266
 267        if (mode && c->mode)
 268                *mode = c->mode;
 269        if (c->nodename)
 270                return kstrdup(c->nodename, GFP_KERNEL);
 271        return NULL;
 272}
 273
 274static int __init misc_init(void)
 275{
 276        int err;
 277
 278#ifdef CONFIG_PROC_FS
 279        proc_create("misc", 0, NULL, &misc_proc_fops);
 280#endif
 281        misc_class = class_create(THIS_MODULE, "misc");
 282        err = PTR_ERR(misc_class);
 283        if (IS_ERR(misc_class))
 284                goto fail_remove;
 285
 286        err = -EIO;
 287        if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
 288                goto fail_printk;
 289        misc_class->devnode = misc_devnode;
 290        return 0;
 291
 292fail_printk:
 293        printk("unable to get major %d for misc devices\n", MISC_MAJOR);
 294        class_destroy(misc_class);
 295fail_remove:
 296        remove_proc_entry("misc", NULL);
 297        return err;
 298}
 299subsys_initcall(misc_init);
 300