diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 76 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 431 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 2 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/otg_whitelist.h | 13 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 2 |
8 files changed, 273 insertions, 275 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 1060657ca1b0..9cfda6a72194 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -56,22 +56,16 @@ config USB_OTG connector. config USB_OTG_WHITELIST - bool "Rely on OTG Targeted Peripherals List" - depends on USB_OTG || EXPERT - default y if USB_OTG + bool "Rely on OTG and EH Targeted Peripherals List" + depends on USB help If you say Y here, the "otg_whitelist.h" file will be used as a product whitelist, so USB peripherals not listed there will be rejected during enumeration. This behavior is required by the - USB OTG specification for all devices not on your product's + USB OTG and EH specification for all devices not on your product's "Targeted Peripherals List". "Embedded Hosts" are likewise allowed to support only a limited number of peripherals. - Otherwise, peripherals not listed there will only generate a - warning and enumeration will continue. That's more like what - normal Linux-USB hosts do (other than the warning), and is - convenient for many stages of product development. - config USB_OTG_BLACKLIST_HUB bool "Disable external hubs" depends on USB_OTG || EXPERT diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 487abcfcccd8..b84fb141e122 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -42,6 +42,7 @@ #include <linux/pm_runtime.h> #include <linux/types.h> +#include <linux/phy/phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/usb/phy.h> @@ -1272,7 +1273,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); * The usb core itself is however optimized for host controllers that can dma * using regular system memory - like pci devices doing bus mastering. * - * To support host controllers with limited dma capabilites we provide dma + * To support host controllers with limited dma capabilities we provide dma * bounce buffers. This feature can be enabled using the HCD_LOCAL_MEM flag. * For this to work properly the host controller code must first use the * function dma_declare_coherent_memory() to point out which memory area @@ -1664,6 +1665,8 @@ static void __usb_hcd_giveback_urb(struct urb *urb) usbmon_urb_complete(&hcd->self, urb, status); usb_anchor_suspend_wakeups(anchor); usb_unanchor_urb(urb); + if (likely(status == 0)) + usb_led_activity(USB_LED_EVENT_HOST); /* pass ownership to the completion handler */ urb->status = status; @@ -2301,7 +2304,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * Context: in_interrupt() * * Starts enumeration, with an immediate reset followed later by - * khubd identifying and possibly configuring the device. + * hub_wq identifying and possibly configuring the device. * This is needed by OTG controller drivers, where it helps meet * HNP protocol timing requirements for starting a port reset. * @@ -2320,7 +2323,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) if (port_num && hcd->driver->start_port_reset) status = hcd->driver->start_port_reset(hcd, port_num); - /* run khubd shortly after (first) root port reset finishes; + /* allocate hub_wq shortly after (first) root port reset finishes; * it may issue others, until at least 50 msecs have passed. */ if (status == 0) @@ -2383,20 +2386,20 @@ void usb_hc_died (struct usb_hcd *hcd) if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - /* make khubd clean up old urbs and devices */ + /* make hub_wq clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, USB_STATE_NOTATTACHED); - usb_kick_khubd (hcd->self.root_hub); + usb_kick_hub_wq(hcd->self.root_hub); } if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) { hcd = hcd->shared_hcd; if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - /* make khubd clean up old urbs and devices */ + /* make hub_wq clean up old urbs and devices */ usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); - usb_kick_khubd(hcd->self.root_hub); + usb_kick_hub_wq(hcd->self.root_hub); } } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); @@ -2627,7 +2630,7 @@ int usb_add_hcd(struct usb_hcd *hcd, int retval; struct usb_device *rhdev; - if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->phy) { + if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) { struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0); if (IS_ERR(phy)) { @@ -2640,11 +2643,34 @@ int usb_add_hcd(struct usb_hcd *hcd, usb_put_phy(phy); return retval; } - hcd->phy = phy; + hcd->usb_phy = phy; hcd->remove_phy = 1; } } + if (IS_ENABLED(CONFIG_GENERIC_PHY)) { + struct phy *phy = phy_get(hcd->self.controller, "usb"); + + if (IS_ERR(phy)) { + retval = PTR_ERR(phy); + if (retval == -EPROBE_DEFER) + goto err_phy; + } else { + retval = phy_init(phy); + if (retval) { + phy_put(phy); + goto err_phy; + } + retval = phy_power_on(phy); + if (retval) { + phy_exit(phy); + phy_put(phy); + goto err_phy; + } + hcd->phy = phy; + } + } + dev_info(hcd->self.controller, "%s\n", hcd->product_desc); /* Keep old behaviour if authorized_default is not in [0, 1]. */ @@ -2655,12 +2681,12 @@ int usb_add_hcd(struct usb_hcd *hcd, set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); /* HC is in reset state, but accessible. Now do the one-time init, - * bottom up so that hcds can customize the root hubs before khubd + * bottom up so that hcds can customize the root hubs before hub_wq * starts talking to them. (Note, bus id is assigned early too.) */ if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); - goto err_remove_phy; + goto err_create_buf; } if ((retval = usb_register_bus(&hcd->self)) < 0) @@ -2787,12 +2813,19 @@ err_allocate_root_hub: usb_deregister_bus(&hcd->self); err_register_bus: hcd_buffer_destroy(hcd); -err_remove_phy: - if (hcd->remove_phy && hcd->phy) { - usb_phy_shutdown(hcd->phy); - usb_put_phy(hcd->phy); +err_create_buf: + if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) { + phy_power_off(hcd->phy); + phy_exit(hcd->phy); + phy_put(hcd->phy); hcd->phy = NULL; } +err_phy: + if (hcd->remove_phy && hcd->usb_phy) { + usb_phy_shutdown(hcd->usb_phy); + usb_put_phy(hcd->usb_phy); + hcd->usb_phy = NULL; + } return retval; } EXPORT_SYMBOL_GPL(usb_add_hcd); @@ -2864,11 +2897,18 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); - if (hcd->remove_phy && hcd->phy) { - usb_phy_shutdown(hcd->phy); - usb_put_phy(hcd->phy); + + if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) { + phy_power_off(hcd->phy); + phy_exit(hcd->phy); + phy_put(hcd->phy); hcd->phy = NULL; } + if (hcd->remove_phy && hcd->usb_phy) { + usb_phy_shutdown(hcd->usb_phy); + usb_put_phy(hcd->usb_phy); + hcd->usb_phy = NULL; + } usb_put_invalidate_rhdev(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d481c99a20d7..11e80ac31324 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -22,9 +22,8 @@ #include <linux/usb/hcd.h> #include <linux/usb/otg.h> #include <linux/usb/quirks.h> -#include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/mutex.h> -#include <linux/freezer.h> #include <linux/random.h> #include <linux/pm_qos.h> @@ -32,6 +31,7 @@ #include <asm/byteorder.h> #include "hub.h" +#include "otg_whitelist.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 @@ -41,14 +41,9 @@ * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static DEFINE_SPINLOCK(device_state_lock); -/* khubd's worklist and its lock */ -static DEFINE_SPINLOCK(hub_event_lock); -static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ - -/* Wakes up khubd */ -static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); - -static struct task_struct *khubd_task; +/* workqueue to process hub events */ +static struct workqueue_struct *hub_wq; +static void hub_event(struct work_struct *work); /* synchronize hub-port add/remove and peering operations */ DEFINE_MUTEX(usb_port_peer_mutex); @@ -104,6 +99,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STABLE 100 +static void hub_release(struct kref *kref); static int usb_reset_and_verify_device(struct usb_device *udev); static inline char *portspeed(struct usb_hub *hub, int portstatus) @@ -575,28 +571,39 @@ static int hub_port_status(struct usb_hub *hub, int port1, return ret; } -static void kick_khubd(struct usb_hub *hub) +static void kick_hub_wq(struct usb_hub *hub) { - unsigned long flags; + struct usb_interface *intf; - spin_lock_irqsave(&hub_event_lock, flags); - if (!hub->disconnected && list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); + if (hub->disconnected || work_pending(&hub->events)) + return; - /* Suppress autosuspend until khubd runs */ - usb_autopm_get_interface_no_resume( - to_usb_interface(hub->intfdev)); - wake_up(&khubd_wait); - } - spin_unlock_irqrestore(&hub_event_lock, flags); + /* + * Suppress autosuspend until the event is proceed. + * + * Be careful and make sure that the symmetric operation is + * always called. We are here only when there is no pending + * work for this hub. Therefore put the interface either when + * the new work is called or when it is canceled. + */ + intf = to_usb_interface(hub->intfdev); + usb_autopm_get_interface_no_resume(intf); + kref_get(&hub->kref); + + if (queue_work(hub_wq, &hub->events)) + return; + + /* the work has already been scheduled */ + usb_autopm_put_interface_async(intf); + kref_put(&hub->kref, hub_release); } -void usb_kick_khubd(struct usb_device *hdev) +void usb_kick_hub_wq(struct usb_device *hdev) { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); if (hub) - kick_khubd(hub); + kick_hub_wq(hub); } /* @@ -618,7 +625,7 @@ void usb_wakeup_notification(struct usb_device *hdev, hub = usb_hub_to_struct_hub(hdev); if (hub) { set_bit(portnum, hub->wakeup_bits); - kick_khubd(hub); + kick_hub_wq(hub); } } EXPORT_SYMBOL_GPL(usb_wakeup_notification); @@ -645,7 +652,7 @@ static void hub_irq(struct urb *urb) hub->error = status; /* FALL THROUGH */ - /* let khubd handle things */ + /* let hub_wq handle things */ case 0: /* we got data: port status changed */ bits = 0; for (i = 0; i < urb->actual_length; ++i) @@ -657,8 +664,8 @@ static void hub_irq(struct urb *urb) hub->nerrors = 0; - /* Something happened, let khubd figure it out */ - kick_khubd(hub); + /* Something happened, let hub_wq figure it out */ + kick_hub_wq(hub); resubmit: if (hub->quiescing) @@ -688,7 +695,7 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) } /* - * enumeration blocks khubd for a long time. we use keventd instead, since + * enumeration blocks hub_wq for a long time. we use keventd instead, since * long blocking there is the exception, not the rule. accordingly, HCDs * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. @@ -954,7 +961,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) /* * Disable a port and mark a logical connect-change event, so that some - * time later khubd will disconnect() any existing usb_device on the port + * time later hub_wq will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. */ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) @@ -967,12 +974,12 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * - SRP saves power that way * - ... new call, TBD ... * That's easy if this hub can switch power per-port, and - * khubd reactivates the port later (timer, SRP, etc). + * hub_wq reactivates the port later (timer, SRP, etc). * Powerdown must be optional, because of reset/DFU. */ set_bit(port1, hub->change_bits); - kick_khubd(hub); + kick_hub_wq(hub); } /** @@ -980,7 +987,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * @udev: device to be disabled and removed * Context: @udev locked, must be able to sleep. * - * After @udev's port has been disabled, khubd is notified and it will + * After @udev's port has been disabled, hub_wq is notified and it will * see that the device has been disconnected. When the device is * physically unplugged and something is plugged in, the events will * be received and processed normally. @@ -1100,7 +1107,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) init2: /* - * Check each port and set hub->change_bits to let khubd know + * Check each port and set hub->change_bits to let hub_wq know * which ports need attention. */ for (port1 = 1; port1 <= hdev->maxchild; ++port1) { @@ -1167,7 +1174,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_bit(port1, hub->removed_bits); if (!udev || udev->state == USB_STATE_NOTATTACHED) { - /* Tell khubd to disconnect the device or + /* Tell hub_wq to disconnect the device or * check for a new connection */ if (udev || (portstatus & USB_PORT_STAT_CONNECTION) || @@ -1180,7 +1187,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) 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 + * (i.e., remote wakeup request), have hub_wq * 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 @@ -1201,7 +1208,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else { - /* The power session is gone; tell khubd */ + /* The power session is gone; tell hub_wq */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); set_bit(port1, hub->change_bits); } @@ -1209,10 +1216,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* If no port-status-change flags were set, we don't need any * debouncing. If flags were set we can try to debounce the - * ports all at once right now, instead of letting khubd do them + * ports all at once right now, instead of letting hub_wq do them * one at a time later on. * - * If any port-status changes do occur during this delay, khubd + * If any port-status changes do occur during this delay, hub_wq * will see them later and handle them normally. */ if (need_debounce_delay) { @@ -1240,7 +1247,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) &hub->leds, LED_CYCLE_PERIOD); /* Scan all ports that need attention */ - kick_khubd(hub); + kick_hub_wq(hub); /* Allow autosuspend if it was suppressed */ if (type <= HUB_INIT3) @@ -1273,7 +1280,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) cancel_delayed_work_sync(&hub->init_work); - /* khubd and related activity won't re-trigger */ + /* hub_wq and related activity won't re-trigger */ hub->quiescing = 1; if (type != HUB_SUSPEND) { @@ -1284,7 +1291,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) } } - /* Stop khubd and related activity */ + /* Stop hub_wq and related activity */ usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); @@ -1606,7 +1613,7 @@ static int hub_configure(struct usb_hub *hub, if (ret < 0) goto fail; - /* Update the HCD's internal representation of this hub before khubd + /* Update the HCD's internal representation of this hub before hub_wq * starts getting port status changes for devices under the hub. */ if (hcd->driver->update_hub_device) { @@ -1634,6 +1641,7 @@ static void hub_release(struct kref *kref) { struct usb_hub *hub = container_of(kref, struct usb_hub, kref); + usb_put_dev(hub->hdev); usb_put_intf(to_usb_interface(hub->intfdev)); kfree(hub); } @@ -1646,14 +1654,11 @@ static void hub_disconnect(struct usb_interface *intf) struct usb_device *hdev = interface_to_usbdev(intf); int port1; - /* Take the hub off the event list and don't let it be added again */ - spin_lock_irq(&hub_event_lock); - if (!list_empty(&hub->event_list)) { - list_del_init(&hub->event_list); - usb_autopm_put_interface_no_suspend(intf); - } + /* + * Stop adding new hub events. We do not want to block here and thus + * will not try to remove any pending work item. + */ hub->disconnected = 1; - spin_unlock_irq(&hub_event_lock); /* Disconnect all children and quiesce the hub */ hub->error = 0; @@ -1793,12 +1798,13 @@ descriptor_error: } kref_init(&hub->kref); - INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->init_work, NULL); + INIT_WORK(&hub->events, hub_event); usb_get_intf(intf); + usb_get_dev(hdev); usb_set_intfdata (intf, hub); intf->needs_remote_wakeup = 1; @@ -1983,8 +1989,10 @@ void usb_set_device_state(struct usb_device *udev, || new_state == USB_STATE_SUSPENDED) ; /* No change to wakeup settings */ else if (new_state == USB_STATE_CONFIGURED) - wakeup = udev->actconfig->desc.bmAttributes - & USB_CONFIG_ATT_WAKEUP; + wakeup = (udev->quirks & + USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 : + udev->actconfig->desc.bmAttributes & + USB_CONFIG_ATT_WAKEUP; else wakeup = 0; } @@ -2037,7 +2045,8 @@ static void choose_devnum(struct usb_device *udev) int devnum; struct usb_bus *bus = udev->bus; - /* If khubd ever becomes multithreaded, this will need a lock */ + /* be safe when more hub events are proceed in parallel */ + mutex_lock(&bus->usb_address0_mutex); if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); @@ -2055,6 +2064,7 @@ static void choose_devnum(struct usb_device *udev) set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } + mutex_unlock(&bus->usb_address0_mutex); } static void release_devnum(struct usb_device *udev) @@ -2205,9 +2215,6 @@ static void announce_device(struct usb_device *udev) static inline void announce_device(struct usb_device *udev) { } #endif -#ifdef CONFIG_USB_OTG -#include "otg_whitelist.h" -#endif /** * usb_enumerate_device_otg - FIXME (usbcore-internal) @@ -2267,21 +2274,6 @@ static int usb_enumerate_device_otg(struct usb_device *udev) } } } - - if (!is_targeted(udev)) { - - /* Maybe it can talk to us, though we can't talk to it. - * (Includes HNP test device.) - */ - if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - err = usb_port_suspend(udev, PMSG_SUSPEND); - if (err < 0) - dev_dbg(&udev->dev, "HNP fail, %d\n", err); - } - err = -ENOTSUPP; - goto fail; - } -fail: #endif return err; } @@ -2304,6 +2296,7 @@ fail: static int usb_enumerate_device(struct usb_device *udev) { int err; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); if (udev->config == NULL) { err = usb_get_configuration(udev); @@ -2325,6 +2318,20 @@ static int usb_enumerate_device(struct usb_device *udev) if (err < 0) return err; + if (IS_ENABLED(CONFIG_USB_OTG_WHITELIST) && hcd->tpl_support && + !is_targeted(udev)) { + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ + if (IS_ENABLED(CONFIG_USB_OTG) && (udev->bus->b_hnp_enable + || udev->bus->is_b_host)) { + err = usb_port_suspend(udev, PMSG_AUTO_SUSPEND); + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + return -ENOTSUPP; + } + usb_detect_interface_quirks(udev); return 0; @@ -3070,7 +3077,7 @@ static unsigned wakeup_enabled_descendants(struct usb_device *udev) * Once VBUS drop breaks the circuit, the port it's using has to go through * normal re-enumeration procedures, starting with enabling VBUS power. * Other than re-initializing the hub (plug/unplug, except for root hubs), - * Linux (2.6) currently has NO mechanisms to initiate that: no khubd + * Linux (2.6) currently has NO mechanisms to initiate that: no hub_wq * timer, no SRP, no requests through sysfs. * * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get @@ -3212,7 +3219,7 @@ static int finish_port_resume(struct usb_device *udev) /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the * first two on the host side; they'd be inside hub_port_init() - * during many timeouts, but khubd can't suspend until later. + * during many timeouts, but hub_wq can't suspend until later. */ usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED @@ -3577,7 +3584,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&intf->dev, "%s\n", __func__); - /* stop khubd and related activity */ + /* stop hub_wq and related activity */ hub_quiesce(hub, HUB_SUSPEND); return 0; } @@ -4461,8 +4468,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - if (hcd->phy && !hdev->parent) - usb_phy_notify_connect(hcd->phy, udev->speed); + if (hcd->usb_phy && !hdev->parent) + usb_phy_notify_connect(hcd->usb_phy, udev->speed); /* * Some superspeed devices have finished the link training process @@ -4538,6 +4545,9 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) struct usb_qualifier_descriptor *qual; int status; + if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER) + return; + qual = kmalloc (sizeof *qual, GFP_KERNEL); if (qual == NULL) return; @@ -4617,9 +4627,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, /* Disconnect any existing devices under this port */ if (udev) { - if (hcd->phy && !hdev->parent && + if (hcd->usb_phy && !hdev->parent && !(portstatus & USB_PORT_STAT_CONNECTION)) - usb_phy_notify_disconnect(hcd->phy, udev->speed); + usb_phy_notify_disconnect(hcd->usb_phy, udev->speed); usb_disconnect(&port_dev->child); } @@ -4970,10 +4980,10 @@ static void port_event(struct usb_hub *hub, int port1) * On disconnect USB3 protocol ports transit from U0 to * SS.Inactive to Rx.Detect. If this happens a warm- * reset is not needed, but a (re)connect may happen - * before khubd runs and sees the disconnect, and the + * before hub_wq runs and sees the disconnect, and the * device may be an unknown state. * - * If the port went through SS.Inactive without khubd + * If the port went through SS.Inactive without hub_wq * seeing it the C_LINK_STATE change flag will be set, * and we reset the dev to put it in a known state. */ @@ -4992,10 +5002,8 @@ static void port_event(struct usb_hub *hub, int port1) hub_port_connect_change(hub, port1, portstatus, portchange); } - -static void hub_events(void) +static void hub_event(struct work_struct *work) { - struct list_head *tmp; struct usb_device *hdev; struct usb_interface *intf; struct usb_hub *hub; @@ -5004,166 +5012,117 @@ static void hub_events(void) u16 hubchange; int i, ret; - /* - * We restart the list every time to avoid a deadlock with - * deleting hubs downstream from this one. This should be - * safe since we delete the hub from the event list. - * Not the most efficient, but avoids deadlocks. - */ - while (1) { + hub = container_of(work, struct usb_hub, events); + hdev = hub->hdev; + hub_dev = hub->intfdev; + intf = to_usb_interface(hub_dev); + + dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", + hdev->state, hdev->maxchild, + /* NOTE: expects max 15 ports... */ + (u16) hub->change_bits[0], + (u16) hub->event_bits[0]); + + /* Lock the device, then check to see if we were + * disconnected while waiting for the lock to succeed. */ + usb_lock_device(hdev); + if (unlikely(hub->disconnected)) + goto out_hdev_lock; + + /* If the hub has died, clean up after it */ + if (hdev->state == USB_STATE_NOTATTACHED) { + hub->error = -ENODEV; + hub_quiesce(hub, HUB_DISCONNECT); + goto out_hdev_lock; + } + + /* Autoresume */ + ret = usb_autopm_get_interface(intf); + if (ret) { + dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); + goto out_hdev_lock; + } - /* Grab the first entry at the beginning of the list */ - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub_event_list)) { - spin_unlock_irq(&hub_event_lock); - break; - } + /* If this is an inactive hub, do nothing */ + if (hub->quiescing) + goto out_autopm; - tmp = hub_event_list.next; - list_del_init(tmp); - - hub = list_entry(tmp, struct usb_hub, event_list); - kref_get(&hub->kref); - hdev = hub->hdev; - usb_get_dev(hdev); - spin_unlock_irq(&hub_event_lock); - - hub_dev = hub->intfdev; - intf = to_usb_interface(hub_dev); - dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", - hdev->state, hdev->maxchild, - /* NOTE: expects max 15 ports... */ - (u16) hub->change_bits[0], - (u16) hub->event_bits[0]); - - /* Lock the device, then check to see if we were - * disconnected while waiting for the lock to succeed. */ - usb_lock_device(hdev); - if (unlikely(hub->disconnected)) - goto loop_disconnected; - - /* If the hub has died, clean up after it */ - if (hdev->state == USB_STATE_NOTATTACHED) { - hub->error = -ENODEV; - hub_quiesce(hub, HUB_DISCONNECT); - goto loop; - } + if (hub->error) { + dev_dbg(hub_dev, "resetting for error %d\n", hub->error); - /* Autoresume */ - ret = usb_autopm_get_interface(intf); + ret = usb_reset_device(hdev); if (ret) { - dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); - goto loop; + dev_dbg(hub_dev, "error resetting hub: %d\n", ret); + goto out_autopm; } - /* If this is an inactive hub, do nothing */ - if (hub->quiescing) - goto loop_autopm; - - if (hub->error) { - dev_dbg (hub_dev, "resetting for error %d\n", - hub->error); + hub->nerrors = 0; + hub->error = 0; + } - ret = usb_reset_device(hdev); - if (ret) { - dev_dbg (hub_dev, - "error resetting hub: %d\n", ret); - goto loop_autopm; - } + /* deal with port status changes */ + for (i = 1; i <= hdev->maxchild; i++) { + struct usb_port *port_dev = hub->ports[i - 1]; - hub->nerrors = 0; - hub->error = 0; + if (test_bit(i, hub->event_bits) + || test_bit(i, hub->change_bits) + || test_bit(i, hub->wakeup_bits)) { + /* + * The get_noresume and barrier ensure that if + * the port was in the process of resuming, we + * flush that work and keep the port active for + * the duration of the port_event(). However, + * if the port is runtime pm suspended + * (powered-off), we leave it in that state, run + * an abbreviated port_event(), and move on. + */ + pm_runtime_get_noresume(&port_dev->dev); + pm_runtime_barrier(&port_dev->dev); + usb_lock_port(port_dev); + port_event(hub, i); + usb_unlock_port(port_dev); + pm_runtime_put_sync(&port_dev->dev); } + } - /* deal with port status changes */ - for (i = 1; i <= hdev->maxchild; i++) { - struct usb_port *port_dev = hub->ports[i - 1]; - - if (test_bit(i, hub->event_bits) - || test_bit(i, hub->change_bits) - || test_bit(i, hub->wakeup_bits)) { - /* - * The get_noresume and barrier ensure that if - * the port was in the process of resuming, we - * flush that work and keep the port active for - * the duration of the port_event(). However, - * if the port is runtime pm suspended - * (powered-off), we leave it in that state, run - * an abbreviated port_event(), and move on. - */ - pm_runtime_get_noresume(&port_dev->dev); - pm_runtime_barrier(&port_dev->dev); - usb_lock_port(port_dev); - port_event(hub, i); - usb_unlock_port(port_dev); - pm_runtime_put_sync(&port_dev->dev); - } + /* deal with hub status changes */ + if (test_and_clear_bit(0, hub->event_bits) == 0) + ; /* do nothing */ + else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) + dev_err(hub_dev, "get_hub_status failed\n"); + else { + if (hubchange & HUB_CHANGE_LOCAL_POWER) { + dev_dbg(hub_dev, "power change\n"); + clear_hub_feature(hdev, C_HUB_LOCAL_POWER); + if (hubstatus & HUB_STATUS_LOCAL_POWER) + /* FIXME: Is this always true? */ + hub->limited_power = 1; + else + hub->limited_power = 0; } + if (hubchange & HUB_CHANGE_OVERCURRENT) { + u16 status = 0; + u16 unused; - /* deal with hub status changes */ - if (test_and_clear_bit(0, hub->event_bits) == 0) - ; /* do nothing */ - else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) - dev_err (hub_dev, "get_hub_status failed\n"); - else { - if (hubchange & HUB_CHANGE_LOCAL_POWER) { - dev_dbg (hub_dev, "power change\n"); - clear_hub_feature(hdev, C_HUB_LOCAL_POWER); - if (hubstatus & HUB_STATUS_LOCAL_POWER) - /* FIXME: Is this always true? */ - hub->limited_power = 1; - else - hub->limited_power = 0; - } - if (hubchange & HUB_CHANGE_OVERCURRENT) { - u16 status = 0; - u16 unused; - - dev_dbg(hub_dev, "over-current change\n"); - clear_hub_feature(hdev, C_HUB_OVER_CURRENT); - msleep(500); /* Cool down */ - hub_power_on(hub, true); - hub_hub_status(hub, &status, &unused); - if (status & HUB_STATUS_OVERCURRENT) - dev_err(hub_dev, "over-current " - "condition\n"); - } + dev_dbg(hub_dev, "over-current change\n"); + clear_hub_feature(hdev, C_HUB_OVER_CURRENT); + msleep(500); /* Cool down */ + hub_power_on(hub, true); + hub_hub_status(hub, &status, &unused); + if (status & HUB_STATUS_OVERCURRENT) + dev_err(hub_dev, "over-current condition\n"); } + } - loop_autopm: - /* Balance the usb_autopm_get_interface() above */ - usb_autopm_put_interface_no_suspend(intf); - loop: - /* Balance the usb_autopm_get_interface_no_resume() in - * kick_khubd() and allow autosuspend. - */ - usb_autopm_put_interface(intf); - loop_disconnected: - usb_unlock_device(hdev); - usb_put_dev(hdev); - kref_put(&hub->kref, hub_release); - - } /* end while (1) */ -} - -static int hub_thread(void *__unused) -{ - /* khubd needs to be freezable to avoid interfering with USB-PERSIST - * port handover. Otherwise it might see that a full-speed device - * was gone before the EHCI controller had handed its port over to - * the companion full-speed controller. - */ - set_freezable(); - - do { - hub_events(); - wait_event_freezable(khubd_wait, - !list_empty(&hub_event_list) || - kthread_should_stop()); - } while (!kthread_should_stop() || !list_empty(&hub_event_list)); +out_autopm: + /* Balance the usb_autopm_get_interface() above */ + usb_autopm_put_interface_no_suspend(intf); +out_hdev_lock: + usb_unlock_device(hdev); - pr_debug("%s: khubd exiting\n", usbcore_name); - return 0; + /* Balance the stuff in kick_hub_wq() and allow autosuspend */ + usb_autopm_put_interface(intf); + kref_put(&hub->kref, hub_release); } static const struct usb_device_id hub_id_table[] = { @@ -5203,20 +5162,26 @@ int usb_hub_init(void) return -1; } - khubd_task = kthread_run(hub_thread, NULL, "khubd"); - if (!IS_ERR(khubd_task)) + /* + * The workqueue needs to be freezable to avoid interfering with + * USB-PERSIST port handover. Otherwise it might see that a full-speed + * device was gone before the EHCI controller had handed its port + * over to the companion full-speed controller. + */ + hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); + if (hub_wq) return 0; /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); - printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); + pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name); return -1; } void usb_hub_cleanup(void) { - kthread_stop(khubd_task); + destroy_workqueue(hub_wq); /* * Hub resources are freed for us by usb_deregister. It calls @@ -5325,7 +5290,7 @@ static int descriptors_changed(struct usb_device *udev, * former operating configuration. If the reset fails, or the device's * descriptors change from their values before the reset, or the original * configuration and altsettings cannot be restored, a flag will be set - * telling khubd to pretend the device has been disconnected and then + * telling hub_wq to pretend the device has been disconnected and then * re-connected. All drivers will be unbound, and the device will be * re-enumerated and probed all over again. * diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index c77d8778af4b..688817fb3246 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -41,7 +41,6 @@ struct usb_hub { int error; /* last reported error */ int nerrors; /* track consecutive errors */ - struct list_head event_list; /* hubs w/data or errs ready */ unsigned long event_bits[1]; /* status change bitmask */ unsigned long change_bits[1]; /* ports with logical connect status change */ @@ -77,6 +76,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; + struct work_struct events; struct usb_port **ports; }; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0c8a7fc4dad8..f7b7713cfb2a 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -770,9 +770,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) dev->string_langid = 0x0409; dev->have_langid = 1; dev_err(&dev->dev, - "string descriptor 0 malformed (err = %d), " - "defaulting to 0x%04x\n", - err, dev->string_langid); + "language id specifier not provided by device, defaulting to English\n"); return 0; } diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index e8cdce571bb1..de0c9c9d7091 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -10,8 +10,8 @@ */ /* - * This OTG Whitelist is the OTG "Targeted Peripheral List". It should - * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. + * This OTG and Embedded Host Whitelist is "Targeted Peripheral List". + * It should mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. * * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING! */ @@ -50,10 +50,6 @@ static int is_targeted(struct usb_device *dev) { struct usb_device_id *id = whitelist_table; - /* possible in developer configs only! */ - if (!dev->bus->otg_port) - return 1; - /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && le16_to_cpu(dev->descriptor.idProduct) == 0xbadd)) @@ -103,10 +99,7 @@ static int is_targeted(struct usb_device *dev) dev_err(&dev->dev, "device v%04x p%04x is not supported\n", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); -#ifdef CONFIG_USB_OTG_WHITELIST + return 0; -#else - return 1; -#endif } diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index bae636e2a1a3..5ae883dc21f5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -93,6 +93,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Elan Touchscreen */ + { USB_DEVICE(0x04f3, 0x0089), .driver_info = + USB_QUIRK_DEVICE_QUALIFIER }, + /* Roland SC-8820 */ { USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -159,6 +163,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* USB3503 */ { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME }, + /* ASUS Base Station(T100) */ + { USB_DEVICE(0x0b05, 0x17e0), .driver_info = + USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d9d08720c386..b1b34d0557c9 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -48,7 +48,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev, return c->desc.bMaxPower * mul; } -extern void usb_kick_khubd(struct usb_device *dev); +extern void usb_kick_hub_wq(struct usb_device *dev); extern int usb_match_one_id_intf(struct usb_device *dev, struct usb_host_interface *intf, const struct usb_device_id *id); |