This patch removes the character device array, so that we can handle
now an arbitrary number of character devices.
The tty hack was moved where it belongs to.
The char_device structure for registered major devices is now stored
at hash(0,major), so it's basically out of the way for further
cleanups.
bye, Roman
diff -pur -X /home/devel/roman/nodiff linux-2.5.66-cdev2/drivers/char/tty_io.c linux-2.5.66-cdev3/drivers/char/tty_io.c
--- linux-2.5.66-cdev2/drivers/char/tty_io.c 2003-03-25 11:02:25.000000000 +0100
+++ linux-2.5.66-cdev3/drivers/char/tty_io.c 2003-03-25 11:02:38.000000000 +0100
@@ -327,11 +327,15 @@ static int tty_set_ldisc(struct tty_stru
/*
* This routine returns a tty driver structure, given a device number
*/
-struct tty_driver *get_tty_driver(kdev_t device)
+static struct tty_driver *get_tty_driver(kdev_t device)
{
int major, minor;
struct tty_driver *p;
-
+#ifdef CONFIG_KMOD
+ char name[20] = "";
+again:
+#endif
+
minor = minor(device);
major = major(device);
@@ -344,6 +348,13 @@ struct tty_driver *get_tty_driver(kdev_t
continue;
return p;
}
+#ifdef CONFIG_KMOD
+ if (!name[0]) {
+ sprintf(name, "char-major-%d", major);
+ request_module(name);
+ goto again;
+ }
+#endif
return NULL;
}
@@ -2113,21 +2124,33 @@ EXPORT_SYMBOL(tty_unregister_device);
int tty_register_driver(struct tty_driver *driver)
{
int error;
- int i;
+ int i;
if (driver->flags & TTY_DRIVER_INSTALLED)
return 0;
+ down_tty_sem(0);
+ if (driver->major) {
+ struct char_device *cdev = cdget(MKDEV(driver->major, 0));
+ struct file_operations *fops = cdev->cd_fops;
+ cdput(cdev);
+ error = 0;
+ if (fops == &tty_fops)
+ goto skip;
+ }
error = register_chrdev(driver->major, driver->name, &tty_fops);
- if (error < 0)
+ if (error < 0) {
+ up_tty_sem(0);
return error;
- else if(driver->major == 0)
+ } else if (driver->major == 0)
driver->major = error;
+skip:
if (!driver->put_char)
driver->put_char = tty_default_put_char;
list_add(&driver->tty_drivers, &tty_drivers);
+ up_tty_sem(0);
if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
for(i = 0; i < driver->num; i++)
@@ -2151,24 +2174,30 @@ int tty_unregister_driver(struct tty_dri
if (*driver->refcount)
return -EBUSY;
+ down_tty_sem(0);
list_for_each_entry(p, &tty_drivers, tty_drivers) {
if (p == driver)
found++;
else if (p->major == driver->major)
othername = p->name;
}
-
- if (!found)
- return -ENOENT;
- if (othername == NULL) {
- retval = unregister_chrdev(driver->major, driver->name);
- if (retval)
- return retval;
+ if (found) {
+ if (othername) {
+ struct char_device *cdev = cdget(MKDEV(driver->major, 0));
+ cdev->cd_name = othername;
+ cdput(cdev);
+ retval = 0;
+ } else
+ retval = unregister_chrdev(driver->major, driver->name);
} else
- register_chrdev(driver->major, othername, &tty_fops);
+ retval = -ENOENT;
- list_del(&driver->tty_drivers);
+ if (!retval)
+ list_del(&driver->tty_drivers);
+ up_tty_sem(0);
+ if (retval)
+ return retval;
/*
* Free the termios and termios_locked structures because
diff -pur -X /home/devel/roman/nodiff linux-2.5.66-cdev2/fs/char_dev.c linux-2.5.66-cdev3/fs/char_dev.c
--- linux-2.5.66-cdev2/fs/char_dev.c 2003-03-25 11:02:27.000000000 +0100
+++ linux-2.5.66-cdev3/fs/char_dev.c 2003-03-25 11:02:38.000000000 +0100
@@ -15,17 +15,8 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
-#include <linux/devfs_fs_kernel.h>
-#ifdef CONFIG_KMOD
#include <linux/kmod.h>
-#include <linux/tty.h>
-
-/* serial module kmod load support */
-struct tty_driver *get_tty_driver(kdev_t device);
-#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
-#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL)
-#endif
#define HASH_BITS 6
#define HASH_SIZE (1UL << HASH_BITS)
@@ -33,6 +24,7 @@ struct tty_driver *get_tty_driver(kdev_t
static struct list_head cdev_hashtable[HASH_SIZE];
static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED;
static kmem_cache_t *cdev_cachep;
+static LIST_HEAD(cdev_list);
#define alloc_cdev() \
((struct char_device *) kmem_cache_alloc(cdev_cachep, SLAB_KERNEL))
@@ -43,8 +35,10 @@ static void init_once(void *foo, kmem_ca
struct char_device *cdev = (struct char_device *) foo;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
- SLAB_CTOR_CONSTRUCTOR)
+ SLAB_CTOR_CONSTRUCTOR) {
memset(cdev, 0, sizeof(*cdev));
+ sema_init(&cdev->cd_sem, 1);
+ }
}
void __init cdev_cache_init(void)
@@ -131,27 +125,19 @@ void cdput(struct char_device *cdev)
}
}
-struct device_struct {
- const char * name;
- struct file_operations * fops;
-};
-
-static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
-static struct device_struct chrdevs[MAX_CHRDEV];
-
int get_chrdev_list(char *page)
{
- int i;
+ struct char_device *cdev;
int len;
len = sprintf(page, "Character devices:\n");
- read_lock(&chrdevs_lock);
- for (i = 0; i < MAX_CHRDEV ; i++) {
- if (chrdevs[i].fops) {
- len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
- }
+ spin_lock(&cdev_lock);
+ list_for_each_entry(cdev, &cdev_list, cd_list) {
+ if (cdev->cd_name)
+ len += sprintf(page+len, "%3d %s\n",
+ MAJOR(cdev->cd_dev), cdev->cd_name);
}
- read_unlock(&chrdevs_lock);
+ spin_unlock(&cdev_lock);
return len;
}
@@ -160,80 +146,98 @@ int get_chrdev_list(char *page)
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(struct char_device *cdev)
{
- struct file_operations *ret = NULL;
-
- if (!major || major >= MAX_CHRDEV)
- return NULL;
+ struct file_operations *fops;
- read_lock(&chrdevs_lock);
- ret = fops_get(chrdevs[major].fops);
- read_unlock(&chrdevs_lock);
+ down(&cdev->cd_sem);
+ fops = fops_get(cdev->cd_fops);
+ up(&cdev->cd_sem);
+ if (fops)
+ return fops;
+
+ cdev = cdget_major(MAJOR(cdev->cd_dev));
+ down(&cdev->cd_sem);
+ fops = fops_get(cdev->cd_fops);
+ up(&cdev->cd_sem);
#ifdef CONFIG_KMOD
- if (ret && isa_tty_dev(major)) {
- lock_kernel();
- if (need_serial(major,minor)) {
- /* Force request_module anyway, but what for? */
- fops_put(ret);
- ret = NULL;
- }
- unlock_kernel();
- }
- if (!ret) {
- char name[20];
- sprintf(name, "char-major-%d", major);
- request_module(name);
+ if (!fops) {
+ char name[32];
- read_lock(&chrdevs_lock);
- ret = fops_get(chrdevs[major].fops);
- read_unlock(&chrdevs_lock);
+ sprintf(name, "char-major-%d", MAJOR(cdev->cd_dev));
+ request_module(name);
+ down(&cdev->cd_sem);
+ fops = fops_get(cdev->cd_fops);
+ up(&cdev->cd_sem);
}
#endif
- return ret;
+ cdput(cdev);
+ if (fops->open)
+ return fops;
+ fops_put(fops);
+ return NULL;
}
-
+
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
{
+ struct char_device *cdev;
+ int res;
+
if (major == 0) {
- write_lock(&chrdevs_lock);
for (major = MAX_CHRDEV-1; major > 0; major--) {
- if (chrdevs[major].fops == NULL) {
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
- write_unlock(&chrdevs_lock);
- return major;
+ cdev = cdget_major(major);
+ down(&cdev->cd_sem);
+ if (!cdev->cd_fops) {
+ res = major;
+ goto found;
}
+ up(&cdev->cd_sem);
+ cdput(cdev);
}
- write_unlock(&chrdevs_lock);
return -EBUSY;
}
+
if (major >= MAX_CHRDEV)
return -EINVAL;
- write_lock(&chrdevs_lock);
- if (chrdevs[major].fops && chrdevs[major].fops != fops) {
- write_unlock(&chrdevs_lock);
+
+ cdev = cdget_major(major);
+ down(&cdev->cd_sem);
+ if (cdev->cd_fops) {
+ up(&cdev->cd_sem);
+ cdput(cdev);
return -EBUSY;
}
- chrdevs[major].name = name;
- chrdevs[major].fops = fops;
- write_unlock(&chrdevs_lock);
+ res = 0;
+found:
+ cdev->cd_fops = fops;
+ cdev->cd_name = name;
+ list_add_tail(&cdev->cd_list, &cdev_list);
+ up(&cdev->cd_sem);
return 0;
}
int unregister_chrdev(unsigned int major, const char * name)
{
+ struct char_device *cdev;
+ int res;
+
if (major >= MAX_CHRDEV)
return -EINVAL;
- write_lock(&chrdevs_lock);
- if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
- write_unlock(&chrdevs_lock);
- return -EINVAL;
+
+ cdev = cdget_major(major);
+ down(&cdev->cd_sem);
+ res = -EINVAL;
+ if (cdev->cd_fops && !strcmp(cdev->cd_name, name)) {
+ list_del(&cdev->cd_list);
+ cdev->cd_fops = NULL;
+ cdev->cd_name = NULL;
+ cdput(cdev);
+ res = 0;
}
- chrdevs[major].name = NULL;
- chrdevs[major].fops = NULL;
- write_unlock(&chrdevs_lock);
- return 0;
+ up(&cdev->cd_sem);
+
+ cdput(cdev);
+ return res;
}
/*
@@ -241,14 +245,16 @@ int unregister_chrdev(unsigned int major
*/
int chrdev_open(struct inode * inode, struct file * filp)
{
+ struct file_operations *fops;
int ret = -ENODEV;
- filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
- if (filp->f_op) {
+ fops = get_chrfops(inode->i_cdev);
+ if (fops) {
ret = 0;
- if (filp->f_op->open != NULL) {
+ filp->f_op = fops;
+ if (fops->open) {
lock_kernel();
- ret = filp->f_op->open(inode,filp);
+ ret = fops->open(inode, filp);
unlock_kernel();
}
}
@@ -266,11 +272,13 @@ struct file_operations def_chr_fops = {
const char *cdevname(kdev_t dev)
{
- static char buffer[40];
- const char * name = chrdevs[major(dev)].name;
+ static char buffer[64];
+ struct char_device *cdev;
+
+ cdev = cdget(kdev_t_to_nr(dev));
+ down(&cdev->cd_sem);
+ sprintf(buffer, "%s(%d,%d)", cdev->cd_name ? cdev->cd_name : "unknown-char", major(dev), minor(dev));
+ up(&cdev->cd_sem);
- if (!name)
- name = "unknown-char";
- sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
return buffer;
}
diff -pur -X /home/devel/roman/nodiff linux-2.5.66-cdev2/include/linux/fs.h linux-2.5.66-cdev3/include/linux/fs.h
--- linux-2.5.66-cdev2/include/linux/fs.h 2003-03-25 11:02:27.000000000 +0100
+++ linux-2.5.66-cdev3/include/linux/fs.h 2003-03-25 11:02:38.000000000 +0100
@@ -333,8 +333,13 @@ struct address_space {
struct char_device {
struct list_head cd_hash;
+ struct list_head cd_list;
+ struct file_operations *cd_fops;
+ const char * cd_name;
atomic_t cd_count;
dev_t cd_dev;
+ struct semaphore cd_sem;
+ void * cd_data;
};
struct block_device {
@@ -1068,6 +1073,8 @@ extern int chrdev_open(struct inode *, s
extern struct char_device *cdget(dev_t);
extern void cdput(struct char_device *);
+#define cdget_major(maj) cdget(MKDEV(0, maj))
+
/* fs/block_dev.c */
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
extern const char *__bdevname(dev_t, char *buffer);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/