162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/char/misc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic misc open routine by Johan Myreen 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on code from Linus 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's 1062306a36Sopenharmony_ci * changes incorporated into 0.97pl4 1162306a36Sopenharmony_ci * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92) 1262306a36Sopenharmony_ci * See busmouse.c for particulars. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Made things a lot mode modular - easy to compile in just one or two 1562306a36Sopenharmony_ci * of the misc drivers, as they are now completely independent. Linus. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Fixed a failing symbol register to free the device registration 2062306a36Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Handling of mouse minor numbers for kerneld: 2762306a36Sopenharmony_ci * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, 2862306a36Sopenharmony_ci * adapted by Bjorn Ekwall <bj0rn@blox.se> 2962306a36Sopenharmony_ci * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Changes for kmod (from kerneld): 3262306a36Sopenharmony_ci * Cyrus Durgin <cider@speakeasy.org> 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/module.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/fs.h> 4062306a36Sopenharmony_ci#include <linux/errno.h> 4162306a36Sopenharmony_ci#include <linux/miscdevice.h> 4262306a36Sopenharmony_ci#include <linux/kernel.h> 4362306a36Sopenharmony_ci#include <linux/major.h> 4462306a36Sopenharmony_ci#include <linux/mutex.h> 4562306a36Sopenharmony_ci#include <linux/proc_fs.h> 4662306a36Sopenharmony_ci#include <linux/seq_file.h> 4762306a36Sopenharmony_ci#include <linux/stat.h> 4862306a36Sopenharmony_ci#include <linux/init.h> 4962306a36Sopenharmony_ci#include <linux/device.h> 5062306a36Sopenharmony_ci#include <linux/tty.h> 5162306a36Sopenharmony_ci#include <linux/kmod.h> 5262306a36Sopenharmony_ci#include <linux/gfp.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Head entry for the doubly linked miscdevice list 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic LIST_HEAD(misc_list); 5862306a36Sopenharmony_cistatic DEFINE_MUTEX(misc_mtx); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Assigned numbers, used for dynamic minors 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci#define DYNAMIC_MINORS 128 /* like dynamic majors */ 6462306a36Sopenharmony_cistatic DEFINE_IDA(misc_minors_ida); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int misc_minor_alloc(void) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL); 7162306a36Sopenharmony_ci if (ret >= 0) { 7262306a36Sopenharmony_ci ret = DYNAMIC_MINORS - ret - 1; 7362306a36Sopenharmony_ci } else { 7462306a36Sopenharmony_ci ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, 7562306a36Sopenharmony_ci MINORMASK, GFP_KERNEL); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci return ret; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void misc_minor_free(int minor) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci if (minor < DYNAMIC_MINORS) 8362306a36Sopenharmony_ci ida_free(&misc_minors_ida, DYNAMIC_MINORS - minor - 1); 8462306a36Sopenharmony_ci else if (minor > MISC_DYNAMIC_MINOR) 8562306a36Sopenharmony_ci ida_free(&misc_minors_ida, minor); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 8962306a36Sopenharmony_cistatic void *misc_seq_start(struct seq_file *seq, loff_t *pos) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci mutex_lock(&misc_mtx); 9262306a36Sopenharmony_ci return seq_list_start(&misc_list, *pos); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return seq_list_next(v, &misc_list, pos); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void misc_seq_stop(struct seq_file *seq, void *v) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci mutex_unlock(&misc_mtx); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int misc_seq_show(struct seq_file *seq, void *v) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci const struct miscdevice *p = list_entry(v, struct miscdevice, list); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct seq_operations misc_seq_ops = { 11562306a36Sopenharmony_ci .start = misc_seq_start, 11662306a36Sopenharmony_ci .next = misc_seq_next, 11762306a36Sopenharmony_ci .stop = misc_seq_stop, 11862306a36Sopenharmony_ci .show = misc_seq_show, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int misc_open(struct inode *inode, struct file *file) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int minor = iminor(inode); 12562306a36Sopenharmony_ci struct miscdevice *c = NULL, *iter; 12662306a36Sopenharmony_ci int err = -ENODEV; 12762306a36Sopenharmony_ci const struct file_operations *new_fops = NULL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci mutex_lock(&misc_mtx); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci list_for_each_entry(iter, &misc_list, list) { 13262306a36Sopenharmony_ci if (iter->minor != minor) 13362306a36Sopenharmony_ci continue; 13462306a36Sopenharmony_ci c = iter; 13562306a36Sopenharmony_ci new_fops = fops_get(iter->fops); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!new_fops) { 14062306a36Sopenharmony_ci mutex_unlock(&misc_mtx); 14162306a36Sopenharmony_ci request_module("char-major-%d-%d", MISC_MAJOR, minor); 14262306a36Sopenharmony_ci mutex_lock(&misc_mtx); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci list_for_each_entry(iter, &misc_list, list) { 14562306a36Sopenharmony_ci if (iter->minor != minor) 14662306a36Sopenharmony_ci continue; 14762306a36Sopenharmony_ci c = iter; 14862306a36Sopenharmony_ci new_fops = fops_get(iter->fops); 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (!new_fops) 15262306a36Sopenharmony_ci goto fail; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Place the miscdevice in the file's 15762306a36Sopenharmony_ci * private_data so it can be used by the 15862306a36Sopenharmony_ci * file operations, including f_op->open below 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci file->private_data = c; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci err = 0; 16362306a36Sopenharmony_ci replace_fops(file, new_fops); 16462306a36Sopenharmony_ci if (file->f_op->open) 16562306a36Sopenharmony_ci err = file->f_op->open(inode, file); 16662306a36Sopenharmony_cifail: 16762306a36Sopenharmony_ci mutex_unlock(&misc_mtx); 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic char *misc_devnode(const struct device *dev, umode_t *mode) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci const struct miscdevice *c = dev_get_drvdata(dev); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (mode && c->mode) 17662306a36Sopenharmony_ci *mode = c->mode; 17762306a36Sopenharmony_ci if (c->nodename) 17862306a36Sopenharmony_ci return kstrdup(c->nodename, GFP_KERNEL); 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct class misc_class = { 18362306a36Sopenharmony_ci .name = "misc", 18462306a36Sopenharmony_ci .devnode = misc_devnode, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct file_operations misc_fops = { 18862306a36Sopenharmony_ci .owner = THIS_MODULE, 18962306a36Sopenharmony_ci .open = misc_open, 19062306a36Sopenharmony_ci .llseek = noop_llseek, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/** 19462306a36Sopenharmony_ci * misc_register - register a miscellaneous device 19562306a36Sopenharmony_ci * @misc: device structure 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Register a miscellaneous device with the kernel. If the minor 19862306a36Sopenharmony_ci * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 19962306a36Sopenharmony_ci * and placed in the minor field of the structure. For other cases 20062306a36Sopenharmony_ci * the minor number requested is used. 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * The structure passed is linked into the kernel and may not be 20362306a36Sopenharmony_ci * destroyed until it has been unregistered. By default, an open() 20462306a36Sopenharmony_ci * syscall to the device sets file->private_data to point to the 20562306a36Sopenharmony_ci * structure. Drivers don't need open in fops for this. 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * A zero is returned on success and a negative errno code for 20862306a36Sopenharmony_ci * failure. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciint misc_register(struct miscdevice *misc) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci dev_t dev; 21462306a36Sopenharmony_ci int err = 0; 21562306a36Sopenharmony_ci bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci INIT_LIST_HEAD(&misc->list); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci mutex_lock(&misc_mtx); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (is_dynamic) { 22262306a36Sopenharmony_ci int i = misc_minor_alloc(); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (i < 0) { 22562306a36Sopenharmony_ci err = -EBUSY; 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci misc->minor = i; 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci struct miscdevice *c; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci list_for_each_entry(c, &misc_list, list) { 23362306a36Sopenharmony_ci if (c->minor == misc->minor) { 23462306a36Sopenharmony_ci err = -EBUSY; 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci dev = MKDEV(MISC_MAJOR, misc->minor); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci misc->this_device = 24362306a36Sopenharmony_ci device_create_with_groups(&misc_class, misc->parent, dev, 24462306a36Sopenharmony_ci misc, misc->groups, "%s", misc->name); 24562306a36Sopenharmony_ci if (IS_ERR(misc->this_device)) { 24662306a36Sopenharmony_ci if (is_dynamic) { 24762306a36Sopenharmony_ci misc_minor_free(misc->minor); 24862306a36Sopenharmony_ci misc->minor = MISC_DYNAMIC_MINOR; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci err = PTR_ERR(misc->this_device); 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Add it to the front, so that later devices can "override" 25662306a36Sopenharmony_ci * earlier defaults 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci list_add(&misc->list, &misc_list); 25962306a36Sopenharmony_ci out: 26062306a36Sopenharmony_ci mutex_unlock(&misc_mtx); 26162306a36Sopenharmony_ci return err; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ciEXPORT_SYMBOL(misc_register); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * misc_deregister - unregister a miscellaneous device 26762306a36Sopenharmony_ci * @misc: device to unregister 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * Unregister a miscellaneous device that was previously 27062306a36Sopenharmony_ci * successfully registered with misc_register(). 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_civoid misc_deregister(struct miscdevice *misc) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci if (WARN_ON(list_empty(&misc->list))) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci mutex_lock(&misc_mtx); 27962306a36Sopenharmony_ci list_del(&misc->list); 28062306a36Sopenharmony_ci device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor)); 28162306a36Sopenharmony_ci misc_minor_free(misc->minor); 28262306a36Sopenharmony_ci mutex_unlock(&misc_mtx); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ciEXPORT_SYMBOL(misc_deregister); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int __init misc_init(void) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int err; 28962306a36Sopenharmony_ci struct proc_dir_entry *ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); 29262306a36Sopenharmony_ci err = class_register(&misc_class); 29362306a36Sopenharmony_ci if (err) 29462306a36Sopenharmony_ci goto fail_remove; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci err = -EIO; 29762306a36Sopenharmony_ci if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) 29862306a36Sopenharmony_ci goto fail_printk; 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cifail_printk: 30262306a36Sopenharmony_ci pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 30362306a36Sopenharmony_ci class_unregister(&misc_class); 30462306a36Sopenharmony_cifail_remove: 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci remove_proc_entry("misc", NULL); 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_cisubsys_initcall(misc_init); 310