diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-05-30 15:38:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:34:30 -0400 |
commit | f07600cf9eb3ee92777b2001e564faa413144a99 (patch) | |
tree | e48f2e3051fde642e80269bf9c54b289d4abdb44 /drivers/usb | |
parent | 624d6c0732d2c4ac00945ad79dbb6ff39ba90ee3 (diff) |
USB: add reset_resume method
This patch (as918) introduces a new USB driver method: reset_resume.
It is called when a device needs to be reset as part of a resume
procedure (whether because of a device quirk or because of the
USB-Persist facility), thereby taking over a role formerly assigned to
the post_reset method. As a consequence, post_reset no longer needs
an argument indicating whether it is being called as part of a
reset-resume. This separation of functions makes the code clearer.
In addition, the pre_reset and post_reset method return types are
changed; they now must return an error code. The return value is
unused at present, but at some later time we may unbind drivers and
re-probe if they encounter an error during reset handling.
The existing pre_reset and post_reset methods in the usbhid,
usb-storage, and hub drivers are updated to match the new
requirements. For usbhid the post_reset routine is also used for
reset_resume (duplicate method pointers); for the other drivers a new
reset_resume routine is added. The change to hub.c looks bigger than
it really is, because mark_children_for_reset_resume() gets moved down
next to the new hub_reset_resume() routine.
A minor change to usb-storage makes the usb_stor_report_bus_reset()
routine acquire the host lock instead of requiring the caller to hold
it already.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
CC: Matthew Dharm <mdharm-usb@one-eyed-alien.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/driver.c | 51 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 117 | ||||
-rw-r--r-- | drivers/usb/storage/scsiglue.c | 8 | ||||
-rw-r--r-- | drivers/usb/storage/usb.c | 28 |
4 files changed, 129 insertions, 75 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6c62a6d91484..3cd9af2638fc 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -915,21 +915,37 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) | |||
915 | } | 915 | } |
916 | driver = to_usb_driver(intf->dev.driver); | 916 | driver = to_usb_driver(intf->dev.driver); |
917 | 917 | ||
918 | if (reset_resume && driver->post_reset) | 918 | if (reset_resume) { |
919 | driver->post_reset(intf, reset_resume); | 919 | if (driver->reset_resume) { |
920 | else if (driver->resume) { | 920 | status = driver->reset_resume(intf); |
921 | status = driver->resume(intf); | 921 | if (status) |
922 | if (status) | 922 | dev_err(&intf->dev, "%s error %d\n", |
923 | dev_err(&intf->dev, "%s error %d\n", | 923 | "reset_resume", status); |
924 | "resume", status); | 924 | } else { |
925 | } else | 925 | // status = -EOPNOTSUPP; |
926 | dev_warn(&intf->dev, "no resume for driver %s?\n", | 926 | dev_warn(&intf->dev, "no %s for driver %s?\n", |
927 | driver->name); | 927 | "reset_resume", driver->name); |
928 | } | ||
929 | } else { | ||
930 | if (driver->resume) { | ||
931 | status = driver->resume(intf); | ||
932 | if (status) | ||
933 | dev_err(&intf->dev, "%s error %d\n", | ||
934 | "resume", status); | ||
935 | } else { | ||
936 | // status = -EOPNOTSUPP; | ||
937 | dev_warn(&intf->dev, "no %s for driver %s?\n", | ||
938 | "resume", driver->name); | ||
939 | } | ||
940 | } | ||
928 | 941 | ||
929 | done: | 942 | done: |
930 | dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); | 943 | dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); |
931 | if (status == 0) | 944 | if (status == 0) |
932 | mark_active(intf); | 945 | mark_active(intf); |
946 | |||
947 | /* FIXME: Unbind the driver and reprobe if the resume failed | ||
948 | * (not possible if auto_pm is set) */ | ||
933 | return status; | 949 | return status; |
934 | } | 950 | } |
935 | 951 | ||
@@ -966,6 +982,18 @@ static int autosuspend_check(struct usb_device *udev) | |||
966 | "for autosuspend\n"); | 982 | "for autosuspend\n"); |
967 | return -EOPNOTSUPP; | 983 | return -EOPNOTSUPP; |
968 | } | 984 | } |
985 | |||
986 | /* Don't allow autosuspend if the device will need | ||
987 | * a reset-resume and any of its interface drivers | ||
988 | * doesn't include support. | ||
989 | */ | ||
990 | if (udev->quirks & USB_QUIRK_RESET_RESUME) { | ||
991 | struct usb_driver *driver; | ||
992 | |||
993 | driver = to_usb_driver(intf->dev.driver); | ||
994 | if (!driver->reset_resume) | ||
995 | return -EOPNOTSUPP; | ||
996 | } | ||
969 | } | 997 | } |
970 | } | 998 | } |
971 | 999 | ||
@@ -1146,7 +1174,8 @@ static int usb_resume_both(struct usb_device *udev) | |||
1146 | status = usb_autoresume_device(parent); | 1174 | status = usb_autoresume_device(parent); |
1147 | if (status == 0) { | 1175 | if (status == 0) { |
1148 | status = usb_resume_device(udev); | 1176 | status = usb_resume_device(udev); |
1149 | if (status) { | 1177 | if (status || udev->state == |
1178 | USB_STATE_NOTATTACHED) { | ||
1150 | usb_autosuspend_device(parent); | 1179 | usb_autosuspend_device(parent); |
1151 | 1180 | ||
1152 | /* It's possible usb_resume_device() | 1181 | /* It's possible usb_resume_device() |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ca3dbf84e800..0b8ed414d5cf 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -605,73 +605,26 @@ static void disconnect_all_children(struct usb_hub *hub, int logical) | |||
605 | } | 605 | } |
606 | } | 606 | } |
607 | 607 | ||
608 | #ifdef CONFIG_USB_PERSIST | ||
609 | |||
610 | #define USB_PERSIST 1 | ||
611 | |||
612 | /* For "persistent-device" resets we must mark the child devices for reset | ||
613 | * and turn off a possible connect-change status (so khubd won't disconnect | ||
614 | * them later). | ||
615 | */ | ||
616 | static void mark_children_for_reset_resume(struct usb_hub *hub) | ||
617 | { | ||
618 | struct usb_device *hdev = hub->hdev; | ||
619 | int port1; | ||
620 | |||
621 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | ||
622 | struct usb_device *child = hdev->children[port1-1]; | ||
623 | |||
624 | if (child) { | ||
625 | child->reset_resume = 1; | ||
626 | clear_port_feature(hdev, port1, | ||
627 | USB_PORT_FEAT_C_CONNECTION); | ||
628 | } | ||
629 | } | ||
630 | } | ||
631 | |||
632 | #else | ||
633 | |||
634 | #define USB_PERSIST 0 | ||
635 | |||
636 | static inline void mark_children_for_reset_resume(struct usb_hub *hub) | ||
637 | { } | ||
638 | |||
639 | #endif /* CONFIG_USB_PERSIST */ | ||
640 | |||
641 | /* caller has locked the hub device */ | 608 | /* caller has locked the hub device */ |
642 | static void hub_pre_reset(struct usb_interface *intf) | 609 | static int hub_pre_reset(struct usb_interface *intf) |
643 | { | 610 | { |
644 | struct usb_hub *hub = usb_get_intfdata(intf); | 611 | struct usb_hub *hub = usb_get_intfdata(intf); |
645 | 612 | ||
646 | /* This routine doesn't run as part of a reset-resume, so it's safe | ||
647 | * to disconnect all the drivers below the hub. | ||
648 | */ | ||
649 | disconnect_all_children(hub, 0); | 613 | disconnect_all_children(hub, 0); |
650 | hub_quiesce(hub); | 614 | hub_quiesce(hub); |
615 | return 0; | ||
651 | } | 616 | } |
652 | 617 | ||
653 | /* caller has locked the hub device */ | 618 | /* caller has locked the hub device */ |
654 | static void hub_post_reset(struct usb_interface *intf, int reset_resume) | 619 | static int hub_post_reset(struct usb_interface *intf) |
655 | { | 620 | { |
656 | struct usb_hub *hub = usb_get_intfdata(intf); | 621 | struct usb_hub *hub = usb_get_intfdata(intf); |
657 | 622 | ||
658 | hub_power_on(hub); | 623 | hub_power_on(hub); |
659 | if (reset_resume) { | ||
660 | if (USB_PERSIST) | ||
661 | mark_children_for_reset_resume(hub); | ||
662 | else { | ||
663 | /* Reset-resume doesn't call pre_reset, so we have to | ||
664 | * disconnect the children here. But we may not lock | ||
665 | * the child devices, so we have to do a "logical" | ||
666 | * disconnect. | ||
667 | */ | ||
668 | disconnect_all_children(hub, 1); | ||
669 | } | ||
670 | } | ||
671 | hub_activate(hub); | 624 | hub_activate(hub); |
625 | return 0; | ||
672 | } | 626 | } |
673 | 627 | ||
674 | |||
675 | static int hub_configure(struct usb_hub *hub, | 628 | static int hub_configure(struct usb_hub *hub, |
676 | struct usb_endpoint_descriptor *endpoint) | 629 | struct usb_endpoint_descriptor *endpoint) |
677 | { | 630 | { |
@@ -1931,6 +1884,58 @@ static int hub_resume(struct usb_interface *intf) | |||
1931 | return 0; | 1884 | return 0; |
1932 | } | 1885 | } |
1933 | 1886 | ||
1887 | #ifdef CONFIG_USB_PERSIST | ||
1888 | |||
1889 | #define USB_PERSIST 1 | ||
1890 | |||
1891 | /* For "persistent-device" resets we must mark the child devices for reset | ||
1892 | * and turn off a possible connect-change status (so khubd won't disconnect | ||
1893 | * them later). | ||
1894 | */ | ||
1895 | static void mark_children_for_reset_resume(struct usb_hub *hub) | ||
1896 | { | ||
1897 | struct usb_device *hdev = hub->hdev; | ||
1898 | int port1; | ||
1899 | |||
1900 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | ||
1901 | struct usb_device *child = hdev->children[port1-1]; | ||
1902 | |||
1903 | if (child) { | ||
1904 | child->reset_resume = 1; | ||
1905 | clear_port_feature(hdev, port1, | ||
1906 | USB_PORT_FEAT_C_CONNECTION); | ||
1907 | } | ||
1908 | } | ||
1909 | } | ||
1910 | |||
1911 | #else | ||
1912 | |||
1913 | #define USB_PERSIST 0 | ||
1914 | |||
1915 | static inline void mark_children_for_reset_resume(struct usb_hub *hub) | ||
1916 | { } | ||
1917 | |||
1918 | #endif /* CONFIG_USB_PERSIST */ | ||
1919 | |||
1920 | static int hub_reset_resume(struct usb_interface *intf) | ||
1921 | { | ||
1922 | struct usb_hub *hub = usb_get_intfdata(intf); | ||
1923 | |||
1924 | hub_power_on(hub); | ||
1925 | if (USB_PERSIST) | ||
1926 | mark_children_for_reset_resume(hub); | ||
1927 | else { | ||
1928 | /* Reset-resume doesn't call pre_reset, so we have to | ||
1929 | * disconnect the children here. But we may not lock | ||
1930 | * the child devices, so we have to do a "logical" | ||
1931 | * disconnect. | ||
1932 | */ | ||
1933 | disconnect_all_children(hub, 1); | ||
1934 | } | ||
1935 | hub_activate(hub); | ||
1936 | return 0; | ||
1937 | } | ||
1938 | |||
1934 | #else /* CONFIG_PM */ | 1939 | #else /* CONFIG_PM */ |
1935 | 1940 | ||
1936 | static inline int remote_wakeup(struct usb_device *udev) | 1941 | static inline int remote_wakeup(struct usb_device *udev) |
@@ -1938,8 +1943,9 @@ static inline int remote_wakeup(struct usb_device *udev) | |||
1938 | return 0; | 1943 | return 0; |
1939 | } | 1944 | } |
1940 | 1945 | ||
1941 | #define hub_suspend NULL | 1946 | #define hub_suspend NULL |
1942 | #define hub_resume NULL | 1947 | #define hub_resume NULL |
1948 | #define hub_reset_resume NULL | ||
1943 | #endif | 1949 | #endif |
1944 | 1950 | ||
1945 | 1951 | ||
@@ -2768,6 +2774,7 @@ static struct usb_driver hub_driver = { | |||
2768 | .disconnect = hub_disconnect, | 2774 | .disconnect = hub_disconnect, |
2769 | .suspend = hub_suspend, | 2775 | .suspend = hub_suspend, |
2770 | .resume = hub_resume, | 2776 | .resume = hub_resume, |
2777 | .reset_resume = hub_reset_resume, | ||
2771 | .pre_reset = hub_pre_reset, | 2778 | .pre_reset = hub_pre_reset, |
2772 | .post_reset = hub_post_reset, | 2779 | .post_reset = hub_post_reset, |
2773 | .ioctl = hub_ioctl, | 2780 | .ioctl = hub_ioctl, |
@@ -3021,6 +3028,7 @@ int usb_reset_composite_device(struct usb_device *udev, | |||
3021 | drv = to_usb_driver(cintf->dev.driver); | 3028 | drv = to_usb_driver(cintf->dev.driver); |
3022 | if (drv->pre_reset) | 3029 | if (drv->pre_reset) |
3023 | (drv->pre_reset)(cintf); | 3030 | (drv->pre_reset)(cintf); |
3031 | /* FIXME: Unbind if pre_reset returns an error or isn't defined */ | ||
3024 | } | 3032 | } |
3025 | } | 3033 | } |
3026 | } | 3034 | } |
@@ -3038,7 +3046,8 @@ int usb_reset_composite_device(struct usb_device *udev, | |||
3038 | cintf->dev.driver) { | 3046 | cintf->dev.driver) { |
3039 | drv = to_usb_driver(cintf->dev.driver); | 3047 | drv = to_usb_driver(cintf->dev.driver); |
3040 | if (drv->post_reset) | 3048 | if (drv->post_reset) |
3041 | (drv->post_reset)(cintf, 0); | 3049 | (drv->post_reset)(cintf); |
3050 | /* FIXME: Unbind if post_reset returns an error or isn't defined */ | ||
3042 | } | 3051 | } |
3043 | if (cintf != iface) | 3052 | if (cintf != iface) |
3044 | up(&cintf->dev.sem); | 3053 | up(&cintf->dev.sem); |
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e227f64d5641..1ba19eaa1970 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c | |||
@@ -321,10 +321,14 @@ void usb_stor_report_device_reset(struct us_data *us) | |||
321 | 321 | ||
322 | /* Report a driver-initiated bus reset to the SCSI layer. | 322 | /* Report a driver-initiated bus reset to the SCSI layer. |
323 | * Calling this for a SCSI-initiated reset is unnecessary but harmless. | 323 | * Calling this for a SCSI-initiated reset is unnecessary but harmless. |
324 | * The caller must own the SCSI host lock. */ | 324 | * The caller must not own the SCSI host lock. */ |
325 | void usb_stor_report_bus_reset(struct us_data *us) | 325 | void usb_stor_report_bus_reset(struct us_data *us) |
326 | { | 326 | { |
327 | scsi_report_bus_reset(us_to_host(us), 0); | 327 | struct Scsi_Host *host = us_to_host(us); |
328 | |||
329 | scsi_lock(host); | ||
330 | scsi_report_bus_reset(host, 0); | ||
331 | scsi_unlock(host); | ||
328 | } | 332 | } |
329 | 333 | ||
330 | /*********************************************************************** | 334 | /*********************************************************************** |
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index be4cd8fe4ce6..00521f1d6a6b 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c | |||
@@ -219,6 +219,20 @@ static int storage_resume(struct usb_interface *iface) | |||
219 | return 0; | 219 | return 0; |
220 | } | 220 | } |
221 | 221 | ||
222 | static int storage_reset_resume(struct usb_interface *iface) | ||
223 | { | ||
224 | struct us_data *us = usb_get_intfdata(iface); | ||
225 | |||
226 | US_DEBUGP("%s\n", __FUNCTION__); | ||
227 | |||
228 | /* Report the reset to the SCSI core */ | ||
229 | usb_stor_report_bus_reset(us); | ||
230 | |||
231 | /* FIXME: Notify the subdrivers that they need to reinitialize | ||
232 | * the device */ | ||
233 | return 0; | ||
234 | } | ||
235 | |||
222 | #endif /* CONFIG_PM */ | 236 | #endif /* CONFIG_PM */ |
223 | 237 | ||
224 | /* | 238 | /* |
@@ -226,7 +240,7 @@ static int storage_resume(struct usb_interface *iface) | |||
226 | * a USB port reset, whether from this driver or a different one. | 240 | * a USB port reset, whether from this driver or a different one. |
227 | */ | 241 | */ |
228 | 242 | ||
229 | static void storage_pre_reset(struct usb_interface *iface) | 243 | static int storage_pre_reset(struct usb_interface *iface) |
230 | { | 244 | { |
231 | struct us_data *us = usb_get_intfdata(iface); | 245 | struct us_data *us = usb_get_intfdata(iface); |
232 | 246 | ||
@@ -234,26 +248,23 @@ static void storage_pre_reset(struct usb_interface *iface) | |||
234 | 248 | ||
235 | /* Make sure no command runs during the reset */ | 249 | /* Make sure no command runs during the reset */ |
236 | mutex_lock(&us->dev_mutex); | 250 | mutex_lock(&us->dev_mutex); |
251 | return 0; | ||
237 | } | 252 | } |
238 | 253 | ||
239 | static void storage_post_reset(struct usb_interface *iface, int reset_resume) | 254 | static int storage_post_reset(struct usb_interface *iface) |
240 | { | 255 | { |
241 | struct us_data *us = usb_get_intfdata(iface); | 256 | struct us_data *us = usb_get_intfdata(iface); |
242 | 257 | ||
243 | US_DEBUGP("%s\n", __FUNCTION__); | 258 | US_DEBUGP("%s\n", __FUNCTION__); |
244 | 259 | ||
245 | /* Report the reset to the SCSI core */ | 260 | /* Report the reset to the SCSI core */ |
246 | scsi_lock(us_to_host(us)); | ||
247 | usb_stor_report_bus_reset(us); | 261 | usb_stor_report_bus_reset(us); |
248 | scsi_unlock(us_to_host(us)); | ||
249 | 262 | ||
250 | /* FIXME: Notify the subdrivers that they need to reinitialize | 263 | /* FIXME: Notify the subdrivers that they need to reinitialize |
251 | * the device */ | 264 | * the device */ |
252 | 265 | ||
253 | /* If this is a reset-resume then the pre_reset routine wasn't | 266 | mutex_unlock(&us->dev_mutex); |
254 | * called, so we don't need to unlock the mutex. */ | 267 | return 0; |
255 | if (!reset_resume) | ||
256 | mutex_unlock(&us->dev_mutex); | ||
257 | } | 268 | } |
258 | 269 | ||
259 | /* | 270 | /* |
@@ -1061,6 +1072,7 @@ static struct usb_driver usb_storage_driver = { | |||
1061 | #ifdef CONFIG_PM | 1072 | #ifdef CONFIG_PM |
1062 | .suspend = storage_suspend, | 1073 | .suspend = storage_suspend, |
1063 | .resume = storage_resume, | 1074 | .resume = storage_resume, |
1075 | .reset_resume = storage_reset_resume, | ||
1064 | #endif | 1076 | #endif |
1065 | .pre_reset = storage_pre_reset, | 1077 | .pre_reset = storage_pre_reset, |
1066 | .post_reset = storage_post_reset, | 1078 | .post_reset = storage_post_reset, |