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. 62 */ 63 static DEFINE_IDA(misc_minors_ida); 64 65 static int misc_minor_alloc(int minor) 66 { 67 int ret = 0; 68 69 if (minor == MISC_DYNAMIC_MINOR) { 70 /* allocate free id */ 71 ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, 72 MINORMASK, GFP_KERNEL); 73 } else { 74 ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); 75 } 76 return ret; 77 } 78 79 static void misc_minor_free(int minor) 80 { 81 ida_free(&misc_minors_ida, minor); 82 } 83 84 #ifdef CONFIG_PROC_FS 85 static void *misc_seq_start(struct seq_file *seq, loff_t *pos) 86 { 87 mutex_lock(&misc_mtx); 88 return seq_list_start(&misc_list, *pos); 89 } 90 91 static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 92 { 93 return seq_list_next(v, &misc_list, pos); 94 } 95 96 static void misc_seq_stop(struct seq_file *seq, void *v) 97 { 98 mutex_unlock(&misc_mtx); 99 } 100 101 static int misc_seq_show(struct seq_file *seq, void *v) 102 { 103 const struct miscdevice *p = list_entry(v, struct miscdevice, list); 104 105 seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 106 return 0; 107 } 108 109 110 static const struct seq_operations misc_seq_ops = { 111 .start = misc_seq_start, 112 .next = misc_seq_next, 113 .stop = misc_seq_stop, 114 .show = misc_seq_show, 115 }; 116 #endif 117 118 static int misc_open(struct inode *inode, struct file *file) 119 { 120 int minor = iminor(inode); 121 struct miscdevice *c = NULL, *iter; 122 int err = -ENODEV; 123 const struct file_operations *new_fops = NULL; 124 125 mutex_lock(&misc_mtx); 126 127 list_for_each_entry(iter, &misc_list, list) { 128 if (iter->minor != minor) 129 continue; 130 c = iter; 131 new_fops = fops_get(iter->fops); 132 break; 133 } 134 135 if (!new_fops) { 136 mutex_unlock(&misc_mtx); 137 request_module("char-major-%d-%d", MISC_MAJOR, minor); 138 mutex_lock(&misc_mtx); 139 140 list_for_each_entry(iter, &misc_list, list) { 141 if (iter->minor != minor) 142 continue; 143 c = iter; 144 new_fops = fops_get(iter->fops); 145 break; 146 } 147 if (!new_fops) 148 goto fail; 149 } 150 151 /* 152 * Place the miscdevice in the file's 153 * private_data so it can be used by the 154 * file operations, including f_op->open below 155 */ 156 file->private_data = c; 157 158 err = 0; 159 replace_fops(file, new_fops); 160 if (file->f_op->open) 161 err = file->f_op->open(inode, file); 162 fail: 163 mutex_unlock(&misc_mtx); 164 return err; 165 } 166 167 static char *misc_devnode(const struct device *dev, umode_t *mode) 168 { 169 const struct miscdevice *c = dev_get_drvdata(dev); 170 171 if (mode && c->mode) 172 *mode = c->mode; 173 if (c->nodename) 174 return kstrdup(c->nodename, GFP_KERNEL); 175 return NULL; 176 } 177 178 static const struct class misc_class = { 179 .name = "misc", 180 .devnode = misc_devnode, 181 }; 182 183 static const struct file_operations misc_fops = { 184 .owner = THIS_MODULE, 185 .open = misc_open, 186 .llseek = noop_llseek, 187 }; 188 189 /** 190 * misc_register - register a miscellaneous device 191 * @misc: device structure 192 * 193 * Register a miscellaneous device with the kernel. If the minor 194 * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 195 * and placed in the minor field of the structure. For other cases 196 * the minor number requested is used. 197 * 198 * The structure passed is linked into the kernel and may not be 199 * destroyed until it has been unregistered. By default, an open() 200 * syscall to the device sets file->private_data to point to the 201 * structure. Drivers don't need open in fops for this. 202 * 203 * A zero is returned on success and a negative errno code for 204 * failure. 205 */ 206 207 int misc_register(struct miscdevice *misc) 208 { 209 dev_t dev; 210 int err = 0; 211 bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 212 213 INIT_LIST_HEAD(&misc->list); 214 215 mutex_lock(&misc_mtx); 216 217 if (is_dynamic) { 218 int i = misc_minor_alloc(misc->minor); 219 220 if (i < 0) { 221 err = -EBUSY; 222 goto out; 223 } 224 misc->minor = i; 225 } else { 226 struct miscdevice *c; 227 int i; 228 229 list_for_each_entry(c, &misc_list, list) { 230 if (c->minor == misc->minor) { 231 err = -EBUSY; 232 goto out; 233 } 234 } 235 236 i = misc_minor_alloc(misc->minor); 237 if (i < 0) { 238 err = -EBUSY; 239 goto out; 240 } 241 } 242 243 dev = MKDEV(MISC_MAJOR, misc->minor); 244 245 misc->this_device = 246 device_create_with_groups(&misc_class, misc->parent, dev, 247 misc, misc->groups, "%s", misc->name); 248 if (IS_ERR(misc->this_device)) { 249 misc_minor_free(misc->minor); 250 if (is_dynamic) { 251 misc->minor = MISC_DYNAMIC_MINOR; 252 } 253 err = PTR_ERR(misc->this_device); 254 goto out; 255 } 256 257 /* 258 * Add it to the front, so that later devices can "override" 259 * earlier defaults 260 */ 261 list_add(&misc->list, &misc_list); 262 out: 263 mutex_unlock(&misc_mtx); 264 return err; 265 } 266 EXPORT_SYMBOL(misc_register); 267 268 /** 269 * misc_deregister - unregister a miscellaneous device 270 * @misc: device to unregister 271 * 272 * Unregister a miscellaneous device that was previously 273 * successfully registered with misc_register(). 274 */ 275 276 void misc_deregister(struct miscdevice *misc) 277 { 278 if (WARN_ON(list_empty(&misc->list))) 279 return; 280 281 mutex_lock(&misc_mtx); 282 list_del(&misc->list); 283 device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor)); 284 misc_minor_free(misc->minor); 285 mutex_unlock(&misc_mtx); 286 } 287 EXPORT_SYMBOL(misc_deregister); 288 289 static int __init misc_init(void) 290 { 291 int err; 292 struct proc_dir_entry *ret; 293 294 ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); 295 err = class_register(&misc_class); 296 if (err) 297 goto fail_remove; 298 299 err = -EIO; 300 if (__register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops)) 301 goto fail_printk; 302 return 0; 303 304 fail_printk: 305 pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 306 class_unregister(&misc_class); 307 fail_remove: 308 if (ret) 309 remove_proc_entry("misc", NULL); 310 return err; 311 } 312 subsys_initcall(misc_init); 313