Branch data Line data Source code
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 : 0 : static void *misc_seq_start(struct seq_file *seq, loff_t *pos) 68 : : { 69 : 0 : mutex_lock(&misc_mtx); 70 : 0 : return seq_list_start(&misc_list, *pos); 71 : : } 72 : : 73 : 0 : static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 74 : : { 75 : 0 : return seq_list_next(v, &misc_list, pos); 76 : : } 77 : : 78 : 0 : static void misc_seq_stop(struct seq_file *seq, void *v) 79 : : { 80 : 0 : mutex_unlock(&misc_mtx); 81 : 0 : } 82 : : 83 : 0 : 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 : 0 : seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 88 : 0 : 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 : 3 : static int misc_open(struct inode *inode, struct file *file) 101 : : { 102 : 3 : int minor = iminor(inode); 103 : : struct miscdevice *c; 104 : : int err = -ENODEV; 105 : : const struct file_operations *new_fops = NULL; 106 : : 107 : 3 : mutex_lock(&misc_mtx); 108 : : 109 : 3 : list_for_each_entry(c, &misc_list, list) { 110 : 3 : if (c->minor == minor) { 111 : 3 : new_fops = fops_get(c->fops); 112 : 3 : break; 113 : : } 114 : : } 115 : : 116 : 3 : if (!new_fops) { 117 : 3 : mutex_unlock(&misc_mtx); 118 : 3 : request_module("char-major-%d-%d", MISC_MAJOR, minor); 119 : 3 : mutex_lock(&misc_mtx); 120 : : 121 : 3 : list_for_each_entry(c, &misc_list, list) { 122 : 3 : if (c->minor == minor) { 123 : 3 : new_fops = fops_get(c->fops); 124 : 3 : break; 125 : : } 126 : : } 127 : 3 : 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 : 3 : file->private_data = c; 137 : : 138 : : err = 0; 139 : 3 : replace_fops(file, new_fops); 140 : 3 : if (file->f_op->open) 141 : 3 : err = file->f_op->open(inode, file); 142 : : fail: 143 : 3 : mutex_unlock(&misc_mtx); 144 : 3 : 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 : 3 : int misc_register(struct miscdevice *misc) 174 : : { 175 : : dev_t dev; 176 : : int err = 0; 177 : 3 : bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 178 : : 179 : 3 : INIT_LIST_HEAD(&misc->list); 180 : : 181 : 3 : mutex_lock(&misc_mtx); 182 : : 183 : 3 : if (is_dynamic) { 184 : 3 : int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); 185 : : 186 : 3 : if (i >= DYNAMIC_MINORS) { 187 : : err = -EBUSY; 188 : : goto out; 189 : : } 190 : 3 : misc->minor = DYNAMIC_MINORS - i - 1; 191 : 3 : set_bit(i, misc_minors); 192 : : } else { 193 : : struct miscdevice *c; 194 : : 195 : 3 : list_for_each_entry(c, &misc_list, list) { 196 : 3 : if (c->minor == misc->minor) { 197 : : err = -EBUSY; 198 : : goto out; 199 : : } 200 : : } 201 : : } 202 : : 203 : 3 : dev = MKDEV(MISC_MAJOR, misc->minor); 204 : : 205 : 3 : misc->this_device = 206 : 3 : device_create_with_groups(misc_class, misc->parent, dev, 207 : : misc, misc->groups, "%s", misc->name); 208 : 3 : if (IS_ERR(misc->this_device)) { 209 : 0 : if (is_dynamic) { 210 : 0 : int i = DYNAMIC_MINORS - misc->minor - 1; 211 : : 212 : 0 : if (i < DYNAMIC_MINORS && i >= 0) 213 : 0 : clear_bit(i, misc_minors); 214 : 0 : misc->minor = MISC_DYNAMIC_MINOR; 215 : : } 216 : 0 : err = PTR_ERR(misc->this_device); 217 : 0 : 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 : 3 : mutex_unlock(&misc_mtx); 227 : 3 : 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 : 0 : void misc_deregister(struct miscdevice *misc) 240 : : { 241 : 0 : int i = DYNAMIC_MINORS - misc->minor - 1; 242 : : 243 : 0 : if (WARN_ON(list_empty(&misc->list))) 244 : 0 : return; 245 : : 246 : 0 : mutex_lock(&misc_mtx); 247 : : list_del(&misc->list); 248 : 0 : device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); 249 : 0 : if (i < DYNAMIC_MINORS && i >= 0) 250 : 0 : clear_bit(i, misc_minors); 251 : 0 : mutex_unlock(&misc_mtx); 252 : : } 253 : : EXPORT_SYMBOL(misc_deregister); 254 : : 255 : 3 : static char *misc_devnode(struct device *dev, umode_t *mode) 256 : : { 257 : : struct miscdevice *c = dev_get_drvdata(dev); 258 : : 259 : 3 : if (mode && c->mode) 260 : 3 : *mode = c->mode; 261 : 3 : if (c->nodename) 262 : 3 : return kstrdup(c->nodename, GFP_KERNEL); 263 : : return NULL; 264 : : } 265 : : 266 : 3 : static int __init misc_init(void) 267 : : { 268 : : int err; 269 : : struct proc_dir_entry *ret; 270 : : 271 : 3 : ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); 272 : 3 : misc_class = class_create(THIS_MODULE, "misc"); 273 : : err = PTR_ERR(misc_class); 274 : 3 : if (IS_ERR(misc_class)) 275 : : goto fail_remove; 276 : : 277 : : err = -EIO; 278 : 3 : if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) 279 : : goto fail_printk; 280 : 3 : misc_class->devnode = misc_devnode; 281 : 3 : return 0; 282 : : 283 : : fail_printk: 284 : 0 : pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 285 : 0 : class_destroy(misc_class); 286 : : fail_remove: 287 : 0 : if (ret) 288 : 0 : remove_proc_entry("misc", NULL); 289 : 0 : return err; 290 : : } 291 : : subsys_initcall(misc_init);