USB: handle bcd incrementation in usb modalias generation
authorNathaniel McCallum <nathaniel@natemccallum.com>
Thu, 19 Nov 2009 01:15:28 +0000 (20:15 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 19:55:20 +0000 (11:55 -0800)
This patch fixes a bug when incrementing/decrementing on a BCD formatted
integer (i.e. 0x09++ should be 0x10 not 0x0A).  It just adds a function
for incrementing/decrementing BCD integers by converting to decimal,
doing the increment/decrement and then converting back to BCD.

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

index e31f03a..6f426af 100644 (file)
@@ -160,6 +160,45 @@ static void do_usb_entry(struct usb_device_id *id,
                   "MODULE_ALIAS(\"%s\");\n", alias);
 }
 
+/* Handles increment/decrement of BCD formatted integers */
+/* Returns the previous value, so it works like i++ or i-- */
+static unsigned int incbcd(unsigned int *bcd,
+                          int inc,
+                          unsigned char max,
+                          size_t chars)
+{
+       unsigned int init = *bcd, i, j;
+       unsigned long long c, dec = 0;
+
+       /* If bcd is not in BCD format, just increment */
+       if (max > 0x9) {
+               *bcd += inc;
+               return init;
+       }
+
+       /* Convert BCD to Decimal */
+       for (i=0 ; i < chars ; i++) {
+               c = (*bcd >> (i << 2)) & 0xf;
+               c = c > 9 ? 9 : c; /* force to bcd just in case */
+               for (j=0 ; j < i ; j++)
+                       c = c * 10;
+               dec += c;
+       }
+
+       /* Do our increment/decrement */
+       dec += inc;
+       *bcd  = 0;
+
+       /* Convert back to BCD */
+       for (i=0 ; i < chars ; i++) {
+               for (c=1,j=0 ; j < i ; j++)
+                       c = c * 10;
+               c = (dec / c) % 10;
+               *bcd += c << (i << 2);
+       }
+       return init;
+}
+
 static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
 {
        unsigned int devlo, devhi;
@@ -208,10 +247,16 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
                }
 
                if (clo > 0x0)
-                       do_usb_entry(id, devlo++, ndigits, clo, max, max, mod);
+                       do_usb_entry(id,
+                                    incbcd(&devlo, 1, max,
+                                           sizeof(id->bcdDevice_lo) * 2),
+                                    ndigits, clo, max, max, mod);
 
                if (chi < max)
-                       do_usb_entry(id, devhi--, ndigits, 0x0, chi, max, mod);
+                       do_usb_entry(id,
+                                    incbcd(&devhi, -1, max,
+                                           sizeof(id->bcdDevice_lo) * 2),
+                                    ndigits, 0x0, chi, max, mod);
        }
 }