diff options
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/devices.c | 22 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 62 | ||||
| -rw-r--r-- | drivers/usb/core/drivers.c | 6 | ||||
| -rw-r--r-- | drivers/usb/core/file.c | 11 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 10 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 14 | ||||
| -rw-r--r-- | drivers/usb/core/inode.c | 38 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 195 |
8 files changed, 223 insertions, 135 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index c15f5f4e60b2..e701a4975071 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -152,8 +152,8 @@ static const struct class_info clas_info[] = void usbdevfs_conn_disc_event(void) { - wake_up(&deviceconndiscwq); conndiscevcnt++; + wake_up(&deviceconndiscwq); } static const char *class_decode(const int class) @@ -239,6 +239,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct if (start > end) return start; + lock_kernel(); /* driver might be unloaded */ start += sprintf(start, format_iface, desc->bInterfaceNumber, desc->bAlternateSetting, @@ -248,6 +249,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct desc->bInterfaceSubClass, desc->bInterfaceProtocol, iface->driver ? iface->driver->name : "(none)"); + unlock_kernel(); return start; } @@ -597,6 +599,13 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct unlock_kernel(); return POLLIN; } + + /* we may have dropped BKL - need to check for having lost the race */ + if (file->private_data) { + kfree(st); + goto lost_race; + } + /* * need to prevent the module from being unloaded, since * proc_unregister does not call the release method and @@ -606,6 +615,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct file->private_data = st; mask = POLLIN; } +lost_race: if (file->f_mode & FMODE_READ) poll_wait(file, &deviceconndiscwq, wait); if (st->lastev != conndiscevcnt) @@ -656,9 +666,9 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) } struct file_operations usbdevfs_devices_fops = { - llseek: usb_device_lseek, - read: usb_device_read, - poll: usb_device_poll, - open: usb_device_open, - release: usb_device_release, + .llseek = usb_device_lseek, + .read = usb_device_read, + .poll = usb_device_poll, + .open = usb_device_open, + .release = usb_device_release, }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b7c95b292a48..e80a3ed170f8 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -324,9 +324,9 @@ static void driver_disconnect(struct usb_device *dev, void *context) } struct usb_driver usbdevfs_driver = { - name: "usbfs", - probe: driver_probe, - disconnect: driver_disconnect, + .name = "usbfs", + .probe = driver_probe, + .disconnect = driver_disconnect, }; static int claimintf(struct dev_state *ps, unsigned int intf) @@ -361,14 +361,14 @@ static int releaseintf(struct dev_state *ps, unsigned int intf) if (intf >= 8*sizeof(ps->ifclaimed)) return -EINVAL; err = -EINVAL; - lock_kernel(); dev = ps->dev; + down(&dev->serialize); if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { iface = &dev->actconfig->interface[intf]; usb_driver_release_interface(&usbdevfs_driver, iface); err = 0; } - unlock_kernel(); + up(&dev->serialize); return err; } @@ -722,14 +722,11 @@ static int proc_resetdevice(struct dev_state *ps) if (test_bit(i, &ps->ifclaimed)) continue; - if (intf->driver) { - const struct usb_device_id *id; - down(&intf->driver->serialize); - intf->driver->disconnect(ps->dev, intf->private_data); - id = usb_match_id(ps->dev,intf,intf->driver->id_table); - intf->driver->probe(ps->dev, i, id); - up(&intf->driver->serialize); + lock_kernel(); + if (intf->driver && ps->dev) { + usb_bind_driver(intf->driver,ps->dev, i); } + unlock_kernel(); } return 0; @@ -1092,16 +1089,17 @@ static int proc_ioctl (struct dev_state *ps, void *arg) /* disconnect kernel driver from interface, leaving it unbound. */ case USBDEVFS_DISCONNECT: + /* this function is voodoo. without locking it is a maybe thing */ + lock_kernel(); driver = ifp->driver; if (driver) { - down (&driver->serialize); dbg ("disconnect '%s' from dev %d interface %d", driver->name, ps->dev->devnum, ctrl.ifno); - driver->disconnect (ps->dev, ifp->private_data); + usb_unbind_driver(ps->dev, ifp); usb_driver_release_interface (driver, ifp); - up (&driver->serialize); } else retval = -EINVAL; + unlock_kernel(); break; /* let kernel drivers try to (re)bind to the interface */ @@ -1111,18 +1109,28 @@ static int proc_ioctl (struct dev_state *ps, void *arg) /* talk directly to the interface's driver */ default: + lock_kernel(); /* against module unload */ driver = ifp->driver; - if (driver == 0 || driver->ioctl == 0) - retval = -ENOSYS; - else { - if (ifp->driver->owner) + if (driver == 0 || driver->ioctl == 0) { + unlock_kernel(); + retval = -ENOSYS; + } else { + if (ifp->driver->owner) { __MOD_INC_USE_COUNT(ifp->driver->owner); + unlock_kernel(); + } /* ifno might usefully be passed ... */ retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf); /* size = min_t(int, size, retval)? */ - if (ifp->driver->owner) + if (ifp->driver->owner) { __MOD_DEC_USE_COUNT(ifp->driver->owner); + } else { + unlock_kernel(); + } } + + if (retval == -ENOIOCTLCMD) + retval = -ENOTTY; } /* cleanup and return */ @@ -1139,7 +1147,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg) static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dev_state *ps = (struct dev_state *)file->private_data; - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; if (!(file->f_mode & FMODE_WRITE)) return -EPERM; @@ -1248,10 +1256,10 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai } struct file_operations usbdevfs_device_file_operations = { - llseek: usbdev_lseek, - read: usbdev_read, - poll: usbdev_poll, - ioctl: usbdev_ioctl, - open: usbdev_open, - release: usbdev_release, + .llseek = usbdev_lseek, + .read = usbdev_read, + .poll = usbdev_poll, + .ioctl = usbdev_ioctl, + .open = usbdev_open, + .release = usbdev_release, }; diff --git a/drivers/usb/core/drivers.c b/drivers/usb/core/drivers.c index 3419792c7fb6..22dc21354868 100644 --- a/drivers/usb/core/drivers.c +++ b/drivers/usb/core/drivers.c @@ -66,6 +66,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff start = page; end = page + (PAGE_SIZE - 100); pos = *ppos; + lock_kernel(); /* else drivers might be unloaded */ for (; tmp != &usb_driver_list; tmp = tmp->next) { struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); int minor = driver->fops ? driver->minor : -1; @@ -80,6 +81,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff break; } } + unlock_kernel(); if (start == page) start += sprintf(start, "(none)\n"); len = start - page; @@ -120,6 +122,6 @@ static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig) } struct file_operations usbdevfs_drivers_fops = { - llseek: usb_driver_lseek, - read: usb_driver_read, + .llseek = usb_driver_lseek, + .read = usb_driver_read, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index eba43f1d84e5..803fb19e5ad2 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -44,10 +44,13 @@ static int usb_open(struct inode * inode, struct file * file) spin_lock (&minor_lock); c = usb_minors[minor]; - spin_unlock (&minor_lock); - if (!c || !(new_fops = fops_get(c))) + if (!c || !(new_fops = fops_get(c))) { + spin_unlock(&minor_lock); return err; + } + spin_unlock(&minor_lock); + old_fops = file->f_op; file->f_op = new_fops; /* Curiouser and curiouser... NULL ->open() as "no device" ? */ @@ -62,8 +65,8 @@ static int usb_open(struct inode * inode, struct file * file) } static struct file_operations usb_fops = { - owner: THIS_MODULE, - open: usb_open, + .owner = THIS_MODULE, + .open = usb_open, }; int usb_major_init(void) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 15a2c346b4af..05bb930ea5fd 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1240,11 +1240,11 @@ static int hcd_free_dev (struct usb_device *udev) * bus glue for non-PCI system busses will need to use this. */ struct usb_operations usb_hcd_operations = { - allocate: hcd_alloc_dev, - get_frame_number: hcd_get_frame_number, - submit_urb: hcd_submit_urb, - unlink_urb: hcd_unlink_urb, - deallocate: hcd_free_dev, + .allocate = hcd_alloc_dev, + .get_frame_number = hcd_get_frame_number, + .submit_urb = hcd_submit_urb, + .unlink_urb = hcd_unlink_urb, + .deallocate = hcd_free_dev, }; EXPORT_SYMBOL (usb_hcd_operations); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ad1422fabdfa..f80a98621d26 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1046,8 +1046,6 @@ static void usb_hub_events(void) static int usb_hub_thread(void *__hub) { - lock_kernel(); - /* * This thread doesn't need any user-level access, * so get rid of all our resources @@ -1067,8 +1065,6 @@ static int usb_hub_thread(void *__hub) } while (!signal_pending(current)); dbg("usb_hub_thread exiting"); - - unlock_kernel(); complete_and_exit(&khubd_exited, 0); } @@ -1083,11 +1079,11 @@ static struct usb_device_id hub_id_table [] = { MODULE_DEVICE_TABLE (usb, hub_id_table); static struct usb_driver hub_driver = { - name: "hub", - probe: hub_probe, - ioctl: hub_ioctl, - disconnect: hub_disconnect, - id_table: hub_id_table, + .name = "hub", + .probe = hub_probe, + .ioctl = hub_ioctl, + .disconnect = hub_disconnect, + .id_table = hub_id_table, }; /* diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 08d08e3516e8..ffb7e6e13284 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -284,23 +284,23 @@ static int default_open (struct inode *inode, struct file *filp) } static struct file_operations default_file_operations = { - read: default_read_file, - write: default_write_file, - open: default_open, - llseek: default_file_lseek, + .read = default_read_file, + .write = default_write_file, + .open = default_open, + .llseek = default_file_lseek, }; static struct inode_operations usbfs_dir_inode_operations = { - create: usbfs_create, - lookup: simple_lookup, - unlink: usbfs_unlink, - mkdir: usbfs_mkdir, - rmdir: usbfs_rmdir, + .create = usbfs_create, + .lookup = simple_lookup, + .unlink = usbfs_unlink, + .mkdir = usbfs_mkdir, + .rmdir = usbfs_rmdir, }; static struct super_operations usbfs_ops = { - statfs: simple_statfs, - drop_inode: generic_delete_inode, + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, }; static int usbfs_fill_super(struct super_block *sb, void *data, int silent) @@ -468,17 +468,17 @@ static struct super_block *usb_get_sb(struct file_system_type *fs_type, } static struct file_system_type usbdevice_fs_type = { - owner: THIS_MODULE, - name: "usbdevfs", - get_sb: usb_get_sb, - kill_sb: kill_anon_super, + .owner = THIS_MODULE, + .name = "usbdevfs", + .get_sb = usb_get_sb, + .kill_sb = kill_anon_super, }; static struct file_system_type usb_fs_type = { - owner: THIS_MODULE, - name: "usbfs", - get_sb: usb_get_sb, - kill_sb: kill_anon_super, + .owner = THIS_MODULE, + .name = "usbfs", + .get_sb = usb_get_sb, + .kill_sb = kill_anon_super, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 55c9e8955d24..ed2e03089e57 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -32,6 +32,7 @@ #include <linux/init.h> #include <linux/spinlock.h> #include <linux/errno.h> +#include <linux/smp_lock.h> #ifdef CONFIG_USB_DEBUG #define DEBUG @@ -117,6 +118,108 @@ void usb_scan_devices(void) up (&usb_bus_list_lock); } +/** + * usb_unbind_driver - disconnects a driver from a device + * @device: usb device to be disconnected + * @intf: interface of the device to be disconnected + * Context: BKL held + * + * Handles module usage count correctly + */ + +void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf) +{ + struct usb_driver *driver; + void *priv; + int m; + + + driver = intf->driver; + priv = intf->private_data; + + if (!driver) + return; + + /* as soon as we increase the module use count we drop the BKL + before that we must not sleep */ + if (driver->owner) { + m = try_inc_mod_count(driver->owner); + if (m == 0) { + err("Dieing driver still bound to device.\n"); + return; + } + unlock_kernel(); + } + down(&driver->serialize); /* if we sleep here on an umanaged driver + the holder of the lock guards against + module unload */ + + driver->disconnect(device, priv); + + up(&driver->serialize); + if (driver->owner) { + lock_kernel(); + __MOD_DEC_USE_COUNT(driver->owner); + } +} + +/** + * usb_bind_driver - connect a driver to a device's interface + * @driver: device driver to be bound to a devices interface + * @dev: device to be bound + * @ifnum: index number of the interface to be used + * + * Does a save binding of a driver to a device's interface + * Returns a pointer to the drivers private description of the binding + */ + +void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum) +{ + int i,m; + void *private = NULL; + const struct usb_device_id *id; + struct usb_interface *interface; + + if (driver->owner) { + m = try_inc_mod_count(driver->owner); + if (m == 0) + return NULL; /* this horse is dead - don't ride*/ + unlock_kernel(); + } + + interface = &dev->actconfig->interface[ifnum]; + + id = driver->id_table; + /* new style driver? */ + if (id) { + for (i = 0; i < interface->num_altsetting; i++) { + interface->act_altsetting = i; + id = usb_match_id(dev, interface, id); + if (id) { + down(&driver->serialize); + private = driver->probe(dev,ifnum,id); + up(&driver->serialize); + if (private != NULL) + break; + } + } + + /* if driver not bound, leave defaults unchanged */ + if (private == NULL) + interface->act_altsetting = 0; + } else { /* "old style" driver */ + down(&driver->serialize); + private = driver->probe(dev, ifnum, NULL); + up(&driver->serialize); + } + if (driver->owner) { + lock_kernel(); + __MOD_DEC_USE_COUNT(driver->owner); + } + + return private; +} + /* * This function is part of a depth-first search down the device tree, * removing any instances of a device driver. @@ -136,18 +239,12 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) if (!dev->actconfig) return; - + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; - + if (interface->driver == driver) { - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - down(&driver->serialize); - driver->disconnect(dev, interface->private_data); - up(&driver->serialize); - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); + usb_unbind_driver(dev, interface); /* if driver->disconnect didn't release the interface */ if (interface->driver) usb_driver_release_interface(driver, interface); @@ -163,7 +260,7 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) /** * usb_deregister - unregister a USB driver * @driver: USB operations of the driver to unregister - * Context: !in_interrupt () + * Context: !in_interrupt (), must be called with BKL held * * Unlinks the specified driver from the internal USB driver list. * @@ -528,9 +625,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) struct list_head *tmp; struct usb_interface *interface; void *private; - const struct usb_device_id *id; struct usb_driver *driver; - int i; if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { err("bad find_interface_driver params"); @@ -545,37 +640,12 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) goto out_err; private = NULL; + lock_kernel(); for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) { driver = list_entry(tmp, struct usb_driver, driver_list); tmp = tmp->next; - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - id = driver->id_table; - /* new style driver? */ - if (id) { - for (i = 0; i < interface->num_altsetting; i++) { - interface->act_altsetting = i; - id = usb_match_id(dev, interface, id); - if (id) { - down(&driver->serialize); - private = driver->probe(dev,ifnum,id); - up(&driver->serialize); - if (private != NULL) - break; - } - } - - /* if driver not bound, leave defaults unchanged */ - if (private == NULL) - interface->act_altsetting = 0; - } else { /* "old style" driver */ - down(&driver->serialize); - private = driver->probe(dev, ifnum, NULL); - up(&driver->serialize); - } - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); + private = usb_bind_driver(driver, dev, ifnum); /* probe() may have changed the config on us */ interface = dev->actconfig->interface + ifnum; @@ -583,9 +653,11 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) if (private) { usb_driver_claim_interface(driver, interface, private); up(&dev->serialize); + unlock_kernel(); return 0; } } + unlock_kernel(); out_err: up(&dev->serialize); @@ -764,9 +836,9 @@ show_config (struct device *dev, char *buf, size_t count, loff_t off) return sprintf (buf, "%u\n", udev->actconfig->bConfigurationValue); } static struct driver_file_entry usb_config_entry = { - name: "configuration", - mode: S_IRUGO, - show: show_config, + .name = "configuration", + .mode = S_IRUGO, + .show = show_config, }; /* interfaces have one current setting; alternates @@ -783,9 +855,9 @@ show_altsetting (struct device *dev, char *buf, size_t count, loff_t off) return sprintf (buf, "%u\n", interface->altsetting->bAlternateSetting); } static struct driver_file_entry usb_altsetting_entry = { - name: "altsetting", - mode: S_IRUGO, - show: show_altsetting, + .name = "altsetting", + .mode = S_IRUGO, + .show = show_altsetting, }; /* product driverfs file */ @@ -804,9 +876,9 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t return len+1; } static struct driver_file_entry usb_product_entry = { - name: "product", - mode: S_IRUGO, - show: show_product, + .name = "product", + .mode = S_IRUGO, + .show = show_product, }; /* manufacturer driverfs file */ @@ -826,9 +898,9 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off) return len+1; } static struct driver_file_entry usb_manufacturer_entry = { - name: "manufacturer", - mode: S_IRUGO, - show: show_manufacturer, + .name = "manufacturer", + .mode = S_IRUGO, + .show = show_manufacturer, }; /* serial number driverfs file */ @@ -848,9 +920,9 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off) return len+1; } static struct driver_file_entry usb_serial_entry = { - name: "serial", - mode: S_IRUGO, - show: show_serial, + .name = "serial", + .mode = S_IRUGO, + .show = show_serial, }; /* @@ -1121,27 +1193,22 @@ void usb_disconnect(struct usb_device **pdev) info("USB disconnect on device %d", dev->devnum); + lock_kernel(); if (dev->actconfig) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_driver *driver = interface->driver; if (driver) { - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - down(&driver->serialize); - driver->disconnect(dev, interface->private_data); - up(&driver->serialize); + usb_unbind_driver(dev, interface); /* if driver->disconnect didn't release the interface */ if (interface->driver) usb_driver_release_interface(driver, interface); - /* we don't need the driver any longer */ - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); } /* remove our device node for this interface */ put_device(&interface->dev); } } + unlock_kernel(); /* Free up all the children.. */ for (i = 0; i < USB_MAXCHILDREN; i++) { @@ -1416,7 +1483,7 @@ struct list_head *usb_bus_get_list(void) #endif struct bus_type usb_bus_type = { - name: "usb", + .name = "usb", }; /* @@ -1475,6 +1542,8 @@ EXPORT_SYMBOL(usb_new_device); EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_disconnect); +EXPORT_SYMBOL(usb_bind_driver); +EXPORT_SYMBOL(usb_unbind_driver); EXPORT_SYMBOL(__usb_get_extra_descriptor); |
