 drivers/char/tty_io.c      |   58 ++++++++++--
 fs/char_dev.c              |  208 ++++++++++++++++++++++++++++++++-------------
 include/linux/cdev.h       |   30 ++++++
 include/linux/fs.h         |    6 -
 include/linux/tty_driver.h |    2 
 5 files changed, 233 insertions(+), 71 deletions(-)

diff -puN drivers/char/tty_io.c~T30-cdev-C69 drivers/char/tty_io.c
--- 25/drivers/char/tty_io.c~T30-cdev-C69	2003-05-05 22:38:55.000000000 -0700
+++ 25-akpm/drivers/char/tty_io.c	2003-05-05 22:41:35.000000000 -0700
@@ -2130,6 +2130,7 @@ void tty_unregister_device(struct tty_dr
 EXPORT_SYMBOL(tty_register_device);
 EXPORT_SYMBOL(tty_unregister_device);
 
+static struct kobject tty_kobj = {.name = "tty"};
 /*
  * Called by a tty driver to register itself.
  */
@@ -2138,13 +2139,14 @@ int tty_register_driver(struct tty_drive
 	int error;
         int i;
 	dev_t dev;
+	char *s;
 
 	if (driver->flags & TTY_DRIVER_INSTALLED)
 		return 0;
 
 	if (!driver->major) {
 		error = alloc_chrdev_region(&dev, driver->num,
-						(char*)driver->name, &tty_fops);
+						(char*)driver->name);
 		if (!error) {
 			driver->major = MAJOR(dev);
 			driver->minor_start = MINOR(dev);
@@ -2152,11 +2154,24 @@ int tty_register_driver(struct tty_drive
 	} else {
 		dev = MKDEV(driver->major, driver->minor_start);
 		error = register_chrdev_region(dev, driver->num,
-						(char*)driver->name, &tty_fops);
+						(char*)driver->name);
 	}
 	if (error < 0)
 		return error;
 
+	driver->cdev.kobj.parent = &tty_kobj;
+	strcpy(driver->cdev.kobj.name, driver->name);
+	for (s = strchr(driver->cdev.kobj.name, '/'); s; s = strchr(s, '/'))
+		*s = '!';
+	cdev_init(&driver->cdev, &tty_fops);
+	driver->cdev.owner = driver->owner;
+	error = cdev_add(&driver->cdev, dev, driver->num);
+	if (error) {
+		cdev_put(&driver->cdev);
+		unregister_chrdev_region(dev, driver->num);
+		return error;
+	}
+
 	if (!driver->put_char)
 		driver->put_char = tty_default_put_char;
 	
@@ -2167,7 +2182,7 @@ int tty_register_driver(struct tty_drive
 		    tty_register_device(driver, i);
 	}
 	proc_tty_register_driver(driver);
-	return error;
+	return 0;
 }
 
 /*
@@ -2181,6 +2196,7 @@ int tty_unregister_driver(struct tty_dri
 	if (*driver->refcount)
 		return -EBUSY;
 
+	cdev_unmap(MKDEV(driver->major, driver->minor_start), driver->num);
 	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
 				driver->num);
 
@@ -2206,6 +2222,7 @@ int tty_unregister_driver(struct tty_dri
 			tty_unregister_device(driver, i);
 	}
 	proc_tty_unregister_driver(driver);
+	cdev_del(&driver->cdev);
 	return 0;
 }
 
@@ -2257,32 +2274,51 @@ static int __init tty_class_init(void)
 
 postcore_initcall(tty_class_init);
 
+static struct cdev tty_cdev, console_cdev;
+#ifdef CONFIG_UNIX98_PTYS
+static struct cdev ptmx_cdev;
+#endif
+#ifdef CONFIG_VT
+static struct cdev vc0_cdev;
+#endif
+
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
  * on memory allocations, interrupts etc..
  */
 void __init tty_init(void)
 {
-	if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1,
-				   "/dev/tty", &tty_fops) < 0)
+	strcpy(tty_cdev.kobj.name, "dev.tty");
+	cdev_init(&tty_cdev, &tty_fops);
+	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
 		panic("Couldn't register /dev/tty driver\n");
 	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 0), S_IFCHR|S_IRUGO|S_IWUGO, "tty");
 
-	if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1,
-				   "/dev/console", &tty_fops) < 0)
+	strcpy(console_cdev.kobj.name, "dev.console");
+	cdev_init(&console_cdev, &tty_fops);
+	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
 		panic("Couldn't register /dev/console driver\n");
 	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 1), S_IFCHR|S_IRUSR|S_IWUSR, "console");
 
