From dcc9843349a236aad6520a5cb0f2e4fc5cdc356d Mon Sep 17 00:00:00 2001
From: David Brownell <david-b@pacbell.net>
Date: Fri, 5 Jul 2002 09:27:11 -0700
Subject: [PATCH] [PATCH] usb driverfs, +misc

This fixes a couple issues I noted when I finally spent some time
looking at the first version of driverfs support for usb:

- "name" fields (really descriptions) aren't very useful.

     * for devices, they always said "USB device 0000:0000"
         --> Now they'll only say that when there's
             nothing better to be said ...
         --> ... and it's really device 0000:0000!  It was
             using device descriptor fields before they were
             fetched from the device.
         --> Uses product and/or manufacturer strings, which
             most devices have, much like PCI uses the PCI ID
             database (when it's compiled in)

     * for interfaces, it was "figure out some name..."
         --> Now it combines the strings used in the
             usb_make_path() call with interface number
         --> Or in the remote chance a device provides
             an interface string, that's preferred.
         --> In general, I think the driver for each
             interface is best suited to describe it;
             I modified the hub driver to do so.

- "bus_id" field

     * For hub ports, it was wasting code: we know the port
       already, no need to search for it.  Plus, it used
       0-index ids not the 1-index ones matching physical
       labels on hubs, and other user-visible diagnostics.

     * For interfaces, it mixed the device address with the
       interface number ... producing unstable IDs that were
       moreover rather cryptic.  Changed:  "if0" now, using
       the interface ID (not index).

     * For busses, left "usb_bus" alone ... :)

- Adds two files exposing current configuration (for devices)
   and altsetting (for interfaces).

- I was getting a useless diagnostic from the hub driver,
   now it's less useless (it fully identifies the hub)
