diff options
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r-- | drivers/usb/core/driver.c | 304 |
1 files changed, 195 insertions, 109 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 113e484c763..d6eb5ce1dd1 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -205,7 +205,7 @@ static int usb_probe_interface(struct device *dev) if (id) { dev_dbg(dev, "%s - got id\n", __FUNCTION__); - error = usb_autoresume_device(udev, 1); + error = usb_autoresume_device(udev); if (error) return error; @@ -229,7 +229,7 @@ static int usb_probe_interface(struct device *dev) } else intf->condition = USB_INTERFACE_BOUND; - usb_autosuspend_device(udev, 1); + usb_autosuspend_device(udev); } return error; @@ -247,7 +247,7 @@ static int usb_unbind_interface(struct device *dev) /* Autoresume for set_interface call below */ udev = interface_to_usbdev(intf); - error = usb_autoresume_device(udev, 1); + error = usb_autoresume_device(udev); /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -265,7 +265,7 @@ static int usb_unbind_interface(struct device *dev) intf->needs_remote_wakeup = 0; if (!error) - usb_autosuspend_device(udev, 1); + usb_autosuspend_device(udev); return 0; } @@ -408,6 +408,16 @@ static int usb_match_one_id(struct usb_interface *interface, (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) 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. */ + if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && + !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL))) + return 0; + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && (id->bInterfaceClass != intf->desc.bInterfaceClass)) return 0; @@ -476,7 +486,17 @@ static int usb_match_one_id(struct usb_interface *interface, * most general; they let drivers bind to any interface on a * multiple-function device. Use the USB_INTERFACE_INFO * macro, or its siblings, to match class-per-interface style - * devices (as recorded in bDeviceClass). + * devices (as recorded in bInterfaceClass). + * + * Note that an entry created by USB_INTERFACE_INFO won't match + * any interface if the device class is set to Vendor-Specific. + * This is deliberate; according to the USB spec the meanings of + * the interface class/subclass/protocol for these devices are also + * vendor-specific, and hence matching against a standard product + * class wouldn't work anyway. If you really want to use an + * interface-based match for such a device, create a match record + * that also specifies the vendor ID. (Unforunately there isn't a + * standard macro for creating records like this.) * * Within those groups, remember that not all combinations are * meaningful. For example, don't give a product version range @@ -505,7 +525,7 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface, } EXPORT_SYMBOL_GPL_FUTURE(usb_match_id); -int usb_device_match(struct device *dev, struct device_driver *drv) +static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { @@ -790,7 +810,7 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister); #ifdef CONFIG_PM /* Caller has locked udev's pm_mutex */ -static int suspend_device(struct usb_device *udev, pm_message_t msg) +static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; int status = 0; @@ -817,7 +837,7 @@ done: } /* Caller has locked udev's pm_mutex */ -static int resume_device(struct usb_device *udev) +static int usb_resume_device(struct usb_device *udev) { struct usb_device_driver *udriver; int status = 0; @@ -843,7 +863,7 @@ done: } /* Caller has locked intf's usb_device's pm mutex */ -static int suspend_interface(struct usb_interface *intf, pm_message_t msg) +static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; int status = 0; @@ -880,7 +900,7 @@ done: } /* Caller has locked intf's usb_device's pm_mutex */ -static int resume_interface(struct usb_interface *intf) +static int usb_resume_interface(struct usb_interface *intf) { struct usb_driver *driver; int status = 0; @@ -920,6 +940,44 @@ done: return status; } +#ifdef CONFIG_USB_SUSPEND + +/* Internal routine to check whether we may autosuspend a device. */ +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. */ + udev->do_remote_wakeup = device_may_wakeup(&udev->dev); + if (udev->pm_usage_cnt > 0) + return -EBUSY; + if (udev->actconfig) { + for (i = 0; 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; + } + } + } + return 0; +} + +#else + +#define autosuspend_check(udev) 0 + +#endif + /** * usb_suspend_both - suspend a USB device and its interfaces * @udev: the usb_device to suspend @@ -971,52 +1029,34 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) 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; - } + status = autosuspend_check(udev); + if (status < 0) + return status; } /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { for (; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; - status = suspend_interface(intf, msg); + status = usb_suspend_interface(intf, msg); if (status != 0) break; } } if (status == 0) - status = suspend_device(udev, msg); + status = usb_suspend_device(udev, msg); /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { while (--i >= 0) { intf = udev->actconfig->interface[i]; - resume_interface(intf); + usb_resume_interface(intf); } /* If the suspend succeeded, propagate it up the tree */ } else if (parent) - usb_autosuspend_device(parent, 0); + usb_autosuspend_device(parent); // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; @@ -1064,9 +1104,25 @@ int usb_resume_both(struct usb_device *udev) /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { if (parent) { - usb_pm_lock(parent); - parent->auto_pm = 1; - status = usb_resume_both(parent); + status = usb_autoresume_device(parent); + if (status == 0) { + status = usb_resume_device(udev); + if (status) { + usb_autosuspend_device(parent); + + /* It's possible usb_resume_device() + * failed after the port was + * unsuspended, causing udev to be + * logically disconnected. We don't + * want usb_disconnect() to autosuspend + * the parent again, so tell it that + * udev disconnected while still + * suspended. */ + if (udev->state == + USB_STATE_NOTATTACHED) + udev->discon_suspended = 1; + } + } } else { /* We can't progagate beyond the USB subsystem, @@ -1075,24 +1131,20 @@ int usb_resume_both(struct usb_device *udev) if (udev->dev.parent->power.power_state.event != PM_EVENT_ON) status = -EHOSTUNREACH; - } - if (status == 0) - status = resume_device(udev); - if (parent) - usb_pm_unlock(parent); + else + status = usb_resume_device(udev); + } } else { /* Needed only for setting udev->dev.power.power_state.event * and for possible debugging message. */ - status = resume_device(udev); + status = usb_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); + usb_resume_interface(intf); } } @@ -1102,39 +1154,53 @@ int usb_resume_both(struct usb_device *udev) #ifdef CONFIG_USB_SUSPEND +/* Internal routine to adjust a device's usage counter and change + * its autosuspend state. + */ +static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) +{ + int status = 0; + + usb_pm_lock(udev); + udev->pm_usage_cnt += inc_usage_cnt; + WARN_ON(udev->pm_usage_cnt < 0); + if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { + udev->auto_pm = 1; + status = usb_resume_both(udev); + if (status != 0) + 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); + usb_pm_unlock(udev); + return status; +} + /** * 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. + * @udev's usage counter is decremented. If it or any of the usage counters + * for an active interface is greater than 0, no autosuspend request will be + * queued. (If an interface driver does not support autosuspend then its + * usage counter is permanently positive.) Furthermore, 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) +void usb_autosuspend_device(struct usb_device *udev) { - usb_pm_lock(udev); - udev->pm_usage_cnt -= dec_usage_cnt; - if (udev->pm_usage_cnt <= 0) - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); - usb_pm_unlock(udev); + int status; + + status = usb_autopm_do_device(udev, -1); // dev_dbg(&udev->dev, "%s: cnt %d\n", // __FUNCTION__, udev->pm_usage_cnt); } @@ -1142,44 +1208,59 @@ void usb_autosuspend_device(struct usb_device *udev, int dec_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). + * and needs to guarantee that it is not suspended. No autosuspend will + * occur until usb_autosuspend_device is called. (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 or when a remote-wakeup + * request is received. * - * @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. + * @udev's usage counter is incremented to prevent subsequent autosuspends. + * However if the autoresume fails then the usage counter is re-decremented. * * 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 usb_autoresume_device(struct usb_device *udev) { int status; - usb_pm_lock(udev); - udev->pm_usage_cnt += inc_usage_cnt; - udev->auto_pm = 1; - status = usb_resume_both(udev); - if (status != 0) - udev->pm_usage_cnt -= inc_usage_cnt; - usb_pm_unlock(udev); + status = usb_autopm_do_device(udev, 1); // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, udev->pm_usage_cnt); return status; } +/* Internal routine to adjust an interface's usage counter and change + * its device's autosuspend state. + */ +static int usb_autopm_do_interface(struct usb_interface *intf, + int inc_usage_cnt) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status = 0; + + usb_pm_lock(udev); + if (intf->condition == USB_INTERFACE_UNBOUND) + status = -ENODEV; + else { + intf->pm_usage_cnt += inc_usage_cnt; + if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { + udev->auto_pm = 1; + status = usb_resume_both(udev); + if (status != 0) + 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); + } + usb_pm_unlock(udev); + return status; +} + /** * usb_autopm_put_interface - decrement a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be decremented @@ -1213,17 +1294,11 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) */ void usb_autopm_put_interface(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); + int status; - usb_pm_lock(udev); - if (intf->condition != USB_INTERFACE_UNBOUND && - --intf->pm_usage_cnt <= 0) { - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); - } - usb_pm_unlock(udev); - // dev_dbg(&intf->dev, "%s: cnt %d\n", - // __FUNCTION__, intf->pm_usage_cnt); + status = usb_autopm_do_interface(intf, -1); + // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", + // __FUNCTION__, status, intf->pm_usage_cnt); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface); @@ -1260,26 +1335,37 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); */ int usb_autopm_get_interface(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); - int status; + int status; - usb_pm_lock(udev); - 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; - } - usb_pm_unlock(udev); + status = usb_autopm_do_interface(intf, 1); // 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); +/** + * usb_autopm_set_interface - set a USB interface's autosuspend state + * @intf: the usb_interface whose state should be set + * + * This routine sets the autosuspend state of @intf's device according + * to @intf's usage counter, which the caller must have set previously. + * If the counter is <= 0, the device is autosuspended (if it isn't + * already suspended and if nothing else prevents the autosuspend). If + * the counter is > 0, the device is autoresumed (if it isn't already + * awake). + */ +int usb_autopm_set_interface(struct usb_interface *intf) +{ + int status; + + status = usb_autopm_do_interface(intf, 0); + // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", + // __FUNCTION__, status, intf->pm_usage_cnt); + return status; +} +EXPORT_SYMBOL_GPL(usb_autopm_set_interface); + #endif /* CONFIG_USB_SUSPEND */ static int usb_suspend(struct device *dev, pm_message_t message) |