root/drivers/char/misc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. misc_seq_start
  2. misc_seq_next
  3. misc_seq_stop
  4. misc_seq_show
  5. misc_open
  6. misc_register
  7. misc_deregister
  8. misc_devnode
  9. misc_init

   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  */
  57 static LIST_HEAD(misc_list);
  58 static DEFINE_MUTEX(misc_mtx);
  59 
  60 /*
  61  * Assigned numbers, used for dynamic minors
  62  */
  63 #define DYNAMIC_MINORS 64 /* like dynamic majors */
  64 static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
  65 
  66 #ifdef CONFIG_PROC_FS
  67 static 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 
  73 static 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 
  78 static void misc_seq_stop(struct seq_file *seq, void *v)
  79 {
  80         mutex_unlock(&misc_mtx);
  81 }
  82 
  83 static 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 
  92 static 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 
 100 static 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);
 142 fail:
 143         mutex_unlock(&misc_mtx);
 144         return err;
 145 }
 146 
 147 static struct class *misc_class;
 148 
 149 static 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 
 173 int 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 }
 229 EXPORT_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 
 239 void 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 }
 253 EXPORT_SYMBOL(misc_deregister);
 254 
 255 static 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 
 266 static 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 
 283 fail_printk:
 284         pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
 285         class_destroy(misc_class);
 286 fail_remove:
 287         if (ret)
 288                 remove_proc_entry("misc", NULL);
 289         return err;
 290 }
 291 subsys_initcall(misc_init);

/* [<][>][^][v][top][bottom][index][help] */