diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 85 |
1 files changed, 71 insertions, 14 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index eb57fcc701d7..4cfe32a16c37 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -644,6 +644,48 @@ static void hub_stop(struct usb_hub *hub) | |||
644 | 644 | ||
645 | #ifdef CONFIG_PM | 645 | #ifdef CONFIG_PM |
646 | 646 | ||
647 | /* Try to identify which devices need USB-PERSIST handling */ | ||
648 | static int persistent_device(struct usb_device *udev) | ||
649 | { | ||
650 | int i; | ||
651 | int retval; | ||
652 | struct usb_host_config *actconfig; | ||
653 | |||
654 | /* Explicitly not marked persistent? */ | ||
655 | if (!udev->persist_enabled) | ||
656 | return 0; | ||
657 | |||
658 | /* No active config? */ | ||
659 | actconfig = udev->actconfig; | ||
660 | if (!actconfig) | ||
661 | return 0; | ||
662 | |||
663 | /* FIXME! We should check whether it's open here or not! */ | ||
664 | |||
665 | /* | ||
666 | * Check that all the interface drivers have a | ||
667 | * 'reset_resume' entrypoint | ||
668 | */ | ||
669 | retval = 0; | ||
670 | for (i = 0; i < actconfig->desc.bNumInterfaces; i++) { | ||
671 | struct usb_interface *intf; | ||
672 | struct usb_driver *driver; | ||
673 | |||
674 | intf = actconfig->interface[i]; | ||
675 | if (!intf->dev.driver) | ||
676 | continue; | ||
677 | driver = to_usb_driver(intf->dev.driver); | ||
678 | if (!driver->reset_resume) | ||
679 | return 0; | ||
680 | /* | ||
681 | * We have at least one driver, and that one | ||
682 | * has a reset_resume method. | ||
683 | */ | ||
684 | retval = 1; | ||
685 | } | ||
686 | return retval; | ||
687 | } | ||
688 | |||
647 | static void hub_restart(struct usb_hub *hub, int type) | 689 | static void hub_restart(struct usb_hub *hub, int type) |
648 | { | 690 | { |
649 | struct usb_device *hdev = hub->hdev; | 691 | struct usb_device *hdev = hub->hdev; |
@@ -671,26 +713,19 @@ static void hub_restart(struct usb_hub *hub, int type) | |||
671 | } | 713 | } |
672 | 714 | ||
673 | /* Was the power session lost while we were suspended? */ | 715 | /* Was the power session lost while we were suspended? */ |
674 | switch (type) { | 716 | status = hub_port_status(hub, port1, &portstatus, &portchange); |
675 | case HUB_RESET_RESUME: | ||
676 | portstatus = 0; | ||
677 | portchange = USB_PORT_STAT_C_CONNECTION; | ||
678 | break; | ||
679 | 717 | ||
680 | case HUB_RESET: | 718 | /* If the device is gone, khubd will handle it later */ |
681 | case HUB_RESUME: | 719 | if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION)) |
682 | status = hub_port_status(hub, port1, | 720 | continue; |
683 | &portstatus, &portchange); | ||
684 | break; | ||
685 | } | ||
686 | 721 | ||
687 | /* For "USB_PERSIST"-enabled children we must | 722 | /* For "USB_PERSIST"-enabled children we must |
688 | * mark the child device for reset-resume and | 723 | * mark the child device for reset-resume and |
689 | * turn off the various status changes to prevent | 724 | * turn off the various status changes to prevent |
690 | * khubd from disconnecting it later. | 725 | * khubd from disconnecting it later. |
691 | */ | 726 | */ |
692 | if (udev->persist_enabled && status == 0 && | 727 | if (status == 0 && !(portstatus & USB_PORT_STAT_ENABLE) && |
693 | !(portstatus & USB_PORT_STAT_ENABLE)) { | 728 | persistent_device(udev)) { |
694 | if (portchange & USB_PORT_STAT_C_ENABLE) | 729 | if (portchange & USB_PORT_STAT_C_ENABLE) |
695 | clear_port_feature(hub->hdev, port1, | 730 | clear_port_feature(hub->hdev, port1, |
696 | USB_PORT_FEAT_C_ENABLE); | 731 | USB_PORT_FEAT_C_ENABLE); |
@@ -1326,6 +1361,12 @@ void usb_disconnect(struct usb_device **pdev) | |||
1326 | 1361 | ||
1327 | usb_unlock_device(udev); | 1362 | usb_unlock_device(udev); |
1328 | 1363 | ||
1364 | /* Remove the device-specific files from sysfs. This must be | ||
1365 | * done with udev unlocked, because some of the attribute | ||
1366 | * routines try to acquire the device lock. | ||
1367 | */ | ||
1368 | usb_remove_sysfs_dev_files(udev); | ||
1369 | |||
1329 | /* Unregister the device. The device driver is responsible | 1370 | /* Unregister the device. The device driver is responsible |
1330 | * for removing the device files from usbfs and sysfs and for | 1371 | * for removing the device files from usbfs and sysfs and for |
1331 | * de-configuring the device. | 1372 | * de-configuring the device. |
@@ -1541,6 +1582,9 @@ int usb_new_device(struct usb_device *udev) | |||
1541 | goto fail; | 1582 | goto fail; |
1542 | } | 1583 | } |
1543 | 1584 | ||
1585 | /* put device-specific files into sysfs */ | ||
1586 | usb_create_sysfs_dev_files(udev); | ||
1587 | |||
1544 | /* Tell the world! */ | 1588 | /* Tell the world! */ |
1545 | announce_device(udev); | 1589 | announce_device(udev); |
1546 | return err; | 1590 | return err; |
@@ -2029,6 +2073,8 @@ int usb_port_resume(struct usb_device *udev) | |||
2029 | } | 2073 | } |
2030 | 2074 | ||
2031 | clear_bit(port1, hub->busy_bits); | 2075 | clear_bit(port1, hub->busy_bits); |
2076 | if (!hub->hdev->parent && !hub->busy_bits[0]) | ||
2077 | usb_enable_root_hub_irq(hub->hdev->bus); | ||
2032 | 2078 | ||
2033 | if (status == 0) | 2079 | if (status == 0) |
2034 | status = finish_port_resume(udev); | 2080 | status = finish_port_resume(udev); |
@@ -2744,7 +2790,11 @@ loop: | |||
2744 | if ((status == -ENOTCONN) || (status == -ENOTSUPP)) | 2790 | if ((status == -ENOTCONN) || (status == -ENOTSUPP)) |
2745 | break; | 2791 | break; |
2746 | } | 2792 | } |
2747 | dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1); | 2793 | if (hub->hdev->parent || |
2794 | !hcd->driver->port_handed_over || | ||
2795 | !(hcd->driver->port_handed_over)(hcd, port1)) | ||
2796 | dev_err(hub_dev, "unable to enumerate USB device on port %d\n", | ||
2797 | port1); | ||
2748 | 2798 | ||
2749 | done: | 2799 | done: |
2750 | hub_port_disable(hub, port1, 1); | 2800 | hub_port_disable(hub, port1, 1); |
@@ -2954,6 +3004,11 @@ static void hub_events(void) | |||
2954 | 3004 | ||
2955 | hub->activating = 0; | 3005 | hub->activating = 0; |
2956 | 3006 | ||
3007 | /* If this is a root hub, tell the HCD it's okay to | ||
3008 | * re-enable port-change interrupts now. */ | ||
3009 | if (!hdev->parent && !hub->busy_bits[0]) | ||
3010 | usb_enable_root_hub_irq(hdev->bus); | ||
3011 | |||
2957 | loop_autopm: | 3012 | loop_autopm: |
2958 | /* Allow autosuspend if we're not going to run again */ | 3013 | /* Allow autosuspend if we're not going to run again */ |
2959 | if (list_empty(&hub->event_list)) | 3014 | if (list_empty(&hub->event_list)) |
@@ -3179,6 +3234,8 @@ int usb_reset_device(struct usb_device *udev) | |||
3179 | break; | 3234 | break; |
3180 | } | 3235 | } |
3181 | clear_bit(port1, parent_hub->busy_bits); | 3236 | clear_bit(port1, parent_hub->busy_bits); |
3237 | if (!parent_hdev->parent && !parent_hub->busy_bits[0]) | ||
3238 | usb_enable_root_hub_irq(parent_hdev->bus); | ||
3182 | 3239 | ||
3183 | if (ret < 0) | 3240 | if (ret < 0) |
3184 | goto re_enumerate; | 3241 | goto re_enumerate; |