diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 59 |
1 files changed, 35 insertions, 24 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b3137fa65f2a..ba9509454ed5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -3488,6 +3488,39 @@ done: | |||
3488 | hcd->driver->relinquish_port(hcd, port1); | 3488 | hcd->driver->relinquish_port(hcd, port1); |
3489 | } | 3489 | } |
3490 | 3490 | ||
3491 | /* Returns 1 if there was a remote wakeup and a connect status change. */ | ||
3492 | static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, | ||
3493 | u16 portchange) | ||
3494 | { | ||
3495 | struct usb_device *hdev; | ||
3496 | struct usb_device *udev; | ||
3497 | int connect_change = 0; | ||
3498 | int ret; | ||
3499 | |||
3500 | hdev = hub->hdev; | ||
3501 | if (!(portchange & USB_PORT_STAT_C_SUSPEND)) | ||
3502 | return 0; | ||
3503 | clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); | ||
3504 | |||
3505 | udev = hdev->children[port-1]; | ||
3506 | if (udev) { | ||
3507 | /* TRSMRCY = 10 msec */ | ||
3508 | msleep(10); | ||
3509 | |||
3510 | usb_lock_device(udev); | ||
3511 | ret = usb_remote_wakeup(udev); | ||
3512 | usb_unlock_device(udev); | ||
3513 | if (ret < 0) | ||
3514 | connect_change = 1; | ||
3515 | } else { | ||
3516 | ret = -ENODEV; | ||
3517 | hub_port_disable(hub, port, 1); | ||
3518 | } | ||
3519 | dev_dbg(hub->intfdev, "resume on port %d, status %d\n", | ||
3520 | port, ret); | ||
3521 | return connect_change; | ||
3522 | } | ||
3523 | |||
3491 | static void hub_events(void) | 3524 | static void hub_events(void) |
3492 | { | 3525 | { |
3493 | struct list_head *tmp; | 3526 | struct list_head *tmp; |
@@ -3621,31 +3654,9 @@ static void hub_events(void) | |||
3621 | } | 3654 | } |
3622 | } | 3655 | } |
3623 | 3656 | ||
3624 | if (portchange & USB_PORT_STAT_C_SUSPEND) { | 3657 | if (hub_handle_remote_wakeup(hub, i, portchange)) |
3625 | struct usb_device *udev; | 3658 | connect_change = 1; |
3626 | 3659 | ||
3627 | clear_port_feature(hdev, i, | ||
3628 | USB_PORT_FEAT_C_SUSPEND); | ||
3629 | udev = hdev->children[i-1]; | ||
3630 | if (udev) { | ||
3631 | /* TRSMRCY = 10 msec */ | ||
3632 | msleep(10); | ||
3633 | |||
3634 | usb_lock_device(udev); | ||
3635 | ret = usb_remote_wakeup(hdev-> | ||
3636 | children[i-1]); | ||
3637 | usb_unlock_device(udev); | ||
3638 | if (ret < 0) | ||
3639 | connect_change = 1; | ||
3640 | } else { | ||
3641 | ret = -ENODEV; | ||
3642 | hub_port_disable(hub, i, 1); | ||
3643 | } | ||
3644 | dev_dbg (hub_dev, | ||
3645 | "resume on port %d, status %d\n", | ||
3646 | i, ret); | ||
3647 | } | ||
3648 | |||
3649 | if (portchange & USB_PORT_STAT_C_OVERCURRENT) { | 3660 | if (portchange & USB_PORT_STAT_C_OVERCURRENT) { |
3650 | u16 status = 0; | 3661 | u16 status = 0; |
3651 | u16 unused; | 3662 | u16 unused; |