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