Blame view
drivers/char/misc.c
6.87 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/* * linux/drivers/char/misc.c * * Generic misc open routine by Johan Myreen * * Based on code from Linus * * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's * changes incorporated into 0.97pl4 * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92) * See busmouse.c for particulars. * * Made things a lot mode modular - easy to compile in just one or two * of the misc drivers, as they are now completely independent. Linus. * * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> * * Fixed a failing symbol register to free the device registration * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96 * * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 * * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 * * Handling of mouse minor numbers for kerneld: * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, * adapted by Bjorn Ekwall <bj0rn@blox.se> * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> * * Changes for kmod (from kerneld): * Cyrus Durgin <cider@speakeasy.org> * * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998 */ #include <linux/module.h> |
1da177e4c Linux-2.6.12-rc2 |
37 38 39 40 41 42 |
#include <linux/fs.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> |
0e82d5b61 use mutex instead... |
43 |
#include <linux/mutex.h> |
1da177e4c Linux-2.6.12-rc2 |
44 45 |
#include <linux/proc_fs.h> #include <linux/seq_file.h> |
1da177e4c Linux-2.6.12-rc2 |
46 47 48 49 50 |
#include <linux/stat.h> #include <linux/init.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> |
5a0e3ad6a include cleanup: ... |
51 |
#include <linux/gfp.h> |
1da177e4c Linux-2.6.12-rc2 |
52 53 54 55 56 |
/* * Head entry for the doubly linked miscdevice list */ static LIST_HEAD(misc_list); |
0e82d5b61 use mutex instead... |
57 |
static DEFINE_MUTEX(misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
58 59 60 61 62 |
/* * Assigned numbers, used for dynamic minors */ #define DYNAMIC_MINORS 64 /* like dynamic majors */ |
1f2f38d89 drivers/char/misc... |
63 |
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); |
1da177e4c Linux-2.6.12-rc2 |
64 |
|
1da177e4c Linux-2.6.12-rc2 |
65 66 67 |
#ifdef CONFIG_PROC_FS static void *misc_seq_start(struct seq_file *seq, loff_t *pos) { |
0e82d5b61 use mutex instead... |
68 |
mutex_lock(&misc_mtx); |
46c65b71e Make /proc/misc u... |
69 |
return seq_list_start(&misc_list, *pos); |
1da177e4c Linux-2.6.12-rc2 |
70 71 72 73 |
} static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) { |
46c65b71e Make /proc/misc u... |
74 |
return seq_list_next(v, &misc_list, pos); |
1da177e4c Linux-2.6.12-rc2 |
75 76 77 78 |
} static void misc_seq_stop(struct seq_file *seq, void *v) { |
0e82d5b61 use mutex instead... |
79 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
80 81 82 83 |
} static int misc_seq_show(struct seq_file *seq, void *v) { |
46c65b71e Make /proc/misc u... |
84 |
const struct miscdevice *p = list_entry(v, struct miscdevice, list); |
1da177e4c Linux-2.6.12-rc2 |
85 86 87 88 89 |
seq_printf(seq, "%3i %s ", p->minor, p->name ? p->name : ""); return 0; } |
88e9d34c7 seq_file: constif... |
90 |
static const struct seq_operations misc_seq_ops = { |
1da177e4c Linux-2.6.12-rc2 |
91 92 93 94 95 96 97 98 99 100 |
.start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, .show = misc_seq_show, }; static int misc_seq_open(struct inode *inode, struct file *file) { return seq_open(file, &misc_seq_ops); } |
62322d255 [PATCH] make more... |
101 |
static const struct file_operations misc_proc_fops = { |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 106 107 108 109 110 111 112 113 114 |
.owner = THIS_MODULE, .open = misc_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; #endif static int misc_open(struct inode * inode, struct file * file) { int minor = iminor(inode); struct miscdevice *c; int err = -ENODEV; |
99ac48f54 [PATCH] mark f_op... |
115 |
const struct file_operations *old_fops, *new_fops = NULL; |
40b798efe drivers: Remove B... |
116 |
|
0e82d5b61 use mutex instead... |
117 |
mutex_lock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
118 119 120 121 122 123 124 125 126 |
list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) { |
0e82d5b61 use mutex instead... |
127 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
128 |
request_module("char-major-%d-%d", MISC_MAJOR, minor); |
0e82d5b61 use mutex instead... |
129 |
mutex_lock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) goto fail; } err = 0; old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { |
fa1f68db6 drivers: misc: pa... |
145 |
file->private_data = c; |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 150 151 152 153 |
err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } } fops_put(old_fops); fail: |
0e82d5b61 use mutex instead... |
154 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
155 156 |
return err; } |
ca8eca688 [PATCH] class: co... |
157 |
static struct class *misc_class; |
1da177e4c Linux-2.6.12-rc2 |
158 |
|
62322d255 [PATCH] make more... |
159 |
static const struct file_operations misc_fops = { |
1da177e4c Linux-2.6.12-rc2 |
160 161 |
.owner = THIS_MODULE, .open = misc_open, |
6038f373a llseek: automatic... |
162 |
.llseek = noop_llseek, |
1da177e4c Linux-2.6.12-rc2 |
163 |
}; |
1da177e4c Linux-2.6.12-rc2 |
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
/** * misc_register - register a miscellaneous device * @misc: device structure * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. */ int misc_register(struct miscdevice * misc) { struct miscdevice *c; dev_t dev; |
7c69ef797 [PATCH] devfs: Re... |
184 |
int err = 0; |
1da177e4c Linux-2.6.12-rc2 |
185 |
|
5d469ec0f [PATCH] Correct m... |
186 |
INIT_LIST_HEAD(&misc->list); |
0e82d5b61 use mutex instead... |
187 |
mutex_lock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
188 189 |
list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { |
0e82d5b61 use mutex instead... |
190 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
191 192 193 194 195 |
return -EBUSY; } } if (misc->minor == MISC_DYNAMIC_MINOR) { |
1f2f38d89 drivers/char/misc... |
196 197 |
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { |
0e82d5b61 use mutex instead... |
198 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
199 200 |
return -EBUSY; } |
1f2f38d89 drivers/char/misc... |
201 202 |
misc->minor = DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); |
1da177e4c Linux-2.6.12-rc2 |
203 |
} |
1da177e4c Linux-2.6.12-rc2 |
204 |
dev = MKDEV(MISC_MAJOR, misc->minor); |
d40564053 Driver Core: misc... |
205 206 |
misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); |
94fbcded4 Driver core: chan... |
207 |
if (IS_ERR(misc->this_device)) { |
1f2f38d89 drivers/char/misc... |
208 |
int i = DYNAMIC_MINORS - misc->minor - 1; |
4ae717da8 drivers/char/misc... |
209 |
if (i < DYNAMIC_MINORS && i >= 0) |
1f2f38d89 drivers/char/misc... |
210 |
clear_bit(i, misc_minors); |
94fbcded4 Driver core: chan... |
211 |
err = PTR_ERR(misc->this_device); |
1da177e4c Linux-2.6.12-rc2 |
212 213 |
goto out; } |
1da177e4c Linux-2.6.12-rc2 |
214 215 216 217 218 219 |
/* * Add it to the front, so that later devices can "override" * earlier defaults */ list_add(&misc->list, &misc_list); out: |
0e82d5b61 use mutex instead... |
220 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
221 222 223 224 |
return err; } /** |
b844eba29 PM: Remove destro... |
225 |
* misc_deregister - unregister a miscellaneous device |
1da177e4c Linux-2.6.12-rc2 |
226 227 228 229 230 231 232 |
* @misc: device to unregister * * Unregister a miscellaneous device that was previously * successfully registered with misc_register(). Success * is indicated by a zero return, a negative errno code * indicates an error. */ |
b844eba29 PM: Remove destro... |
233 |
int misc_deregister(struct miscdevice *misc) |
1da177e4c Linux-2.6.12-rc2 |
234 |
{ |
1f2f38d89 drivers/char/misc... |
235 |
int i = DYNAMIC_MINORS - misc->minor - 1; |
1da177e4c Linux-2.6.12-rc2 |
236 |
|
b329becfc char: add WARN_ON... |
237 |
if (WARN_ON(list_empty(&misc->list))) |
1da177e4c Linux-2.6.12-rc2 |
238 |
return -EINVAL; |
0e82d5b61 use mutex instead... |
239 |
mutex_lock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
240 |
list_del(&misc->list); |
b844eba29 PM: Remove destro... |
241 |
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); |
4ae717da8 drivers/char/misc... |
242 |
if (i < DYNAMIC_MINORS && i >= 0) |
1f2f38d89 drivers/char/misc... |
243 |
clear_bit(i, misc_minors); |
0e82d5b61 use mutex instead... |
244 |
mutex_unlock(&misc_mtx); |
1da177e4c Linux-2.6.12-rc2 |
245 246 247 248 |
return 0; } EXPORT_SYMBOL(misc_register); |
b844eba29 PM: Remove destro... |
249 |
EXPORT_SYMBOL(misc_deregister); |
1da177e4c Linux-2.6.12-rc2 |
250 |
|
2c9ede55e switch device_get... |
251 |
static char *misc_devnode(struct device *dev, umode_t *mode) |
d40564053 Driver Core: misc... |
252 253 |
{ struct miscdevice *c = dev_get_drvdata(dev); |
e454cea20 Driver-Core: exte... |
254 255 256 257 |
if (mode && c->mode) *mode = c->mode; if (c->nodename) return kstrdup(c->nodename, GFP_KERNEL); |
d40564053 Driver Core: misc... |
258 259 |
return NULL; } |
1da177e4c Linux-2.6.12-rc2 |
260 261 |
static int __init misc_init(void) { |
1b5022173 drivers: use non-... |
262 |
int err; |
1da177e4c Linux-2.6.12-rc2 |
263 |
|
1b5022173 drivers: use non-... |
264 265 |
#ifdef CONFIG_PROC_FS proc_create("misc", 0, NULL, &misc_proc_fops); |
1da177e4c Linux-2.6.12-rc2 |
266 |
#endif |
ca8eca688 [PATCH] class: co... |
267 |
misc_class = class_create(THIS_MODULE, "misc"); |
1b5022173 drivers: use non-... |
268 |
err = PTR_ERR(misc_class); |
1da177e4c Linux-2.6.12-rc2 |
269 |
if (IS_ERR(misc_class)) |
1b5022173 drivers: use non-... |
270 |
goto fail_remove; |
573fc1131 [PATCH] move m68k... |
271 |
|
1b5022173 drivers: use non-... |
272 273 274 |
err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) goto fail_printk; |
e454cea20 Driver-Core: exte... |
275 |
misc_class->devnode = misc_devnode; |
1da177e4c Linux-2.6.12-rc2 |
276 |
return 0; |
1b5022173 drivers: use non-... |
277 278 279 280 281 282 283 284 |
fail_printk: printk("unable to get major %d for misc devices ", MISC_MAJOR); class_destroy(misc_class); fail_remove: remove_proc_entry("misc", NULL); return err; |
1da177e4c Linux-2.6.12-rc2 |
285 286 |
} subsys_initcall(misc_init); |