18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/char/misc.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Generic misc open routine by Johan Myreen 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on code from Linus 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's 108c2ecf20Sopenharmony_ci * changes incorporated into 0.97pl4 118c2ecf20Sopenharmony_ci * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92) 128c2ecf20Sopenharmony_ci * See busmouse.c for particulars. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Made things a lot mode modular - easy to compile in just one or two 158c2ecf20Sopenharmony_ci * of the misc drivers, as they are now completely independent. Linus. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Fixed a failing symbol register to free the device registration 208c2ecf20Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Handling of mouse minor numbers for kerneld: 278c2ecf20Sopenharmony_ci * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, 288c2ecf20Sopenharmony_ci * adapted by Bjorn Ekwall <bj0rn@blox.se> 298c2ecf20Sopenharmony_ci * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Changes for kmod (from kerneld): 328c2ecf20Sopenharmony_ci * Cyrus Durgin <cider@speakeasy.org> 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/module.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <linux/fs.h> 408c2ecf20Sopenharmony_ci#include <linux/errno.h> 418c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 428c2ecf20Sopenharmony_ci#include <linux/kernel.h> 438c2ecf20Sopenharmony_ci#include <linux/major.h> 448c2ecf20Sopenharmony_ci#include <linux/mutex.h> 458c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 468c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 478c2ecf20Sopenharmony_ci#include <linux/stat.h> 488c2ecf20Sopenharmony_ci#include <linux/init.h> 498c2ecf20Sopenharmony_ci#include <linux/device.h> 508c2ecf20Sopenharmony_ci#include <linux/tty.h> 518c2ecf20Sopenharmony_ci#include <linux/kmod.h> 528c2ecf20Sopenharmony_ci#include <linux/gfp.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Head entry for the doubly linked miscdevice list 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic LIST_HEAD(misc_list); 588c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(misc_mtx); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Assigned numbers, used for dynamic minors 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci#define DYNAMIC_MINORS 64 /* like dynamic majors */ 648c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 678c2ecf20Sopenharmony_cistatic void *misc_seq_start(struct seq_file *seq, loff_t *pos) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci mutex_lock(&misc_mtx); 708c2ecf20Sopenharmony_ci return seq_list_start(&misc_list, *pos); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return seq_list_next(v, &misc_list, pos); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void misc_seq_stop(struct seq_file *seq, void *v) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci mutex_unlock(&misc_mtx); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int misc_seq_show(struct seq_file *seq, void *v) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci const struct miscdevice *p = list_entry(v, struct miscdevice, list); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct seq_operations misc_seq_ops = { 938c2ecf20Sopenharmony_ci .start = misc_seq_start, 948c2ecf20Sopenharmony_ci .next = misc_seq_next, 958c2ecf20Sopenharmony_ci .stop = misc_seq_stop, 968c2ecf20Sopenharmony_ci .show = misc_seq_show, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci#endif 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int misc_open(struct inode *inode, struct file *file) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int minor = iminor(inode); 1038c2ecf20Sopenharmony_ci struct miscdevice *c; 1048c2ecf20Sopenharmony_ci int err = -ENODEV; 1058c2ecf20Sopenharmony_ci const struct file_operations *new_fops = NULL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci mutex_lock(&misc_mtx); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci list_for_each_entry(c, &misc_list, list) { 1108c2ecf20Sopenharmony_ci if (c->minor == minor) { 1118c2ecf20Sopenharmony_ci new_fops = fops_get(c->fops); 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!new_fops) { 1178c2ecf20Sopenharmony_ci mutex_unlock(&misc_mtx); 1188c2ecf20Sopenharmony_ci request_module("char-major-%d-%d", MISC_MAJOR, minor); 1198c2ecf20Sopenharmony_ci mutex_lock(&misc_mtx); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci list_for_each_entry(c, &misc_list, list) { 1228c2ecf20Sopenharmony_ci if (c->minor == minor) { 1238c2ecf20Sopenharmony_ci new_fops = fops_get(c->fops); 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci if (!new_fops) 1288c2ecf20Sopenharmony_ci goto fail; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Place the miscdevice in the file's 1338c2ecf20Sopenharmony_ci * private_data so it can be used by the 1348c2ecf20Sopenharmony_ci * file operations, including f_op->open below 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci file->private_data = c; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = 0; 1398c2ecf20Sopenharmony_ci replace_fops(file, new_fops); 1408c2ecf20Sopenharmony_ci if (file->f_op->open) 1418c2ecf20Sopenharmony_ci err = file->f_op->open(inode, file); 1428c2ecf20Sopenharmony_cifail: 1438c2ecf20Sopenharmony_ci mutex_unlock(&misc_mtx); 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct class *misc_class; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct file_operations misc_fops = { 1508c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1518c2ecf20Sopenharmony_ci .open = misc_open, 1528c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * misc_register - register a miscellaneous device 1578c2ecf20Sopenharmony_ci * @misc: device structure 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Register a miscellaneous device with the kernel. If the minor 1608c2ecf20Sopenharmony_ci * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 1618c2ecf20Sopenharmony_ci * and placed in the minor field of the structure. For other cases 1628c2ecf20Sopenharmony_ci * the minor number requested is used. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * The structure passed is linked into the kernel and may not be 1658c2ecf20Sopenharmony_ci * destroyed until it has been unregistered. By default, an open() 1668c2ecf20Sopenharmony_ci * syscall to the device sets file->private_data to point to the 1678c2ecf20Sopenharmony_ci * structure. Drivers don't need open in fops for this. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * A zero is returned on success and a negative errno code for 1708c2ecf20Sopenharmony_ci * failure. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint misc_register(struct miscdevice *misc) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci dev_t dev; 1768c2ecf20Sopenharmony_ci int err = 0; 1778c2ecf20Sopenharmony_ci bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&misc->list); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci mutex_lock(&misc_mtx); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (is_dynamic) { 1848c2ecf20Sopenharmony_ci int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (i >= DYNAMIC_MINORS) { 1878c2ecf20Sopenharmony_ci err = -EBUSY; 1888c2ecf20Sopenharmony_ci goto out; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci misc->minor = DYNAMIC_MINORS - i - 1; 1918c2ecf20Sopenharmony_ci set_bit(i, misc_minors); 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci struct miscdevice *c; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci list_for_each_entry(c, &misc_list, list) { 1968c2ecf20Sopenharmony_ci if (c->minor == misc->minor) { 1978c2ecf20Sopenharmony_ci err = -EBUSY; 1988c2ecf20Sopenharmony_ci goto out; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dev = MKDEV(MISC_MAJOR, misc->minor); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci misc->this_device = 2068c2ecf20Sopenharmony_ci device_create_with_groups(misc_class, misc->parent, dev, 2078c2ecf20Sopenharmony_ci misc, misc->groups, "%s", misc->name); 2088c2ecf20Sopenharmony_ci if (IS_ERR(misc->this_device)) { 2098c2ecf20Sopenharmony_ci if (is_dynamic) { 2108c2ecf20Sopenharmony_ci int i = DYNAMIC_MINORS - misc->minor - 1; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (i < DYNAMIC_MINORS && i >= 0) 2138c2ecf20Sopenharmony_ci clear_bit(i, misc_minors); 2148c2ecf20Sopenharmony_ci misc->minor = MISC_DYNAMIC_MINOR; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci err = PTR_ERR(misc->this_device); 2178c2ecf20Sopenharmony_ci goto out; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Add it to the front, so that later devices can "override" 2228c2ecf20Sopenharmony_ci * earlier defaults 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci list_add(&misc->list, &misc_list); 2258c2ecf20Sopenharmony_ci out: 2268c2ecf20Sopenharmony_ci mutex_unlock(&misc_mtx); 2278c2ecf20Sopenharmony_ci return err; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(misc_register); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/** 2328c2ecf20Sopenharmony_ci * misc_deregister - unregister a miscellaneous device 2338c2ecf20Sopenharmony_ci * @misc: device to unregister 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Unregister a miscellaneous device that was previously 2368c2ecf20Sopenharmony_ci * successfully registered with misc_register(). 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_civoid misc_deregister(struct miscdevice *misc) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci int i = DYNAMIC_MINORS - misc->minor - 1; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (WARN_ON(list_empty(&misc->list))) 2448c2ecf20Sopenharmony_ci return; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mutex_lock(&misc_mtx); 2478c2ecf20Sopenharmony_ci list_del(&misc->list); 2488c2ecf20Sopenharmony_ci device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); 2498c2ecf20Sopenharmony_ci if (i < DYNAMIC_MINORS && i >= 0) 2508c2ecf20Sopenharmony_ci clear_bit(i, misc_minors); 2518c2ecf20Sopenharmony_ci mutex_unlock(&misc_mtx); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(misc_deregister); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic char *misc_devnode(struct device *dev, umode_t *mode) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct miscdevice *c = dev_get_drvdata(dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (mode && c->mode) 2608c2ecf20Sopenharmony_ci *mode = c->mode; 2618c2ecf20Sopenharmony_ci if (c->nodename) 2628c2ecf20Sopenharmony_ci return kstrdup(c->nodename, GFP_KERNEL); 2638c2ecf20Sopenharmony_ci return NULL; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int __init misc_init(void) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int err; 2698c2ecf20Sopenharmony_ci struct proc_dir_entry *ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); 2728c2ecf20Sopenharmony_ci misc_class = class_create(THIS_MODULE, "misc"); 2738c2ecf20Sopenharmony_ci err = PTR_ERR(misc_class); 2748c2ecf20Sopenharmony_ci if (IS_ERR(misc_class)) 2758c2ecf20Sopenharmony_ci goto fail_remove; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci err = -EIO; 2788c2ecf20Sopenharmony_ci if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) 2798c2ecf20Sopenharmony_ci goto fail_printk; 2808c2ecf20Sopenharmony_ci misc_class->devnode = misc_devnode; 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cifail_printk: 2848c2ecf20Sopenharmony_ci pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 2858c2ecf20Sopenharmony_ci class_destroy(misc_class); 2868c2ecf20Sopenharmony_cifail_remove: 2878c2ecf20Sopenharmony_ci if (ret) 2888c2ecf20Sopenharmony_ci remove_proc_entry("misc", NULL); 2898c2ecf20Sopenharmony_ci return err; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_cisubsys_initcall(misc_init); 292