diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index bf1585b203cab4a94ba0cf6999774431090143cb..0a0e8cea0afc6d84fc822cd4cac0642cdba6592f 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1537,14 +1537,11 @@ static int usb_resume(struct device *dev) udev = to_usb_device(dev); /* If udev->skip_sys_resume is set then udev was already suspended - * when the system suspend started, so we don't want to resume - * udev during this system wakeup. However a reset-resume counts - * as a wakeup event, so allow a reset-resume to occur if remote - * wakeup is enabled. */ - if (udev->skip_sys_resume) { - if (!(udev->reset_resume && udev->do_remote_wakeup)) - return -EHOSTUNREACH; - } + * when the system sleep started, so we don't want to resume it + * during this system wakeup. + */ + if (udev->skip_sys_resume) + return 0; return usb_external_resume_device(udev); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 054a76dc5d5be49ca2c248e7c1b788effd96e729..8ea095e59099baabaae7e6b1cef6fd1d0b3c215c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -690,18 +690,11 @@ static void hub_restart(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else if (udev->persist_enabled) { - /* Turn off the status changes to prevent khubd - * from disconnecting the device. - */ - 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); #ifdef CONFIG_PM udev->reset_resume = 1; #endif + set_bit(port1, hub->change_bits); + } else { /* The power session is gone; tell khubd */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); @@ -2075,17 +2068,16 @@ int usb_port_resume(struct usb_device *udev) return status; } +/* caller has locked udev */ static int remote_wakeup(struct usb_device *udev) { int status = 0; - usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); usb_mark_last_busy(udev); status = usb_external_resume_device(udev); } - usb_unlock_device(udev); return status; } @@ -2632,6 +2624,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, struct usb_hcd *hcd = bus_to_hcd(hdev->bus); unsigned wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + struct usb_device *udev; int status, i; dev_dbg (hub_dev, @@ -2666,8 +2659,45 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } + /* Try to resuscitate an existing device */ + udev = hdev->children[port1-1]; + if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && + udev->state != USB_STATE_NOTATTACHED) { + + usb_lock_device(udev); + if (portstatus & USB_PORT_STAT_ENABLE) { + status = 0; /* Nothing to do */ + } else if (!udev->persist_enabled) { + status = -ENODEV; /* Mustn't resuscitate */ + +#ifdef CONFIG_USB_SUSPEND + } else if (udev->state == USB_STATE_SUSPENDED) { + /* For a suspended device, treat this as a + * remote wakeup event. + */ + if (udev->do_remote_wakeup) + status = remote_wakeup(udev); + + /* Otherwise leave it be; devices can't tell the + * difference between suspended and disabled. + */ + else + status = 0; +#endif + + } else { + status = usb_reset_composite_device(udev, NULL); + } + usb_unlock_device(udev); + + if (status == 0) { + clear_bit(port1, hub->change_bits); + return; + } + } + /* Disconnect any existing devices under this port */ - if (hdev->children[port1-1]) + if (udev) usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); @@ -2685,7 +2715,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } for (i = 0; i < SET_CONFIG_TRIES; i++) { - struct usb_device *udev; /* reallocate for each attempt, since references * to the previous one can escape in various ways @@ -2944,11 +2973,16 @@ static void hub_events(void) } if (portchange & USB_PORT_STAT_C_SUSPEND) { + struct usb_device *udev; + clear_port_feature(hdev, i, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i-1]) { + udev = hdev->children[i-1]; + if (udev) { + usb_lock_device(udev); ret = remote_wakeup(hdev-> children[i-1]); + usb_unlock_device(udev); if (ret < 0) connect_change = 1; } else {