+	tty_kobj.kset = tty_cdev.kobj.kset;
+	kobject_register(&tty_kobj);
+
 #ifdef CONFIG_UNIX98_PTYS
-	if (register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1,
-				   "/dev/ptmx", &tty_fops) < 0)
+	strcpy(ptmx_cdev.kobj.name, "dev.ptmx");
+	cdev_init(&ptmx_cdev, &tty_fops);
+	if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
 		panic("Couldn't register /dev/ptmx driver\n");
 	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
 #endif
 	
 #ifdef CONFIG_VT
-	if (register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1,
-				   "/dev/vc/0", &tty_fops) < 0)
+	strcpy(vc0_cdev.kobj.name, "dev.vc0");
+	cdev_init(&vc0_cdev, &tty_fops);
+	if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
+	    register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
 		panic("Couldn't register /dev/tty0 driver\n");
 	devfs_mk_cdev(MKDEV(TTY_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vc/0");
 	vty_init();
diff -puN fs/char_dev.c~T30-cdev-C69 fs/char_dev.c
--- 25/fs/char_dev.c~T30-cdev-C69	2003-05-05 22:38:55.000000000 -0700
+++ 25-akpm/fs/char_dev.c	2003-05-05 22:38:55.000000000 -0700
@@ -17,10 +17,16 @@
 #include <linux/smp_lock.h>
 #include <linux/devfs_fs_kernel.h>
 
+#include <linux/kobject.h>
+#include <linux/kobj_map.h>
+#include <linux/cdev.h>
+
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
 
+static struct kobj_map *cdev_map;
+
 #define MAX_PROBE_HASH 255	/* random */
 
 static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
@@ -32,6 +38,7 @@ static struct char_device_struct {
 	int minorct;
 	const char *name;
 	struct file_operations *fops;
+	struct cdev *cdev;		/* will die */
 } *chrdevs[MAX_PROBE_HASH];
 
 /* index in the above */
@@ -61,56 +68,22 @@ int get_chrdev_list(char *page)
 
 /*
  * Return the function table of a device, if present.
- * Increment the reference count of module in question.
- */
-static struct file_operations *
-lookup_chrfops(unsigned int major, unsigned int minor)
-{
-	struct char_device_struct *cd;
-	struct file_operations *ret = NULL;
-	int i;
-
-	i = major_to_index(major);
-
-	read_lock(&chrdevs_lock);
-	for (cd = chrdevs[i]; cd; cd = cd->next) {
-		if (major == cd->major &&
-		    minor - cd->baseminor < cd->minorct) {
-			ret = fops_get(cd->fops);
-			break;
-		}
-	}
-	read_unlock(&chrdevs_lock);
-
-	return ret;
-}
-
-/*
- * Return the function table of a device, if present.
  * Load the driver if needed.
  * Increment the reference count of module in question.
  */
-static struct file_operations *
-get_chrfops(unsigned int major, unsigned int minor)
+static struct file_operations *get_chrfops(dev_t dev)
 {
 	struct file_operations *ret = NULL;
+	int index;
+	struct kobject *kobj = kobj_lookup(cdev_map, dev, &index);
 
-	if (!major)
-		return NULL;
-
-	ret = lookup_chrfops(major, minor);
-
-#ifdef CONFIG_KMOD
-	if (!ret) {
-		char name[32];
-		sprintf(name, "char-major-%d", major);
-		request_module(name);
-
-		read_lock(&chrdevs_lock);
-		ret = lookup_chrfops(major, minor);
-		read_unlock(&chrdevs_lock);
+	if (kobj) {
+		struct cdev *p = container_of(kobj, struct cdev, kobj);
+		struct module *owner = p->owner;
+		ret = fops_get(p->ops);
+		cdev_put(p);
+		module_put(owner);
 	}
-#endif
 	return ret;
 }
 
@@ -127,8 +100,7 @@ get_chrfops(unsigned int major, unsigned
  */
 static struct char_device_struct *
 __register_chrdev_region(unsigned int major, unsigned int baseminor,
-			   int minorct, const char *name,
-			   struct file_operations *fops)
+			   int minorct, const char *name)
 {
 	struct char_device_struct *cd, **cp;
 	int ret = 0;
@@ -159,7 +131,6 @@ __register_chrdev_region(unsigned int ma
 	cd->baseminor = baseminor;
 	cd->minorct = minorct;
 	cd->name = name;
-	cd->fops = fops;
 
 	i = major_to_index(major);
 
@@ -202,8 +173,7 @@ __unregister_chrdev_region(unsigned majo
 	return cd;
 }
 
-int register_chrdev_region(dev_t from, unsigned count, char *name,
-			   struct file_operations *fops)
+int register_chrdev_region(dev_t from, unsigned count, char *name)
 {
 	struct char_device_struct *cd;
 	dev_t to = from + count;
@@ -214,7 +184,7 @@ int register_chrdev_region(dev_t from, u
 		if (next > to)
 			next = to;
 		cd = __register_chrdev_region(MAJOR(n), MINOR(n),
-			       next - n, name, fops);
+			       next - n, name);
 		if (IS_ERR(cd))
 			goto fail;
 	}
@@ -228,11 +198,10 @@ fail:
 	return PTR_ERR(cd);
 }
 
-int alloc_chrdev_region(dev_t *dev, unsigned count, char *name,
-			   struct file_operations *fops)
+int alloc_chrdev_region(dev_t *dev, unsigned count, char *name)
 {
 	struct char_device_struct *cd;
-	cd = __register_chrdev_region(0, 0, count, name, fops);
+	cd = __register_chrdev_region(0, 0, count, name);
 	if (IS_ERR(cd))
 		return PTR_ERR(cd);
 	*dev = MKDEV(cd->major, cd->baseminor);
@@ -243,11 +212,36 @@ int register_chrdev(unsigned int major, 
 		    struct file_operations *fops)
 {
 	struct char_device_struct *cd;
+	struct cdev *cdev;
+	char *s;
+	int err = -ENOMEM;
 
-	cd = __register_chrdev_region(major, 0, 256, name, fops);
+	cd = __register_chrdev_region(major, 0, 256, name);
 	if (IS_ERR(cd))
 		return PTR_ERR(cd);
-	return cd->major;
+	
+	cdev = cdev_alloc();
+	if (!cdev)
+		goto out2;
+
+	cdev->owner = fops->owner;
+	cdev->ops = fops;
+	strcpy(cdev->kobj.name, name);
+	for (s = strchr(cdev->kobj.name, '/'); s; s = strchr(s, '/'))
+		*s = '!';
+		
+	err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
+	if (err)
+		goto out;
+
+	cd->cdev = cdev;
+
+	return major ? 0 : cd->major;
+out:
+	cdev_put(cdev);
+out2:
+	__unregister_chrdev_region(cd->major, 0, 256);
+	return err;
 }
 
 void unregister_chrdev_region(dev_t from, unsigned count)
@@ -265,7 +259,12 @@ void unregister_chrdev_region(dev_t from
 
 int unregister_chrdev(unsigned int major, const char *name)
 {
-	kfree(__unregister_chrdev_region(major, 0, 256));
+	struct char_device_struct *cd;
+	cdev_unmap(MKDEV(major, 0), 256);
+	cd = __unregister_chrdev_region(major, 0, 256);
+	if (cd && cd->cdev)
+		cdev_del(cd->cdev);
+	kfree(cd);
 	return 0;
 }
 
@@ -276,7 +275,7 @@ int chrdev_open(struct inode * inode, st
 {
 	int ret = -ENODEV;
 
-	filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
+	filp->f_op = get_chrfops(kdev_t_to_nr(inode->i_rdev));
 	if (filp->f_op) {
 		ret = 0;
 		if (filp->f_op->open != NULL) {
@@ -317,3 +316,100 @@ const char *cdevname(kdev_t dev)
 
 	return buffer;
 }
+
+static struct kobject *exact_match(dev_t dev, int *part, void *data)
+{
+	struct cdev *p = data;
+	return &p->kobj;
+}
+
+static int exact_lock(dev_t dev, void *data)
+{
+	struct cdev *p = data;
+	return cdev_get(p) ? 0 : -1;
+}
+
+int cdev_add(struct cdev *p, dev_t dev, unsigned count)
+{
+	int err = kobject_add(&p->kobj);
+	if (err)
+		return err;
+	return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
+}
+
+void cdev_unmap(dev_t dev, unsigned count)
+{
+	kobj_unmap(cdev_map, dev, count);
+}
+
+void cdev_del(struct cdev *p)
+{
+	kobject_del(&p->kobj);
+	cdev_put(p);
+}
+
+struct kobject *cdev_get(struct cdev *p)
+{
+	struct module *owner = p->owner;
+	struct kobject *kobj;
+
+	if (owner && !try_module_get(owner))
+		return NULL;
+	kobj = kobject_get(&p->kobj);
+	if (!kobj)
+		module_put(owner);
+	return kobj;
+}
+
+static decl_subsys(cdev, NULL, NULL);
+
+static void cdev_dynamic_release(struct kobject *kobj)
+{
+	struct cdev *p = container_of(kobj, struct cdev, kobj);
+	kfree(p);
+}
+
+static struct kobj_type ktype_cdev_dynamic = {
+	.release	= cdev_dynamic_release,
+};
+
+static struct kset kset_dynamic = {
+	.subsys = &cdev_subsys,
+	.kobj = {.name = "major",},
+	.ktype = &ktype_cdev_dynamic,
+};
+
+struct cdev *cdev_alloc(void)
+{
+	struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(struct cdev));
+		p->kobj.kset = &kset_dynamic;
+		kobject_init(&p->kobj);
+	}
+	return p;
+}
+
+void cdev_init(struct cdev *cdev, struct file_operations *fops)
+{
+	kobj_set_kset_s(cdev, cdev_subsys);
+	kobject_init(&cdev->kobj);
+	cdev->ops = fops;
+}
+
+static struct kobject *base_probe(dev_t dev, int *part, void *data)
+{
+	char name[30];
+	sprintf(name, "char-major-%d", MAJOR(dev));
+	request_module(name);
+	return NULL;
+}
+
+static int __init chrdev_init(void)
+{
+	subsystem_register(&cdev_subsys);
+	kset_register(&kset_dynamic);
+	cdev_map = kobj_map_init(base_probe, &cdev_subsys);
+	return 0;
+}
+subsys_initcall(chrdev_init);
diff -puN /dev/null include/linux/cdev.h
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ 25-akpm/include/linux/cdev.h	2003-05-05 22:38:55.000000000 -0700
@@ -0,0 +1,30 @@
+#ifndef _LINUX_CDEV_H
+#define _LINUX_CDEV_H
+#ifdef __KERNEL__
+
+struct cdev {
+	struct kobject kobj;
+	struct module *owner;
+	struct file_operations *ops;
+};
+
+void cdev_init(struct cdev *, struct file_operations *);
+
+struct cdev *cdev_alloc(void);
+
+static inline void cdev_put(struct cdev *p)
+{
+	if (p)
+		kobject_put(&p->kobj);
+}
+
+struct kobject *cdev_get(struct cdev *);
+
+int cdev_add(struct cdev *, dev_t, unsigned);
+
+void cdev_del(struct cdev *);
+
+void cdev_unmap(dev_t, unsigned);
+
+#endif
+#endif
diff -puN include/linux/fs.h~T30-cdev-C69 include/linux/fs.h
--- 25/include/linux/fs.h~T30-cdev-C69	2003-05-05 22:38:55.000000000 -0700
+++ 25-akpm/include/linux/fs.h	2003-05-05 22:38:55.000000000 -0700
@@ -1056,10 +1056,8 @@ extern void bd_release(struct block_devi
 extern void blk_run_queues(void);
 
 /* fs/char_dev.c */
-extern int alloc_chrdev_region(dev_t *, unsigned, char *,
-				struct file_operations *);
-extern int register_chrdev_region(dev_t, unsigned, char *,
-				struct file_operations *);
+extern int alloc_chrdev_region(dev_t *, unsigned, char *);
+extern int register_chrdev_region(dev_t, unsigned, char *);
 extern int register_chrdev(unsigned int, const char *,
 			   struct file_operations *);
 extern int unregister_chrdev(unsigned int, const char *);
diff -puN include/linux/tty_driver.h~T30-cdev-C69 include/linux/tty_driver.h
--- 25/include/linux/tty_driver.h~T30-cdev-C69	2003-05-05 22:38:55.000000000 -0700
+++ 25-akpm/include/linux/tty_driver.h	2003-05-05 22:38:55.000000000 -0700
@@ -117,9 +117,11 @@
 
 #include <linux/fs.h>
 #include <linux/list.h>
+#include <linux/cdev.h>
 
 struct tty_driver {
 	int	magic;		/* magic number for this structure */
+	struct cdev cdev;
 	struct module	*owner;
 	const char	*driver_name;
 	const char	*name;

_
