X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fdriver.c;h=593386eb974dc6f34b12d2419ae0357b03a69f4b;hb=9f8b17e643fe6aa505629658445849397bda4e4f;hp=600d1bc8272a9503caf399171775c3bf05bb4ff9;hpb=fe6af6faec078ec8137631f24cb541f9918244f0;p=linux-2.6-omap-h63xx.git diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 600d1bc8272..593386eb974 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -287,9 +287,9 @@ static int usb_unbind_interface(struct device *dev) * 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. + * Callers must own the device lock, so driver probe() entries don't need + * extra locking, but other call contexts may need to explicitly claim that + * lock. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) @@ -330,9 +330,9 @@ EXPORT_SYMBOL(usb_driver_claim_interface); * 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. + * Callers must own the device lock, so driver disconnect() entries don't + * need extra locking, but other call contexts may need to explicitly claim + * that lock. */ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) @@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver, EXPORT_SYMBOL(usb_driver_release_interface); /* returns 0 if no match, 1 if match */ -int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id) +int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) { - struct usb_host_interface *intf; - struct usb_device *dev; - - /* proc_connectinfo in devio.c may call us with id == NULL. */ - if (id == NULL) - return 0; - - intf = interface->cur_altsetting; - dev = interface_to_usbdev(interface); - if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) return 0; @@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface, (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) return 0; + return 1; +} + +/* returns 0 if no match, 1 if match */ +int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *intf; + struct usb_device *dev; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return 0; + + intf = interface->cur_altsetting; + dev = interface_to_usbdev(interface); + + if (!usb_match_device(dev, id)) + return 0; + /* The interface class, subclass, and protocol should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ @@ -565,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) } #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; @@ -591,13 +587,11 @@ 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)) { + if (is_usb_device(dev)) usb_dev = to_usb_device(dev); - alt = NULL; - } else { - intf = to_usb_interface(dev); + else { + struct usb_interface *intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); - alt = intf->cur_altsetting; } if (usb_dev->devnum < 0) { @@ -612,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, #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 + * act as usermode drivers. */ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, @@ -641,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM; - if (!is_usb_device(dev)) { - - if (add_uevent_var(envp, num_envp, &i, + 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; + "BUSNUM=%03d", + usb_dev->bus->busnum)) + 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), - 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; - } + "DEVNUM=%03d", + usb_dev->devnum)) + 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) + int num_envp, char *buffer, int buffer_size) { return -ENODEV; } - #endif /* CONFIG_HOTPLUG */ /** @@ -743,6 +720,7 @@ 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. + * @mod_name: module name string * * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is @@ -862,8 +840,10 @@ static int usb_resume_device(struct usb_device *udev) done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) + if (status == 0) { + udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; + } return status; } @@ -953,12 +933,16 @@ static int autosuspend_check(struct usb_device *udev) int i; struct usb_interface *intf; - /* For autosuspend, fail fast if anything is in use. - * Also fail if any interfaces require remote wakeup but it - * isn't available. */ + /* For autosuspend, fail fast if anything is in use or autosuspend + * is disabled. Also fail if any interfaces require remote wakeup + * but it isn't available. + */ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; + if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) + return -EPERM; + if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -981,7 +965,7 @@ static int autosuspend_check(struct usb_device *udev) #define autosuspend_check(udev) 0 -#endif +#endif /* CONFIG_USB_SUSPEND */ /** * usb_suspend_both - suspend a USB device and its interfaces @@ -1019,7 +1003,7 @@ static int autosuspend_check(struct usb_device *udev) * * This routine can run only in process context. */ -int usb_suspend_both(struct usb_device *udev, pm_message_t msg) +static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i = 0; @@ -1095,13 +1079,15 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * * This routine can run only in process context. */ -int usb_resume_both(struct usb_device *udev) +static int usb_resume_both(struct usb_device *udev) { int status = 0; int i; struct usb_interface *intf; struct usb_device *parent = udev->parent; + if (udev->auto_pm && udev->autoresume_disabled) + return -EPERM; cancel_delayed_work(&udev->autosuspend); if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; @@ -1159,6 +1145,18 @@ int usb_resume_both(struct usb_device *udev) #ifdef CONFIG_USB_SUSPEND +/* usb_autosuspend_work - callback routine to autosuspend a USB device */ +void usb_autosuspend_work(struct work_struct *work) +{ + struct usb_device *udev = + container_of(work, struct usb_device, autosuspend.work); + + usb_pm_lock(udev); + udev->auto_pm = 1; + usb_suspend_both(udev, PMSG_SUSPEND); + usb_pm_unlock(udev); +} + /* Internal routine to adjust a device's usage counter and change * its autosuspend state. */ @@ -1176,7 +1174,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) udev->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); usb_pm_unlock(udev); return status; } @@ -1210,6 +1208,26 @@ void usb_autosuspend_device(struct usb_device *udev) // __FUNCTION__, udev->pm_usage_cnt); } +/** + * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces + * @udev: the usb_device to autosuspend + * + * This routine should be called when a core subsystem thinks @udev may + * be ready to autosuspend. + * + * @udev's usage counter left unchanged. If it or any of the usage counters + * for an active interface is greater than 0, or autosuspend is not allowed + * for any other reason, no autosuspend request will be queued. + * + * This routine can run only in process context. + */ +void usb_try_autosuspend_device(struct usb_device *udev) +{ + usb_autopm_do_device(udev, 0); + // 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 @@ -1260,7 +1278,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf, intf->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); } usb_pm_unlock(udev); return status; @@ -1371,50 +1389,96 @@ int usb_autopm_set_interface(struct usb_interface *intf) } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); +#else + +void usb_autosuspend_work(struct work_struct *work) +{} + #endif /* CONFIG_USB_SUSPEND */ -static int usb_suspend(struct device *dev, pm_message_t message) +/** + * usb_external_suspend_device - external suspend of a USB device and its interfaces + * @udev: the usb_device to suspend + * @msg: Power Management message describing this state transition + * + * This routine handles external suspend requests: ones not generated + * internally by a USB driver (autosuspend) but rather coming from the user + * (via sysfs) or the PM core (system sleep). The suspend will be carried + * out regardless of @udev's usage counter or those of its interfaces, + * and regardless of whether or not remote wakeup is enabled. Of course, + * interface drivers still have the option of failing the suspend (if + * there are unsuspended children, for example). + * + * The caller must hold @udev's device lock. + */ +int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_suspend_both(udev, message); - usb_pm_unlock(udev); - } else - status = 0; + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); + usb_pm_unlock(udev); return status; } -static int usb_resume(struct device *dev) +/** + * usb_external_resume_device - external resume of a USB device and its interfaces + * @udev: the usb_device to resume + * + * This routine handles external resume requests: ones not generated + * internally by a USB driver (autoresume) but rather coming from the user + * (via sysfs), the PM core (system resume), or the device itself (remote + * wakeup). @udev's usage counter is unaffected. + * + * The caller must hold @udev's device lock. + */ +int usb_external_resume_device(struct usb_device *udev) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_resume_both(udev); - usb_pm_unlock(udev); + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_resume_both(udev); + usb_pm_unlock(udev); - /* Rebind drivers that had no suspend method? */ - } else - status = 0; + /* Now that the device is awake, we can start trying to autosuspend + * it again. */ + if (status == 0) + usb_try_autosuspend_device(udev); return status; } +static int usb_suspend(struct device *dev, pm_message_t message) +{ + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + return usb_external_suspend_device(to_usb_device(dev), message); +} + +static int usb_resume(struct device *dev) +{ + struct usb_device *udev; + + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + udev = to_usb_device(dev); + if (udev->autoresume_disabled) + return -EPERM; + return usb_external_resume_device(udev); +} + +#else + +#define usb_suspend NULL +#define usb_resume NULL + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, -#ifdef CONFIG_PM .suspend = usb_suspend, .resume = usb_resume, -#endif };