brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · a82a6a0 Raw
310 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * linux/drivers/char/misc.c4 *5 * Generic misc open routine by Johan Myreen6 *7 * Based on code from Linus8 *9 * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's10 *   changes incorporated into 0.97pl411 *   by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)12 *   See busmouse.c for particulars.13 *14 * Made things a lot mode modular - easy to compile in just one or two15 * of the misc drivers, as they are now completely independent. Linus.16 *17 * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>18 *19 * Fixed a failing symbol register to free the device registration20 *		Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-9621 *22 * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-9623 *24 * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-9625 *26 * Handling of mouse minor numbers for kerneld:27 *  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,28 *  adapted by Bjorn Ekwall <bj0rn@blox.se>29 *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>30 *31 * Changes for kmod (from kerneld):32 *	Cyrus Durgin <cider@speakeasy.org>33 *34 * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  10-Jan-199835 */36 37#include <linux/module.h>38 39#include <linux/fs.h>40#include <linux/errno.h>41#include <linux/miscdevice.h>42#include <linux/kernel.h>43#include <linux/major.h>44#include <linux/mutex.h>45#include <linux/proc_fs.h>46#include <linux/seq_file.h>47#include <linux/stat.h>48#include <linux/init.h>49#include <linux/device.h>50#include <linux/tty.h>51#include <linux/kmod.h>52#include <linux/gfp.h>53 54/*55 * Head entry for the doubly linked miscdevice list56 */57static LIST_HEAD(misc_list);58static DEFINE_MUTEX(misc_mtx);59 60/*61 * Assigned numbers, used for dynamic minors62 */63#define DYNAMIC_MINORS 128 /* like dynamic majors */64static DEFINE_IDA(misc_minors_ida);65 66static int misc_minor_alloc(void)67{68	int ret;69 70	ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);71	if (ret >= 0) {72		ret = DYNAMIC_MINORS - ret - 1;73	} else {74		ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,75				      MINORMASK, GFP_KERNEL);76	}77	return ret;78}79 80static void misc_minor_free(int minor)81{82	if (minor < DYNAMIC_MINORS)83		ida_free(&misc_minors_ida, DYNAMIC_MINORS - minor - 1);84	else if (minor > MISC_DYNAMIC_MINOR)85		ida_free(&misc_minors_ida, minor);86}87 88#ifdef CONFIG_PROC_FS89static void *misc_seq_start(struct seq_file *seq, loff_t *pos)90{91	mutex_lock(&misc_mtx);92	return seq_list_start(&misc_list, *pos);93}94 95static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)96{97	return seq_list_next(v, &misc_list, pos);98}99 100static HWJS_SUSPENDS void misc_seq_stop(struct seq_file *seq, void *v)101{102	mutex_unlock(&misc_mtx);103}104 105static int misc_seq_show(struct seq_file *seq, void *v)106{107	const struct miscdevice *p = list_entry(v, struct miscdevice, list);108 109	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");110	return 0;111}112 113 114static const struct seq_operations misc_seq_ops = {115	.start = misc_seq_start,116	.next  = misc_seq_next,117	.stop  = misc_seq_stop,118	.show  = misc_seq_show,119};120#endif121 122static int misc_open(struct inode *inode, struct file *file)123{124	int minor = iminor(inode);125	struct miscdevice *c = NULL, *iter;126	int err = -ENODEV;127	const struct file_operations *new_fops = NULL;128 129	mutex_lock(&misc_mtx);130 131	list_for_each_entry(iter, &misc_list, list) {132		if (iter->minor != minor)133			continue;134		c = iter;135		new_fops = fops_get(iter->fops);136		break;137	}138 139	if (!new_fops) {140		mutex_unlock(&misc_mtx);141		request_module("char-major-%d-%d", MISC_MAJOR, minor);142		mutex_lock(&misc_mtx);143 144		list_for_each_entry(iter, &misc_list, list) {145			if (iter->minor != minor)146				continue;147			c = iter;148			new_fops = fops_get(iter->fops);149			break;150		}151		if (!new_fops)152			goto fail;153	}154 155	/*156	 * Place the miscdevice in the file's157	 * private_data so it can be used by the158	 * file operations, including f_op->open below159	 */160	file->private_data = c;161 162	err = 0;163	replace_fops(file, new_fops);164	if (file->f_op->open)165		err = file->f_op->open(inode, file);166fail:167	mutex_unlock(&misc_mtx);168	return err;169}170 171static char *misc_devnode(const struct device *dev, umode_t *mode)172{173	const struct miscdevice *c = dev_get_drvdata(dev);174 175	if (mode && c->mode)176		*mode = c->mode;177	if (c->nodename)178		return kstrdup(c->nodename, GFP_KERNEL);179	return NULL;180}181 182static const struct class misc_class = {183	.name		= "misc",184	.devnode	= misc_devnode,185};186 187static const struct file_operations misc_fops = {188	.owner		= THIS_MODULE,189	.open		= misc_open,190	.llseek		= noop_llseek,191};192 193/**194 *	misc_register	-	register a miscellaneous device195 *	@misc: device structure196 *197 *	Register a miscellaneous device with the kernel. If the minor198 *	number is set to %MISC_DYNAMIC_MINOR a minor number is assigned199 *	and placed in the minor field of the structure. For other cases200 *	the minor number requested is used.201 *202 *	The structure passed is linked into the kernel and may not be203 *	destroyed until it has been unregistered. By default, an open()204 *	syscall to the device sets file->private_data to point to the205 *	structure. Drivers don't need open in fops for this.206 *207 *	A zero is returned on success and a negative errno code for208 *	failure.209 */210 211int misc_register(struct miscdevice *misc)212{213	dev_t dev;214	int err = 0;215	bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);216 217	INIT_LIST_HEAD(&misc->list);218 219	mutex_lock(&misc_mtx);220 221	if (is_dynamic) {222		int i = misc_minor_alloc();223 224		if (i < 0) {225			err = -EBUSY;226			goto out;227		}228		misc->minor = i;229	} else {230		struct miscdevice *c;231 232		list_for_each_entry(c, &misc_list, list) {233			if (c->minor == misc->minor) {234				err = -EBUSY;235				goto out;236			}237		}238	}239 240	dev = MKDEV(MISC_MAJOR, misc->minor);241 242	misc->this_device =243		device_create_with_groups(&misc_class, misc->parent, dev,244					  misc, misc->groups, "%s", misc->name);245	if (IS_ERR(misc->this_device)) {246		if (is_dynamic) {247			misc_minor_free(misc->minor);248			misc->minor = MISC_DYNAMIC_MINOR;249		}250		err = PTR_ERR(misc->this_device);251		goto out;252	}253 254	/*255	 * Add it to the front, so that later devices can "override"256	 * earlier defaults257	 */258	list_add(&misc->list, &misc_list);259 out:260	mutex_unlock(&misc_mtx);261	return err;262}263EXPORT_SYMBOL(misc_register);264 265/**266 *	misc_deregister - unregister a miscellaneous device267 *	@misc: device to unregister268 *269 *	Unregister a miscellaneous device that was previously270 *	successfully registered with misc_register().271 */272 273void misc_deregister(struct miscdevice *misc)274{275	if (WARN_ON(list_empty(&misc->list)))276		return;277 278	mutex_lock(&misc_mtx);279	list_del(&misc->list);280	device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));281	misc_minor_free(misc->minor);282	mutex_unlock(&misc_mtx);283}284EXPORT_SYMBOL(misc_deregister);285 286static int __init misc_init(void)287{288	int err;289	struct proc_dir_entry *ret;290 291	ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);292	err = class_register(&misc_class);293	if (err)294		goto fail_remove;295 296	err = -EIO;297	if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))298		goto fail_printk;299	return 0;300 301fail_printk:302	pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);303	class_unregister(&misc_class);304fail_remove:305	if (ret)306		remove_proc_entry("misc", NULL);307	return err;308}309subsys_initcall(misc_init);310