aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/usb/persist.txt35
-rw-r--r--drivers/usb/core/Kconfig25
-rw-r--r--drivers/usb/core/hub.c27
-rw-r--r--drivers/usb/core/quirks.c12
-rw-r--r--drivers/usb/core/sysfs.c22
-rw-r--r--drivers/usb/host/ehci-hub.c11
-rw-r--r--include/linux/usb.h2
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
68Setting CONFIG_USB_PERSIST will cause the kernel to work around these 68The kernel includes a feature called USB-persist. It tries to work
69issues. It enables a mode in which the core USB device data 69around these issues by allowing the core USB device data structures to
70structures are allowed to persist across a power-session disruption. 70persist across a power-session disruption.
71
71It works like this. If the kernel sees that a USB host controller is 72It works like this. If the kernel sees that a USB host controller is
72not in the expected state during resume (i.e., if the controller was 73not in the expected state during resume (i.e., if the controller was
73reset or otherwise had lost power) then it applies a persistence check 74reset 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
80same descriptors as before, including the Vendor and Product IDs, then 81same descriptors as before, including the Vendor and Product IDs, then
81the kernel continues to use the same device structure. In effect, the 82the kernel continues to use the same device structure. In effect, the
82kernel treats the device as though it had merely been reset instead of 83kernel treats the device as though it had merely been reset instead of
83unplugged. 84unplugged. The same thing happens if the host controller is in the
85expected state but a USB device was unplugged and then replugged.
84 86
85If no device is now attached to the port, or if the descriptors are 87If no device is now attached to the port, or if the descriptors are
86different from what the kernel remembers, then the treatment is what 88different from what the kernel remembers, then the treatment is what
87you would expect. The kernel destroys the old device structure and 89you would expect. The kernel destroys the old device structure and
88behaves as though the old device had been unplugged and a new device 90behaves as though the old device had been unplugged and a new device
89plugged in, just as it would without the CONFIG_USB_PERSIST option. 91plugged in.
90 92
91The end result is that the USB device remains available and usable. 93The end result is that the USB device remains available and usable.
92Filesystem mounts and memory mappings are unaffected, and the world is 94Filesystem mounts and memory mappings are unaffected, and the world is
93now a good and happy place. 95now a good and happy place.
94 96
95Note that even when CONFIG_USB_PERSIST is set, the "persist" feature 97Note that the "USB-persist" feature will be applied only to those
96will be applied only to those devices for which it is enabled. You 98devices for which it is enabled. You can enable the feature by doing
97can 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
101where the "..." should be filled in the with the device's ID. Disable 103where the "..." should be filled in the with the device's ID. Disable
102the feature by writing 0 instead of 1. For hubs the feature is 104the feature by writing 0 instead of 1. For hubs the feature is
103automatically and permanently enabled, so you only have to worry about 105automatically and permanently enabled and the power/persist file
104setting it for devices where it really matters. 106doesn't even exist, so you only have to worry about setting it for
107devices 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
112to plug in a USB flash device, create a persistent volume associated 115to plug in a USB flash device, create a persistent volume associated
113with it, unplug the flash device, plug it back in later, and still 116with it, unplug the flash device, plug it back in later, and still
114have the same persistent volume associated with the device. As such 117have the same persistent volume associated with the device. As such
115it would be more far-reaching than CONFIG_USB_PERSIST. 118it would be more far-reaching than USB-persist.
116 119
117On the other hand, writing a persistent volume manager would be a big 120On the other hand, writing a persistent volume manager would be a big
118job and using it would require significant input from the user. This 121job and using it would require significant input from the user. This
119solution is much quicker and easier -- and it exists now, a giant 122solution is much quicker and easier -- and it exists now, a giant
120point in its favor! 123point in its favor!
121 124
122Furthermore, the USB_PERSIST option applies to _all_ USB devices, not 125Furthermore, the USB-persist feature applies to _all_ USB devices, not
123just mass-storage devices. It might turn out to be equally useful for 126just mass-storage devices. It might turn out to be equally useful for
124other device types, such as network interfaces. 127other 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
129When recovering an interrupted power session the kernel does its best 132When recovering an interrupted power session the kernel does its best
130to make sure the USB device hasn't been changed; that is, the same 133to make sure the USB device hasn't been changed; that is, the same
@@ -152,5 +155,5 @@ but yourself.
152YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK! 155YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
153 156
154That having been said, most of the time there shouldn't be any trouble 157That having been said, most of the time there shouldn't be any trouble
155at all. The "persist" feature can be extremely useful. Make the most 158at all. The USB-persist feature can be extremely useful. Make the
156of it. 159most 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
105config 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
130config USB_OTG 105config 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 */
2121void usb_root_hub_lost_power(struct usb_device *rhdev) 2114void 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)
180static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); 180static 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
184static const char power_group[] = "power";
185#endif
186 184
187#ifdef CONFIG_USB_PERSIST 185static const char power_group[] = "power";
188 186
189static ssize_t 187static ssize_t
190show_persist(struct device *dev, struct device_attribute *attr, char *buf) 188show_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
33static int ehci_hub_control( 33static 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
109static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
110{ }
111
112#endif
113
114#ifdef CONFIG_PM
115
116static int ehci_bus_suspend (struct usb_hcd *hcd) 107static 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 */