diff options
Diffstat (limited to 'drivers/usb/core/usb.c')
-rw-r--r-- | drivers/usb/core/usb.c | 462 |
1 files changed, 24 insertions, 438 deletions
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e80ef946782..56a3520863a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -32,7 +32,6 @@ #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/smp_lock.h> -#include <linux/rwsem.h> #include <linux/usb.h> #include <asm/io.h> @@ -47,165 +46,7 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ - /* Not honored on modular build */ -static DECLARE_RWSEM(usb_all_devices_rwsem); - - -static int generic_probe (struct device *dev) -{ - return 0; -} -static int generic_remove (struct device *dev) -{ - struct usb_device *udev = to_usb_device(dev); - - /* if this is only an unbind, not a physical disconnect, then - * unconfigure the device */ - if (udev->state == USB_STATE_CONFIGURED) - usb_set_configuration(udev, 0); - - /* in case the call failed or the device was suspended */ - if (udev->state >= USB_STATE_CONFIGURED) - usb_disable_device(udev, 0); - return 0; -} - -static struct device_driver usb_generic_driver = { - .owner = THIS_MODULE, - .name = "usb", - .bus = &usb_bus_type, - .probe = generic_probe, - .remove = generic_remove, -}; - -static int usb_generic_driver_data; - -/* called from driver core with usb_bus_type.subsys writelock */ -static int usb_probe_interface(struct device *dev) -{ - struct usb_interface * intf = to_usb_interface(dev); - struct usb_driver * driver = to_usb_driver(dev->driver); - const struct usb_device_id *id; - int error = -ENODEV; - - dev_dbg(dev, "%s\n", __FUNCTION__); - - if (!driver->probe) - return error; - /* FIXME we'd much prefer to just resume it ... */ - if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - - id = usb_match_id (intf, driver->id_table); - if (id) { - dev_dbg (dev, "%s - got id\n", __FUNCTION__); - - /* Interface "power state" doesn't correspond to any hardware - * state whatsoever. We use it to record when it's bound to - * a driver that may start I/0: it's not frozen/quiesced. - */ - mark_active(intf); - intf->condition = USB_INTERFACE_BINDING; - error = driver->probe (intf, id); - if (error) { - mark_quiesced(intf); - intf->condition = USB_INTERFACE_UNBOUND; - } else - intf->condition = USB_INTERFACE_BOUND; - } - - return error; -} - -/* called from driver core with usb_bus_type.subsys writelock */ -static int usb_unbind_interface(struct device *dev) -{ - struct usb_interface *intf = to_usb_interface(dev); - struct usb_driver *driver = to_usb_driver(intf->dev.driver); - - intf->condition = USB_INTERFACE_UNBINDING; - - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); - - if (driver && driver->disconnect) - driver->disconnect(intf); - - /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); - usb_set_intfdata(intf, NULL); - intf->condition = USB_INTERFACE_UNBOUND; - mark_quiesced(intf); - - return 0; -} - -/** - * usb_register - register a USB driver - * @new_driver: USB operations for the driver - * - * Registers a USB driver with the USB core. The list of unattached - * interfaces will be rescanned whenever a new driver is added, allowing - * the new driver to attach to any recognized devices. - * Returns a negative error code on failure and 0 on success. - * - * NOTE: if you want your driver to use the USB major number, you must call - * usb_register_dev() to enable that functionality. This function no longer - * takes care of that. - */ -int usb_register(struct usb_driver *new_driver) -{ - int retval = 0; - - if (nousb) - return -ENODEV; - - new_driver->driver.name = (char *)new_driver->name; - new_driver->driver.bus = &usb_bus_type; - new_driver->driver.probe = usb_probe_interface; - new_driver->driver.remove = usb_unbind_interface; - new_driver->driver.owner = new_driver->owner; - - usb_lock_all_devices(); - retval = driver_register(&new_driver->driver); - usb_unlock_all_devices(); - - if (!retval) { - pr_info("%s: registered new driver %s\n", - usbcore_name, new_driver->name); - usbfs_update_special(); - } else { - printk(KERN_ERR "%s: error %d registering driver %s\n", - usbcore_name, retval, new_driver->name); - } - - return retval; -} - -/** - * usb_deregister - unregister a USB driver - * @driver: USB operations of the driver to unregister - * Context: must be able to sleep - * - * Unlinks the specified driver from the internal USB driver list. - * - * NOTE: If you called usb_register_dev(), you still need to call - * usb_deregister_dev() to clean up your driver's allocated minor numbers, - * this * call will no longer do it for you. - */ -void usb_deregister(struct usb_driver *driver) -{ - pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); - - usb_lock_all_devices(); - driver_unregister (&driver->driver); - usb_unlock_all_devices(); - - usbfs_update_special(); -} /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -351,152 +192,23 @@ void usb_driver_release_interface(struct usb_driver *driver, iface->condition = USB_INTERFACE_UNBOUND; mark_quiesced(iface); } - -/** - * usb_match_id - find first usb_device_id matching device or interface - * @interface: the interface of interest - * @id: array of usb_device_id structures, terminated by zero entry - * - * usb_match_id searches an array of usb_device_id's and returns - * the first one matching the device or interface, or null. - * This is used when binding (or rebinding) a driver to an interface. - * Most USB device drivers will use this indirectly, through the usb core, - * but some layered driver frameworks use it directly. - * These device tables are exported with MODULE_DEVICE_TABLE, through - * modutils and "modules.usbmap", to support the driver loading - * functionality of USB hotplugging. - * - * What Matches: - * - * The "match_flags" element in a usb_device_id controls which - * members are used. If the corresponding bit is set, the - * value in the device_id must match its corresponding member - * in the device or interface descriptor, or else the device_id - * does not match. - * - * "driver_info" is normally used only by device drivers, - * but you can create a wildcard "matches anything" usb_device_id - * as a driver's "modules.usbmap" entry if you provide an id with - * only a nonzero "driver_info" field. If you do this, the USB device - * driver's probe() routine should use additional intelligence to - * decide whether to bind to the specified interface. - * - * What Makes Good usb_device_id Tables: - * - * The match algorithm is very simple, so that intelligence in - * driver selection must come from smart driver id records. - * Unless you have good reasons to use another selection policy, - * provide match elements only in related groups, and order match - * specifiers from specific to general. Use the macros provided - * for that purpose if you can. - * - * The most specific match specifiers use device descriptor - * data. These are commonly used with product-specific matches; - * the USB_DEVICE macro lets you provide vendor and product IDs, - * and you can also match against ranges of product revisions. - * These are widely used for devices with application or vendor - * specific bDeviceClass values. - * - * Matches based on device class/subclass/protocol specifications - * are slightly more general; use the USB_DEVICE_INFO macro, or - * its siblings. These are used with single-function devices - * where bDeviceClass doesn't specify that each interface has - * its own class. - * - * Matches based on interface class/subclass/protocol are the - * most general; they let drivers bind to any interface on a - * multiple-function device. Use the USB_INTERFACE_INFO - * macro, or its siblings, to match class-per-interface style - * devices (as recorded in bDeviceClass). - * - * Within those groups, remember that not all combinations are - * meaningful. For example, don't give a product version range - * without vendor and product IDs; or specify a protocol without - * its associated class and subclass. - */ -const struct usb_device_id * -usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) -{ - struct usb_host_interface *intf; - struct usb_device *dev; - - /* proc_connectinfo in devio.c may call us with id == NULL. */ - if (id == NULL) - return NULL; - - intf = interface->cur_altsetting; - dev = interface_to_usbdev(interface); - - /* It is important to check that id->driver_info is nonzero, - since an entry that is all zeroes except for a nonzero - id->driver_info is the way to create an entry that - indicates that the driver want to examine every - device and interface. */ - for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || - id->driver_info; id++) { - - if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && - id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && - id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) - continue; - - /* No need to test id->bcdDevice_lo != 0, since 0 is never - greater than any unsigned number. */ - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && - (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && - (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && - (id->bDeviceClass != dev->descriptor.bDeviceClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && - (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && - (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && - (id->bInterfaceClass != intf->desc.bInterfaceClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && - (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && - (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) - continue; - - return id; - } - - return NULL; -} - +struct find_interface_arg { + int minor; + struct usb_interface *interface; +}; static int __find_interface(struct device * dev, void * data) { - struct usb_interface ** ret = (struct usb_interface **)data; - struct usb_interface * intf = *ret; - int *minor = (int *)data; + struct find_interface_arg *arg = data; + struct usb_interface *intf; /* can't look at usb devices, only interfaces */ if (dev->driver == &usb_generic_driver) return 0; intf = to_usb_interface(dev); - if (intf->minor != -1 && intf->minor == *minor) { - *ret = intf; + if (intf->minor != -1 && intf->minor == arg->minor) { + arg->interface = intf; return 1; } return 0; @@ -513,35 +225,14 @@ static int __find_interface(struct device * dev, void * data) */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { - struct usb_interface *intf = (struct usb_interface *)(long)minor; - int ret; - - ret = driver_for_each_device(&drv->driver, NULL, &intf, __find_interface); - - return ret ? intf : NULL; -} - -static int usb_device_match (struct device *dev, struct device_driver *drv) -{ - struct usb_interface *intf; - struct usb_driver *usb_drv; - const struct usb_device_id *id; - - /* check for generic driver, which we don't match any device with */ - if (drv == &usb_generic_driver) - return 0; - - intf = to_usb_interface(dev); - usb_drv = to_usb_driver(drv); - - id = usb_match_id (intf, usb_drv->id_table); - if (id) - return 1; + struct find_interface_arg argb; - return 0; + argb.minor = minor; + argb.interface = NULL; + driver_for_each_device(&drv->driver, NULL, &argb, __find_interface); + return argb.interface; } - #ifdef CONFIG_HOTPLUG /* @@ -750,12 +441,11 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) /* hub driver sets up TT records */ } + dev->portnum = port1; dev->bus = bus; dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); - init_MUTEX(&dev->serialize); - return dev; } @@ -828,76 +518,21 @@ void usb_put_intf(struct usb_interface *intf) /* USB device locking * - * Although locking USB devices should be straightforward, it is - * complicated by the way the driver-model core works. When a new USB - * driver is registered or unregistered, the core will automatically - * probe or disconnect all matching interfaces on all USB devices while - * holding the USB subsystem writelock. There's no good way for us to - * tell which devices will be used or to lock them beforehand; our only - * option is to effectively lock all the USB devices. - * - * We do that by using a private rw-semaphore, usb_all_devices_rwsem. - * When locking an individual device you must first acquire the rwsem's - * readlock. When a driver is registered or unregistered the writelock - * must be held. These actions are encapsulated in the subroutines - * below, so all a driver needs to do is call usb_lock_device() and - * usb_unlock_device(). + * USB devices and interfaces are locked using the semaphore in their + * embedded struct device. The hub driver guarantees that whenever a + * device is connected or disconnected, drivers are called with the + * USB device locked as well as their particular interface. * * Complications arise when several devices are to be locked at the same * time. Only hub-aware drivers that are part of usbcore ever have to - * do this; nobody else needs to worry about it. The problem is that - * usb_lock_device() must not be called to lock a second device since it - * would acquire the rwsem's readlock reentrantly, leading to deadlock if - * another thread was waiting for the writelock. The solution is simple: - * - * When locking more than one device, call usb_lock_device() - * to lock the first one. Lock the others by calling - * down(&udev->serialize) directly. - * - * When unlocking multiple devices, use up(&udev->serialize) - * to unlock all but the last one. Unlock the last one by - * calling usb_unlock_device(). + * do this; nobody else needs to worry about it. The rule for locking + * is simple: * * When locking both a device and its parent, always lock the * the parent first. */ /** - * usb_lock_device - acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Use this routine when you don't hold any other device locks; - * to acquire nested inner locks call down(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_lock_device(struct usb_device *udev) -{ - down_read(&usb_all_devices_rwsem); - down(&udev->serialize); -} - -/** - * usb_trylock_device - attempt to acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Don't use this routine if you already hold a device lock; - * use down_trylock(&udev->serialize) instead. - * This is necessary for proper interaction with usb_lock_all_devices(). - * - * Returns 1 if successful, 0 if contention. - */ -int usb_trylock_device(struct usb_device *udev) -{ - if (!down_read_trylock(&usb_all_devices_rwsem)) - return 0; - if (down_trylock(&udev->serialize)) { - up_read(&usb_all_devices_rwsem); - return 0; - } - return 1; -} - -/** * usb_lock_device_for_reset - cautiously acquire the lock for a * usb device structure * @udev: device that's being locked @@ -935,7 +570,7 @@ int usb_lock_device_for_reset(struct usb_device *udev, } } - while (!usb_trylock_device(udev)) { + while (usb_trylock_device(udev) != 0) { /* If we can't acquire the lock after waiting one second, * we're probably deadlocked */ @@ -953,39 +588,6 @@ int usb_lock_device_for_reset(struct usb_device *udev, return 1; } -/** - * usb_unlock_device - release the lock for a usb device structure - * @udev: device that's being unlocked - * - * Use this routine when releasing the only device lock you hold; - * to release inner nested locks call up(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_unlock_device(struct usb_device *udev) -{ - up(&udev->serialize); - up_read(&usb_all_devices_rwsem); -} - -/** - * usb_lock_all_devices - acquire the lock for all usb device structures - * - * This is necessary when registering a new driver or probing a bus, - * since the driver-model core may try to use any usb_device. - */ -void usb_lock_all_devices(void) -{ - down_write(&usb_all_devices_rwsem); -} - -/** - * usb_unlock_all_devices - release the lock for all usb device structures - */ -void usb_unlock_all_devices(void) -{ - up_write(&usb_all_devices_rwsem); -} - static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) @@ -1008,10 +610,10 @@ static struct usb_device *match_device(struct usb_device *dev, /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { - down(&dev->children[child]->serialize); + usb_lock_device(dev->children[child]); ret_dev = match_device(dev->children[child], vendor_id, product_id); - up(&dev->children[child]->serialize); + usb_unlock_device(dev->children[child]); if (ret_dev) goto exit; } @@ -1496,18 +1098,8 @@ struct bus_type usb_bus_type = { .resume = usb_generic_resume, }; -#ifndef MODULE - -static int __init usb_setup_disable(char *str) -{ - nousb = 1; - return 1; -} - /* format to disable USB on kernel command line is: nousb */ -__setup("nousb", usb_setup_disable); - -#endif +__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444); /* * for external read access to <nousb> @@ -1598,8 +1190,6 @@ module_exit(usb_exit); * driver modules to use. */ -EXPORT_SYMBOL(usb_register); -EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_disabled); EXPORT_SYMBOL_GPL(usb_get_intf); @@ -1610,14 +1200,10 @@ EXPORT_SYMBOL(usb_put_dev); EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); -EXPORT_SYMBOL(usb_lock_device); -EXPORT_SYMBOL(usb_trylock_device); EXPORT_SYMBOL(usb_lock_device_for_reset); -EXPORT_SYMBOL(usb_unlock_device); EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); -EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_altnum_to_altsetting); |