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