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 *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        /*
 144         * Place the miscdevice in the file's
 145         * private_data so it can be used by the
 146         * file operations, including f_op->open below
 147         */
 148        file->private_data = c;
 149
 150        err = 0;
 151        replace_fops(file, new_fops);
 152        if (file->f_op->open)
 153                err = file->f_op->open(inode,file);
 154fail:
 155        mutex_unlock(&misc_mtx);
 156        return err;
 157}
 158
 159static struct class *misc_class;
 160
 161static const struct file_operations misc_fops = {
 162        .owner          = THIS_MODULE,
 163        .open           = misc_open,
 164        .llseek         = noop_llseek,
 165};
 166
 167/**
 168 *      misc_register   -       register a miscellaneous device
 169 *      @misc: device structure
 170 *      
 171 *      Register a miscellaneous device with the kernel. If the minor
 172 *      number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
 173 *      and placed in the minor field of the structure. For other cases
 174 *      the minor number requested is used.
 175 *
 176 *      The structure passed is linked into the kernel and may not be
 177 *      destroyed until it has been unregistered. By default, an open()
 178 *      syscall to the device sets file->private_data to point to the
 179 *      structure. Drivers don't need open in fops for this.
 180 *
 181 *      A zero is returned on success and a negative errno code for
 182 *      failure.
 183 */
 184 
 185int misc_register(struct miscdevice * misc)
 186{
 187        dev_t dev;
 188        int err = 0;
 189
 190        INIT_LIST_HEAD(&misc->list);
 191
 192        mutex_lock(&misc_mtx);
 193
 194        if (misc->minor == MISC_DYNAMIC_MINOR) {
 195                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
 196                if (i >= DYNAMIC_MINORS) {
 197                        err = -EBUSY;
 198                        goto out;
 199                }
 200                misc->minor = DYNAMIC_MINORS - i - 1;
 201                set_bit(i, misc_minors);
 202        } else {
 203                struct miscdevice *c;
 204
 205                list_for_each_entry(c, &misc_list, list) {
 206                        if (c->minor == misc->minor) {
 207                                err = -EBUSY;
 208                                goto out;
 209                        }
 210                }
 211        }
 212
 213        dev = MKDEV(MISC_MAJOR, misc->minor);
 214
 215        misc->this_device =
 216                device_create_with_groups(misc_class, misc->parent, dev,
 217                                          misc, misc->groups, "%s", misc->name);
 218        if (IS_ERR(misc->this_device)) {
 219                int i = DYNAMIC_MINORS - misc->minor - 1;
 220                if (i < DYNAMIC_MINORS && i >= 0)
 221                        clear_bit(i, misc_minors);
 222                err = PTR_ERR(misc->this_device);
 223                goto out;
 224        }
 225
 226        /*
 227         * Add it to the front, so that later devices can "override"
 228         * earlier defaults
 229         */
 230        list_add(&misc->list, &misc_list);
 231 out:
 232        mutex_unlock(&misc_mtx);
 233        return err;
 234}
 235
 236/**
 237 *      misc_deregister - unregister a miscellaneous device
 238 *      @misc: device to unregister
 239 *
 240 *      Unregister a miscellaneous device that was previously
 241 *      successfully registered with misc_register(). Success
 242 *      is indicated by a zero return, a negative errno code
 243 *      indicates an error.
 244 */
 245
 246int misc_deregister(struct miscdevice *misc)
 247{
 248        int i = DYNAMIC_MINORS - misc->minor - 1;
 249
 250        if (WARN_ON(list_empty(&misc->list)))
 251                return -EINVAL;
 252
 253        mutex_lock(&misc_mtx);
 254        list_del(&misc->list);
 255        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
 256        if (i < DYNAMIC_MINORS && i >= 0)
 257                clear_bit(i, misc_minors);
 258        mutex_unlock(&misc_mtx);
 259        return 0;
 260}
 261
 262EXPORT_SYMBOL(misc_register);
 263EXPORT_SYMBOL(misc_deregister);
 264
 265static char *misc_devnode(struct device *dev, umode_t *mode)
 266{
 267        struct miscdevice *c = dev_get_drvdata(dev);
 268
 269        if (mode && c->mode)
 270                *mode = c->mode;
 271        if (c->nodename)
 272                return kstrdup(c->nodename, GFP_KERNEL);
 273        return NULL;
 274}
 275
 276static int __init misc_init(void)
 277{
 278        int err;
 279
 280#ifdef CONFIG_PROC_FS
 281        proc_create("misc", 0, NULL, &misc_proc_fops);
 282#endif
 283        misc_class = class_create(THIS_MODULE, "misc");
 284        err = PTR_ERR(misc_class);
 285        if (IS_ERR(misc_class))
 286                goto fail_remove;
 287
 288        err = -EIO;
 289        if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
 290                goto fail_printk;
 291        misc_class->devnode = misc_devnode;
 292        return 0;
 293
 294fail_printk:
 295        printk("unable to get major %d for misc devices\n", MISC_MAJOR);
 296        class_destroy(misc_class);
 297fail_remove:
 298        remove_proc_entry("misc", NULL);
 299        return err;
 300}
 301subsys_initcall(misc_init);
 302