|
@@ -644,6 +644,81 @@ static void hub_stop(struct usb_hub *hub)
|
|
hub_quiesce(hub);
|
|
hub_quiesce(hub);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define HUB_RESET 1
|
|
|
|
+#define HUB_RESUME 2
|
|
|
|
+#define HUB_RESET_RESUME 3
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
|
+
|
|
|
|
+static void hub_restart(struct usb_hub *hub, int type)
|
|
|
|
+{
|
|
|
|
+ struct usb_device *hdev = hub->hdev;
|
|
|
|
+ int port1;
|
|
|
|
+
|
|
|
|
+ /* Check each of the children to see if they require
|
|
|
|
+ * USB-PERSIST handling or disconnection. Also check
|
|
|
|
+ * each unoccupied port to make sure it is still disabled.
|
|
|
|
+ */
|
|
|
|
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
|
|
|
+ struct usb_device *udev = hdev->children[port1-1];
|
|
|
|
+ int status = 0;
|
|
|
|
+ u16 portstatus, portchange;
|
|
|
|
+
|
|
|
|
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
|
|
|
|
+ if (type != HUB_RESET) {
|
|
|
|
+ status = hub_port_status(hub, port1,
|
|
|
|
+ &portstatus, &portchange);
|
|
|
|
+ if (status == 0 && (portstatus &
|
|
|
|
+ USB_PORT_STAT_ENABLE))
|
|
|
|
+ clear_port_feature(hdev, port1,
|
|
|
|
+ USB_PORT_FEAT_ENABLE);
|
|
|
|
+ }
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Was the power session lost while we were suspended? */
|
|
|
|
+ switch (type) {
|
|
|
|
+ case HUB_RESET_RESUME:
|
|
|
|
+ portstatus = 0;
|
|
|
|
+ portchange = USB_PORT_STAT_C_CONNECTION;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case HUB_RESET:
|
|
|
|
+ case HUB_RESUME:
|
|
|
|
+ status = hub_port_status(hub, port1,
|
|
|
|
+ &portstatus, &portchange);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* For "USB_PERSIST"-enabled children we must
|
|
|
|
+ * mark the child device for reset-resume and
|
|
|
|
+ * turn off the various status changes to prevent
|
|
|
|
+ * khubd from disconnecting it later.
|
|
|
|
+ */
|
|
|
|
+ if (USB_PERSIST && udev->persist_enabled && status == 0 &&
|
|
|
|
+ !(portstatus & USB_PORT_STAT_ENABLE)) {
|
|
|
|
+ if (portchange & USB_PORT_STAT_C_ENABLE)
|
|
|
|
+ clear_port_feature(hub->hdev, port1,
|
|
|
|
+ USB_PORT_FEAT_C_ENABLE);
|
|
|
|
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
|
|
|
|
+ clear_port_feature(hub->hdev, port1,
|
|
|
|
+ USB_PORT_FEAT_C_CONNECTION);
|
|
|
|
+ udev->reset_resume = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Otherwise for a reset_resume we must disconnect the child,
|
|
|
|
+ * but as we may not lock the child device here
|
|
|
|
+ * we have to do a "logical" disconnect.
|
|
|
|
+ */
|
|
|
|
+ else if (type == HUB_RESET_RESUME)
|
|
|
|
+ hub_port_logical_disconnect(hub, port1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ hub_activate(hub);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#endif /* CONFIG_PM */
|
|
|
|
+
|
|
/* caller has locked the hub device */
|
|
/* caller has locked the hub device */
|
|
static int hub_pre_reset(struct usb_interface *intf)
|
|
static int hub_pre_reset(struct usb_interface *intf)
|
|
{
|
|
{
|
|
@@ -2015,49 +2090,20 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|
|
|
|
|
static int hub_resume(struct usb_interface *intf)
|
|
static int hub_resume(struct usb_interface *intf)
|
|
{
|
|
{
|
|
- struct usb_hub *hub = usb_get_intfdata (intf);
|
|
|
|
-
|
|
|
|
- dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
|
|
|
|
|
|
+ struct usb_hub *hub = usb_get_intfdata(intf);
|
|
|
|
|
|
- /* tell khubd to look for changes on this hub */
|
|
|
|
- hub_activate(hub);
|
|
|
|
|
|
+ dev_dbg(&intf->dev, "%s\n", __func__);
|
|
|
|
+ hub_restart(hub, HUB_RESUME);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int hub_reset_resume(struct usb_interface *intf)
|
|
static int hub_reset_resume(struct usb_interface *intf)
|
|
{
|
|
{
|
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
|
- struct usb_device *hdev = hub->hdev;
|
|
|
|
- int port1;
|
|
|
|
|
|
|
|
|
|
+ dev_dbg(&intf->dev, "%s\n", __func__);
|
|
hub_power_on(hub);
|
|
hub_power_on(hub);
|
|
-
|
|
|
|
- for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
|
|
|
- struct usb_device *child = hdev->children[port1-1];
|
|
|
|
-
|
|
|
|
- if (child) {
|
|
|
|
-
|
|
|
|
- /* For "USB_PERSIST"-enabled children we must
|
|
|
|
- * mark the child device for reset-resume and
|
|
|
|
- * turn off the connect-change status to prevent
|
|
|
|
- * khubd from disconnecting it later.
|
|
|
|
- */
|
|
|
|
- if (USB_PERSIST && child->persist_enabled) {
|
|
|
|
- child->reset_resume = 1;
|
|
|
|
- clear_port_feature(hdev, port1,
|
|
|
|
- USB_PORT_FEAT_C_CONNECTION);
|
|
|
|
-
|
|
|
|
- /* Otherwise we must disconnect the child,
|
|
|
|
- * but as we may not lock the child device here
|
|
|
|
- * we have to do a "logical" disconnect.
|
|
|
|
- */
|
|
|
|
- } else {
|
|
|
|
- hub_port_logical_disconnect(hub, port1);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- hub_activate(hub);
|
|
|
|
|
|
+ hub_restart(hub, HUB_RESET_RESUME);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|