|
@@ -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,
|
|
@@ -1681,52 +1716,6 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * 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
|
|
@@ -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;
|