From 4a2a8a2cce86b9d001378cc25acb5c61e6ca7d63 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:05:01 -0400 Subject: usbfs: private mutex for open, release, and remove The usbfs code doesn't provide sufficient mutual exclusion among open, release, and remove. Release vs. remove is okay because they both acquire the device lock, but open is not exclusive with either one. All three routines modify the udev->filelist linked list, so they must not run concurrently. Apparently someone gave this a minimum amount of thought in the past by explicitly acquiring the BKL at the start of the usbdev_open routine. Oddly enough, there's a comment pointing out that locking is unnecessary because chrdev_open already has acquired the BKL. But this ignores the point that the files in /proc/bus/usb/* are not char device files; they are regular files and so they don't get any special locking. Furthermore it's necessary to acquire the same lock in the release and remove routines, which the code does not do. Yet another problem arises because the same file_operations structure is accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file nodes. Even when one of them has been removed, it's still possible for userspace to open the other. So simple locking around the individual remove routines is insufficient; we need to lock the entire usb_notify_remove_device notifier chain. Rather than rely on the BKL, this patch (as723) introduces a new private mutex for the purpose. Holding the BKL while invoking a notifier chain doesn't seem like a good idea. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 49f69236b420..1217fbbe5829 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -59,6 +59,7 @@ static inline int is_active(struct usb_interface *f) extern const char *usbcore_name; /* usbfs stuff */ +extern struct mutex usbfs_mutex; extern struct usb_driver usbfs_driver; extern struct file_operations usbfs_devices_fops; extern struct file_operations usbfs_device_file_operations; -- cgit v1.2.3 From 140d8f687457c40a66af362838fac0d7893e7df5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:07:21 -0400 Subject: usbcore: rename usb_suspend_device to usb_port_suspend This revised patch (as715b) renames usb_suspend_device to usb_port_suspend, usb_resume_device to usb_port_resume, and finish_device_resume to finish_port_resume. There was no objection to the original version of the patch so this should be okay to apply. The revision was needed only because I have re-arranged the order of the earlier patches. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 38 +++++++++++++++++++++----------------- drivers/usb/core/usb.c | 7 ++----- drivers/usb/core/usb.h | 4 ++-- 3 files changed, 25 insertions(+), 24 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 26c8cb5f3e67..b00514d9a605 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1407,9 +1407,9 @@ int usb_new_device(struct usb_device *udev) * (Includes HNP test device.) */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - static int __usb_suspend_device(struct usb_device *, + static int __usb_port_suspend(struct usb_device *, int port1); - err = __usb_suspend_device(udev, udev->bus->otg_port); + err = __usb_port_suspend(udev, udev->bus->otg_port); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } @@ -1684,7 +1684,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, * the root hub for their bus goes into global suspend ... so we don't * (falsely) update the device power state to say it suspended. */ -static int __usb_suspend_device (struct usb_device *udev, int port1) +static int __usb_port_suspend (struct usb_device *udev, int port1) { int status = 0; @@ -1712,8 +1712,8 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) } } - /* we only change a device's upstream USB link. - * root hubs have no upstream USB link. + /* we change the device's upstream USB link, + * but root hubs have no upstream USB link. */ if (udev->parent) status = hub_port_suspend(hdev_to_hub(udev->parent), port1, @@ -1727,14 +1727,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) #endif /* - * usb_suspend_device - suspend a usb device + * usb_port_suspend - suspend a usb device's upstream port * @udev: device that's no longer in active use * Context: must be able to sleep; device not locked; pm locks held * * Suspends a USB device that isn't in active use, conserving power. * Devices may wake out of a suspend, if anything important happens, * using the remote wakeup mechanism. They may also be taken out of - * suspend by the host, using usb_resume_device(). It's also routine + * suspend by the host, using usb_port_resume(). It's also routine * to disconnect devices while they are suspended. * * This only affects the USB hardware for a device; its interfaces @@ -1746,12 +1746,12 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) * * Returns 0 on success, else negative errno. */ -int usb_suspend_device(struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev) { #ifdef CONFIG_USB_SUSPEND if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; - return __usb_suspend_device(udev, udev->portnum); + return __usb_port_suspend(udev, udev->portnum); #else /* NOTE: udev->state unchanged, it's not lying ... */ udev->dev.power.power_state = PMSG_SUSPEND; @@ -1767,7 +1767,7 @@ int usb_suspend_device(struct usb_device *udev) * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. */ -static int finish_device_resume(struct usb_device *udev) +static int finish_port_resume(struct usb_device *udev) { int status; u16 devstatus; @@ -1891,7 +1891,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) /* TRSMRCY = 10 msec */ msleep(10); if (udev) - status = finish_device_resume(udev); + status = finish_port_resume(udev); } } if (status < 0) @@ -1903,7 +1903,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) #endif /* - * usb_resume_device - re-activate a suspended usb device + * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate * Context: must be able to sleep; device not locked; pm locks held * @@ -1915,14 +1915,16 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) * * Returns 0 on success, else negative errno. */ -int usb_resume_device(struct usb_device *udev) +int usb_port_resume(struct usb_device *udev) { int status; if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; - /* selective resume of one downstream hub-to-device port */ + /* we change the device's upstream USB link, + * but root hubs have no upstream USB link. + */ if (udev->parent) { #ifdef CONFIG_USB_SUSPEND if (udev->state == USB_STATE_SUSPENDED) { @@ -1934,7 +1936,7 @@ int usb_resume_device(struct usb_device *udev) #endif status = 0; } else - status = finish_device_resume(udev); + status = finish_port_resume(udev); if (status < 0) dev_dbg(&udev->dev, "can't resume, status %d\n", status); @@ -1962,7 +1964,7 @@ static int remote_wakeup(struct usb_device *udev) dev_dbg(&udev->dev, "RESUME (wakeup)\n"); /* TRSMRCY = 10 msec */ msleep(10); - status = finish_device_resume(udev); + status = finish_port_resume(udev); } usb_unlock_device(udev); #endif @@ -2069,7 +2071,7 @@ static int hub_resume(struct usb_interface *intf) if (portstat & USB_PORT_STAT_SUSPEND) status = hub_port_resume(hub, port1, udev); else { - status = finish_device_resume(udev); + status = finish_port_resume(udev); if (status < 0) { dev_dbg(&intf->dev, "resume port %d --> %d\n", port1, status); @@ -3128,6 +3130,7 @@ re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } +EXPORT_SYMBOL(usb_reset_device); /** * usb_reset_composite_device - warn interface drivers and perform a USB port reset @@ -3206,3 +3209,4 @@ int usb_reset_composite_device(struct usb_device *udev, return ret; } +EXPORT_SYMBOL(usb_reset_composite_device); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ab766e0fe4ef..b28a31b20308 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1014,7 +1014,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) status = device_for_each_child(dev, NULL, verify_suspended); if (status) return status; - return usb_suspend_device (to_usb_device(dev)); + return usb_port_suspend(to_usb_device(dev)); } if ((dev->driver == NULL) || @@ -1061,7 +1061,7 @@ static int usb_generic_resume(struct device *dev) udev = to_usb_device(dev); if (udev->state == USB_STATE_NOTATTACHED) return 0; - return usb_resume_device (to_usb_device(dev)); + return usb_port_resume(udev); } if ((dev->driver == NULL) || @@ -1209,9 +1209,6 @@ EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_altnum_to_altsetting); -EXPORT_SYMBOL(usb_reset_device); -EXPORT_SYMBOL(usb_reset_composite_device); - EXPORT_SYMBOL(__usb_get_extra_descriptor); EXPORT_SYMBOL(usb_find_device); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1217fbbe5829..5a4eff5eec86 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -30,8 +30,8 @@ extern void usb_major_cleanup(void); extern int usb_host_init(void); extern void usb_host_cleanup(void); -extern int usb_suspend_device(struct usb_device *dev); -extern int usb_resume_device(struct usb_device *dev); +extern int usb_port_suspend(struct usb_device *dev); +extern int usb_port_resume(struct usb_device *dev); extern struct device_driver usb_generic_driver; extern int usb_generic_driver_data; -- cgit v1.2.3 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/Makefile | 2 +- drivers/usb/core/driver.c | 348 ++++++++++++++++++++++++++++++++++++++++----- drivers/usb/core/generic.c | 53 +++++++ drivers/usb/core/usb.c | 307 --------------------------------------- drivers/usb/core/usb.h | 2 +- 5 files changed, 370 insertions(+), 342 deletions(-) create mode 100644 drivers/usb/core/generic.c (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index ec510922af63..34e9bac319b4 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -4,7 +4,7 @@ usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ config.o file.o buffer.o sysfs.o endpoint.o \ - devio.o notify.o + devio.o notify.o generic.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ec8906501415..8dcf2cd0c569 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 +}; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c new file mode 100644 index 000000000000..7bab9769b34f --- /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; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b28a31b20308..0b8c67bcde60 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -112,87 +112,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, return NULL; } -/** - * 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; -} - -/** - * 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); -} - struct find_interface_arg { int minor; struct usb_interface *interface; @@ -234,120 +153,6 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) return argb.interface; } -#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_release_dev - free a usb device structure when all users of it are finished. * @dev: device that's been disconnected @@ -990,116 +795,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } -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; -} - -struct bus_type usb_bus_type = { - .name = "usb", - .match = usb_device_match, - .uevent = usb_uevent, - .suspend = usb_generic_suspend, - .resume = usb_generic_resume, -}; - /* format to disable USB on kernel command line is: nousb */ __module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444); @@ -1203,8 +898,6 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer); EXPORT_SYMBOL(usb_lock_device_for_reset); -EXPORT_SYMBOL(usb_driver_claim_interface); -EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_altnum_to_altsetting); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 5a4eff5eec86..82d397a6f773 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -33,9 +33,9 @@ extern void usb_host_cleanup(void); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +extern struct bus_type usb_bus_type; extern struct device_driver usb_generic_driver; extern int usb_generic_driver_data; -extern int usb_device_match(struct device *dev, struct device_driver *drv); /* Interfaces and their "power state" are owned by usbcore */ -- cgit v1.2.3 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 +++++++++++++++++++++++++++++++++------------ drivers/usb/core/generic.c | 17 +--- drivers/usb/core/usb.c | 15 ++-- drivers/usb/core/usb.h | 20 ++++- include/linux/usb.h | 58 ++++++++++-- 5 files changed, 245 insertions(+), 84 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8dcf2cd0c569..0d4b5dcee3ab 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; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7bab9769b34f..fa6f34a12b4b 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; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0b8c67bcde60..6dfbc284369b 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -123,7 +123,7 @@ static int __find_interface(struct device * dev, void * data) struct usb_interface *intf; /* can't look at usb devices, only interfaces */ - if (dev->driver == &usb_generic_driver) + if (is_usb_device(dev)) return 0; intf = to_usb_interface(dev); @@ -149,7 +149,8 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) argb.minor = minor; argb.interface = NULL; - driver_for_each_device(&drv->driver, NULL, &argb, __find_interface); + driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, + __find_interface); return argb.interface; } @@ -204,11 +205,13 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) device_initialize(&dev->dev); dev->dev.bus = &usb_bus_type; dev->dev.dma_mask = bus->controller->dma_mask; - dev->dev.driver_data = &usb_generic_driver_data; - dev->dev.driver = &usb_generic_driver; + dev->dev.driver = &usb_generic_driver.drvwrap.driver; dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; + /* This magic assignment distinguishes devices from interfaces */ + dev->dev.platform_data = &usb_generic_driver; + INIT_LIST_HEAD(&dev->ep0.urb_list); dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; @@ -838,7 +841,7 @@ static int __init usb_init(void) retval = usb_hub_init(); if (retval) goto hub_init_failed; - retval = driver_register(&usb_generic_driver); + retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE); if (!retval) goto out; @@ -868,7 +871,7 @@ static void __exit usb_exit(void) if (nousb) return; - driver_unregister(&usb_generic_driver); + usb_deregister_device_driver(&usb_generic_driver); usb_major_cleanup(); usbfs_cleanup(); usb_deregister(&usbfs_driver); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 82d397a6f773..1d25ccac7832 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -34,8 +34,24 @@ extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); extern struct bus_type usb_bus_type; -extern struct device_driver usb_generic_driver; -extern int usb_generic_driver_data; +extern struct usb_device_driver usb_generic_driver; + +/* Here's how we tell apart devices and interfaces. Luckily there's + * no such thing as a platform USB device, so we can steal the use + * of the platform_data field. */ + +static inline int is_usb_device(struct device *dev) +{ + return dev->platform_data == &usb_generic_driver; +} + +/* Do the same for device drivers and interface drivers. */ + +static inline int is_usb_device_driver(struct device_driver *drv) +{ + return container_of(drv, struct usbdrv_wrap, driver)-> + for_devices; +} /* Interfaces and their "power state" are owned by usbcore */ diff --git a/include/linux/usb.h b/include/linux/usb.h index d2bd0c8e0154..b4ccce6d0982 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -540,7 +540,17 @@ struct usb_dynids { }; /** - * struct usb_driver - identifies USB driver to usbcore + * struct usbdrv_wrap - wrapper for driver-model structure + * @driver: The driver-model core driver structure. + * @for_devices: Non-zero for device drivers, 0 for interface drivers. + */ +struct usbdrv_wrap { + struct device_driver driver; + int for_devices; +}; + +/** + * struct usb_driver - identifies USB interface driver to usbcore * @name: The driver name should be unique among USB drivers, * and should normally be the same as the module name. * @probe: Called to see if the driver is willing to manage a particular @@ -567,12 +577,12 @@ struct usb_dynids { * or your driver's probe function will never get called. * @dynids: used internally to hold the list of dynamically added device * ids for this driver. - * @driver: the driver model core driver structure. + * @drvwrap: Driver-model core structure wrapper. * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be * added to this driver by preventing the sysfs file from being created. * - * USB drivers must provide a name, probe() and disconnect() methods, - * and an id_table. Other driver fields are optional. + * USB interface drivers must provide a name, probe() and disconnect() + * methods, and an id_table. Other driver fields are optional. * * The id_table is used in hotplugging. It holds a set of descriptors, * and specialized data may be associated with each entry. That table @@ -606,10 +616,40 @@ struct usb_driver { const struct usb_device_id *id_table; struct usb_dynids dynids; - struct device_driver driver; + struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; }; -#define to_usb_driver(d) container_of(d, struct usb_driver, driver) +#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) + +/** + * struct usb_device_driver - identifies USB device driver to usbcore + * @name: The driver name should be unique among USB drivers, + * and should normally be the same as the module name. + * @probe: Called to see if the driver is willing to manage a particular + * device. If it is, probe returns zero and uses dev_set_drvdata() + * to associate driver-specific data with the device. If unwilling + * to manage the device, return a negative errno value. + * @disconnect: Called when the device is no longer accessible, usually + * because it has been (or is being) disconnected or the driver's + * module is being unloaded. + * @suspend: Called when the device is going to be suspended by the system. + * @resume: Called when the device is being resumed by the system. + * @drvwrap: Driver-model core structure wrapper. + * + * USB drivers must provide all the fields listed above except drvwrap. + */ +struct usb_device_driver { + const char *name; + + int (*probe) (struct usb_device *udev); + void (*disconnect) (struct usb_device *udev); + + int (*suspend) (struct usb_device *udev, pm_message_t message); + int (*resume) (struct usb_device *udev); + struct usbdrv_wrap drvwrap; +}; +#define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ + drvwrap.driver) extern struct bus_type usb_bus_type; @@ -633,13 +673,17 @@ struct usb_class_driver { * use these in module_init()/module_exit() * and don't forget MODULE_DEVICE_TABLE(usb, ...) */ -int usb_register_driver(struct usb_driver *, struct module *); +extern int usb_register_driver(struct usb_driver *, struct module *); static inline int usb_register(struct usb_driver *driver) { return usb_register_driver(driver, THIS_MODULE); } extern void usb_deregister(struct usb_driver *); +extern int usb_register_device_driver(struct usb_device_driver *, + struct module *); +extern void usb_deregister_device_driver(struct usb_device_driver *); + extern int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver); extern void usb_deregister_dev(struct usb_interface *intf, -- cgit v1.2.3 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 ++++++++++++++++++++++++++---- drivers/usb/core/generic.c | 14 ------- drivers/usb/core/hub.c | 96 +++++++--------------------------------------- drivers/usb/core/usb.h | 2 + 4 files changed, 70 insertions(+), 105 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b0db1583c522..eefc98584eac 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; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 1522195de715..b6dacd7551d2 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). diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a372332440b2..a39112041e69 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1662,9 +1662,6 @@ static int finish_port_resume(struct usb_device *udev) "gone after usb resume? status %d\n", status); else if (udev->actconfig) { - unsigned i; - int (*resume)(struct device *); - le16_to_cpus(&devstatus); if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) && udev->parent) { @@ -1675,24 +1672,9 @@ static int finish_port_resume(struct usb_device *udev) USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (status) { + if (status) dev_dbg(&udev->dev, "disable remote " "wakeup, status %d\n", status); - status = 0; - } - } - - /* resume interface drivers; if this is a hub, it - * may have a child resume event to deal with soon - */ - resume = udev->dev.bus->resume; - for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { - struct device *dev = - &udev->actconfig->interface[i]->dev; - - down(&dev->sem); - (void) resume(dev); - up(&dev->sem); } status = 0; @@ -1802,15 +1784,7 @@ int usb_port_resume(struct usb_device *udev) } else status = finish_port_resume(udev); if (status < 0) - dev_dbg(&udev->dev, "can't resume, status %d\n", - status); - - /* rebind drivers that had no suspend() */ - if (status == 0) { - usb_unlock_device(udev); - bus_rescan_devices(&usb_bus_type); - usb_lock_device(udev); - } + dev_dbg(&udev->dev, "can't resume, status %d\n", status); return status; } @@ -1830,6 +1804,9 @@ static int remote_wakeup(struct usb_device *udev) msleep(10); status = finish_port_resume(udev); } + + if (status == 0) + usb_resume_both(udev); usb_unlock_device(udev); #endif return status; @@ -1901,51 +1878,8 @@ static int hub_resume(struct usb_interface *intf) } } + /* tell khubd to look for changes on this hub */ hub_activate(hub); - - /* REVISIT: this recursion probably shouldn't exist. Remove - * this code sometime, after retesting with different root and - * external hubs. - */ -#ifdef CONFIG_USB_SUSPEND - { - unsigned port1; - - for (port1 = 1; port1 <= hdev->maxchild; port1++) { - struct usb_device *udev; - u16 portstat, portchange; - - udev = hdev->children [port1-1]; - status = hub_port_status(hub, port1, &portstat, &portchange); - if (status == 0) { - if (portchange & USB_PORT_STAT_C_SUSPEND) { - clear_port_feature(hdev, port1, - USB_PORT_FEAT_C_SUSPEND); - portchange &= ~USB_PORT_STAT_C_SUSPEND; - } - - /* let khubd handle disconnects etc */ - if (portchange) - continue; - } - - if (!udev || status < 0) - continue; - usb_lock_device(udev); - if (portstat & USB_PORT_STAT_SUSPEND) - status = hub_port_resume(hub, port1, udev); - else { - status = finish_port_resume(udev); - if (status < 0) { - dev_dbg(&intf->dev, "resume port %d --> %d\n", - port1, status); - hub_port_logical_disconnect(hub, port1); - } - } - usb_unlock_device(udev); - } - } -#endif return 0; } @@ -2602,17 +2536,6 @@ static void hub_events(void) usb_get_intf(intf); spin_unlock_irq(&hub_event_lock); - /* Is this is a root hub wanting to reactivate the downstream - * ports? If so, be sure the interface resumes even if its - * stub "device" node was never suspended. - */ - if (i) { - dpm_runtime_resume(&hdev->dev); - dpm_runtime_resume(&intf->dev); - usb_put_intf(intf); - continue; - } - /* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */ if (locktree(hdev) < 0) { @@ -2629,6 +2552,13 @@ static void hub_events(void) goto loop; } + /* Is this is a root hub wanting to reactivate the downstream + * ports? If so, be sure the interface resumes even if its + * stub "device" node was never suspended. + */ + if (i) + usb_resume_both(hdev); + /* If this is an inactive or suspended hub, do nothing */ if (hub->quiescing) goto loop; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1d25ccac7832..cc42972b6bb0 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -30,6 +30,8 @@ extern void usb_major_cleanup(void); extern int usb_host_init(void); extern void usb_host_cleanup(void); +extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); +extern int usb_resume_both(struct usb_device *udev); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); -- cgit v1.2.3 From 4d064c080265a41324d108fccc26b72106d43db3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:11:44 -0400 Subject: usbcore: track whether interfaces are suspended Currently we rely on intf->dev.power.power_state.event for tracking whether intf is suspended. This is not a reliable technique because that value is owned by the PM core, not by usbcore. This patch (as718b) adds a new flag so that we can accurately tell which interfaces are suspended and which aren't. At first one might think these flags aren't needed, since interfaces will be suspended along with their devices. It turns out there are a couple of intermediate situations where that's not quite true, such as while processing a remote-wakeup request. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.h | 6 +++--- include/linux/usb.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index cc42972b6bb0..74df0db954c9 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -59,17 +59,17 @@ static inline int is_usb_device_driver(struct device_driver *drv) static inline void mark_active(struct usb_interface *f) { - f->dev.power.power_state.event = PM_EVENT_ON; + f->is_active = 1; } static inline void mark_quiesced(struct usb_interface *f) { - f->dev.power.power_state.event = PM_EVENT_FREEZE; + f->is_active = 0; } static inline int is_active(struct usb_interface *f) { - return f->dev.power.power_state.event == PM_EVENT_ON; + return f->is_active; } diff --git a/include/linux/usb.h b/include/linux/usb.h index b4ccce6d0982..e22f4b386605 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -102,6 +102,7 @@ enum usb_interface_condition { * number from the USB core by calling usb_register_dev(). * @condition: binding state of the interface: not bound, binding * (in probe()), bound to a driver, or unbinding (in disconnect()) + * @is_active: flag set when the interface is bound and not suspended. * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. * @@ -142,6 +143,8 @@ struct usb_interface { int minor; /* minor number this interface is * bound to */ enum usb_interface_condition condition; /* state of binding */ + unsigned is_active:1; /* the interface is not suspended */ + struct device dev; /* interface specific device info */ struct class_device *class_dev; }; -- cgit v1.2.3 From d388dab7b562b76525761f89702246686747ba30 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:14:24 -0400 Subject: hub driver: improve use of #ifdef This patch (as736) makes the hub driver more readable by improving the usage of "#ifdef CONFIG_PM" and "#ifdef CONFIG_USB_SUSPEND". Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 64 ++++++++++++++++++++++++++++++++++++-------------- drivers/usb/core/usb.h | 11 +++++++++ 2 files changed, 57 insertions(+), 18 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a310c7cede99..64e80b964b87 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1041,7 +1041,7 @@ void usb_set_device_state(struct usb_device *udev, } -#ifdef CONFIG_PM +#ifdef CONFIG_PM /** * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power @@ -1072,7 +1072,7 @@ void usb_root_hub_lost_power(struct usb_device *rhdev) } EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); -#endif +#endif /* CONFIG_PM */ static void choose_address(struct usb_device *udev) { @@ -1471,6 +1471,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) kick_khubd(hub); } +#ifdef CONFIG_PM #ifdef CONFIG_USB_SUSPEND @@ -1569,8 +1570,6 @@ static int __usb_port_suspend (struct usb_device *udev, int port1) return status; } -#endif - /* * usb_port_suspend - suspend a usb device's upstream port * @udev: device that's no longer in active use @@ -1593,11 +1592,7 @@ static int __usb_port_suspend (struct usb_device *udev, int port1) */ int usb_port_suspend(struct usb_device *udev) { -#ifdef CONFIG_USB_SUSPEND return __usb_port_suspend(udev, udev->portnum); -#else - return 0; -#endif } /* @@ -1661,8 +1656,6 @@ static int finish_port_resume(struct usb_device *udev) return status; } -#ifdef CONFIG_USB_SUSPEND - static int hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) { @@ -1722,8 +1715,6 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) return status; } -#endif - /* * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate @@ -1739,17 +1730,15 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) */ int usb_port_resume(struct usb_device *udev) { - int status = 0; + int status; /* we change the device's upstream USB link, * but root hubs have no upstream USB link. */ if (udev->parent) { -#ifdef CONFIG_USB_SUSPEND // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), udev->portnum, udev); -#endif } else status = finish_port_resume(udev); if (status < 0) @@ -1761,8 +1750,6 @@ static int remote_wakeup(struct usb_device *udev) { int status = 0; -#ifdef CONFIG_USB_SUSPEND - /* don't repeat RESUME sequence if this device * was already woken up by some other task */ @@ -1777,10 +1764,42 @@ static int remote_wakeup(struct usb_device *udev) if (status == 0) usb_resume_both(udev); usb_unlock_device(udev); -#endif return status; } +#else /* CONFIG_USB_SUSPEND */ + +/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */ + +int usb_port_suspend(struct usb_device *udev) +{ + return 0; +} + +static inline int +finish_port_resume(struct usb_device *udev) +{ + return 0; +} + +static inline int +hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) +{ + return 0; +} + +int usb_port_resume(struct usb_device *udev) +{ + return 0; +} + +static inline int remote_wakeup(struct usb_device *udev) +{ + return 0; +} + +#endif + static int hub_suspend(struct usb_interface *intf, pm_message_t msg) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -1854,6 +1873,15 @@ static int hub_resume(struct usb_interface *intf) return 0; } +#else /* CONFIG_PM */ + +static inline int remote_wakeup(struct usb_device *udev) +{ + return 0; +} + +#endif + void usb_suspend_root_hub(struct usb_device *hdev) { struct usb_hub *hub = hdev_to_hub(hdev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 74df0db954c9..98675fb1bc4c 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -30,11 +30,22 @@ extern void usb_major_cleanup(void); extern int usb_host_init(void); extern void usb_host_cleanup(void); +#ifdef CONFIG_PM + extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); extern int usb_resume_both(struct usb_device *udev); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +#else + +#define usb_suspend_both(udev, msg) 0 +#define usb_resume_both(udev) 0 +#define usb_port_suspend(dev) 0 +#define usb_port_resume(dev) 0 + +#endif + extern struct bus_type usb_bus_type; extern struct usb_device_driver usb_generic_driver; -- cgit v1.2.3 From 066202dd48cf3296b6cc22b5fcf89aef33fa0efc Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Sat, 5 Aug 2006 20:37:11 -0300 Subject: USB: Make file operations structs in drivers/usb const. Making structs const prevents accidental bugs and with the proper debug options they're protected against corruption. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 2 +- drivers/usb/core/devices.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/file.c | 2 +- drivers/usb/core/inode.c | 6 +++--- drivers/usb/core/usb.h | 4 ++-- drivers/usb/gadget/at91_udc.c | 2 +- drivers/usb/gadget/inode.c | 8 ++++---- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/host/isp116x-hcd.c | 2 +- drivers/usb/host/sl811-hcd.c | 2 +- drivers/usb/host/uhci-debug.c | 4 ++-- drivers/usb/image/mdc800.c | 4 ++-- drivers/usb/input/hiddev.c | 2 +- drivers/usb/misc/auerswald.c | 2 +- drivers/usb/misc/idmouse.c | 2 +- drivers/usb/misc/ldusb.c | 2 +- drivers/usb/misc/legousbtower.c | 2 +- drivers/usb/misc/sisusbvga/sisusb.c | 2 +- drivers/usb/misc/usblcd.c | 2 +- drivers/usb/mon/mon_stat.c | 2 +- drivers/usb/mon/mon_text.c | 2 +- drivers/usb/mon/usb_mon.h | 4 ++-- drivers/usb/usb-skeleton.c | 2 +- 24 files changed, 33 insertions(+), 33 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 48dee4b8d8e5..292919d260a0 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -813,7 +813,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product) return 0; } -static struct file_operations usblp_fops = { +static const struct file_operations usblp_fops = { .owner = THIS_MODULE, .read = usblp_read, .write = usblp_write, diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index c0f37343a276..241d37326ea9 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) return ret; } -struct file_operations usbfs_devices_fops = { +const struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e84f19d4089c..43c08724a2d8 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1572,7 +1572,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -struct file_operations usbfs_device_file_operations = { +const struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 8de4f8c99d61..c376c655c5de 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -55,7 +55,7 @@ static int usb_open(struct inode * inode, struct file * file) return err; } -static struct file_operations usb_fops = { +static const struct file_operations usb_fops = { .owner = THIS_MODULE, .open = usb_open, }; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 58b4b1012120..df3d152f0493 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -44,7 +44,7 @@ #include "hcd.h" static struct super_operations usbfs_ops; -static struct file_operations default_file_operations; +static const struct file_operations default_file_operations; static struct vfsmount *usbfs_mount; static int usbfs_mount_count; /* = 0 */ static int ignore_mount = 0; @@ -407,7 +407,7 @@ static int default_open (struct inode *inode, struct file *file) return 0; } -static struct file_operations default_file_operations = { +static const struct file_operations default_file_operations = { .read = default_read_file, .write = default_write_file, .open = default_open, @@ -494,7 +494,7 @@ static int fs_create_by_name (const char *name, mode_t mode, static struct dentry *fs_create_file (const char *name, mode_t mode, struct dentry *parent, void *data, - struct file_operations *fops, + const struct file_operations *fops, uid_t uid, gid_t gid) { struct dentry *dentry; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 98675fb1bc4c..6096ead2758c 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -90,8 +90,8 @@ extern const char *usbcore_name; /* usbfs stuff */ extern struct mutex usbfs_mutex; extern struct usb_driver usbfs_driver; -extern struct file_operations usbfs_devices_fops; -extern struct file_operations usbfs_device_file_operations; +extern const struct file_operations usbfs_devices_fops; +extern const struct file_operations usbfs_device_file_operations; extern void usbfs_conn_disc_event(void); extern int usbdev_init(void); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index cfebca05ead5..d00958a01cfb 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -247,7 +247,7 @@ static int proc_udc_open(struct inode *inode, struct file *file) return single_open(file, proc_udc_show, PDE(inode)->data); } -static struct file_operations proc_ops = { +static const struct file_operations proc_ops = { .open = proc_udc_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index ab508858f341..1072e987ff21 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -714,7 +714,7 @@ ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o) /*----------------------------------------------------------------------*/ /* used after endpoint configuration */ -static struct file_operations ep_io_operations = { +static const struct file_operations ep_io_operations = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -873,7 +873,7 @@ ep_open (struct inode *inode, struct file *fd) } /* used before endpoint configuration */ -static struct file_operations ep_config_operations = { +static const struct file_operations ep_config_operations = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -1247,7 +1247,7 @@ static int dev_ioctl (struct inode *inode, struct file *fd, } /* used after device configuration */ -static struct file_operations ep0_io_operations = { +static const struct file_operations ep0_io_operations = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -1929,7 +1929,7 @@ dev_open (struct inode *inode, struct file *fd) return value; } -static struct file_operations dev_init_operations = { +static const struct file_operations dev_init_operations = { .owner = THIS_MODULE, .llseek = no_llseek, diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 81f0389fcc94..0a64504c2545 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2437,7 +2437,7 @@ static int proc_udc_open(struct inode *inode, struct file *file) return single_open(file, proc_udc_show, NULL); } -static struct file_operations proc_ops = { +static const struct file_operations proc_ops = { .open = proc_udc_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 8c6b38a0b5bb..a72e041df8e7 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1207,7 +1207,7 @@ static int isp116x_open_seq(struct inode *inode, struct file *file) return single_open(file, isp116x_show_dbg, inode->i_private); } -static struct file_operations isp116x_debug_fops = { +static const struct file_operations isp116x_debug_fops = { .open = isp116x_open_seq, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 9de115d9db27..8c17da37600b 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1517,7 +1517,7 @@ static int proc_sl811h_open(struct inode *inode, struct file *file) return single_open(file, proc_sl811h_show, PDE(inode)->data); } -static struct file_operations proc_ops = { +static const struct file_operations proc_ops = { .open = proc_sl811h_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index d1372cb27f33..e345f15b7d87 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -16,7 +16,7 @@ #include "uhci-hcd.h" -#define uhci_debug_operations (* (struct file_operations *) NULL) +#define uhci_debug_operations (* (const struct file_operations *) NULL) static struct dentry *uhci_debugfs_root; #ifdef DEBUG @@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file) } #undef uhci_debug_operations -static struct file_operations uhci_debug_operations = { +static const struct file_operations uhci_debug_operations = { .owner = THIS_MODULE, .open = uhci_debug_open, .llseek = uhci_debug_lseek, diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 08daf400f985..ca6305c1d64c 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -424,7 +424,7 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res) ***************************************************************************/ static struct usb_driver mdc800_usb_driver; -static struct file_operations mdc800_device_ops; +static const struct file_operations mdc800_device_ops; static struct usb_class_driver mdc800_class = { .name = "mdc800%d", .fops = &mdc800_device_ops, @@ -941,7 +941,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s ****************************************************************************/ /* File Operations of this drivers */ -static struct file_operations mdc800_device_ops = +static const struct file_operations mdc800_device_ops = { .owner = THIS_MODULE, .read = mdc800_device_read, diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index f6b839c257a7..a2b419d13740 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -722,7 +722,7 @@ inval: return -EINVAL; } -static struct file_operations hiddev_fops = { +static const struct file_operations hiddev_fops = { .owner = THIS_MODULE, .read = hiddev_read, .write = hiddev_write, diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 1fef36e71c57..4a329d8488b7 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -1858,7 +1858,7 @@ static int auerchar_release (struct inode *inode, struct file *file) /*----------------------------------------------------------------------*/ /* File operation structure */ -static struct file_operations auerswald_fops = +static const struct file_operations auerswald_fops = { .owner = THIS_MODULE, .llseek = no_llseek, diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index fcd69c52aea9..8e6e195a22ba 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -98,7 +98,7 @@ static int idmouse_probe(struct usb_interface *interface, static void idmouse_disconnect(struct usb_interface *interface); /* file operation pointers */ -static struct file_operations idmouse_fops = { +static const struct file_operations idmouse_fops = { .owner = THIS_MODULE, .read = idmouse_read, .open = idmouse_open, diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index f30ab1fbb3c8..741736b6c134 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -589,7 +589,7 @@ exit: } /* file operations needed when we register this driver */ -static struct file_operations ld_usb_fops = { +static const struct file_operations ld_usb_fops = { .owner = THIS_MODULE, .read = ld_usb_read, .write = ld_usb_write, diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 7699d970e680..77c36e63c7bf 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -259,7 +259,7 @@ static void tower_disconnect (struct usb_interface *interface); static DEFINE_MUTEX (disconnect_mutex); /* file operations needed when we register this driver */ -static struct file_operations tower_fops = { +static const struct file_operations tower_fops = { .owner = THIS_MODULE, .read = tower_read, .write = tower_write, diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index e16582f3733c..a44124c7e851 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3179,7 +3179,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) } #endif -static struct file_operations usb_sisusb_fops = { +static const struct file_operations usb_sisusb_fops = { .owner = THIS_MODULE, .open = sisusb_open, .release = sisusb_release, diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index e095772dd8e9..53e59e362330 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -239,7 +239,7 @@ error: return retval; } -static struct file_operations lcd_fops = { +static const struct file_operations lcd_fops = { .owner = THIS_MODULE, .read = lcd_read, .write = lcd_write, diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index 86ad2b381c4b..f6d1491256c4 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -62,7 +62,7 @@ static int mon_stat_release(struct inode *inode, struct file *file) return 0; } -struct file_operations mon_fops_stat = { +const struct file_operations mon_fops_stat = { .owner = THIS_MODULE, .open = mon_stat_open, .llseek = no_llseek, diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 2fd39b4fa166..775e374a960c 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -435,7 +435,7 @@ static int mon_text_release(struct inode *inode, struct file *file) return 0; } -struct file_operations mon_fops_text = { +const struct file_operations mon_fops_text = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index 33678c24ebee..50efc8ee7c21 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -53,7 +53,7 @@ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); extern struct mutex mon_lock; -extern struct file_operations mon_fops_text; -extern struct file_operations mon_fops_stat; +extern const struct file_operations mon_fops_text; +extern const struct file_operations mon_fops_stat; #endif /* __USB_MON_H */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 33f0e81c58d3..3339373239ad 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -243,7 +243,7 @@ exit: return retval; } -static struct file_operations skel_fops = { +static const struct file_operations skel_fops = { .owner = THIS_MODULE, .read = skel_read, .write = skel_write, -- cgit v1.2.3 From 095bc335360a51623dd8571839bbf465851a7f4b Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Sat, 26 Aug 2006 23:48:11 -0300 Subject: USB core: Use const where possible. This patch marks some USB core's functions parameters as const. This improves the design (we're saying to the caller that its parameter is not going to be modified) and may help in compiler's optimisation work. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 21 +++++++++++---------- drivers/usb/core/usb.h | 4 ++-- include/linux/usb.h | 18 +++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 82837d45b484..4eb98eb3804f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -67,7 +67,8 @@ static int nousb; /* Disable USB when built into kernel image */ * Don't call this function unless you are bound to one of the interfaces * on this device or you have locked the device! */ -struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) +struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, + unsigned ifnum) { struct usb_host_config *config = dev->actconfig; int i; @@ -100,8 +101,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) * Don't call this function unless you are bound to the intf interface * or you have locked the device! */ -struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, - unsigned int altnum) +struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, + unsigned int altnum) { int i; @@ -356,7 +357,7 @@ void usb_put_intf(struct usb_interface *intf) * case the driver already owns the device lock.) */ int usb_lock_device_for_reset(struct usb_device *udev, - struct usb_interface *iface) + const struct usb_interface *iface) { unsigned long jiffies_expire = jiffies + HZ; @@ -852,8 +853,8 @@ void usb_buffer_unmap (struct urb *urb) * * Reverse the effect of this call with usb_buffer_unmap_sg(). */ -int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int nents) +int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int nents) { struct usb_bus *bus; struct device *controller; @@ -887,8 +888,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, * Use this when you are re-using a scatterlist's data buffers for * another USB request. */ -void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int n_hw_ents) +void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents) { struct usb_bus *bus; struct device *controller; @@ -913,8 +914,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, * * Reverses the effect of usb_buffer_map_sg(). */ -void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int n_hw_ents) +void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents) { struct usb_bus *bus; struct device *controller; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 6096ead2758c..67da6d0b316f 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -53,7 +53,7 @@ extern struct usb_device_driver usb_generic_driver; * no such thing as a platform USB device, so we can steal the use * of the platform_data field. */ -static inline int is_usb_device(struct device *dev) +static inline int is_usb_device(const struct device *dev) { return dev->platform_data == &usb_generic_driver; } @@ -78,7 +78,7 @@ static inline void mark_quiesced(struct usb_interface *f) f->is_active = 0; } -static inline int is_active(struct usb_interface *f) +static inline int is_active(const struct usb_interface *f) { return f->is_active; } diff --git a/include/linux/usb.h b/include/linux/usb.h index f807479ef65b..26d8a5f36896 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -387,7 +387,7 @@ extern void usb_put_dev(struct usb_device *dev); #define usb_unlock_device(udev) up(&(udev)->dev.sem) #define usb_trylock_device(udev) down_trylock(&(udev)->dev.sem) extern int usb_lock_device_for_reset(struct usb_device *udev, - struct usb_interface *iface); + const struct usb_interface *iface); /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); @@ -426,10 +426,10 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface, extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor); -extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, +extern struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, unsigned ifnum); extern struct usb_host_interface *usb_altnum_to_altsetting( - struct usb_interface *intf, unsigned int altnum); + const struct usb_interface *intf, unsigned int altnum); /** @@ -1064,14 +1064,14 @@ void usb_buffer_unmap (struct urb *urb); #endif struct scatterlist; -int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int nents); +int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int nents); #if 0 -void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int n_hw_ents); +void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents); #endif -void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, - struct scatterlist *sg, int n_hw_ents); +void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents); /*-------------------------------------------------------------------* * SYNCHRONOUS CALL SUPPORT * -- cgit v1.2.3 From 42d8a2d22bbaed80f1ade65a08e4e1097a111d4b Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sun, 20 Aug 2006 18:58:42 +0400 Subject: USB: Turn usb_resume_both() into static inline drivers/usb/core/hub.c: In function `hub_events': drivers/usb/core/hub.c:2591: warning: statement with no effect Signed-off-by: Alexey Dobriyan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 67da6d0b316f..5162cb370215 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -40,7 +40,10 @@ extern int usb_port_resume(struct usb_device *dev); #else #define usb_suspend_both(udev, msg) 0 -#define usb_resume_both(udev) 0 +static inline int usb_resume_both(struct usb_device *udev) +{ + return 0; +} #define usb_port_suspend(dev) 0 #define usb_port_resume(dev) 0 -- cgit v1.2.3 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 ++++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/hub.c | 49 ++++-- drivers/usb/core/usb.c | 23 +++ drivers/usb/core/usb.h | 14 ++ include/linux/usb.h | 33 ++++ 5 files changed, 493 insertions(+), 45 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index a5d11461f5a9..2b2000ac05ab 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 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 78e910b2046c..dee812bc6c43 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1017,19 +1017,22 @@ void usb_set_device_state(struct usb_device *udev, if (udev->state == USB_STATE_NOTATTACHED) ; /* do nothing */ else if (new_state != USB_STATE_NOTATTACHED) { - udev->state = new_state; /* root hub wakeup capabilities are managed out-of-band * and may involve silicon errata ... ignore them here. */ if (udev->parent) { - if (new_state == USB_STATE_CONFIGURED) + if (udev->state == USB_STATE_SUSPENDED + || new_state == USB_STATE_SUSPENDED) + ; /* No change to wakeup settings */ + else if (new_state == USB_STATE_CONFIGURED) device_init_wakeup(&udev->dev, (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP)); - else if (new_state != USB_STATE_SUSPENDED) + else device_init_wakeup(&udev->dev, 0); } + udev->state = new_state; } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); @@ -1507,7 +1510,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, * NOTE: OTG devices may issue remote wakeup (or SRP) even when * we don't explicitly enable it here. */ - if (device_may_wakeup(&udev->dev)) { + if (udev->do_remote_wakeup) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, @@ -1533,7 +1536,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, USB_CTRL_SET_TIMEOUT); } else { /* device has up to 10 msec to fully suspend */ - dev_dbg(&udev->dev, "usb suspend\n"); + dev_dbg(&udev->dev, "usb %ssuspend\n", + udev->auto_pm ? "auto-" : ""); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } @@ -1573,7 +1577,8 @@ static int __usb_port_suspend (struct usb_device *udev, int port1) status = hub_port_suspend(hdev_to_hub(udev->parent), port1, udev); else { - dev_dbg(&udev->dev, "usb suspend\n"); + dev_dbg(&udev->dev, "usb %ssuspend\n", + udev->auto_pm ? "auto-" : ""); usb_set_device_state(udev, USB_STATE_SUSPENDED); } return status; @@ -1687,7 +1692,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) /* drive resume for at least 20 msec */ if (udev) - dev_dbg(&udev->dev, "RESUME\n"); + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); msleep(25); #define LIVE_FLAGS ( USB_PORT_STAT_POWER \ @@ -1754,8 +1760,11 @@ int usb_port_resume(struct usb_device *udev) // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), udev->portnum, udev); - } else + } else { + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); status = finish_port_resume(udev); + } if (status < 0) dev_dbg(&udev->dev, "can't resume, status %d\n", status); return status; @@ -1765,19 +1774,23 @@ static int remote_wakeup(struct usb_device *udev) { int status = 0; - /* don't repeat RESUME sequence if this device - * was already woken up by some other task - */ + /* All this just to avoid sending a port-resume message + * to the parent hub! */ + usb_lock_device(udev); + mutex_lock_nested(&udev->pm_mutex, udev->level); if (udev->state == USB_STATE_SUSPENDED) { - dev_dbg(&udev->dev, "RESUME (wakeup)\n"); + dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); /* TRSMRCY = 10 msec */ msleep(10); status = finish_port_resume(udev); + if (status == 0) + udev->dev.power.power_state.event = PM_EVENT_ON; } + mutex_unlock(&udev->pm_mutex); if (status == 0) - usb_resume_both(udev); + usb_autoresume_device(udev, 0); usb_unlock_device(udev); return status; } @@ -1834,7 +1847,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) == PM_EVENT_ON #endif ) { - dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); + if (!hdev->auto_pm) + dev_dbg(&intf->dev, "port %d nyet suspended\n", + port1); return -EBUSY; } } @@ -2587,7 +2602,7 @@ static void hub_events(void) * stub "device" node was never suspended. */ if (i) - usb_resume_both(hdev); + usb_autoresume_device(hdev, 0); /* If this is an inactive or suspended hub, do nothing */ if (hub->quiescing) @@ -2993,6 +3008,9 @@ int usb_reset_composite_device(struct usb_device *udev, return -EINVAL; } + /* Prevent autosuspend during the reset */ + usb_autoresume_device(udev, 1); + if (iface && iface->condition != USB_INTERFACE_BINDING) iface = NULL; @@ -3034,6 +3052,7 @@ int usb_reset_composite_device(struct usb_device *udev, } } + usb_autosuspend_device(udev, 1); return ret; } EXPORT_SYMBOL(usb_reset_composite_device); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b0c0a993338f..6b029cdb8671 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -168,6 +168,10 @@ static void usb_release_dev(struct device *dev) udev = to_usb_device(dev); +#ifdef CONFIG_PM + cancel_delayed_work(&udev->autosuspend); + flush_scheduled_work(); +#endif usb_destroy_configuration(udev); usb_put_hcd(bus_to_hcd(udev->bus)); kfree(udev->product); @@ -176,6 +180,21 @@ static void usb_release_dev(struct device *dev) kfree(udev); } +#ifdef CONFIG_PM + +/* usb_autosuspend_work - callback routine to autosuspend a USB device */ +static void usb_autosuspend_work(void *_udev) +{ + struct usb_device *udev = _udev; + + mutex_lock_nested(&udev->pm_mutex, udev->level); + udev->auto_pm = 1; + usb_suspend_both(udev, PMSG_SUSPEND); + mutex_unlock(&udev->pm_mutex); +} + +#endif + /** * usb_alloc_dev - usb device constructor (usbcore-internal) * @parent: hub to which device is connected; null to allocate a root hub @@ -251,6 +270,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); +#ifdef CONFIG_PM + mutex_init(&dev->pm_mutex); + INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev); +#endif return dev; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 5162cb370215..10688ad73c6d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -49,6 +49,20 @@ static inline int usb_resume_both(struct usb_device *udev) #endif +#ifdef CONFIG_USB_SUSPEND + +#define USB_AUTOSUSPEND_DELAY (HZ*2) + +extern void usb_autosuspend_device(struct usb_device *udev, int dec_busy_cnt); +extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt); + +#else + +#define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0) +#define usb_autoresume_device(udev, inc_busy_cnt) 0 + +#endif + extern struct bus_type usb_bus_type; extern struct usb_device_driver usb_generic_driver; diff --git a/include/linux/usb.h b/include/linux/usb.h index df5c93eb3ce9..0da15b0b02be 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -19,6 +19,7 @@ #include /* for struct file_operations */ #include /* for struct completion */ #include /* for current && schedule_timeout */ +#include /* for struct mutex */ struct usb_device; struct usb_driver; @@ -103,8 +104,12 @@ enum usb_interface_condition { * @condition: binding state of the interface: not bound, binding * (in probe()), bound to a driver, or unbinding (in disconnect()) * @is_active: flag set when the interface is bound and not suspended. + * @needs_remote_wakeup: flag set when the driver requires remote-wakeup + * capability during autosuspend. * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. + * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not + * allowed unless the counter is 0. * * USB device drivers attach to interfaces on a physical device. Each * interface encapsulates a single high level function, such as feeding @@ -144,9 +149,11 @@ struct usb_interface { * bound to */ enum usb_interface_condition condition; /* state of binding */ unsigned is_active:1; /* the interface is not suspended */ + unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ struct device dev; /* interface specific device info */ struct class_device *class_dev; + int pm_usage_cnt; /* usage counter for autosuspend */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) #define interface_to_usbdev(intf) \ @@ -372,6 +379,15 @@ struct usb_device { int maxchild; /* Number of ports if hub */ struct usb_device *children[USB_MAXCHILDREN]; + +#ifdef CONFIG_PM + struct work_struct autosuspend; /* for delayed autosuspends */ + struct mutex pm_mutex; /* protects PM operations */ + int pm_usage_cnt; /* usage counter for autosuspend */ + + unsigned auto_pm:1; /* autosuspend/resume in progress */ + unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ +#endif }; #define to_usb_device(d) container_of(d, struct usb_device, dev) @@ -392,6 +408,17 @@ extern int usb_reset_composite_device(struct usb_device *dev, extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); +/* USB autosuspend and autoresume */ +#ifdef CONFIG_USB_SUSPEND +extern int usb_autopm_get_interface(struct usb_interface *intf); +extern void usb_autopm_put_interface(struct usb_interface *intf); + +#else +#define usb_autopm_get_interface(intf) 0 +#define usb_autopm_put_interface(intf) do {} while (0) +#endif + + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ @@ -593,6 +620,8 @@ struct usbdrv_wrap { * @drvwrap: Driver-model core structure wrapper. * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be * added to this driver by preventing the sysfs file from being created. + * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend + * for interfaces bound to this driver. * * USB interface drivers must provide a name, probe() and disconnect() * methods, and an id_table. Other driver fields are optional. @@ -631,6 +660,7 @@ struct usb_driver { struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; + unsigned int supports_autosuspend:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) @@ -648,6 +678,8 @@ struct usb_driver { * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. * @drvwrap: Driver-model core structure wrapper. + * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend + * for devices bound to this driver. * * USB drivers must provide all the fields listed above except drvwrap. */ @@ -660,6 +692,7 @@ struct usb_device_driver { int (*suspend) (struct usb_device *udev, pm_message_t message); int (*resume) (struct usb_device *udev); struct usbdrv_wrap drvwrap; + unsigned int supports_autosuspend:1; }; #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ drvwrap.driver) -- cgit v1.2.3 From 02c399ee45a54987c152fe5f627ed949bb55f187 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 Aug 2006 15:47:11 -0400 Subject: usbcore: remove usb_suspend_root_hub This patch (as740) removes the existing support for autosuspend of root hubs. That support fit in rather awkwardly with the rest of usbcore and it was used only by ohci-hcd. It won't be needed any more since the hub driver will take care of autosuspending all hubs, root or external. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 44 -------------------------------------------- drivers/usb/core/hcd.h | 6 ------ drivers/usb/core/hub.c | 18 +----------------- drivers/usb/core/usb.h | 1 - drivers/usb/host/ohci-hub.c | 3 --- 5 files changed, 1 insertion(+), 71 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9dfc812de034..e86f62957085 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1431,50 +1431,6 @@ int hcd_bus_resume (struct usb_bus *bus) return status; } -/* - * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports - * @hcd: host controller for this root hub - * - * This call arranges that usb_hcd_resume_root_hub() is safe to call later; - * that the HCD's root hub polling is deactivated; and that the root's hub - * driver is suspended. HCDs may call this to autosuspend when their root - * hub's downstream ports are all inactive: unpowered, disconnected, - * disabled, or suspended. - * - * The HCD will autoresume on device connect change detection (using SRP - * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling - * from any ports that are suspended (if that is enabled). In most cases, - * overcurrent signaling (on powered ports) will also start autoresume. - * - * Always called with IRQs blocked. - */ -void usb_hcd_suspend_root_hub (struct usb_hcd *hcd) -{ - struct urb *urb; - - spin_lock (&hcd_root_hub_lock); - usb_suspend_root_hub (hcd->self.root_hub); - - /* force status urb to complete/unlink while suspended */ - if (hcd->status_urb) { - urb = hcd->status_urb; - urb->status = -ECONNRESET; - urb->hcpriv = NULL; - urb->actual_length = 0; - - del_timer (&hcd->rh_timer); - hcd->poll_pending = 0; - hcd->status_urb = NULL; - } else - urb = NULL; - spin_unlock (&hcd_root_hub_lock); - hcd->state = HC_STATE_SUSPENDED; - - if (urb) - usb_hcd_giveback_urb (hcd, urb, NULL); -} -EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub); - /** * usb_hcd_resume_root_hub - called by HCD to resume its root hub * @hcd: host controller for this root hub diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 7a2bcba2ae61..676877c15f81 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -368,17 +368,11 @@ extern int usb_find_interface_driver (struct usb_device *dev, #define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) #ifdef CONFIG_PM -extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd); extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); extern void usb_root_hub_lost_power (struct usb_device *rhdev); extern int hcd_bus_suspend (struct usb_bus *bus); extern int hcd_bus_resume (struct usb_bus *bus); #else -static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd) -{ - return; -} - static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { return; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index dee812bc6c43..7479bd329abb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub) msleep(max(pgood_delay, (unsigned) 100)); } -static inline void __hub_quiesce(struct usb_hub *hub) +static void hub_quiesce(struct usb_hub *hub) { /* (nonblocking) khubd and related activity won't re-trigger */ hub->quiescing = 1; hub->activating = 0; hub->resume_root_hub = 0; -} -static void hub_quiesce(struct usb_hub *hub) -{ /* (blocking) stop khubd and related activity */ - __hub_quiesce(hub); usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work(&hub->leds); @@ -1914,18 +1910,6 @@ static inline int remote_wakeup(struct usb_device *udev) #define hub_resume NULL #endif -void usb_suspend_root_hub(struct usb_device *hdev) -{ - struct usb_hub *hub = hdev_to_hub(hdev); - - /* This also makes any led blinker stop retriggering. We're called - * from irq, so the blinker might still be scheduled. Caller promises - * that the root hub status URB will be canceled. - */ - __hub_quiesce(hub); - mark_quiesced(to_usb_interface(hub->intfdev)); -} - void usb_resume_root_hub(struct usb_device *hdev) { struct usb_hub *hub = hdev_to_hub(hdev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 10688ad73c6d..e8bc2e48913a 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -20,7 +20,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern void usb_kick_khubd(struct usb_device *dev); -extern void usb_suspend_root_hub(struct usb_device *hdev); extern void usb_resume_root_hub(struct usb_device *dev); extern int usb_hub_init(void); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index f1b1ed086bde..0b899339cac8 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -135,9 +135,6 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) hcd->poll_rh = 0; done: - /* external suspend vs self autosuspend ... same effect */ - if (status == 0) - usb_hcd_suspend_root_hub(hcd); spin_unlock_irqrestore (&ohci->lock, flags); return status; } -- cgit v1.2.3 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/devio.c | 2 +- drivers/usb/core/driver.c | 10 ++++++--- drivers/usb/core/endpoint.c | 30 ++++++++++++++++++------- drivers/usb/core/sysfs.c | 55 +++++++++++++++++++++++++++++++++++---------- drivers/usb/core/usb.c | 6 +++-- drivers/usb/core/usb.h | 6 ++--- 6 files changed, 80 insertions(+), 29 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index fd345ad810f8..71bbd25a4ed0 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1359,7 +1359,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: usb_unlock_device(ps->dev); - bus_rescan_devices(intf->dev.bus); + retval = bus_rescan_devices(intf->dev.bus); usb_lock_device(ps->dev); break; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0d063c8ca4b4..b10463244413 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); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 247b5a4913a8..3ebb90149e93 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -207,9 +207,9 @@ static void ep_device_release(struct device *dev) kfree(ep_dev); } -void usb_create_ep_files(struct device *parent, - struct usb_host_endpoint *endpoint, - struct usb_device *udev) +int usb_create_ep_files(struct device *parent, + struct usb_host_endpoint *endpoint, + struct usb_device *udev) { char name[8]; struct ep_device *ep_dev; @@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent, retval = device_register(&ep_dev->dev); if (retval) goto error; - sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); + retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); + if (retval) + goto error_group; endpoint->ep_dev = ep_dev; /* create the symlink to the old-style "ep_XX" directory */ sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); - sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name); - + retval = sysfs_create_link(&parent->kobj, + &endpoint->ep_dev->dev.kobj, name); + if (retval) + goto error_link; exit: - return; + return retval; + +error_link: + sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); + +error_group: + device_unregister(&ep_dev->dev); + endpoint->ep_dev = NULL; + destroy_endpoint_class(); + return retval; error: kfree(ep_dev); - return; + destroy_endpoint_class(); + return retval; } void usb_remove_ep_files(struct usb_host_endpoint *endpoint) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cd2286246f6a..55d8f575206d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -210,19 +210,40 @@ static struct attribute_group dev_attr_grp = { .attrs = dev_attrs, }; -void usb_create_sysfs_dev_files (struct usb_device *udev) +int usb_create_sysfs_dev_files(struct usb_device *udev) { struct device *dev = &udev->dev; + int retval; - sysfs_create_group(&dev->kobj, &dev_attr_grp); + retval = sysfs_create_group(&dev->kobj, &dev_attr_grp); + if (retval) + return retval; - if (udev->manufacturer) - device_create_file (dev, &dev_attr_manufacturer); - if (udev->product) - device_create_file (dev, &dev_attr_product); - if (udev->serial) - device_create_file (dev, &dev_attr_serial); - usb_create_ep_files(dev, &udev->ep0, udev); + if (udev->manufacturer) { + retval = device_create_file (dev, &dev_attr_manufacturer); + if (retval) + goto error; + } + if (udev->product) { + retval = device_create_file (dev, &dev_attr_product); + if (retval) + goto error; + } + if (udev->serial) { + retval = device_create_file (dev, &dev_attr_serial); + if (retval) + goto error; + } + retval = usb_create_ep_files(dev, &udev->ep0, udev); + if (retval) + goto error; + return 0; +error: + usb_remove_ep_files(&udev->ep0); + device_remove_file(dev, &dev_attr_manufacturer); + device_remove_file(dev, &dev_attr_product); + device_remove_file(dev, &dev_attr_serial); + return retval; } void usb_remove_sysfs_dev_files (struct usb_device *udev) @@ -339,18 +360,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf) usb_remove_ep_files(&iface_desc->endpoint[i]); } -void usb_create_sysfs_intf_files (struct usb_interface *intf) +int usb_create_sysfs_intf_files(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *alt = intf->cur_altsetting; + int retval; - sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + if (retval) + goto error; if (alt->string == NULL) alt->string = usb_cache_string(udev, alt->desc.iInterface); if (alt->string) - device_create_file(&intf->dev, &dev_attr_interface); + retval = device_create_file(&intf->dev, &dev_attr_interface); usb_create_intf_ep_files(intf, udev); + return 0; +error: + if (alt->string) + device_remove_file(&intf->dev, &dev_attr_interface); + sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + usb_remove_intf_ep_files(intf); + return retval; } void usb_remove_sysfs_intf_files (struct usb_interface *intf) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 6b029cdb8671..60ef4ef0101a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -147,11 +147,13 @@ static int __find_interface(struct device * dev, void * data) struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { struct find_interface_arg argb; + int retval; argb.minor = minor; argb.interface = NULL; - driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, - __find_interface); + /* eat the error, it will be in argb.interface */ + retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, + __find_interface); return argb.interface; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index e8bc2e48913a..0c09ecced6e1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,10 +1,10 @@ /* Functions local to drivers/usb/core/ */ -extern void usb_create_sysfs_dev_files (struct usb_device *dev); +extern int usb_create_sysfs_dev_files (struct usb_device *dev); extern void usb_remove_sysfs_dev_files (struct usb_device *dev); -extern void usb_create_sysfs_intf_files (struct usb_interface *intf); +extern int usb_create_sysfs_intf_files (struct usb_interface *intf); extern void usb_remove_sysfs_intf_files (struct usb_interface *intf); -extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint, +extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev); extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint); -- cgit v1.2.3 From bd859281c09b4318153dc7222b5e9052aad83b61 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 19 Sep 2006 10:14:07 -0400 Subject: USB: create new workqueue thread for USB autosuspend This patch (as787) creates a new workqueue thread to handle delayed USB autosuspend requests. Previously the code used keventd. However it turns out that the hub driver's suspend routine calls flush_scheduled_work(), making it a poor candidate for running in keventd (the call immediately deadlocks). The solution is to use a new thread instead of keventd. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 11 ++++++----- drivers/usb/core/usb.c | 42 +++++++++++++++++++++++++++++++++++++++--- drivers/usb/core/usb.h | 1 + 3 files changed, 46 insertions(+), 8 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index c2aad6a4d49f..ee18d187ca17 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,6 +24,7 @@ #include #include +#include #include "hcd.h" #include "usb.h" @@ -1131,7 +1132,7 @@ 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, + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, USB_AUTOSUSPEND_DELAY); mutex_unlock(&udev->pm_mutex); // dev_dbg(&udev->dev, "%s: cnt %d\n", @@ -1215,10 +1216,10 @@ 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); + if (intf->condition != USB_INTERFACE_UNBOUND && + --intf->pm_usage_cnt <= 0) { + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + USB_AUTOSUSPEND_DELAY); } mutex_unlock(&udev->pm_mutex); // dev_dbg(&intf->dev, "%s: cnt %d\n", diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 60ef4ef0101a..239f8e5d247f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ +struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ + /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -170,9 +173,9 @@ static void usb_release_dev(struct device *dev) udev = to_usb_device(dev); -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND cancel_delayed_work(&udev->autosuspend); - flush_scheduled_work(); + flush_workqueue(ksuspend_usb_wq); #endif usb_destroy_configuration(udev); usb_put_hcd(bus_to_hcd(udev->bus)); @@ -184,6 +187,28 @@ static void usb_release_dev(struct device *dev) #ifdef CONFIG_PM +static int ksuspend_usb_init(void) +{ + ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd"); + if (!ksuspend_usb_wq) + return -ENOMEM; + return 0; +} + +static void ksuspend_usb_cleanup(void) +{ + destroy_workqueue(ksuspend_usb_wq); +} + +#else + +#define ksuspend_usb_init() 0 +#define ksuspend_usb_cleanup() do {} while (0) + +#endif + +#ifdef CONFIG_USB_SUSPEND + /* usb_autosuspend_work - callback routine to autosuspend a USB device */ static void usb_autosuspend_work(void *_udev) { @@ -195,6 +220,11 @@ static void usb_autosuspend_work(void *_udev) mutex_unlock(&udev->pm_mutex); } +#else + +static void usb_autosuspend_work(void *_udev) +{} + #endif /** @@ -976,9 +1006,12 @@ static int __init usb_init(void) return 0; } + retval = ksuspend_usb_init(); + if (retval) + goto out; retval = bus_register(&usb_bus_type); if (retval) - goto out; + goto bus_register_failed; retval = usb_host_init(); if (retval) goto host_init_failed; @@ -1014,6 +1047,8 @@ major_init_failed: usb_host_cleanup(); host_init_failed: bus_unregister(&usb_bus_type); +bus_register_failed: + ksuspend_usb_cleanup(); out: return retval; } @@ -1035,6 +1070,7 @@ static void __exit usb_exit(void) usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); + ksuspend_usb_cleanup(); } subsys_initcall(usb_init); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0c09ecced6e1..fb6eb41c374f 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -62,6 +62,7 @@ extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt); #endif +extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; extern struct usb_device_driver usb_generic_driver; -- cgit v1.2.3 From e0318ebff4d96131bb3524308b845f642e64df81 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Sep 2006 14:50:20 -0400 Subject: USB: fix autosuspend when CONFIG_PM isn't set This patch (as791b) fixes things up to avoid compiler warnings or errors when CONFIG_USB_SUSPEND or CONFIG_PM isn't set. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 42 +++++++++++++++++++++--------------------- drivers/usb/core/hub.c | 4 ++-- drivers/usb/core/usb.c | 4 ++-- drivers/usb/core/usb.h | 18 +++++++++++++++++- include/linux/usb.h | 2 +- 5 files changed, 43 insertions(+), 27 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ee18d187ca17..113e484c763e 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -303,11 +303,11 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; mark_active(iface); iface->pm_usage_cnt = !(driver->supports_autosuspend); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -356,11 +356,11 @@ 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); + usb_pm_lock(udev); iface->condition = USB_INTERFACE_UNBOUND; mark_quiesced(iface); iface->needs_remote_wakeup = 0; - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } EXPORT_SYMBOL(usb_driver_release_interface); @@ -789,7 +789,7 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -/* Caller has locked udev->pm_mutex */ +/* Caller has locked udev's pm_mutex */ static int suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -816,7 +816,7 @@ done: return status; } -/* Caller has locked udev->pm_mutex */ +/* Caller has locked udev's pm_mutex */ static int resume_device(struct usb_device *udev) { struct usb_device_driver *udriver; @@ -842,7 +842,7 @@ done: return status; } -/* Caller has locked intf's usb_device's pm_mutex */ +/* 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; @@ -1064,7 +1064,7 @@ int usb_resume_both(struct usb_device *udev) /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { if (parent) { - mutex_lock_nested(&parent->pm_mutex, parent->level); + usb_pm_lock(parent); parent->auto_pm = 1; status = usb_resume_both(parent); } else { @@ -1079,7 +1079,7 @@ int usb_resume_both(struct usb_device *udev) if (status == 0) status = resume_device(udev); if (parent) - mutex_unlock(&parent->pm_mutex); + usb_pm_unlock(parent); } else { /* Needed only for setting udev->dev.power.power_state.event @@ -1129,12 +1129,12 @@ int usb_resume_both(struct usb_device *udev) */ void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) { - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->pm_usage_cnt -= dec_usage_cnt; if (udev->pm_usage_cnt <= 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, USB_AUTOSUSPEND_DELAY); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&udev->dev, "%s: cnt %d\n", // __FUNCTION__, udev->pm_usage_cnt); } @@ -1168,13 +1168,13 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) { int status; - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); 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); + usb_pm_unlock(udev); // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, udev->pm_usage_cnt); return status; @@ -1215,13 +1215,13 @@ 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); + usb_pm_lock(udev); if (intf->condition != USB_INTERFACE_UNBOUND && --intf->pm_usage_cnt <= 0) { queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, USB_AUTOSUSPEND_DELAY); } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&intf->dev, "%s: cnt %d\n", // __FUNCTION__, intf->pm_usage_cnt); } @@ -1263,7 +1263,7 @@ 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); + usb_pm_lock(udev); if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { @@ -1273,7 +1273,7 @@ int usb_autopm_get_interface(struct usb_interface *intf) if (status != 0) --intf->pm_usage_cnt; } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, intf->pm_usage_cnt); return status; @@ -1289,10 +1289,10 @@ static int usb_suspend(struct device *dev, pm_message_t message) if (is_usb_device(dev)) { struct usb_device *udev = to_usb_device(dev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, message); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } else status = 0; return status; @@ -1305,10 +1305,10 @@ static int usb_resume(struct device *dev) if (is_usb_device(dev)) { struct usb_device *udev = to_usb_device(dev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 0; status = usb_resume_both(udev); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); /* Rebind drivers that had no suspend method? */ } else diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2a8cb3c2b19c..7676690a0386 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1779,7 +1779,7 @@ static int remote_wakeup(struct usb_device *udev) * to the parent hub! */ usb_lock_device(udev); - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); /* TRSMRCY = 10 msec */ @@ -1788,7 +1788,7 @@ static int remote_wakeup(struct usb_device *udev) if (status == 0) udev->dev.power.power_state.event = PM_EVENT_ON; } - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); if (status == 0) usb_autoresume_device(udev, 0); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 239f8e5d247f..e4df9edf1bc0 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -214,10 +214,10 @@ static void usb_autosuspend_work(void *_udev) { struct usb_device *udev = _udev; - mutex_lock_nested(&udev->pm_mutex, udev->level); + usb_pm_lock(udev); udev->auto_pm = 1; usb_suspend_both(udev, PMSG_SUSPEND); - mutex_unlock(&udev->pm_mutex); + usb_pm_unlock(udev); } #else diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index fb6eb41c374f..f69df137ec0e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -36,6 +36,16 @@ extern int usb_resume_both(struct usb_device *udev); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +static inline void usb_pm_lock(struct usb_device *udev) +{ + mutex_lock_nested(&udev->pm_mutex, udev->level); +} + +static inline void usb_pm_unlock(struct usb_device *udev) +{ + mutex_unlock(&udev->pm_mutex); +} + #else #define usb_suspend_both(udev, msg) 0 @@ -45,6 +55,8 @@ static inline int usb_resume_both(struct usb_device *udev) } #define usb_port_suspend(dev) 0 #define usb_port_resume(dev) 0 +static inline void usb_pm_lock(struct usb_device *udev) {} +static inline void usb_pm_unlock(struct usb_device *udev) {} #endif @@ -58,7 +70,11 @@ extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt); #else #define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0) -#define usb_autoresume_device(udev, inc_busy_cnt) 0 +static inline int usb_autoresume_device(struct usb_device *udev, + int inc_busy_cnt) +{ + return 0; +} #endif diff --git a/include/linux/usb.h b/include/linux/usb.h index 0da15b0b02be..190cc1b78fe2 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -380,10 +380,10 @@ struct usb_device { int maxchild; /* Number of ports if hub */ struct usb_device *children[USB_MAXCHILDREN]; + int pm_usage_cnt; /* usage counter for autosuspend */ #ifdef CONFIG_PM struct work_struct autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ - int pm_usage_cnt; /* usage counter for autosuspend */ unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ -- cgit v1.2.3