1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/drivers/char/misc.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Generic misc open routine by Johan Myreen
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Based on code from Linus
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
101da177e4SLinus Torvalds * changes incorporated into 0.97pl4
111da177e4SLinus Torvalds * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
121da177e4SLinus Torvalds * See busmouse.c for particulars.
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds * Made things a lot mode modular - easy to compile in just one or two
151da177e4SLinus Torvalds * of the misc drivers, as they are now completely independent. Linus.
161da177e4SLinus Torvalds *
171da177e4SLinus Torvalds * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
181da177e4SLinus Torvalds *
191da177e4SLinus Torvalds * Fixed a failing symbol register to free the device registration
201da177e4SLinus Torvalds * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
211da177e4SLinus Torvalds *
221da177e4SLinus Torvalds * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
231da177e4SLinus Torvalds *
241da177e4SLinus Torvalds * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
251da177e4SLinus Torvalds *
261da177e4SLinus Torvalds * Handling of mouse minor numbers for kerneld:
271da177e4SLinus Torvalds * Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
281da177e4SLinus Torvalds * adapted by Bjorn Ekwall <bj0rn@blox.se>
291da177e4SLinus Torvalds * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
301da177e4SLinus Torvalds *
311da177e4SLinus Torvalds * Changes for kmod (from kerneld):
321da177e4SLinus Torvalds * Cyrus Durgin <cider@speakeasy.org>
331da177e4SLinus Torvalds *
341da177e4SLinus Torvalds * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998
351da177e4SLinus Torvalds */
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds #include <linux/module.h>
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds #include <linux/fs.h>
401da177e4SLinus Torvalds #include <linux/errno.h>
411da177e4SLinus Torvalds #include <linux/miscdevice.h>
421da177e4SLinus Torvalds #include <linux/kernel.h>
431da177e4SLinus Torvalds #include <linux/major.h>
440e82d5b6SMatthias Kaehlcke #include <linux/mutex.h>
451da177e4SLinus Torvalds #include <linux/proc_fs.h>
461da177e4SLinus Torvalds #include <linux/seq_file.h>
471da177e4SLinus Torvalds #include <linux/stat.h>
481da177e4SLinus Torvalds #include <linux/init.h>
491da177e4SLinus Torvalds #include <linux/device.h>
501da177e4SLinus Torvalds #include <linux/tty.h>
511da177e4SLinus Torvalds #include <linux/kmod.h>
525a0e3ad6STejun Heo #include <linux/gfp.h>
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds /*
551da177e4SLinus Torvalds * Head entry for the doubly linked miscdevice list
561da177e4SLinus Torvalds */
571da177e4SLinus Torvalds static LIST_HEAD(misc_list);
580e82d5b6SMatthias Kaehlcke static DEFINE_MUTEX(misc_mtx);
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds /*
611da177e4SLinus Torvalds * Assigned numbers, used for dynamic minors
621da177e4SLinus Torvalds */
63c62b1f97SSangmoon Kim #define DYNAMIC_MINORS 128 /* like dynamic majors */
64ab760791SD Scott Phillips static DEFINE_IDA(misc_minors_ida);
65ab760791SD Scott Phillips
misc_minor_alloc(void)66ab760791SD Scott Phillips static int misc_minor_alloc(void)
67ab760791SD Scott Phillips {
68ab760791SD Scott Phillips int ret;
69ab760791SD Scott Phillips
70ab760791SD Scott Phillips ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
71ab760791SD Scott Phillips if (ret >= 0) {
72ab760791SD Scott Phillips ret = DYNAMIC_MINORS - ret - 1;
73ab760791SD Scott Phillips } else {
74ab760791SD Scott Phillips ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
75ab760791SD Scott Phillips MINORMASK, GFP_KERNEL);
76ab760791SD Scott Phillips }
77ab760791SD Scott Phillips return ret;
78ab760791SD Scott Phillips }
79ab760791SD Scott Phillips
misc_minor_free(int minor)80ab760791SD Scott Phillips static void misc_minor_free(int minor)
81ab760791SD Scott Phillips {
82ab760791SD Scott Phillips if (minor < DYNAMIC_MINORS)
83ab760791SD Scott Phillips ida_free(&misc_minors_ida, DYNAMIC_MINORS - minor - 1);
84ab760791SD Scott Phillips else if (minor > MISC_DYNAMIC_MINOR)
85ab760791SD Scott Phillips ida_free(&misc_minors_ida, minor);
86ab760791SD Scott Phillips }
871da177e4SLinus Torvalds
881da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
misc_seq_start(struct seq_file * seq,loff_t * pos)891da177e4SLinus Torvalds static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
901da177e4SLinus Torvalds {
910e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx);
9246c65b71SPavel Emelianov return seq_list_start(&misc_list, *pos);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds
misc_seq_next(struct seq_file * seq,void * v,loff_t * pos)951da177e4SLinus Torvalds static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
961da177e4SLinus Torvalds {
9746c65b71SPavel Emelianov return seq_list_next(v, &misc_list, pos);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
misc_seq_stop(struct seq_file * seq,void * v)1001da177e4SLinus Torvalds static void misc_seq_stop(struct seq_file *seq, void *v)
1011da177e4SLinus Torvalds {
1020e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds
misc_seq_show(struct seq_file * seq,void * v)1051da177e4SLinus Torvalds static int misc_seq_show(struct seq_file *seq, void *v)
1061da177e4SLinus Torvalds {
10746c65b71SPavel Emelianov const struct miscdevice *p = list_entry(v, struct miscdevice, list);
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
1101da177e4SLinus Torvalds return 0;
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds
11488e9d34cSJames Morris static const struct seq_operations misc_seq_ops = {
1151da177e4SLinus Torvalds .start = misc_seq_start,
1161da177e4SLinus Torvalds .next = misc_seq_next,
1171da177e4SLinus Torvalds .stop = misc_seq_stop,
1181da177e4SLinus Torvalds .show = misc_seq_show,
1191da177e4SLinus Torvalds };
1201da177e4SLinus Torvalds #endif
1211da177e4SLinus Torvalds
misc_open(struct inode * inode,struct file * file)1221da177e4SLinus Torvalds static int misc_open(struct inode *inode, struct file *file)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds int minor = iminor(inode);
1253a5e6502SJakob Koschel struct miscdevice *c = NULL, *iter;
1261da177e4SLinus Torvalds int err = -ENODEV;
127e84f9e57SAl Viro const struct file_operations *new_fops = NULL;
1281da177e4SLinus Torvalds
1290e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx);
1301da177e4SLinus Torvalds
1313a5e6502SJakob Koschel list_for_each_entry(iter, &misc_list, list) {
1323a5e6502SJakob Koschel if (iter->minor != minor)
1333a5e6502SJakob Koschel continue;
1343a5e6502SJakob Koschel c = iter;
1353a5e6502SJakob Koschel new_fops = fops_get(iter->fops);
1361da177e4SLinus Torvalds break;
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds
1391da177e4SLinus Torvalds if (!new_fops) {
1400e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx);
1411da177e4SLinus Torvalds request_module("char-major-%d-%d", MISC_MAJOR, minor);
1420e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx);
1431da177e4SLinus Torvalds
1443a5e6502SJakob Koschel list_for_each_entry(iter, &misc_list, list) {
1453a5e6502SJakob Koschel if (iter->minor != minor)
1463a5e6502SJakob Koschel continue;
1473a5e6502SJakob Koschel c = iter;
1483a5e6502SJakob Koschel new_fops = fops_get(iter->fops);
1491da177e4SLinus Torvalds break;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds if (!new_fops)
1521da177e4SLinus Torvalds goto fail;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds
1550b509d8dSTom Van Braeckel /*
1560b509d8dSTom Van Braeckel * Place the miscdevice in the file's
1570b509d8dSTom Van Braeckel * private_data so it can be used by the
1580b509d8dSTom Van Braeckel * file operations, including f_op->open below
1590b509d8dSTom Van Braeckel */
1600b509d8dSTom Van Braeckel file->private_data = c;
1610b509d8dSTom Van Braeckel
1621da177e4SLinus Torvalds err = 0;
163e84f9e57SAl Viro replace_fops(file, new_fops);
1640b509d8dSTom Van Braeckel if (file->f_op->open)
1651da177e4SLinus Torvalds err = file->f_op->open(inode, file);
1661da177e4SLinus Torvalds fail:
1670e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx);
1681da177e4SLinus Torvalds return err;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds
misc_devnode(const struct device * dev,umode_t * mode)171*eafd52e6SIvan Orlov static char *misc_devnode(const struct device *dev, umode_t *mode)
172*eafd52e6SIvan Orlov {
173*eafd52e6SIvan Orlov const struct miscdevice *c = dev_get_drvdata(dev);
174*eafd52e6SIvan Orlov
175*eafd52e6SIvan Orlov if (mode && c->mode)
176*eafd52e6SIvan Orlov *mode = c->mode;
177*eafd52e6SIvan Orlov if (c->nodename)
178*eafd52e6SIvan Orlov return kstrdup(c->nodename, GFP_KERNEL);
179*eafd52e6SIvan Orlov return NULL;
180*eafd52e6SIvan Orlov }
181*eafd52e6SIvan Orlov
182*eafd52e6SIvan Orlov static const struct class misc_class = {
183*eafd52e6SIvan Orlov .name = "misc",
184*eafd52e6SIvan Orlov .devnode = misc_devnode,
185*eafd52e6SIvan Orlov };
1861da177e4SLinus Torvalds
18762322d25SArjan van de Ven static const struct file_operations misc_fops = {
1881da177e4SLinus Torvalds .owner = THIS_MODULE,
1891da177e4SLinus Torvalds .open = misc_open,
1906038f373SArnd Bergmann .llseek = noop_llseek,
1911da177e4SLinus Torvalds };
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds /**
1941da177e4SLinus Torvalds * misc_register - register a miscellaneous device
1951da177e4SLinus Torvalds * @misc: device structure
1961da177e4SLinus Torvalds *
1971da177e4SLinus Torvalds * Register a miscellaneous device with the kernel. If the minor
1981da177e4SLinus Torvalds * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
1991da177e4SLinus Torvalds * and placed in the minor field of the structure. For other cases
2001da177e4SLinus Torvalds * the minor number requested is used.
2011da177e4SLinus Torvalds *
2021da177e4SLinus Torvalds * The structure passed is linked into the kernel and may not be
20303190c67SMartin Kepplinger * destroyed until it has been unregistered. By default, an open()
20403190c67SMartin Kepplinger * syscall to the device sets file->private_data to point to the
20503190c67SMartin Kepplinger * structure. Drivers don't need open in fops for this.
2061da177e4SLinus Torvalds *
2071da177e4SLinus Torvalds * A zero is returned on success and a negative errno code for
2081da177e4SLinus Torvalds * failure.
2091da177e4SLinus Torvalds */
2101da177e4SLinus Torvalds
misc_register(struct miscdevice * misc)2111da177e4SLinus Torvalds int misc_register(struct miscdevice *misc)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds dev_t dev;
2147c69ef79SGreg Kroah-Hartman int err = 0;
215b575f712SVladimir Zapolskiy bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
2161da177e4SLinus Torvalds
2175d469ec0SNeil Horman INIT_LIST_HEAD(&misc->list);
2185d469ec0SNeil Horman
2190e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx);
2201da177e4SLinus Torvalds
221b575f712SVladimir Zapolskiy if (is_dynamic) {
222ab760791SD Scott Phillips int i = misc_minor_alloc();
2235b884a95SVarsha Rao
224ab760791SD Scott Phillips if (i < 0) {
225684116caSElad Wexler err = -EBUSY;
226684116caSElad Wexler goto out;
2271da177e4SLinus Torvalds }
228ab760791SD Scott Phillips misc->minor = i;
2293c94ce6fSDae S. Kim } else {
2303c94ce6fSDae S. Kim struct miscdevice *c;
2313c94ce6fSDae S. Kim
2323c94ce6fSDae S. Kim list_for_each_entry(c, &misc_list, list) {
2333c94ce6fSDae S. Kim if (c->minor == misc->minor) {
234684116caSElad Wexler err = -EBUSY;
235684116caSElad Wexler goto out;
2363c94ce6fSDae S. Kim }
2373c94ce6fSDae S. Kim }
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds dev = MKDEV(MISC_MAJOR, misc->minor);
2411da177e4SLinus Torvalds
242bd735995STakashi Iwai misc->this_device =
243*eafd52e6SIvan Orlov device_create_with_groups(&misc_class, misc->parent, dev,
244bd735995STakashi Iwai misc, misc->groups, "%s", misc->name);
24594fbcdedSGreg Kroah-Hartman if (IS_ERR(misc->this_device)) {
246b575f712SVladimir Zapolskiy if (is_dynamic) {
247ab760791SD Scott Phillips misc_minor_free(misc->minor);
248b575f712SVladimir Zapolskiy misc->minor = MISC_DYNAMIC_MINOR;
249b575f712SVladimir Zapolskiy }
25094fbcdedSGreg Kroah-Hartman err = PTR_ERR(misc->this_device);
2511da177e4SLinus Torvalds goto out;
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds
2541da177e4SLinus Torvalds /*
2551da177e4SLinus Torvalds * Add it to the front, so that later devices can "override"
2561da177e4SLinus Torvalds * earlier defaults
2571da177e4SLinus Torvalds */
2581da177e4SLinus Torvalds list_add(&misc->list, &misc_list);
2591da177e4SLinus Torvalds out:
2600e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx);
2611da177e4SLinus Torvalds return err;
2621da177e4SLinus Torvalds }
263e89bec3aSNaveen Kumar Parna EXPORT_SYMBOL(misc_register);
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds /**
266b844eba2SRafael J. Wysocki * misc_deregister - unregister a miscellaneous device
2671da177e4SLinus Torvalds * @misc: device to unregister
2681da177e4SLinus Torvalds *
2691da177e4SLinus Torvalds * Unregister a miscellaneous device that was previously
270f368ed60SGreg Kroah-Hartman * successfully registered with misc_register().
2711da177e4SLinus Torvalds */
2721da177e4SLinus Torvalds
misc_deregister(struct miscdevice * misc)273f368ed60SGreg Kroah-Hartman void misc_deregister(struct miscdevice *misc)
2741da177e4SLinus Torvalds {
275b329becfSAkinobu Mita if (WARN_ON(list_empty(&misc->list)))
276f368ed60SGreg Kroah-Hartman return;
2771da177e4SLinus Torvalds
2780e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx);
2791da177e4SLinus Torvalds list_del(&misc->list);
280*eafd52e6SIvan Orlov device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));
281ab760791SD Scott Phillips misc_minor_free(misc->minor);
2820e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx);
2831da177e4SLinus Torvalds }
284b844eba2SRafael J. Wysocki EXPORT_SYMBOL(misc_deregister);
2851da177e4SLinus Torvalds
misc_init(void)2861da177e4SLinus Torvalds static int __init misc_init(void)
2871da177e4SLinus Torvalds {
2881b502217SDenis V. Lunev int err;
2891037b278SSudip Mukherjee struct proc_dir_entry *ret;
2901da177e4SLinus Torvalds
291fddda2b7SChristoph Hellwig ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
292*eafd52e6SIvan Orlov err = class_register(&misc_class);
293*eafd52e6SIvan Orlov if (err)
2941b502217SDenis V. Lunev goto fail_remove;
295573fc113SChristoph Hellwig
2961b502217SDenis V. Lunev err = -EIO;
2971b502217SDenis V. Lunev if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
2981b502217SDenis V. Lunev goto fail_printk;
2991da177e4SLinus Torvalds return 0;
3001b502217SDenis V. Lunev
3011b502217SDenis V. Lunev fail_printk:
3028ab44b40SVarsha Rao pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
303*eafd52e6SIvan Orlov class_unregister(&misc_class);
3041b502217SDenis V. Lunev fail_remove:
3051037b278SSudip Mukherjee if (ret)
3061b502217SDenis V. Lunev remove_proc_entry("misc", NULL);
3071b502217SDenis V. Lunev return err;
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds subsys_initcall(misc_init);
310