Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
misc.c
Go to the documentation of this file.
1 /*
2  * linux/drivers/char/misc.c
3  *
4  * Generic misc open routine by Johan Myreen
5  *
6  * Based on code from Linus
7  *
8  * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
9  * changes incorporated into 0.97pl4
10  * by Peter Cervasio (pete%[email protected]) (08SEP92)
11  * See busmouse.c for particulars.
12  *
13  * Made things a lot mode modular - easy to compile in just one or two
14  * of the misc drivers, as they are now completely independent. Linus.
15  *
16  * Support for loadable modules. 8-Sep-95 Philip Blundell <[email protected]>
17  *
18  * Fixed a failing symbol register to free the device registration
19  * Alan Cox <[email protected]> 21-Jan-96
20  *
21  * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
22  *
23  * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
24  *
25  * Handling of mouse minor numbers for kerneld:
26  * Idea by Jacques Gelinas <[email protected]>,
27  * adapted by Bjorn Ekwall <[email protected]>
28  * corrected by Alan Cox <[email protected]>
29  *
30  * Changes for kmod (from kerneld):
31  * Cyrus Durgin <[email protected]>
32  *
33  * Added devfs support. Richard Gooch <[email protected]> 10-Jan-1998
34  */
35 
36 #include <linux/module.h>
37 
38 #include <linux/fs.h>
39 #include <linux/errno.h>
40 #include <linux/miscdevice.h>
41 #include <linux/kernel.h>
42 #include <linux/major.h>
43 #include <linux/mutex.h>
44 #include <linux/proc_fs.h>
45 #include <linux/seq_file.h>
46 #include <linux/stat.h>
47 #include <linux/init.h>
48 #include <linux/device.h>
49 #include <linux/tty.h>
50 #include <linux/kmod.h>
51 #include <linux/gfp.h>
52 
53 /*
54  * Head entry for the doubly linked miscdevice list
55  */
56 static LIST_HEAD(misc_list);
57 static DEFINE_MUTEX(misc_mtx);
58 
59 /*
60  * Assigned numbers, used for dynamic minors
61  */
62 #define DYNAMIC_MINORS 64 /* like dynamic majors */
63 static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
64 
65 #ifdef CONFIG_PROC_FS
66 static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
67 {
68  mutex_lock(&misc_mtx);
69  return seq_list_start(&misc_list, *pos);
70 }
71 
72 static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
73 {
74  return seq_list_next(v, &misc_list, pos);
75 }
76 
77 static void misc_seq_stop(struct seq_file *seq, void *v)
78 {
79  mutex_unlock(&misc_mtx);
80 }
81 
82 static int misc_seq_show(struct seq_file *seq, void *v)
83 {
84  const struct miscdevice *p = list_entry(v, struct miscdevice, list);
85 
86  seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
87  return 0;
88 }
89 
90 
91 static const struct seq_operations misc_seq_ops = {
92  .start = misc_seq_start,
93  .next = misc_seq_next,
94  .stop = misc_seq_stop,
95  .show = misc_seq_show,
96 };
97 
98 static int misc_seq_open(struct inode *inode, struct file *file)
99 {
100  return seq_open(file, &misc_seq_ops);
101 }
102 
103 static const struct file_operations misc_proc_fops = {
104  .owner = THIS_MODULE,
105  .open = misc_seq_open,
106  .read = seq_read,
107  .llseek = seq_lseek,
108  .release = seq_release,
109 };
110 #endif
111 
112 static int misc_open(struct inode * inode, struct file * file)
113 {
114  int minor = iminor(inode);
115  struct miscdevice *c;
116  int err = -ENODEV;
117  const struct file_operations *old_fops, *new_fops = NULL;
118 
119  mutex_lock(&misc_mtx);
120 
121  list_for_each_entry(c, &misc_list, list) {
122  if (c->minor == minor) {
123  new_fops = fops_get(c->fops);
124  break;
125  }
126  }
127 
128  if (!new_fops) {
129  mutex_unlock(&misc_mtx);
130  request_module("char-major-%d-%d", MISC_MAJOR, minor);
131  mutex_lock(&misc_mtx);
132 
133  list_for_each_entry(c, &misc_list, list) {
134  if (c->minor == minor) {
135  new_fops = fops_get(c->fops);
136  break;
137  }
138  }
139  if (!new_fops)
140  goto fail;
141  }
142 
143  err = 0;
144  old_fops = file->f_op;
145  file->f_op = new_fops;
146  if (file->f_op->open) {
147  file->private_data = c;
148  err=file->f_op->open(inode,file);
149  if (err) {
150  fops_put(file->f_op);
151  file->f_op = fops_get(old_fops);
152  }
153  }
154  fops_put(old_fops);
155 fail:
156  mutex_unlock(&misc_mtx);
157  return err;
158 }
159 
160 static struct class *misc_class;
161 
162 static const struct file_operations misc_fops = {
163  .owner = THIS_MODULE,
164  .open = misc_open,
165  .llseek = noop_llseek,
166 };
167 
184 int misc_register(struct miscdevice * misc)
185 {
186  struct miscdevice *c;
187  dev_t dev;
188  int err = 0;
189 
190  INIT_LIST_HEAD(&misc->list);
191 
192  mutex_lock(&misc_mtx);
193  list_for_each_entry(c, &misc_list, list) {
194  if (c->minor == misc->minor) {
195  mutex_unlock(&misc_mtx);
196  return -EBUSY;
197  }
198  }
199 
200  if (misc->minor == MISC_DYNAMIC_MINOR) {
201  int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
202  if (i >= DYNAMIC_MINORS) {
203  mutex_unlock(&misc_mtx);
204  return -EBUSY;
205  }
206  misc->minor = DYNAMIC_MINORS - i - 1;
207  set_bit(i, misc_minors);
208  }
209 
210  dev = MKDEV(MISC_MAJOR, misc->minor);
211 
212  misc->this_device = device_create(misc_class, misc->parent, dev,
213  misc, "%s", misc->name);
214  if (IS_ERR(misc->this_device)) {
215  int i = DYNAMIC_MINORS - misc->minor - 1;
216  if (i < DYNAMIC_MINORS && i >= 0)
217  clear_bit(i, misc_minors);
218  err = PTR_ERR(misc->this_device);
219  goto out;
220  }
221 
222  /*
223  * Add it to the front, so that later devices can "override"
224  * earlier defaults
225  */
226  list_add(&misc->list, &misc_list);
227  out:
228  mutex_unlock(&misc_mtx);
229  return err;
230 }
231 
242 int misc_deregister(struct miscdevice *misc)
243 {
244  int i = DYNAMIC_MINORS - misc->minor - 1;
245 
246  if (WARN_ON(list_empty(&misc->list)))
247  return -EINVAL;
248 
249  mutex_lock(&misc_mtx);
250  list_del(&misc->list);
251  device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
252  if (i < DYNAMIC_MINORS && i >= 0)
253  clear_bit(i, misc_minors);
254  mutex_unlock(&misc_mtx);
255  return 0;
256 }
257 
260 
261 static char *misc_devnode(struct device *dev, umode_t *mode)
262 {
263  struct miscdevice *c = dev_get_drvdata(dev);
264 
265  if (mode && c->mode)
266  *mode = c->mode;
267  if (c->nodename)
268  return kstrdup(c->nodename, GFP_KERNEL);
269  return NULL;
270 }
271 
272 static int __init misc_init(void)
273 {
274  int err;
275 
276 #ifdef CONFIG_PROC_FS
277  proc_create("misc", 0, NULL, &misc_proc_fops);
278 #endif
279  misc_class = class_create(THIS_MODULE, "misc");
280  err = PTR_ERR(misc_class);
281  if (IS_ERR(misc_class))
282  goto fail_remove;
283 
284  err = -EIO;
285  if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
286  goto fail_printk;
287  misc_class->devnode = misc_devnode;
288  return 0;
289 
290 fail_printk:
291  printk("unable to get major %d for misc devices\n", MISC_MAJOR);
292  class_destroy(misc_class);
293 fail_remove:
294  remove_proc_entry("misc", NULL);
295  return err;
296 }
297 subsys_initcall(misc_init);