1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * linux/drivers/char/misc.c 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * Generic misc open routine by Johan Myreen 5*1da177e4SLinus Torvalds * 6*1da177e4SLinus Torvalds * Based on code from Linus 7*1da177e4SLinus Torvalds * 8*1da177e4SLinus Torvalds * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's 9*1da177e4SLinus Torvalds * changes incorporated into 0.97pl4 10*1da177e4SLinus Torvalds * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92) 11*1da177e4SLinus Torvalds * See busmouse.c for particulars. 12*1da177e4SLinus Torvalds * 13*1da177e4SLinus Torvalds * Made things a lot mode modular - easy to compile in just one or two 14*1da177e4SLinus Torvalds * of the misc drivers, as they are now completely independent. Linus. 15*1da177e4SLinus Torvalds * 16*1da177e4SLinus Torvalds * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> 17*1da177e4SLinus Torvalds * 18*1da177e4SLinus Torvalds * Fixed a failing symbol register to free the device registration 19*1da177e4SLinus Torvalds * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96 20*1da177e4SLinus Torvalds * 21*1da177e4SLinus Torvalds * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 22*1da177e4SLinus Torvalds * 23*1da177e4SLinus Torvalds * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 24*1da177e4SLinus Torvalds * 25*1da177e4SLinus Torvalds * Handling of mouse minor numbers for kerneld: 26*1da177e4SLinus Torvalds * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, 27*1da177e4SLinus Torvalds * adapted by Bjorn Ekwall <bj0rn@blox.se> 28*1da177e4SLinus Torvalds * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> 29*1da177e4SLinus Torvalds * 30*1da177e4SLinus Torvalds * Changes for kmod (from kerneld): 31*1da177e4SLinus Torvalds * Cyrus Durgin <cider@speakeasy.org> 32*1da177e4SLinus Torvalds * 33*1da177e4SLinus Torvalds * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au> 10-Jan-1998 34*1da177e4SLinus Torvalds */ 35*1da177e4SLinus Torvalds 36*1da177e4SLinus Torvalds #include <linux/module.h> 37*1da177e4SLinus Torvalds #include <linux/config.h> 38*1da177e4SLinus Torvalds 39*1da177e4SLinus Torvalds #include <linux/fs.h> 40*1da177e4SLinus Torvalds #include <linux/errno.h> 41*1da177e4SLinus Torvalds #include <linux/miscdevice.h> 42*1da177e4SLinus Torvalds #include <linux/kernel.h> 43*1da177e4SLinus Torvalds #include <linux/major.h> 44*1da177e4SLinus Torvalds #include <linux/slab.h> 45*1da177e4SLinus Torvalds #include <linux/proc_fs.h> 46*1da177e4SLinus Torvalds #include <linux/seq_file.h> 47*1da177e4SLinus Torvalds #include <linux/devfs_fs_kernel.h> 48*1da177e4SLinus Torvalds #include <linux/stat.h> 49*1da177e4SLinus Torvalds #include <linux/init.h> 50*1da177e4SLinus Torvalds #include <linux/device.h> 51*1da177e4SLinus Torvalds #include <linux/tty.h> 52*1da177e4SLinus Torvalds #include <linux/kmod.h> 53*1da177e4SLinus Torvalds 54*1da177e4SLinus Torvalds /* 55*1da177e4SLinus Torvalds * Head entry for the doubly linked miscdevice list 56*1da177e4SLinus Torvalds */ 57*1da177e4SLinus Torvalds static LIST_HEAD(misc_list); 58*1da177e4SLinus Torvalds static DECLARE_MUTEX(misc_sem); 59*1da177e4SLinus Torvalds 60*1da177e4SLinus Torvalds /* 61*1da177e4SLinus Torvalds * Assigned numbers, used for dynamic minors 62*1da177e4SLinus Torvalds */ 63*1da177e4SLinus Torvalds #define DYNAMIC_MINORS 64 /* like dynamic majors */ 64*1da177e4SLinus Torvalds static unsigned char misc_minors[DYNAMIC_MINORS / 8]; 65*1da177e4SLinus Torvalds 66*1da177e4SLinus Torvalds extern int rtc_DP8570A_init(void); 67*1da177e4SLinus Torvalds extern int rtc_MK48T08_init(void); 68*1da177e4SLinus Torvalds extern int pmu_device_init(void); 69*1da177e4SLinus Torvalds extern int tosh_init(void); 70*1da177e4SLinus Torvalds extern int i8k_init(void); 71*1da177e4SLinus Torvalds 72*1da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 73*1da177e4SLinus Torvalds static void *misc_seq_start(struct seq_file *seq, loff_t *pos) 74*1da177e4SLinus Torvalds { 75*1da177e4SLinus Torvalds struct miscdevice *p; 76*1da177e4SLinus Torvalds loff_t off = 0; 77*1da177e4SLinus Torvalds 78*1da177e4SLinus Torvalds down(&misc_sem); 79*1da177e4SLinus Torvalds list_for_each_entry(p, &misc_list, list) { 80*1da177e4SLinus Torvalds if (*pos == off++) 81*1da177e4SLinus Torvalds return p; 82*1da177e4SLinus Torvalds } 83*1da177e4SLinus Torvalds return NULL; 84*1da177e4SLinus Torvalds } 85*1da177e4SLinus Torvalds 86*1da177e4SLinus Torvalds static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 87*1da177e4SLinus Torvalds { 88*1da177e4SLinus Torvalds struct list_head *n = ((struct miscdevice *)v)->list.next; 89*1da177e4SLinus Torvalds 90*1da177e4SLinus Torvalds ++*pos; 91*1da177e4SLinus Torvalds 92*1da177e4SLinus Torvalds return (n != &misc_list) ? list_entry(n, struct miscdevice, list) 93*1da177e4SLinus Torvalds : NULL; 94*1da177e4SLinus Torvalds } 95*1da177e4SLinus Torvalds 96*1da177e4SLinus Torvalds static void misc_seq_stop(struct seq_file *seq, void *v) 97*1da177e4SLinus Torvalds { 98*1da177e4SLinus Torvalds up(&misc_sem); 99*1da177e4SLinus Torvalds } 100*1da177e4SLinus Torvalds 101*1da177e4SLinus Torvalds static int misc_seq_show(struct seq_file *seq, void *v) 102*1da177e4SLinus Torvalds { 103*1da177e4SLinus Torvalds const struct miscdevice *p = v; 104*1da177e4SLinus Torvalds 105*1da177e4SLinus Torvalds seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); 106*1da177e4SLinus Torvalds return 0; 107*1da177e4SLinus Torvalds } 108*1da177e4SLinus Torvalds 109*1da177e4SLinus Torvalds 110*1da177e4SLinus Torvalds static struct seq_operations misc_seq_ops = { 111*1da177e4SLinus Torvalds .start = misc_seq_start, 112*1da177e4SLinus Torvalds .next = misc_seq_next, 113*1da177e4SLinus Torvalds .stop = misc_seq_stop, 114*1da177e4SLinus Torvalds .show = misc_seq_show, 115*1da177e4SLinus Torvalds }; 116*1da177e4SLinus Torvalds 117*1da177e4SLinus Torvalds static int misc_seq_open(struct inode *inode, struct file *file) 118*1da177e4SLinus Torvalds { 119*1da177e4SLinus Torvalds return seq_open(file, &misc_seq_ops); 120*1da177e4SLinus Torvalds } 121*1da177e4SLinus Torvalds 122*1da177e4SLinus Torvalds static struct file_operations misc_proc_fops = { 123*1da177e4SLinus Torvalds .owner = THIS_MODULE, 124*1da177e4SLinus Torvalds .open = misc_seq_open, 125*1da177e4SLinus Torvalds .read = seq_read, 126*1da177e4SLinus Torvalds .llseek = seq_lseek, 127*1da177e4SLinus Torvalds .release = seq_release, 128*1da177e4SLinus Torvalds }; 129*1da177e4SLinus Torvalds #endif 130*1da177e4SLinus Torvalds 131*1da177e4SLinus Torvalds static int misc_open(struct inode * inode, struct file * file) 132*1da177e4SLinus Torvalds { 133*1da177e4SLinus Torvalds int minor = iminor(inode); 134*1da177e4SLinus Torvalds struct miscdevice *c; 135*1da177e4SLinus Torvalds int err = -ENODEV; 136*1da177e4SLinus Torvalds struct file_operations *old_fops, *new_fops = NULL; 137*1da177e4SLinus Torvalds 138*1da177e4SLinus Torvalds down(&misc_sem); 139*1da177e4SLinus Torvalds 140*1da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 141*1da177e4SLinus Torvalds if (c->minor == minor) { 142*1da177e4SLinus Torvalds new_fops = fops_get(c->fops); 143*1da177e4SLinus Torvalds break; 144*1da177e4SLinus Torvalds } 145*1da177e4SLinus Torvalds } 146*1da177e4SLinus Torvalds 147*1da177e4SLinus Torvalds if (!new_fops) { 148*1da177e4SLinus Torvalds up(&misc_sem); 149*1da177e4SLinus Torvalds request_module("char-major-%d-%d", MISC_MAJOR, minor); 150*1da177e4SLinus Torvalds down(&misc_sem); 151*1da177e4SLinus Torvalds 152*1da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 153*1da177e4SLinus Torvalds if (c->minor == minor) { 154*1da177e4SLinus Torvalds new_fops = fops_get(c->fops); 155*1da177e4SLinus Torvalds break; 156*1da177e4SLinus Torvalds } 157*1da177e4SLinus Torvalds } 158*1da177e4SLinus Torvalds if (!new_fops) 159*1da177e4SLinus Torvalds goto fail; 160*1da177e4SLinus Torvalds } 161*1da177e4SLinus Torvalds 162*1da177e4SLinus Torvalds err = 0; 163*1da177e4SLinus Torvalds old_fops = file->f_op; 164*1da177e4SLinus Torvalds file->f_op = new_fops; 165*1da177e4SLinus Torvalds if (file->f_op->open) { 166*1da177e4SLinus Torvalds err=file->f_op->open(inode,file); 167*1da177e4SLinus Torvalds if (err) { 168*1da177e4SLinus Torvalds fops_put(file->f_op); 169*1da177e4SLinus Torvalds file->f_op = fops_get(old_fops); 170*1da177e4SLinus Torvalds } 171*1da177e4SLinus Torvalds } 172*1da177e4SLinus Torvalds fops_put(old_fops); 173*1da177e4SLinus Torvalds fail: 174*1da177e4SLinus Torvalds up(&misc_sem); 175*1da177e4SLinus Torvalds return err; 176*1da177e4SLinus Torvalds } 177*1da177e4SLinus Torvalds 178*1da177e4SLinus Torvalds /* 179*1da177e4SLinus Torvalds * TODO for 2.7: 180*1da177e4SLinus Torvalds * - add a struct class_device to struct miscdevice and make all usages of 181*1da177e4SLinus Torvalds * them dynamic. 182*1da177e4SLinus Torvalds */ 183*1da177e4SLinus Torvalds static struct class_simple *misc_class; 184*1da177e4SLinus Torvalds 185*1da177e4SLinus Torvalds static struct file_operations misc_fops = { 186*1da177e4SLinus Torvalds .owner = THIS_MODULE, 187*1da177e4SLinus Torvalds .open = misc_open, 188*1da177e4SLinus Torvalds }; 189*1da177e4SLinus Torvalds 190*1da177e4SLinus Torvalds 191*1da177e4SLinus Torvalds /** 192*1da177e4SLinus Torvalds * misc_register - register a miscellaneous device 193*1da177e4SLinus Torvalds * @misc: device structure 194*1da177e4SLinus Torvalds * 195*1da177e4SLinus Torvalds * Register a miscellaneous device with the kernel. If the minor 196*1da177e4SLinus Torvalds * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 197*1da177e4SLinus Torvalds * and placed in the minor field of the structure. For other cases 198*1da177e4SLinus Torvalds * the minor number requested is used. 199*1da177e4SLinus Torvalds * 200*1da177e4SLinus Torvalds * The structure passed is linked into the kernel and may not be 201*1da177e4SLinus Torvalds * destroyed until it has been unregistered. 202*1da177e4SLinus Torvalds * 203*1da177e4SLinus Torvalds * A zero is returned on success and a negative errno code for 204*1da177e4SLinus Torvalds * failure. 205*1da177e4SLinus Torvalds */ 206*1da177e4SLinus Torvalds 207*1da177e4SLinus Torvalds int misc_register(struct miscdevice * misc) 208*1da177e4SLinus Torvalds { 209*1da177e4SLinus Torvalds struct miscdevice *c; 210*1da177e4SLinus Torvalds dev_t dev; 211*1da177e4SLinus Torvalds int err; 212*1da177e4SLinus Torvalds 213*1da177e4SLinus Torvalds down(&misc_sem); 214*1da177e4SLinus Torvalds list_for_each_entry(c, &misc_list, list) { 215*1da177e4SLinus Torvalds if (c->minor == misc->minor) { 216*1da177e4SLinus Torvalds up(&misc_sem); 217*1da177e4SLinus Torvalds return -EBUSY; 218*1da177e4SLinus Torvalds } 219*1da177e4SLinus Torvalds } 220*1da177e4SLinus Torvalds 221*1da177e4SLinus Torvalds if (misc->minor == MISC_DYNAMIC_MINOR) { 222*1da177e4SLinus Torvalds int i = DYNAMIC_MINORS; 223*1da177e4SLinus Torvalds while (--i >= 0) 224*1da177e4SLinus Torvalds if ( (misc_minors[i>>3] & (1 << (i&7))) == 0) 225*1da177e4SLinus Torvalds break; 226*1da177e4SLinus Torvalds if (i<0) { 227*1da177e4SLinus Torvalds up(&misc_sem); 228*1da177e4SLinus Torvalds return -EBUSY; 229*1da177e4SLinus Torvalds } 230*1da177e4SLinus Torvalds misc->minor = i; 231*1da177e4SLinus Torvalds } 232*1da177e4SLinus Torvalds 233*1da177e4SLinus Torvalds if (misc->minor < DYNAMIC_MINORS) 234*1da177e4SLinus Torvalds misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); 235*1da177e4SLinus Torvalds if (misc->devfs_name[0] == '\0') { 236*1da177e4SLinus Torvalds snprintf(misc->devfs_name, sizeof(misc->devfs_name), 237*1da177e4SLinus Torvalds "misc/%s", misc->name); 238*1da177e4SLinus Torvalds } 239*1da177e4SLinus Torvalds dev = MKDEV(MISC_MAJOR, misc->minor); 240*1da177e4SLinus Torvalds 241*1da177e4SLinus Torvalds misc->class = class_simple_device_add(misc_class, dev, 242*1da177e4SLinus Torvalds misc->dev, misc->name); 243*1da177e4SLinus Torvalds if (IS_ERR(misc->class)) { 244*1da177e4SLinus Torvalds err = PTR_ERR(misc->class); 245*1da177e4SLinus Torvalds goto out; 246*1da177e4SLinus Torvalds } 247*1da177e4SLinus Torvalds 248*1da177e4SLinus Torvalds err = devfs_mk_cdev(dev, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, 249*1da177e4SLinus Torvalds misc->devfs_name); 250*1da177e4SLinus Torvalds if (err) { 251*1da177e4SLinus Torvalds class_simple_device_remove(dev); 252*1da177e4SLinus Torvalds goto out; 253*1da177e4SLinus Torvalds } 254*1da177e4SLinus Torvalds 255*1da177e4SLinus Torvalds /* 256*1da177e4SLinus Torvalds * Add it to the front, so that later devices can "override" 257*1da177e4SLinus Torvalds * earlier defaults 258*1da177e4SLinus Torvalds */ 259*1da177e4SLinus Torvalds list_add(&misc->list, &misc_list); 260*1da177e4SLinus Torvalds out: 261*1da177e4SLinus Torvalds up(&misc_sem); 262*1da177e4SLinus Torvalds return err; 263*1da177e4SLinus Torvalds } 264*1da177e4SLinus Torvalds 265*1da177e4SLinus Torvalds /** 266*1da177e4SLinus Torvalds * misc_deregister - unregister a miscellaneous device 267*1da177e4SLinus Torvalds * @misc: device to unregister 268*1da177e4SLinus Torvalds * 269*1da177e4SLinus Torvalds * Unregister a miscellaneous device that was previously 270*1da177e4SLinus Torvalds * successfully registered with misc_register(). Success 271*1da177e4SLinus Torvalds * is indicated by a zero return, a negative errno code 272*1da177e4SLinus Torvalds * indicates an error. 273*1da177e4SLinus Torvalds */ 274*1da177e4SLinus Torvalds 275*1da177e4SLinus Torvalds int misc_deregister(struct miscdevice * misc) 276*1da177e4SLinus Torvalds { 277*1da177e4SLinus Torvalds int i = misc->minor; 278*1da177e4SLinus Torvalds 279*1da177e4SLinus Torvalds if (list_empty(&misc->list)) 280*1da177e4SLinus Torvalds return -EINVAL; 281*1da177e4SLinus Torvalds 282*1da177e4SLinus Torvalds down(&misc_sem); 283*1da177e4SLinus Torvalds list_del(&misc->list); 284*1da177e4SLinus Torvalds class_simple_device_remove(MKDEV(MISC_MAJOR, misc->minor)); 285*1da177e4SLinus Torvalds devfs_remove(misc->devfs_name); 286*1da177e4SLinus Torvalds if (i < DYNAMIC_MINORS && i>0) { 287*1da177e4SLinus Torvalds misc_minors[i>>3] &= ~(1 << (misc->minor & 7)); 288*1da177e4SLinus Torvalds } 289*1da177e4SLinus Torvalds up(&misc_sem); 290*1da177e4SLinus Torvalds return 0; 291*1da177e4SLinus Torvalds } 292*1da177e4SLinus Torvalds 293*1da177e4SLinus Torvalds EXPORT_SYMBOL(misc_register); 294*1da177e4SLinus Torvalds EXPORT_SYMBOL(misc_deregister); 295*1da177e4SLinus Torvalds 296*1da177e4SLinus Torvalds static int __init misc_init(void) 297*1da177e4SLinus Torvalds { 298*1da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 299*1da177e4SLinus Torvalds struct proc_dir_entry *ent; 300*1da177e4SLinus Torvalds 301*1da177e4SLinus Torvalds ent = create_proc_entry("misc", 0, NULL); 302*1da177e4SLinus Torvalds if (ent) 303*1da177e4SLinus Torvalds ent->proc_fops = &misc_proc_fops; 304*1da177e4SLinus Torvalds #endif 305*1da177e4SLinus Torvalds misc_class = class_simple_create(THIS_MODULE, "misc"); 306*1da177e4SLinus Torvalds if (IS_ERR(misc_class)) 307*1da177e4SLinus Torvalds return PTR_ERR(misc_class); 308*1da177e4SLinus Torvalds #ifdef CONFIG_MVME16x 309*1da177e4SLinus Torvalds rtc_MK48T08_init(); 310*1da177e4SLinus Torvalds #endif 311*1da177e4SLinus Torvalds #ifdef CONFIG_BVME6000 312*1da177e4SLinus Torvalds rtc_DP8570A_init(); 313*1da177e4SLinus Torvalds #endif 314*1da177e4SLinus Torvalds #ifdef CONFIG_PMAC_PBOOK 315*1da177e4SLinus Torvalds pmu_device_init(); 316*1da177e4SLinus Torvalds #endif 317*1da177e4SLinus Torvalds #ifdef CONFIG_TOSHIBA 318*1da177e4SLinus Torvalds tosh_init(); 319*1da177e4SLinus Torvalds #endif 320*1da177e4SLinus Torvalds #ifdef CONFIG_I8K 321*1da177e4SLinus Torvalds i8k_init(); 322*1da177e4SLinus Torvalds #endif 323*1da177e4SLinus Torvalds if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { 324*1da177e4SLinus Torvalds printk("unable to get major %d for misc devices\n", 325*1da177e4SLinus Torvalds MISC_MAJOR); 326*1da177e4SLinus Torvalds class_simple_destroy(misc_class); 327*1da177e4SLinus Torvalds return -EIO; 328*1da177e4SLinus Torvalds } 329*1da177e4SLinus Torvalds return 0; 330*1da177e4SLinus Torvalds } 331*1da177e4SLinus Torvalds subsys_initcall(misc_init); 332