xref: /openbmc/linux/drivers/char/misc.c (revision 360823a09426347ea8f232b0b0b5156d0aed0302)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * linux/drivers/char/misc.c
4  *
5  * Generic misc open routine by Johan Myreen
6  *
7  * Based on code from Linus
8  *
9  * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
10  *   changes incorporated into 0.97pl4
11  *   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 two
15  * 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 registration
20  *		Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
21  *
22  * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
23  *
24  * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
25  *
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-1998
35  */
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 list
56  */
57 static LIST_HEAD(misc_list);
58 static DEFINE_MUTEX(misc_mtx);
59 
60 /*
61  * Assigned numbers, used for dynamic minors
62  */
63 #define DYNAMIC_MINORS 128 /* like dynamic majors */
64 static DEFINE_IDA(misc_minors_ida);
65 
misc_minor_alloc(int minor)66 static int misc_minor_alloc(int minor)
67 {
68 	int ret = 0;
69 
70 	if (minor == MISC_DYNAMIC_MINOR) {
71 		/* allocate free id */
72 		ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
73 		if (ret >= 0) {
74 			ret = DYNAMIC_MINORS - ret - 1;
75 		} else {
76 			ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
77 					      MINORMASK, GFP_KERNEL);
78 		}
79 	} else {
80 		/* specific minor, check if it is in dynamic or misc dynamic range  */
81 		if (minor < DYNAMIC_MINORS) {
82 			minor = DYNAMIC_MINORS - minor - 1;
83 			ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
84 		} else if (minor > MISC_DYNAMIC_MINOR) {
85 			ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
86 		} else {
87 			/* case of non-dynamic minors, no need to allocate id */
88 			ret = 0;
89 		}
90 	}
91 	return ret;
92 }
93 
misc_minor_free(int minor)94 static void misc_minor_free(int minor)
95 {
96 	if (minor < DYNAMIC_MINORS)
97 		ida_free(&misc_minors_ida, DYNAMIC_MINORS - minor - 1);
98 	else if (minor > MISC_DYNAMIC_MINOR)
99 		ida_free(&misc_minors_ida, minor);
100 }
101 
102 #ifdef CONFIG_PROC_FS
misc_seq_start(struct seq_file * seq,loff_t * pos)103 static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
104 {
105 	mutex_lock(&misc_mtx);
106 	return seq_list_start(&misc_list, *pos);
107 }
108 
misc_seq_next(struct seq_file * seq,void * v,loff_t * pos)109 static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
110 {
111 	return seq_list_next(v, &misc_list, pos);
112 }
113 
misc_seq_stop(struct seq_file * seq,void * v)114 static void misc_seq_stop(struct seq_file *seq, void *v)
115 {
116 	mutex_unlock(&misc_mtx);
117 }
118 
misc_seq_show(struct seq_file * seq,void * v)119 static int misc_seq_show(struct seq_file *seq, void *v)
120 {
121 	const struct miscdevice *p = list_entry(v, struct miscdevice, list);
122 
123 	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
124 	return 0;
125 }
126 
127 
128 static const struct seq_operations misc_seq_ops = {
129 	.start = misc_seq_start,
130 	.next  = misc_seq_next,
131 	.stop  = misc_seq_stop,
132 	.show  = misc_seq_show,
133 };
134 #endif
135 
misc_open(struct inode * inode,struct file * file)136 static int misc_open(struct inode *inode, struct file *file)
137 {
138 	int minor = iminor(inode);
139 	struct miscdevice *c = NULL, *iter;
140 	int err = -ENODEV;
141 	const struct file_operations *new_fops = NULL;
142 
143 	mutex_lock(&misc_mtx);
144 
145 	list_for_each_entry(iter, &misc_list, list) {
146 		if (iter->minor != minor)
147 			continue;
148 		c = iter;
149 		new_fops = fops_get(iter->fops);
150 		break;
151 	}
152 
153 	if (!new_fops) {
154 		mutex_unlock(&misc_mtx);
155 		request_module("char-major-%d-%d", MISC_MAJOR, minor);
156 		mutex_lock(&misc_mtx);
157 
158 		list_for_each_entry(iter, &misc_list, list) {
159 			if (iter->minor != minor)
160 				continue;
161 			c = iter;
162 			new_fops = fops_get(iter->fops);
163 			break;
164 		}
165 		if (!new_fops)
166 			goto fail;
167 	}
168 
169 	/*
170 	 * Place the miscdevice in the file's
171 	 * private_data so it can be used by the
172 	 * file operations, including f_op->open below
173 	 */
174 	file->private_data = c;
175 
176 	err = 0;
177 	replace_fops(file, new_fops);
178 	if (file->f_op->open)
179 		err = file->f_op->open(inode, file);
180 fail:
181 	mutex_unlock(&misc_mtx);
182 	return err;
183 }
184 
misc_devnode(const struct device * dev,umode_t * mode)185 static char *misc_devnode(const struct device *dev, umode_t *mode)
186 {
187 	const struct miscdevice *c = dev_get_drvdata(dev);
188 
189 	if (mode && c->mode)
190 		*mode = c->mode;
191 	if (c->nodename)
192 		return kstrdup(c->nodename, GFP_KERNEL);
193 	return NULL;
194 }
195 
196 static const struct class misc_class = {
197 	.name		= "misc",
198 	.devnode	= misc_devnode,
199 };
200 
201 static const struct file_operations misc_fops = {
202 	.owner		= THIS_MODULE,
203 	.open		= misc_open,
204 	.llseek		= noop_llseek,
205 };
206 
207 /**
208  *	misc_register	-	register a miscellaneous device
209  *	@misc: device structure
210  *
211  *	Register a miscellaneous device with the kernel. If the minor
212  *	number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
213  *	and placed in the minor field of the structure. For other cases
214  *	the minor number requested is used.
215  *
216  *	The structure passed is linked into the kernel and may not be
217  *	destroyed until it has been unregistered. By default, an open()
218  *	syscall to the device sets file->private_data to point to the
219  *	structure. Drivers don't need open in fops for this.
220  *
221  *	A zero is returned on success and a negative errno code for
222  *	failure.
223  */
224 
misc_register(struct miscdevice * misc)225 int misc_register(struct miscdevice *misc)
226 {
227 	dev_t dev;
228 	int err = 0;
229 	bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
230 
231 	INIT_LIST_HEAD(&misc->list);
232 
233 	mutex_lock(&misc_mtx);
234 
235 	if (is_dynamic) {
236 		int i = misc_minor_alloc(misc->minor);
237 
238 		if (i < 0) {
239 			err = -EBUSY;
240 			goto out;
241 		}
242 		misc->minor = i;
243 	} else {
244 		struct miscdevice *c;
245 		int i;
246 
247 		list_for_each_entry(c, &misc_list, list) {
248 			if (c->minor == misc->minor) {
249 				err = -EBUSY;
250 				goto out;
251 			}
252 		}
253 
254 		i = misc_minor_alloc(misc->minor);
255 		if (i < 0) {
256 			err = -EBUSY;
257 			goto out;
258 		}
259 	}
260 
261 	dev = MKDEV(MISC_MAJOR, misc->minor);
262 
263 	misc->this_device =
264 		device_create_with_groups(&misc_class, misc->parent, dev,
265 					  misc, misc->groups, "%s", misc->name);
266 	if (IS_ERR(misc->this_device)) {
267 		if (is_dynamic) {
268 			misc_minor_free(misc->minor);
269 			misc->minor = MISC_DYNAMIC_MINOR;
270 		}
271 		err = PTR_ERR(misc->this_device);
272 		goto out;
273 	}
274 
275 	/*
276 	 * Add it to the front, so that later devices can "override"
277 	 * earlier defaults
278 	 */
279 	list_add(&misc->list, &misc_list);
280  out:
281 	mutex_unlock(&misc_mtx);
282 	return err;
283 }
284 EXPORT_SYMBOL(misc_register);
285 
286 /**
287  *	misc_deregister - unregister a miscellaneous device
288  *	@misc: device to unregister
289  *
290  *	Unregister a miscellaneous device that was previously
291  *	successfully registered with misc_register().
292  */
293 
misc_deregister(struct miscdevice * misc)294 void misc_deregister(struct miscdevice *misc)
295 {
296 	if (WARN_ON(list_empty(&misc->list)))
297 		return;
298 
299 	mutex_lock(&misc_mtx);
300 	list_del(&misc->list);
301 	device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));
302 	misc_minor_free(misc->minor);
303 	mutex_unlock(&misc_mtx);
304 }
305 EXPORT_SYMBOL(misc_deregister);
306 
misc_init(void)307 static int __init misc_init(void)
308 {
309 	int err;
310 	struct proc_dir_entry *ret;
311 
312 	ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
313 	err = class_register(&misc_class);
314 	if (err)
315 		goto fail_remove;
316 
317 	err = -EIO;
318 	if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
319 		goto fail_printk;
320 	return 0;
321 
322 fail_printk:
323 	pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
324 	class_unregister(&misc_class);
325 fail_remove:
326 	if (ret)
327 		remove_proc_entry("misc", NULL);
328 	return err;
329 }
330 subsys_initcall(misc_init);
331