11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/drivers/char/misc.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Generic misc open routine by Johan Myreen 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Based on code from Linus 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's 91da177e4SLinus Torvalds * changes incorporated into 0.97pl4 101da177e4SLinus Torvalds * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92) 111da177e4SLinus Torvalds * See busmouse.c for particulars. 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Made things a lot mode modular - easy to compile in just one or two 141da177e4SLinus Torvalds * of the misc drivers, as they are now completely independent. Linus. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Fixed a failing symbol register to free the device registration 191da177e4SLinus Torvalds * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 241da177e4SLinus Torvalds * 251da177e4SLinus Torvalds * Handling of mouse minor numbers for kerneld: 261da177e4SLinus Torvalds * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, 271da177e4SLinus Torvalds * adapted by Bjorn Ekwall <bj0rn@blox.se> 281da177e4SLinus Torvalds * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> 291da177e4SLinus Torvalds * 301da177e4SLinus Torvalds * Changes for kmod (from kerneld): 311da177e4SLinus Torvalds * Cyrus Durgin <cider@speakeasy.org> 321da177e4SLinus Torvalds * 331da177e4SLinus Torvalds * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998 341da177e4SLinus Torvalds */ 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #include <linux/module.h> 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds #include <linux/fs.h> 391da177e4SLinus Torvalds #include <linux/errno.h> 401da177e4SLinus Torvalds #include <linux/miscdevice.h> 411da177e4SLinus Torvalds #include <linux/kernel.h> 421da177e4SLinus Torvalds #include <linux/major.h> 430e82d5b6SMatthias Kaehlcke #include <linux/mutex.h> 441da177e4SLinus Torvalds #include <linux/proc_fs.h> 451da177e4SLinus Torvalds #include <linux/seq_file.h> 461da177e4SLinus Torvalds #include <linux/stat.h> 471da177e4SLinus Torvalds #include <linux/init.h> 481da177e4SLinus Torvalds #include <linux/device.h> 491da177e4SLinus Torvalds #include <linux/tty.h> 501da177e4SLinus Torvalds #include <linux/kmod.h> 515a0e3ad6STejun Heo #include <linux/gfp.h> 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds /* 541da177e4SLinus Torvalds * Head entry for the doubly linked miscdevice list 551da177e4SLinus Torvalds */ 561da177e4SLinus Torvalds static LIST_HEAD(misc_list); 570e82d5b6SMatthias Kaehlcke static DEFINE_MUTEX(misc_mtx); 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds /* 601da177e4SLinus Torvalds * Assigned numbers, used for dynamic minors 611da177e4SLinus Torvalds */ 621da177e4SLinus Torvalds #define DYNAMIC_MINORS 64 /* like dynamic majors */ 631f2f38d8SThadeu Lima de Souza Cascardo static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 661da177e4SLinus Torvalds static void *misc_seq_start(struct seq_file *seq, loff_t *pos) 671da177e4SLinus Torvalds { 680e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx); 6946c65b71SPavel Emelianov return seq_list_start(&misc_list, *pos); 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 731da177e4SLinus Torvalds { 7446c65b71SPavel Emelianov return seq_list_next(v, &misc_list, pos); 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds static void misc_seq_stop(struct seq_file *seq, void *v) 781da177e4SLinus Torvalds { 790e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx); 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds static int misc_seq_show(struct seq_file *seq, void *v) 831da177e4SLinus Torvalds { 8446c65b71SPavel Emelianov const struct miscdevice *p = list_entry(v, struct miscdevice, list); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 871da177e4SLinus Torvalds return 0; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds 9188e9d34cSJames Morris static const struct seq_operations misc_seq_ops = { 921da177e4SLinus Torvalds .start = misc_seq_start, 931da177e4SLinus Torvalds .next = misc_seq_next, 941da177e4SLinus Torvalds .stop = misc_seq_stop, 951da177e4SLinus Torvalds .show = misc_seq_show, 961da177e4SLinus Torvalds }; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds static int misc_seq_open(struct inode *inode, struct file *file) 991da177e4SLinus Torvalds { 1001da177e4SLinus Torvalds return seq_open(file, &misc_seq_ops); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 10362322d25SArjan van de Ven static const struct file_operations misc_proc_fops = { 1041da177e4SLinus Torvalds .owner = THIS_MODULE, 1051da177e4SLinus Torvalds .open = misc_seq_open, 1061da177e4SLinus Torvalds .read = seq_read, 1071da177e4SLinus Torvalds .llseek = seq_lseek, 1081da177e4SLinus Torvalds .release = seq_release, 1091da177e4SLinus Torvalds }; 1101da177e4SLinus Torvalds #endif 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static int misc_open(struct inode *inode, struct file *file) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds int minor = iminor(inode); 1151da177e4SLinus Torvalds struct miscdevice *c; 1161da177e4SLinus Torvalds int err = -ENODEV; 117e84f9e57SAl Viro const struct file_operations *new_fops = NULL; 1181da177e4SLinus Torvalds 1190e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx); 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 1221da177e4SLinus Torvalds if (c->minor == minor) { 1231da177e4SLinus Torvalds new_fops = fops_get(c->fops); 1241da177e4SLinus Torvalds break; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds if (!new_fops) { 1290e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx); 1301da177e4SLinus Torvalds request_module("char-major-%d-%d", MISC_MAJOR, minor); 1310e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx); 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 1341da177e4SLinus Torvalds if (c->minor == minor) { 1351da177e4SLinus Torvalds new_fops = fops_get(c->fops); 1361da177e4SLinus Torvalds break; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds if (!new_fops) 1401da177e4SLinus Torvalds goto fail; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1430b509d8dSTom Van Braeckel /* 1440b509d8dSTom Van Braeckel * Place the miscdevice in the file's 1450b509d8dSTom Van Braeckel * private_data so it can be used by the 1460b509d8dSTom Van Braeckel * file operations, including f_op->open below 1470b509d8dSTom Van Braeckel */ 1480b509d8dSTom Van Braeckel file->private_data = c; 1490b509d8dSTom Van Braeckel 1501da177e4SLinus Torvalds err = 0; 151e84f9e57SAl Viro replace_fops(file, new_fops); 1520b509d8dSTom Van Braeckel if (file->f_op->open) 1531da177e4SLinus Torvalds err = file->f_op->open(inode, file); 1541da177e4SLinus Torvalds fail: 1550e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx); 1561da177e4SLinus Torvalds return err; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 159ca8eca68Sgregkh@suse.de static struct class *misc_class; 1601da177e4SLinus Torvalds 16162322d25SArjan van de Ven static const struct file_operations misc_fops = { 1621da177e4SLinus Torvalds .owner = THIS_MODULE, 1631da177e4SLinus Torvalds .open = misc_open, 1646038f373SArnd Bergmann .llseek = noop_llseek, 1651da177e4SLinus Torvalds }; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /** 1681da177e4SLinus Torvalds * misc_register - register a miscellaneous device 1691da177e4SLinus Torvalds * @misc: device structure 1701da177e4SLinus Torvalds * 1711da177e4SLinus Torvalds * Register a miscellaneous device with the kernel. If the minor 1721da177e4SLinus Torvalds * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 1731da177e4SLinus Torvalds * and placed in the minor field of the structure. For other cases 1741da177e4SLinus Torvalds * the minor number requested is used. 1751da177e4SLinus Torvalds * 1761da177e4SLinus Torvalds * The structure passed is linked into the kernel and may not be 17703190c67SMartin Kepplinger * destroyed until it has been unregistered. By default, an open() 17803190c67SMartin Kepplinger * syscall to the device sets file->private_data to point to the 17903190c67SMartin Kepplinger * structure. Drivers don't need open in fops for this. 1801da177e4SLinus Torvalds * 1811da177e4SLinus Torvalds * A zero is returned on success and a negative errno code for 1821da177e4SLinus Torvalds * failure. 1831da177e4SLinus Torvalds */ 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds int misc_register(struct miscdevice *misc) 1861da177e4SLinus Torvalds { 1871da177e4SLinus Torvalds dev_t dev; 1887c69ef79SGreg Kroah-Hartman int err = 0; 189b575f712SVladimir Zapolskiy bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); 1901da177e4SLinus Torvalds 1915d469ec0SNeil Horman INIT_LIST_HEAD(&misc->list); 1925d469ec0SNeil Horman 1930e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx); 1941da177e4SLinus Torvalds 195b575f712SVladimir Zapolskiy if (is_dynamic) { 1961f2f38d8SThadeu Lima de Souza Cascardo int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); 1975b884a95SVarsha Rao 1981f2f38d8SThadeu Lima de Souza Cascardo if (i >= DYNAMIC_MINORS) { 199684116caSElad Wexler err = -EBUSY; 200684116caSElad Wexler goto out; 2011da177e4SLinus Torvalds } 2021f2f38d8SThadeu Lima de Souza Cascardo misc->minor = DYNAMIC_MINORS - i - 1; 2031f2f38d8SThadeu Lima de Souza Cascardo set_bit(i, misc_minors); 2043c94ce6fSDae S. Kim } else { 2053c94ce6fSDae S. Kim struct miscdevice *c; 2063c94ce6fSDae S. Kim 2073c94ce6fSDae S. Kim list_for_each_entry(c, &misc_list, list) { 2083c94ce6fSDae S. Kim if (c->minor == misc->minor) { 209684116caSElad Wexler err = -EBUSY; 210684116caSElad Wexler goto out; 2113c94ce6fSDae S. Kim } 2123c94ce6fSDae S. Kim } 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds dev = MKDEV(MISC_MAJOR, misc->minor); 2161da177e4SLinus Torvalds 217bd735995STakashi Iwai misc->this_device = 218bd735995STakashi Iwai device_create_with_groups(misc_class, misc->parent, dev, 219bd735995STakashi Iwai misc, misc->groups, "%s", misc->name); 22094fbcdedSGreg Kroah-Hartman if (IS_ERR(misc->this_device)) { 221b575f712SVladimir Zapolskiy if (is_dynamic) { 2221f2f38d8SThadeu Lima de Souza Cascardo int i = DYNAMIC_MINORS - misc->minor - 1; 223b575f712SVladimir Zapolskiy 2244ae717daSThadeu Lima de Souza Cascardo if (i < DYNAMIC_MINORS && i >= 0) 2251f2f38d8SThadeu Lima de Souza Cascardo clear_bit(i, misc_minors); 226b575f712SVladimir Zapolskiy misc->minor = MISC_DYNAMIC_MINOR; 227b575f712SVladimir Zapolskiy } 22894fbcdedSGreg Kroah-Hartman err = PTR_ERR(misc->this_device); 2291da177e4SLinus Torvalds goto out; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds /* 2331da177e4SLinus Torvalds * Add it to the front, so that later devices can "override" 2341da177e4SLinus Torvalds * earlier defaults 2351da177e4SLinus Torvalds */ 2361da177e4SLinus Torvalds list_add(&misc->list, &misc_list); 2371da177e4SLinus Torvalds out: 2380e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx); 2391da177e4SLinus Torvalds return err; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds /** 243b844eba2SRafael J. Wysocki * misc_deregister - unregister a miscellaneous device 2441da177e4SLinus Torvalds * @misc: device to unregister 2451da177e4SLinus Torvalds * 2461da177e4SLinus Torvalds * Unregister a miscellaneous device that was previously 247f368ed60SGreg Kroah-Hartman * successfully registered with misc_register(). 2481da177e4SLinus Torvalds */ 2491da177e4SLinus Torvalds 250f368ed60SGreg Kroah-Hartman void misc_deregister(struct miscdevice *misc) 2511da177e4SLinus Torvalds { 2521f2f38d8SThadeu Lima de Souza Cascardo int i = DYNAMIC_MINORS - misc->minor - 1; 2531da177e4SLinus Torvalds 254b329becfSAkinobu Mita if (WARN_ON(list_empty(&misc->list))) 255f368ed60SGreg Kroah-Hartman return; 2561da177e4SLinus Torvalds 2570e82d5b6SMatthias Kaehlcke mutex_lock(&misc_mtx); 2581da177e4SLinus Torvalds list_del(&misc->list); 25994fbcdedSGreg Kroah-Hartman device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); 2604ae717daSThadeu Lima de Souza Cascardo if (i < DYNAMIC_MINORS && i >= 0) 2611f2f38d8SThadeu Lima de Souza Cascardo clear_bit(i, misc_minors); 2620e82d5b6SMatthias Kaehlcke mutex_unlock(&misc_mtx); 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds EXPORT_SYMBOL(misc_register); 266b844eba2SRafael J. Wysocki EXPORT_SYMBOL(misc_deregister); 2671da177e4SLinus Torvalds 2682c9ede55SAl Viro static char *misc_devnode(struct device *dev, umode_t *mode) 269d4056405SKay Sievers { 270d4056405SKay Sievers struct miscdevice *c = dev_get_drvdata(dev); 271d4056405SKay Sievers 272e454cea2SKay Sievers if (mode && c->mode) 273e454cea2SKay Sievers *mode = c->mode; 274e454cea2SKay Sievers if (c->nodename) 275e454cea2SKay Sievers return kstrdup(c->nodename, GFP_KERNEL); 276d4056405SKay Sievers return NULL; 277d4056405SKay Sievers } 278d4056405SKay Sievers 2791da177e4SLinus Torvalds static int __init misc_init(void) 2801da177e4SLinus Torvalds { 2811b502217SDenis V. Lunev int err; 2821037b278SSudip Mukherjee struct proc_dir_entry *ret; 2831da177e4SLinus Torvalds 2841037b278SSudip Mukherjee ret = proc_create("misc", 0, NULL, &misc_proc_fops); 285ca8eca68Sgregkh@suse.de misc_class = class_create(THIS_MODULE, "misc"); 2861b502217SDenis V. Lunev err = PTR_ERR(misc_class); 2871da177e4SLinus Torvalds if (IS_ERR(misc_class)) 2881b502217SDenis V. Lunev goto fail_remove; 289573fc113SChristoph Hellwig 2901b502217SDenis V. Lunev err = -EIO; 2911b502217SDenis V. Lunev if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) 2921b502217SDenis V. Lunev goto fail_printk; 293e454cea2SKay Sievers misc_class->devnode = misc_devnode; 2941da177e4SLinus Torvalds return 0; 2951b502217SDenis V. Lunev 2961b502217SDenis V. Lunev fail_printk: 297*8ab44b40SVarsha Rao pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); 2981b502217SDenis V. Lunev class_destroy(misc_class); 2991b502217SDenis V. Lunev fail_remove: 3001037b278SSudip Mukherjee if (ret) 3011b502217SDenis V. Lunev remove_proc_entry("misc", NULL); 3021b502217SDenis V. Lunev return err; 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds subsys_initcall(misc_init); 305