From b94badbb47cb50f4fca8440efdaa8ebd32604fe4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 1 Aug 2006 22:33:34 -0400 Subject: USB: Make usb_buffer_free() NULL-safe kfree() handles NULL arguments which is handy in error handling paths as one does need to insert bunch of ifs. How about making usb_buffer_free() do the same? Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 184c24660a4c..ab766e0fe4ef 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -761,7 +761,9 @@ void usb_buffer_free ( ) { if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) - return; + return; + if (!addr) + return; dev->bus->op->buffer_free (dev->bus, size, addr, dma); } -- cgit 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.c') 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 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.c') 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 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.c') 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 From 782da727b0d59e93c84a627948b1535a3db90392 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 1 Jul 2006 22:09:35 -0400 Subject: usbcore: make usb_generic a usb_device_driver This patch (as714b) makes usb_generic into a usb_device_driver capable of being probed and unbound, just like other drivers. A fair amount of the work that used to get done during discovery or removal of a USB device have been moved to the probe and disconnect methods of usb_generic: creating the sysfs attributes and selecting an initial configuration. However the normal behavior should continue to be the same as before. We will now have the possibility of creating other USB device drivers, They will assist with exporting devices to remote systems (USB-over-TCPIP) or to paravirtual guest operating systems. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 69 +++++++++-------- drivers/usb/core/generic.c | 184 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/hub.c | 164 ++++------------------------------------ drivers/usb/core/usb.c | 1 - 4 files changed, 232 insertions(+), 186 deletions(-) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0d4b5dcee3ab..a62de0a85406 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -530,9 +530,10 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, /* driver is often null here; dev_dbg() would oops */ pr_debug ("usb %s: uevent\n", dev->bus_id); - if (is_usb_device(dev)) - return 0; - else { + if (is_usb_device(dev)) { + usb_dev = to_usb_device(dev); + alt = NULL; + } else { intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); alt = intf->cur_altsetting; @@ -579,15 +580,17 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (!is_usb_device(dev)) { + + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "INTERFACE=%d/%d/%d", alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, alt->desc.bInterfaceProtocol)) - return -ENOMEM; + return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", le16_to_cpu(usb_dev->descriptor.idVendor), @@ -599,7 +602,8 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, alt->desc.bInterfaceProtocol)) - return -ENOMEM; + return -ENOMEM; + } envp[i] = NULL; @@ -747,31 +751,22 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM -static int verify_suspended(struct device *dev, void *unused) +static int usb_suspend(struct device *dev, pm_message_t message) { - if (dev->driver == NULL) - return 0; - return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; -} + struct usb_device *udev; + struct usb_device_driver *udriver; + struct usb_interface *intf; + struct usb_driver *driver; + int status; -static int usb_generic_suspend(struct device *dev, pm_message_t message) -{ - struct usb_interface *intf; - struct usb_driver *driver; - int status; - - /* USB devices enter SUSPEND state through their hubs, but can be - * marked for FREEZE as soon as their children are already idled. - * But those semantics are useless, so we equate the two (sigh). - */ if (is_usb_device(dev)) { + if (dev->driver == NULL) + return 0; + udev = to_usb_device(dev); + udriver = to_usb_device_driver(dev->driver); if (dev->power.power_state.event == message.event) return 0; - /* we need to rule out bogus requests through sysfs */ - status = device_for_each_child(dev, NULL, verify_suspended); - if (status) - return status; - return usb_port_suspend(to_usb_device(dev)); + return udriver->suspend(udev, message); } if (dev->driver == NULL) @@ -799,12 +794,13 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) return status; } -static int usb_generic_resume(struct device *dev) +static int usb_resume(struct device *dev) { - struct usb_interface *intf; - struct usb_driver *driver; - struct usb_device *udev; - int status; + struct usb_device *udev; + struct usb_device_driver *udriver; + struct usb_interface *intf; + struct usb_driver *driver; + int status; if (dev->power.power_state.event == PM_EVENT_ON) return 0; @@ -814,10 +810,13 @@ static int usb_generic_resume(struct device *dev) /* devices resume through their hubs */ if (is_usb_device(dev)) { + if (dev->driver == NULL) + return 0; udev = to_usb_device(dev); + udriver = to_usb_device_driver(dev->driver); if (udev->state == USB_STATE_NOTATTACHED) return 0; - return usb_port_resume(udev); + return udriver->resume(udev); } if (dev->driver == NULL) { @@ -854,7 +853,7 @@ struct bus_type usb_bus_type = { .match = usb_device_match, .uevent = usb_uevent, #ifdef CONFIG_PM - .suspend = usb_generic_suspend, - .resume = usb_generic_resume, + .suspend = usb_suspend, + .resume = usb_resume, #endif }; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index fa6f34a12b4b..1522195de715 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -21,24 +21,208 @@ #include #include "usb.h" +static inline const char *plural(int n) +{ + return (n == 1 ? "" : "s"); +} + +static int choose_configuration(struct usb_device *udev) +{ + int i; + int num_configs; + int insufficient_power = 0; + struct usb_host_config *c, *best; + + best = NULL; + c = udev->config; + num_configs = udev->descriptor.bNumConfigurations; + for (i = 0; i < num_configs; (i++, c++)) { + struct usb_interface_descriptor *desc = NULL; + + /* It's possible that a config has no interfaces! */ + if (c->desc.bNumInterfaces > 0) + desc = &c->intf_cache[0]->altsetting->desc; + + /* + * HP's USB bus-powered keyboard has only one configuration + * and it claims to be self-powered; other devices may have + * similar errors in their descriptors. If the next test + * were allowed to execute, such configurations would always + * be rejected and the devices would not work as expected. + * In the meantime, we run the risk of selecting a config + * that requires external power at a time when that power + * isn't available. It seems to be the lesser of two evils. + * + * Bugzilla #6448 reports a device that appears to crash + * when it receives a GET_DEVICE_STATUS request! We don't + * have any other way to tell whether a device is self-powered, + * but since we don't use that information anywhere but here, + * the call has been removed. + * + * Maybe the GET_DEVICE_STATUS call and the test below can + * be reinstated when device firmwares become more reliable. + * Don't hold your breath. + */ +#if 0 + /* Rule out self-powered configs for a bus-powered device */ + if (bus_powered && (c->desc.bmAttributes & + USB_CONFIG_ATT_SELFPOWER)) + continue; +#endif + + /* + * The next test may not be as effective as it should be. + * Some hubs have errors in their descriptor, claiming + * to be self-powered when they are really bus-powered. + * We will overestimate the amount of current such hubs + * make available for each port. + * + * This is a fairly benign sort of failure. It won't + * cause us to reject configurations that we should have + * accepted. + */ + + /* Rule out configs that draw too much bus current */ + if (c->desc.bMaxPower * 2 > udev->bus_mA) { + insufficient_power++; + continue; + } + + /* If the first config's first interface is COMM/2/0xff + * (MSFT RNDIS), rule it out unless Linux has host-side + * RNDIS support. */ + if (i == 0 && desc + && desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff) { +#ifndef CONFIG_USB_NET_RNDIS_HOST + continue; +#else + best = c; +#endif + } + + /* From the remaining configs, choose the first one whose + * first interface is for a non-vendor-specific class. + * Reason: Linux is more likely to have a class driver + * than a vendor-specific driver. */ + else if (udev->descriptor.bDeviceClass != + USB_CLASS_VENDOR_SPEC && + (!desc || desc->bInterfaceClass != + USB_CLASS_VENDOR_SPEC)) { + best = c; + break; + } + + /* If all the remaining configs are vendor-specific, + * choose the first one. */ + else if (!best) + best = c; + } + + if (insufficient_power > 0) + dev_info(&udev->dev, "rejected %d configuration%s " + "due to insufficient available bus power\n", + insufficient_power, plural(insufficient_power)); + + if (best) { + i = best->desc.bConfigurationValue; + dev_info(&udev->dev, + "configuration #%d chosen from %d choice%s\n", + i, num_configs, plural(num_configs)); + } else { + i = -1; + dev_warn(&udev->dev, + "no configuration chosen from %d choice%s\n", + num_configs, plural(num_configs)); + } + return i; +} + static int generic_probe(struct usb_device *udev) { + int err, c; + + /* put device-specific files into sysfs */ + usb_create_sysfs_dev_files(udev); + + /* Choose and set the configuration. This registers the interfaces + * with the driver core and lets interface drivers bind to them. + */ + c = choose_configuration(udev); + if (c >= 0) { + err = usb_set_configuration(udev, c); + if (err) { + dev_err(&udev->dev, "can't set config #%d, error %d\n", + c, err); + /* This need not be fatal. The user can try to + * set other configurations. */ + } + } + + /* USB device state == configured ... usable */ + usb_notify_add_device(udev); + return 0; } + static void generic_disconnect(struct usb_device *udev) { + usb_notify_remove_device(udev); + /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ if (udev->state == USB_STATE_CONFIGURED) usb_set_configuration(udev, 0); + usb_remove_sysfs_dev_files(udev); + /* in case the call failed or the device was suspended */ if (udev->state >= USB_STATE_CONFIGURED) usb_disable_device(udev, 0); } +#ifdef CONFIG_PM + +static int verify_suspended(struct device *dev, void *unused) +{ + if (dev->driver == NULL) + return 0; + return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; +} + +static int generic_suspend(struct usb_device *udev, pm_message_t msg) +{ + int status; + + /* rule out bogus requests through sysfs */ + status = device_for_each_child(&udev->dev, NULL, verify_suspended); + if (status) + return status; + + /* USB devices enter SUSPEND state through their hubs, but can be + * marked for FREEZE as soon as their children are already idled. + * But those semantics are useless, so we equate the two (sigh). + */ + return usb_port_suspend(udev); +} + +static int generic_resume(struct usb_device *udev) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_port_resume(udev); +} + +#endif /* CONFIG_PM */ + struct usb_device_driver usb_generic_driver = { .name = "usb", .probe = generic_probe, .disconnect = generic_disconnect, +#ifdef CONFIG_PM + .suspend = generic_suspend, + .resume = generic_resume, +#endif }; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b00514d9a605..a372332440b2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1148,144 +1148,28 @@ void usb_disconnect(struct usb_device **pdev) * cleaning up all state associated with the current configuration * so that the hardware is now fully quiesced. */ + dev_dbg (&udev->dev, "unregistering device\n"); usb_disable_device(udev, 0); - usb_notify_remove_device(udev); + usb_unlock_device(udev); + + /* Unregister the device. The device driver is responsible + * for removing the device files from usbfs and sysfs and for + * de-configuring the device. + */ + device_del(&udev->dev); - /* Free the device number, remove the /proc/bus/usb entry and - * the sysfs attributes, and delete the parent's children[] + /* Free the device number and delete the parent's children[] * (or root_hub) pointer. */ - dev_dbg (&udev->dev, "unregistering device\n"); release_address(udev); - usb_remove_sysfs_dev_files(udev); /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); - usb_unlock_device(udev); - - device_unregister(&udev->dev); -} - -static inline const char *plural(int n) -{ - return (n == 1 ? "" : "s"); -} - -static int choose_configuration(struct usb_device *udev) -{ - int i; - int num_configs; - int insufficient_power = 0; - struct usb_host_config *c, *best; - - best = NULL; - c = udev->config; - num_configs = udev->descriptor.bNumConfigurations; - for (i = 0; i < num_configs; (i++, c++)) { - struct usb_interface_descriptor *desc = NULL; - - /* It's possible that a config has no interfaces! */ - if (c->desc.bNumInterfaces > 0) - desc = &c->intf_cache[0]->altsetting->desc; - - /* - * HP's USB bus-powered keyboard has only one configuration - * and it claims to be self-powered; other devices may have - * similar errors in their descriptors. If the next test - * were allowed to execute, such configurations would always - * be rejected and the devices would not work as expected. - * In the meantime, we run the risk of selecting a config - * that requires external power at a time when that power - * isn't available. It seems to be the lesser of two evils. - * - * Bugzilla #6448 reports a device that appears to crash - * when it receives a GET_DEVICE_STATUS request! We don't - * have any other way to tell whether a device is self-powered, - * but since we don't use that information anywhere but here, - * the call has been removed. - * - * Maybe the GET_DEVICE_STATUS call and the test below can - * be reinstated when device firmwares become more reliable. - * Don't hold your breath. - */ -#if 0 - /* Rule out self-powered configs for a bus-powered device */ - if (bus_powered && (c->desc.bmAttributes & - USB_CONFIG_ATT_SELFPOWER)) - continue; -#endif - - /* - * The next test may not be as effective as it should be. - * Some hubs have errors in their descriptor, claiming - * to be self-powered when they are really bus-powered. - * We will overestimate the amount of current such hubs - * make available for each port. - * - * This is a fairly benign sort of failure. It won't - * cause us to reject configurations that we should have - * accepted. - */ - - /* Rule out configs that draw too much bus current */ - if (c->desc.bMaxPower * 2 > udev->bus_mA) { - insufficient_power++; - continue; - } - - /* If the first config's first interface is COMM/2/0xff - * (MSFT RNDIS), rule it out unless Linux has host-side - * RNDIS support. */ - if (i == 0 && desc - && desc->bInterfaceClass == USB_CLASS_COMM - && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) { -#ifndef CONFIG_USB_NET_RNDIS_HOST - continue; -#else - best = c; -#endif - } - - /* From the remaining configs, choose the first one whose - * first interface is for a non-vendor-specific class. - * Reason: Linux is more likely to have a class driver - * than a vendor-specific driver. */ - else if (udev->descriptor.bDeviceClass != - USB_CLASS_VENDOR_SPEC && - (!desc || desc->bInterfaceClass != - USB_CLASS_VENDOR_SPEC)) { - best = c; - break; - } - - /* If all the remaining configs are vendor-specific, - * choose the first one. */ - else if (!best) - best = c; - } - - if (insufficient_power > 0) - dev_info(&udev->dev, "rejected %d configuration%s " - "due to insufficient available bus power\n", - insufficient_power, plural(insufficient_power)); - - if (best) { - i = best->desc.bConfigurationValue; - dev_info(&udev->dev, - "configuration #%d chosen from %d choice%s\n", - i, num_configs, plural(num_configs)); - } else { - i = -1; - dev_warn(&udev->dev, - "no configuration chosen from %d choice%s\n", - num_configs, plural(num_configs)); - } - return i; + put_device(&udev->dev); } #ifdef DEBUG @@ -1328,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) int usb_new_device(struct usb_device *udev) { int err; - int c; err = usb_get_configuration(udev); if (err < 0) { @@ -1418,34 +1301,15 @@ int usb_new_device(struct usb_device *udev) } #endif - /* put device-specific files into sysfs */ + /* Register the device. The device driver is responsible + * for adding the device files to usbfs and sysfs and for + * configuring the device. + */ err = device_add (&udev->dev); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); goto fail; } - usb_create_sysfs_dev_files (udev); - - usb_lock_device(udev); - - /* choose and set the configuration. that registers the interfaces - * with the driver core, and lets usb device drivers bind to them. - */ - c = choose_configuration(udev); - if (c >= 0) { - err = usb_set_configuration(udev, c); - if (err) { - dev_err(&udev->dev, "can't set config #%d, error %d\n", - c, err); - /* This need not be fatal. The user can try to - * set other configurations. */ - } - } - - /* USB device state == configured ... usable */ - usb_notify_add_device(udev); - - usb_unlock_device(udev); return 0; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 6dfbc284369b..9ebfc0fe819d 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -205,7 +205,6 @@ 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 = &usb_generic_driver.drvwrap.driver; dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; -- cgit From b7cfaaaf86571732c7728e95a2231a860385463c Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Wed, 27 Sep 2006 11:58:53 -0700 Subject: USB: New functions to check endpoints info. These functions makes USB driver's code simpler when dealing with endpoints by avoiding them from accessing the endpoint's descriptor structure directly when they only need to know the endpoint's transfer type and/or direction. Please, read each functions' documentation in order to know how to use them. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 14 +++++ 2 files changed, 158 insertions(+) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 9ebfc0fe819d..82837d45b484 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -482,6 +482,138 @@ int usb_get_current_frame_number(struct usb_device *dev) return dev->bus->op->get_frame_number (dev); } +/** + * usb_endpoint_dir_in - check if the endpoint has IN direction + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type IN, otherwise it returns false. + */ +int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); +} + +/** + * usb_endpoint_dir_out - check if the endpoint has OUT direction + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type OUT, otherwise it returns false. + */ +int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); +} + +/** + * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type bulk, otherwise it returns false. + */ +int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK); +} + +/** + * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type interrupt, otherwise it returns + * false. + */ +int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_INT); +} + +/** + * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type isochronous, otherwise it returns + * false. + */ +int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC); +} + +/** + * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has bulk transfer type and IN direction, + * otherwise it returns false. + */ +int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd)); +} + +/** + * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has bulk transfer type and OUT direction, + * otherwise it returns false. + */ +int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd)); +} + +/** + * usb_endpoint_is_int_in - check if the endpoint is interrupt IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has interrupt transfer type and IN direction, + * otherwise it returns false. + */ +int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd)); +} + +/** + * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has interrupt transfer type and OUT direction, + * otherwise it returns false. + */ +int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd)); +} + +/** + * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has isochronous transfer type and IN direction, + * otherwise it returns false. + */ +int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd)); +} + +/** + * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has isochronous transfer type and OUT direction, + * otherwise it returns false. + */ +int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd) +{ + return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd)); +} + /*-------------------------------------------------------------------*/ /* * __usb_get_extra_descriptor() finds a descriptor of specific type in the @@ -909,6 +1041,18 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor); EXPORT_SYMBOL(usb_find_device); EXPORT_SYMBOL(usb_get_current_frame_number); +EXPORT_SYMBOL_GPL(usb_endpoint_dir_in); +EXPORT_SYMBOL_GPL(usb_endpoint_dir_out); +EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk); +EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int); +EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc); +EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in); +EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out); +EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in); +EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out); +EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in); +EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out); + EXPORT_SYMBOL (usb_buffer_alloc); EXPORT_SYMBOL (usb_buffer_free); diff --git a/include/linux/usb.h b/include/linux/usb.h index 3d5cfa731680..f807479ef65b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -467,6 +467,20 @@ static inline int usb_make_path (struct usb_device *dev, char *buf, /*-------------------------------------------------------------------------*/ +extern int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd); +extern int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd); + +/*-------------------------------------------------------------------------*/ + #define USB_DEVICE_ID_MATCH_DEVICE \ (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT) #define USB_DEVICE_ID_MATCH_DEV_RANGE \ -- cgit 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.c') 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 From a6d2bb9ff919b4685bd684620ec7a1ffa8bf2349 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 Aug 2006 11:27:36 -0400 Subject: USB: remove struct usb_operations All of the currently-supported USB host controller drivers use the HCD bus-glue framework. As part of the program for flattening out the glue layer, this patch (as769) removes the usb_operations structure. All function calls now go directly to the HCD routines (slightly renamed to remain within the "usb_" namespace). The patch also removes usb_alloc_bus(), because it's not useful in the HCD framework and it wasn't referenced anywhere. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 49 +++++----------------------------------------- drivers/usb/core/hcd.h | 32 +++++++----------------------- drivers/usb/core/message.c | 4 ++-- drivers/usb/core/urb.c | 13 +++++------- drivers/usb/core/usb.c | 10 +++++----- include/linux/usb.h | 4 ---- 6 files changed, 24 insertions(+), 88 deletions(-) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index ea20a3a5a9b9..2102c4deec1e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -731,30 +731,6 @@ static void usb_bus_init (struct usb_bus *bus) kref_init(&bus->kref); } -/** - * usb_alloc_bus - creates a new USB host controller structure - * @op: pointer to a struct usb_operations that this bus structure should use - * Context: !in_interrupt() - * - * Creates a USB host controller bus structure with the specified - * usb_operations and initializes all the necessary internal objects. - * - * If no memory is available, NULL is returned. - * - * The caller should call usb_put_bus() when it is finished with the structure. - */ -struct usb_bus *usb_alloc_bus (struct usb_operations *op) -{ - struct usb_bus *bus; - - bus = kzalloc (sizeof *bus, GFP_KERNEL); - if (!bus) - return NULL; - usb_bus_init (bus); - bus->op = op; - return bus; -} - /*-------------------------------------------------------------------------*/ /** @@ -1102,7 +1078,7 @@ static void urb_unlink (struct urb *urb) * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */ -static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) +int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) { int status; struct usb_hcd *hcd = urb->dev->bus->hcpriv; @@ -1211,7 +1187,7 @@ done: /*-------------------------------------------------------------------------*/ /* called in any context */ -static int hcd_get_frame_number (struct usb_device *udev) +int usb_hcd_get_frame_number (struct usb_device *udev) { struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv; if (!HC_IS_RUNNING (hcd->state)) @@ -1253,7 +1229,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) * caller guarantees urb won't be recycled till both unlink() * and the urb's completion function return */ -static int hcd_unlink_urb (struct urb *urb, int status) +int usb_hcd_unlink_urb (struct urb *urb, int status) { struct usb_host_endpoint *ep; struct usb_hcd *hcd = NULL; @@ -1351,8 +1327,8 @@ done: * example: a qh stored in ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */ -static void -hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) +void usb_hcd_endpoint_disable (struct usb_device *udev, + struct usb_host_endpoint *ep) { struct usb_hcd *hcd; struct urb *urb; @@ -1589,20 +1565,6 @@ EXPORT_SYMBOL (usb_bus_start_enum); /*-------------------------------------------------------------------------*/ -/* - * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue) - */ -static struct usb_operations usb_hcd_operations = { - .get_frame_number = hcd_get_frame_number, - .submit_urb = hcd_submit_urb, - .unlink_urb = hcd_unlink_urb, - .buffer_alloc = hcd_buffer_alloc, - .buffer_free = hcd_buffer_free, - .disable = hcd_endpoint_disable, -}; - -/*-------------------------------------------------------------------------*/ - /** * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB @@ -1744,7 +1706,6 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, dev_set_drvdata(dev, hcd); usb_bus_init(&hcd->self); - hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = hcd; hcd->self.release = &hcd_release; hcd->self.controller = dev; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index fc71a08a1af4..83e229914797 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -139,28 +139,6 @@ struct hcd_timeout { /* timeouts we allocate */ /*-------------------------------------------------------------------------*/ -/* - * FIXME usb_operations should vanish or become hc_driver, - * when usb_bus and usb_hcd become the same thing. - */ - -struct usb_operations { - int (*get_frame_number) (struct usb_device *usb_dev); - int (*submit_urb) (struct urb *urb, gfp_t mem_flags); - int (*unlink_urb) (struct urb *urb, int status); - - /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ - void *(*buffer_alloc)(struct usb_bus *bus, size_t size, - gfp_t mem_flags, - dma_addr_t *dma); - void (*buffer_free)(struct usb_bus *bus, size_t size, - void *addr, dma_addr_t dma); - - void (*disable)(struct usb_device *udev, - struct usb_host_endpoint *ep); -}; - -/* each driver provides one of these, and hardware init support */ struct pt_regs; @@ -222,7 +200,13 @@ struct hc_driver { /* Needed only if port-change IRQs are level-triggered */ }; -extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); +extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags); +extern int usb_hcd_unlink_urb (struct urb *urb, int status); +extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, + struct pt_regs *regs); +extern void usb_hcd_endpoint_disable (struct usb_device *udev, + struct usb_host_endpoint *ep); +extern int usb_hcd_get_frame_number (struct usb_device *udev); extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name); @@ -361,8 +345,6 @@ extern long usb_calc_bus_time (int speed, int is_input, /*-------------------------------------------------------------------------*/ -extern struct usb_bus *usb_alloc_bus (struct usb_operations *); - extern void usb_set_device_state(struct usb_device *udev, enum usb_device_state new_state); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 333b22c68aa4..1580c81a0db7 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -984,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) ep = dev->ep_in[epnum]; dev->ep_in[epnum] = NULL; } - if (ep && dev->bus && dev->bus->op && dev->bus->op->disable) - dev->bus->op->disable(dev, ep); + if (ep && dev->bus) + usb_hcd_endpoint_disable(dev, ep); } /** diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9864988377c7..576919927f53 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { int pipe, temp, max; struct usb_device *dev; - struct usb_operations *op; int is_out; if (!urb || urb->hcpriv || !urb->complete) @@ -233,8 +232,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (dev->bus->controller->power.power_state.event != PM_EVENT_ON || dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (!(op = dev->bus->op) || !op->submit_urb) - return -ENODEV; urb->status = -EINPROGRESS; urb->actual_length = 0; @@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->interval = temp; } - return op->submit_urb (urb, mem_flags); + return usb_hcd_submit_urb (urb, mem_flags); } /*-------------------------------------------------------------------*/ @@ -440,9 +437,9 @@ int usb_unlink_urb(struct urb *urb) { if (!urb) return -EINVAL; - if (!(urb->dev && urb->dev->bus && urb->dev->bus->op)) + if (!(urb->dev && urb->dev->bus)) return -ENODEV; - return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET); + return usb_hcd_unlink_urb(urb, -ECONNRESET); } /** @@ -468,13 +465,13 @@ int usb_unlink_urb(struct urb *urb) void usb_kill_urb(struct urb *urb) { might_sleep(); - if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op)) + if (!(urb && urb->dev && urb->dev->bus)) return; spin_lock_irq(&urb->lock); ++urb->reject; spin_unlock_irq(&urb->lock); - urb->dev->bus->op->unlink_urb(urb, -ENOENT); + usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); spin_lock_irq(&urb->lock); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4eb98eb3804f..7ab9d29215f8 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -480,7 +480,7 @@ exit: */ int usb_get_current_frame_number(struct usb_device *dev) { - return dev->bus->op->get_frame_number (dev); + return usb_hcd_get_frame_number (dev); } /** @@ -677,9 +677,9 @@ void *usb_buffer_alloc ( dma_addr_t *dma ) { - if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc) + if (!dev || !dev->bus) return NULL; - return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma); + return hcd_buffer_alloc (dev->bus, size, mem_flags, dma); } /** @@ -700,11 +700,11 @@ void usb_buffer_free ( dma_addr_t dma ) { - if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) + if (!dev || !dev->bus) return; if (!addr) return; - dev->bus->op->buffer_free (dev->bus, size, addr, dma); + hcd_buffer_free (dev->bus, size, addr, dma); } /** diff --git a/include/linux/usb.h b/include/linux/usb.h index f104efa04d79..4709033f8fa7 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -257,8 +257,6 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, /* ----------------------------------------------------------------------- */ -struct usb_operations; - /* USB device number allocation bitmap */ struct usb_devmap { unsigned long devicemap[128 / (8*sizeof(unsigned long))]; @@ -279,7 +277,6 @@ struct usb_bus { * round-robin allocation */ struct usb_devmap devmap; /* device address allocation map */ - struct usb_operations *op; /* Operations (specific to the HC) */ struct usb_device *root_hub; /* Root hub */ struct list_head bus_list; /* list of busses */ void *hcpriv; /* Host Controller private data */ @@ -1051,7 +1048,6 @@ extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_unlink_urb(struct urb *urb); extern void usb_kill_urb(struct urb *urb); -#define HAVE_USB_BUFFERS void *usb_buffer_alloc (struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); void usb_buffer_free (struct usb_device *dev, size_t size, -- cgit From 1720058343fa43a1a25bfad9e62ea06e7e9743b6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 30 Aug 2006 11:32:52 -0400 Subject: usbcore: trim down usb_bus structure As part of the ongoing program to flatten out the HCD bus-glue layer, this patch (as771b) eliminates the hcpriv, release, and kref fields from struct usb_bus. hcpriv and release were not being used for anything worthwhile, and kref has been moved into the enclosing usb_hcd structure. Along with those changes, the patch gets rid of usb_bus_get and usb_bus_put, replacing them with usb_get_hcd and usb_put_hcd. The one interesting aspect is that the dev_set_drvdata call was removed from usb_put_hcd, where it clearly doesn't belong. This means the driver private data won't get reset to NULL. It shouldn't cause any problems, since the private data is undefined when no driver is bound. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/buffer.c | 4 +-- drivers/usb/core/hcd.c | 67 +++++++++++++++--------------------------- drivers/usb/core/hcd.h | 13 ++++---- drivers/usb/core/usb.c | 5 ++-- drivers/usb/gadget/dummy_hcd.c | 8 ++--- drivers/usb/host/ehci-dbg.c | 6 ++-- drivers/usb/host/ohci-dbg.c | 6 ++-- drivers/usb/mon/mon_main.c | 6 ++-- include/linux/usb.h | 3 -- 9 files changed, 44 insertions(+), 74 deletions(-) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index f4f4ef0f377a..840442a25b61 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -104,7 +104,7 @@ void *hcd_buffer_alloc ( dma_addr_t *dma ) { - struct usb_hcd *hcd = bus->hcpriv; + struct usb_hcd *hcd = bus_to_hcd(bus); int i; /* some USB hosts just use PIO */ @@ -127,7 +127,7 @@ void hcd_buffer_free ( dma_addr_t dma ) { - struct usb_hcd *hcd = bus->hcpriv; + struct usb_hcd *hcd = bus_to_hcd(bus); int i; if (!addr) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0cc14206920a..9dfc812de034 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -664,31 +664,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) /*-------------------------------------------------------------------------*/ -/* exported only within usbcore */ -struct usb_bus *usb_bus_get(struct usb_bus *bus) -{ - if (bus) - kref_get(&bus->kref); - return bus; -} - -static void usb_host_release(struct kref *kref) -{ - struct usb_bus *bus = container_of(kref, struct usb_bus, kref); - - if (bus->release) - bus->release(bus); -} - -/* exported only within usbcore */ -void usb_bus_put(struct usb_bus *bus) -{ - if (bus) - kref_put(&bus->kref, usb_host_release); -} - -/*-------------------------------------------------------------------------*/ - static struct class *usb_host_class; int usb_host_init(void) @@ -720,15 +695,12 @@ static void usb_bus_init (struct usb_bus *bus) bus->devnum_next = 1; bus->root_hub = NULL; - bus->hcpriv = NULL; bus->busnum = -1; bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; INIT_LIST_HEAD (&bus->bus_list); - - kref_init(&bus->kref); } /*-------------------------------------------------------------------------*/ @@ -1081,7 +1053,7 @@ static void urb_unlink (struct urb *urb) int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) { int status; - struct usb_hcd *hcd = urb->dev->bus->hcpriv; + struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); struct usb_host_endpoint *ep; unsigned long flags; @@ -1189,7 +1161,8 @@ done: /* called in any context */ int usb_hcd_get_frame_number (struct usb_device *udev) { - struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + if (!HC_IS_RUNNING (hcd->state)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); @@ -1262,7 +1235,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) spin_lock (&hcd_data_lock); sys = &urb->dev->dev; - hcd = urb->dev->bus->hcpriv; + hcd = bus_to_hcd(urb->dev->bus); if (hcd == NULL) { retval = -ENODEV; goto done; @@ -1333,7 +1306,7 @@ void usb_hcd_endpoint_disable (struct usb_device *udev, struct usb_hcd *hcd; struct urb *urb; - hcd = udev->bus->hcpriv; + hcd = bus_to_hcd(udev->bus); WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && udev->state != USB_STATE_NOTATTACHED); @@ -1673,14 +1646,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ -static void hcd_release (struct usb_bus *bus) -{ - struct usb_hcd *hcd; - - hcd = container_of(bus, struct usb_hcd, self); - kfree(hcd); -} - /** * usb_create_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd @@ -1705,10 +1670,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, return NULL; } dev_set_drvdata(dev, hcd); + kref_init(&hcd->kref); usb_bus_init(&hcd->self); - hcd->self.hcpriv = hcd; - hcd->self.release = &hcd_release; hcd->self.controller = dev; hcd->self.bus_name = bus_name; hcd->self.uses_dma = (dev->dma_mask != NULL); @@ -1725,10 +1689,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, } EXPORT_SYMBOL (usb_create_hcd); +static void hcd_release (struct kref *kref) +{ + struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); + + kfree(hcd); +} + +struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) +{ + if (hcd) + kref_get (&hcd->kref); + return hcd; +} +EXPORT_SYMBOL (usb_get_hcd); + void usb_put_hcd (struct usb_hcd *hcd) { - dev_set_drvdata(hcd->self.controller, NULL); - usb_bus_put(&hcd->self); + if (hcd) + kref_put (&hcd->kref, hcd_release); } EXPORT_SYMBOL (usb_put_hcd); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 83e229914797..7a2bcba2ae61 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -55,12 +55,13 @@ /*-------------------------------------------------------------------------*/ -struct usb_hcd { /* usb_bus.hcpriv points to this */ +struct usb_hcd { /* * housekeeping */ struct usb_bus self; /* hcd is-a bus */ + struct kref kref; /* reference counter */ const char *product_desc; /* product/vendor string */ char irq_descr[24]; /* driver + bus # */ @@ -129,8 +130,10 @@ static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) return &hcd->self; } - -// urb.hcpriv is really hardware-specific +static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus) +{ + return container_of(bus, struct usb_hcd, self); +} struct hcd_timeout { /* timeouts we allocate */ struct list_head timeout_list; @@ -210,6 +213,7 @@ extern int usb_hcd_get_frame_number (struct usb_device *udev); extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name); +extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd); extern void usb_put_hcd (struct usb_hcd *hcd); extern int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags); @@ -356,9 +360,6 @@ extern struct list_head usb_bus_list; extern struct mutex usb_bus_list_lock; extern wait_queue_head_t usb_kill_urb_queue; -extern struct usb_bus *usb_bus_get (struct usb_bus *bus); -extern void usb_bus_put (struct usb_bus *bus); - extern void usb_enable_root_hub_irq (struct usb_bus *bus); extern int usb_find_interface_driver (struct usb_device *dev, diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7ab9d29215f8..b0c0a993338f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -169,7 +169,7 @@ static void usb_release_dev(struct device *dev) udev = to_usb_device(dev); usb_destroy_configuration(udev); - usb_bus_put(udev->bus); + usb_put_hcd(bus_to_hcd(udev->bus)); kfree(udev->product); kfree(udev->manufacturer); kfree(udev->serial); @@ -197,8 +197,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) if (!dev) return NULL; - bus = usb_bus_get(bus); - if (!bus) { + if (!usb_get_hcd(bus_to_hcd(bus))) { kfree(dev); return NULL; } diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 7d1c22c34957..fdab97a27c08 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -889,11 +889,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode); static void dummy_gadget_release (struct device *dev) { -#if 0 /* usb_bus_put isn't EXPORTed! */ struct dummy *dum = gadget_dev_to_dummy (dev); - usb_bus_put (&dummy_to_hcd (dum)->self); -#endif + usb_put_hcd (dummy_to_hcd (dum)); } static int dummy_udc_probe (struct platform_device *pdev) @@ -915,9 +913,7 @@ static int dummy_udc_probe (struct platform_device *pdev) if (rc < 0) return rc; -#if 0 /* usb_bus_get isn't EXPORTed! */ - usb_bus_get (&dummy_to_hcd (dum)->self); -#endif + usb_get_hcd (dummy_to_hcd (dum)); platform_set_drvdata (pdev, dum); device_create_file (&dum->gadget.dev, &dev_attr_function); diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 65ac9fef3a7c..215ce6d06394 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf) *buf = 0; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ehci = hcd_to_ehci (hcd); next = buf; size = PAGE_SIZE; @@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf) seen_count = 0; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ehci = hcd_to_ehci (hcd); next = buf; size = PAGE_SIZE; @@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf) static char label [] = ""; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ehci = hcd_to_ehci (hcd); next = buf; size = PAGE_SIZE; diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index da52609a9290..534d07dcb824 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -477,7 +477,7 @@ show_async (struct class_device *class_dev, char *buf) unsigned long flags; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); /* display control and bulk lists together, for simplicity */ @@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf) seen_count = 0; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); next = buf; size = PAGE_SIZE; @@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf) u32 rdata; bus = class_get_devdata(class_dev); - hcd = bus->hcpriv; + hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); regs = ohci->regs; next = buf; diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 275a66f83058..e0ed36cdfd8b 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -265,7 +265,6 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) ubus->mon_bus = NULL; mbus->u_bus = NULL; mb(); - // usb_bus_put(ubus); } /* @@ -297,10 +296,9 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) INIT_LIST_HEAD(&mbus->r_list); /* - * This usb_bus_get here is superfluous, because we receive - * a notification if usb_bus is about to be removed. + * We don't need to take a reference to ubus, because we receive + * a notification if the bus is about to be removed. */ - // usb_bus_get(ubus); mbus->u_bus = ubus; ubus->mon_bus = mbus; diff --git a/include/linux/usb.h b/include/linux/usb.h index 09661759621f..c66303285a45 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -280,7 +280,6 @@ struct usb_bus { struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct list_head bus_list; /* list of busses */ - void *hcpriv; /* Host Controller private data */ int bandwidth_allocated; /* on this bus: how much of the time * reserved for periodic (intr/iso) @@ -295,8 +294,6 @@ struct usb_bus { struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ struct class_device *class_dev; /* class device for this bus */ - struct kref kref; /* reference counting for this bus */ - void (*release)(struct usb_bus *bus); #if defined(CONFIG_USB_MON) struct mon_bus *mon_bus; /* non-null when associated */ -- cgit 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.c') 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 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.c') 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