aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-05-30 15:39:33 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:34:30 -0400
commitb41a60eca833d76593d4dac8a59f5c38714194ee (patch)
treea7c5cf721d9978503c3c8c88183747cf954b8733 /drivers/usb/core
parent54515fe528d8c6f9bfaf7d0b9fffb908deecad78 (diff)
USB: add power/persist device attribute
This patch (as920) adds an extra level of protection to the USB-Persist facility. Now it will apply by default only to hubs; for all other devices the user must enable it explicitly by setting the power/persist device attribute. The disconnect_all_children() routine in hub.c has been removed and its code placed inline. This is the way it was originally as part of hub_pre_reset(); the revised usage in hub_reset_resume() is sufficiently different that the code can no longer be shared. Likewise, mark_children_for_reset() is now inline as part of hub_reset_resume(). The end result looks much cleaner than before. The sysfs interface is updated to add the new attribute file, and there are corresponding documentation updates. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Kconfig13
-rw-r--r--drivers/usb/core/hub.c78
-rw-r--r--drivers/usb/core/sysfs.c75
3 files changed, 110 insertions, 56 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 5113ef4cb7f6..97b09f282705 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -91,12 +91,15 @@ config USB_PERSIST
91 depends on USB && PM && EXPERIMENTAL 91 depends on USB && PM && EXPERIMENTAL
92 default n 92 default n
93 help 93 help
94 If you say Y here, USB device data structures will remain 94
95 If you say Y here and enable the "power/persist" attribute
96 for a USB device, the device's data structures will remain
95 persistent across system suspend, even if the USB bus loses 97 persistent across system suspend, even if the USB bus loses
96 power. (This includes software-suspend, also known as swsusp, 98 power. (This includes hibernation, also known as swsusp or
97 or suspend-to-disk.) The devices will reappear as if by magic 99 suspend-to-disk.) The devices will reappear as if by magic
98 when the system wakes up, with no need to unmount USB filesystems, 100 when the system wakes up, with no need to unmount USB
99 rmmod host-controller drivers, or do anything else. 101 filesystems, rmmod host-controller drivers, or do anything
102 else.
100 103
101 WARNING: This option can be dangerous! 104 WARNING: This option can be dangerous!
102 105
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index c4cdb69a6e9e..50e79010401c 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -596,27 +596,18 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
596 kick_khubd(hub); 596 kick_khubd(hub);
597} 597}
598 598
599static void disconnect_all_children(struct usb_hub *hub, int logical)
600{
601 struct usb_device *hdev = hub->hdev;
602 int port1;
603
604 for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
605 if (hdev->children[port1-1]) {
606 if (logical)
607 hub_port_logical_disconnect(hub, port1);
608 else
609 usb_disconnect(&hdev->children[port1-1]);
610 }
611 }
612}
613
614/* caller has locked the hub device */ 599/* caller has locked the hub device */
615static int hub_pre_reset(struct usb_interface *intf) 600static int hub_pre_reset(struct usb_interface *intf)
616{ 601{
617 struct usb_hub *hub = usb_get_intfdata(intf); 602 struct usb_hub *hub = usb_get_intfdata(intf);
603 struct usb_device *hdev = hub->hdev;
604 int i;
618 605
619 disconnect_all_children(hub, 0); 606 /* Disconnect all the children */
607 for (i = 0; i < hdev->maxchild; ++i) {
608 if (hdev->children[i])
609 usb_disconnect(&hdev->children[i]);
610 }
620 hub_quiesce(hub); 611 hub_quiesce(hub);
621 return 0; 612 return 0;
622} 613}
@@ -1872,50 +1863,39 @@ static int hub_resume(struct usb_interface *intf)
1872 return 0; 1863 return 0;
1873} 1864}
1874 1865
1875#ifdef CONFIG_USB_PERSIST 1866static int hub_reset_resume(struct usb_interface *intf)
1876
1877/* For "persistent-device" resets we must mark the child devices for reset
1878 * and turn off a possible connect-change status (so khubd won't disconnect
1879 * them later).
1880 */
1881static void mark_children_for_reset_resume(struct usb_hub *hub)
1882{ 1867{
1868 struct usb_hub *hub = usb_get_intfdata(intf);
1883 struct usb_device *hdev = hub->hdev; 1869 struct usb_device *hdev = hub->hdev;
1884 int port1; 1870 int port1;
1885 1871
1872 hub_power_on(hub);
1873
1886 for (port1 = 1; port1 <= hdev->maxchild; ++port1) { 1874 for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
1887 struct usb_device *child = hdev->children[port1-1]; 1875 struct usb_device *child = hdev->children[port1-1];
1888 1876
1889 if (child) { 1877 if (child) {
1890 child->reset_resume = 1; 1878
1891 clear_port_feature(hdev, port1, 1879 /* For "USB_PERSIST"-enabled children we must
1892 USB_PORT_FEAT_C_CONNECTION); 1880 * mark the child device for reset-resume and
1881 * turn off the connect-change status to prevent
1882 * khubd from disconnecting it later.
1883 */
1884 if (USB_PERSIST && child->persist_enabled) {
1885 child->reset_resume = 1;
1886 clear_port_feature(hdev, port1,
1887 USB_PORT_FEAT_C_CONNECTION);
1888
1889 /* Otherwise we must disconnect the child,
1890 * but as we may not lock the child device here
1891 * we have to do a "logical" disconnect.
1892 */
1893 } else {
1894 hub_port_logical_disconnect(hub, port1);
1895 }
1893 } 1896 }
1894 } 1897 }
1895}
1896
1897#else
1898
1899static inline void mark_children_for_reset_resume(struct usb_hub *hub)
1900{ }
1901
1902#endif /* CONFIG_USB_PERSIST */
1903
1904static int hub_reset_resume(struct usb_interface *intf)
1905{
1906 struct usb_hub *hub = usb_get_intfdata(intf);
1907 1898
1908 hub_power_on(hub);
1909 if (USB_PERSIST)
1910 mark_children_for_reset_resume(hub);
1911 else {
1912 /* Reset-resume doesn't call pre_reset, so we have to
1913 * disconnect the children here. But we may not lock
1914 * the child devices, so we have to do a "logical"
1915 * disconnect.
1916 */
1917 disconnect_all_children(hub, 1);
1918 }
1919 hub_activate(hub); 1899 hub_activate(hub);
1920 return 0; 1900 return 0;
1921} 1901}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index be37c863fdfb..5dfe31bc32ba 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -169,6 +169,73 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
169} 169}
170static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); 170static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
171 171
172
173#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND)
174static const char power_group[] = "power";
175#endif
176
177#ifdef CONFIG_USB_PERSIST
178
179static ssize_t
180show_persist(struct device *dev, struct device_attribute *attr, char *buf)
181{
182 struct usb_device *udev = to_usb_device(dev);
183
184 return sprintf(buf, "%d\n", udev->persist_enabled);
185}
186
187static ssize_t
188set_persist(struct device *dev, struct device_attribute *attr,
189 const char *buf, size_t count)
190{
191 struct usb_device *udev = to_usb_device(dev);
192 int value;
193
194 /* Hubs are always enabled for USB_PERSIST */
195 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
196 return -EPERM;
197
198 if (sscanf(buf, "%d", &value) != 1)
199 return -EINVAL;
200 usb_pm_lock(udev);
201 udev->persist_enabled = !!value;
202 usb_pm_unlock(udev);
203 return count;
204}
205
206static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist);
207
208static int add_persist_attributes(struct device *dev)
209{
210 int rc = 0;
211
212 if (is_usb_device(dev)) {
213 struct usb_device *udev = to_usb_device(dev);
214
215 /* Hubs are automatically enabled for USB_PERSIST */
216 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
217 udev->persist_enabled = 1;
218 rc = sysfs_add_file_to_group(&dev->kobj,
219 &dev_attr_persist.attr,
220 power_group);
221 }
222 return rc;
223}
224
225static void remove_persist_attributes(struct device *dev)
226{
227 sysfs_remove_file_from_group(&dev->kobj,
228 &dev_attr_persist.attr,
229 power_group);
230}
231
232#else
233
234#define add_persist_attributes(dev) 0
235#define remove_persist_attributes(dev) do {} while (0)
236
237#endif /* CONFIG_USB_PERSIST */
238
172#ifdef CONFIG_USB_SUSPEND 239#ifdef CONFIG_USB_SUSPEND
173 240
174static ssize_t 241static ssize_t
@@ -276,8 +343,6 @@ set_level(struct device *dev, struct device_attribute *attr,
276 343
277static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); 344static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
278 345
279static char power_group[] = "power";
280
281static int add_power_attributes(struct device *dev) 346static int add_power_attributes(struct device *dev)
282{ 347{
283 int rc = 0; 348 int rc = 0;
@@ -311,6 +376,7 @@ static void remove_power_attributes(struct device *dev)
311 376
312#endif /* CONFIG_USB_SUSPEND */ 377#endif /* CONFIG_USB_SUSPEND */
313 378
379
314/* Descriptor fields */ 380/* Descriptor fields */
315#define usb_descriptor_attr_le16(field, format_string) \ 381#define usb_descriptor_attr_le16(field, format_string) \
316static ssize_t \ 382static ssize_t \
@@ -384,6 +450,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
384 if (retval) 450 if (retval)
385 return retval; 451 return retval;
386 452
453 retval = add_persist_attributes(dev);
454 if (retval)
455 goto error;
456
387 retval = add_power_attributes(dev); 457 retval = add_power_attributes(dev);
388 if (retval) 458 if (retval)
389 goto error; 459 goto error;
@@ -421,6 +491,7 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
421 device_remove_file(dev, &dev_attr_product); 491 device_remove_file(dev, &dev_attr_product);
422 device_remove_file(dev, &dev_attr_serial); 492 device_remove_file(dev, &dev_attr_serial);
423 remove_power_attributes(dev); 493 remove_power_attributes(dev);
494 remove_persist_attributes(dev);
424 sysfs_remove_group(&dev->kobj, &dev_attr_grp); 495 sysfs_remove_group(&dev->kobj, &dev_attr_grp);
425} 496}
426 497