diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hub.c | 175 |
1 files changed, 71 insertions, 104 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ac1ef1527dd..ca3dbf84e80 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1625,6 +1625,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1, #ifdef CONFIG_USB_SUSPEND /* + * usb_port_suspend - suspend a usb device's upstream port + * @udev: device that's no longer in active use, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * Suspends a USB device that isn't in active use, conserving power. + * Devices may wake out of a suspend, if anything important happens, + * using the remote wakeup mechanism. They may also be taken out of + * suspend by the host, using usb_port_resume(). It's also routine + * to disconnect devices while they are suspended. + * + * This only affects the USB hardware for a device; its interfaces + * (and, for hubs, child devices) must already have been suspended. + * * Selective port suspend reduces power; most suspended devices draw * less than 500 uA. It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. @@ -1633,11 +1646,35 @@ static int hub_port_reset(struct usb_hub *hub, int port1, * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet. In * some cases, this wakes the USB host. + * + * Suspending OTG devices may trigger HNP, if that's been enabled + * between a pair of dual-role devices. That will change roles, such + * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. + * + * Devices on USB hub ports have only one "suspend" state, corresponding + * to ACPI D2, "may cause the device to lose some context". + * State transitions include: + * + * - suspend, resume ... when the VBUS power link stays live + * - suspend, disconnect ... VBUS lost + * + * 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 + * timer, no SRP, no requests through sysfs. + * + * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when + * the root hub for their bus goes into global suspend ... so we don't + * (falsely) update the device power state to say it suspended. + * + * Returns 0 on success, else negative errno. */ -static int hub_port_suspend(struct usb_hub *hub, int port1, - struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev) { - int status; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); @@ -1654,17 +1691,15 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) - dev_dbg(&udev->dev, - "won't remote wakeup, status %d\n", - status); + dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", + status); } /* see 7.1.7.6 */ status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't suspend port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", + port1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1682,52 +1717,6 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, } /* - * usb_port_suspend - suspend a usb device's upstream port - * @udev: device that's no longer in active use - * Context: must be able to sleep; device not locked; pm locks held - * - * Suspends a USB device that isn't in active use, conserving power. - * Devices may wake out of a suspend, if anything important happens, - * using the remote wakeup mechanism. They may also be taken out of - * suspend by the host, using usb_port_resume(). It's also routine - * to disconnect devices while they are suspended. - * - * This only affects the USB hardware for a device; its interfaces - * (and, for hubs, child devices) must already have been suspended. - * - * Suspending OTG devices may trigger HNP, if that's been enabled - * between a pair of dual-role devices. That will change roles, such - * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. - * - * Devices on USB hub ports have only one "suspend" state, corresponding - * to ACPI D2, "may cause the device to lose some context". - * State transitions include: - * - * - suspend, resume ... when the VBUS power link stays live - * - suspend, disconnect ... VBUS lost - * - * 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 - * timer, no SRP, no requests through sysfs. - * - * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when - * the root hub for their bus goes into global suspend ... so we don't - * (falsely) update the device power state to say it suspended. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_suspend(struct usb_device *udev) -{ - int status = 0; - - status = hub_port_suspend(hdev_to_hub(udev->parent), - udev->portnum, udev); - return status; -} - -/* * If the USB "suspend" state is in use (rather than "global suspend"), * many devices will be individually taken out of suspend state using * special" resume" signaling. These routines kick in shortly after @@ -1760,11 +1749,10 @@ static int finish_port_resume(struct usb_device *udev) if (status >= 0) status = (status == 2 ? 0 : -ENODEV); - if (status) - dev_dbg(&udev->dev, - "gone after usb resume? status %d\n", - status); - else if (udev->actconfig) { + if (status) { + dev_dbg(&udev->dev, "gone after usb resume? status %d\n", + status); + } else if (udev->actconfig) { le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { status = usb_control_msg(udev, @@ -1783,11 +1771,25 @@ static int finish_port_resume(struct usb_device *udev) return status; } -static int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) +/* + * usb_port_resume - re-activate a suspended usb device's upstream port + * @udev: device to re-activate, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * This will re-activate the suspended device, increasing power usage + * while letting drivers communicate again with its endpoints. + * USB resume explicitly guarantees that the power session between + * the host and the device is the same as it was when the device + * suspended. + * + * Returns 0 on success, else negative errno. + */ +int usb_port_resume(struct usb_device *udev) { - int status; - u16 portchange, portstatus; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; + u16 portchange, portstatus; /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); @@ -1802,9 +1804,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) status = clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't resume port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", + port1, status); } else { /* drive resume for at least 20 msec */ dev_dbg(&udev->dev, "usb %sresume\n", @@ -1839,8 +1840,10 @@ SuspendCleared: status = finish_port_resume(udev); } } - if (status < 0) + if (status < 0) { + dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } clear_bit(port1, hub->busy_bits); if (!hub->hdev->parent && !hub->busy_bits[0]) @@ -1849,30 +1852,6 @@ SuspendCleared: return status; } -/* - * usb_port_resume - re-activate a suspended usb device's upstream port - * @udev: device to re-activate - * Context: must be able to sleep; device not locked; pm locks held - * - * This will re-activate the suspended device, increasing power usage - * while letting drivers communicate again with its endpoints. - * USB resume explicitly guarantees that the power session between - * the host and the device is the same as it was when the device - * suspended. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_resume(struct usb_device *udev) -{ - int status; - - status = hub_port_resume(hdev_to_hub(udev->parent), - udev->portnum, udev); - if (status < 0) - dev_dbg(&udev->dev, "can't resume, status %d\n", status); - return status; -} - static int remote_wakeup(struct usb_device *udev) { int status = 0; @@ -1896,18 +1875,6 @@ int usb_port_suspend(struct usb_device *udev) return 0; } -static inline int -finish_port_resume(struct usb_device *udev) -{ - return 0; -} - -static inline int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) -{ - return 0; -} - int usb_port_resume(struct usb_device *udev) { return 0; |