---
 drivers/usb/core/hub.c |  24 ++++----
 drivers/usb/core/usb.c | 133 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 133 insertions(+), 24 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 6138dc4d44a5..ad1422fabdfa 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -490,8 +490,11 @@ static void *hub_probe(struct usb_device *dev, unsigned int i,
 	list_add(&hub->hub_list, &hub_list);
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 
-	if (usb_hub_configure(hub, endpoint) >= 0)
+	if (usb_hub_configure(hub, endpoint) >= 0) {
+		strcpy (dev->actconfig->interface[i].dev.name,
+			"Hub/Port Status Changes");
 		return hub;
+	}
 
 	err("hub configuration failed for device at %s", dev->devpath);
 
@@ -637,7 +640,8 @@ static int usb_hub_port_status(struct usb_device *hub, int port,
 	if (portsts) {
 		ret = usb_get_port_status(hub, port + 1, portsts);
 		if (ret < 0)
-			err("%s(%s) failed (err = %d)", __FUNCTION__, hub->devpath, ret);
+			err("%s(%s-%s) failed (err = %d)", __FUNCTION__,
+				hub->bus->bus_name, hub->devpath, ret);
 		else {
 			*status = le16_to_cpu(portsts->wPortStatus);
 			*change = le16_to_cpu(portsts->wPortChange); 
@@ -884,19 +888,11 @@ static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port,
 		info("new USB device %s-%s, assigned address %d",
 			dev->bus->bus_name, dev->devpath, dev->devnum);
 
-		/* put the device in the global device tree */
+		/* put the device in the global device tree. the hub port
+		 * is the "bus_id"; hubs show in hierarchy like bridges
+		 */
 		dev->dev.parent = &dev->parent->dev;
-		sprintf (&dev->dev.name[0], "USB device %04x:%04x",
-			 dev->descriptor.idVendor,
-			 dev->descriptor.idProduct);
-		/* find the number of the port this device is connected to */
-		sprintf (&dev->dev.bus_id[0], "unknown_port_%03d", dev->devnum);
-		for (i = 0; i < USB_MAXCHILDREN; ++i) {
-			if (dev->parent->children[i] == dev) {
-				sprintf (&dev->dev.bus_id[0], "%02d", i);
-				break;
-			}
-		}
+		sprintf (&dev->dev.bus_id[0], "%d", port + 1);
 
 		/* Run it through the hoops (find a driver, etc) */
 		if (!usb_new_device(dev))
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 10591d54aebb..5ef9e9f56a5c 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -748,6 +748,46 @@ call_policy (char *verb, struct usb_device *dev)
 
 #endif	/* CONFIG_HOTPLUG */
 
+/* driverfs files */
+
+/* devices have one current configuration, with one
+ * or more interfaces that are used concurrently 
+ */
+static ssize_t
+show_config (struct device *dev, char *buf, size_t count, loff_t off)
+{
+	struct usb_device	*udev;
+
+	if (off)
+		return 0;
+	udev = list_entry (dev, struct usb_device, dev);
+	return sprintf (buf, "%u\n", udev->actconfig->bConfigurationValue);
+}
+static struct driver_file_entry usb_config_entry = {
+	name:	"configuration",
+	mode:	S_IRUGO,
+	show:	show_config,
+};
+
+/* interfaces have one current setting; alternates
+ * can have different endpoints and class info.
+ */
+static ssize_t
+show_altsetting (struct device *dev, char *buf, size_t count, loff_t off)
+{
+	struct usb_interface	*interface;
+
+	if (off)
+		return 0;
+	interface = list_entry (dev, struct usb_interface, dev);
+	return sprintf (buf, "%u\n", interface->altsetting->bAlternateSetting);
+}
+static struct driver_file_entry usb_altsetting_entry = {
+	name:	"altsetting",
+	mode:	S_IRUGO,
+	show:	show_altsetting,
+};
+
 
 /*
  * This entrypoint gets called for each new device.
@@ -760,15 +800,35 @@ static void usb_find_drivers(struct usb_device *dev)
 	unsigned rejected = 0;
 	unsigned claimed = 0;
 
+	/* FIXME should get called for each new configuration not just the
+	 * first one for a device. switching configs (or altesettings) should
+	 * undo driverfs and HCD state for the previous interfaces.
+	 */
 	for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
 		struct usb_interface *interface = &dev->actconfig->interface[ifnum];
+		struct usb_interface_descriptor *desc = interface->altsetting;
 		
 		/* register this interface with driverfs */
 		interface->dev.parent = &dev->dev;
 		interface->dev.bus = &usb_bus_type;
-		sprintf (&interface->dev.bus_id[0], "%03d%03d", dev->devnum,ifnum);
-		sprintf (&interface->dev.name[0], "figure out some name...");
+		sprintf (&interface->dev.bus_id[0], "if%d",
+			interface->altsetting->bInterfaceNumber);
+		if (!desc->iInterface
+				|| usb_string (dev, desc->iInterface,
+					interface->dev.name,
+					sizeof interface->dev.name) <= 0) {
+			/* typically devices won't bother with interface
+			 * descriptions; this is the normal case.  an
+			 * interface's driver might describe it better.
+			 * (also: iInterface is per-altsetting ...)
+			 */
+			sprintf (&interface->dev.name[0],
+				"usb-%s-%s interface %d",
+				dev->bus->bus_name, dev->devpath,
+				interface->altsetting->bInterfaceNumber);
+		}
 		device_register (&interface->dev);
+		device_create_file (&interface->dev, &usb_altsetting_entry);
 
 		/* if this interface hasn't already been claimed */
 		if (!usb_interface_claimed(interface)) {
@@ -1081,10 +1141,6 @@ void usb_connect(struct usb_device *dev)
 	}
 }
 
-/*
- * These are the actual routines to send
- * and receive control messages.
- */
 
 // hub-only!! ... and only exported for reset/reinit path.
 // otherwise used internally, for usb_new_device()
@@ -1096,6 +1152,64 @@ int usb_set_address(struct usb_device *dev)
 }
 
 
+/* improve on the default device description, if we can ... and
+ * while we're at it, maybe show the vendor and product strings.
+ */
+static void set_device_description (struct usb_device *dev)
+{
+	char	*buf, *here, *end;
+	int	mfgr = dev->descriptor.iManufacturer;
+	int	prod = dev->descriptor.iProduct;
+
+	/* set default; keep it if there are no strings */
+	sprintf (dev->dev.name, "USB device %04x:%04x",
+		 dev->descriptor.idVendor,
+		 dev->descriptor.idProduct);
+	if (!mfgr && !prod)
+		return;
+
+	if (!(buf = kmalloc(256, GFP_KERNEL)))
+		return;
+	here = dev->dev.name;
+	end = here + sizeof dev->dev.name - 2;
+	*end = 0;
+
+	/* much like pci ... describe as either:
+	 * - both strings:   'product descr (vendor descr)'
+	 * - product only:   'product descr (USB device vvvv:pppp)'
+	 * - vendor only:    'USB device vvvv:pppp (vendor descr)'
+	 * - neither string: 'USB device vvvv:pppp'
+	 */
+	if (prod && usb_string (dev, prod, buf, 256) > 0) {
+		strncpy (here, buf, end - here);
+#ifdef DEBUG
+		printk (KERN_INFO "Product: %s\n", buf);
+#endif
+	} else {
+		buf [0] = 0;
+		prod = -1;
+	}
+	here = strchr (here, 0);
+	if (mfgr && usb_string (dev, mfgr, buf, 256) > 0) {
+		*here++ = ' ';
+		*here++ = '(';
+		strncpy (here, buf, end - here - 1);
+		here = strchr (here, 0);
+		*here++ = ')';
+#ifdef DEBUG
+		printk (KERN_INFO "Manufacturer: %s\n", buf);
+#endif
+	} else {
+		if (prod != -1)
+			snprintf (here, end - here - 1,
+				" (USB device %04x:%04x)",
+				dev->descriptor.idVendor,
+				dev->descriptor.idProduct);
+		/* both strings unavailable, keep the default */
+	}
+	kfree(buf);
+}
+
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
@@ -1193,11 +1307,9 @@ int usb_new_device(struct usb_device *dev)
 
 	dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
 		dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+	set_device_description (dev);
+
 #ifdef DEBUG
-	if (dev->descriptor.iManufacturer)
-		usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
-	if (dev->descriptor.iProduct)
-		usb_show_string(dev, "Product", dev->descriptor.iProduct);
 	if (dev->descriptor.iSerialNumber)
 		usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
 #endif
@@ -1206,6 +1318,7 @@ int usb_new_device(struct usb_device *dev)
 	err = device_register (&dev->dev);
 	if (err)
 		return err;
+	device_create_file (&dev->dev, &usb_config_entry);
 
 	/* now that the basic setup is over, add a /proc/bus/usb entry */
 	usbfs_add_device(dev);
-- 
2.30.9