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/driver.c | 348 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 315 insertions(+), 33 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ec89065..8dcf2cd 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -17,7 +17,8 @@ * * NOTE! This is not actually a driver at all, rather this is * just a collection of helper routines that implement the - * generic USB things that the real drivers can use.. + * matching, probing, releasing, suspending and resuming for + * real drivers. * */ @@ -34,38 +35,6 @@ struct usb_dynid { struct usb_device_id id; }; - -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; - #ifdef CONFIG_HOTPLUG /* @@ -238,6 +207,89 @@ static int usb_unbind_interface(struct device *dev) return 0; } +/** + * usb_driver_claim_interface - bind a driver to an interface + * @driver: the driver to be bound + * @iface: the interface to which it will be bound; must be in the + * usb device's active configuration + * @priv: driver data associated with that interface + * + * This is used by usb device drivers that need to claim more than one + * interface on a device when probing (audio and acm are current examples). + * No device driver should directly modify internal usb_interface or + * usb_device structure members. + * + * Few drivers should need to use this routine, since the most natural + * way to bind to an interface is to return the private data from + * the driver's probe() method. + * + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver probe() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. + */ +int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void* priv) +{ + struct device *dev = &iface->dev; + + if (dev->driver) + return -EBUSY; + + dev->driver = &driver->driver; + usb_set_intfdata(iface, priv); + iface->condition = USB_INTERFACE_BOUND; + mark_active(iface); + + /* if interface was already added, bind now; else let + * the future device_add() bind it, bypassing probe() + */ + if (device_is_registered(dev)) + device_bind_driver(dev); + + return 0; +} +EXPORT_SYMBOL(usb_driver_claim_interface); + +/** + * usb_driver_release_interface - unbind a driver from an interface + * @driver: the driver to be unbound + * @iface: the interface from which it will be unbound + * + * This can be used by drivers to release an interface without waiting + * for their disconnect() methods to be called. In typical cases this + * also causes the driver disconnect() method to be called. + * + * This call is synchronous, and may not be used in an interrupt context. + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver disconnect() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. + */ +void usb_driver_release_interface(struct usb_driver *driver, + struct usb_interface *iface) +{ + struct device *dev = &iface->dev; + + /* this should never happen, don't release something that's not ours */ + if (!dev->driver || dev->driver != &driver->driver) + return; + + /* don't release from within disconnect() */ + if (iface->condition != USB_INTERFACE_BOUND) + return; + + /* don't release if the interface hasn't been added yet */ + if (device_is_registered(dev)) { + iface->condition = USB_INTERFACE_UNBINDING; + device_release_driver(dev); + } + + dev->driver = NULL; + usb_set_intfdata(iface, NULL); + iface->condition = USB_INTERFACE_UNBOUND; + mark_quiesced(iface); +} +EXPORT_SYMBOL(usb_driver_release_interface); + /* returns 0 if no match, 1 if match */ static int usb_match_one_id(struct usb_interface *interface, const struct usb_device_id *id) @@ -402,6 +454,120 @@ int usb_device_match(struct device *dev, struct device_driver *drv) return 0; } +#ifdef CONFIG_HOTPLUG + +/* + * This sends an uevent to userspace, typically helping to load driver + * or other modules, configure the device, and more. Drivers can provide + * a MODULE_DEVICE_TABLE to help with module loading subtasks. + * + * We're called either from khubd (the typical case) or from root hub + * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle + * delays in event delivery. Use sysfs (and DEVPATH) to make sure the + * device (and this configuration!) are still present. + */ +static int usb_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct usb_interface *intf; + struct usb_device *usb_dev; + struct usb_host_interface *alt; + int i = 0; + int length = 0; + + if (!dev) + return -ENODEV; + + /* driver is often null here; dev_dbg() would oops */ + pr_debug ("usb %s: uevent\n", dev->bus_id); + + /* Must check driver_data here, as on remove driver is always NULL */ + if ((dev->driver == &usb_generic_driver) || + (dev->driver_data == &usb_generic_driver_data)) + return 0; + + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev (intf); + alt = intf->cur_altsetting; + + if (usb_dev->devnum < 0) { + pr_debug ("usb %s: already deleted?\n", dev->bus_id); + return -ENODEV; + } + if (!usb_dev->bus) { + pr_debug ("usb %s: bus removed?\n", dev->bus_id); + return -ENODEV; + } + +#ifdef CONFIG_USB_DEVICEFS + /* If this is available, userspace programs can directly read + * all the device descriptors we don't tell them about. Or + * even act as usermode drivers. + * + * FIXME reduce hardwired intelligence here + */ + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVICE=/proc/bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum)) + return -ENOMEM; +#endif + + /* per-device configurations are common */ + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PRODUCT=%x/%x/%x", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice))) + return -ENOMEM; + + /* class-based driver binding models */ + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "TYPE=%d/%d/%d", + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol)) + return -ENOMEM; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "INTERFACE=%d/%d/%d", + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol, + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +#else + +static int usb_uevent(struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + /** * usb_register_driver - register a USB driver * @new_driver: USB operations for the driver @@ -469,3 +635,119 @@ void usb_deregister(struct usb_driver *driver) usbfs_update_special(); } EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); + +#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 usb_generic_suspend(struct device *dev, pm_message_t message) +{ + struct usb_interface *intf; + struct usb_driver *driver; + int 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). + */ + if (dev->driver == &usb_generic_driver) { + if (dev->power.power_state.event == message.event) + return 0; + /* we need to rule out bogus requests through sysfs */ + status = device_for_each_child(dev, NULL, verify_suspended); + if (status) + return status; + return usb_port_suspend(to_usb_device(dev)); + } + + if ((dev->driver == NULL) || + (dev->driver_data == &usb_generic_driver_data)) + return 0; + + intf = to_usb_interface(dev); + driver = to_usb_driver(dev->driver); + + /* with no hardware, USB interfaces only use FREEZE and ON states */ + if (!is_active(intf)) + return 0; + + if (driver->suspend && driver->resume) { + status = driver->suspend(intf, message); + if (status) + dev_err(dev, "%s error %d\n", "suspend", status); + else + mark_quiesced(intf); + } else { + // FIXME else if there's no suspend method, disconnect... + dev_warn(dev, "no suspend for driver %s?\n", driver->name); + mark_quiesced(intf); + status = 0; + } + return status; +} + +static int usb_generic_resume(struct device *dev) +{ + struct usb_interface *intf; + struct usb_driver *driver; + struct usb_device *udev; + int status; + + if (dev->power.power_state.event == PM_EVENT_ON) + return 0; + + /* mark things as "on" immediately, no matter what errors crop up */ + dev->power.power_state.event = PM_EVENT_ON; + + /* devices resume through their hubs */ + if (dev->driver == &usb_generic_driver) { + udev = to_usb_device(dev); + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + return usb_port_resume(udev); + } + + if ((dev->driver == NULL) || + (dev->driver_data == &usb_generic_driver_data)) { + dev->power.power_state.event = PM_EVENT_FREEZE; + return 0; + } + + intf = to_usb_interface(dev); + driver = to_usb_driver(dev->driver); + + udev = interface_to_usbdev(intf); + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + + /* if driver was suspended, it has a resume method; + * however, sysfs can wrongly mark things as suspended + * (on the "no suspend method" FIXME path above) + */ + if (driver->resume) { + status = driver->resume(intf); + if (status) { + dev_err(dev, "%s error %d\n", "resume", status); + mark_quiesced(intf); + } + } else + dev_warn(dev, "no resume for driver %s?\n", driver->name); + return 0; +} + +#endif /* CONFIG_PM */ + +struct bus_type usb_bus_type = { + .name = "usb", + .match = usb_device_match, + .uevent = usb_uevent, +#ifdef CONFIG_PM + .suspend = usb_generic_suspend, + .resume = usb_generic_resume, +#endif +}; -- 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/driver.c | 219 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 163 insertions(+), 56 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8dcf2cd..0d4b5dc 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -84,7 +84,7 @@ static int usb_create_newid_file(struct usb_driver *usb_drv) goto exit; if (usb_drv->probe != NULL) - error = sysfs_create_file(&usb_drv->driver.kobj, + error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj, &driver_attr_new_id.attr); exit: return error; @@ -96,7 +96,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) return; if (usb_drv->probe != NULL) - sysfs_remove_file(&usb_drv->driver.kobj, + sysfs_remove_file(&usb_drv->drvwrap.driver.kobj, &driver_attr_new_id.attr); } @@ -143,18 +143,55 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in } -/* called from driver core with usb_bus_type.subsys writelock */ +/* called from driver core with dev locked */ +static int usb_probe_device(struct device *dev) +{ + struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); + struct usb_device *udev; + int error = -ENODEV; + + dev_dbg(dev, "%s\n", __FUNCTION__); + + if (!is_usb_device(dev)) /* Sanity check */ + return error; + + udev = to_usb_device(dev); + + /* FIXME: resume a suspended device */ + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + /* TODO: Add real matching code */ + + error = udriver->probe(udev); + return error; +} + +/* called from driver core with dev locked */ +static int usb_unbind_device(struct device *dev) +{ + struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); + + udriver->disconnect(to_usb_device(dev)); + return 0; +} + + +/* called from driver core with dev locked */ static int usb_probe_interface(struct device *dev) { - struct usb_interface * intf = to_usb_interface(dev); - struct usb_driver * driver = to_usb_driver(dev->driver); + struct usb_driver *driver = to_usb_driver(dev->driver); + struct usb_interface *intf; const struct usb_device_id *id; int error = -ENODEV; dev_dbg(dev, "%s\n", __FUNCTION__); - if (!driver->probe) + if (is_usb_device(dev)) /* Sanity check */ return error; + + intf = to_usb_interface(dev); + /* FIXME we'd much prefer to just resume it ... */ if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; @@ -182,19 +219,18 @@ static int usb_probe_interface(struct device *dev) return error; } -/* called from driver core with usb_bus_type.subsys writelock */ +/* called from driver core with dev locked */ static int usb_unbind_interface(struct device *dev) { + struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); - struct usb_driver *driver = to_usb_driver(intf->dev.driver); intf->condition = USB_INTERFACE_UNBINDING; /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); - if (driver && driver->disconnect) - driver->disconnect(intf); + driver->disconnect(intf); /* reset other interface state */ usb_set_interface(interface_to_usbdev(intf), @@ -235,7 +271,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (dev->driver) return -EBUSY; - dev->driver = &driver->driver; + dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); iface->condition = USB_INTERFACE_BOUND; mark_active(iface); @@ -270,7 +306,7 @@ void usb_driver_release_interface(struct usb_driver *driver, struct device *dev = &iface->dev; /* this should never happen, don't release something that's not ours */ - if (!dev->driver || dev->driver != &driver->driver) + if (!dev->driver || dev->driver != &driver->drvwrap.driver) return; /* don't release from within disconnect() */ @@ -433,24 +469,37 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_match_id); int usb_device_match(struct device *dev, struct device_driver *drv) { - struct usb_interface *intf; - struct usb_driver *usb_drv; - const struct usb_device_id *id; - - /* check for generic driver, which we don't match any device with */ - if (drv == &usb_generic_driver) - return 0; + /* devices and interfaces are handled separately */ + if (is_usb_device(dev)) { - intf = to_usb_interface(dev); - usb_drv = to_usb_driver(drv); + /* interface drivers never match devices */ + if (!is_usb_device_driver(drv)) + return 0; - id = usb_match_id(intf, usb_drv->id_table); - if (id) + /* TODO: Add real matching code */ return 1; - id = usb_match_dynamic_id(intf, usb_drv); - if (id) - return 1; + } else { + struct usb_interface *intf; + struct usb_driver *usb_drv; + const struct usb_device_id *id; + + /* device drivers never match interfaces */ + if (is_usb_device_driver(drv)) + return 0; + + intf = to_usb_interface(dev); + usb_drv = to_usb_driver(drv); + + id = usb_match_id(intf, usb_drv->id_table); + if (id) + return 1; + + id = usb_match_dynamic_id(intf, usb_drv); + if (id) + return 1; + } + return 0; } @@ -481,14 +530,13 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, /* driver is often null here; dev_dbg() would oops */ pr_debug ("usb %s: uevent\n", dev->bus_id); - /* Must check driver_data here, as on remove driver is always NULL */ - if ((dev->driver == &usb_generic_driver) || - (dev->driver_data == &usb_generic_driver_data)) + if (is_usb_device(dev)) return 0; - - intf = to_usb_interface(dev); - usb_dev = interface_to_usbdev (intf); - alt = intf->cur_altsetting; + else { + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev(intf); + alt = intf->cur_altsetting; + } if (usb_dev->devnum < 0) { pr_debug ("usb %s: already deleted?\n", dev->bus_id); @@ -569,13 +617,71 @@ static int usb_uevent(struct device *dev, char **envp, #endif /* CONFIG_HOTPLUG */ /** - * usb_register_driver - register a USB driver - * @new_driver: USB operations for the driver + * usb_register_device_driver - register a USB device (not interface) driver + * @new_udriver: USB operations for the device driver * @owner: module owner of this driver. * - * Registers a USB driver with the USB core. The list of unattached - * interfaces will be rescanned whenever a new driver is added, allowing - * the new driver to attach to any recognized devices. + * Registers a USB device driver with the USB core. The list of + * unattached devices will be rescanned whenever a new driver is + * added, allowing the new driver to attach to any recognized devices. + * Returns a negative error code on failure and 0 on success. + */ +int usb_register_device_driver(struct usb_device_driver *new_udriver, + struct module *owner) +{ + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + new_udriver->drvwrap.for_devices = 1; + new_udriver->drvwrap.driver.name = (char *) new_udriver->name; + new_udriver->drvwrap.driver.bus = &usb_bus_type; + new_udriver->drvwrap.driver.probe = usb_probe_device; + new_udriver->drvwrap.driver.remove = usb_unbind_device; + new_udriver->drvwrap.driver.owner = owner; + + retval = driver_register(&new_udriver->drvwrap.driver); + + if (!retval) { + pr_info("%s: registered new device driver %s\n", + usbcore_name, new_udriver->name); + usbfs_update_special(); + } else { + printk(KERN_ERR "%s: error %d registering device " + " driver %s\n", + usbcore_name, retval, new_udriver->name); + } + + return retval; +} +EXPORT_SYMBOL_GPL(usb_register_device_driver); + +/** + * usb_deregister_device_driver - unregister a USB device (not interface) driver + * @udriver: USB operations of the device driver to unregister + * Context: must be able to sleep + * + * Unlinks the specified driver from the internal USB driver list. + */ +void usb_deregister_device_driver(struct usb_device_driver *udriver) +{ + pr_info("%s: deregistering device driver %s\n", + usbcore_name, udriver->name); + + driver_unregister(&udriver->drvwrap.driver); + usbfs_update_special(); +} +EXPORT_SYMBOL_GPL(usb_deregister_device_driver); + +/** + * usb_register_driver - register a USB interface driver + * @new_driver: USB operations for the interface driver + * @owner: module owner of this driver. + * + * Registers a USB interface driver with the USB core. The list of + * unattached interfaces will be rescanned whenever a new driver is + * added, allowing the new driver to attach to any recognized interfaces. * Returns a negative error code on failure and 0 on success. * * NOTE: if you want your driver to use the USB major number, you must call @@ -589,23 +695,25 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner) if (usb_disabled()) return -ENODEV; - new_driver->driver.name = (char *)new_driver->name; - new_driver->driver.bus = &usb_bus_type; - new_driver->driver.probe = usb_probe_interface; - new_driver->driver.remove = usb_unbind_interface; - new_driver->driver.owner = owner; + new_driver->drvwrap.for_devices = 0; + new_driver->drvwrap.driver.name = (char *) new_driver->name; + new_driver->drvwrap.driver.bus = &usb_bus_type; + new_driver->drvwrap.driver.probe = usb_probe_interface; + new_driver->drvwrap.driver.remove = usb_unbind_interface; + new_driver->drvwrap.driver.owner = owner; spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); - retval = driver_register(&new_driver->driver); + retval = driver_register(&new_driver->drvwrap.driver); if (!retval) { - pr_info("%s: registered new driver %s\n", + pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name); usbfs_update_special(); usb_create_newid_file(new_driver); } else { - printk(KERN_ERR "%s: error %d registering driver %s\n", + printk(KERN_ERR "%s: error %d registering interface " + " driver %s\n", usbcore_name, retval, new_driver->name); } @@ -614,8 +722,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner) EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver); /** - * usb_deregister - unregister a USB driver - * @driver: USB operations of the driver to unregister + * usb_deregister - unregister a USB interface driver + * @driver: USB operations of the interface driver to unregister * Context: must be able to sleep * * Unlinks the specified driver from the internal USB driver list. @@ -626,11 +734,12 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver); */ void usb_deregister(struct usb_driver *driver) { - pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + pr_info("%s: deregistering interface driver %s\n", + usbcore_name, driver->name); usb_remove_newid_file(driver); usb_free_dynids(driver); - driver_unregister(&driver->driver); + driver_unregister(&driver->drvwrap.driver); usbfs_update_special(); } @@ -655,7 +764,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) * marked for FREEZE as soon as their children are already idled. * But those semantics are useless, so we equate the two (sigh). */ - if (dev->driver == &usb_generic_driver) { + if (is_usb_device(dev)) { if (dev->power.power_state.event == message.event) return 0; /* we need to rule out bogus requests through sysfs */ @@ -665,8 +774,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) return usb_port_suspend(to_usb_device(dev)); } - if ((dev->driver == NULL) || - (dev->driver_data == &usb_generic_driver_data)) + if (dev->driver == NULL) return 0; intf = to_usb_interface(dev); @@ -705,15 +813,14 @@ static int usb_generic_resume(struct device *dev) dev->power.power_state.event = PM_EVENT_ON; /* devices resume through their hubs */ - if (dev->driver == &usb_generic_driver) { + if (is_usb_device(dev)) { udev = to_usb_device(dev); if (udev->state == USB_STATE_NOTATTACHED) return 0; return usb_port_resume(udev); } - if ((dev->driver == NULL) || - (dev->driver_data == &usb_generic_driver_data)) { + if (dev->driver == NULL) { dev->power.power_state.event = PM_EVENT_FREEZE; return 0; } -- 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/driver.c | 69 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 35 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0d4b5dc..a62de0a 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -530,9 +530,10 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, /* driver is often null here; dev_dbg() would oops */ pr_debug ("usb %s: uevent\n", dev->bus_id); - if (is_usb_device(dev)) - return 0; - else { + if (is_usb_device(dev)) { + usb_dev = to_usb_device(dev); + alt = NULL; + } else { intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); alt = intf->cur_altsetting; @@ -579,15 +580,17 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (!is_usb_device(dev)) { + + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "INTERFACE=%d/%d/%d", alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, alt->desc.bInterfaceProtocol)) - return -ENOMEM; + return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", le16_to_cpu(usb_dev->descriptor.idVendor), @@ -599,7 +602,8 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, alt->desc.bInterfaceProtocol)) - return -ENOMEM; + return -ENOMEM; + } envp[i] = NULL; @@ -747,31 +751,22 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -static int verify_suspended(struct device *dev, void *unused) +static int usb_suspend(struct device *dev, pm_message_t message) { - if (dev->driver == NULL) - return 0; - return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; -} + struct usb_device *udev; + struct usb_device_driver *udriver; + struct usb_interface *intf; + struct usb_driver *driver; + int status; -static int usb_generic_suspend(struct device *dev, pm_message_t message) -{ - struct usb_interface *intf; - struct usb_driver *driver; - int 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). - */ if (is_usb_device(dev)) { + if (dev->driver == NULL) + return 0; + udev = to_usb_device(dev); + udriver = to_usb_device_driver(dev->driver); if (dev->power.power_state.event == message.event) return 0; - /* we need to rule out bogus requests through sysfs */ - status = device_for_each_child(dev, NULL, verify_suspended); - if (status) - return status; - return usb_port_suspend(to_usb_device(dev)); + return udriver->suspend(udev, message); } if (dev->driver == NULL) @@ -799,12 +794,13 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) return status; } -static int usb_generic_resume(struct device *dev) +static int usb_resume(struct device *dev) { - struct usb_interface *intf; - struct usb_driver *driver; - struct usb_device *udev; - int status; + struct usb_device *udev; + struct usb_device_driver *udriver; + struct usb_interface *intf; + struct usb_driver *driver; + int status; if (dev->power.power_state.event == PM_EVENT_ON) return 0; @@ -814,10 +810,13 @@ static int usb_generic_resume(struct device *dev) /* devices resume through their hubs */ if (is_usb_device(dev)) { + if (dev->driver == NULL) + return 0; udev = to_usb_device(dev); + udriver = to_usb_device_driver(dev->driver); if (udev->state == USB_STATE_NOTATTACHED) return 0; - return usb_port_resume(udev); + return udriver->resume(udev); } if (dev->driver == NULL) { @@ -854,7 +853,7 @@ struct bus_type usb_bus_type = { .match = usb_device_match, .uevent = usb_uevent, #ifdef CONFIG_PM - .suspend = usb_generic_suspend, - .resume = usb_generic_resume, + .suspend = usb_suspend, + .resume = usb_resume, #endif }; -- cgit v1.1 From 1cc8a25d5b680ff656927ffa9b66fae6b415b1d3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:10:15 -0400 Subject: usbcore: split suspend/resume for device and interfaces This patch (as716b) splits up the core suspend and resume routines into two parts each: one for handling devices and one for handling interfaces. The behavior of the parts should be the same as in the old unified code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 122 +++++++++++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 45 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index a62de0a..b0db158 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -751,81 +751,89 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -static int usb_suspend(struct device *dev, pm_message_t message) +/* Caller has locked udev */ +static int suspend_device(struct usb_device *udev, pm_message_t msg) { - struct usb_device *udev; struct usb_device_driver *udriver; - struct usb_interface *intf; - struct usb_driver *driver; - int status; - if (is_usb_device(dev)) { - if (dev->driver == NULL) - return 0; - udev = to_usb_device(dev); - udriver = to_usb_device_driver(dev->driver); - if (dev->power.power_state.event == message.event) - return 0; - return udriver->suspend(udev, message); - } + if (udev->dev.driver == NULL) + return 0; + udriver = to_usb_device_driver(udev->dev.driver); + if (udev->dev.power.power_state.event == msg.event) + return 0; + return udriver->suspend(udev, msg); +} + +/* Caller has locked udev */ +static int resume_device(struct usb_device *udev) +{ + struct usb_device_driver *udriver; - if (dev->driver == NULL) + if (udev->dev.power.power_state.event == PM_EVENT_ON) return 0; - intf = to_usb_interface(dev); - driver = to_usb_driver(dev->driver); + /* mark things as "on" immediately, no matter what errors crop up */ + udev->dev.power.power_state.event = PM_EVENT_ON; + + if (udev->dev.driver == NULL) + return 0; + udriver = to_usb_device_driver(udev->dev.driver); + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + return udriver->resume(udev); +} + +/* Caller has locked intf */ +static int suspend_interface(struct usb_interface *intf, pm_message_t msg) +{ + struct usb_driver *driver; + int status; + + if (intf->dev.driver == NULL) + return 0; + + driver = to_usb_driver(intf->dev.driver); /* with no hardware, USB interfaces only use FREEZE and ON states */ if (!is_active(intf)) return 0; if (driver->suspend && driver->resume) { - status = driver->suspend(intf, message); + status = driver->suspend(intf, msg); if (status) - dev_err(dev, "%s error %d\n", "suspend", status); + dev_err(&intf->dev, "%s error %d\n", + "suspend", status); else mark_quiesced(intf); } else { // FIXME else if there's no suspend method, disconnect... - dev_warn(dev, "no suspend for driver %s?\n", driver->name); + dev_warn(&intf->dev, "no suspend for driver %s?\n", + driver->name); mark_quiesced(intf); status = 0; } return status; } -static int usb_resume(struct device *dev) +/* Caller has locked intf */ +static int resume_interface(struct usb_interface *intf) { - struct usb_device *udev; - struct usb_device_driver *udriver; - struct usb_interface *intf; - struct usb_driver *driver; - int status; + struct usb_driver *driver; + struct usb_device *udev; + int status; - if (dev->power.power_state.event == PM_EVENT_ON) + if (intf->dev.power.power_state.event == PM_EVENT_ON) return 0; /* mark things as "on" immediately, no matter what errors crop up */ - dev->power.power_state.event = PM_EVENT_ON; + intf->dev.power.power_state.event = PM_EVENT_ON; - /* devices resume through their hubs */ - if (is_usb_device(dev)) { - if (dev->driver == NULL) - return 0; - udev = to_usb_device(dev); - udriver = to_usb_device_driver(dev->driver); - if (udev->state == USB_STATE_NOTATTACHED) - return 0; - return udriver->resume(udev); - } - - if (dev->driver == NULL) { - dev->power.power_state.event = PM_EVENT_FREEZE; + if (intf->dev.driver == NULL) { + intf->dev.power.power_state.event = PM_EVENT_FREEZE; return 0; } - intf = to_usb_interface(dev); - driver = to_usb_driver(dev->driver); + driver = to_usb_driver(intf->dev.driver); udev = interface_to_usbdev(intf); if (udev->state == USB_STATE_NOTATTACHED) @@ -838,14 +846,38 @@ static int usb_resume(struct device *dev) if (driver->resume) { status = driver->resume(intf); if (status) { - dev_err(dev, "%s error %d\n", "resume", status); + dev_err(&intf->dev, "%s error %d\n", + "resume", status); mark_quiesced(intf); } } else - dev_warn(dev, "no resume for driver %s?\n", driver->name); + dev_warn(&intf->dev, "no resume for driver %s?\n", + driver->name); return 0; } +static int usb_suspend(struct device *dev, pm_message_t message) +{ + int status; + + if (is_usb_device(dev)) + status = suspend_device(to_usb_device(dev), message); + else + status = suspend_interface(to_usb_interface(dev), message); + return status; +} + +static int usb_resume(struct device *dev) +{ + int status; + + if (is_usb_device(dev)) + status = resume_device(to_usb_device(dev)); + else + status = resume_interface(to_usb_interface(dev)); + return status; +} + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { -- 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/driver.c | 63 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b0db158..eefc985 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -783,7 +783,7 @@ static int resume_device(struct usb_device *udev) return udriver->resume(udev); } -/* Caller has locked intf */ +/* Caller has locked intf's usb_device */ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; @@ -815,7 +815,7 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) return status; } -/* Caller has locked intf */ +/* Caller has locked intf's usb_device */ static int resume_interface(struct usb_interface *intf) { struct usb_driver *driver; @@ -856,14 +856,59 @@ static int resume_interface(struct usb_interface *intf) return 0; } +/* Caller has locked udev */ +int usb_suspend_both(struct usb_device *udev, pm_message_t msg) +{ + int status = 0; + int i = 0; + struct usb_interface *intf; + + if (udev->actconfig) { + for (; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; + status = suspend_interface(intf, msg); + if (status != 0) + break; + } + } + if (status == 0) + status = suspend_device(udev, msg); + + /* If the suspend failed, resume interfaces that did get suspended */ + if (status != 0) { + while (--i >= 0) { + intf = udev->actconfig->interface[i]; + resume_interface(intf); + } + } + return status; +} + +/* Caller has locked udev */ +int usb_resume_both(struct usb_device *udev) +{ + int status; + int i; + struct usb_interface *intf; + + status = resume_device(udev); + if (status == 0 && udev->actconfig) { + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; + resume_interface(intf); + } + } + return status; +} + static int usb_suspend(struct device *dev, pm_message_t message) { int status; if (is_usb_device(dev)) - status = suspend_device(to_usb_device(dev), message); + status = usb_suspend_both(to_usb_device(dev), message); else - status = suspend_interface(to_usb_interface(dev), message); + status = 0; return status; } @@ -871,10 +916,12 @@ static int usb_resume(struct device *dev) { int status; - if (is_usb_device(dev)) - status = resume_device(to_usb_device(dev)); - else - status = resume_interface(to_usb_interface(dev)); + if (is_usb_device(dev)) { + status = usb_resume_both(to_usb_device(dev)); + + /* Rebind drivers that had no suspend method? */ + } else + status = 0; return status; } -- cgit v1.1 From 2bf4086d7a7722b470aa24e1be725cc58619c6fe Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:12:19 -0400 Subject: usbcore: set device and power states properly This patch (as733) fixes up the places where device states and power states are set in usbcore. Right now things are duplicated or missing; this should straighten things out. The idea is that udev->state is USB_STATE_SUSPENDED exactly when the device's upstream port has been suspended, whereas udev->dev.power.power_state.event reflects the result of the last call to the suspend/resume routines (which might not actually change the device state, especially if CONFIG_USB_SUSPEND isn't set). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 71 ++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 29 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index eefc985..92ecc4e 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -755,48 +755,57 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); static int suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; + int status = 0; if (udev->dev.driver == NULL) - return 0; + goto done; udriver = to_usb_device_driver(udev->dev.driver); if (udev->dev.power.power_state.event == msg.event) - return 0; - return udriver->suspend(udev, msg); + goto done; + status = udriver->suspend(udev, msg); + +done: + if (status == 0) + udev->dev.power.power_state.event = msg.event; + return status; } /* Caller has locked udev */ static int resume_device(struct usb_device *udev) { struct usb_device_driver *udriver; + int status = 0; if (udev->dev.power.power_state.event == PM_EVENT_ON) - return 0; - - /* mark things as "on" immediately, no matter what errors crop up */ - udev->dev.power.power_state.event = PM_EVENT_ON; + goto done; if (udev->dev.driver == NULL) - return 0; + goto done; udriver = to_usb_device_driver(udev->dev.driver); if (udev->state == USB_STATE_NOTATTACHED) - return 0; - return udriver->resume(udev); + goto done; + status = udriver->resume(udev); + +done: + if (status == 0) + udev->dev.power.power_state.event = PM_EVENT_ON; + return status; } /* Caller has locked intf's usb_device */ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; - int status; + int status = 0; if (intf->dev.driver == NULL) - return 0; + goto done; driver = to_usb_driver(intf->dev.driver); /* with no hardware, USB interfaces only use FREEZE and ON states */ if (!is_active(intf)) - return 0; + goto done; if (driver->suspend && driver->resume) { status = driver->suspend(intf, msg); @@ -810,8 +819,11 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) dev_warn(&intf->dev, "no suspend for driver %s?\n", driver->name); mark_quiesced(intf); - status = 0; } + +done: + if (status == 0) + intf->dev.power.power_state.event = msg.event; return status; } @@ -820,24 +832,19 @@ static int resume_interface(struct usb_interface *intf) { struct usb_driver *driver; struct usb_device *udev; - int status; + int status = 0; if (intf->dev.power.power_state.event == PM_EVENT_ON) - return 0; - - /* mark things as "on" immediately, no matter what errors crop up */ - intf->dev.power.power_state.event = PM_EVENT_ON; + goto done; - if (intf->dev.driver == NULL) { - intf->dev.power.power_state.event = PM_EVENT_FREEZE; - return 0; - } + if (intf->dev.driver == NULL) + goto done; driver = to_usb_driver(intf->dev.driver); udev = interface_to_usbdev(intf); if (udev->state == USB_STATE_NOTATTACHED) - return 0; + goto done; /* if driver was suspended, it has a resume method; * however, sysfs can wrongly mark things as suspended @@ -845,15 +852,21 @@ static int resume_interface(struct usb_interface *intf) */ if (driver->resume) { status = driver->resume(intf); - if (status) { + if (status) dev_err(&intf->dev, "%s error %d\n", "resume", status); - mark_quiesced(intf); - } - } else + else + mark_active(intf); + } else { dev_warn(&intf->dev, "no resume for driver %s?\n", driver->name); - return 0; + mark_active(intf); + } + +done: + if (status == 0) + intf->dev.power.power_state.event = PM_EVENT_ON; + return status; } /* Caller has locked udev */ -- 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/driver.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 92ecc4e..affbfb5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -757,11 +757,13 @@ static int suspend_device(struct usb_device *udev, pm_message_t msg) struct usb_device_driver *udriver; int status = 0; + if (udev->state == USB_STATE_NOTATTACHED || + udev->state == USB_STATE_SUSPENDED) + goto done; + if (udev->dev.driver == NULL) goto done; udriver = to_usb_device_driver(udev->dev.driver); - if (udev->dev.power.power_state.event == msg.event) - goto done; status = udriver->suspend(udev, msg); done: @@ -776,14 +778,13 @@ static int resume_device(struct usb_device *udev) struct usb_device_driver *udriver; int status = 0; - if (udev->dev.power.power_state.event == PM_EVENT_ON) + if (udev->state == USB_STATE_NOTATTACHED || + udev->state != USB_STATE_SUSPENDED) goto done; if (udev->dev.driver == NULL) goto done; udriver = to_usb_device_driver(udev->dev.driver); - if (udev->state == USB_STATE_NOTATTACHED) - goto done; status = udriver->resume(udev); done: @@ -798,14 +799,14 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) struct usb_driver *driver; int status = 0; - if (intf->dev.driver == NULL) + /* with no hardware, USB interfaces only use FREEZE and ON states */ + if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED || + !is_active(intf)) goto done; - driver = to_usb_driver(intf->dev.driver); - - /* with no hardware, USB interfaces only use FREEZE and ON states */ - if (!is_active(intf)) + if (intf->dev.driver == NULL) goto done; + driver = to_usb_driver(intf->dev.driver); if (driver->suspend && driver->resume) { status = driver->suspend(intf, msg); @@ -831,25 +832,16 @@ done: static int resume_interface(struct usb_interface *intf) { struct usb_driver *driver; - struct usb_device *udev; int status = 0; - if (intf->dev.power.power_state.event == PM_EVENT_ON) + if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED || + is_active(intf)) goto done; if (intf->dev.driver == NULL) goto done; - driver = to_usb_driver(intf->dev.driver); - udev = interface_to_usbdev(intf); - if (udev->state == USB_STATE_NOTATTACHED) - goto done; - - /* if driver was suspended, it has a resume method; - * however, sysfs can wrongly mark things as suspended - * (on the "no suspend method" FIXME path above) - */ if (driver->resume) { status = driver->resume(intf); if (status) @@ -904,6 +896,12 @@ int usb_resume_both(struct usb_device *udev) int i; struct usb_interface *intf; + /* Can't resume if the parent is suspended */ + if (udev->parent && udev->parent->state == USB_STATE_SUSPENDED) { + dev_warn(&udev->dev, "can't resume; parent is suspended\n"); + return -EHOSTUNREACH; + } + status = resume_device(udev); if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { -- cgit v1.1 From 1c5df7e705671f11a71112eb3a1f9765cd1719f9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:13:50 -0400 Subject: usbcore: suspending devices with no driver Since usb_generic can be unbound from a USB device, we need to be able to handle the possibility that a suspend or resume request arrives for a device with no driver. This patch (as735) arranges things so that resume requests will fail and suspend requests will use the standard USB port-suspend code. Attempts to suspend or resume an unbound interface are handled similarly (although the error caused by trying to resume an unbound interface is dropped by the calling routine). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index affbfb5..a5d1146 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -761,8 +761,12 @@ static int suspend_device(struct usb_device *udev, pm_message_t msg) udev->state == USB_STATE_SUSPENDED) goto done; - if (udev->dev.driver == NULL) + /* For devices that don't have a driver, we do a standard suspend. */ + if (udev->dev.driver == NULL) { + status = usb_port_suspend(udev); goto done; + } + udriver = to_usb_device_driver(udev->dev.driver); status = udriver->suspend(udev, msg); @@ -782,8 +786,12 @@ static int resume_device(struct usb_device *udev) udev->state != USB_STATE_SUSPENDED) goto done; - if (udev->dev.driver == NULL) + /* Can't resume it if it doesn't have a driver. */ + if (udev->dev.driver == NULL) { + status = -ENOTCONN; goto done; + } + udriver = to_usb_device_driver(udev->dev.driver); status = udriver->resume(udev); @@ -804,7 +812,7 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) !is_active(intf)) goto done; - if (intf->dev.driver == NULL) + if (intf->dev.driver == NULL) /* This can't happen */ goto done; driver = to_usb_driver(intf->dev.driver); @@ -838,8 +846,11 @@ static int resume_interface(struct usb_interface *intf) is_active(intf)) goto done; - if (intf->dev.driver == NULL) + /* Can't resume it if it doesn't have a driver. */ + if (intf->dev.driver == NULL) { + status = -ENOTCONN; goto done; + } driver = to_usb_driver(intf->dev.driver); if (driver->resume) { -- cgit v1.1 From 645daaab0b6adc35c1838df2a82f9d729fdb1767 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 Aug 2006 15:47:02 -0400 Subject: usbcore: add autosuspend/autoresume infrastructure This patch (as739) adds the basic infrastructure for USB autosuspend and autoresume. The main features are: PM usage counters added to struct usb_device and struct usb_interface, indicating whether it's okay to autosuspend them or they are currently in use. Flag added to usb_device indicating whether the current suspend/resume operation originated from outside or as an autosuspend/autoresume. Flag added to usb_driver indicating whether the driver supports autosuspend. If not, no device bound to the driver will be autosuspended. Mutex added to usb_device for protecting PM operations. Unlike the device semaphore, the locking rule for the pm_mutex is that you must acquire the locks going _up_ the device tree. New routines handling autosuspend/autoresume requests for interfaces and devices. Suspend and resume requests are propagated up the device tree (but not outside the USB subsystem). work_struct added to usb_device, for carrying out delayed autosuspend requests. Autoresume added (and autosuspend prevented) during probe and disconnect. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 419 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 389 insertions(+), 30 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index a5d1146..2b2000a 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -157,12 +157,13 @@ static int usb_probe_device(struct device *dev) udev = to_usb_device(dev); - /* FIXME: resume a suspended device */ - if (udev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - /* TODO: Add real matching code */ + /* The device should always appear to be in use + * unless the driver suports autosuspend. + */ + udev->pm_usage_cnt = !(udriver->supports_autosuspend); + error = udriver->probe(udev); return error; } @@ -182,6 +183,7 @@ static int usb_probe_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf; + struct usb_device *udev; const struct usb_device_id *id; int error = -ENODEV; @@ -191,10 +193,7 @@ static int usb_probe_interface(struct device *dev) return error; intf = to_usb_interface(dev); - - /* FIXME we'd much prefer to just resume it ... */ - if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; + udev = interface_to_usbdev(intf); id = usb_match_id(intf, driver->id_table); if (!id) @@ -202,18 +201,31 @@ static int usb_probe_interface(struct device *dev) if (id) { dev_dbg(dev, "%s - got id\n", __FUNCTION__); + error = usb_autoresume_device(udev, 1); + if (error) + return error; + /* Interface "power state" doesn't correspond to any hardware * state whatsoever. We use it to record when it's bound to * a driver that may start I/0: it's not frozen/quiesced. */ mark_active(intf); intf->condition = USB_INTERFACE_BINDING; + + /* The interface should always appear to be in use + * unless the driver suports autosuspend. + */ + intf->pm_usage_cnt = !(driver->supports_autosuspend); + error = driver->probe(intf, id); if (error) { mark_quiesced(intf); + intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; } else intf->condition = USB_INTERFACE_BOUND; + + usb_autosuspend_device(udev, 1); } return error; @@ -224,9 +236,15 @@ static int usb_unbind_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev; + int error; intf->condition = USB_INTERFACE_UNBINDING; + /* Autoresume for set_interface call below */ + udev = interface_to_usbdev(intf); + error = usb_autoresume_device(udev, 1); + /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -237,8 +255,13 @@ static int usb_unbind_interface(struct device *dev) intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; mark_quiesced(intf); + intf->needs_remote_wakeup = 0; + + if (!error) + usb_autosuspend_device(udev, 1); return 0; } @@ -267,14 +290,19 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) { struct device *dev = &iface->dev; + struct usb_device *udev = interface_to_usbdev(iface); if (dev->driver) return -EBUSY; dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); + + mutex_lock_nested(&udev->pm_mutex, udev->level); iface->condition = USB_INTERFACE_BOUND; mark_active(iface); + iface->pm_usage_cnt = !(driver->supports_autosuspend); + mutex_unlock(&udev->pm_mutex); /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -304,6 +332,7 @@ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { struct device *dev = &iface->dev; + struct usb_device *udev = interface_to_usbdev(iface); /* this should never happen, don't release something that's not ours */ if (!dev->driver || dev->driver != &driver->drvwrap.driver) @@ -321,8 +350,12 @@ void usb_driver_release_interface(struct usb_driver *driver, dev->driver = NULL; usb_set_intfdata(iface, NULL); + + mutex_lock_nested(&udev->pm_mutex, udev->level); iface->condition = USB_INTERFACE_UNBOUND; mark_quiesced(iface); + iface->needs_remote_wakeup = 0; + mutex_unlock(&udev->pm_mutex); } EXPORT_SYMBOL(usb_driver_release_interface); @@ -751,7 +784,7 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -/* Caller has locked udev */ +/* Caller has locked udev->pm_mutex */ static int suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -763,6 +796,7 @@ static int suspend_device(struct usb_device *udev, pm_message_t msg) /* For devices that don't have a driver, we do a standard suspend. */ if (udev->dev.driver == NULL) { + udev->do_remote_wakeup = 0; status = usb_port_suspend(udev); goto done; } @@ -771,12 +805,13 @@ static int suspend_device(struct usb_device *udev, pm_message_t msg) status = udriver->suspend(udev, msg); done: + // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) udev->dev.power.power_state.event = msg.event; return status; } -/* Caller has locked udev */ +/* Caller has locked udev->pm_mutex */ static int resume_device(struct usb_device *udev) { struct usb_device_driver *udriver; @@ -796,12 +831,13 @@ static int resume_device(struct usb_device *udev) status = udriver->resume(udev); done: + // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) udev->dev.power.power_state.event = PM_EVENT_ON; return status; } -/* Caller has locked intf's usb_device */ +/* Caller has locked intf's usb_device's pm_mutex */ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; @@ -812,31 +848,33 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) !is_active(intf)) goto done; - if (intf->dev.driver == NULL) /* This can't happen */ + if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */ goto done; driver = to_usb_driver(intf->dev.driver); if (driver->suspend && driver->resume) { status = driver->suspend(intf, msg); - if (status) + if (status == 0) + mark_quiesced(intf); + else if (!interface_to_usbdev(intf)->auto_pm) dev_err(&intf->dev, "%s error %d\n", "suspend", status); - else - mark_quiesced(intf); } else { // FIXME else if there's no suspend method, disconnect... + // Not possible if auto_pm is set... dev_warn(&intf->dev, "no suspend for driver %s?\n", driver->name); mark_quiesced(intf); } done: + // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) intf->dev.power.power_state.event = msg.event; return status; } -/* Caller has locked intf's usb_device */ +/* Caller has locked intf's usb_device's pm_mutex */ static int resume_interface(struct usb_interface *intf) { struct usb_driver *driver; @@ -846,8 +884,12 @@ static int resume_interface(struct usb_interface *intf) is_active(intf)) goto done; + /* Don't let autoresume interfere with unbinding */ + if (intf->condition == USB_INTERFACE_UNBINDING) + goto done; + /* Can't resume it if it doesn't have a driver. */ - if (intf->dev.driver == NULL) { + if (intf->condition == USB_INTERFACE_UNBOUND) { status = -ENOTCONN; goto done; } @@ -867,18 +909,88 @@ static int resume_interface(struct usb_interface *intf) } done: + // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) intf->dev.power.power_state.event = PM_EVENT_ON; return status; } -/* Caller has locked udev */ +/** + * usb_suspend_both - suspend a USB device and its interfaces + * @udev: the usb_device to suspend + * @msg: Power Management message describing this state transition + * + * This is the central routine for suspending USB devices. It calls the + * suspend methods for all the interface drivers in @udev and then calls + * the suspend method for @udev itself. If an error occurs at any stage, + * all the interfaces which were suspended are resumed so that they remain + * in the same state as the device. + * + * If an autosuspend is in progress (@udev->auto_pm is set), the routine + * checks first to make sure that neither the device itself or any of its + * active interfaces is in use (pm_usage_cnt is greater than 0). If they + * are, the autosuspend fails. + * + * If the suspend succeeds, the routine recursively queues an autosuspend + * request for @udev's parent device, thereby propagating the change up + * the device tree. If all of the parent's children are now suspended, + * the parent will autosuspend in turn. + * + * The suspend method calls are subject to mutual exclusion under control + * of @udev's pm_mutex. Many of these calls are also under the protection + * of @udev's device lock (including all requests originating outside the + * USB subsystem), but autosuspend requests generated by a child device or + * interface driver may not be. Usbcore will insure that the method calls + * do not arrive during bind, unbind, or reset operations. However, drivers + * must be prepared to handle suspend calls arriving at unpredictable times. + * The only way to block such calls is to do an autoresume (preventing + * autosuspends) while holding @udev's device lock (preventing outside + * suspends). + * + * The caller must hold @udev->pm_mutex. + * + * This routine can run only in process context. + */ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i = 0; struct usb_interface *intf; + struct usb_device *parent = udev->parent; + + cancel_delayed_work(&udev->autosuspend); + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + if (udev->state == USB_STATE_SUSPENDED) + return 0; + udev->do_remote_wakeup = device_may_wakeup(&udev->dev); + + /* For autosuspend, fail fast if anything is in use. + * Also fail if any interfaces require remote wakeup but it + * isn't available. */ + if (udev->auto_pm) { + if (udev->pm_usage_cnt > 0) + return -EBUSY; + if (udev->actconfig) { + for (; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; + if (!is_active(intf)) + continue; + if (intf->pm_usage_cnt > 0) + return -EBUSY; + if (intf->needs_remote_wakeup && + !udev->do_remote_wakeup) { + dev_dbg(&udev->dev, + "remote wakeup needed for autosuspend\n"); + return -EOPNOTSUPP; + } + } + i = 0; + } + } + + /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { for (; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -896,40 +1008,282 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) intf = udev->actconfig->interface[i]; resume_interface(intf); } - } + + /* If the suspend succeeded, propagate it up the tree */ + } else if (parent) + usb_autosuspend_device(parent, 0); + + // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } -/* Caller has locked udev */ +/** + * usb_resume_both - resume a USB device and its interfaces + * @udev: the usb_device to resume + * + * This is the central routine for resuming USB devices. It calls the + * the resume method for @udev and then calls the resume methods for all + * the interface drivers in @udev. + * + * Before starting the resume, the routine calls itself recursively for + * the parent device of @udev, thereby propagating the change up the device + * tree and assuring that @udev will be able to resume. If the parent is + * unable to resume successfully, the routine fails. + * + * The resume method calls are subject to mutual exclusion under control + * of @udev's pm_mutex. Many of these calls are also under the protection + * of @udev's device lock (including all requests originating outside the + * USB subsystem), but autoresume requests generated by a child device or + * interface driver may not be. Usbcore will insure that the method calls + * do not arrive during bind, unbind, or reset operations. However, drivers + * must be prepared to handle resume calls arriving at unpredictable times. + * The only way to block such calls is to do an autoresume (preventing + * other autoresumes) while holding @udev's device lock (preventing outside + * resumes). + * + * The caller must hold @udev->pm_mutex. + * + * This routine can run only in process context. + */ int usb_resume_both(struct usb_device *udev) { - int status; + int status = 0; int i; struct usb_interface *intf; + struct usb_device *parent = udev->parent; + + cancel_delayed_work(&udev->autosuspend); + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; - /* Can't resume if the parent is suspended */ - if (udev->parent && udev->parent->state == USB_STATE_SUSPENDED) { - dev_warn(&udev->dev, "can't resume; parent is suspended\n"); - return -EHOSTUNREACH; + /* Propagate the resume up the tree, if necessary */ + if (udev->state == USB_STATE_SUSPENDED) { + if (parent) { + mutex_lock_nested(&parent->pm_mutex, parent->level); + parent->auto_pm = 1; + status = usb_resume_both(parent); + } else { + + /* We can't progagate beyond the USB subsystem, + * so if a root hub's controller is suspended + * then we're stuck. */ + if (udev->dev.parent->power.power_state.event != + PM_EVENT_ON) + status = -EHOSTUNREACH; + } + if (status == 0 && udev->state == USB_STATE_SUSPENDED) + status = resume_device(udev); + if (parent) + mutex_unlock(&parent->pm_mutex); } - status = resume_device(udev); + /* Now the parent won't suspend until we are finished */ + if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; resume_interface(intf); } } + + // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + return status; +} + +#ifdef CONFIG_USB_SUSPEND + +/** + * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces + * @udev - the usb_device to autosuspend + * @dec_usage_cnt - flag to decrement @udev's PM-usage counter + * + * This routine should be called when a core subsystem is finished using + * @udev and wants to allow it to autosuspend. Examples would be when + * @udev's device file in usbfs is closed or after a configuration change. + * + * @dec_usage_cnt should be 1 if the subsystem previously incremented + * @udev's usage counter (such as by passing 1 to usb_autoresume_device); + * otherwise it should be 0. + * + * If the usage counter for @udev or any of its active interfaces is greater + * than 0, the autosuspend request will not be queued. (If an interface + * driver does not support autosuspend then its usage counter is permanently + * positive.) Likewise, if an interface driver requires remote-wakeup + * capability during autosuspend but remote wakeup is disabled, the + * autosuspend will fail. + * + * Often the caller will hold @udev's device lock, but this is not + * necessary. + * + * This routine can run only in process context. + */ +void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) +{ + mutex_lock_nested(&udev->pm_mutex, udev->level); + udev->pm_usage_cnt -= dec_usage_cnt; + if (udev->pm_usage_cnt <= 0) + schedule_delayed_work(&udev->autosuspend, + USB_AUTOSUSPEND_DELAY); + mutex_unlock(&udev->pm_mutex); + // dev_dbg(&udev->dev, "%s: cnt %d\n", + // __FUNCTION__, udev->pm_usage_cnt); +} + +/** + * usb_autoresume_device - immediately autoresume a USB device and its interfaces + * @udev - the usb_device to autoresume + * @inc_usage_cnt - flag to increment @udev's PM-usage counter + * + * This routine should be called when a core subsystem wants to use @udev + * and needs to guarantee that it is not suspended. In addition, the + * caller can prevent @udev from being autosuspended subsequently. (Note + * that this will not prevent suspend events originating in the PM core.) + * Examples would be when @udev's device file in usbfs is opened (autosuspend + * should be prevented until the file is closed) or when a remote-wakeup + * request is received (later autosuspends should not be prevented). + * + * @inc_usage_cnt should be 1 to increment @udev's usage counter and prevent + * autosuspends. This prevention will persist until the usage counter is + * decremented again (such as by passing 1 to usb_autosuspend_device). + * Otherwise @inc_usage_cnt should be 0 to leave the usage counter unchanged. + * Regardless, if the autoresume fails then the usage counter is not + * incremented. + * + * Often the caller will hold @udev's device lock, but this is not + * necessary (and attempting it might cause deadlock). + * + * This routine can run only in process context. + */ +int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) +{ + int status; + + mutex_lock_nested(&udev->pm_mutex, udev->level); + udev->pm_usage_cnt += inc_usage_cnt; + udev->auto_pm = 1; + status = usb_resume_both(udev); + if (status != 0) + udev->pm_usage_cnt -= inc_usage_cnt; + mutex_unlock(&udev->pm_mutex); + // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", + // __FUNCTION__, status, udev->pm_usage_cnt); + return status; +} + +/** + * usb_autopm_put_interface - decrement a USB interface's PM-usage counter + * @intf - the usb_interface whose counter should be decremented + * + * This routine should be called by an interface driver when it is + * finished using @intf and wants to allow it to autosuspend. A typical + * example would be a character-device driver when its device file is + * closed. + * + * The routine decrements @intf's usage counter. When the counter reaches + * 0, a delayed autosuspend request for @intf's device is queued. When + * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all + * the other usage counters for the sibling interfaces and @intf's + * usb_device, the device and all its interfaces will be autosuspended. + * + * Note that @intf->pm_usage_cnt is owned by the interface driver. The + * core will not change its value other than the increment and decrement + * in usb_autopm_get_interface and usb_autopm_put_interface. The driver + * may use this simple counter-oriented discipline or may set the value + * any way it likes. + * + * If the driver has set @intf->needs_remote_wakeup then autosuspend will + * take place only if the device's remote-wakeup facility is enabled. + * + * Suspend method calls queued by this routine can arrive at any time + * while @intf is resumed and its usage counter is equal to 0. They are + * not protected by the usb_device's lock but only by its pm_mutex. + * Drivers must provide their own synchronization. + * + * This routine can run only in process context. + */ +void usb_autopm_put_interface(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + mutex_lock_nested(&udev->pm_mutex, udev->level); + if (intf->condition != USB_INTERFACE_UNBOUND) { + if (--intf->pm_usage_cnt <= 0) + schedule_delayed_work(&udev->autosuspend, + USB_AUTOSUSPEND_DELAY); + } + mutex_unlock(&udev->pm_mutex); + // dev_dbg(&intf->dev, "%s: cnt %d\n", + // __FUNCTION__, intf->pm_usage_cnt); +} +EXPORT_SYMBOL_GPL(usb_autopm_put_interface); + +/** + * usb_autopm_get_interface - increment a USB interface's PM-usage counter + * @intf - the usb_interface whose counter should be incremented + * + * This routine should be called by an interface driver when it wants to + * use @intf and needs to guarantee that it is not suspended. In addition, + * the routine prevents @intf from being autosuspended subsequently. (Note + * that this will not prevent suspend events originating in the PM core.) + * This prevention will persist until usb_autopm_put_interface() is called + * or @intf is unbound. A typical example would be a character-device + * driver when its device file is opened. + * + * The routine increments @intf's usage counter. So long as the counter + * is greater than 0, autosuspend will not be allowed for @intf or its + * usb_device. When the driver is finished using @intf it should call + * usb_autopm_put_interface() to decrement the usage counter and queue + * a delayed autosuspend request (if the counter is <= 0). + * + * Note that @intf->pm_usage_cnt is owned by the interface driver. The + * core will not change its value other than the increment and decrement + * in usb_autopm_get_interface and usb_autopm_put_interface. The driver + * may use this simple counter-oriented discipline or may set the value + * any way it likes. + * + * Resume method calls generated by this routine can arrive at any time + * while @intf is suspended. They are not protected by the usb_device's + * lock but only by its pm_mutex. Drivers must provide their own + * synchronization. + * + * This routine can run only in process context. + */ +int usb_autopm_get_interface(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status; + + mutex_lock_nested(&udev->pm_mutex, udev->level); + if (intf->condition == USB_INTERFACE_UNBOUND) + status = -ENODEV; + else { + ++intf->pm_usage_cnt; + udev->auto_pm = 1; + status = usb_resume_both(udev); + if (status != 0) + --intf->pm_usage_cnt; + } + mutex_unlock(&udev->pm_mutex); + // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", + // __FUNCTION__, status, intf->pm_usage_cnt); return status; } +EXPORT_SYMBOL_GPL(usb_autopm_get_interface); + +#endif /* CONFIG_USB_SUSPEND */ static int usb_suspend(struct device *dev, pm_message_t message) { int status; - if (is_usb_device(dev)) - status = usb_suspend_both(to_usb_device(dev), message); - else + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); + + mutex_lock_nested(&udev->pm_mutex, udev->level); + udev->auto_pm = 0; + status = usb_suspend_both(udev, message); + mutex_unlock(&udev->pm_mutex); + } else status = 0; return status; } @@ -939,7 +1293,12 @@ static int usb_resume(struct device *dev) int status; if (is_usb_device(dev)) { - status = usb_resume_both(to_usb_device(dev)); + struct usb_device *udev = to_usb_device(dev); + + mutex_lock_nested(&udev->pm_mutex, udev->level); + udev->auto_pm = 0; + status = usb_resume_both(udev); + mutex_unlock(&udev->pm_mutex); /* Rebind drivers that had no suspend method? */ } else -- cgit v1.1 From 592fbbe4bc339399d363dd55f0391e0623400706 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 19 Sep 2006 10:08:43 -0400 Subject: USB: fix root-hub resume when CONFIG_USB_SUSPEND is not set This patch (as786) removes a redundant test and fixes a problem involving repeated system sleeps when CONFIG_USB_SUSPEND is not set. During the first wakeup, the root hub's dev.power.power_state.event field doesn't get updated, causing it not to be suspended during the second sleep transition. This takes care of the issue raised by Rafael J. Wysocki and Mattia Dongili. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2b2000a..0d063c8 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1071,10 +1071,15 @@ int usb_resume_both(struct usb_device *udev) PM_EVENT_ON) status = -EHOSTUNREACH; } - if (status == 0 && udev->state == USB_STATE_SUSPENDED) + if (status == 0) status = resume_device(udev); if (parent) mutex_unlock(&parent->pm_mutex); + } else { + + /* Needed only for setting udev->dev.power.power_state.event + * and for possible debugging message. */ + status = resume_device(udev); } /* Now the parent won't suspend until we are finished */ -- cgit v1.1 From 1b21d5e166e104f8914441ef52e2cd50ce65b479 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 28 Aug 2006 11:43:25 -0700 Subject: USB: fix __must_check warnings in drivers/usb/core/ Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0d063c8..b104632 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -49,6 +49,7 @@ static ssize_t store_new_id(struct device_driver *driver, u32 idVendor = 0; u32 idProduct = 0; int fields = 0; + int retval = 0; fields = sscanf(buf, "%x %x", &idVendor, &idProduct); if (fields < 2) @@ -68,10 +69,12 @@ static ssize_t store_new_id(struct device_driver *driver, spin_unlock(&usb_drv->dynids.lock); if (get_driver(driver)) { - driver_attach(driver); + retval = driver_attach(driver); put_driver(driver); } + if (retval) + return retval; return count; } static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); @@ -291,6 +294,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, { struct device *dev = &iface->dev; struct usb_device *udev = interface_to_usbdev(iface); + int retval = 0; if (dev->driver) return -EBUSY; @@ -308,9 +312,9 @@ int usb_driver_claim_interface(struct usb_driver *driver, * the future device_add() bind it, bypassing probe() */ if (device_is_registered(dev)) - device_bind_driver(dev); + retval = device_bind_driver(dev); - return 0; + return retval; } EXPORT_SYMBOL(usb_driver_claim_interface); -- cgit v1.1