|
@@ -18,6 +18,7 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/usb.h>
|
|
|
#include <linux/usb/audio.h>
|
|
|
+#include <linux/usb/midi.h>
|
|
|
|
|
|
#include <sound/control.h>
|
|
|
#include <sound/core.h>
|
|
@@ -175,6 +176,178 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver)
|
|
|
+{
|
|
|
+ struct usb_host_interface *alts;
|
|
|
+ struct usb_interface_descriptor *altsd;
|
|
|
+ struct usb_endpoint_descriptor *epd;
|
|
|
+ struct uac1_as_header_descriptor *ashd;
|
|
|
+ struct uac_format_type_i_discrete_descriptor *fmtd;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Most Roland/Yamaha audio streaming interfaces have more or less
|
|
|
+ * standard descriptors, but older devices might lack descriptors, and
|
|
|
+ * future ones might change, so ensure that we fail silently if the
|
|
|
+ * interface doesn't look exactly right.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* must have a non-zero altsetting for streaming */
|
|
|
+ if (iface->num_altsetting < 2)
|
|
|
+ return -ENODEV;
|
|
|
+ alts = &iface->altsetting[1];
|
|
|
+ altsd = get_iface_desc(alts);
|
|
|
+
|
|
|
+ /* must have an isochronous endpoint for streaming */
|
|
|
+ if (altsd->bNumEndpoints < 1)
|
|
|
+ return -ENODEV;
|
|
|
+ epd = get_endpoint(alts, 0);
|
|
|
+ if (!usb_endpoint_xfer_isoc(epd))
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ /* must have format descriptors */
|
|
|
+ ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
|
+ UAC_AS_GENERAL);
|
|
|
+ fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
|
+ UAC_FORMAT_TYPE);
|
|
|
+ if (!ashd || ashd->bLength < 7 ||
|
|
|
+ !fmtd || fmtd->bLength < 8)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return create_standard_audio_quirk(chip, iface, driver, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver,
|
|
|
+ struct usb_host_interface *alts)
|
|
|
+{
|
|
|
+ static const struct snd_usb_audio_quirk yamaha_midi_quirk = {
|
|
|
+ .type = QUIRK_MIDI_YAMAHA
|
|
|
+ };
|
|
|
+ struct usb_midi_in_jack_descriptor *injd;
|
|
|
+ struct usb_midi_out_jack_descriptor *outjd;
|
|
|
+
|
|
|
+ /* must have some valid jack descriptors */
|
|
|
+ injd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
|
+ NULL, USB_MS_MIDI_IN_JACK);
|
|
|
+ outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
|
+ NULL, USB_MS_MIDI_OUT_JACK);
|
|
|
+ if (!injd && !outjd)
|
|
|
+ return -ENODEV;
|
|
|
+ if (injd && (injd->bLength < 5 ||
|
|
|
+ (injd->bJackType != USB_MS_EMBEDDED &&
|
|
|
+ injd->bJackType != USB_MS_EXTERNAL)))
|
|
|
+ return -ENODEV;
|
|
|
+ if (outjd && (outjd->bLength < 6 ||
|
|
|
+ (outjd->bJackType != USB_MS_EMBEDDED &&
|
|
|
+ outjd->bJackType != USB_MS_EXTERNAL)))
|
|
|
+ return -ENODEV;
|
|
|
+ return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk);
|
|
|
+}
|
|
|
+
|
|
|
+static int create_roland_midi_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver,
|
|
|
+ struct usb_host_interface *alts)
|
|
|
+{
|
|
|
+ static const struct snd_usb_audio_quirk roland_midi_quirk = {
|
|
|
+ .type = QUIRK_MIDI_ROLAND
|
|
|
+ };
|
|
|
+ u8 *roland_desc = NULL;
|
|
|
+
|
|
|
+ /* might have a vendor-specific descriptor <06 24 F1 02 ...> */
|
|
|
+ for (;;) {
|
|
|
+ roland_desc = snd_usb_find_csint_desc(alts->extra,
|
|
|
+ alts->extralen,
|
|
|
+ roland_desc, 0xf1);
|
|
|
+ if (!roland_desc)
|
|
|
+ return -ENODEV;
|
|
|
+ if (roland_desc[0] < 6 || roland_desc[3] != 2)
|
|
|
+ continue;
|
|
|
+ return create_any_midi_quirk(chip, iface, driver,
|
|
|
+ &roland_midi_quirk);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int create_std_midi_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver,
|
|
|
+ struct usb_host_interface *alts)
|
|
|
+{
|
|
|
+ struct usb_ms_header_descriptor *mshd;
|
|
|
+ struct usb_ms_endpoint_descriptor *msepd;
|
|
|
+
|
|
|
+ /* must have the MIDIStreaming interface header descriptor*/
|
|
|
+ mshd = (struct usb_ms_header_descriptor *)alts->extra;
|
|
|
+ if (alts->extralen < 7 ||
|
|
|
+ mshd->bLength < 7 ||
|
|
|
+ mshd->bDescriptorType != USB_DT_CS_INTERFACE ||
|
|
|
+ mshd->bDescriptorSubtype != USB_MS_HEADER)
|
|
|
+ return -ENODEV;
|
|
|
+ /* must have the MIDIStreaming endpoint descriptor*/
|
|
|
+ msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra;
|
|
|
+ if (alts->endpoint[0].extralen < 4 ||
|
|
|
+ msepd->bLength < 4 ||
|
|
|
+ msepd->bDescriptorType != USB_DT_CS_ENDPOINT ||
|
|
|
+ msepd->bDescriptorSubtype != UAC_MS_GENERAL ||
|
|
|
+ msepd->bNumEmbMIDIJack < 1 ||
|
|
|
+ msepd->bNumEmbMIDIJack > 16)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return create_any_midi_quirk(chip, iface, driver, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static int create_auto_midi_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver)
|
|
|
+{
|
|
|
+ struct usb_host_interface *alts;
|
|
|
+ struct usb_interface_descriptor *altsd;
|
|
|
+ struct usb_endpoint_descriptor *epd;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ alts = &iface->altsetting[0];
|
|
|
+ altsd = get_iface_desc(alts);
|
|
|
+
|
|
|
+ /* must have at least one bulk/interrupt endpoint for streaming */
|
|
|
+ if (altsd->bNumEndpoints < 1)
|
|
|
+ return -ENODEV;
|
|
|
+ epd = get_endpoint(alts, 0);
|
|
|
+ if (!usb_endpoint_xfer_bulk(epd) ||
|
|
|
+ !usb_endpoint_xfer_int(epd))
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ switch (USB_ID_VENDOR(chip->usb_id)) {
|
|
|
+ case 0x0499: /* Yamaha */
|
|
|
+ err = create_yamaha_midi_quirk(chip, iface, driver, alts);
|
|
|
+ if (err < 0 && err != -ENODEV)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
+ case 0x0582: /* Roland */
|
|
|
+ err = create_roland_midi_quirk(chip, iface, driver, alts);
|
|
|
+ if (err < 0 && err != -ENODEV)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return create_std_midi_quirk(chip, iface, driver, alts);
|
|
|
+}
|
|
|
+
|
|
|
+static int create_autodetect_quirk(struct snd_usb_audio *chip,
|
|
|
+ struct usb_interface *iface,
|
|
|
+ struct usb_driver *driver,
|
|
|
+ const struct snd_usb_audio_quirk *quirk)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = create_auto_pcm_quirk(chip, iface, driver);
|
|
|
+ if (err == -ENODEV)
|
|
|
+ err = create_auto_midi_quirk(chip, iface, driver);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
|
|
|
* 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,
|
|
|
static const quirk_func_t quirk_funcs[] = {
|
|
|
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
|
|
|
[QUIRK_COMPOSITE] = create_composite_quirk,
|
|
|
+ [QUIRK_AUTODETECT] = create_autodetect_quirk,
|
|
|
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
|
|
|
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
|
|
|
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
|
|
|
+ [QUIRK_MIDI_ROLAND] = create_any_midi_quirk,
|
|
|
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
|
|
|
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
|
|
|
[QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
|