aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2012-07-19 06:39:13 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-19 18:44:58 -0400
commit80da2e0df5af700518611b7d1cc4fc9945bcaf95 (patch)
treec914e555d34b2339c49bd3f29ef5268b642664f5 /drivers/usb
parent66177cc10295ffdfc613c06f59b07a577d91ee82 (diff)
usb: Add quirk detection based on interface information
When a whole class of devices (possibly from a specific vendor, or across multiple vendors) require a quirk, explictly listing all devices in the class make the quirks table unnecessarily large. Fix this by allowing matching devices based on interface information. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/driver.c38
-rw-r--r--drivers/usb/core/hub.c10
-rw-r--r--drivers/usb/core/quirks.c93
-rw-r--r--drivers/usb/core/usb.h4
4 files changed, 106 insertions, 39 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 69781016a266..445455a4429b 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -607,22 +607,10 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
607} 607}
608 608
609/* returns 0 if no match, 1 if match */ 609/* returns 0 if no match, 1 if match */
610int usb_match_one_id(struct usb_interface *interface, 610int usb_match_one_id_intf(struct usb_device *dev,
611 const struct usb_device_id *id) 611 struct usb_host_interface *intf,
612 const struct usb_device_id *id)
612{ 613{
613 struct usb_host_interface *intf;
614 struct usb_device *dev;
615
616 /* proc_connectinfo in devio.c may call us with id == NULL. */
617 if (id == NULL)
618 return 0;
619
620 intf = interface->cur_altsetting;
621 dev = interface_to_usbdev(interface);
622
623 if (!usb_match_device(dev, id))
624 return 0;
625
626 /* The interface class, subclass, protocol and number should never be 614 /* The interface class, subclass, protocol and number should never be
627 * checked for a match if the device class is Vendor Specific, 615 * checked for a match if the device class is Vendor Specific,
628 * unless the match record specifies the Vendor ID. */ 616 * unless the match record specifies the Vendor ID. */
@@ -652,6 +640,26 @@ int usb_match_one_id(struct usb_interface *interface,
652 640
653 return 1; 641 return 1;
654} 642}
643
644/* returns 0 if no match, 1 if match */
645int usb_match_one_id(struct usb_interface *interface,
646 const struct usb_device_id *id)
647{
648 struct usb_host_interface *intf;
649 struct usb_device *dev;
650
651 /* proc_connectinfo in devio.c may call us with id == NULL. */
652 if (id == NULL)
653 return 0;
654
655 intf = interface->cur_altsetting;
656 dev = interface_to_usbdev(interface);
657
658 if (!usb_match_device(dev, id))
659 return 0;
660
661 return usb_match_one_id_intf(dev, intf, id);
662}
655EXPORT_SYMBOL_GPL(usb_match_one_id); 663EXPORT_SYMBOL_GPL(usb_match_one_id);
656 664
657/** 665/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 540f20bf9e22..821126eb8176 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2069,7 +2069,7 @@ static int usb_enumerate_device(struct usb_device *udev)
2069 if (err < 0) { 2069 if (err < 0) {
2070 dev_err(&udev->dev, "can't read configurations, error %d\n", 2070 dev_err(&udev->dev, "can't read configurations, error %d\n",
2071 err); 2071 err);
2072 goto fail; 2072 return err;
2073 } 2073 }
2074 } 2074 }
2075 if (udev->wusb == 1 && udev->authorized == 0) { 2075 if (udev->wusb == 1 && udev->authorized == 0) {
@@ -2085,8 +2085,12 @@ static int usb_enumerate_device(struct usb_device *udev)
2085 udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); 2085 udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
2086 } 2086 }
2087 err = usb_enumerate_device_otg(udev); 2087 err = usb_enumerate_device_otg(udev);
2088fail: 2088 if (err < 0)
2089 return err; 2089 return err;
2090
2091 usb_detect_interface_quirks(udev);
2092
2093 return 0;
2090} 2094}
2091 2095
2092static void set_usb_port_removable(struct usb_device *udev) 2096static void set_usb_port_removable(struct usb_device *udev)
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 32d3adc315f5..cbd15d1d25d3 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -15,17 +15,22 @@
15#include <linux/usb/quirks.h> 15#include <linux/usb/quirks.h>
16#include "usb.h" 16#include "usb.h"
17 17
18/* List of quirky USB devices. Please keep this list ordered by: 18/* Lists of quirky USB devices, split in device quirks and interface quirks.
19 * Device quirks are applied at the very beginning of the enumeration process,
20 * right after reading the device descriptor. They can thus only match on device
21 * information.
22 *
23 * Interface quirks are applied after reading all the configuration descriptors.
24 * They can match on both device and interface information.
25 *
26 * Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as
27 * interface quirks, as they only influence the enumeration process which is run
28 * before processing the interface quirks.
29 *
30 * Please keep the lists ordered by:
19 * 1) Vendor ID 31 * 1) Vendor ID
20 * 2) Product ID 32 * 2) Product ID
21 * 3) Class ID 33 * 3) Class ID
22 *
23 * as we want specific devices to be overridden first, and only after that, any
24 * class specific quirks.
25 *
26 * Right now the logic aborts if it finds a valid device in the table, we might
27 * want to change that in the future if it turns out that a whole class of
28 * devices is broken...
29 */ 34 */
30static const struct usb_device_id usb_quirk_list[] = { 35static const struct usb_device_id usb_quirk_list[] = {
31 /* CBM - Flash disk */ 36 /* CBM - Flash disk */
@@ -156,16 +161,53 @@ static const struct usb_device_id usb_quirk_list[] = {
156 { } /* terminating entry must be last */ 161 { } /* terminating entry must be last */
157}; 162};
158 163
159static const struct usb_device_id *find_id(struct usb_device *udev) 164static const struct usb_device_id usb_interface_quirk_list[] = {
165 { } /* terminating entry must be last */
166};
167
168static bool usb_match_any_interface(struct usb_device *udev,
169 const struct usb_device_id *id)
170{
171 unsigned int i;
172
173 for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
174 struct usb_host_config *cfg = &udev->config[i];
175 unsigned int j;
176
177 for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
178 struct usb_interface_cache *cache;
179 struct usb_host_interface *intf;
180
181 cache = cfg->intf_cache[j];
182 if (cache->num_altsetting == 0)
183 continue;
184
185 intf = &cache->altsetting[0];
186 if (usb_match_one_id_intf(udev, intf, id))
187 return true;
188 }
189 }
190
191 return false;
192}
193
194static u32 __usb_detect_quirks(struct usb_device *udev,
195 const struct usb_device_id *id)
160{ 196{
161 const struct usb_device_id *id = usb_quirk_list; 197 u32 quirks = 0;
162 198
163 for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || 199 for (; id->match_flags; id++) {
164 id->driver_info; id++) { 200 if (!usb_match_device(udev, id))
165 if (usb_match_device(udev, id)) 201 continue;
166 return id; 202
203 if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
204 !usb_match_any_interface(udev, id))
205 continue;
206
207 quirks |= (u32)(id->driver_info);
167 } 208 }
168 return NULL; 209
210 return quirks;
169} 211}
170 212
171/* 213/*
@@ -173,14 +215,10 @@ static const struct usb_device_id *find_id(struct usb_device *udev)
173 */ 215 */
174void usb_detect_quirks(struct usb_device *udev) 216void usb_detect_quirks(struct usb_device *udev)
175{ 217{
176 const struct usb_device_id *id = usb_quirk_list; 218 udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
177
178 id = find_id(udev);
179 if (id)
180 udev->quirks = (u32)(id->driver_info);
181 if (udev->quirks) 219 if (udev->quirks)
182 dev_dbg(&udev->dev, "USB quirks for this device: %x\n", 220 dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
183 udev->quirks); 221 udev->quirks);
184 222
185 /* For the present, all devices default to USB-PERSIST enabled */ 223 /* For the present, all devices default to USB-PERSIST enabled */
186#if 0 /* was: #ifdef CONFIG_PM */ 224#if 0 /* was: #ifdef CONFIG_PM */
@@ -197,3 +235,16 @@ void usb_detect_quirks(struct usb_device *udev)
197 udev->persist_enabled = 1; 235 udev->persist_enabled = 1;
198#endif /* CONFIG_PM */ 236#endif /* CONFIG_PM */
199} 237}
238
239void usb_detect_interface_quirks(struct usb_device *udev)
240{
241 u32 quirks;
242
243 quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
244 if (quirks == 0)
245 return;
246
247 dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
248 quirks);
249 udev->quirks |= quirks;
250}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 67875a89cfa1..acb103c5c391 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -26,6 +26,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
26extern int usb_deauthorize_device(struct usb_device *); 26extern int usb_deauthorize_device(struct usb_device *);
27extern int usb_authorize_device(struct usb_device *); 27extern int usb_authorize_device(struct usb_device *);
28extern void usb_detect_quirks(struct usb_device *udev); 28extern void usb_detect_quirks(struct usb_device *udev);
29extern void usb_detect_interface_quirks(struct usb_device *udev);
29extern int usb_remove_device(struct usb_device *udev); 30extern int usb_remove_device(struct usb_device *udev);
30 31
31extern int usb_get_device_descriptor(struct usb_device *dev, 32extern int usb_get_device_descriptor(struct usb_device *dev,
@@ -37,6 +38,9 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration);
37extern int usb_choose_configuration(struct usb_device *udev); 38extern int usb_choose_configuration(struct usb_device *udev);
38 39
39extern void usb_kick_khubd(struct usb_device *dev); 40extern void usb_kick_khubd(struct usb_device *dev);
41extern int usb_match_one_id_intf(struct usb_device *dev,
42 struct usb_host_interface *intf,
43 const struct usb_device_id *id);
40extern int usb_match_device(struct usb_device *dev, 44extern int usb_match_device(struct usb_device *dev,
41 const struct usb_device_id *id); 45 const struct usb_device_id *id);
42extern void usb_forced_unbind_intf(struct usb_interface *intf); 46extern void usb_forced_unbind_intf(struct usb_interface *intf);