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 /* Only request module for fixed minor code */ 136 if (!new_fops && minor < MISC_DYNAMIC_MINOR) { 137 mutex_unlock(&misc_mtx); 138 request_module("char-major-%d-%d", MISC_MAJOR, minor); 139 mutex_lock(&misc_mtx); 140 141 list_for_each_entry(iter, &misc_list, list) { 142 if (iter->minor != minor) 143 continue; 144 c = iter; 145 new_fops = fops_get(iter->fops); 146 break; 147 } 148 } 149 150 if (!new_fops) 151 goto fail; 152 153 /* 154 * Place the miscdevice in the file's 155 * private_data so it can be used by the 156 * file operations, including f_op->open below 157 */ 158 file->private_data = c; 159 160 err = 0; 161 replace_fops(file, new_fops); 162 if (file->f_op->open) 163 err = file->f_op->open(inode, file); 164 fail: 165 mutex_unlock(&misc_mtx); 166 return err; 167 } 168 169 static char *misc_devnode(const struct device *dev, umode_t *mode) 170 { 171 const struct miscdevice *c = dev_get_drvdata(dev); 172 173 if (mode && c->mode) 174 *mode = c->mode; 175 if (c->nodename) 176 return kstrdup(c->nodename, GFP_KERNEL); 177 return NULL; 178 } 179 180 static const struct class misc_class = { 181 .name = "misc", 182 .devnode = misc_devnode, 183 }; 184 185 static const struct file_operations misc_fops = { 186 .owner = THIS_MODULE, 187 .open = misc_open, 188 .llseek = noop_llseek, 189 }; 190 191 /** 192 * misc_register - register a miscellaneous device 193 * @misc: device structure 194 * 195 * Register a miscellaneous device with the kernel. If the minor 196 * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 197 * and placed in the minor field of the structure. For other cases 198 * the minor number requested is used. 199 * 200 * The structure passed is linked into the kernel and may not be 201 * destroyed until it has been unregistered. By default, an open() 202 * syscall to the device sets file->private_data to point to the 203 * structure. Drivers don't need open in fops for this. 204 * 205 * A zero is returned on success and a negative errno code for 206 * failure. 207 */ 208 209 int misc_register(struct miscdevice *misc) 210 { 211 dev_t dev; 212 int err = 0; 213 bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 214 215 if (misc->minor > MISC_DYNAMIC_MINOR) { 216 pr_err("Invalid fixed minor %d for miscdevice '%s'\n", 217 misc->minor, misc->name); 218 return -EINVAL; 219 } 220 221 INIT_LIST_HEAD(&misc->list); 222 223 mutex_lock(&misc_mtx); 224 225 if (is_dynamic) { 226 int i = misc_minor_alloc(misc->minor); 227 228 if (i < 0) { 229 err = -EBUSY; 230 goto out; 231 } 232 misc->minor = i; 233 } else { 234 struct miscdevice *c; 235 int i; 236 237 list_for_each_entry(c, &misc_list, list) { 238 if (c->minor == misc->minor) { 239 err = -EBUSY; 240 goto out; 241 } 242 } 243 244 i = misc_minor_alloc(misc->minor); 245 if (i < 0) { 246 err = -EBUSY; 247 goto out; 248 } 249 } 250 251 dev = MKDEV(MISC_MAJOR, misc->minor); 252 253 misc->this_device = 254 device_create_with_groups(&misc_class, misc->parent, dev, 255 misc, misc->groups, "%s", misc->name); 256 if (IS_ERR(misc->this_device)) { 257 misc_minor_free(misc->minor); 258 if (is_dynamic) { 259 misc->minor = MISC_DYNAMIC_MINOR; 260 } 261 err = PTR_ERR(misc->this_device); 262 goto out; 263 } 264 265 /* 266 * Add it to the front, so that later devices can "override" 267 * earlier defaults 268 */ 269 list_add(&misc->list, &misc_list); 270 out: 271 mutex_unlock(&misc_mtx); 272 return err; 273 } 274 EXPORT_SYMBOL(misc_register); 275 276 /** 277 * misc_deregister - unregister a miscellaneous device 278 * @misc: device to unregister 279 * 280 * Unregister a miscellaneous device that was previously 281 * successfully registered with misc_register(). 282 */ 283 284 void misc_deregister(struct miscdevice *misc) 285 { 286 mutex_lock(&misc_mtx); 287 list_del_init(&misc->list); 288 device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor)); 289 misc_minor_free(misc->minor); 290 if (misc->minor > MISC_DYNAMIC_MINOR) 291 misc->minor = MISC_DYNAMIC_MINOR; 292 mutex_unlock(&misc_mtx); 293 } 294 EXPORT_SYMBOL(misc_deregister); 295 296 static int __init misc_init(void) 297 { 298 int err; 299 struct proc_dir_entry *misc_proc_file; 300 301 misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops); 302 err = class_register(&misc_class); 303 if (err) 304 goto fail_remove; 305 306 err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops); 307 if (err < 0) 308 goto fail_printk; 309 return 0; 310 311 fail_printk: 312 pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 313 class_unregister(&misc_class); 314 fail_remove: 315 if (misc_proc_file) 316 remove_proc_entry("misc", NULL); 317 return err; 318 } 319 subsys_initcall(misc_init); 320