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> 431da177e4SLinus Torvalds #include <linux/slab.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> 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds /* 531da177e4SLinus Torvalds * Head entry for the doubly linked miscdevice list 541da177e4SLinus Torvalds */ 551da177e4SLinus Torvalds static LIST_HEAD(misc_list); 561da177e4SLinus Torvalds static DECLARE_MUTEX(misc_sem); 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* 591da177e4SLinus Torvalds * Assigned numbers, used for dynamic minors 601da177e4SLinus Torvalds */ 611da177e4SLinus Torvalds #define DYNAMIC_MINORS 64 /* like dynamic majors */ 621da177e4SLinus Torvalds static unsigned char misc_minors[DYNAMIC_MINORS / 8]; 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds extern int pmu_device_init(void); 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 671da177e4SLinus Torvalds static void *misc_seq_start(struct seq_file *seq, loff_t *pos) 681da177e4SLinus Torvalds { 691da177e4SLinus Torvalds struct miscdevice *p; 701da177e4SLinus Torvalds loff_t off = 0; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds down(&misc_sem); 731da177e4SLinus Torvalds list_for_each_entry(p, &misc_list, list) { 741da177e4SLinus Torvalds if (*pos == off++) 751da177e4SLinus Torvalds return p; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds return NULL; 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds struct list_head *n = ((struct miscdevice *)v)->list.next; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds ++*pos; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds return (n != &misc_list) ? list_entry(n, struct miscdevice, list) 871da177e4SLinus Torvalds : NULL; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static void misc_seq_stop(struct seq_file *seq, void *v) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds up(&misc_sem); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds static int misc_seq_show(struct seq_file *seq, void *v) 961da177e4SLinus Torvalds { 971da177e4SLinus Torvalds const struct miscdevice *p = v; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 1001da177e4SLinus Torvalds return 0; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds static struct seq_operations misc_seq_ops = { 1051da177e4SLinus Torvalds .start = misc_seq_start, 1061da177e4SLinus Torvalds .next = misc_seq_next, 1071da177e4SLinus Torvalds .stop = misc_seq_stop, 1081da177e4SLinus Torvalds .show = misc_seq_show, 1091da177e4SLinus Torvalds }; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds static int misc_seq_open(struct inode *inode, struct file *file) 1121da177e4SLinus Torvalds { 1131da177e4SLinus Torvalds return seq_open(file, &misc_seq_ops); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 116*62322d25SArjan van de Ven static const struct file_operations misc_proc_fops = { 1171da177e4SLinus Torvalds .owner = THIS_MODULE, 1181da177e4SLinus Torvalds .open = misc_seq_open, 1191da177e4SLinus Torvalds .read = seq_read, 1201da177e4SLinus Torvalds .llseek = seq_lseek, 1211da177e4SLinus Torvalds .release = seq_release, 1221da177e4SLinus Torvalds }; 1231da177e4SLinus Torvalds #endif 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds static int misc_open(struct inode * inode, struct file * file) 1261da177e4SLinus Torvalds { 1271da177e4SLinus Torvalds int minor = iminor(inode); 1281da177e4SLinus Torvalds struct miscdevice *c; 1291da177e4SLinus Torvalds int err = -ENODEV; 13099ac48f5SArjan van de Ven const struct file_operations *old_fops, *new_fops = NULL; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds down(&misc_sem); 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 1351da177e4SLinus Torvalds if (c->minor == minor) { 1361da177e4SLinus Torvalds new_fops = fops_get(c->fops); 1371da177e4SLinus Torvalds break; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds if (!new_fops) { 1421da177e4SLinus Torvalds up(&misc_sem); 1431da177e4SLinus Torvalds request_module("char-major-%d-%d", MISC_MAJOR, minor); 1441da177e4SLinus Torvalds down(&misc_sem); 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 1471da177e4SLinus Torvalds if (c->minor == minor) { 1481da177e4SLinus Torvalds new_fops = fops_get(c->fops); 1491da177e4SLinus Torvalds break; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds if (!new_fops) 1531da177e4SLinus Torvalds goto fail; 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds err = 0; 1571da177e4SLinus Torvalds old_fops = file->f_op; 1581da177e4SLinus Torvalds file->f_op = new_fops; 1591da177e4SLinus Torvalds if (file->f_op->open) { 1601da177e4SLinus Torvalds err=file->f_op->open(inode,file); 1611da177e4SLinus Torvalds if (err) { 1621da177e4SLinus Torvalds fops_put(file->f_op); 1631da177e4SLinus Torvalds file->f_op = fops_get(old_fops); 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds fops_put(old_fops); 1671da177e4SLinus Torvalds fail: 1681da177e4SLinus Torvalds up(&misc_sem); 1691da177e4SLinus Torvalds return err; 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds /* 1731da177e4SLinus Torvalds * TODO for 2.7: 174ca8eca68Sgregkh@suse.de * - add a struct kref to struct miscdevice and make all usages of 1751da177e4SLinus Torvalds * them dynamic. 1761da177e4SLinus Torvalds */ 177ca8eca68Sgregkh@suse.de static struct class *misc_class; 1781da177e4SLinus Torvalds 179*62322d25SArjan van de Ven static const struct file_operations misc_fops = { 1801da177e4SLinus Torvalds .owner = THIS_MODULE, 1811da177e4SLinus Torvalds .open = misc_open, 1821da177e4SLinus Torvalds }; 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds /** 1861da177e4SLinus Torvalds * misc_register - register a miscellaneous device 1871da177e4SLinus Torvalds * @misc: device structure 1881da177e4SLinus Torvalds * 1891da177e4SLinus Torvalds * Register a miscellaneous device with the kernel. If the minor 1901da177e4SLinus Torvalds * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 1911da177e4SLinus Torvalds * and placed in the minor field of the structure. For other cases 1921da177e4SLinus Torvalds * the minor number requested is used. 1931da177e4SLinus Torvalds * 1941da177e4SLinus Torvalds * The structure passed is linked into the kernel and may not be 1951da177e4SLinus Torvalds * destroyed until it has been unregistered. 1961da177e4SLinus Torvalds * 1971da177e4SLinus Torvalds * A zero is returned on success and a negative errno code for 1981da177e4SLinus Torvalds * failure. 1991da177e4SLinus Torvalds */ 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds int misc_register(struct miscdevice * misc) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds struct miscdevice *c; 2041da177e4SLinus Torvalds dev_t dev; 2057c69ef79SGreg Kroah-Hartman int err = 0; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds down(&misc_sem); 2081da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 2091da177e4SLinus Torvalds if (c->minor == misc->minor) { 2101da177e4SLinus Torvalds up(&misc_sem); 2111da177e4SLinus Torvalds return -EBUSY; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds if (misc->minor == MISC_DYNAMIC_MINOR) { 2161da177e4SLinus Torvalds int i = DYNAMIC_MINORS; 2171da177e4SLinus Torvalds while (--i >= 0) 2181da177e4SLinus Torvalds if ( (misc_minors[i>>3] & (1 << (i&7))) == 0) 2191da177e4SLinus Torvalds break; 2201da177e4SLinus Torvalds if (i<0) { 2211da177e4SLinus Torvalds up(&misc_sem); 2221da177e4SLinus Torvalds return -EBUSY; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds misc->minor = i; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds if (misc->minor < DYNAMIC_MINORS) 2281da177e4SLinus Torvalds misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); 2291da177e4SLinus Torvalds dev = MKDEV(MISC_MAJOR, misc->minor); 2301da177e4SLinus Torvalds 23153f46542SGreg Kroah-Hartman misc->class = class_device_create(misc_class, NULL, dev, misc->dev, 232ca8eca68Sgregkh@suse.de "%s", misc->name); 2331da177e4SLinus Torvalds if (IS_ERR(misc->class)) { 2341da177e4SLinus Torvalds err = PTR_ERR(misc->class); 2351da177e4SLinus Torvalds goto out; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds /* 2391da177e4SLinus Torvalds * Add it to the front, so that later devices can "override" 2401da177e4SLinus Torvalds * earlier defaults 2411da177e4SLinus Torvalds */ 2421da177e4SLinus Torvalds list_add(&misc->list, &misc_list); 2431da177e4SLinus Torvalds out: 2441da177e4SLinus Torvalds up(&misc_sem); 2451da177e4SLinus Torvalds return err; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds /** 2491da177e4SLinus Torvalds * misc_deregister - unregister a miscellaneous device 2501da177e4SLinus Torvalds * @misc: device to unregister 2511da177e4SLinus Torvalds * 2521da177e4SLinus Torvalds * Unregister a miscellaneous device that was previously 2531da177e4SLinus Torvalds * successfully registered with misc_register(). Success 2541da177e4SLinus Torvalds * is indicated by a zero return, a negative errno code 2551da177e4SLinus Torvalds * indicates an error. 2561da177e4SLinus Torvalds */ 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds int misc_deregister(struct miscdevice * misc) 2591da177e4SLinus Torvalds { 2601da177e4SLinus Torvalds int i = misc->minor; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds if (list_empty(&misc->list)) 2631da177e4SLinus Torvalds return -EINVAL; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds down(&misc_sem); 2661da177e4SLinus Torvalds list_del(&misc->list); 267ca8eca68Sgregkh@suse.de class_device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); 2681da177e4SLinus Torvalds if (i < DYNAMIC_MINORS && i>0) { 2691da177e4SLinus Torvalds misc_minors[i>>3] &= ~(1 << (misc->minor & 7)); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds up(&misc_sem); 2721da177e4SLinus Torvalds return 0; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds EXPORT_SYMBOL(misc_register); 2761da177e4SLinus Torvalds EXPORT_SYMBOL(misc_deregister); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static int __init misc_init(void) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 2811da177e4SLinus Torvalds struct proc_dir_entry *ent; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds ent = create_proc_entry("misc", 0, NULL); 2841da177e4SLinus Torvalds if (ent) 2851da177e4SLinus Torvalds ent->proc_fops = &misc_proc_fops; 2861da177e4SLinus Torvalds #endif 287ca8eca68Sgregkh@suse.de misc_class = class_create(THIS_MODULE, "misc"); 2881da177e4SLinus Torvalds if (IS_ERR(misc_class)) 2891da177e4SLinus Torvalds return PTR_ERR(misc_class); 290573fc113SChristoph Hellwig 2911da177e4SLinus Torvalds if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { 2921da177e4SLinus Torvalds printk("unable to get major %d for misc devices\n", 2931da177e4SLinus Torvalds MISC_MAJOR); 294ca8eca68Sgregkh@suse.de class_destroy(misc_class); 2951da177e4SLinus Torvalds return -EIO; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds return 0; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds subsys_initcall(misc_init); 300