/* * linux/fs/devices.c * * (C) 1993 Matthias Urlichs -- collected common code and tables. * * Copyright (C) 1991, 1992 Linus Torvalds * * Added kerneld support: Jacques Gelinas and Bjorn Ekwall * (changed to kmod) */ #include <linux/config.h> #include <linux/fs.h> #include <linux/major.h> #include <linux/string.h> #include <linux/sched.h> #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/smp_lock.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(MKDEV(ma,mi)) == NULL) #endif 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]; extern int get_blkdev_list(char *); 43 int get_device_list(char * page) { int i; int len; len = sprintf(page, "Character devices:\n"); read_lock(&chrdevs_lock); 50 for (i = 0; i < MAX_CHRDEV ; i++) { 51 if (chrdevs[i].fops) { len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name); } } 55 read_unlock(&chrdevs_lock); len += get_blkdev_list(page+len); 57 return len; } /* Return the function table of a device. Load the driver if needed. Increment the reference count of module in question. */ 65 static struct file_operations * get_chrfops(unsigned int major, unsigned int minor) { struct file_operations *ret = NULL; 69 if (!major || major >= MAX_CHRDEV) 70 return NULL; read_lock(&chrdevs_lock); ret = fops_get(chrdevs[major].fops); 74 read_unlock(&chrdevs_lock); #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); read_lock(&chrdevs_lock); ret = fops_get(chrdevs[major].fops); read_unlock(&chrdevs_lock); } #endif 95 return ret; } 98 int register_chrdev(unsigned int major, const char * name, struct file_operations *fops) { 100 if (major == 0) { write_lock(&chrdevs_lock); 102 for (major = MAX_CHRDEV-1; major > 0; major--) { 103 if (chrdevs[major].fops == NULL) { chrdevs[major].name = name; chrdevs[major].fops = fops; 106 write_unlock(&chrdevs_lock); 107 return major; } } 110 write_unlock(&chrdevs_lock); 111 return -EBUSY; } 113 if (major >= MAX_CHRDEV) 114 return -EINVAL; write_lock(&chrdevs_lock); 116 if (chrdevs[major].fops && chrdevs[major].fops != fops) { 117 write_unlock(&chrdevs_lock); 118 return -EBUSY; } chrdevs[major].name = name; chrdevs[major].fops = fops; 122 write_unlock(&chrdevs_lock); 123 return 0; } 126 int unregister_chrdev(unsigned int major, const char * name) { 128 if (major >= MAX_CHRDEV) 129 return -EINVAL; write_lock(&chrdevs_lock); 131 if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) { 132 write_unlock(&chrdevs_lock); 133 return -EINVAL; } chrdevs[major].name = NULL; chrdevs[major].fops = NULL; 137 write_unlock(&chrdevs_lock); 138 return 0; } /* * Called every time a character special file is opened */ 144 int chrdev_open(struct inode * inode, struct file * filp) { int ret = -ENODEV; filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); 149 if (filp->f_op) { ret = 0; 151 if (filp->f_op->open != NULL) { 152 lock_kernel(); ret = filp->f_op->open(inode,filp); 154 unlock_kernel(); } } 157 return ret; } /* * Dummy default file-operations: the only thing this does * is contain the open that then fills in the correct operations * depending on the special file... */ static struct file_operations def_chr_fops = { open: chrdev_open, }; /* * Print device name (in decimal, hexadecimal or symbolic) * Note: returns pointer to static data! */ 173 const char * kdevname(kdev_t dev) { static char buffer[32]; sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev)); 177 return buffer; } 180 const char * cdevname(kdev_t dev) { static char buffer[32]; const char * name = chrdevs[MAJOR(dev)].name; 185 if (!name) name = "unknown-char"; sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev)); 188 return buffer; } 191 static int sock_no_open(struct inode *irrelevant, struct file *dontcare) { 193 return -ENXIO; } static struct file_operations bad_sock_fops = { open: sock_no_open }; 200 void init_special_inode(struct inode *inode, umode_t mode, int rdev) { inode->i_mode = mode; 203 if (S_ISCHR(mode)) { inode->i_fop = &def_chr_fops; inode->i_rdev = to_kdev_t(rdev); 206 } else if (S_ISBLK(mode)) { inode->i_fop = &def_blk_fops; inode->i_rdev = to_kdev_t(rdev); inode->i_bdev = bdget(rdev); 210 } else if (S_ISFIFO(mode)) inode->i_fop = &def_fifo_fops; 212 else if (S_ISSOCK(mode)) inode->i_fop = &bad_sock_fops; 214 else printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode); }