aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r--drivers/usb/core/driver.c331
1 files changed, 215 insertions, 116 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 600d1bc8272..b9f7f90aef8 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;
}
@@ -952,13 +932,19 @@ static int autosuspend_check(struct usb_device *udev)
{
int i;
struct usb_interface *intf;
+ unsigned long suspend_time;
- /* 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;
+
+ suspend_time = udev->last_busy + udev->autosuspend_delay;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -974,6 +960,24 @@ static int autosuspend_check(struct usb_device *udev)
}
}
}
+
+ /* If everything is okay but the device hasn't been idle for long
+ * enough, queue a delayed autosuspend request.
+ */
+ if (time_after(suspend_time, jiffies)) {
+ if (!timer_pending(&udev->autosuspend.timer)) {
+
+ /* The value of jiffies may change between the
+ * time_after() comparison above and the subtraction
+ * below. That's okay; the system behaves sanely
+ * when a timer is registered for the present moment
+ * or for the past.
+ */
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ suspend_time - jiffies);
+ }
+ return -EAGAIN;
+ }
return 0;
}
@@ -981,7 +985,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,26 +1023,25 @@ 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;
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;
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED)
+ goto done;
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->auto_pm) {
status = autosuspend_check(udev);
if (status < 0)
- return status;
+ goto done;
}
+ cancel_delayed_work(&udev->autosuspend);
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
@@ -1063,6 +1066,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
} else if (parent)
usb_autosuspend_device(parent);
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1095,7 +1099,7 @@ 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;
@@ -1103,11 +1107,17 @@ int usb_resume_both(struct usb_device *udev)
struct usb_device *parent = udev->parent;
cancel_delayed_work(&udev->autosuspend);
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ status = -ENODEV;
+ goto done;
+ }
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
+ if (udev->auto_pm && udev->autoresume_disabled) {
+ status = -EPERM;
+ goto done;
+ }
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
@@ -1153,6 +1163,7 @@ int usb_resume_both(struct usb_device *udev)
}
}
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1167,20 +1178,34 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
int status = 0;
usb_pm_lock(udev);
+ udev->auto_pm = 1;
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 (udev->state == USB_STATE_SUSPENDED)
+ 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);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
usb_pm_unlock(udev);
return status;
}
+/* 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_autopm_do_device(udev, 0);
+}
+
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
@@ -1211,6 +1236,26 @@ void usb_autosuspend_device(struct usb_device *udev)
}
/**
+ * 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
*
@@ -1252,15 +1297,20 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
+ udev->auto_pm = 1;
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 (udev->state == USB_STATE_SUSPENDED)
+ 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);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
}
usb_pm_unlock(udev);
return status;
@@ -1319,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
* 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).
+ *
+ * The routine increments @intf's usage counter. (However if the
+ * autoresume fails then the counter is re-decremented.) 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
@@ -1371,50 +1424,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
};