summary refs log tree commit diff
path: root/drivers/usb/core/driver.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-12-05 14:37:56 +0000
committerDavid Howells <dhowells@warthog.cambridge.redhat.com>2006-12-05 14:37:56 +0000
commit4c1ac1b49122b805adfa4efc620592f68dccf5db (patch)
tree87557f4bc2fd4fe65b7570489c2f610c45c0adcd /drivers/usb/core/driver.c
parentc4028958b6ecad064b1a6303a6a5906d4fe48d73 (diff)
parentd916faace3efc0bf19fe9a615a1ab8fa1a24cd93 (diff)
downloadlinux-4c1ac1b49122b805adfa4efc620592f68dccf5db.tar.gz
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts:

	drivers/infiniband/core/iwcm.c
	drivers/net/chelsio/cxgb2.c
	drivers/net/wireless/bcm43xx/bcm43xx_main.c
	drivers/net/wireless/prism54/islpci_eth.c
	drivers/usb/core/hub.h
	drivers/usb/input/hid-core.c
	net/core/netpoll.c

Fix up merge failures with Linus's head and fix new compilation failures.

Signed-Off-By: David Howells <dhowells@redhat.com>
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r--drivers/usb/core/driver.c304
1 files changed, 195 insertions, 109 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 113e484c763e..d6eb5ce1dd1d 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)