diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 47 |
1 files changed, 39 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 87c938adbc7..abb0084795c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1693,6 +1693,12 @@ void usb_disconnect(struct usb_device **pdev) dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); +#ifdef CONFIG_USB_OTG + if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) { + cancel_delayed_work_sync(&udev->bus->hnp_polling); + udev->bus->hnp_support = 0; + } +#endif usb_lock_device(udev); /* Free up all the children before we remove this device */ @@ -1797,15 +1803,10 @@ static int usb_enumerate_device_otg(struct usb_device *udev) (port1 == bus->otg_port) ? "" : "non-"); - /* enable HNP before suspend, it's simpler */ - if (port1 == bus->otg_port) - bus->b_hnp_enable = 1; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, 0, - bus->b_hnp_enable - ? USB_DEVICE_B_HNP_ENABLE - : USB_DEVICE_A_ALT_HNP_SUPPORT, + USB_DEVICE_A_HNP_SUPPORT, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (err < 0) { /* OTG MESSAGE: report errors here, @@ -1814,24 +1815,34 @@ static int usb_enumerate_device_otg(struct usb_device *udev) dev_info(&udev->dev, "can't set HNP mode: %d\n", err); - bus->b_hnp_enable = 0; + bus->hnp_support = 0; } } } } +out: 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) { + if (udev->bus->hnp_support) { err = usb_port_suspend(udev, PMSG_SUSPEND); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } err = -ENOTSUPP; goto fail; + } else if (udev->bus->hnp_support && + udev->portnum == udev->bus->otg_port) { + /* HNP polling is introduced in OTG supplement Rev 2.0 + * and older devices does not support. Work is not + * re-armed if device returns STALL. B-Host also performs + * HNP polling. + */ + schedule_delayed_work(&udev->bus->hnp_polling, + msecs_to_jiffies(THOST_REQ_POLL)); } fail: #endif @@ -2497,6 +2508,20 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); +#ifdef CONFIG_USB_OTG + if (!udev->bus->is_b_host && udev->bus->hnp_support && + udev->portnum == udev->bus->otg_port) { + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_B_HNP_ENABLE, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (status < 0) + dev_dbg(&udev->dev, "can't enable HNP on port %d, " + "status %d\n", port1, status); + else + udev->bus->b_hnp_enable = 1; + } +#endif /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2651,6 +2676,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) int status; u16 portchange, portstatus; +#ifdef CONFIG_USB_OTG + if (!udev->bus->is_b_host && udev->bus->hnp_support && + udev->portnum == udev->bus->otg_port) + udev->bus->b_hnp_enable = 0; +#endif + /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); if (status == 0 && !port_is_suspended(hub, portstatus)) |