diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-03-03 15:16:04 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-04-25 00:16:33 -0400 |
commit | eb764c4be1e5db3ee34df5745e98cf2f148c7320 (patch) | |
tree | f434cf49540ab6ea2c7fae6490a693916c2e5501 | |
parent | feccc30d90155bcbc937f87643182a43d25873eb (diff) |
USB: check serial-number string after device reset
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>
-rw-r--r-- | Documentation/usb/persist.txt | 8 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 65 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 2 |
3 files changed, 55 insertions, 20 deletions
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt index bea58dbd30fe..d56cb1a11550 100644 --- a/Documentation/usb/persist.txt +++ b/Documentation/usb/persist.txt | |||
@@ -136,10 +136,10 @@ aren't guaranteed to be 100% accurate. | |||
136 | 136 | ||
137 | If you replace one USB device with another of the same type (same | 137 | If you replace one USB device with another of the same type (same |
138 | manufacturer, same IDs, and so on) there's an excellent chance the | 138 | manufacturer, same IDs, and so on) there's an excellent chance the |
139 | kernel won't detect the change. Serial numbers and other strings are | 139 | kernel won't detect the change. The serial number string and other |
140 | not compared. In many cases it wouldn't help if they were, because | 140 | descriptors are compared with the kernel's stored values, but this |
141 | manufacturers frequently omit serial numbers entirely in their | 141 | might not help since manufacturers frequently omit serial numbers |
142 | devices. | 142 | entirely in their devices. |
143 | 143 | ||
144 | Furthermore it's quite possible to leave a USB device exactly the same | 144 | Furthermore it's quite possible to leave a USB device exactly the same |
145 | while changing its media. If you replace the flash memory card in a | 145 | while changing its media. If you replace the flash memory card in a |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6dc589955d75..9fc5179dfc60 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -3010,16 +3010,36 @@ void usb_hub_cleanup(void) | |||
3010 | usb_deregister(&hub_driver); | 3010 | usb_deregister(&hub_driver); |
3011 | } /* usb_hub_cleanup() */ | 3011 | } /* usb_hub_cleanup() */ |
3012 | 3012 | ||
3013 | static int config_descriptors_changed(struct usb_device *udev) | 3013 | static int descriptors_changed(struct usb_device *udev, |
3014 | { | 3014 | struct usb_device_descriptor *old_device_descriptor) |
3015 | unsigned index; | 3015 | { |
3016 | unsigned len = 0; | 3016 | int changed = 0; |
3017 | struct usb_config_descriptor *buf; | 3017 | unsigned index; |
3018 | unsigned serial_len = 0; | ||
3019 | unsigned len; | ||
3020 | unsigned old_length; | ||
3021 | int length; | ||
3022 | char *buf; | ||
3023 | |||
3024 | if (memcmp(&udev->descriptor, old_device_descriptor, | ||
3025 | sizeof(*old_device_descriptor)) != 0) | ||
3026 | return 1; | ||
3027 | |||
3028 | /* Since the idVendor, idProduct, and bcdDevice values in the | ||
3029 | * device descriptor haven't changed, we will assume the | ||
3030 | * Manufacturer and Product strings haven't changed either. | ||
3031 | * But the SerialNumber string could be different (e.g., a | ||
3032 | * different flash card of the same brand). | ||
3033 | */ | ||
3034 | if (udev->serial) | ||
3035 | serial_len = strlen(udev->serial) + 1; | ||
3018 | 3036 | ||
3037 | len = serial_len; | ||
3019 | for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { | 3038 | for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { |
3020 | if (len < le16_to_cpu(udev->config[index].desc.wTotalLength)) | 3039 | old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); |
3021 | len = le16_to_cpu(udev->config[index].desc.wTotalLength); | 3040 | len = max(len, old_length); |
3022 | } | 3041 | } |
3042 | |||
3023 | buf = kmalloc(len, GFP_NOIO); | 3043 | buf = kmalloc(len, GFP_NOIO); |
3024 | if (buf == NULL) { | 3044 | if (buf == NULL) { |
3025 | dev_err(&udev->dev, "no mem to re-read configs after reset\n"); | 3045 | 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) | |||
3027 | return 1; | 3047 | return 1; |
3028 | } | 3048 | } |
3029 | for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { | 3049 | for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { |
3030 | int length; | 3050 | old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); |
3031 | int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); | ||
3032 | |||
3033 | length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, | 3051 | length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, |
3034 | old_length); | 3052 | old_length); |
3035 | if (length < old_length) { | 3053 | if (length != old_length) { |
3036 | dev_dbg(&udev->dev, "config index %d, error %d\n", | 3054 | dev_dbg(&udev->dev, "config index %d, error %d\n", |
3037 | index, length); | 3055 | index, length); |
3056 | changed = 1; | ||
3038 | break; | 3057 | break; |
3039 | } | 3058 | } |
3040 | if (memcmp (buf, udev->rawdescriptors[index], old_length) | 3059 | if (memcmp (buf, udev->rawdescriptors[index], old_length) |
3041 | != 0) { | 3060 | != 0) { |
3042 | dev_dbg(&udev->dev, "config index %d changed (#%d)\n", | 3061 | dev_dbg(&udev->dev, "config index %d changed (#%d)\n", |
3043 | index, buf->bConfigurationValue); | 3062 | index, |
3063 | ((struct usb_config_descriptor *) buf)-> | ||
3064 | bConfigurationValue); | ||
3065 | changed = 1; | ||
3044 | break; | 3066 | break; |
3045 | } | 3067 | } |
3046 | } | 3068 | } |
3069 | |||
3070 | if (!changed && serial_len) { | ||
3071 | length = usb_string(udev, udev->descriptor.iSerialNumber, | ||
3072 | buf, serial_len); | ||
3073 | if (length + 1 != serial_len) { | ||
3074 | dev_dbg(&udev->dev, "serial string error %d\n", | ||
3075 | length); | ||
3076 | changed = 1; | ||
3077 | } else if (memcmp(buf, udev->serial, length) != 0) { | ||
3078 | dev_dbg(&udev->dev, "serial string changed\n"); | ||
3079 | changed = 1; | ||
3080 | } | ||
3081 | } | ||
3082 | |||
3047 | kfree(buf); | 3083 | kfree(buf); |
3048 | return index != udev->descriptor.bNumConfigurations; | 3084 | return changed; |
3049 | } | 3085 | } |
3050 | 3086 | ||
3051 | /** | 3087 | /** |
@@ -3118,8 +3154,7 @@ int usb_reset_device(struct usb_device *udev) | |||
3118 | goto re_enumerate; | 3154 | goto re_enumerate; |
3119 | 3155 | ||
3120 | /* Device might have changed firmware (DFU or similar) */ | 3156 | /* Device might have changed firmware (DFU or similar) */ |
3121 | if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor) | 3157 | if (descriptors_changed(udev, &descriptor)) { |
3122 | || config_descriptors_changed (udev)) { | ||
3123 | dev_info(&udev->dev, "device firmware changed\n"); | 3158 | dev_info(&udev->dev, "device firmware changed\n"); |
3124 | udev->descriptor = descriptor; /* for disconnect() calls */ | 3159 | udev->descriptor = descriptor; /* for disconnect() calls */ |
3125 | goto re_enumerate; | 3160 | goto re_enumerate; |
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index c311f67b7f08..a3695b5115ff 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -784,7 +784,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) | |||
784 | if (size <= 0 || !buf || !index) | 784 | if (size <= 0 || !buf || !index) |
785 | return -EINVAL; | 785 | return -EINVAL; |
786 | buf[0] = 0; | 786 | buf[0] = 0; |
787 | tbuf = kmalloc(256, GFP_KERNEL); | 787 | tbuf = kmalloc(256, GFP_NOIO); |
788 | if (!tbuf) | 788 | if (!tbuf) |
789 | return -ENOMEM; | 789 | return -ENOMEM; |
790 | 790 | ||