ChangeSet 1.1337.3.6, 2003/10/23 17:03:25-07:00, david-b@pacbell.net

[PATCH] USB: usbcore, better heuristic for choosing configs

Until now, the Linux-USB core has always chosen the first device
configuration, even when there was a choice.  In 2.4 kernels,
device driver probe() routines were allowed to override that
initial policy decisions.  But 2.6 kernels can't do that from
probe() routines, causing problems with some CDC-ACM modems
where the first config uses MSFT-proprietary protocols.

This patch switches to a smarter heuristic:  Linux now prefers
standard interface classes when there's a choice.  So those
CDC-ACM modems don't need a "write bConfigurationValue in sysfs"
step when they are connected; they act just like on 2.4 kernels.
(And sysfs can still be used to handle any problem cases.)


 drivers/usb/core/usb.c |   21 +++++++++++++++++----
 1 files changed, 17 insertions(+), 4 deletions(-)


diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Mon Dec 29 14:27:37 2003
+++ b/drivers/usb/core/usb.c	Mon Dec 29 14:27:37 2003
@@ -991,6 +991,7 @@
 	int err = -EINVAL;
 	int i;
 	int j;
+	int config;
 
 	/*
 	 * Set the driver for the usb device to point to the "generic" driver.
@@ -1105,18 +1106,30 @@
 
 	/* choose and set the configuration. that registers the interfaces
 	 * with the driver core, and lets usb device drivers bind to them.
+	 * NOTE:  should interact with hub power budgeting.
 	 */
+	config = dev->config[0].desc.bConfigurationValue;
 	if (dev->descriptor.bNumConfigurations != 1) {
+		for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+			/* heuristic:  Linux is more likely to have class
+			 * drivers, so avoid vendor-specific interfaces.
+			 */
+			if (dev->config[i].interface[0]->altsetting
+						->desc.bInterfaceClass
+					== USB_CLASS_VENDOR_SPEC)
+				continue;
+			config = dev->config[i].desc.bConfigurationValue;
+			break;
+		}
 		dev_info(&dev->dev,
 			"configuration #%d chosen from %d choices\n",
-			dev->config[0].desc.bConfigurationValue,
+			config,
 			dev->descriptor.bNumConfigurations);
 	}
-	err = usb_set_configuration(dev,
-			dev->config[0].desc.bConfigurationValue);
+	err = usb_set_configuration(dev, config);
 	if (err) {
 		dev_err(&dev->dev, "can't set config #%d, error %d\n",
-			dev->config[0].desc.bConfigurationValue, err);
+			config, err);
 		goto fail;
 	}
 
