|
2 | 2 | /* |
3 | 3 | */ |
4 | 4 |
|
| 5 | +#include <linux/cleanup.h> |
| 6 | +#include <linux/err.h> |
5 | 7 | #include <linux/init.h> |
6 | 8 | #include <linux/slab.h> |
| 9 | +#include <linux/string.h> |
7 | 10 | #include <linux/usb.h> |
8 | 11 | #include <linux/usb/audio.h> |
9 | 12 | #include <linux/usb/midi.h> |
@@ -2135,16 +2138,39 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, |
2135 | 2138 | /* |
2136 | 2139 | * driver behavior quirk flags |
2137 | 2140 | */ |
| 2141 | +struct usb_string_match { |
| 2142 | + const char *manufacturer; |
| 2143 | + const char *product; |
| 2144 | +}; |
| 2145 | + |
2138 | 2146 | struct usb_audio_quirk_flags_table { |
2139 | 2147 | u32 id; |
2140 | 2148 | u32 flags; |
| 2149 | + const struct usb_string_match *usb_string_match; |
2141 | 2150 | }; |
2142 | 2151 |
|
2143 | 2152 | #define DEVICE_FLG(vid, pid, _flags) \ |
2144 | 2153 | { .id = USB_ID(vid, pid), .flags = (_flags) } |
2145 | 2154 | #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags) |
2146 | 2155 |
|
| 2156 | +/* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. */ |
| 2157 | +#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags) \ |
| 2158 | +{ \ |
| 2159 | + .id = USB_ID(vid, pid), \ |
| 2160 | + .usb_string_match = &(const struct usb_string_match) { \ |
| 2161 | + .manufacturer = _manufacturer, \ |
| 2162 | + .product = _product, \ |
| 2163 | + }, \ |
| 2164 | + .flags = (_flags), \ |
| 2165 | +} |
| 2166 | + |
| 2167 | +/* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. */ |
| 2168 | +#define VENDOR_STRING_FLG(vid, _manufacturer, _flags) \ |
| 2169 | + DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags) |
| 2170 | + |
2147 | 2171 | static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { |
| 2172 | + /* Device and string descriptor matches */ |
| 2173 | + |
2148 | 2174 | /* Device matches */ |
2149 | 2175 | DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */ |
2150 | 2176 | QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), |
@@ -2416,6 +2442,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { |
2416 | 2442 | DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ |
2417 | 2443 | QUIRK_FLAG_ALIGN_TRANSFER), |
2418 | 2444 |
|
| 2445 | + /* Vendor and string descriptor matches */ |
| 2446 | + |
2419 | 2447 | /* Vendor matches */ |
2420 | 2448 | VENDOR_FLG(0x045e, /* MS Lifecam */ |
2421 | 2449 | QUIRK_FLAG_GET_SAMPLE_RATE), |
@@ -2560,14 +2588,64 @@ void snd_usb_apply_flag_dbg(const char *reason, |
2560 | 2588 | } |
2561 | 2589 | } |
2562 | 2590 |
|
| 2591 | +#define USB_STRING_SIZE 128 |
| 2592 | + |
| 2593 | +static char *snd_usb_get_string(struct snd_usb_audio *chip, int id) |
| 2594 | +{ |
| 2595 | + char *buf; |
| 2596 | + int ret; |
| 2597 | + |
| 2598 | + /* |
| 2599 | + * Devices without the corresponding string descriptor. |
| 2600 | + * This is non-fatal as *_STRING_FLG have nothing to do in this case. |
| 2601 | + */ |
| 2602 | + if (id == 0) |
| 2603 | + return ERR_PTR(-ENODATA); |
| 2604 | + |
| 2605 | + buf = kmalloc(USB_STRING_SIZE, GFP_KERNEL); |
| 2606 | + if (buf == NULL) |
| 2607 | + return ERR_PTR(-ENOMEM); |
| 2608 | + |
| 2609 | + ret = usb_string(chip->dev, id, buf, USB_STRING_SIZE); |
| 2610 | + if (ret < 0) { |
| 2611 | + usb_audio_warn(chip, "failed to get string for id%d: %d\n", id, ret); |
| 2612 | + kfree(buf); |
| 2613 | + return ERR_PTR(ret); |
| 2614 | + } |
| 2615 | + |
| 2616 | + return buf; |
| 2617 | +} |
| 2618 | + |
2563 | 2619 | void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip) |
2564 | 2620 | { |
2565 | 2621 | const struct usb_audio_quirk_flags_table *p; |
| 2622 | + char *manufacturer __free(kfree) = NULL; |
| 2623 | + char *product __free(kfree) = NULL; |
2566 | 2624 |
|
2567 | 2625 | for (p = quirk_flags_table; p->id; p++) { |
2568 | 2626 | if (chip->usb_id == p->id || |
2569 | 2627 | (!USB_ID_PRODUCT(p->id) && |
2570 | 2628 | USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) { |
| 2629 | + /* Handle DEVICE_STRING_FLG/VENDOR_STRING_FLG. */ |
| 2630 | + if (p->usb_string_match && p->usb_string_match->manufacturer) { |
| 2631 | + if (!manufacturer) { |
| 2632 | + manufacturer = snd_usb_get_string(chip, |
| 2633 | + chip->dev->descriptor.iManufacturer); |
| 2634 | + } |
| 2635 | + if (IS_ERR_OR_NULL(manufacturer) || |
| 2636 | + strcmp(p->usb_string_match->manufacturer, manufacturer)) |
| 2637 | + continue; |
| 2638 | + } |
| 2639 | + if (p->usb_string_match && p->usb_string_match->product) { |
| 2640 | + if (!product) { |
| 2641 | + product = snd_usb_get_string(chip, |
| 2642 | + chip->dev->descriptor.iProduct); |
| 2643 | + } |
| 2644 | + if (IS_ERR_OR_NULL(product) || |
| 2645 | + strcmp(p->usb_string_match->product, product)) |
| 2646 | + continue; |
| 2647 | + } |
| 2648 | + |
2571 | 2649 | snd_usb_apply_flag_dbg("builtin table", chip, p->flags); |
2572 | 2650 | chip->quirk_flags |= p->flags; |
2573 | 2651 | return; |
|
0 commit comments