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        struct miscdevice *c;
 187        dev_t dev;
 188        int err = 0;
 189
 190        INIT_LIST_HEAD(&misc->list);
 191
 192        mutex_lock(&misc_mtx);
 193        list_for_each_entry(c, &misc_list, list) {
 194                if (c->minor == misc->minor) {
 195                        mutex_unlock(&misc_mtx);
 196                        return -EBUSY;
 197                }
 198        }
 199
 200        if (misc->minor == MISC_DYNAMIC_MINOR) {
 201                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
 202                if (i >= DYNAMIC_MINORS) {
 203                        mutex_unlock(&misc_mtx);
 204                        return -EBUSY;
 205                }
 206                misc->minor = DYNAMIC_MINORS - i - 1;
 207                set_bit(i, misc_minors);
 208        }
 209
 210        dev = MKDEV(MISC_MAJOR, misc->minor);
 211
 212        misc->this_device = device_create(misc_class, misc->parent, dev,
 213                                          misc, "%s", misc->name);
 214        if (IS_ERR(misc->this_device)) {
 215                int i = DYNAMIC_MINORS - misc->minor - 1;
 216                if (i < DYNAMIC_MINORS && i >= 0)
 217                        clear_bit(i, misc_minors);
 218                err = PTR_ERR(misc->this_device);
 219                goto out;
 220        }
 221
 222        /*
 223         * Add it to the front, so that later devices can "override"
 224         * earlier defaults
 225         */
 226        list_add(&misc->list, &misc_list);
 227 out:
 228        mutex_unlock(&misc_mtx);
 229        return err;
 230}
 231
 232/**
 233 *      misc_deregister - unregister a miscellaneous device
 234 *      @misc: device to unregister
 235 *
 236 *      Unregister a miscellaneous device that was previously
 237 *      successfully registered with misc_register(). Success
 238 *      is indicated by a zero return, a negative errno code
 239 *      indicates an error.
 240 */
 241
 242int misc_deregister(struct miscdevice *misc)
 243{
 244        int i = DYNAMIC_MINORS - misc->minor - 1;
 245
 246        if (WARN_ON(list_empty(&misc->list)))
 247                return -EINVAL;
 248
 249        mutex_lock(&misc_mtx);
 250        list_del(&misc->list);
 251        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
 252        if (i < DYNAMIC_MINORS && i >= 0)
 253                clear_bit(i, misc_minors);
 254        mutex_unlock(&misc_mtx);
 255        return 0;
 256}
 257
 258EXPORT_SYMBOL(misc_register);
 259EXPORT_SYMBOL(misc_deregister);
 260
 261static char *misc_devnode(struct device *dev, umode_t *mode)
 262{
 263        struct miscdevice *c = dev_get_drvdata(dev);
 264
 265        if (mode && c->mode)
 266                *mode = c->mode;
 267        if (c->nodename)
 268                return kstrdup(c->nodename, GFP_KERNEL);
 269        return NULL;
 270}
 271
 272static int __init misc_init(void)
 273{
 274        int err;
 275
 276#ifdef CONFIG_PROC_FS
 277        proc_create("misc", 0, NULL, &misc_proc_fops);
 278#endif
 279        misc_class = class_create(THIS_MODULE, "misc");
 280        err = PTR_ERR(misc_class);
 281        if (IS_ERR(misc_class))
 282                goto fail_remove;
 283
 284        err = -EIO;
 285        if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
 286                goto fail_printk;
 287        misc_class->devnode = misc_devnode;
 288        return 0;
 289
 290fail_printk:
 291        printk("unable to get major %d for misc devices\n", MISC_MAJOR);
 292        class_destroy(misc_class);
 293fail_remove:
 294        remove_proc_entry("misc", NULL);
 295        return err;
 296}
 297subsys_initcall(misc_init);
 298
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.