diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/driver.c | 186 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 212 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 32 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 1 |
8 files changed, 306 insertions, 158 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d40ff9568813..f8e2d6d52e5c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -71,10 +71,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, list_add_tail(&dynid->node, &dynids->list); spin_unlock(&dynids->lock); - if (get_driver(driver)) { - retval = driver_attach(driver); - put_driver(driver); - } + retval = driver_attach(driver); if (retval) return retval; @@ -132,43 +129,39 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); -static int usb_create_newid_file(struct usb_driver *usb_drv) +static int usb_create_newid_files(struct usb_driver *usb_drv) { int error = 0; if (usb_drv->no_dynamic_id) goto exit; - if (usb_drv->probe != NULL) + if (usb_drv->probe != NULL) { error = driver_create_file(&usb_drv->drvwrap.driver, &driver_attr_new_id); + if (error == 0) { + error = driver_create_file(&usb_drv->drvwrap.driver, + &driver_attr_remove_id); + if (error) + driver_remove_file(&usb_drv->drvwrap.driver, + &driver_attr_new_id); + } + } exit: return error; } -static void usb_remove_newid_file(struct usb_driver *usb_drv) +static void usb_remove_newid_files(struct usb_driver *usb_drv) { if (usb_drv->no_dynamic_id) return; - if (usb_drv->probe != NULL) + if (usb_drv->probe != NULL) { driver_remove_file(&usb_drv->drvwrap.driver, - &driver_attr_new_id); -} - -static int -usb_create_removeid_file(struct usb_driver *drv) -{ - int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->drvwrap.driver, &driver_attr_remove_id); - return error; -} - -static void usb_remove_removeid_file(struct usb_driver *drv) -{ - driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id); + driver_remove_file(&usb_drv->drvwrap.driver, + &driver_attr_new_id); + } } static void usb_free_dynids(struct usb_driver *usb_drv) @@ -183,22 +176,12 @@ static void usb_free_dynids(struct usb_driver *usb_drv) spin_unlock(&usb_drv->dynids.lock); } #else -static inline int usb_create_newid_file(struct usb_driver *usb_drv) -{ - return 0; -} - -static void usb_remove_newid_file(struct usb_driver *usb_drv) -{ -} - -static int -usb_create_removeid_file(struct usb_driver *drv) +static inline int usb_create_newid_files(struct usb_driver *usb_drv) { return 0; } -static void usb_remove_removeid_file(struct usb_driver *drv) +static void usb_remove_newid_files(struct usb_driver *usb_drv) { } @@ -875,22 +858,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, usbfs_update_special(); - retval = usb_create_newid_file(new_driver); + retval = usb_create_newid_files(new_driver); if (retval) goto out_newid; - retval = usb_create_removeid_file(new_driver); - if (retval) - goto out_removeid; - pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name); out: return retval; -out_removeid: - usb_remove_newid_file(new_driver); out_newid: driver_unregister(&new_driver->drvwrap.driver); @@ -917,10 +894,9 @@ void usb_deregister(struct usb_driver *driver) pr_info("%s: deregistering interface driver %s\n", usbcore_name, driver->name); - usb_remove_removeid_file(driver); - usb_remove_newid_file(driver); - usb_free_dynids(driver); + usb_remove_newid_files(driver); driver_unregister(&driver->drvwrap.driver); + usb_free_dynids(driver); usbfs_update_special(); } @@ -958,13 +934,8 @@ void usb_rebind_intf(struct usb_interface *intf) int rc; /* Delayed unbind of an existing driver */ - if (intf->dev.driver) { - struct usb_driver *driver = - to_usb_driver(intf->dev.driver); - - dev_dbg(&intf->dev, "forced unbind\n"); - usb_driver_release_interface(driver, intf); - } + if (intf->dev.driver) + usb_forced_unbind_intf(intf); /* Try to rebind the interface */ if (!intf->dev.power.is_prepared) { @@ -977,15 +948,13 @@ void usb_rebind_intf(struct usb_interface *intf) #ifdef CONFIG_PM -#define DO_UNBIND 0 -#define DO_REBIND 1 - -/* Unbind drivers for @udev's interfaces that don't support suspend/resume, - * or rebind interfaces that have been unbound, according to @action. +/* Unbind drivers for @udev's interfaces that don't support suspend/resume + * There is no check for reset_resume here because it can be determined + * only during resume whether reset_resume is needed. * * The caller must hold @udev's device lock. */ -static void do_unbind_rebind(struct usb_device *udev, int action) +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; @@ -996,23 +965,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action) if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - switch (action) { - case DO_UNBIND: - if (intf->dev.driver) { - drv = to_usb_driver(intf->dev.driver); - if (!drv->suspend || !drv->resume) - usb_forced_unbind_intf(intf); - } - break; - case DO_REBIND: - if (intf->needs_binding) - usb_rebind_intf(intf); - break; + + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); } } } } +/* Unbind drivers for @udev's interfaces that failed to support reset-resume. + * These interfaces have the needs_binding flag set by usb_resume_interface(). + * + * The caller must hold @udev's device lock. + */ +static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->dev.driver && intf->needs_binding) + usb_forced_unbind_intf(intf); + } + } +} + +static void do_rebind_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->needs_binding) + usb_rebind_intf(intf); + } + } +} + static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1302,35 +1301,48 @@ int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev = to_usb_device(dev); - do_unbind_rebind(udev, DO_UNBIND); + unbind_no_pm_drivers_interfaces(udev); + + /* From now on we are sure all drivers support suspend/resume + * but not necessarily reset_resume() + * so we may still need to unbind and rebind upon resume + */ choose_wakeup(udev, msg); return usb_suspend_both(udev, msg); } /* The device lock is held by the PM core */ +int usb_resume_complete(struct device *dev) +{ + struct usb_device *udev = to_usb_device(dev); + + /* For PM complete calls, all we do is rebind interfaces + * whose needs_binding flag is set + */ + if (udev->state != USB_STATE_NOTATTACHED) + do_rebind_interfaces(udev); + return 0; +} + +/* The device lock is held by the PM core */ int usb_resume(struct device *dev, pm_message_t msg) { struct usb_device *udev = to_usb_device(dev); int status; - /* For PM complete calls, all we do is rebind interfaces */ - if (msg.event == PM_EVENT_ON) { - if (udev->state != USB_STATE_NOTATTACHED) - do_unbind_rebind(udev, DO_REBIND); - status = 0; - - /* For all other calls, take the device back to full power and + /* For all calls, take the device back to full power and * tell the PM core in case it was autosuspended previously. - * Unbind the interfaces that will need rebinding later. + * Unbind the interfaces that will need rebinding later, + * because they fail to support reset_resume. + * (This can't be done in usb_resume_interface() + * above because it doesn't own the right set of locks.) */ - } else { - status = usb_resume_both(udev, msg); - if (status == 0) { - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - do_unbind_rebind(udev, DO_REBIND); - } + status = usb_resume_both(udev, msg); + if (status == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + unbind_no_reset_resume_drivers_interfaces(udev); } /* Avoid PM error messages for devices disconnected while suspended diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 81e2c0d9c17d..622b4a48e732 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -380,6 +380,7 @@ static int check_root_hub_suspended(struct device *dev) return 0; } +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) static int suspend_common(struct device *dev, bool do_wakeup) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -471,6 +472,7 @@ static int resume_common(struct device *dev, int event) } return retval; } +#endif /* SLEEP || RUNTIME */ #ifdef CONFIG_PM_SLEEP diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e1282328fc27..9d7fc9a39933 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2352,7 +2352,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, "io mem" : "io base", (unsigned long long)hcd->rsrc_start); } else { - hcd->irq = -1; + hcd->irq = 0; if (hcd->rsrc_start) dev_info(hcd->self.controller, "%s 0x%08llx\n", (hcd->driver->flags & HCD_MEMORY) ? @@ -2508,7 +2508,7 @@ err_register_root_hub: clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: - if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0) + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) free_irq(irqnum, hcd); err_request_irq: err_hcd_driver_setup: @@ -2573,7 +2573,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) del_timer_sync(&hcd->rh_timer); if (usb_hcd_is_primary_hcd(hcd)) { - if (hcd->irq >= 0) + if (hcd->irq > 0) free_irq(hcd->irq, hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 265c2f675d04..28664eb7f555 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -62,6 +62,8 @@ struct usb_hub { resumed */ unsigned long removed_bits[1]; /* ports with a "removed" device present */ + unsigned long wakeup_bits[1]; /* ports that have signaled + remote wakeup */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev) kick_khubd(hub); } +/* + * Let the USB core know that a USB 3.0 device has sent a Function Wake Device + * Notification, which indicates it had initiated remote wakeup. + * + * USB 3.0 hubs do not report the port link state change from U3 to U0 when the + * device initiates resume, so the USB core will not receive notice of the + * resume through the normal hub interrupt URB. + */ +void usb_wakeup_notification(struct usb_device *hdev, + unsigned int portnum) +{ + struct usb_hub *hub; + + if (!hdev) + return; + + hub = hdev_to_hub(hdev); + if (hub) { + set_bit(portnum, hub->wakeup_bits); + kick_khubd(hub); + } +} +EXPORT_SYMBOL_GPL(usb_wakeup_notification); /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb) @@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_ENABLE); } - if (portchange & USB_PORT_STAT_C_LINK_STATE) { - need_debounce_delay = true; - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_PORT_LINK_STATE); - } - if ((portchange & USB_PORT_STAT_C_BH_RESET) && hub_is_superspeed(hub->hdev)) { need_debounce_delay = true; @@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else if (portstatus & USB_PORT_STAT_ENABLE) { + bool port_resumed = (portstatus & + USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_U0; /* The power session apparently survived the resume. * If there was an overcurrent or suspend change * (i.e., remote wakeup request), have khubd - * take care of it. + * take care of it. Look at the port link state + * for USB 3.0 hubs, since they don't have a suspend + * change bit, and they don't set the port link change + * bit on device-initiated resume. */ - if (portchange) + if (portchange || (hub_is_superspeed(hub->hdev) && + port_resumed)) set_bit(port1, hub->change_bits); } else if (udev->persist_enabled) { @@ -1021,8 +1047,10 @@ static int hub_configure(struct usb_hub *hub, dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); + hdev->children = kzalloc(hdev->maxchild * + sizeof(struct usb_device *), GFP_KERNEL); hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); - if (!hub->port_owners) { + if (!hdev->children || !hub->port_owners) { ret = -ENOMEM; goto fail; } @@ -1253,7 +1281,8 @@ static unsigned highspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { - struct usb_hub *hub = usb_get_intfdata (intf); + struct usb_hub *hub = usb_get_intfdata(intf); + struct usb_device *hdev = interface_to_usbdev(intf); /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); @@ -1275,6 +1304,7 @@ static void hub_disconnect(struct usb_interface *intf) highspeed_hubs--; usb_free_urb(hub->urb); + kfree(hdev->children); kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); @@ -1293,14 +1323,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); - /* Hubs have proper suspend/resume support. USB 3.0 device suspend is - * different from USB 2.0/1.1 device suspend, and unfortunately we - * don't support it yet. So leave autosuspend disabled for USB 3.0 - * external hubs for now. Enable autosuspend for USB 3.0 roothubs, - * since that isn't a "real" hub. - */ - if (!hub_is_superspeed(hdev) || !hdev->parent) - usb_enable_autosuspend(hdev); + /* Hubs have proper suspend/resume support. */ + usb_enable_autosuspend(hdev); if (hdev->level == MAX_TOPO_LEVEL) { dev_err(&intf->dev, @@ -1656,7 +1680,7 @@ void usb_disconnect(struct usb_device **pdev) usb_lock_device(udev); /* Free up all the children before we remove this device */ - for (i = 0; i < USB_MAXCHILDREN; i++) { + for (i = 0; i < udev->maxchild; i++) { if (udev->children[i]) usb_disconnect(&udev->children[i]); } @@ -1842,6 +1866,37 @@ fail: return err; } +static void set_usb_port_removable(struct usb_device *udev) +{ + struct usb_device *hdev = udev->parent; + struct usb_hub *hub; + u8 port = udev->portnum; + u16 wHubCharacteristics; + bool removable = true; + + if (!hdev) + return; + + hub = hdev_to_hub(udev->parent); + + wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + + if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) + return; + + if (hub_is_superspeed(hdev)) { + if (hub->descriptor->u.ss.DeviceRemovable & (1 << port)) + removable = false; + } else { + if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8))) + removable = false; + } + + if (removable) + udev->removable = USB_DEVICE_REMOVABLE; + else + udev->removable = USB_DEVICE_FIXED; +} /** * usb_new_device - perform initial device setup (usbcore-internal) @@ -1900,6 +1955,15 @@ int usb_new_device(struct usb_device *udev) announce_device(udev); device_enable_async_suspend(&udev->dev); + + /* + * check whether the hub marks this port as non-removable. Do it + * now so that platform-specific data can override it in + * device_add() + */ + if (udev->parent) + set_usb_port_removable(udev); + /* Register the device. The device driver is responsible * for configuring the device and invoking the add-device * notifier chain (used by usbfs and possibly others). @@ -2385,11 +2449,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * we don't explicitly enable it here. */ if (udev->do_remote_wakeup) { - status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + if (!hub_is_superspeed(hub->hdev)) { + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + /* Assume there's only one function on the USB 3.0 + * device and enable remote wake for the first + * interface. FIXME if the interface association + * descriptor shows there's more than one function. + */ + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, + USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW | + USB_INTRF_FUNC_SUSPEND_LP, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); @@ -2679,6 +2759,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; + int status; /* Warn if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -2691,6 +2772,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) return -EBUSY; } } + if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) { + /* Enable hub to send remote wakeup for all ports. */ + for (port1 = 1; port1 <= hdev->maxchild; port1++) { + status = set_port_feature(hdev, + port1 | + USB_PORT_FEAT_REMOTE_WAKE_CONNECT | + USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | + USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, + USB_PORT_FEAT_REMOTE_WAKE_MASK); + } + } dev_dbg(&intf->dev, "%s\n", __func__); @@ -3424,6 +3516,46 @@ done: hcd->driver->relinquish_port(hcd, port1); } +/* Returns 1 if there was a remote wakeup and a connect status change. */ +static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, + u16 portstatus, u16 portchange) +{ + struct usb_device *hdev; + struct usb_device *udev; + int connect_change = 0; + int ret; + + hdev = hub->hdev; + udev = hdev->children[port-1]; + if (!hub_is_superspeed(hdev)) { + if (!(portchange & USB_PORT_STAT_C_SUSPEND)) + return 0; + clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); + } else { + if (!udev || udev->state != USB_STATE_SUSPENDED || + (portstatus & USB_PORT_STAT_LINK_STATE) != + USB_SS_PORT_LS_U0) + return 0; + } + + if (udev) { + /* TRSMRCY = 10 msec */ + msleep(10); + + usb_lock_device(udev); + ret = usb_remote_wakeup(udev); + usb_unlock_device(udev); + if (ret < 0) + connect_change = 1; + } else { + ret = -ENODEV; + hub_port_disable(hub, port, 1); + } + dev_dbg(hub->intfdev, "resume on port %d, status %d\n", + port, ret); + return connect_change; +} + static void hub_events(void) { struct list_head *tmp; @@ -3436,7 +3568,7 @@ static void hub_events(void) u16 portstatus; u16 portchange; int i, ret; - int connect_change; + int connect_change, wakeup_change; /* * We restart the list every time to avoid a deadlock with @@ -3515,8 +3647,9 @@ static void hub_events(void) if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); + wakeup_change = test_and_clear_bit(i, hub->wakeup_bits); if (!test_and_clear_bit(i, hub->event_bits) && - !connect_change) + !connect_change && !wakeup_change) continue; ret = hub_port_status(hub, i, @@ -3557,31 +3690,10 @@ static void hub_events(void) } } - if (portchange & USB_PORT_STAT_C_SUSPEND) { - struct usb_device *udev; + if (hub_handle_remote_wakeup(hub, i, + portstatus, portchange)) + connect_change = 1; - clear_port_feature(hdev, i, - USB_PORT_FEAT_C_SUSPEND); - udev = hdev->children[i-1]; - if (udev) { - /* TRSMRCY = 10 msec */ - msleep(10); - - usb_lock_device(udev); - ret = usb_remote_wakeup(hdev-> - children[i-1]); - usb_unlock_device(udev); - if (ret < 0) - connect_change = 1; - } else { - ret = -ENODEV; - hub_port_disable(hub, i, 1); - } - dev_dbg (hub_dev, - "resume on port %d, status %d\n", - i, ret); - } - if (portchange & USB_PORT_STAT_C_OVERCURRENT) { u16 status = 0; u16 unused; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9e491ca2e5c4..566d9f94f735 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); +static ssize_t +show_removable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + char *state; + + udev = to_usb_device(dev); + + switch (udev->removable) { + case USB_DEVICE_REMOVABLE: + state = "removable"; + break; + case USB_DEVICE_FIXED: + state = "fixed"; + break; + default: + state = "unknown"; + } + + return sprintf(buf, "%s\n", state); +} +static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); #ifdef CONFIG_PM @@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_avoid_reset_quirk.attr, &dev_attr_authorized.attr, &dev_attr_remove.attr, + &dev_attr_removable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 909625b91eb3..7239a73c1b8c 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) * cause problems in HCDs if they get it wrong. */ { - unsigned int orig_flags = urb->transfer_flags; unsigned int allowed; static int pipetypes[4] = { PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT }; /* Check that the pipe's type matches the endpoint's type */ - if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) { - dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", + if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) + dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", usb_pipetype(urb->pipe), pipetypes[xfertype]); - return -EPIPE; /* The most suitable error code :-) */ - } - /* enforce simple/standard policy */ + /* Check against a simple/standard policy */ allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER); switch (xfertype) { @@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) allowed |= URB_ISO_ASAP; break; } - urb->transfer_flags &= allowed; + allowed &= urb->transfer_flags; - /* fail if submitter gave bogus flags */ - if (urb->transfer_flags != orig_flags) { - dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n", - orig_flags, urb->transfer_flags); - return -EINVAL; - } + /* warn if submitter gave bogus flags */ + if (allowed != urb->transfer_flags) + dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n", + urb->transfer_flags, allowed); } #endif /* @@ -532,10 +527,13 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); * a driver's I/O routines to insure that all URB-related activity has * completed before it returns. * - * This request is always asynchronous. Success is indicated by - * returning -EINPROGRESS, at which time the URB will probably not yet - * have been given back to the device driver. When it is eventually - * called, the completion function will see @urb->status == -ECONNRESET. + * This request is asynchronous, however the HCD might call the ->complete() + * callback during unlink. Therefore when drivers call usb_unlink_urb(), they + * must not hold any locks that may be taken by the completion function. + * Success is indicated by returning -EINPROGRESS, at which time the URB will + * probably not yet have been given back to the device driver. When it is + * eventually called, the completion function will see @urb->status == + * -ECONNRESET. * Failure is indicated by usb_unlink_urb() returning any other value. * Unlinking will fail when @urb is not currently "linked" (i.e., it was * never submitted, or it was unlinked before, or the hardware is already diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8ca9f994a280..c74ba7bbc748 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev) static void usb_dev_complete(struct device *dev) { /* Currently used only for rebinding interfaces */ - usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */ + usb_resume_complete(dev); } static int usb_dev_suspend(struct device *dev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 45e8479c377d..71648dcbe438 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -56,6 +56,7 @@ extern void usb_major_cleanup(void); extern int usb_suspend(struct device *dev, pm_message_t msg); extern int usb_resume(struct device *dev, pm_message_t msg); +extern int usb_resume_complete(struct device *dev); extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); |