diff options
-rw-r--r-- | Documentation/usb/persist.txt | 35 | ||||
-rw-r--r-- | drivers/usb/core/Kconfig | 25 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 22 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 11 | ||||
-rw-r--r-- | include/linux/usb.h | 2 |
7 files changed, 53 insertions, 81 deletions
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt index df54d645cbb5..bea58dbd30fe 100644 --- a/Documentation/usb/persist.txt +++ b/Documentation/usb/persist.txt | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | Alan Stern <stern@rowland.harvard.edu> | 3 | Alan Stern <stern@rowland.harvard.edu> |
4 | 4 | ||
5 | September 2, 2006 (Updated May 29, 2007) | 5 | September 2, 2006 (Updated February 25, 2008) |
6 | 6 | ||
7 | 7 | ||
8 | What is the problem? | 8 | What is the problem? |
@@ -65,9 +65,10 @@ much better.) | |||
65 | 65 | ||
66 | What is the solution? | 66 | What is the solution? |
67 | 67 | ||
68 | Setting CONFIG_USB_PERSIST will cause the kernel to work around these | 68 | The kernel includes a feature called USB-persist. It tries to work |
69 | issues. It enables a mode in which the core USB device data | 69 | around these issues by allowing the core USB device data structures to |
70 | structures are allowed to persist across a power-session disruption. | 70 | persist across a power-session disruption. |
71 | |||
71 | It works like this. If the kernel sees that a USB host controller is | 72 | 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 | 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 | reset or otherwise had lost power) then it applies a persistence check |
@@ -80,28 +81,30 @@ re-enumeration shows that the device now attached to that port has the | |||
80 | same descriptors as before, including the Vendor and Product IDs, then | 81 | same descriptors as before, including the Vendor and Product IDs, then |
81 | the kernel continues to use the same device structure. In effect, the | 82 | the kernel continues to use the same device structure. In effect, the |
82 | kernel treats the device as though it had merely been reset instead of | 83 | kernel treats the device as though it had merely been reset instead of |
83 | unplugged. | 84 | unplugged. The same thing happens if the host controller is in the |
85 | expected state but a USB device was unplugged and then replugged. | ||
84 | 86 | ||
85 | If no device is now attached to the port, or if the descriptors are | 87 | If no device is now attached to the port, or if the descriptors are |
86 | different from what the kernel remembers, then the treatment is what | 88 | different from what the kernel remembers, then the treatment is what |
87 | you would expect. The kernel destroys the old device structure and | 89 | you would expect. The kernel destroys the old device structure and |
88 | behaves as though the old device had been unplugged and a new device | 90 | behaves as though the old device had been unplugged and a new device |
89 | plugged in, just as it would without the CONFIG_USB_PERSIST option. | 91 | plugged in. |
90 | 92 | ||
91 | The end result is that the USB device remains available and usable. | 93 | The end result is that the USB device remains available and usable. |
92 | Filesystem mounts and memory mappings are unaffected, and the world is | 94 | Filesystem mounts and memory mappings are unaffected, and the world is |
93 | now a good and happy place. | 95 | now a good and happy place. |
94 | 96 | ||
95 | Note that even when CONFIG_USB_PERSIST is set, the "persist" feature | 97 | Note that the "USB-persist" feature will be applied only to those |
96 | will be applied only to those devices for which it is enabled. You | 98 | devices for which it is enabled. You can enable the feature by doing |
97 | can enable the feature by doing (as root): | 99 | (as root): |
98 | 100 | ||
99 | echo 1 >/sys/bus/usb/devices/.../power/persist | 101 | echo 1 >/sys/bus/usb/devices/.../power/persist |
100 | 102 | ||
101 | where the "..." should be filled in the with the device's ID. Disable | 103 | where the "..." should be filled in the with the device's ID. Disable |
102 | the feature by writing 0 instead of 1. For hubs the feature is | 104 | the feature by writing 0 instead of 1. For hubs the feature is |
103 | automatically and permanently enabled, so you only have to worry about | 105 | automatically and permanently enabled and the power/persist file |
104 | setting it for devices where it really matters. | 106 | doesn't even exist, so you only have to worry about setting it for |
107 | devices where it really matters. | ||
105 | 108 | ||
106 | 109 | ||
107 | Is this the best solution? | 110 | Is this the best solution? |
@@ -112,19 +115,19 @@ centralized Logical Volume Manager. Such a solution would allow you | |||
112 | to plug in a USB flash device, create a persistent volume associated | 115 | to plug in a USB flash device, create a persistent volume associated |
113 | with it, unplug the flash device, plug it back in later, and still | 116 | with it, unplug the flash device, plug it back in later, and still |
114 | have the same persistent volume associated with the device. As such | 117 | have the same persistent volume associated with the device. As such |
115 | it would be more far-reaching than CONFIG_USB_PERSIST. | 118 | it would be more far-reaching than USB-persist. |
116 | 119 | ||
117 | On the other hand, writing a persistent volume manager would be a big | 120 | On the other hand, writing a persistent volume manager would be a big |
118 | job and using it would require significant input from the user. This | 121 | job and using it would require significant input from the user. This |
119 | solution is much quicker and easier -- and it exists now, a giant | 122 | solution is much quicker and easier -- and it exists now, a giant |
120 | point in its favor! | 123 | point in its favor! |
121 | 124 | ||
122 | Furthermore, the USB_PERSIST option applies to _all_ USB devices, not | 125 | Furthermore, the USB-persist feature applies to _all_ USB devices, not |
123 | just mass-storage devices. It might turn out to be equally useful for | 126 | just mass-storage devices. It might turn out to be equally useful for |
124 | other device types, such as network interfaces. | 127 | other device types, such as network interfaces. |
125 | 128 | ||
126 | 129 | ||
127 | WARNING: Using CONFIG_USB_PERSIST can be dangerous!! | 130 | WARNING: USB-persist can be dangerous!! |
128 | 131 | ||
129 | When recovering an interrupted power session the kernel does its best | 132 | When recovering an interrupted power session the kernel does its best |
130 | to make sure the USB device hasn't been changed; that is, the same | 133 | to make sure the USB device hasn't been changed; that is, the same |
@@ -152,5 +155,5 @@ but yourself. | |||
152 | YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK! | 155 | YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK! |
153 | 156 | ||
154 | That having been said, most of the time there shouldn't be any trouble | 157 | That having been said, most of the time there shouldn't be any trouble |
155 | at all. The "persist" feature can be extremely useful. Make the most | 158 | at all. The USB-persist feature can be extremely useful. Make the |
156 | of it. | 159 | most of it. |
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index a2b0aa48b8ea..c15621d64579 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig | |||
@@ -102,31 +102,6 @@ config USB_SUSPEND | |||
102 | 102 | ||
103 | If you are unsure about this, say N here. | 103 | If you are unsure about this, say N here. |
104 | 104 | ||
105 | config USB_PERSIST | ||
106 | bool "USB device persistence during system suspend (DANGEROUS)" | ||
107 | depends on USB && PM && EXPERIMENTAL | ||
108 | default n | ||
109 | help | ||
110 | |||
111 | If you say Y here and enable the "power/persist" attribute | ||
112 | for a USB device, the device's data structures will remain | ||
113 | persistent across system suspend, even if the USB bus loses | ||
114 | power. (This includes hibernation, also known as swsusp or | ||
115 | suspend-to-disk.) The devices will reappear as if by magic | ||
116 | when the system wakes up, with no need to unmount USB | ||
117 | filesystems, rmmod host-controller drivers, or do anything | ||
118 | else. | ||
119 | |||
120 | WARNING: This option can be dangerous! | ||
121 | |||
122 | If a USB device is replaced by another of the same type while | ||
123 | the system is asleep, there's a good chance the kernel won't | ||
124 | detect the change. Likewise if the media in a USB storage | ||
125 | device is replaced. When this happens it's almost certain to | ||
126 | cause data corruption and maybe even crash your system. | ||
127 | |||
128 | If you are unsure, say N here. | ||
129 | |||
130 | config USB_OTG | 105 | config USB_OTG |
131 | bool | 106 | bool |
132 | depends on USB && EXPERIMENTAL | 107 | depends on USB && EXPERIMENTAL |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index df68e2562582..6dc589955d75 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -30,12 +30,6 @@ | |||
30 | #include "hcd.h" | 30 | #include "hcd.h" |
31 | #include "hub.h" | 31 | #include "hub.h" |
32 | 32 | ||
33 | #ifdef CONFIG_USB_PERSIST | ||
34 | #define USB_PERSIST 1 | ||
35 | #else | ||
36 | #define USB_PERSIST 0 | ||
37 | #endif | ||
38 | |||
39 | /* if we are in debug mode, always announce new devices */ | 33 | /* if we are in debug mode, always announce new devices */ |
40 | #ifdef DEBUG | 34 | #ifdef DEBUG |
41 | #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES | 35 | #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES |
@@ -695,7 +689,7 @@ static void hub_restart(struct usb_hub *hub, int type) | |||
695 | * turn off the various status changes to prevent | 689 | * turn off the various status changes to prevent |
696 | * khubd from disconnecting it later. | 690 | * khubd from disconnecting it later. |
697 | */ | 691 | */ |
698 | if (USB_PERSIST && udev->persist_enabled && status == 0 && | 692 | if (udev->persist_enabled && status == 0 && |
699 | !(portstatus & USB_PORT_STAT_ENABLE)) { | 693 | !(portstatus & USB_PORT_STAT_ENABLE)) { |
700 | if (portchange & USB_PORT_STAT_C_ENABLE) | 694 | if (portchange & USB_PORT_STAT_C_ENABLE) |
701 | clear_port_feature(hub->hdev, port1, | 695 | clear_port_feature(hub->hdev, port1, |
@@ -1923,9 +1917,8 @@ static int finish_port_resume(struct usb_device *udev) | |||
1923 | * the host and the device is the same as it was when the device | 1917 | * the host and the device is the same as it was when the device |
1924 | * suspended. | 1918 | * suspended. |
1925 | * | 1919 | * |
1926 | * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this | 1920 | * If @udev->reset_resume is set then this routine won't check that the |
1927 | * routine won't check that the port is still enabled. Furthermore, | 1921 | * port is still enabled. Furthermore, finish_port_resume() above will |
1928 | * if @udev->reset_resume is set then finish_port_resume() above will | ||
1929 | * reset @udev. The end result is that a broken power session can be | 1922 | * reset @udev. The end result is that a broken power session can be |
1930 | * recovered and @udev will appear to persist across a loss of VBUS power. | 1923 | * recovered and @udev will appear to persist across a loss of VBUS power. |
1931 | * | 1924 | * |
@@ -1937,8 +1930,8 @@ static int finish_port_resume(struct usb_device *udev) | |||
1937 | * to it will be lost. Using the USB_PERSIST facility, the device can be | 1930 | * to it will be lost. Using the USB_PERSIST facility, the device can be |
1938 | * made to appear as if it had not disconnected. | 1931 | * made to appear as if it had not disconnected. |
1939 | * | 1932 | * |
1940 | * This facility is inherently dangerous. Although usb_reset_device() | 1933 | * This facility can be dangerous. Although usb_reset_device() makes |
1941 | * makes every effort to insure that the same device is present after the | 1934 | * every effort to insure that the same device is present after the |
1942 | * reset as before, it cannot provide a 100% guarantee. Furthermore it's | 1935 | * reset as before, it cannot provide a 100% guarantee. Furthermore it's |
1943 | * quite possible for a device to remain unaltered but its media to be | 1936 | * quite possible for a device to remain unaltered but its media to be |
1944 | * changed. If the user replaces a flash memory card while the system is | 1937 | * changed. If the user replaces a flash memory card while the system is |
@@ -1983,7 +1976,7 @@ int usb_port_resume(struct usb_device *udev) | |||
1983 | status = hub_port_status(hub, port1, &portstatus, &portchange); | 1976 | status = hub_port_status(hub, port1, &portstatus, &portchange); |
1984 | 1977 | ||
1985 | SuspendCleared: | 1978 | SuspendCleared: |
1986 | if (USB_PERSIST && udev->reset_resume) | 1979 | if (udev->reset_resume) |
1987 | want_flags = USB_PORT_STAT_POWER | 1980 | want_flags = USB_PORT_STAT_POWER |
1988 | | USB_PORT_STAT_CONNECTION; | 1981 | | USB_PORT_STAT_CONNECTION; |
1989 | else | 1982 | else |
@@ -2113,10 +2106,10 @@ static int hub_reset_resume(struct usb_interface *intf) | |||
2113 | * | 2106 | * |
2114 | * The USB host controller driver calls this function when its root hub | 2107 | * The USB host controller driver calls this function when its root hub |
2115 | * is resumed and Vbus power has been interrupted or the controller | 2108 | * is resumed and Vbus power has been interrupted or the controller |
2116 | * has been reset. The routine marks @rhdev as having lost power. When | 2109 | * has been reset. The routine marks @rhdev as having lost power. |
2117 | * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST | 2110 | * When the hub driver is resumed it will take notice and carry out |
2118 | * is enabled then it will carry out power-session recovery, otherwise | 2111 | * power-session recovery for all the "USB-PERSIST"-enabled child devices; |
2119 | * it will disconnect all the child devices. | 2112 | * the others will be disconnected. |
2120 | */ | 2113 | */ |
2121 | void usb_root_hub_lost_power(struct usb_device *rhdev) | 2114 | void usb_root_hub_lost_power(struct usb_device *rhdev) |
2122 | { | 2115 | { |
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index dfc5418ea10c..f384edf35b44 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c | |||
@@ -97,4 +97,16 @@ void usb_detect_quirks(struct usb_device *udev) | |||
97 | if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) | 97 | if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) |
98 | udev->autosuspend_disabled = 1; | 98 | udev->autosuspend_disabled = 1; |
99 | #endif | 99 | #endif |
100 | |||
101 | #ifdef CONFIG_PM | ||
102 | /* Hubs are automatically enabled for USB-PERSIST */ | ||
103 | if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) | ||
104 | udev->persist_enabled = 1; | ||
105 | #else | ||
106 | /* In the absense of PM, we can safely enable USB-PERSIST | ||
107 | * for all devices. It will affect things like hub resets | ||
108 | * and EMF-related port disables. | ||
109 | */ | ||
110 | udev->persist_enabled = 1; | ||
111 | #endif /* CONFIG_PM */ | ||
100 | } | 112 | } |
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index a37ccbd1e007..5b20a60de8ba 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -180,11 +180,9 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) | |||
180 | static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); | 180 | static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); |
181 | 181 | ||
182 | 182 | ||
183 | #if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND) | 183 | #ifdef CONFIG_PM |
184 | static const char power_group[] = "power"; | ||
185 | #endif | ||
186 | 184 | ||
187 | #ifdef CONFIG_USB_PERSIST | 185 | static const char power_group[] = "power"; |
188 | 186 | ||
189 | static ssize_t | 187 | static ssize_t |
190 | show_persist(struct device *dev, struct device_attribute *attr, char *buf) | 188 | show_persist(struct device *dev, struct device_attribute *attr, char *buf) |
@@ -222,12 +220,13 @@ static int add_persist_attributes(struct device *dev) | |||
222 | if (is_usb_device(dev)) { | 220 | if (is_usb_device(dev)) { |
223 | struct usb_device *udev = to_usb_device(dev); | 221 | struct usb_device *udev = to_usb_device(dev); |
224 | 222 | ||
225 | /* Hubs are automatically enabled for USB_PERSIST */ | 223 | /* Hubs are automatically enabled for USB_PERSIST, |
226 | if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) | 224 | * no point in creating the attribute file. |
227 | udev->persist_enabled = 1; | 225 | */ |
228 | rc = sysfs_add_file_to_group(&dev->kobj, | 226 | if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) |
229 | &dev_attr_persist.attr, | 227 | rc = sysfs_add_file_to_group(&dev->kobj, |
230 | power_group); | 228 | &dev_attr_persist.attr, |
229 | power_group); | ||
231 | } | 230 | } |
232 | return rc; | 231 | return rc; |
233 | } | 232 | } |
@@ -238,13 +237,12 @@ static void remove_persist_attributes(struct device *dev) | |||
238 | &dev_attr_persist.attr, | 237 | &dev_attr_persist.attr, |
239 | power_group); | 238 | power_group); |
240 | } | 239 | } |
241 | |||
242 | #else | 240 | #else |
243 | 241 | ||
244 | #define add_persist_attributes(dev) 0 | 242 | #define add_persist_attributes(dev) 0 |
245 | #define remove_persist_attributes(dev) do {} while (0) | 243 | #define remove_persist_attributes(dev) do {} while (0) |
246 | 244 | ||
247 | #endif /* CONFIG_USB_PERSIST */ | 245 | #endif /* CONFIG_PM */ |
248 | 246 | ||
249 | #ifdef CONFIG_USB_SUSPEND | 247 | #ifdef CONFIG_USB_SUSPEND |
250 | 248 | ||
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8d513a15d0cd..fea9e47192db 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -28,7 +28,7 @@ | |||
28 | 28 | ||
29 | /*-------------------------------------------------------------------------*/ | 29 | /*-------------------------------------------------------------------------*/ |
30 | 30 | ||
31 | #ifdef CONFIG_USB_PERSIST | 31 | #ifdef CONFIG_PM |
32 | 32 | ||
33 | static int ehci_hub_control( | 33 | static int ehci_hub_control( |
34 | struct usb_hcd *hcd, | 34 | struct usb_hcd *hcd, |
@@ -104,15 +104,6 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) | |||
104 | ehci->owned_ports = 0; | 104 | ehci->owned_ports = 0; |
105 | } | 105 | } |
106 | 106 | ||
107 | #else /* CONFIG_USB_PERSIST */ | ||
108 | |||
109 | static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci) | ||
110 | { } | ||
111 | |||
112 | #endif | ||
113 | |||
114 | #ifdef CONFIG_PM | ||
115 | |||
116 | static int ehci_bus_suspend (struct usb_hcd *hcd) | 107 | static int ehci_bus_suspend (struct usb_hcd *hcd) |
117 | { | 108 | { |
118 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 109 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 583e0481dfa0..7e31cacfe69c 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -387,6 +387,7 @@ struct usb_device { | |||
387 | 387 | ||
388 | unsigned can_submit:1; /* URBs may be submitted */ | 388 | unsigned can_submit:1; /* URBs may be submitted */ |
389 | unsigned discon_suspended:1; /* Disconnected while suspended */ | 389 | unsigned discon_suspended:1; /* Disconnected while suspended */ |
390 | unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ | ||
390 | unsigned have_langid:1; /* whether string_langid is valid */ | 391 | unsigned have_langid:1; /* whether string_langid is valid */ |
391 | unsigned authorized:1; /* Policy has said we can use it */ | 392 | unsigned authorized:1; /* Policy has said we can use it */ |
392 | unsigned wusb:1; /* Device is Wireless USB */ | 393 | unsigned wusb:1; /* Device is Wireless USB */ |
@@ -433,7 +434,6 @@ struct usb_device { | |||
433 | unsigned auto_pm:1; /* autosuspend/resume in progress */ | 434 | unsigned auto_pm:1; /* autosuspend/resume in progress */ |
434 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ | 435 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ |
435 | unsigned reset_resume:1; /* needs reset instead of resume */ | 436 | unsigned reset_resume:1; /* needs reset instead of resume */ |
436 | unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ | ||
437 | unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ | 437 | unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ |
438 | unsigned autoresume_disabled:1; /* disabled by the user */ | 438 | unsigned autoresume_disabled:1; /* disabled by the user */ |
439 | unsigned skip_sys_resume:1; /* skip the next system resume */ | 439 | unsigned skip_sys_resume:1; /* skip the next system resume */ |