USB: add hex/bcd detection to usb modalias generation
Nathaniel McCallum [Thu, 19 Nov 2009 01:11:23 +0000 (20:11 -0500)]
The current code to generate usb modaliases from usb_device_id assumes
that the device's bcdDevice descriptor will actually be in BCD format.
While this should be a sane assumption, some devices don't follow spec
and just use plain old hex.  This causes drivers for these devices to
generate invalid modalias lines which will never actually match for the
hardware.

The following patch adds hex support for bcdDevice in file2alias.c by
detecting when a driver uses a hex formatted bcdDevice_(lo|hi) and
adjusts the output to hex format accordingly.

Drivers for devices which have bcdDevice conforming to BCD will have no
change in modalias output.  Drivers for devices which don't conform
(i.e. ibmcam) should now generate valid modaliases.

EXAMPLE OUTPUT (ibmcam; space added to highlight change)
    Old: usb:v0545p800D d030[10-9] dc*dsc*dp*ic*isc*ip*
    New: usb:v0545p800D d030a      dc*dsc*dp*ic*isc*ip*

Signed-off-by: Nathaniel McCallum <nathaniel@natemccallum.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

scripts/mod/file2alias.c

index 62a9025..e31f03a 100644 (file)
@@ -104,7 +104,7 @@ static void device_id_check(const char *modname, const char *device_id,
 static void do_usb_entry(struct usb_device_id *id,
                         unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
                         unsigned char range_lo, unsigned char range_hi,
-                        struct module *mod)
+                        unsigned char max, struct module *mod)
 {
        char alias[500];
        strcpy(alias, "usb:");
@@ -118,9 +118,22 @@ static void do_usb_entry(struct usb_device_id *id,
                sprintf(alias + strlen(alias), "%0*X",
                        bcdDevice_initial_digits, bcdDevice_initial);
        if (range_lo == range_hi)
-               sprintf(alias + strlen(alias), "%u", range_lo);
-       else if (range_lo > 0 || range_hi < 9)
-               sprintf(alias + strlen(alias), "[%u-%u]", range_lo, range_hi);
+               sprintf(alias + strlen(alias), "%X", range_lo);
+       else if (range_lo > 0 || range_hi < max) {
+               if (range_lo > 0x9 || range_hi < 0xA)
+                       sprintf(alias + strlen(alias),
+                               "[%X-%X]",
+                               range_lo,
+                               range_hi);
+               else {
+                       sprintf(alias + strlen(alias),
+                               range_lo < 0x9 ? "[%X-9" : "[%X",
+                               range_lo);
+                       sprintf(alias + strlen(alias),
+                               range_hi > 0xA ? "a-%X]" : "%X]",
+                               range_lo);
+               }
+       }
        if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
                strcat(alias, "*");
 
@@ -150,7 +163,7 @@ static void do_usb_entry(struct usb_device_id *id,
 static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
 {
        unsigned int devlo, devhi;
-       unsigned char chi, clo;
+       unsigned char chi, clo, max;
        int ndigits;
 
        id->match_flags = TO_NATIVE(id->match_flags);
@@ -162,6 +175,17 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
        devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
                TO_NATIVE(id->bcdDevice_hi) : ~0x0U;
 
+       /* Figure out if this entry is in bcd or hex format */
+       max = 0x9; /* Default to decimal format */
+       for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) {
+               clo = (devlo >> (ndigits << 2)) & 0xf;
+               chi = ((devhi > 0x9999 ? 0x9999 : devhi) >> (ndigits << 2)) & 0xf;
+               if (clo > max || chi > max) {
+                       max = 0xf;
+                       break;
+               }
+       }
+
        /*
         * Some modules (visor) have empty slots as placeholder for
         * run-time specification that results in catch-all alias
@@ -173,21 +197,21 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
        for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) {
                clo = devlo & 0xf;
                chi = devhi & 0xf;
-               if (chi > 9)    /* it's bcd not hex */
-                       chi = 9;
+               if (chi > max)  /* If we are in bcd mode, truncate if necessary */
+                       chi = max;
                devlo >>= 4;
                devhi >>= 4;
 
                if (devlo == devhi || !ndigits) {
-                       do_usb_entry(id, devlo, ndigits, clo, chi, mod);
+                       do_usb_entry(id, devlo, ndigits, clo, chi, max, mod);
                        break;
                }
 
-               if (clo > 0)
-                       do_usb_entry(id, devlo++, ndigits, clo, 9, mod);
+               if (clo > 0x0)
+                       do_usb_entry(id, devlo++, ndigits, clo, max, max, mod);
 
-               if (chi < 9)
-                       do_usb_entry(id, devhi--, ndigits, 0, chi, mod);
+               if (chi < max)
+                       do_usb_entry(id, devhi--, ndigits, 0x0, chi, max, mod);
        }
 }