#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
+#include <linux/seq_file.h>
#include <linux/kobject.h>
#include <linux/kobj_map.h>
#include <linux/cdev.h>
+#include <linux/mutex.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
static struct kobj_map *cdev_map;
-#define MAX_PROBE_HASH 255 /* random */
-
-static DEFINE_RWLOCK(chrdevs_lock);
+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.
*
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) {
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
- cd->name = name;
+ strncpy(cd->name,name, 64);
i = major_to_index(major);
}
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);
}
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 &&
cd = *cp;
*cp = cd->next;
}
- write_unlock_irq(&chrdevs_lock);
+ mutex_unlock(&chrdevs_lock);
return cd;
}
}
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;
void cdev_put(struct cdev *p)
{
if (p) {
+ struct module *owner = p->owner;
kobject_put(&p->kobj);
- module_put(p->owner);
+ module_put(owner);
}
}
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)) {
* 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,
};
}
-static decl_subsys(cdev, NULL, NULL);
-
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
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);
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);
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);
}