X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fchar_dev.c;h=3483d3cf80873432d7696f6f3f68b4adef928b01;hb=6ab77c740182a84d72cc2f942bc24be599a3275a;hp=a40ae83adc92c256980c906c19e060d0c528fda8;hpb=5167311cae6aa3a5ff5afd39f88c32a435c969ef;p=linux-2.6.git diff --git a/fs/char_dev.c b/fs/char_dev.c index a40ae83ad..3483d3cf8 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include @@ -14,11 +13,12 @@ #include #include #include -#include +#include #include #include #include +#include #ifdef CONFIG_KMOD #include @@ -26,45 +26,40 @@ static struct kobj_map *cdev_map; -#define MAX_PROBE_HASH 255 /* random */ - -static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED; +static DEFINE_MUTEX(chrdevs_lock); static struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; - const char *name; + char name[64]; struct file_operations *fops; struct cdev *cdev; /* will die */ -} *chrdevs[MAX_PROBE_HASH]; +} *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; /* index in the above */ static inline int major_to_index(int major) { - return major % MAX_PROBE_HASH; + return major % CHRDEV_MAJOR_HASH_SIZE; } -/* get char device names in somewhat random order */ -int get_chrdev_list(char *page) +#ifdef CONFIG_PROC_FS + +void chrdev_show(struct seq_file *f, off_t offset) { struct char_device_struct *cd; - int i, len; - - len = sprintf(page, "Character devices:\n"); - read_lock(&chrdevs_lock); - for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { - for (cd = chrdevs[i]; cd; cd = cd->next) - len += sprintf(page+len, "%3d %s\n", - cd->major, cd->name); + if (offset < CHRDEV_MAJOR_HASH_SIZE) { + mutex_lock(&chrdevs_lock); + for (cd = chrdevs[offset]; cd; cd = cd->next) + seq_printf(f, "%3d %s\n", cd->major, cd->name); + mutex_unlock(&chrdevs_lock); } - read_unlock(&chrdevs_lock); - - return len; } +#endif /* CONFIG_PROC_FS */ + /* * Register a single major with a specified minor range. * @@ -84,13 +79,11 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, int ret = 0; int i; - cd = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL); + cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); - memset(cd, 0, sizeof(struct char_device_struct)); - - write_lock_irq(&chrdevs_lock); + mutex_lock(&chrdevs_lock); /* temporary */ if (major == 0) { @@ -110,7 +103,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; - cd->name = name; + strncpy(cd->name,name, 64); i = major_to_index(major); @@ -125,10 +118,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, } cd->next = *cp; *cp = cd; - write_unlock_irq(&chrdevs_lock); + mutex_unlock(&chrdevs_lock); return cd; out: - write_unlock_irq(&chrdevs_lock); + mutex_unlock(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); } @@ -139,7 +132,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) struct char_device_struct *cd = NULL, **cp; int i = major_to_index(major); - write_lock_irq(&chrdevs_lock); + mutex_lock(&chrdevs_lock); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major == major && (*cp)->baseminor == baseminor && @@ -149,7 +142,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) cd = *cp; *cp = cd->next; } - write_unlock_irq(&chrdevs_lock); + mutex_unlock(&chrdevs_lock); return cd; } @@ -189,8 +182,30 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, return 0; } +/** + * register_chrdev() - Register a major number for character devices. + * @major: major device number or 0 for dynamic allocation + * @name: name of this range of devices + * @fops: file operations associated with this devices + * + * If @major == 0 this functions will dynamically allocate a major and return + * its number. + * + * If @major > 0 this function will attempt to reserve a device with the given + * major number and will return zero on success. + * + * Returns a -ve errno on failure. + * + * The name of this device has nothing to do with the name of the device in + * /dev. It only helps to keep track of the different owners of devices. If + * your module name has only one type of devices it's ok to use e.g. the name + * of the module here. + * + * This function registers a range of 256 minor numbers. The first minor number + * is 0. + */ int register_chrdev(unsigned int major, const char *name, - struct file_operations *fops) + const struct file_operations *fops) { struct char_device_struct *cd; struct cdev *cdev; @@ -248,7 +263,7 @@ int unregister_chrdev(unsigned int major, const char *name) return 0; } -static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(cdev_lock); static struct kobject *cdev_get(struct cdev *p) { @@ -266,8 +281,9 @@ static struct kobject *cdev_get(struct cdev *p) void cdev_put(struct cdev *p) { if (p) { + struct module *owner = p->owner; kobject_put(&p->kobj); - module_put(p->owner); + module_put(owner); } } @@ -328,7 +344,7 @@ void cd_forget(struct inode *inode) spin_unlock(&cdev_lock); } -void cdev_purge(struct cdev *cdev) +static void cdev_purge(struct cdev *cdev) { spin_lock(&cdev_lock); while (!list_empty(&cdev->list)) { @@ -345,7 +361,7 @@ void cdev_purge(struct cdev *cdev) * is contain the open that then fills in the correct operations * depending on the special file... */ -struct file_operations def_chr_fops = { +const struct file_operations def_chr_fops = { .open = chrdev_open, }; @@ -380,8 +396,6 @@ void cdev_del(struct cdev *p) } -static decl_subsys(cdev, NULL, NULL); - static void cdev_default_release(struct kobject *kobj) { struct cdev *p = container_of(kobj, struct cdev, kobj); @@ -405,9 +419,8 @@ static struct kobj_type ktype_cdev_dynamic = { struct cdev *cdev_alloc(void) { - struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL); + struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); if (p) { - memset(p, 0, sizeof(struct cdev)); p->kobj.ktype = &ktype_cdev_dynamic; INIT_LIST_HEAD(&p->list); kobject_init(&p->kobj); @@ -415,7 +428,7 @@ struct cdev *cdev_alloc(void) return p; } -void cdev_init(struct cdev *cdev, struct file_operations *fops) +void cdev_init(struct cdev *cdev, const struct file_operations *fops) { memset(cdev, 0, sizeof *cdev); INIT_LIST_HEAD(&cdev->list); @@ -434,13 +447,7 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data) void __init chrdev_init(void) { -/* - * Keep cdev_subsys around because (and only because) the kobj_map code - * depends on the rwsem it contains. We don't make it public in sysfs, - * however. - */ - subsystem_init(&cdev_subsys); - cdev_map = kobj_map_init(base_probe, &cdev_subsys); + cdev_map = kobj_map_init(base_probe, &chrdevs_lock); }