diff options
Diffstat (limited to 'fs/char_dev.c')
-rw-r--r-- | fs/char_dev.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/fs/char_dev.c b/fs/char_dev.c new file mode 100644 index 000000000000..7357a9127df1 --- /dev/null +++ b/fs/char_dev.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * linux/fs/char_dev.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/fs.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/string.h> | ||
12 | |||
13 | #include <linux/major.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #include <linux/devfs_fs_kernel.h> | ||
18 | |||
19 | #include <linux/kobject.h> | ||
20 | #include <linux/kobj_map.h> | ||
21 | #include <linux/cdev.h> | ||
22 | |||
23 | #ifdef CONFIG_KMOD | ||
24 | #include <linux/kmod.h> | ||
25 | #endif | ||
26 | |||
27 | static struct kobj_map *cdev_map; | ||
28 | |||
29 | /* degrade to linked list for small systems */ | ||
30 | #define MAX_PROBE_HASH (CONFIG_BASE_SMALL ? 1 : 255) | ||
31 | |||
32 | static DECLARE_MUTEX(chrdevs_lock); | ||
33 | |||
34 | static struct char_device_struct { | ||
35 | struct char_device_struct *next; | ||
36 | unsigned int major; | ||
37 | unsigned int baseminor; | ||
38 | int minorct; | ||
39 | const char *name; | ||
40 | struct file_operations *fops; | ||
41 | struct cdev *cdev; /* will die */ | ||
42 | } *chrdevs[MAX_PROBE_HASH]; | ||
43 | |||
44 | /* index in the above */ | ||
45 | static inline int major_to_index(int major) | ||
46 | { | ||
47 | return major % MAX_PROBE_HASH; | ||
48 | } | ||
49 | |||
50 | /* get char device names in somewhat random order */ | ||
51 | int get_chrdev_list(char *page) | ||
52 | { | ||
53 | struct char_device_struct *cd; | ||
54 | int i, len; | ||
55 | |||
56 | len = sprintf(page, "Character devices:\n"); | ||
57 | |||
58 | down(&chrdevs_lock); | ||
59 | for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { | ||
60 | for (cd = chrdevs[i]; cd; cd = cd->next) | ||
61 | len += sprintf(page+len, "%3d %s\n", | ||
62 | cd->major, cd->name); | ||
63 | } | ||
64 | up(&chrdevs_lock); | ||
65 | |||
66 | return len; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Register a single major with a specified minor range. | ||
71 | * | ||
72 | * If major == 0 this functions will dynamically allocate a major and return | ||
73 | * its number. | ||
74 | * | ||
75 | * If major > 0 this function will attempt to reserve the passed range of | ||
76 | * minors and will return zero on success. | ||
77 | * | ||
78 | * Returns a -ve errno on failure. | ||
79 | */ | ||
80 | static struct char_device_struct * | ||
81 | __register_chrdev_region(unsigned int major, unsigned int baseminor, | ||
82 | int minorct, const char *name) | ||
83 | { | ||
84 | struct char_device_struct *cd, **cp; | ||
85 | int ret = 0; | ||
86 | int i; | ||
87 | |||
88 | cd = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL); | ||
89 | if (cd == NULL) | ||
90 | return ERR_PTR(-ENOMEM); | ||
91 | |||
92 | memset(cd, 0, sizeof(struct char_device_struct)); | ||
93 | |||
94 | down(&chrdevs_lock); | ||
95 | |||
96 | /* temporary */ | ||
97 | if (major == 0) { | ||
98 | for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { | ||
99 | if (chrdevs[i] == NULL) | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | if (i == 0) { | ||
104 | ret = -EBUSY; | ||
105 | goto out; | ||
106 | } | ||
107 | major = i; | ||
108 | ret = major; | ||
109 | } | ||
110 | |||
111 | cd->major = major; | ||
112 | cd->baseminor = baseminor; | ||
113 | cd->minorct = minorct; | ||
114 | cd->name = name; | ||
115 | |||
116 | i = major_to_index(major); | ||
117 | |||
118 | for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) | ||
119 | if ((*cp)->major > major || | ||
120 | ((*cp)->major == major && (*cp)->baseminor >= baseminor)) | ||
121 | break; | ||
122 | if (*cp && (*cp)->major == major && | ||
123 | (*cp)->baseminor < baseminor + minorct) { | ||
124 | ret = -EBUSY; | ||
125 | goto out; | ||
126 | } | ||
127 | cd->next = *cp; | ||
128 | *cp = cd; | ||
129 | up(&chrdevs_lock); | ||
130 | return cd; | ||
131 | out: | ||
132 | up(&chrdevs_lock); | ||
133 | kfree(cd); | ||
134 | return ERR_PTR(ret); | ||
135 | } | ||
136 | |||
137 | static struct char_device_struct * | ||
138 | __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) | ||
139 | { | ||
140 | struct char_device_struct *cd = NULL, **cp; | ||
141 | int i = major_to_index(major); | ||
142 | |||
143 | up(&chrdevs_lock); | ||
144 | for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) | ||
145 | if ((*cp)->major == major && | ||
146 | (*cp)->baseminor == baseminor && | ||
147 | (*cp)->minorct == minorct) | ||
148 | break; | ||
149 | if (*cp) { | ||
150 | cd = *cp; | ||
151 | *cp = cd->next; | ||
152 | } | ||
153 | up(&chrdevs_lock); | ||
154 | return cd; | ||
155 | } | ||
156 | |||
157 | int register_chrdev_region(dev_t from, unsigned count, const char *name) | ||
158 | { | ||
159 | struct char_device_struct *cd; | ||
160 | dev_t to = from + count; | ||
161 | dev_t n, next; | ||
162 | |||
163 | for (n = from; n < to; n = next) { | ||
164 | next = MKDEV(MAJOR(n)+1, 0); | ||
165 | if (next > to) | ||
166 | next = to; | ||
167 | cd = __register_chrdev_region(MAJOR(n), MINOR(n), | ||
168 | next - n, name); | ||
169 | if (IS_ERR(cd)) | ||
170 | goto fail; | ||
171 | } | ||
172 | return 0; | ||
173 | fail: | ||
174 | to = n; | ||
175 | for (n = from; n < to; n = next) { | ||
176 | next = MKDEV(MAJOR(n)+1, 0); | ||
177 | kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); | ||
178 | } | ||
179 | return PTR_ERR(cd); | ||
180 | } | ||
181 | |||
182 | int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | ||
183 | const char *name) | ||
184 | { | ||
185 | struct char_device_struct *cd; | ||
186 | cd = __register_chrdev_region(0, baseminor, count, name); | ||
187 | if (IS_ERR(cd)) | ||
188 | return PTR_ERR(cd); | ||
189 | *dev = MKDEV(cd->major, cd->baseminor); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | int register_chrdev(unsigned int major, const char *name, | ||
194 | struct file_operations *fops) | ||
195 | { | ||
196 | struct char_device_struct *cd; | ||
197 | struct cdev *cdev; | ||
198 | char *s; | ||
199 | int err = -ENOMEM; | ||
200 | |||
201 | cd = __register_chrdev_region(major, 0, 256, name); | ||
202 | if (IS_ERR(cd)) | ||
203 | return PTR_ERR(cd); | ||
204 | |||
205 | cdev = cdev_alloc(); | ||
206 | if (!cdev) | ||
207 | goto out2; | ||
208 | |||
209 | cdev->owner = fops->owner; | ||
210 | cdev->ops = fops; | ||
211 | kobject_set_name(&cdev->kobj, "%s", name); | ||
212 | for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/')) | ||
213 | *s = '!'; | ||
214 | |||
215 | err = cdev_add(cdev, MKDEV(cd->major, 0), 256); | ||
216 | if (err) | ||
217 | goto out; | ||
218 | |||
219 | cd->cdev = cdev; | ||
220 | |||
221 | return major ? 0 : cd->major; | ||
222 | out: | ||
223 | kobject_put(&cdev->kobj); | ||
224 | out2: | ||
225 | kfree(__unregister_chrdev_region(cd->major, 0, 256)); | ||
226 | return err; | ||
227 | } | ||
228 | |||
229 | void unregister_chrdev_region(dev_t from, unsigned count) | ||
230 | { | ||
231 | dev_t to = from + count; | ||
232 | dev_t n, next; | ||
233 | |||
234 | for (n = from; n < to; n = next) { | ||
235 | next = MKDEV(MAJOR(n)+1, 0); | ||
236 | if (next > to) | ||
237 | next = to; | ||
238 | kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | int unregister_chrdev(unsigned int major, const char *name) | ||
243 | { | ||
244 | struct char_device_struct *cd; | ||
245 | cd = __unregister_chrdev_region(major, 0, 256); | ||
246 | if (cd && cd->cdev) | ||
247 | cdev_del(cd->cdev); | ||
248 | kfree(cd); | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static DEFINE_SPINLOCK(cdev_lock); | ||
253 | |||
254 | static struct kobject *cdev_get(struct cdev *p) | ||
255 | { | ||
256 | struct module *owner = p->owner; | ||
257 | struct kobject *kobj; | ||
258 | |||
259 | if (owner && !try_module_get(owner)) | ||
260 | return NULL; | ||
261 | kobj = kobject_get(&p->kobj); | ||
262 | if (!kobj) | ||
263 | module_put(owner); | ||
264 | return kobj; | ||
265 | } | ||
266 | |||
267 | void cdev_put(struct cdev *p) | ||
268 | { | ||
269 | if (p) { | ||
270 | kobject_put(&p->kobj); | ||
271 | module_put(p->owner); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * Called every time a character special file is opened | ||
277 | */ | ||
278 | int chrdev_open(struct inode * inode, struct file * filp) | ||
279 | { | ||
280 | struct cdev *p; | ||
281 | struct cdev *new = NULL; | ||
282 | int ret = 0; | ||
283 | |||
284 | spin_lock(&cdev_lock); | ||
285 | p = inode->i_cdev; | ||
286 | if (!p) { | ||
287 | struct kobject *kobj; | ||
288 | int idx; | ||
289 | spin_unlock(&cdev_lock); | ||
290 | kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); | ||
291 | if (!kobj) | ||
292 | return -ENXIO; | ||
293 | new = container_of(kobj, struct cdev, kobj); | ||
294 | spin_lock(&cdev_lock); | ||
295 | p = inode->i_cdev; | ||
296 | if (!p) { | ||
297 | inode->i_cdev = p = new; | ||
298 | inode->i_cindex = idx; | ||
299 | list_add(&inode->i_devices, &p->list); | ||
300 | new = NULL; | ||
301 | } else if (!cdev_get(p)) | ||
302 | ret = -ENXIO; | ||
303 | } else if (!cdev_get(p)) | ||
304 | ret = -ENXIO; | ||
305 | spin_unlock(&cdev_lock); | ||
306 | cdev_put(new); | ||
307 | if (ret) | ||
308 | return ret; | ||
309 | filp->f_op = fops_get(p->ops); | ||
310 | if (!filp->f_op) { | ||
311 | cdev_put(p); | ||
312 | return -ENXIO; | ||
313 | } | ||
314 | if (filp->f_op->open) { | ||
315 | lock_kernel(); | ||
316 | ret = filp->f_op->open(inode,filp); | ||
317 | unlock_kernel(); | ||
318 | } | ||
319 | if (ret) | ||
320 | cdev_put(p); | ||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | void cd_forget(struct inode *inode) | ||
325 | { | ||
326 | spin_lock(&cdev_lock); | ||
327 | list_del_init(&inode->i_devices); | ||
328 | inode->i_cdev = NULL; | ||
329 | spin_unlock(&cdev_lock); | ||
330 | } | ||
331 | |||
332 | void cdev_purge(struct cdev *cdev) | ||
333 | { | ||
334 | spin_lock(&cdev_lock); | ||
335 | while (!list_empty(&cdev->list)) { | ||
336 | struct inode *inode; | ||
337 | inode = container_of(cdev->list.next, struct inode, i_devices); | ||
338 | list_del_init(&inode->i_devices); | ||
339 | inode->i_cdev = NULL; | ||
340 | } | ||
341 | spin_unlock(&cdev_lock); | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * Dummy default file-operations: the only thing this does | ||
346 | * is contain the open that then fills in the correct operations | ||
347 | * depending on the special file... | ||
348 | */ | ||
349 | struct file_operations def_chr_fops = { | ||
350 | .open = chrdev_open, | ||
351 | }; | ||
352 | |||
353 | static struct kobject *exact_match(dev_t dev, int *part, void *data) | ||
354 | { | ||
355 | struct cdev *p = data; | ||
356 | return &p->kobj; | ||
357 | } | ||
358 | |||
359 | static int exact_lock(dev_t dev, void *data) | ||
360 | { | ||
361 | struct cdev *p = data; | ||
362 | return cdev_get(p) ? 0 : -1; | ||
363 | } | ||
364 | |||
365 | int cdev_add(struct cdev *p, dev_t dev, unsigned count) | ||
366 | { | ||
367 | p->dev = dev; | ||
368 | p->count = count; | ||
369 | return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); | ||
370 | } | ||
371 | |||
372 | static void cdev_unmap(dev_t dev, unsigned count) | ||
373 | { | ||
374 | kobj_unmap(cdev_map, dev, count); | ||
375 | } | ||
376 | |||
377 | void cdev_del(struct cdev *p) | ||
378 | { | ||
379 | cdev_unmap(p->dev, p->count); | ||
380 | kobject_put(&p->kobj); | ||
381 | } | ||
382 | |||
383 | |||
384 | static void cdev_default_release(struct kobject *kobj) | ||
385 | { | ||
386 | struct cdev *p = container_of(kobj, struct cdev, kobj); | ||
387 | cdev_purge(p); | ||
388 | } | ||
389 | |||
390 | static void cdev_dynamic_release(struct kobject *kobj) | ||
391 | { | ||
392 | struct cdev *p = container_of(kobj, struct cdev, kobj); | ||
393 | cdev_purge(p); | ||
394 | kfree(p); | ||
395 | } | ||
396 | |||
397 | static struct kobj_type ktype_cdev_default = { | ||
398 | .release = cdev_default_release, | ||
399 | }; | ||
400 | |||
401 | static struct kobj_type ktype_cdev_dynamic = { | ||
402 | .release = cdev_dynamic_release, | ||
403 | }; | ||
404 | |||
405 | struct cdev *cdev_alloc(void) | ||
406 | { | ||
407 | struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL); | ||
408 | if (p) { | ||
409 | memset(p, 0, sizeof(struct cdev)); | ||
410 | p->kobj.ktype = &ktype_cdev_dynamic; | ||
411 | INIT_LIST_HEAD(&p->list); | ||
412 | kobject_init(&p->kobj); | ||
413 | } | ||
414 | return p; | ||
415 | } | ||
416 | |||
417 | void cdev_init(struct cdev *cdev, struct file_operations *fops) | ||
418 | { | ||
419 | memset(cdev, 0, sizeof *cdev); | ||
420 | INIT_LIST_HEAD(&cdev->list); | ||
421 | cdev->kobj.ktype = &ktype_cdev_default; | ||
422 | kobject_init(&cdev->kobj); | ||
423 | cdev->ops = fops; | ||
424 | } | ||
425 | |||
426 | static struct kobject *base_probe(dev_t dev, int *part, void *data) | ||
427 | { | ||
428 | if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0) | ||
429 | /* Make old-style 2.4 aliases work */ | ||
430 | request_module("char-major-%d", MAJOR(dev)); | ||
431 | return NULL; | ||
432 | } | ||
433 | |||
434 | void __init chrdev_init(void) | ||
435 | { | ||
436 | cdev_map = kobj_map_init(base_probe, &chrdevs_lock); | ||
437 | } | ||
438 | |||
439 | |||
440 | /* Let modules do char dev stuff */ | ||
441 | EXPORT_SYMBOL(register_chrdev_region); | ||
442 | EXPORT_SYMBOL(unregister_chrdev_region); | ||
443 | EXPORT_SYMBOL(alloc_chrdev_region); | ||
444 | EXPORT_SYMBOL(cdev_init); | ||
445 | EXPORT_SYMBOL(cdev_alloc); | ||
446 | EXPORT_SYMBOL(cdev_del); | ||
447 | EXPORT_SYMBOL(cdev_add); | ||
448 | EXPORT_SYMBOL(register_chrdev); | ||
449 | EXPORT_SYMBOL(unregister_chrdev); | ||