|
18 | 18 | #include <linux/slab.h>
|
19 | 19 | #include <linux/usb.h>
|
20 | 20 | #include <linux/usb/audio.h>
|
| 21 | +#include <linux/usb/midi.h> |
21 | 22 |
|
22 | 23 | #include <sound/control.h>
|
23 | 24 | #include <sound/core.h>
|
@@ -175,6 +176,178 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
175 | 176 | return 0;
|
176 | 177 | }
|
177 | 178 |
|
| 179 | +static int create_auto_pcm_quirk(struct snd_usb_audio *chip, |
| 180 | + struct usb_interface *iface, |
| 181 | + struct usb_driver *driver) |
| 182 | +{ |
| 183 | + struct usb_host_interface *alts; |
| 184 | + struct usb_interface_descriptor *altsd; |
| 185 | + struct usb_endpoint_descriptor *epd; |
| 186 | + struct uac1_as_header_descriptor *ashd; |
| 187 | + struct uac_format_type_i_discrete_descriptor *fmtd; |
| 188 | + |
| 189 | + /* |
| 190 | + * Most Roland/Yamaha audio streaming interfaces have more or less |
| 191 | + * standard descriptors, but older devices might lack descriptors, and |
| 192 | + * future ones might change, so ensure that we fail silently if the |
| 193 | + * interface doesn't look exactly right. |
| 194 | + */ |
| 195 | + |
| 196 | + /* must have a non-zero altsetting for streaming */ |
| 197 | + if (iface->num_altsetting < 2) |
| 198 | + return -ENODEV; |
| 199 | + alts = &iface->altsetting[1]; |
| 200 | + altsd = get_iface_desc(alts); |
| 201 | + |
| 202 | + /* must have an isochronous endpoint for streaming */ |
| 203 | + if (altsd->bNumEndpoints < 1) |
| 204 | + return -ENODEV; |
| 205 | + epd = get_endpoint(alts, 0); |
| 206 | + if (!usb_endpoint_xfer_isoc(epd)) |
| 207 | + return -ENODEV; |
| 208 | + |
| 209 | + /* must have format descriptors */ |
| 210 | + ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, |
| 211 | + UAC_AS_GENERAL); |
| 212 | + fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, |
| 213 | + UAC_FORMAT_TYPE); |
| 214 | + if (!ashd || ashd->bLength < 7 || |
| 215 | + !fmtd || fmtd->bLength < 8) |
| 216 | + return -ENODEV; |
| 217 | + |
| 218 | + return create_standard_audio_quirk(chip, iface, driver, NULL); |
| 219 | +} |
| 220 | + |
| 221 | +static int create_yamaha_midi_quirk(struct snd_usb_audio *chip, |
| 222 | + struct usb_interface *iface, |
| 223 | + struct usb_driver *driver, |
| 224 | + struct usb_host_interface *alts) |
| 225 | +{ |
| 226 | + static const struct snd_usb_audio_quirk yamaha_midi_quirk = { |
| 227 | + .type = QUIRK_MIDI_YAMAHA |
| 228 | + }; |
| 229 | + struct usb_midi_in_jack_descriptor *injd; |
| 230 | + struct usb_midi_out_jack_descriptor *outjd; |
| 231 | + |
| 232 | + /* must have some valid jack descriptors */ |
| 233 | + injd = snd_usb_find_csint_desc(alts->extra, alts->extralen, |
| 234 | + NULL, USB_MS_MIDI_IN_JACK); |
| 235 | + outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen, |
| 236 | + NULL, USB_MS_MIDI_OUT_JACK); |
| 237 | + if (!injd && !outjd) |
| 238 | + return -ENODEV; |
| 239 | + if (injd && (injd->bLength < 5 || |
| 240 | + (injd->bJackType != USB_MS_EMBEDDED && |
| 241 | + injd->bJackType != USB_MS_EXTERNAL))) |
| 242 | + return -ENODEV; |
| 243 | + if (outjd && (outjd->bLength < 6 || |
| 244 | + (outjd->bJackType != USB_MS_EMBEDDED && |
| 245 | + outjd->bJackType != USB_MS_EXTERNAL))) |
| 246 | + return -ENODEV; |
| 247 | + return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk); |
| 248 | +} |
| 249 | + |
| 250 | +static int create_roland_midi_quirk(struct snd_usb_audio *chip, |
| 251 | + struct usb_interface *iface, |
| 252 | + struct usb_driver *driver, |
| 253 | + struct usb_host_interface *alts) |
| 254 | +{ |
| 255 | + static const struct snd_usb_audio_quirk roland_midi_quirk = { |
| 256 | + .type = QUIRK_MIDI_ROLAND |
| 257 | + }; |
| 258 | + u8 *roland_desc = NULL; |
| 259 | + |
| 260 | + /* might have a vendor-specific descriptor <06 24 F1 02 ...> */ |
| 261 | + for (;;) { |
| 262 | + roland_desc = snd_usb_find_csint_desc(alts->extra, |
| 263 | + alts->extralen, |
| 264 | + roland_desc, 0xf1); |
| 265 | + if (!roland_desc) |
| 266 | + return -ENODEV; |
| 267 | + if (roland_desc[0] < 6 || roland_desc[3] != 2) |
| 268 | + continue; |
| 269 | + return create_any_midi_quirk(chip, iface, driver, |
| 270 | + &roland_midi_quirk); |
| 271 | + } |
| 272 | +} |
| 273 | + |
| 274 | +static int create_std_midi_quirk(struct snd_usb_audio *chip, |
| 275 | + struct usb_interface *iface, |
| 276 | + struct usb_driver *driver, |
| 277 | + struct usb_host_interface *alts) |
| 278 | +{ |
| 279 | + struct usb_ms_header_descriptor *mshd; |
| 280 | + struct usb_ms_endpoint_descriptor *msepd; |
| 281 | + |
| 282 | + /* must have the MIDIStreaming interface header descriptor*/ |
| 283 | + mshd = (struct usb_ms_header_descriptor *)alts->extra; |
| 284 | + if (alts->extralen < 7 || |
| 285 | + mshd->bLength < 7 || |
| 286 | + mshd->bDescriptorType != USB_DT_CS_INTERFACE || |
| 287 | + mshd->bDescriptorSubtype != USB_MS_HEADER) |
| 288 | + return -ENODEV; |
| 289 | + /* must have the MIDIStreaming endpoint descriptor*/ |
| 290 | + msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra; |
| 291 | + if (alts->endpoint[0].extralen < 4 || |
| 292 | + msepd->bLength < 4 || |
| 293 | + msepd->bDescriptorType != USB_DT_CS_ENDPOINT || |
| 294 | + msepd->bDescriptorSubtype != UAC_MS_GENERAL || |
| 295 | + msepd->bNumEmbMIDIJack < 1 || |
| 296 | + msepd->bNumEmbMIDIJack > 16) |
| 297 | + return -ENODEV; |
| 298 | + |
| 299 | + return create_any_midi_quirk(chip, iface, driver, NULL); |
| 300 | +} |
| 301 | + |
| 302 | +static int create_auto_midi_quirk(struct snd_usb_audio *chip, |
| 303 | + struct usb_interface *iface, |
| 304 | + struct usb_driver *driver) |
| 305 | +{ |
| 306 | + struct usb_host_interface *alts; |
| 307 | + struct usb_interface_descriptor *altsd; |
| 308 | + struct usb_endpoint_descriptor *epd; |
| 309 | + int err; |
| 310 | + |
| 311 | + alts = &iface->altsetting[0]; |
| 312 | + altsd = get_iface_desc(alts); |
| 313 | + |
| 314 | + /* must have at least one bulk/interrupt endpoint for streaming */ |
| 315 | + if (altsd->bNumEndpoints < 1) |
| 316 | + return -ENODEV; |
| 317 | + epd = get_endpoint(alts, 0); |
| 318 | + if (!usb_endpoint_xfer_bulk(epd) || |
| 319 | + !usb_endpoint_xfer_int(epd)) |
| 320 | + return -ENODEV; |
| 321 | + |
| 322 | + switch (USB_ID_VENDOR(chip->usb_id)) { |
| 323 | + case 0x0499: /* Yamaha */ |
| 324 | + err = create_yamaha_midi_quirk(chip, iface, driver, alts); |
| 325 | + if (err < 0 && err != -ENODEV) |
| 326 | + return err; |
| 327 | + break; |
| 328 | + case 0x0582: /* Roland */ |
| 329 | + err = create_roland_midi_quirk(chip, iface, driver, alts); |
| 330 | + if (err < 0 && err != -ENODEV) |
| 331 | + return err; |
| 332 | + break; |
| 333 | + } |
| 334 | + |
| 335 | + return create_std_midi_quirk(chip, iface, driver, alts); |
| 336 | +} |
| 337 | + |
| 338 | +static int create_autodetect_quirk(struct snd_usb_audio *chip, |
| 339 | + struct usb_interface *iface, |
| 340 | + struct usb_driver *driver, |
| 341 | + const struct snd_usb_audio_quirk *quirk) |
| 342 | +{ |
| 343 | + int err; |
| 344 | + |
| 345 | + err = create_auto_pcm_quirk(chip, iface, driver); |
| 346 | + if (err == -ENODEV) |
| 347 | + err = create_auto_midi_quirk(chip, iface, driver); |
| 348 | + return err; |
| 349 | +} |
| 350 | + |
178 | 351 | /*
|
179 | 352 | * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
|
180 | 353 | * The only way to detect the sample rate is by looking at wMaxPacketSize.
|
@@ -303,9 +476,11 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
|
303 | 476 | static const quirk_func_t quirk_funcs[] = {
|
304 | 477 | [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
|
305 | 478 | [QUIRK_COMPOSITE] = create_composite_quirk,
|
| 479 | + [QUIRK_AUTODETECT] = create_autodetect_quirk, |
306 | 480 | [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
|
307 | 481 | [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
|
308 | 482 | [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
|
| 483 | + [QUIRK_MIDI_ROLAND] = create_any_midi_quirk, |
309 | 484 | [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
|
310 | 485 | [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
|
311 | 486 | [QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
|
|
0 commit comments