diff options
-rw-r--r-- | drivers/usb/core/driver.c | 131 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 2 | ||||
-rw-r--r-- | include/linux/usb.h | 1 |
4 files changed, 140 insertions, 21 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8da1a56659be..ddb54e14a5c5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev) | |||
201 | 201 | ||
202 | intf = to_usb_interface(dev); | 202 | intf = to_usb_interface(dev); |
203 | udev = interface_to_usbdev(intf); | 203 | udev = interface_to_usbdev(intf); |
204 | intf->needs_binding = 0; | ||
204 | 205 | ||
205 | if (udev->authorized == 0) { | 206 | if (udev->authorized == 0) { |
206 | dev_err(&intf->dev, "Device is not authorized for usage\n"); | 207 | dev_err(&intf->dev, "Device is not authorized for usage\n"); |
@@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, | |||
311 | 312 | ||
312 | dev->driver = &driver->drvwrap.driver; | 313 | dev->driver = &driver->drvwrap.driver; |
313 | usb_set_intfdata(iface, priv); | 314 | usb_set_intfdata(iface, priv); |
315 | iface->needs_binding = 0; | ||
314 | 316 | ||
315 | usb_pm_lock(udev); | 317 | usb_pm_lock(udev); |
316 | iface->condition = USB_INTERFACE_BOUND; | 318 | iface->condition = USB_INTERFACE_BOUND; |
@@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver) | |||
772 | } | 774 | } |
773 | EXPORT_SYMBOL_GPL(usb_deregister); | 775 | EXPORT_SYMBOL_GPL(usb_deregister); |
774 | 776 | ||
777 | |||
778 | /* Forced unbinding of a USB interface driver, either because | ||
779 | * it doesn't support pre_reset/post_reset/reset_resume or | ||
780 | * because it doesn't support suspend/resume. | ||
781 | * | ||
782 | * The caller must hold @intf's device's lock, but not its pm_mutex | ||
783 | * and not @intf->dev.sem. | ||
784 | */ | ||
785 | void usb_forced_unbind_intf(struct usb_interface *intf) | ||
786 | { | ||
787 | struct usb_driver *driver = to_usb_driver(intf->dev.driver); | ||
788 | |||
789 | dev_dbg(&intf->dev, "forced unbind\n"); | ||
790 | usb_driver_release_interface(driver, intf); | ||
791 | |||
792 | /* Mark the interface for later rebinding */ | ||
793 | intf->needs_binding = 1; | ||
794 | } | ||
795 | |||
796 | /* Delayed forced unbinding of a USB interface driver and scan | ||
797 | * for rebinding. | ||
798 | * | ||
799 | * The caller must hold @intf's device's lock, but not its pm_mutex | ||
800 | * and not @intf->dev.sem. | ||
801 | * | ||
802 | * FIXME: The caller must block system sleep transitions. | ||
803 | */ | ||
804 | void usb_rebind_intf(struct usb_interface *intf) | ||
805 | { | ||
806 | int rc; | ||
807 | |||
808 | /* Delayed unbind of an existing driver */ | ||
809 | if (intf->dev.driver) { | ||
810 | struct usb_driver *driver = | ||
811 | to_usb_driver(intf->dev.driver); | ||
812 | |||
813 | dev_dbg(&intf->dev, "forced unbind\n"); | ||
814 | usb_driver_release_interface(driver, intf); | ||
815 | } | ||
816 | |||
817 | /* Try to rebind the interface */ | ||
818 | intf->needs_binding = 0; | ||
819 | rc = device_attach(&intf->dev); | ||
820 | if (rc < 0) | ||
821 | dev_warn(&intf->dev, "rebind failed: %d\n", rc); | ||
822 | } | ||
823 | |||
824 | #define DO_UNBIND 0 | ||
825 | #define DO_REBIND 1 | ||
826 | |||
827 | /* Unbind drivers for @udev's interfaces that don't support suspend/resume, | ||
828 | * or rebind interfaces that have been unbound, according to @action. | ||
829 | * | ||
830 | * The caller must hold @udev's device lock. | ||
831 | * FIXME: For rebinds, the caller must block system sleep transitions. | ||
832 | */ | ||
833 | static void do_unbind_rebind(struct usb_device *udev, int action) | ||
834 | { | ||
835 | struct usb_host_config *config; | ||
836 | int i; | ||
837 | struct usb_interface *intf; | ||
838 | struct usb_driver *drv; | ||
839 | |||
840 | config = udev->actconfig; | ||
841 | if (config) { | ||
842 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { | ||
843 | intf = config->interface[i]; | ||
844 | switch (action) { | ||
845 | case DO_UNBIND: | ||
846 | if (intf->dev.driver) { | ||
847 | drv = to_usb_driver(intf->dev.driver); | ||
848 | if (!drv->suspend || !drv->resume) | ||
849 | usb_forced_unbind_intf(intf); | ||
850 | } | ||
851 | break; | ||
852 | case DO_REBIND: | ||
853 | if (intf->needs_binding) { | ||
854 | |||
855 | /* FIXME: The next line is needed because we are going to probe | ||
856 | * the interface, but as far as the PM core is concerned the | ||
857 | * interface is still suspended. The problem wouldn't exist | ||
858 | * if we could rebind the interface during the interface's own | ||
859 | * resume() call, but at the time the usb_device isn't locked! | ||
860 | * | ||
861 | * The real solution will be to carry this out during the device's | ||
862 | * complete() callback. Until that is implemented, we have to | ||
863 | * use this hack. | ||
864 | */ | ||
865 | // intf->dev.power.sleeping = 0; | ||
866 | |||
867 | usb_rebind_intf(intf); | ||
868 | } | ||
869 | break; | ||
870 | } | ||
871 | } | ||
872 | } | ||
873 | } | ||
874 | |||
775 | #ifdef CONFIG_PM | 875 | #ifdef CONFIG_PM |
776 | 876 | ||
777 | /* Caller has locked udev's pm_mutex */ | 877 | /* Caller has locked udev's pm_mutex */ |
@@ -841,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) | |||
841 | goto done; | 941 | goto done; |
842 | driver = to_usb_driver(intf->dev.driver); | 942 | driver = to_usb_driver(intf->dev.driver); |
843 | 943 | ||
844 | if (driver->suspend && driver->resume) { | 944 | if (driver->suspend) { |
845 | status = driver->suspend(intf, msg); | 945 | status = driver->suspend(intf, msg); |
846 | if (status == 0) | 946 | if (status == 0) |
847 | mark_quiesced(intf); | 947 | mark_quiesced(intf); |
@@ -849,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) | |||
849 | dev_err(&intf->dev, "%s error %d\n", | 949 | dev_err(&intf->dev, "%s error %d\n", |
850 | "suspend", status); | 950 | "suspend", status); |
851 | } else { | 951 | } else { |
852 | /* | 952 | /* Later we will unbind the driver and reprobe */ |
853 | * FIXME else if there's no suspend method, disconnect... | 953 | intf->needs_binding = 1; |
854 | * Not possible if auto_pm is set... | 954 | dev_warn(&intf->dev, "no %s for driver %s?\n", |
855 | */ | 955 | "suspend", driver->name); |
856 | dev_warn(&intf->dev, "no suspend for driver %s?\n", | ||
857 | driver->name); | ||
858 | mark_quiesced(intf); | 956 | mark_quiesced(intf); |
859 | } | 957 | } |
860 | 958 | ||
@@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) | |||
878 | goto done; | 976 | goto done; |
879 | 977 | ||
880 | /* Can't resume it if it doesn't have a driver. */ | 978 | /* Can't resume it if it doesn't have a driver. */ |
881 | if (intf->condition == USB_INTERFACE_UNBOUND) { | 979 | if (intf->condition == USB_INTERFACE_UNBOUND) |
882 | status = -ENOTCONN; | 980 | goto done; |
981 | |||
982 | /* Don't resume if the interface is marked for rebinding */ | ||
983 | if (intf->needs_binding) | ||
883 | goto done; | 984 | goto done; |
884 | } | ||
885 | driver = to_usb_driver(intf->dev.driver); | 985 | driver = to_usb_driver(intf->dev.driver); |
886 | 986 | ||
887 | if (reset_resume) { | 987 | if (reset_resume) { |
@@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) | |||
891 | dev_err(&intf->dev, "%s error %d\n", | 991 | dev_err(&intf->dev, "%s error %d\n", |
892 | "reset_resume", status); | 992 | "reset_resume", status); |
893 | } else { | 993 | } else { |
894 | /* status = -EOPNOTSUPP; */ | 994 | intf->needs_binding = 1; |
895 | dev_warn(&intf->dev, "no %s for driver %s?\n", | 995 | dev_warn(&intf->dev, "no %s for driver %s?\n", |
896 | "reset_resume", driver->name); | 996 | "reset_resume", driver->name); |
897 | } | 997 | } |
@@ -902,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) | |||
902 | dev_err(&intf->dev, "%s error %d\n", | 1002 | dev_err(&intf->dev, "%s error %d\n", |
903 | "resume", status); | 1003 | "resume", status); |
904 | } else { | 1004 | } else { |
905 | /* status = -EOPNOTSUPP; */ | 1005 | intf->needs_binding = 1; |
906 | dev_warn(&intf->dev, "no %s for driver %s?\n", | 1006 | dev_warn(&intf->dev, "no %s for driver %s?\n", |
907 | "resume", driver->name); | 1007 | "resume", driver->name); |
908 | } | 1008 | } |
@@ -910,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) | |||
910 | 1010 | ||
911 | done: | 1011 | done: |
912 | dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); | 1012 | dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); |
913 | if (status == 0) | 1013 | if (status == 0 && intf->condition == USB_INTERFACE_BOUND) |
914 | mark_active(intf); | 1014 | mark_active(intf); |
915 | 1015 | ||
916 | /* FIXME: Unbind the driver and reprobe if the resume failed | 1016 | /* Later we will unbind the driver and/or reprobe, if necessary */ |
917 | * (not possible if auto_pm is set) */ | ||
918 | return status; | 1017 | return status; |
919 | } | 1018 | } |
920 | 1019 | ||
@@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) | |||
1470 | { | 1569 | { |
1471 | int status; | 1570 | int status; |
1472 | 1571 | ||
1572 | do_unbind_rebind(udev, DO_UNBIND); | ||
1473 | usb_pm_lock(udev); | 1573 | usb_pm_lock(udev); |
1474 | udev->auto_pm = 0; | 1574 | udev->auto_pm = 0; |
1475 | status = usb_suspend_both(udev, msg); | 1575 | status = usb_suspend_both(udev, msg); |
@@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev) | |||
1497 | status = usb_resume_both(udev); | 1597 | status = usb_resume_both(udev); |
1498 | udev->last_busy = jiffies; | 1598 | udev->last_busy = jiffies; |
1499 | usb_pm_unlock(udev); | 1599 | usb_pm_unlock(udev); |
1600 | do_unbind_rebind(udev, DO_REBIND); | ||
1500 | 1601 | ||
1501 | /* Now that the device is awake, we can start trying to autosuspend | 1602 | /* Now that the device is awake, we can start trying to autosuspend |
1502 | * it again. */ | 1603 | * it again. */ |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bb3ecc4c08f2..f1efabbc1ca2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -3367,6 +3367,11 @@ re_enumerate: | |||
3367 | * this from a driver probe() routine after downloading new firmware. | 3367 | * this from a driver probe() routine after downloading new firmware. |
3368 | * For calls that might not occur during probe(), drivers should lock | 3368 | * For calls that might not occur during probe(), drivers should lock |
3369 | * the device using usb_lock_device_for_reset(). | 3369 | * the device using usb_lock_device_for_reset(). |
3370 | * | ||
3371 | * If an interface is currently being probed or disconnected, we assume | ||
3372 | * its driver knows how to handle resets. For all other interfaces, | ||
3373 | * if the driver doesn't have pre_reset and post_reset methods then | ||
3374 | * we attempt to unbind it and rebind afterward. | ||
3370 | */ | 3375 | */ |
3371 | int usb_reset_device(struct usb_device *udev) | 3376 | int usb_reset_device(struct usb_device *udev) |
3372 | { | 3377 | { |
@@ -3388,12 +3393,17 @@ int usb_reset_device(struct usb_device *udev) | |||
3388 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { | 3393 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { |
3389 | struct usb_interface *cintf = config->interface[i]; | 3394 | struct usb_interface *cintf = config->interface[i]; |
3390 | struct usb_driver *drv; | 3395 | struct usb_driver *drv; |
3396 | int unbind = 0; | ||
3391 | 3397 | ||
3392 | if (cintf->dev.driver) { | 3398 | if (cintf->dev.driver) { |
3393 | drv = to_usb_driver(cintf->dev.driver); | 3399 | drv = to_usb_driver(cintf->dev.driver); |
3394 | if (drv->pre_reset) | 3400 | if (drv->pre_reset && drv->post_reset) |
3395 | (drv->pre_reset)(cintf); | 3401 | unbind = (drv->pre_reset)(cintf); |
3396 | /* FIXME: Unbind if pre_reset returns an error or isn't defined */ | 3402 | else if (cintf->condition == |
3403 | USB_INTERFACE_BOUND) | ||
3404 | unbind = 1; | ||
3405 | if (unbind) | ||
3406 | usb_forced_unbind_intf(cintf); | ||
3397 | } | 3407 | } |
3398 | } | 3408 | } |
3399 | } | 3409 | } |
@@ -3404,13 +3414,18 @@ int usb_reset_device(struct usb_device *udev) | |||
3404 | for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { | 3414 | for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { |
3405 | struct usb_interface *cintf = config->interface[i]; | 3415 | struct usb_interface *cintf = config->interface[i]; |
3406 | struct usb_driver *drv; | 3416 | struct usb_driver *drv; |
3417 | int rebind = cintf->needs_binding; | ||
3407 | 3418 | ||
3408 | if (cintf->dev.driver) { | 3419 | if (!rebind && cintf->dev.driver) { |
3409 | drv = to_usb_driver(cintf->dev.driver); | 3420 | drv = to_usb_driver(cintf->dev.driver); |
3410 | if (drv->post_reset) | 3421 | if (drv->post_reset) |
3411 | (drv->post_reset)(cintf); | 3422 | rebind = (drv->post_reset)(cintf); |
3412 | /* FIXME: Unbind if post_reset returns an error or isn't defined */ | 3423 | else if (cintf->condition == |
3424 | USB_INTERFACE_BOUND) | ||
3425 | rebind = 1; | ||
3413 | } | 3426 | } |
3427 | if (rebind) | ||
3428 | usb_rebind_intf(cintf); | ||
3414 | } | 3429 | } |
3415 | } | 3430 | } |
3416 | 3431 | ||
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1a8bc21c335e..d3eb0a29bca1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev); | |||
29 | extern void usb_kick_khubd(struct usb_device *dev); | 29 | extern void usb_kick_khubd(struct usb_device *dev); |
30 | extern int usb_match_device(struct usb_device *dev, | 30 | extern int usb_match_device(struct usb_device *dev, |
31 | const struct usb_device_id *id); | 31 | const struct usb_device_id *id); |
32 | extern void usb_forced_unbind_intf(struct usb_interface *intf); | ||
33 | extern void usb_rebind_intf(struct usb_interface *intf); | ||
32 | 34 | ||
33 | extern int usb_hub_init(void); | 35 | extern int usb_hub_init(void); |
34 | extern void usb_hub_cleanup(void); | 36 | extern void usb_hub_cleanup(void); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 3cc8db5254d1..5811c5da69f9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -160,6 +160,7 @@ struct usb_interface { | |||
160 | unsigned is_active:1; /* the interface is not suspended */ | 160 | unsigned is_active:1; /* the interface is not suspended */ |
161 | unsigned sysfs_files_created:1; /* the sysfs attributes exist */ | 161 | unsigned sysfs_files_created:1; /* the sysfs attributes exist */ |
162 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ | 162 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ |
163 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ | ||
163 | 164 | ||
164 | struct device dev; /* interface specific device info */ | 165 | struct device dev; /* interface specific device info */ |
165 | struct device *usb_dev; | 166 | struct device *usb_dev; |