diff options
Diffstat (limited to 'drivers/usb/core/file.c')
| -rw-r--r-- | drivers/usb/core/file.c | 210 |
1 files changed, 117 insertions, 93 deletions
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index e695308095a..ea337a718cc 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -1,5 +1,5 @@ /* - * drivers/usb/file.c + * drivers/usb/core/file.c * * (C) Copyright Linus Torvalds 1999 * (C) Copyright Johannes Erdfelt 1999-2001 @@ -8,92 +8,124 @@ * (C) Copyright Deti Fliegl 1999 (new USB architecture) * (C) Copyright Randy Dunlap 2000 * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, - more docs, etc) + * more docs, etc) * (C) Copyright Yggdrasil Computing, Inc. 2000 * (usb_device_id matching changes by Adam J. Richter) * (C) Copyright Greg Kroah-Hartman 2002-2003 * */ -#include <linux/config.h> #include <linux/module.h> -#include <linux/spinlock.h> #include <linux/errno.h> - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif +#include <linux/rwsem.h> +#include <linux/slab.h> #include <linux/usb.h> #include "usb.h" #define MAX_USB_MINORS 256 -static struct file_operations *usb_minors[MAX_USB_MINORS]; -static DEFINE_SPINLOCK(minor_lock); +static const struct file_operations *usb_minors[MAX_USB_MINORS]; +static DECLARE_RWSEM(minor_rwsem); -static int usb_open(struct inode * inode, struct file * file) +static int usb_open(struct inode *inode, struct file *file) { - int minor = iminor(inode); - struct file_operations *c; int err = -ENODEV; - struct file_operations *old_fops, *new_fops = NULL; + const struct file_operations *new_fops; - spin_lock (&minor_lock); - c = usb_minors[minor]; + down_read(&minor_rwsem); + new_fops = fops_get(usb_minors[iminor(inode)]); - if (!c || !(new_fops = fops_get(c))) { - spin_unlock(&minor_lock); - return err; - } - spin_unlock(&minor_lock); + if (!new_fops) + goto done; - old_fops = file->f_op; - file->f_op = new_fops; + replace_fops(file, new_fops); /* Curiouser and curiouser... NULL ->open() as "no device" ? */ if (file->f_op->open) - err = file->f_op->open(inode,file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); + err = file->f_op->open(inode, file); + done: + up_read(&minor_rwsem); return err; } -static struct file_operations usb_fops = { +static const struct file_operations usb_fops = { .owner = THIS_MODULE, .open = usb_open, + .llseek = noop_llseek, }; -static struct class *usb_class; +static struct usb_class { + struct kref kref; + struct class *class; +} *usb_class; -int usb_major_init(void) +static char *usb_devnode(struct device *dev, umode_t *mode) { - int error; + struct usb_class_driver *drv; - error = register_chrdev(USB_MAJOR, "usb", &usb_fops); - if (error) { - err("unable to get major %d for usb devices", USB_MAJOR); - goto out; + drv = dev_get_drvdata(dev); + if (!drv || !drv->devnode) + return NULL; + return drv->devnode(dev, mode); +} + +static int init_usb_class(void) +{ + int result = 0; + + if (usb_class != NULL) { + kref_get(&usb_class->kref); + goto exit; + } + + usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL); + if (!usb_class) { + result = -ENOMEM; + goto exit; } - usb_class = class_create(THIS_MODULE, "usb"); - if (IS_ERR(usb_class)) { - error = PTR_ERR(usb_class); - err("class_create failed for usb devices"); - unregister_chrdev(USB_MAJOR, "usb"); - goto out; + kref_init(&usb_class->kref); + usb_class->class = class_create(THIS_MODULE, "usbmisc"); + if (IS_ERR(usb_class->class)) { + result = PTR_ERR(usb_class->class); + printk(KERN_ERR "class_create failed for usb devices\n"); + kfree(usb_class); + usb_class = NULL; + goto exit; } + usb_class->class->devnode = usb_devnode; + +exit: + return result; +} + +static void release_usb_class(struct kref *kref) +{ + /* Ok, we cheat as we know we only have one usb_class */ + class_destroy(usb_class->class); + kfree(usb_class); + usb_class = NULL; +} + +static void destroy_usb_class(void) +{ + if (usb_class) + kref_put(&usb_class->kref, release_usb_class); +} + +int usb_major_init(void) +{ + int error; + + error = register_chrdev(USB_MAJOR, "usb", &usb_fops); + if (error) + printk(KERN_ERR "Unable to get major %d for usb devices\n", + USB_MAJOR); -out: return error; } void usb_major_cleanup(void) { - class_destroy(usb_class); unregister_chrdev(USB_MAJOR, "usb"); } @@ -113,70 +145,71 @@ void usb_major_cleanup(void) * usb_deregister_dev() must be called when the driver is done with * the minor numbers given out by this function. * - * Returns -EINVAL if something bad happens with trying to register a + * Return: -EINVAL if something bad happens with trying to register a * device, and 0 on success. */ int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { - int retval = -EINVAL; + int retval; int minor_base = class_driver->minor_base; - int minor = 0; - char name[BUS_ID_SIZE]; + int minor; + char name[20]; char *temp; #ifdef CONFIG_USB_DYNAMIC_MINORS - /* + /* * We don't care what the device tries to start at, we want to start * at zero to pack the devices into the smallest available space with * no holes in the minor range. */ minor_base = 0; #endif - intf->minor = -1; - - dbg ("looking for a minor, starting at %d", minor_base); if (class_driver->fops == NULL) - goto exit; + return -EINVAL; + if (intf->minor >= 0) + return -EADDRINUSE; + + retval = init_usb_class(); + if (retval) + return retval; - spin_lock (&minor_lock); + dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base); + + down_write(&minor_rwsem); for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { if (usb_minors[minor]) continue; usb_minors[minor] = class_driver->fops; - - retval = 0; + intf->minor = minor; break; } - spin_unlock (&minor_lock); - - if (retval) - goto exit; - - intf->minor = minor; + up_write(&minor_rwsem); + if (intf->minor < 0) + return -EXFULL; /* create a usb class device for this usb interface */ - snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base); + snprintf(name, sizeof(name), class_driver->name, minor - minor_base); temp = strrchr(name, '/'); - if (temp && (temp[1] != 0x00)) + if (temp && (temp[1] != '\0')) ++temp; else temp = name; - intf->class_dev = class_device_create(usb_class, NULL, - MKDEV(USB_MAJOR, minor), - &intf->dev, "%s", temp); - if (IS_ERR(intf->class_dev)) { - spin_lock (&minor_lock); - usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); - retval = PTR_ERR(intf->class_dev); + intf->usb_dev = device_create(usb_class->class, &intf->dev, + MKDEV(USB_MAJOR, minor), class_driver, + "%s", temp); + if (IS_ERR(intf->usb_dev)) { + down_write(&minor_rwsem); + usb_minors[minor] = NULL; + intf->minor = -1; + up_write(&minor_rwsem); + retval = PTR_ERR(intf->usb_dev); } -exit: return retval; } -EXPORT_SYMBOL(usb_register_dev); +EXPORT_SYMBOL_GPL(usb_register_dev); /** * usb_deregister_dev - deregister a USB device's dynamic minor. @@ -195,27 +228,18 @@ EXPORT_SYMBOL(usb_register_dev); void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { - int minor_base = class_driver->minor_base; - char name[BUS_ID_SIZE]; - -#ifdef CONFIG_USB_DYNAMIC_MINORS - minor_base = 0; -#endif - if (intf->minor == -1) return; - dbg ("removing %d minor", intf->minor); + dev_dbg(&intf->dev, "removing %d minor\n", intf->minor); - spin_lock (&minor_lock); + down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); + up_write(&minor_rwsem); - snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); - class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor)); - intf->class_dev = NULL; + device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); + intf->usb_dev = NULL; intf->minor = -1; + destroy_usb_class(); } -EXPORT_SYMBOL(usb_deregister_dev); - - +EXPORT_SYMBOL_GPL(usb_deregister_dev); |
