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
  99static int misc_seq_open(struct inode *inode, struct file *file)
 100{
 101        return seq_open(file, &misc_seq_ops);
 102}
 103
 104static const struct file_operations misc_proc_fops = {
 105        .owner   = THIS_MODULE,
 106        .open    = misc_seq_open,
 107        .read    = seq_read,
 108        .llseek  = seq_lseek,
 109        .release = seq_release,
 110};
 111#endif
 112
 113static int misc_open(struct inode *inode, struct file *file)
 114{
 115        int minor = iminor(inode);
 116        struct miscdevice *c;
 117        int err = -ENODEV;
 118        const struct file_operations *new_fops = NULL;
 119
 120        mutex_lock(&misc_mtx);
 121
 122        list_for_each_entry(c, &misc_list, list) {
 123                if (c->minor == minor) {
 124                        new_fops = fops_get(c->fops);
 125                        break;
 126                }
 127        }
 128
 129        if (!new_fops) {
 130                mutex_unlock(&misc_mtx);
 131                request_module("char-major-%d-%d", MISC_MAJOR, minor);
 132                mutex_lock(&misc_mtx);
 133
 134                list_for_each_entry(c, &misc_list, list) {
 135                        if (c->minor == minor) {
 136                                new_fops = fops_get(c->fops);
 137                                break;
 138                        }
 139                }
 140                if (!new_fops)
 141                        goto fail;
 142        }
 143
 144        /*
 145         * Place the miscdevice in the file's
 146         * private_data so it can be used by the
 147         * file operations, including f_op->open below
 148         */
 149        file->private_data = c;
 150
 151        err = 0;
 152        replace_fops(file, new_fops);
 153        if (file->f_op->open)
 154                err = file->f_op->open(inode, file);
 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. By default, an open()
 179 *      syscall to the device sets file->private_data to point to the
 180 *      structure. Drivers don't need open in fops for this.
 181 *
 182 *      A zero is returned on success and a negative errno code for
 183 *      failure.
 184 */
 185
 186int misc_register(struct miscdevice *misc)
 187{
 188        dev_t dev;
 189        int err = 0;
 190        bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
 191
 192        INIT_LIST_HEAD(&misc->list);
 193
 194        mutex_lock(&misc_mtx);
 195
 196        if (is_dynamic) {
 197                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
 198
 199                if (i >= DYNAMIC_MINORS) {
 200                        err = -EBUSY;
 201                        goto out;
 202                }
 203                misc->minor = DYNAMIC_MINORS - i - 1;
 204                set_bit(i, misc_minors);
 205        } else {
 206                struct miscdevice *c;
 207
 208                list_for_each_entry(c, &misc_list, list) {
 209                        if (c->minor == misc->minor) {
 210                                err = -EBUSY;
 211                                goto out;
 212                        }
 213                }
 214        }
 215
 216        dev = MKDEV(MISC_MAJOR, misc->minor);
 217
 218        misc->this_device =
 219                device_create_with_groups(misc_class, misc->parent, dev,
 220                                          misc, misc->groups, "%s", misc->name);
 221        if (IS_ERR(misc->this_device)) {
 222                if (is_dynamic) {
 223                        int i = DYNAMIC_MINORS - misc->minor - 1;
 224
 225                        if (i < DYNAMIC_MINORS && i >= 0)
 226                                clear_bit(i, misc_minors);
 227                        misc->minor = MISC_DYNAMIC_MINOR;
 228                }
 229                err = PTR_ERR(misc->this_device);
 230                goto out;
 231        }
 232
 233        /*
 234         * Add it to the front, so that later devices can "override"
 235         * earlier defaults
 236         */
 237        list_add(&misc->list, &misc_list);
 238 out:
 239        mutex_unlock(&misc_mtx);
 240        return err;
 241}
 242
 243/**
 244 *      misc_deregister - unregister a miscellaneous device
 245 *      @misc: device to unregister
 246 *
 247 *      Unregister a miscellaneous device that was previously
 248 *      successfully registered with misc_register().
 249 */
 250
 251void misc_deregister(struct miscdevice *misc)
 252{
 253        int i = DYNAMIC_MINORS - misc->minor - 1;
 254
 255        if (WARN_ON(list_empty(&misc->list)))
 256                return;
 257
 258        mutex_lock(&misc_mtx);
 259        list_del(&misc->list);
 260        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
 261        if (i < DYNAMIC_MINORS && i >= 0)
 262                clear_bit(i, misc_minors);
 263        mutex_unlock(&misc_mtx);
 264}
 265
 266EXPORT_SYMBOL(misc_register);
 267EXPORT_SYMBOL(misc_deregister);
 268
 269static char *misc_devnode(struct device *dev, umode_t *mode)
 270{
 271        struct miscdevice *c = dev_get_drvdata(dev);
 272
 273        if (mode && c->mode)
 274                *mode = c->mode;
 275        if (c->nodename)
 276                return kstrdup(c->nodename, GFP_KERNEL);
 277        return NULL;
 278}
 279
 280static int __init misc_init(void)
 281{
 282        int err;
 283        struct proc_dir_entry *ret;
 284
 285        ret = proc_create("misc", 0, NULL, &misc_proc_fops);
 286        misc_class = class_create(THIS_MODULE, "misc");
 287        err = PTR_ERR(misc_class);
 288        if (IS_ERR(misc_class))
 289                goto fail_remove;
 290
 291        err = -EIO;
 292        if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
 293                goto fail_printk;
 294        misc_class->devnode = misc_devnode;
 295        return 0;
 296
 297fail_printk:
 298        pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
 299        class_destroy(misc_class);
 300fail_remove:
 301        if (ret)
 302                remove_proc_entry("misc", NULL);
 303        return err;
 304}
 305subsys_initcall(misc_init);
 306