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