From 36e56a34586783c7986ce09d39db80b27c95ce24 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:08:06 -0400 Subject: usbcore: move code among source files This revised patch (as713b) moves a few routines among source files in usbcore. Some driver-related code in usb.c (claiming interfaces and matching IDs) is moved to driver.c, where it belongs. Also the usb_generic stuff in driver.c is moved to a new source file: generic.c. (That's the reason for revising the patch.) Although not very big now, it will get bigger in a later patch. None of the code has been changed; it has only been re-arranged. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 drivers/usb/core/generic.c (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c new file mode 100644 index 0000000..7bab976 --- /dev/null +++ b/drivers/usb/core/generic.c @@ -0,0 +1,53 @@ +/* + * drivers/usb/generic.c - generic driver for USB devices (not interfaces) + * + * (C) Copyright 2005 Greg Kroah-Hartman + * + * based on drivers/usb/usb.c which had the following copyrights: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2004 + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * (C) Copyright Greg Kroah-Hartman 2002-2003 + * + */ + +#include +#include +#include "usb.h" + +static int generic_probe(struct device *dev) +{ + return 0; +} +static int generic_remove(struct device *dev) +{ + struct usb_device *udev = to_usb_device(dev); + + /* if this is only an unbind, not a physical disconnect, then + * unconfigure the device */ + if (udev->state == USB_STATE_CONFIGURED) + usb_set_configuration(udev, 0); + + /* in case the call failed or the device was suspended */ + if (udev->state >= USB_STATE_CONFIGURED) + usb_disable_device(udev, 0); + return 0; +} + +struct device_driver usb_generic_driver = { + .owner = THIS_MODULE, + .name = "usb", + .bus = &usb_bus_type, + .probe = generic_probe, + .remove = generic_remove, +}; + +/* Fun hack to determine if the struct device is a + * usb device or a usb interface. */ +int usb_generic_driver_data; -- cgit v1.1 From 8bb54ab573ecd1b4fe2ed66416a8d99a86e65316 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:08:49 -0400 Subject: usbcore: add usb_device_driver definition This patch (as732) adds a usb_device_driver structure, for representing drivers that manage an entire USB device as opposed to just an interface. Support routines like usb_register_device_driver, usb_deregister_device_driver, usb_probe_device, and usb_unbind_device are also added. Unlike an earlier version of this patch, the new code is type-safe. To accomplish this, the existing struct driver embedded in struct usb_driver had to be wrapped in an intermediate wrapper. This enables the core to tell at runtime whether a particular struct driver belongs to a device driver or to an interface driver. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7bab976..fa6f34a 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -21,14 +21,12 @@ #include #include "usb.h" -static int generic_probe(struct device *dev) +static int generic_probe(struct usb_device *udev) { return 0; } -static int generic_remove(struct device *dev) +static void generic_disconnect(struct usb_device *udev) { - struct usb_device *udev = to_usb_device(dev); - /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ if (udev->state == USB_STATE_CONFIGURED) @@ -37,17 +35,10 @@ static int generic_remove(struct device *dev) /* in case the call failed or the device was suspended */ if (udev->state >= USB_STATE_CONFIGURED) usb_disable_device(udev, 0); - return 0; } -struct device_driver usb_generic_driver = { - .owner = THIS_MODULE, +struct usb_device_driver usb_generic_driver = { .name = "usb", - .bus = &usb_bus_type, .probe = generic_probe, - .remove = generic_remove, + .disconnect = generic_disconnect, }; - -/* Fun hack to determine if the struct device is a - * usb device or a usb interface. */ -int usb_generic_driver_data; -- cgit v1.1 From 782da727b0d59e93c84a627948b1535a3db90392 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:09:35 -0400 Subject: usbcore: make usb_generic a usb_device_driver This patch (as714b) makes usb_generic into a usb_device_driver capable of being probed and unbound, just like other drivers. A fair amount of the work that used to get done during discovery or removal of a USB device have been moved to the probe and disconnect methods of usb_generic: creating the sysfs attributes and selecting an initial configuration. However the normal behavior should continue to be the same as before. We will now have the possibility of creating other USB device drivers, They will assist with exporting devices to remote systems (USB-over-TCPIP) or to paravirtual guest operating systems. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 184 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index fa6f34a..1522195 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -21,24 +21,208 @@ #include #include "usb.h" +static inline const char *plural(int n) +{ + return (n == 1 ? "" : "s"); +} + +static int choose_configuration(struct usb_device *udev) +{ + int i; + int num_configs; + int insufficient_power = 0; + struct usb_host_config *c, *best; + + best = NULL; + c = udev->config; + num_configs = udev->descriptor.bNumConfigurations; + for (i = 0; i < num_configs; (i++, c++)) { + struct usb_interface_descriptor *desc = NULL; + + /* It's possible that a config has no interfaces! */ + if (c->desc.bNumInterfaces > 0) + desc = &c->intf_cache[0]->altsetting->desc; + + /* + * HP's USB bus-powered keyboard has only one configuration + * and it claims to be self-powered; other devices may have + * similar errors in their descriptors. If the next test + * were allowed to execute, such configurations would always + * be rejected and the devices would not work as expected. + * In the meantime, we run the risk of selecting a config + * that requires external power at a time when that power + * isn't available. It seems to be the lesser of two evils. + * + * Bugzilla #6448 reports a device that appears to crash + * when it receives a GET_DEVICE_STATUS request! We don't + * have any other way to tell whether a device is self-powered, + * but since we don't use that information anywhere but here, + * the call has been removed. + * + * Maybe the GET_DEVICE_STATUS call and the test below can + * be reinstated when device firmwares become more reliable. + * Don't hold your breath. + */ +#if 0 + /* Rule out self-powered configs for a bus-powered device */ + if (bus_powered && (c->desc.bmAttributes & + USB_CONFIG_ATT_SELFPOWER)) + continue; +#endif + + /* + * The next test may not be as effective as it should be. + * Some hubs have errors in their descriptor, claiming + * to be self-powered when they are really bus-powered. + * We will overestimate the amount of current such hubs + * make available for each port. + * + * This is a fairly benign sort of failure. It won't + * cause us to reject configurations that we should have + * accepted. + */ + + /* Rule out configs that draw too much bus current */ + if (c->desc.bMaxPower * 2 > udev->bus_mA) { + insufficient_power++; + continue; + } + + /* If the first config's first interface is COMM/2/0xff + * (MSFT RNDIS), rule it out unless Linux has host-side + * RNDIS support. */ + if (i == 0 && desc + && desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff) { +#ifndef CONFIG_USB_NET_RNDIS_HOST + continue; +#else + best = c; +#endif + } + + /* From the remaining configs, choose the first one whose + * first interface is for a non-vendor-specific class. + * Reason: Linux is more likely to have a class driver + * than a vendor-specific driver. */ + else if (udev->descriptor.bDeviceClass != + USB_CLASS_VENDOR_SPEC && + (!desc || desc->bInterfaceClass != + USB_CLASS_VENDOR_SPEC)) { + best = c; + break; + } + + /* If all the remaining configs are vendor-specific, + * choose the first one. */ + else if (!best) + best = c; + } + + if (insufficient_power > 0) + dev_info(&udev->dev, "rejected %d configuration%s " + "due to insufficient available bus power\n", + insufficient_power, plural(insufficient_power)); + + if (best) { + i = best->desc.bConfigurationValue; + dev_info(&udev->dev, + "configuration #%d chosen from %d choice%s\n", + i, num_configs, plural(num_configs)); + } else { + i = -1; + dev_warn(&udev->dev, + "no configuration chosen from %d choice%s\n", + num_configs, plural(num_configs)); + } + return i; +} + static int generic_probe(struct usb_device *udev) { + int err, c; + + /* put device-specific files into sysfs */ + usb_create_sysfs_dev_files(udev); + + /* Choose and set the configuration. This registers the interfaces + * with the driver core and lets interface drivers bind to them. + */ + c = choose_configuration(udev); + if (c >= 0) { + err = usb_set_configuration(udev, c); + if (err) { + dev_err(&udev->dev, "can't set config #%d, error %d\n", + c, err); + /* This need not be fatal. The user can try to + * set other configurations. */ + } + } + + /* USB device state == configured ... usable */ + usb_notify_add_device(udev); + return 0; } + static void generic_disconnect(struct usb_device *udev) { + usb_notify_remove_device(udev); + /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ if (udev->state == USB_STATE_CONFIGURED) usb_set_configuration(udev, 0); + usb_remove_sysfs_dev_files(udev); + /* in case the call failed or the device was suspended */ if (udev->state >= USB_STATE_CONFIGURED) usb_disable_device(udev, 0); } +#ifdef CONFIG_PM + +static int verify_suspended(struct device *dev, void *unused) +{ + if (dev->driver == NULL) + return 0; + return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; +} + +static int generic_suspend(struct usb_device *udev, pm_message_t msg) +{ + int status; + + /* rule out bogus requests through sysfs */ + status = device_for_each_child(&udev->dev, NULL, verify_suspended); + if (status) + return status; + + /* USB devices enter SUSPEND state through their hubs, but can be + * marked for FREEZE as soon as their children are already idled. + * But those semantics are useless, so we equate the two (sigh). + */ + return usb_port_suspend(udev); +} + +static int generic_resume(struct usb_device *udev) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_port_resume(udev); +} + +#endif /* CONFIG_PM */ + struct usb_device_driver usb_generic_driver = { .name = "usb", .probe = generic_probe, .disconnect = generic_disconnect, +#ifdef CONFIG_PM + .suspend = generic_suspend, + .resume = generic_resume, +#endif }; -- cgit v1.1 From a8e7c5653562f88c0f5f53eac0a890c012655789 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:11:02 -0400 Subject: usbcore: resume device resume recursion This patch (as717b) removes the existing recursion in hub resume code: Resuming a hub will no longer automatically resume the devices attached to the hub. At the same time, it adds one level of recursion: Suspending a USB device will automatically suspend all the device's interfaces. Failure at an intermediate stage will cause all the already-suspended interfaces to be resumed. Attempts to suspend or resume an interface by itself will do nothing, although they won't return an error. Thus the regular system-suspend and system-resume procedures should continue to work as before; only runtime PM will be affected. The patch also removes the code that tests state of the interfaces before suspending a device. It's no longer needed, since everything gets suspended together. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 1522195..b6dacd7 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -184,22 +184,8 @@ static void generic_disconnect(struct usb_device *udev) #ifdef CONFIG_PM -static int verify_suspended(struct device *dev, void *unused) -{ - if (dev->driver == NULL) - return 0; - return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; -} - static int generic_suspend(struct usb_device *udev, pm_message_t msg) { - int status; - - /* rule out bogus requests through sysfs */ - status = device_for_each_child(&udev->dev, NULL, verify_suspended); - if (status) - return status; - /* USB devices enter SUSPEND state through their hubs, but can be * marked for FREEZE as soon as their children are already idled. * But those semantics are useless, so we equate the two (sigh). -- cgit v1.1 From 114b368c07964caa3f4e1fa575b16e87fa11936c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:13:04 -0400 Subject: usbcore: fix up device and power state tests This patch (as734) rationalizes the various tests of device state and power states. There are duplications and mistaken tests in several places. Perhaps the most interesting challenge is where the hub driver tests to see that all the child devices are suspended before allowing itself to be suspended. When CONFIG_USB_SUSPEND is set the test is straightforward, since we expect that the children _will_ be suspended. But when CONFIG_USB_SUSPEND isn't set, it's not so clear what should be done. The code compromises by checking the child's power.power_state.event field. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index b6dacd7..5358e65 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,9 +195,6 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) static int generic_resume(struct usb_device *udev) { - if (udev->state == USB_STATE_NOTATTACHED) - return 0; - return usb_port_resume(udev); } -- cgit v1.1 From 01d883d44a1ca8dc77486635d428cba63e7fdadf Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 Aug 2006 15:47:18 -0400 Subject: usbcore: non-hub-specific uses of autosuspend This patch (as741) makes the non-hub parts of usbcore actually use the autosuspend facilities added by an earlier patch. Devices opened through usbfs are autoresumed and then autosuspended upon close. Likewise for usb-skeleton. Devices are autoresumed for usb_set_configuration. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb/core/generic.c') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 5358e65..16332cc 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -172,14 +172,10 @@ static void generic_disconnect(struct usb_device *udev) /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ - if (udev->state == USB_STATE_CONFIGURED) + if (udev->actconfig) usb_set_configuration(udev, 0); usb_remove_sysfs_dev_files(udev); - - /* in case the call failed or the device was suspended */ - if (udev->state >= USB_STATE_CONFIGURED) - usb_disable_device(udev, 0); } #ifdef CONFIG_PM @@ -208,4 +204,5 @@ struct usb_device_driver usb_generic_driver = { .suspend = generic_suspend, .resume = generic_resume, #endif + .supports_autosuspend = 1, }; -- cgit v1.1