USB: check serial-number string after device reset
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 3 Mar 2008 20:16:04 +0000 (15:16 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 25 Apr 2008 04:16:33 +0000 (21:16 -0700)
This patch (as1048) extends the descriptor checking after a device is
reset.  Now the SerialNumber string descriptor is compared to its old
value, in addition to the device and configuration descriptors.

As a consequence, the kmalloc() call in usb_string() is now on the
error-handling pathway for usb-storage.  Hence its allocation type is
changed to GFO_NOIO.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Documentation/usb/persist.txt
drivers/usb/core/hub.c
drivers/usb/core/message.c

index bea58dbd30fe07249d662f8298b4f32a1aa82cd1..d56cb1a1155073987fc2ca108c766f03100d35cf 100644 (file)
@@ -136,10 +136,10 @@ aren't guaranteed to be 100% accurate.
 
 If you replace one USB device with another of the same type (same
 manufacturer, same IDs, and so on) there's an excellent chance the
 
 If you replace one USB device with another of the same type (same
 manufacturer, same IDs, and so on) there's an excellent chance the
-kernel won't detect the change.  Serial numbers and other strings are
-not compared.  In many cases it wouldn't help if they were, because
-manufacturers frequently omit serial numbers entirely in their
-devices.
+kernel won't detect the change.  The serial number string and other
+descriptors are compared with the kernel's stored values, but this
+might not help since manufacturers frequently omit serial numbers
+entirely in their devices.
 
 Furthermore it's quite possible to leave a USB device exactly the same
 while changing its media.  If you replace the flash memory card in a
 
 Furthermore it's quite possible to leave a USB device exactly the same
 while changing its media.  If you replace the flash memory card in a
index 6dc589955d75da4a8d5edc37e52f7e8aaedfffe8..9fc5179dfc60741e9659ec130db92b28f2740944 100644 (file)
@@ -3010,16 +3010,36 @@ void usb_hub_cleanup(void)
        usb_deregister(&hub_driver);
 } /* usb_hub_cleanup() */
 
        usb_deregister(&hub_driver);
 } /* usb_hub_cleanup() */
 
-static int config_descriptors_changed(struct usb_device *udev)
-{
-       unsigned                        index;
-       unsigned                        len = 0;
-       struct usb_config_descriptor    *buf;
+static int descriptors_changed(struct usb_device *udev,
+               struct usb_device_descriptor *old_device_descriptor)
+{
+       int             changed = 0;
+       unsigned        index;
+       unsigned        serial_len = 0;
+       unsigned        len;
+       unsigned        old_length;
+       int             length;
+       char            *buf;
+
+       if (memcmp(&udev->descriptor, old_device_descriptor,
+                       sizeof(*old_device_descriptor)) != 0)
+               return 1;
+
+       /* Since the idVendor, idProduct, and bcdDevice values in the
+        * device descriptor haven't changed, we will assume the
+        * Manufacturer and Product strings haven't changed either.
+        * But the SerialNumber string could be different (e.g., a
+        * different flash card of the same brand).
+        */
+       if (udev->serial)
+               serial_len = strlen(udev->serial) + 1;
 
 
+       len = serial_len;
        for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
        for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
-               if (len < le16_to_cpu(udev->config[index].desc.wTotalLength))
-                       len = le16_to_cpu(udev->config[index].desc.wTotalLength);
+               old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
+               len = max(len, old_length);
        }
        }
+
        buf = kmalloc(len, GFP_NOIO);
        if (buf == NULL) {
                dev_err(&udev->dev, "no mem to re-read configs after reset\n");
        buf = kmalloc(len, GFP_NOIO);
        if (buf == NULL) {
                dev_err(&udev->dev, "no mem to re-read configs after reset\n");
@@ -3027,25 +3047,41 @@ static int config_descriptors_changed(struct usb_device *udev)
                return 1;
        }
        for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
                return 1;
        }
        for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
-               int length;
-               int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
-
+               old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
                length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
                                old_length);
                length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
                                old_length);
-               if (length < old_length) {
+               if (length != old_length) {
                        dev_dbg(&udev->dev, "config index %d, error %d\n",
                                        index, length);
                        dev_dbg(&udev->dev, "config index %d, error %d\n",
                                        index, length);
+                       changed = 1;
                        break;
                }
                if (memcmp (buf, udev->rawdescriptors[index], old_length)
                                != 0) {
                        dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
                        break;
                }
                if (memcmp (buf, udev->rawdescriptors[index], old_length)
                                != 0) {
                        dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
-                               index, buf->bConfigurationValue);
+                               index,
+                               ((struct usb_config_descriptor *) buf)->
+                                       bConfigurationValue);
+                       changed = 1;
                        break;
                }
        }
                        break;
                }
        }
+
+       if (!changed && serial_len) {
+               length = usb_string(udev, udev->descriptor.iSerialNumber,
+                               buf, serial_len);
+               if (length + 1 != serial_len) {
+                       dev_dbg(&udev->dev, "serial string error %d\n",
+                                       length);
+                       changed = 1;
+               } else if (memcmp(buf, udev->serial, length) != 0) {
+                       dev_dbg(&udev->dev, "serial string changed\n");
+                       changed = 1;
+               }
+       }
+
        kfree(buf);
        kfree(buf);
-       return index != udev->descriptor.bNumConfigurations;
+       return changed;
 }
 
 /**
 }
 
 /**
@@ -3118,8 +3154,7 @@ int usb_reset_device(struct usb_device *udev)
                goto re_enumerate;
  
        /* Device might have changed firmware (DFU or similar) */
                goto re_enumerate;
  
        /* Device might have changed firmware (DFU or similar) */
-       if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor)
-                       || config_descriptors_changed (udev)) {
+       if (descriptors_changed(udev, &descriptor)) {
                dev_info(&udev->dev, "device firmware changed\n");
                udev->descriptor = descriptor;  /* for disconnect() calls */
                goto re_enumerate;
                dev_info(&udev->dev, "device firmware changed\n");
                udev->descriptor = descriptor;  /* for disconnect() calls */
                goto re_enumerate;
index c311f67b7f0824dba591b50d4b21f943827349b0..a3695b5115ff563b97ee96d4f2aee167dfb394e5 100644 (file)
@@ -784,7 +784,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
        if (size <= 0 || !buf || !index)
                return -EINVAL;
        buf[0] = 0;
        if (size <= 0 || !buf || !index)
                return -EINVAL;
        buf[0] = 0;
-       tbuf = kmalloc(256, GFP_KERNEL);
+       tbuf = kmalloc(256, GFP_NOIO);
        if (!tbuf)
                return -ENOMEM;
 
        if (!tbuf)
                return -ENOMEM;