aboutsummaryrefslogtreecommitdiffstats
path: root/fs/char_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/char_dev.c')
-rw-r--r--fs/char_dev.c449
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
27static 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
32static DECLARE_MUTEX(chrdevs_lock);
33
34static 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 */
45static 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 */
51int 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 */
80static 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;
131out:
132 up(&chrdevs_lock);
133 kfree(cd);
134 return ERR_PTR(ret);
135}
136
137static 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
157int 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;
173fail:
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
182int 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
193int 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;
222out:
223 kobject_put(&cdev->kobj);
224out2:
225 kfree(__unregister_chrdev_region(cd->major, 0, 256));
226 return err;
227}
228
229void 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
242int 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
252static DEFINE_SPINLOCK(cdev_lock);
253
254static 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
267void 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 */
278int 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
324void 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
332void 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 */
349struct file_operations def_chr_fops = {
350 .open = chrdev_open,
351};
352
353static struct kobject *exact_match(dev_t dev, int *part, void *data)
354{
355 struct cdev *p = data;
356 return &p->kobj;
357}
358
359static int exact_lock(dev_t dev, void *data)
360{
361 struct cdev *p = data;
362 return cdev_get(p) ? 0 : -1;
363}
364
365int 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
372static void cdev_unmap(dev_t dev, unsigned count)
373{
374 kobj_unmap(cdev_map, dev, count);
375}
376
377void cdev_del(struct cdev *p)
378{
379 cdev_unmap(p->dev, p->count);
380 kobject_put(&p->kobj);
381}
382
383
384static void cdev_default_release(struct kobject *kobj)
385{
386 struct cdev *p = container_of(kobj, struct cdev, kobj);
387 cdev_purge(p);
388}
389
390static 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
397static struct kobj_type ktype_cdev_default = {
398 .release = cdev_default_release,
399};
400
401static struct kobj_type ktype_cdev_dynamic = {
402 .release = cdev_dynamic_release,
403};
404
405struct 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
417void 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
426static 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
434void __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 */
441EXPORT_SYMBOL(register_chrdev_region);
442EXPORT_SYMBOL(unregister_chrdev_region);
443EXPORT_SYMBOL(alloc_chrdev_region);
444EXPORT_SYMBOL(cdev_init);
445EXPORT_SYMBOL(cdev_alloc);
446EXPORT_SYMBOL(cdev_del);
447EXPORT_SYMBOL(cdev_add);
448EXPORT_SYMBOL(register_chrdev);
449EXPORT_SYMBOL(unregister_chrdev);