diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-05-04 11:52:20 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:29:47 -0400 |
commit | 0458d5b4c9cc4ca0f62625d0144ddc4b4bc97a3c (patch) | |
tree | 8b1fcb4f063ef4aa6f2e3cd41a60d986a1e432d4 | |
parent | ce7cd137fced114d49178b73d468b82096a107fb (diff) |
USB: add USB-Persist facility
This patch (as886) adds the controversial USB-persist facility,
allowing USB devices to persist across a power loss during system
suspend.
The facility is controlled by a new Kconfig option (with appropriate
warnings about the potential dangers); when the option is off the
behavior will remain the same as it is now. But when the option is
on, people will be able to use suspend-to-disk and keep their USB
filesystems intact -- something particularly valuable for small
machines where the root filesystem is on a USB device!
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | Documentation/power/swsusp.txt | 3 | ||||
-rw-r--r-- | Documentation/usb/persist.txt | 144 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/Kconfig | 22 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 39 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 196 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 1 | ||||
-rw-r--r-- | drivers/usb/storage/usb.c | 8 | ||||
-rw-r--r-- | include/linux/usb.h | 8 |
10 files changed, 351 insertions, 77 deletions
diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 5b8d6953f05e..152b510d1bbb 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt | |||
@@ -393,6 +393,9 @@ safest thing is to unmount all filesystems on removable media (such USB, | |||
393 | Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays) | 393 | Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays) |
394 | before suspending; then remount them after resuming. | 394 | before suspending; then remount them after resuming. |
395 | 395 | ||
396 | There is a work-around for this problem. For more information, see | ||
397 | Documentation/usb/persist.txt. | ||
398 | |||
396 | Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were | 399 | Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were |
397 | compiled with the similar configuration files. Anyway I found that | 400 | compiled with the similar configuration files. Anyway I found that |
398 | suspend to disk (and resume) is much slower on 2.6.16 compared to | 401 | suspend to disk (and resume) is much slower on 2.6.16 compared to |
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt new file mode 100644 index 000000000000..6dcd5f884795 --- /dev/null +++ b/Documentation/usb/persist.txt | |||
@@ -0,0 +1,144 @@ | |||
1 | USB device persistence during system suspend | ||
2 | |||
3 | Alan Stern <stern@rowland.harvard.edu> | ||
4 | |||
5 | September 2, 2006 (Updated March 27, 2007) | ||
6 | |||
7 | |||
8 | What is the problem? | ||
9 | |||
10 | According to the USB specification, when a USB bus is suspended the | ||
11 | bus must continue to supply suspend current (around 1-5 mA). This | ||
12 | is so that devices can maintain their internal state and hubs can | ||
13 | detect connect-change events (devices being plugged in or unplugged). | ||
14 | The technical term is "power session". | ||
15 | |||
16 | If a USB device's power session is interrupted then the system is | ||
17 | required to behave as though the device has been unplugged. It's a | ||
18 | conservative approach; in the absence of suspend current the computer | ||
19 | has no way to know what has actually happened. Perhaps the same | ||
20 | device is still attached or perhaps it was removed and a different | ||
21 | device plugged into the port. The system must assume the worst. | ||
22 | |||
23 | By default, Linux behaves according to the spec. If a USB host | ||
24 | controller loses power during a system suspend, then when the system | ||
25 | wakes up all the devices attached to that controller are treated as | ||
26 | though they had disconnected. This is always safe and it is the | ||
27 | "officially correct" thing to do. | ||
28 | |||
29 | For many sorts of devices this behavior doesn't matter in the least. | ||
30 | If the kernel wants to believe that your USB keyboard was unplugged | ||
31 | while the system was asleep and a new keyboard was plugged in when the | ||
32 | system woke up, who cares? It'll still work the same when you type on | ||
33 | it. | ||
34 | |||
35 | Unfortunately problems _can_ arise, particularly with mass-storage | ||
36 | devices. The effect is exactly the same as if the device really had | ||
37 | been unplugged while the system was suspended. If you had a mounted | ||
38 | filesystem on the device, you're out of luck -- everything in that | ||
39 | filesystem is now inaccessible. This is especially annoying if your | ||
40 | root filesystem was located on the device, since your system will | ||
41 | instantly crash. | ||
42 | |||
43 | Loss of power isn't the only mechanism to worry about. Anything that | ||
44 | interrupts a power session will have the same effect. For example, | ||
45 | even though suspend current may have been maintained while the system | ||
46 | was asleep, on many systems during the initial stages of wakeup the | ||
47 | firmware (i.e., the BIOS) resets the motherboard's USB host | ||
48 | controllers. Result: all the power sessions are destroyed and again | ||
49 | it's as though you had unplugged all the USB devices. Yes, it's | ||
50 | entirely the BIOS's fault, but that doesn't do _you_ any good unless | ||
51 | you can convince the BIOS supplier to fix the problem (lots of luck!). | ||
52 | |||
53 | On many systems the USB host controllers will get reset after a | ||
54 | suspend-to-RAM. On almost all systems, no suspend current is | ||
55 | available during suspend-to-disk (also known as swsusp). You can | ||
56 | check the kernel log after resuming to see if either of these has | ||
57 | happened; look for lines saying "root hub lost power or was reset". | ||
58 | |||
59 | In practice, people are forced to unmount any filesystems on a USB | ||
60 | device before suspending. If the root filesystem is on a USB device, | ||
61 | the system can't be suspended at all. (All right, it _can_ be | ||
62 | suspended -- but it will crash as soon as it wakes up, which isn't | ||
63 | much better.) | ||
64 | |||
65 | |||
66 | What is the solution? | ||
67 | |||
68 | Setting CONFIG_USB_PERSIST will cause the kernel to work around these | ||
69 | issues. It enables a mode in which the core USB device data | ||
70 | structures are allowed to persist across a power-session disruption. | ||
71 | It works like this. If the kernel sees that a USB host controller is | ||
72 | not in the expected state during resume (i.e., if the controller was | ||
73 | reset or otherwise had lost power) then it applies a persistence check | ||
74 | to each of the USB devices below that controller. It doesn't try to | ||
75 | resume the device; that can't work once the power session is gone. | ||
76 | Instead it issues a USB port reset and then re-enumerates the device. | ||
77 | (This is exactly the same thing that happens whenever a USB device is | ||
78 | reset.) If the re-enumeration shows that the device now attached to | ||
79 | that port has the same descriptors as before, including the Vendor and | ||
80 | Product IDs, then the kernel continues to use the same device | ||
81 | structure. In effect, the kernel treats the device as though it had | ||
82 | merely been reset instead of unplugged. | ||
83 | |||
84 | If no device is now attached to the port, or if the descriptors are | ||
85 | different from what the kernel remembers, then the treatment is what | ||
86 | you would expect. The kernel destroys the old device structure and | ||
87 | behaves as though the old device had been unplugged and a new device | ||
88 | plugged in, just as it would without the CONFIG_USB_PERSIST option. | ||
89 | |||
90 | The end result is that the USB device remains available and usable. | ||
91 | Filesystem mounts and memory mappings are unaffected, and the world is | ||
92 | now a good and happy place. | ||
93 | |||
94 | |||
95 | Is this the best solution? | ||
96 | |||
97 | Perhaps not. Arguably, keeping track of mounted filesystems and | ||
98 | memory mappings across device disconnects should be handled by a | ||
99 | centralized Logical Volume Manager. Such a solution would allow you | ||
100 | to plug in a USB flash device, create a persistent volume associated | ||
101 | with it, unplug the flash device, plug it back in later, and still | ||
102 | have the same persistent volume associated with the device. As such | ||
103 | it would be more far-reaching than CONFIG_USB_PERSIST. | ||
104 | |||
105 | On the other hand, writing a persistent volume manager would be a big | ||
106 | job and using it would require significant input from the user. This | ||
107 | solution is much quicker and easier -- and it exists now, a giant | ||
108 | point in its favor! | ||
109 | |||
110 | Furthermore, the USB_PERSIST option applies to _all_ USB devices, not | ||
111 | just mass-storage devices. It might turn out to be equally useful for | ||
112 | other device types, such as network interfaces. | ||
113 | |||
114 | |||
115 | WARNING: Using CONFIG_USB_PERSIST can be dangerous!! | ||
116 | |||
117 | When recovering an interrupted power session the kernel does its best | ||
118 | to make sure the USB device hasn't been changed; that is, the same | ||
119 | device is still plugged into the port as before. But the checks | ||
120 | aren't guaranteed to be 100% accurate. | ||
121 | |||
122 | If you replace one USB device with another of the same type (same | ||
123 | manufacturer, same IDs, and so on) there's an excellent chance the | ||
124 | kernel won't detect the change. Serial numbers and other strings are | ||
125 | not compared. In many cases it wouldn't help if they were, because | ||
126 | manufacturers frequently omit serial numbers entirely in their | ||
127 | devices. | ||
128 | |||
129 | Furthermore it's quite possible to leave a USB device exactly the same | ||
130 | while changing its media. If you replace the flash memory card in a | ||
131 | USB card reader while the system is asleep, the kernel will have no | ||
132 | way to know you did it. The kernel will assume that nothing has | ||
133 | happened and will continue to use the partition tables, inodes, and | ||
134 | memory mappings for the old card. | ||
135 | |||
136 | If the kernel gets fooled in this way, it's almost certain to cause | ||
137 | data corruption and to crash your system. You'll have no one to blame | ||
138 | but yourself. | ||
139 | |||
140 | YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK! | ||
141 | |||
142 | That having been said, most of the time there shouldn't be any trouble | ||
143 | at all. The "persist" feature can be extremely useful. Make the most | ||
144 | of it. | ||
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 3afa4a5035b7..e221b0d1f667 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -1015,7 +1015,7 @@ static void hid_pre_reset(struct usb_interface *intf) | |||
1015 | hid_suspend(intf, PMSG_ON); | 1015 | hid_suspend(intf, PMSG_ON); |
1016 | } | 1016 | } |
1017 | 1017 | ||
1018 | static void hid_post_reset(struct usb_interface *intf) | 1018 | static void hid_post_reset(struct usb_interface *intf, int reset_resume) |
1019 | { | 1019 | { |
1020 | struct usb_device *dev = interface_to_usbdev (intf); | 1020 | struct usb_device *dev = interface_to_usbdev (intf); |
1021 | 1021 | ||
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 346fc030c929..5113ef4cb7f6 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig | |||
@@ -86,6 +86,28 @@ config USB_SUSPEND | |||
86 | 86 | ||
87 | If you are unsure about this, say N here. | 87 | If you are unsure about this, say N here. |
88 | 88 | ||
89 | config USB_PERSIST | ||
90 | bool "USB device persistence during system suspend (DANGEROUS)" | ||
91 | depends on USB && PM && EXPERIMENTAL | ||
92 | default n | ||
93 | help | ||
94 | If you say Y here, USB device data structures will remain | ||
95 | persistent across system suspend, even if the USB bus loses | ||
96 | power. (This includes software-suspend, also known as swsusp, | ||
97 | or suspend-to-disk.) The devices will reappear as if by magic | ||
98 | when the system wakes up, with no need to unmount USB filesystems, | ||
99 | rmmod host-controller drivers, or do anything else. | ||
100 | |||
101 | WARNING: This option can be dangerous! | ||
102 | |||
103 | If a USB device is replaced by another of the same type while | ||
104 | the system is asleep, there's a good chance the kernel won't | ||
105 | detect the change. Likewise if the media in a USB storage | ||
106 | device is replaced. When this happens it's almost certain to | ||
107 | cause data corruption and maybe even crash your system. | ||
108 | |||
109 | If you are unsure, say N here. | ||
110 | |||
89 | config USB_OTG | 111 | config USB_OTG |
90 | bool | 112 | bool |
91 | depends on USB && EXPERIMENTAL | 113 | depends on USB && EXPERIMENTAL |
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index e8b447e06c54..12dd986bdffd 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -824,8 +824,9 @@ static int usb_resume_device(struct usb_device *udev) | |||
824 | struct usb_device_driver *udriver; | 824 | struct usb_device_driver *udriver; |
825 | int status = 0; | 825 | int status = 0; |
826 | 826 | ||
827 | if (udev->state == USB_STATE_NOTATTACHED || | 827 | if (udev->state == USB_STATE_NOTATTACHED) |
828 | udev->state != USB_STATE_SUSPENDED) | 828 | goto done; |
829 | if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume) | ||
829 | goto done; | 830 | goto done; |
830 | 831 | ||
831 | /* Can't resume it if it doesn't have a driver. */ | 832 | /* Can't resume it if it doesn't have a driver. */ |
@@ -882,7 +883,7 @@ done: | |||
882 | } | 883 | } |
883 | 884 | ||
884 | /* Caller has locked intf's usb_device's pm_mutex */ | 885 | /* Caller has locked intf's usb_device's pm_mutex */ |
885 | static int usb_resume_interface(struct usb_interface *intf) | 886 | static int usb_resume_interface(struct usb_interface *intf, int reset_resume) |
886 | { | 887 | { |
887 | struct usb_driver *driver; | 888 | struct usb_driver *driver; |
888 | int status = 0; | 889 | int status = 0; |
@@ -902,21 +903,21 @@ static int usb_resume_interface(struct usb_interface *intf) | |||
902 | } | 903 | } |
903 | driver = to_usb_driver(intf->dev.driver); | 904 | driver = to_usb_driver(intf->dev.driver); |
904 | 905 | ||
905 | if (driver->resume) { | 906 | if (reset_resume && driver->post_reset) |
907 | driver->post_reset(intf, reset_resume); | ||
908 | else if (driver->resume) { | ||
906 | status = driver->resume(intf); | 909 | status = driver->resume(intf); |
907 | if (status) | 910 | if (status) |
908 | dev_err(&intf->dev, "%s error %d\n", | 911 | dev_err(&intf->dev, "%s error %d\n", |
909 | "resume", status); | 912 | "resume", status); |
910 | else | 913 | } else |
911 | mark_active(intf); | ||
912 | } else { | ||
913 | dev_warn(&intf->dev, "no resume for driver %s?\n", | 914 | dev_warn(&intf->dev, "no resume for driver %s?\n", |
914 | driver->name); | 915 | driver->name); |
915 | mark_active(intf); | ||
916 | } | ||
917 | 916 | ||
918 | done: | 917 | done: |
919 | // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); | 918 | // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); |
919 | if (status == 0) | ||
920 | mark_active(intf); | ||
920 | return status; | 921 | return status; |
921 | } | 922 | } |
922 | 923 | ||
@@ -1063,7 +1064,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1063 | if (status != 0) { | 1064 | if (status != 0) { |
1064 | while (--i >= 0) { | 1065 | while (--i >= 0) { |
1065 | intf = udev->actconfig->interface[i]; | 1066 | intf = udev->actconfig->interface[i]; |
1066 | usb_resume_interface(intf); | 1067 | usb_resume_interface(intf, 0); |
1067 | } | 1068 | } |
1068 | 1069 | ||
1069 | /* Try another autosuspend when the interfaces aren't busy */ | 1070 | /* Try another autosuspend when the interfaces aren't busy */ |
@@ -1162,20 +1163,21 @@ static int usb_resume_both(struct usb_device *udev) | |||
1162 | } | 1163 | } |
1163 | } else { | 1164 | } else { |
1164 | 1165 | ||
1165 | /* Needed only for setting udev->dev.power.power_state.event | 1166 | /* Needed for setting udev->dev.power.power_state.event, |
1166 | * and for possible debugging message. */ | 1167 | * for possible debugging message, and for reset_resume. */ |
1167 | status = usb_resume_device(udev); | 1168 | status = usb_resume_device(udev); |
1168 | } | 1169 | } |
1169 | 1170 | ||
1170 | if (status == 0 && udev->actconfig) { | 1171 | if (status == 0 && udev->actconfig) { |
1171 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | 1172 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { |
1172 | intf = udev->actconfig->interface[i]; | 1173 | intf = udev->actconfig->interface[i]; |
1173 | usb_resume_interface(intf); | 1174 | usb_resume_interface(intf, udev->reset_resume); |
1174 | } | 1175 | } |
1175 | } | 1176 | } |
1176 | 1177 | ||
1177 | done: | 1178 | done: |
1178 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); | 1179 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); |
1180 | udev->reset_resume = 0; | ||
1179 | return status; | 1181 | return status; |
1180 | } | 1182 | } |
1181 | 1183 | ||
@@ -1510,8 +1512,15 @@ static int usb_resume(struct device *dev) | |||
1510 | if (!is_usb_device(dev)) /* Ignore PM for interfaces */ | 1512 | if (!is_usb_device(dev)) /* Ignore PM for interfaces */ |
1511 | return 0; | 1513 | return 0; |
1512 | udev = to_usb_device(dev); | 1514 | udev = to_usb_device(dev); |
1513 | if (udev->autoresume_disabled) | 1515 | |
1514 | return -EPERM; | 1516 | /* If autoresume is disabled then we also want to prevent resume |
1517 | * during system wakeup. However, a "persistent-device" reset-resume | ||
1518 | * after power loss counts as a wakeup event. So allow a | ||
1519 | * reset-resume to occur if remote wakeup is enabled. */ | ||
1520 | if (udev->autoresume_disabled) { | ||
1521 | if (!(udev->reset_resume && udev->do_remote_wakeup)) | ||
1522 | return -EPERM; | ||
1523 | } | ||
1515 | return usb_external_resume_device(udev); | 1524 | return usb_external_resume_device(udev); |
1516 | } | 1525 | } |
1517 | 1526 | ||
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7cbf992adccd..d363b0ea7345 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c | |||
@@ -217,7 +217,10 @@ static int generic_resume(struct usb_device *udev) | |||
217 | { | 217 | { |
218 | int rc; | 218 | int rc; |
219 | 219 | ||
220 | rc = usb_port_resume(udev); | 220 | if (udev->reset_resume) |
221 | rc = usb_reset_suspended_device(udev); | ||
222 | else | ||
223 | rc = usb_port_resume(udev); | ||
221 | 224 | ||
222 | /* Root hubs don't have upstream ports to resume or reset, | 225 | /* Root hubs don't have upstream ports to resume or reset, |
223 | * so the line above won't do much for them. We have to | 226 | * so the line above won't do much for them. We have to |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 77a6627b18d2..51d2d304568b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -553,45 +553,121 @@ static int hub_hub_status(struct usb_hub *hub, | |||
553 | static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) | 553 | static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) |
554 | { | 554 | { |
555 | struct usb_device *hdev = hub->hdev; | 555 | struct usb_device *hdev = hub->hdev; |
556 | int ret; | 556 | int ret = 0; |
557 | 557 | ||
558 | if (hdev->children[port1-1] && set_state) { | 558 | if (hdev->children[port1-1] && set_state) |
559 | usb_set_device_state(hdev->children[port1-1], | 559 | usb_set_device_state(hdev->children[port1-1], |
560 | USB_STATE_NOTATTACHED); | 560 | USB_STATE_NOTATTACHED); |
561 | } | 561 | if (!hub->error) |
562 | ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); | 562 | ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); |
563 | if (ret) | 563 | if (ret) |
564 | dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", | 564 | dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", |
565 | port1, ret); | 565 | port1, ret); |
566 | |||
567 | return ret; | 566 | return ret; |
568 | } | 567 | } |
569 | 568 | ||
569 | /* | ||
570 | * Disable a port and mark a logical connnect-change event, so that some | ||
571 | * time later khubd will disconnect() any existing usb_device on the port | ||
572 | * and will re-enumerate if there actually is a device attached. | ||
573 | */ | ||
574 | static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) | ||
575 | { | ||
576 | dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); | ||
577 | hub_port_disable(hub, port1, 1); | ||
570 | 578 | ||
571 | /* caller has locked the hub device */ | 579 | /* FIXME let caller ask to power down the port: |
572 | static void hub_pre_reset(struct usb_interface *intf) | 580 | * - some devices won't enumerate without a VBUS power cycle |
581 | * - SRP saves power that way | ||
582 | * - ... new call, TBD ... | ||
583 | * That's easy if this hub can switch power per-port, and | ||
584 | * khubd reactivates the port later (timer, SRP, etc). | ||
585 | * Powerdown must be optional, because of reset/DFU. | ||
586 | */ | ||
587 | |||
588 | set_bit(port1, hub->change_bits); | ||
589 | kick_khubd(hub); | ||
590 | } | ||
591 | |||
592 | static void disconnect_all_children(struct usb_hub *hub, int logical) | ||
573 | { | 593 | { |
574 | struct usb_hub *hub = usb_get_intfdata(intf); | ||
575 | struct usb_device *hdev = hub->hdev; | 594 | struct usb_device *hdev = hub->hdev; |
576 | int port1; | 595 | int port1; |
577 | 596 | ||
578 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | 597 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { |
579 | if (hdev->children[port1 - 1]) { | 598 | if (hdev->children[port1-1]) { |
580 | usb_disconnect(&hdev->children[port1 - 1]); | 599 | if (logical) |
581 | if (hub->error == 0) | 600 | hub_port_logical_disconnect(hub, port1); |
582 | hub_port_disable(hub, port1, 0); | 601 | else |
602 | usb_disconnect(&hdev->children[port1-1]); | ||
603 | } | ||
604 | } | ||
605 | } | ||
606 | |||
607 | #ifdef CONFIG_USB_PERSIST | ||
608 | |||
609 | #define USB_PERSIST 1 | ||
610 | |||
611 | /* For "persistent-device" resets we must mark the child devices for reset | ||
612 | * and turn off a possible connect-change status (so khubd won't disconnect | ||
613 | * them later). | ||
614 | */ | ||
615 | static void mark_children_for_reset_resume(struct usb_hub *hub) | ||
616 | { | ||
617 | struct usb_device *hdev = hub->hdev; | ||
618 | int port1; | ||
619 | |||
620 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | ||
621 | struct usb_device *child = hdev->children[port1-1]; | ||
622 | |||
623 | if (child) { | ||
624 | child->reset_resume = 1; | ||
625 | clear_port_feature(hdev, port1, | ||
626 | USB_PORT_FEAT_C_CONNECTION); | ||
583 | } | 627 | } |
584 | } | 628 | } |
629 | } | ||
630 | |||
631 | #else | ||
632 | |||
633 | #define USB_PERSIST 0 | ||
634 | |||
635 | static inline void mark_children_for_reset_resume(struct usb_hub *hub) | ||
636 | { } | ||
637 | |||
638 | #endif /* CONFIG_USB_PERSIST */ | ||
639 | |||
640 | /* caller has locked the hub device */ | ||
641 | static void hub_pre_reset(struct usb_interface *intf) | ||
642 | { | ||
643 | struct usb_hub *hub = usb_get_intfdata(intf); | ||
644 | |||
645 | /* This routine doesn't run as part of a reset-resume, so it's safe | ||
646 | * to disconnect all the drivers below the hub. | ||
647 | */ | ||
648 | disconnect_all_children(hub, 0); | ||
585 | hub_quiesce(hub); | 649 | hub_quiesce(hub); |
586 | } | 650 | } |
587 | 651 | ||
588 | /* caller has locked the hub device */ | 652 | /* caller has locked the hub device */ |
589 | static void hub_post_reset(struct usb_interface *intf) | 653 | static void hub_post_reset(struct usb_interface *intf, int reset_resume) |
590 | { | 654 | { |
591 | struct usb_hub *hub = usb_get_intfdata(intf); | 655 | struct usb_hub *hub = usb_get_intfdata(intf); |
592 | 656 | ||
593 | hub_activate(hub); | ||
594 | hub_power_on(hub); | 657 | hub_power_on(hub); |
658 | if (reset_resume) { | ||
659 | if (USB_PERSIST) | ||
660 | mark_children_for_reset_resume(hub); | ||
661 | else { | ||
662 | /* Reset-resume doesn't call pre_reset, so we have to | ||
663 | * disconnect the children here. But we may not lock | ||
664 | * the child devices, so we have to do a "logical" | ||
665 | * disconnect. | ||
666 | */ | ||
667 | disconnect_all_children(hub, 1); | ||
668 | } | ||
669 | } | ||
670 | hub_activate(hub); | ||
595 | } | 671 | } |
596 | 672 | ||
597 | 673 | ||
@@ -1054,32 +1130,63 @@ void usb_set_device_state(struct usb_device *udev, | |||
1054 | #ifdef CONFIG_PM | 1130 | #ifdef CONFIG_PM |
1055 | 1131 | ||
1056 | /** | 1132 | /** |
1133 | * usb_reset_suspended_device - reset a suspended device instead of resuming it | ||
1134 | * @udev: device to be reset instead of resumed | ||
1135 | * | ||
1136 | * If a host controller doesn't maintain VBUS suspend current during a | ||
1137 | * system sleep or is reset when the system wakes up, all the USB | ||
1138 | * power sessions below it will be broken. This is especially troublesome | ||
1139 | * for mass-storage devices containing mounted filesystems, since the | ||
1140 | * device will appear to have disconnected and all the memory mappings | ||
1141 | * to it will be lost. | ||
1142 | * | ||
1143 | * As an alternative, this routine attempts to recover power sessions for | ||
1144 | * devices that are still present by resetting them instead of resuming | ||
1145 | * them. If all goes well, the devices will appear to persist across the | ||
1146 | * the interruption of the power sessions. | ||
1147 | * | ||
1148 | * This facility is inherently dangerous. Although usb_reset_device() | ||
1149 | * makes every effort to insure that the same device is present after the | ||
1150 | * reset as before, it cannot provide a 100% guarantee. Furthermore it's | ||
1151 | * quite possible for a device to remain unaltered but its media to be | ||
1152 | * changed. If the user replaces a flash memory card while the system is | ||
1153 | * asleep, he will have only himself to blame when the filesystem on the | ||
1154 | * new card is corrupted and the system crashes. | ||
1155 | */ | ||
1156 | int usb_reset_suspended_device(struct usb_device *udev) | ||
1157 | { | ||
1158 | int rc = 0; | ||
1159 | |||
1160 | dev_dbg(&udev->dev, "usb %sresume\n", "reset-"); | ||
1161 | |||
1162 | /* After we're done the device won't be suspended any more. | ||
1163 | * In addition, the reset won't work if udev->state is SUSPENDED. | ||
1164 | */ | ||
1165 | usb_set_device_state(udev, udev->actconfig | ||
1166 | ? USB_STATE_CONFIGURED | ||
1167 | : USB_STATE_ADDRESS); | ||
1168 | |||
1169 | /* Root hubs don't need to be (and can't be) reset */ | ||
1170 | if (udev->parent) | ||
1171 | rc = usb_reset_device(udev); | ||
1172 | return rc; | ||
1173 | } | ||
1174 | |||
1175 | /** | ||
1057 | * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power | 1176 | * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power |
1058 | * @rhdev: struct usb_device for the root hub | 1177 | * @rhdev: struct usb_device for the root hub |
1059 | * | 1178 | * |
1060 | * The USB host controller driver calls this function when its root hub | 1179 | * The USB host controller driver calls this function when its root hub |
1061 | * is resumed and Vbus power has been interrupted or the controller | 1180 | * is resumed and Vbus power has been interrupted or the controller |
1062 | * has been reset. The routine marks all the children of the root hub | 1181 | * has been reset. The routine marks @rhdev as having lost power. When |
1063 | * as NOTATTACHED and marks logical connect-change events on their ports. | 1182 | * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST |
1183 | * is enabled then it will carry out power-session recovery, otherwise | ||
1184 | * it will disconnect all the child devices. | ||
1064 | */ | 1185 | */ |
1065 | void usb_root_hub_lost_power(struct usb_device *rhdev) | 1186 | void usb_root_hub_lost_power(struct usb_device *rhdev) |
1066 | { | 1187 | { |
1067 | struct usb_hub *hub; | ||
1068 | int port1; | ||
1069 | unsigned long flags; | ||
1070 | |||
1071 | dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); | 1188 | dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); |
1072 | 1189 | rhdev->reset_resume = 1; | |
1073 | spin_lock_irqsave(&device_state_lock, flags); | ||
1074 | hub = hdev_to_hub(rhdev); | ||
1075 | for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { | ||
1076 | if (rhdev->children[port1 - 1]) { | ||
1077 | recursively_mark_NOTATTACHED( | ||
1078 | rhdev->children[port1 - 1]); | ||
1079 | set_bit(port1, hub->change_bits); | ||
1080 | } | ||
1081 | } | ||
1082 | spin_unlock_irqrestore(&device_state_lock, flags); | ||
1083 | } | 1190 | } |
1084 | EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); | 1191 | EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); |
1085 | 1192 | ||
@@ -1513,29 +1620,6 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
1513 | return status; | 1620 | return status; |
1514 | } | 1621 | } |
1515 | 1622 | ||
1516 | /* | ||
1517 | * Disable a port and mark a logical connnect-change event, so that some | ||
1518 | * time later khubd will disconnect() any existing usb_device on the port | ||
1519 | * and will re-enumerate if there actually is a device attached. | ||
1520 | */ | ||
1521 | static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) | ||
1522 | { | ||
1523 | dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); | ||
1524 | hub_port_disable(hub, port1, 1); | ||
1525 | |||
1526 | /* FIXME let caller ask to power down the port: | ||
1527 | * - some devices won't enumerate without a VBUS power cycle | ||
1528 | * - SRP saves power that way | ||
1529 | * - ... new call, TBD ... | ||
1530 | * That's easy if this hub can switch power per-port, and | ||
1531 | * khubd reactivates the port later (timer, SRP, etc). | ||
1532 | * Powerdown must be optional, because of reset/DFU. | ||
1533 | */ | ||
1534 | |||
1535 | set_bit(port1, hub->change_bits); | ||
1536 | kick_khubd(hub); | ||
1537 | } | ||
1538 | |||
1539 | #ifdef CONFIG_PM | 1623 | #ifdef CONFIG_PM |
1540 | 1624 | ||
1541 | #ifdef CONFIG_USB_SUSPEND | 1625 | #ifdef CONFIG_USB_SUSPEND |
@@ -3018,7 +3102,7 @@ int usb_reset_composite_device(struct usb_device *udev, | |||
3018 | cintf->dev.driver) { | 3102 | cintf->dev.driver) { |
3019 | drv = to_usb_driver(cintf->dev.driver); | 3103 | drv = to_usb_driver(cintf->dev.driver); |
3020 | if (drv->post_reset) | 3104 | if (drv->post_reset) |
3021 | (drv->post_reset)(cintf); | 3105 | (drv->post_reset)(cintf, 0); |
3022 | } | 3106 | } |
3023 | if (cintf != iface) | 3107 | if (cintf != iface) |
3024 | up(&cintf->dev.sem); | 3108 | up(&cintf->dev.sem); |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 6f361df374fc..1a4862886733 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -36,6 +36,7 @@ extern void usb_host_cleanup(void); | |||
36 | extern void usb_autosuspend_work(struct work_struct *work); | 36 | extern void usb_autosuspend_work(struct work_struct *work); |
37 | extern int usb_port_suspend(struct usb_device *dev); | 37 | extern int usb_port_suspend(struct usb_device *dev); |
38 | extern int usb_port_resume(struct usb_device *dev); | 38 | extern int usb_port_resume(struct usb_device *dev); |
39 | extern int usb_reset_suspended_device(struct usb_device *udev); | ||
39 | extern int usb_external_suspend_device(struct usb_device *udev, | 40 | extern int usb_external_suspend_device(struct usb_device *udev, |
40 | pm_message_t msg); | 41 | pm_message_t msg); |
41 | extern int usb_external_resume_device(struct usb_device *udev); | 42 | extern int usb_external_resume_device(struct usb_device *udev); |
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index df5dc186aef5..be4cd8fe4ce6 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c | |||
@@ -236,7 +236,7 @@ static void storage_pre_reset(struct usb_interface *iface) | |||
236 | mutex_lock(&us->dev_mutex); | 236 | mutex_lock(&us->dev_mutex); |
237 | } | 237 | } |
238 | 238 | ||
239 | static void storage_post_reset(struct usb_interface *iface) | 239 | static void storage_post_reset(struct usb_interface *iface, int reset_resume) |
240 | { | 240 | { |
241 | struct us_data *us = usb_get_intfdata(iface); | 241 | struct us_data *us = usb_get_intfdata(iface); |
242 | 242 | ||
@@ -249,7 +249,11 @@ static void storage_post_reset(struct usb_interface *iface) | |||
249 | 249 | ||
250 | /* FIXME: Notify the subdrivers that they need to reinitialize | 250 | /* FIXME: Notify the subdrivers that they need to reinitialize |
251 | * the device */ | 251 | * the device */ |
252 | mutex_unlock(&us->dev_mutex); | 252 | |
253 | /* If this is a reset-resume then the pre_reset routine wasn't | ||
254 | * called, so we don't need to unlock the mutex. */ | ||
255 | if (!reset_resume) | ||
256 | mutex_unlock(&us->dev_mutex); | ||
253 | } | 257 | } |
254 | 258 | ||
255 | /* | 259 | /* |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 56aa2ee21f1b..3d63e0c2dd70 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -403,6 +403,7 @@ struct usb_device { | |||
403 | 403 | ||
404 | unsigned auto_pm:1; /* autosuspend/resume in progress */ | 404 | unsigned auto_pm:1; /* autosuspend/resume in progress */ |
405 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ | 405 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ |
406 | unsigned reset_resume:1; /* needs reset instead of resume */ | ||
406 | unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ | 407 | unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ |
407 | unsigned autoresume_disabled:1; /* disabled by the user */ | 408 | unsigned autoresume_disabled:1; /* disabled by the user */ |
408 | #endif | 409 | #endif |
@@ -819,7 +820,10 @@ struct usbdrv_wrap { | |||
819 | * @pre_reset: Called by usb_reset_composite_device() when the device | 820 | * @pre_reset: Called by usb_reset_composite_device() when the device |
820 | * is about to be reset. | 821 | * is about to be reset. |
821 | * @post_reset: Called by usb_reset_composite_device() after the device | 822 | * @post_reset: Called by usb_reset_composite_device() after the device |
822 | * has been reset. | 823 | * has been reset, or in lieu of @resume following a reset-resume |
824 | * (i.e., the device is reset instead of being resumed, as might | ||
825 | * happen if power was lost). The second argument tells which is | ||
826 | * the reason. | ||
823 | * @id_table: USB drivers use ID table to support hotplugging. | 827 | * @id_table: USB drivers use ID table to support hotplugging. |
824 | * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set | 828 | * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set |
825 | * or your driver's probe function will never get called. | 829 | * or your driver's probe function will never get called. |
@@ -861,7 +865,7 @@ struct usb_driver { | |||
861 | int (*resume) (struct usb_interface *intf); | 865 | int (*resume) (struct usb_interface *intf); |
862 | 866 | ||
863 | void (*pre_reset) (struct usb_interface *intf); | 867 | void (*pre_reset) (struct usb_interface *intf); |
864 | void (*post_reset) (struct usb_interface *intf); | 868 | void (*post_reset) (struct usb_interface *intf, int reset_resume); |
865 | 869 | ||
866 | const struct usb_device_id *id_table; | 870 | const struct usb_device_id *id_table; |
867 | 871 | ||