aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/Makefile4
-rw-r--r--sound/usb/clock.c14
-rw-r--r--sound/usb/helper.h4
-rw-r--r--sound/usb/line6/driver.c4
-rw-r--r--sound/usb/line6/pcm.c18
-rw-r--r--sound/usb/mixer.c673
-rw-r--r--sound/usb/mixer.h4
-rw-r--r--sound/usb/mixer_quirks.c15
-rw-r--r--sound/usb/mixer_scarlett_gen2.c2075
-rw-r--r--sound/usb/mixer_scarlett_gen2.h7
-rw-r--r--sound/usb/pcm.c2
-rw-r--r--sound/usb/power.c2
-rw-r--r--sound/usb/quirks-table.h57
-rw-r--r--sound/usb/quirks.c15
-rw-r--r--sound/usb/stream.c90
-rw-r--r--sound/usb/validate.c332
16 files changed, 2898 insertions, 418 deletions
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e1ce257ab705..78edd7d2f418 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,12 +11,14 @@ snd-usb-audio-objs := card.o \
mixer.o \
mixer_quirks.o \
mixer_scarlett.o \
+ mixer_scarlett_gen2.o \
mixer_us16x08.o \
pcm.o \
power.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ validate.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 72e9bdf76115..6b8c14f9b5d4 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -38,39 +38,37 @@ static void *find_uac_clock_desc(struct usb_host_interface *iface, int id,
static bool validate_clock_source_v2(void *p, int id)
{
struct uac_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_source_v3(void *p, int id)
{
struct uac3_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v2(void *p, int id)
{
struct uac_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 7 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v3(void *p, int id)
{
struct uac3_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 11 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v2(void *p, int id)
{
struct uac_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v3(void *p, int id)
{
struct uac3_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
#define DEFINE_FIND_HELPER(name, obj, validator, type) \
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index 6afb70156ec4..5e8a18b4e7b9 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
}
+/* in validate.c */
+bool snd_usb_validate_audio_desc(void *p, int protocol);
+bool snd_usb_validate_midi_desc(void *p);
+
#endif /* __USBAUDIO_HELPER_H */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index ab2ec896f49c..b5a3f754a4f1 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -342,7 +342,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
if (address > 0xffff || datalen > 0xff)
return -EINVAL;
- len = kmalloc(sizeof(*len), GFP_KERNEL);
+ len = kmalloc(1, GFP_KERNEL);
if (!len)
return -ENOMEM;
@@ -418,7 +418,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
if (address > 0xffff || datalen > 0xffff)
return -EINVAL;
- status = kmalloc(sizeof(*status), GFP_KERNEL);
+ status = kmalloc(1, GFP_KERNEL);
if (!status)
return -ENOMEM;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 2c03e0f6bf72..f70211e6b174 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -550,6 +550,15 @@ int line6_init_pcm(struct usb_line6 *line6,
line6pcm->volume_monitor = 255;
line6pcm->line6 = line6;
+ spin_lock_init(&line6pcm->out.lock);
+ spin_lock_init(&line6pcm->in.lock);
+ line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
+
+ line6->line6pcm = line6pcm;
+
+ pcm->private_data = line6pcm;
+ pcm->private_free = line6_cleanup_pcm;
+
line6pcm->max_packet_size_in =
usb_maxpacket(line6->usbdev,
usb_rcvisocpipe(line6->usbdev, ep_read), 0);
@@ -562,15 +571,6 @@ int line6_init_pcm(struct usb_line6 *line6,
return -EINVAL;
}
- spin_lock_init(&line6pcm->out.lock);
- spin_lock_init(&line6pcm->in.lock);
- line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
-
- line6->line6pcm = line6pcm;
-
- pcm->private_data = line6pcm;
- pcm->private_free = line6_cleanup_pcm;
-
err = line6_create_audio_out_urbs(line6pcm);
if (err < 0)
return err;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index b5927c3d5bc0..3fd1d1749edf 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -739,14 +739,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc)
{
int mu_channels;
- void *c;
-
- if (desc->bLength < sizeof(*desc))
- return -EINVAL;
- if (!desc->bNrInPins)
- return -EINVAL;
- if (desc->bLength < sizeof(*desc) + desc->bNrInPins)
- return -EINVAL;
switch (state->mixer->protocol) {
case UAC_VERSION_1:
@@ -762,233 +754,246 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
break;
}
- if (!mu_channels)
- return 0;
-
- c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
- if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength)
- return 0; /* no bmControls -> skip */
-
return mu_channels;
}
/*
- * parse the source unit recursively until it reaches to a terminal
- * or a branched unit.
+ * Parse Input Terminal Unit
*/
static int __check_input_term(struct mixer_build *state, int id,
- struct usb_audio_term *term)
+ struct usb_audio_term *term);
+
+static int parse_term_uac1_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
{
- int protocol = state->mixer->protocol;
+ struct uac_input_terminal_descriptor *d = p1;
+
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le16_to_cpu(d->wChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
+
+static int parse_term_uac2_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac2_input_terminal_descriptor *d = p1;
int err;
- void *p1;
- unsigned char *hdr;
- memset(term, 0, sizeof(*term));
- for (;;) {
- /* a loop in the terminal chain? */
- if (test_and_set_bit(id, state->termbitmap))
- return -EINVAL;
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
- p1 = find_audio_control_unit(state, id);
- if (!p1)
- break;
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
- hdr = p1;
- term->id = id;
+static int parse_term_uac3_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_input_terminal_descriptor *d = p1;
+ int err;
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL:
- if (protocol == UAC_VERSION_1) {
- struct uac_input_terminal_descriptor *d = p1;
-
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le16_to_cpu(d->wChannelConfig);
- term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
- struct uac2_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- }
- return 0;
- case UAC_FEATURE_UNIT: {
- /* the header is the same for v1 and v2 */
- struct uac_feature_unit_descriptor *d = p1;
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
-
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
- term->channels = uac_mixer_unit_bNrChannels(d);
- term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
- term->name = uac_mixer_unit_iMixer(d);
- return 0;
- }
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
- return 0;
- }
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT */
- if (protocol == UAC_VERSION_1)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- else /* UAC_VERSION_2 */
- term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 */
- if (protocol == UAC_VERSION_1 && !term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
- else if (protocol == UAC_VERSION_2 && !term->type)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
-
- if (protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
- return 0;
- }
+ err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
+ if (err < 0)
+ return err;
+ term->channels = err;
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- if (!term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
+ /* REVISIT: UAC3 IT doesn't have channels cfg */
+ term->chconfig = 0;
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
- term->name = uac_processing_unit_iProcessing(d, protocol);
- return 0;
- }
- case UAC2_CLOCK_SOURCE: {
- struct uac_clock_source_descriptor *d = p1;
+ term->name = le16_to_cpu(d->wTerminalDescrStr);
+ return 0;
+}
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = d->iClockSource;
- return 0;
- }
- default:
- return -ENODEV;
- }
- } else { /* UAC_VERSION_3 */
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL: {
- struct uac3_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
+static int parse_term_mixer_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_mixer_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
+ err = uac_mixer_unit_get_channels(state, d);
+ if (err <= 0)
+ return err;
- err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
- if (err < 0)
- return err;
- term->channels = err;
+ term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
+ term->channels = err;
+ if (protocol != UAC_VERSION_3) {
+ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+ term->name = uac_mixer_unit_iMixer(d);
+ }
+ return 0;
+}
- /* REVISIT: UAC3 IT doesn't have channels cfg */
- term->chconfig = 0;
+static int parse_term_selector_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_selector_unit_descriptor *d = p1;
+ int err;
- term->name = le16_to_cpu(d->wTerminalDescrStr);
- return 0;
- }
- case UAC3_FEATURE_UNIT: {
- struct uac3_feature_unit_descriptor *d = p1;
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
+ term->id = id;
+ if (state->mixer->protocol != UAC_VERSION_3)
+ term->name = uac_selector_unit_iSelector(d);
+ return 0;
+}
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC3_CLOCK_SOURCE: {
- struct uac3_clock_source_descriptor *d = p1;
+static int parse_term_proc_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id, int vtype)
+{
+ struct uac_processing_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = le16_to_cpu(d->wClockSourceStr);
- return 0;
- }
- case UAC3_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
+ if (d->bNrInPins) {
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ }
- err = uac_mixer_unit_get_channels(state, d);
- if (err <= 0)
- return err;
+ term->type = vtype << 16; /* virtual type */
+ term->id = id;
- term->channels = err;
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
+ if (protocol == UAC_VERSION_3)
+ return 0;
- return 0;
- }
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
+ if (!term->channels) {
+ term->channels = uac_processing_unit_bNrChannels(d);
+ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+ }
+ term->name = uac_processing_unit_iProcessing(d, protocol);
+ return 0;
+}
- return 0;
- }
- case UAC3_PROCESSING_UNIT: {
- struct uac_processing_unit_descriptor *d = p1;
+static int parse_term_uac2_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_clock_source_descriptor *d = p1;
- if (!d->bNrInPins)
- return -EINVAL;
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = d->iClockSource;
+ return 0;
+}
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
+static int parse_term_uac3_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_clock_source_descriptor *d = p1;
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = le16_to_cpu(d->wClockSourceStr);
+ return 0;
+}
- return 0;
- }
- default:
- return -ENODEV;
- }
+#define PTYPE(a, b) ((a) << 8 | (b))
+
+/*
+ * parse the source unit recursively until it reaches to a terminal
+ * or a branched unit.
+ */
+static int __check_input_term(struct mixer_build *state, int id,
+ struct usb_audio_term *term)
+{
+ int protocol = state->mixer->protocol;
+ void *p1;
+ unsigned char *hdr;
+
+ for (;;) {
+ /* a loop in the terminal chain? */
+ if (test_and_set_bit(id, state->termbitmap))
+ return -EINVAL;
+
+ p1 = find_audio_control_unit(state, id);
+ if (!p1)
+ break;
+ if (!snd_usb_validate_audio_desc(p1, protocol))
+ break; /* bad descriptor */
+
+ hdr = p1;
+ term->id = id;
+
+ switch (PTYPE(protocol, hdr[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): {
+ /* the header is the same for all versions */
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ break; /* continue to parse */
+ }
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ return parse_term_uac1_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ return parse_term_uac2_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_term_uac3_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_term_mixer_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_term_selector_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_PROCESSING_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_EFFECT_UNIT);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_EXTENSION_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ return parse_term_uac2_clock_source(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_term_uac3_clock_source(state, term, p1, id);
+ default:
+ return -ENODEV;
}
}
return -ENODEV;
@@ -1032,10 +1037,15 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
};
+static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval)
+{
+ kfree(cval);
+}
+
/* private_free callback */
void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
{
- kfree(kctl->private_data);
+ usb_mixer_elem_info_free(kctl->private_data);
kctl->private_data = NULL;
}
@@ -1558,7 +1568,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
ctl_info = get_feature_control_info(control);
if (!ctl_info) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
if (mixer->protocol == UAC_VERSION_1)
@@ -1591,7 +1601,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -1761,7 +1771,7 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
get_connector_control_name(mixer, term, is_input, kctl->id.name,
@@ -1782,13 +1792,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
if (state->mixer->protocol != UAC_VERSION_2)
return -EINVAL;
- if (hdr->bLength != sizeof(*hdr)) {
- usb_audio_dbg(state->chip,
- "Bogus clock source descriptor length of %d, ignoring.\n",
- hdr->bLength);
- return 0;
- }
-
/*
* The only property of this unit we are interested in is the
* clock source validity. If that isn't readable, just bail out.
@@ -1814,7 +1817,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
@@ -1847,62 +1850,20 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
__u8 *bmaControls;
if (state->mixer->protocol == UAC_VERSION_1) {
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = hdr->bControlSize;
- if (!csize) {
- usb_audio_dbg(state->chip,
- "unit %u: invalid bControlSize == 0\n",
- unitid);
- return -EINVAL;
- }
channels = (hdr->bLength - 7) / csize - 1;
bmaControls = hdr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 6) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 6 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else { /* UAC_VERSION_3 */
struct uac3_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (ftr->bLength - 7) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
}
/* parse the source unit */
@@ -2009,6 +1970,31 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
* Mixer Unit
*/
+/* check whether the given in/out overflows bmMixerControls matrix */
+static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc,
+ int protocol, int num_ins, int num_outs)
+{
+ u8 *hdr = (u8 *)desc;
+ u8 *c = uac_mixer_unit_bmControls(desc, protocol);
+ size_t rest; /* remaining bytes after bmMixerControls */
+
+ switch (protocol) {
+ case UAC_VERSION_1:
+ default:
+ rest = 1; /* iMixer */
+ break;
+ case UAC_VERSION_2:
+ rest = 2; /* bmControls + iMixer */
+ break;
+ case UAC_VERSION_3:
+ rest = 6; /* bmControls + wMixerDescrStr */
+ break;
+ }
+
+ /* overflow? */
+ return c + (num_ins * num_outs + 7) / 8 + rest > hdr + hdr[0];
+}
+
/*
* build a mixer unit control
*
@@ -2051,7 +2037,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2077,15 +2063,11 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
- if (d_v2->bLength < sizeof(*d_v2))
- return -EINVAL;
control = UAC2_TE_CONNECTOR;
term_id = d_v2->bTerminalID;
bmctls = le16_to_cpu(d_v2->bmControls);
} else if (state->mixer->protocol == UAC_VERSION_3) {
struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
- if (d_v3->bLength < sizeof(*d_v3))
- return -EINVAL;
control = UAC3_TE_INSERTION;
term_id = d_v3->bTerminalID;
bmctls = le32_to_cpu(d_v3->bmControls);
@@ -2137,6 +2119,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
if (err < 0)
return err;
num_ins += iterm.channels;
+ if (mixer_bitmap_overflow(desc, state->mixer->protocol,
+ num_ins, num_outs))
+ break;
for (; ich < num_ins; ich++) {
int och, ich_has_controls = 0;
@@ -2344,18 +2329,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
const char *name = extension_unit ?
"Extension Unit" : "Processing Unit";
- if (desc->bLength < 13) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
num_ins = desc->bNrInPins;
- if (desc->bLength < 13 + num_ins ||
- desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
for (i = 0; i < num_ins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2446,7 +2420,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2584,7 +2558,7 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
if (kctl->private_data) {
struct usb_mixer_elem_info *cval = kctl->private_data;
num_ins = cval->max;
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
kctl->private_data = NULL;
}
if (kctl->private_value) {
@@ -2610,13 +2584,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
const struct usbmix_name_map *map;
char **namelist;
- if (desc->bLength < 5 || !desc->bNrInPins ||
- desc->bLength < 5 + desc->bNrInPins) {
- usb_audio_err(state->chip,
- "invalid SELECTOR UNIT descriptor %d\n", unitid);
- return -EINVAL;
- }
-
for (i = 0; i < desc->bNrInPins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2656,10 +2623,10 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
break;
}
- namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
+ namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
if (!namelist) {
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_cval;
}
#define MAX_ITEM_NAME_LEN 64
for (i = 0; i < desc->bNrInPins; i++) {
@@ -2667,11 +2634,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
len = 0;
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
if (!namelist[i]) {
- while (i--)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
len = check_mapped_selector_name(state, unitid, i, namelist[i],
MAX_ITEM_NAME_LEN);
@@ -2685,11 +2649,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- for (i = 0; i < desc->bNrInPins; i++)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
kctl->private_value = (unsigned long)namelist;
kctl->private_free = usb_mixer_selector_elem_free;
@@ -2735,6 +2696,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
cval->head.id, kctl->id.name, desc->bNrInPins);
return snd_usb_mixer_add_control(&cval->head, kctl);
+
+ error_name:
+ for (i = 0; i < desc->bNrInPins; i++)
+ kfree(namelist[i]);
+ kfree(namelist);
+ error_cval:
+ usb_mixer_elem_info_free(cval);
+ return err;
}
/*
@@ -2755,62 +2724,49 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- case UAC_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC2_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_processing_unit(state, unitid, p1);
- else
- return 0; /* FIXME - effect units not implemented yet */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_extension_unit(state, unitid, p1);
- else /* UAC_VERSION_2 */
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC2_EXTENSION_UNIT_V2:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
- } else { /* UAC_VERSION_3 */
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- case UAC3_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC3_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC3_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC3_EFFECT_UNIT:
- return 0; /* FIXME - effect units not implemented yet */
- case UAC3_PROCESSING_UNIT:
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC3_EXTENSION_UNIT:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
+ if (!snd_usb_validate_audio_desc(p1, protocol)) {
+ usb_audio_dbg(state->chip, "invalid unit %d\n", unitid);
+ return 0; /* skip invalid unit */
+ }
+
+ switch (PTYPE(protocol, p1[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_audio_input_terminal(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_clock_source_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_audio_selector_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT):
+ return parse_audio_feature_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_audio_processing_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_audio_extension_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return 0; /* FIXME - effect units not implemented yet */
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n",
+ unitid, p1[2]);
+ return -EINVAL;
}
}
@@ -3125,11 +3081,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen,
p, UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (!snd_usb_validate_audio_desc(p, mixer->protocol))
+ continue; /* skip invalid descriptor */
+
if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3141,8 +3098,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
} else if (mixer->protocol == UAC_VERSION_2) {
struct uac2_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3168,8 +3123,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
} else { /* UAC_VERSION_3 */
struct uac3_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3530,6 +3483,8 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
usb_kill_urb(mixer->urb);
if (mixer->rc_urb)
usb_kill_urb(mixer->rc_urb);
+ if (mixer->private_free)
+ mixer->private_free(mixer);
mixer->disconnected = true;
}
@@ -3557,6 +3512,8 @@ static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
{
snd_usb_mixer_inactivate(mixer);
+ if (mixer->private_suspend)
+ mixer->private_suspend(mixer);
return 0;
}
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 394cd9107507..37e1b234c802 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -28,6 +28,10 @@ struct usb_mixer_interface {
struct media_mixer_ctl *media_mixer_ctl;
bool disconnected;
+
+ void *private_data;
+ void (*private_free)(struct usb_mixer_interface *mixer);
+ void (*private_suspend)(struct usb_mixer_interface *mixer);
};
#define MAX_CHANNELS 16 /* max logical channels */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 199fa157a411..39e27ae6c597 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -32,6 +32,7 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "mixer_scarlett.h"
+#include "mixer_scarlett_gen2.h"
#include "mixer_us16x08.h"
#include "helper.h"
@@ -1155,17 +1156,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
{
struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *cval;
- int unitid = 12; /* SamleRate ExtensionUnit ID */
+ int unitid = 12; /* SampleRate ExtensionUnit ID */
list_for_each_entry(mixer, &chip->mixer_list, list) {
- cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
- if (cval) {
+ if (mixer->id_elems[unitid]) {
+ cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
cval->control << 8,
samplerate_id);
snd_usb_mixer_notify_id(mixer, unitid);
+ break;
}
- break;
}
}
@@ -2258,6 +2259,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_scarlett_controls_create(mixer);
break;
+ case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */
+ case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */
+ case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */
+ err = snd_scarlett_gen2_controls_create(mixer);
+ break;
+
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c
new file mode 100644
index 000000000000..7d460b1f1735
--- /dev/null
+++ b/sound/usb/mixer_scarlett_gen2.c
@@ -0,0 +1,2075 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Focusrite Scarlett 6i6/18i8/18i20 Gen 2 Driver for ALSA
+ *
+ * Copyright (c) 2018-2019 by Geoffrey D. Bennett <g at b4.vu>
+ *
+ * Based on the Scarlett (Gen 1) Driver for ALSA:
+ *
+ * Copyright (c) 2013 by Tobias Hoffmann
+ * Copyright (c) 2013 by Robin Gareus <robin at gareus.org>
+ * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de>
+ * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com>
+ *
+ * Many codes borrowed from audio.c by
+ * Alan Cox (alan at lxorguk.ukuu.org.uk)
+ * Thomas Sailer (sailer at ife.ee.ethz.ch)
+ *
+ * Code cleanup:
+ * David Henningsson <david.henningsson at canonical.com>
+ */
+
+/* Mixer Interface for the Focusrite Scarlett 6i6/18i8/18i20 Gen 2 audio
+ * interface. Based on the Gen 1 driver and rewritten.
+ */
+
+/* The protocol was reverse engineered by looking at the communication
+ * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20
+ * (firmware 1083) using usbmon in July-August 2018.
+ *
+ * Scarlett 18i8 support added in April 2019.
+ *
+ * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann
+ * for providing usbmon output and testing).
+ *
+ * This ALSA mixer gives access to:
+ * - input, output, mixer-matrix muxes
+ * - 18x10 mixer-matrix gain stages
+ * - gain/volume controls
+ * - level meters
+ * - line/inst level and pad controls
+ *
+ * <ditaa>
+ * /--------------\ 18chn 20chn /--------------\
+ * | Hardware in +--+------\ /-------------+--+ ALSA PCM out |
+ * \--------------/ | | | | \--------------/
+ * | | | /-----\ |
+ * | | | | | |
+ * | v v v | |
+ * | +---------------+ | |
+ * | \ Matrix Mux / | |
+ * | +-----+-----+ | |
+ * | | | |
+ * | |18chn | |
+ * | | | |
+ * | | 10chn| |
+ * | v | |
+ * | +------------+ | |
+ * | | Mixer | | |
+ * | | Matrix | | |
+ * | | | | |
+ * | | 18x10 Gain | | |
+ * | | stages | | |
+ * | +-----+------+ | |
+ * | | | |
+ * |18chn |10chn | |20chn
+ * | | | |
+ * | +----------/ |
+ * | | |
+ * v v v
+ * ===========================
+ * +---------------+ +--—------------+
+ * \ Output Mux / \ Capture Mux /
+ * +---+---+---+ +-----+-----+
+ * | | |
+ * 10chn| | |18chn
+ * | | |
+ * /--------------\ | | | /--------------\
+ * | S/PDIF, ADAT |<--/ |10chn \-->| ALSA PCM in |
+ * | Hardware out | | \--------------/
+ * \--------------/ |
+ * v
+ * +-------------+ Software gain per channel.
+ * | Master Gain |<-- 18i20 only: Switch per channel
+ * +------+------+ to select HW or SW gain control.
+ * |
+ * |10chn
+ * /--------------\ |
+ * | Analogue |<------/
+ * | Hardware out |
+ * \--------------/
+ * </ditaa>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "helper.h"
+
+#include "mixer_scarlett_gen2.h"
+
+/* device_setup value to enable */
+#define SCARLETT2_ENABLE 0x01
+
+/* some gui mixers can't handle negative ctl values */
+#define SCARLETT2_VOLUME_BIAS 127
+
+/* mixer range from -80dB to +6dB in 0.5dB steps */
+#define SCARLETT2_MIXER_MIN_DB -80
+#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2)
+#define SCARLETT2_MIXER_MAX_DB 6
+#define SCARLETT2_MIXER_MAX_VALUE \
+ ((SCARLETT2_MIXER_MAX_DB - SCARLETT2_MIXER_MIN_DB) * 2)
+
+/* map from (dB + 80) * 2 to mixer value
+ * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20)))
+ */
+static const u16 scarlett2_mixer_values[173] = {
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 23, 24, 25, 27, 29, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51,
+ 54, 57, 61, 65, 68, 73, 77, 81, 86, 91, 97, 103, 109, 115,
+ 122, 129, 137, 145, 154, 163, 173, 183, 194, 205, 217, 230,
+ 244, 259, 274, 290, 307, 326, 345, 365, 387, 410, 434, 460,
+ 487, 516, 547, 579, 614, 650, 689, 730, 773, 819, 867, 919,
+ 973, 1031, 1092, 1157, 1225, 1298, 1375, 1456, 1543, 1634,
+ 1731, 1833, 1942, 2057, 2179, 2308, 2445, 2590, 2744, 2906,
+ 3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168,
+ 5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191,
+ 9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430,
+ 16345
+};
+
+/* Maximum number of analogue outputs */
+#define SCARLETT2_ANALOGUE_MAX 10
+
+/* Maximum number of level and pad switches */
+#define SCARLETT2_LEVEL_SWITCH_MAX 2
+#define SCARLETT2_PAD_SWITCH_MAX 4
+
+/* Maximum number of inputs to the mixer */
+#define SCARLETT2_INPUT_MIX_MAX 18
+
+/* Maximum number of outputs from the mixer */
+#define SCARLETT2_OUTPUT_MIX_MAX 10
+
+/* Maximum size of the data in the USB mux assignment message:
+ * 18 inputs, 20 outputs, 18 matrix inputs, 8 spare
+ */
+#define SCARLETT2_MUX_MAX 64
+
+/* Number of meters:
+ * 18 inputs, 20 outputs, 18 matrix inputs
+ */
+#define SCARLETT2_NUM_METERS 56
+
+/* Hardware port types:
+ * - None (no input to mux)
+ * - Analogue I/O
+ * - S/PDIF I/O
+ * - ADAT I/O
+ * - Mixer I/O
+ * - PCM I/O
+ */
+enum {
+ SCARLETT2_PORT_TYPE_NONE = 0,
+ SCARLETT2_PORT_TYPE_ANALOGUE = 1,
+ SCARLETT2_PORT_TYPE_SPDIF = 2,
+ SCARLETT2_PORT_TYPE_ADAT = 3,
+ SCARLETT2_PORT_TYPE_MIX = 4,
+ SCARLETT2_PORT_TYPE_PCM = 5,
+ SCARLETT2_PORT_TYPE_COUNT = 6,
+};
+
+/* Count of total I/O and number available at each sample rate */
+enum {
+ SCARLETT2_PORT_IN = 0,
+ SCARLETT2_PORT_OUT = 1,
+ SCARLETT2_PORT_OUT_44 = 2,
+ SCARLETT2_PORT_OUT_88 = 3,
+ SCARLETT2_PORT_OUT_176 = 4,
+ SCARLETT2_PORT_DIRECTIONS = 5,
+};
+
+/* Hardware buttons on the 18i20 */
+#define SCARLETT2_BUTTON_MAX 2
+
+static const char *const scarlett2_button_names[SCARLETT2_BUTTON_MAX] = {
+ "Mute", "Dim"
+};
+
+/* Description of each hardware port type:
+ * - id: hardware ID for this port type
+ * - num: number of sources/destinations of this port type
+ * - src_descr: printf format string for mux input selections
+ * - src_num_offset: added to channel number for the fprintf
+ * - dst_descr: printf format string for mixer controls
+ */
+struct scarlett2_ports {
+ u16 id;
+ int num[SCARLETT2_PORT_DIRECTIONS];
+ const char * const src_descr;
+ int src_num_offset;
+ const char * const dst_descr;
+};
+
+struct scarlett2_device_info {
+ u8 line_out_hw_vol; /* line out hw volume is sw controlled */
+ u8 button_count; /* number of buttons */
+ u8 level_input_count; /* inputs with level selectable */
+ u8 pad_input_count; /* inputs with pad selectable */
+ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
+ struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT];
+};
+
+struct scarlett2_mixer_data {
+ struct usb_mixer_interface *mixer;
+ struct mutex usb_mutex; /* prevent sending concurrent USB requests */
+ struct mutex data_mutex; /* lock access to this data */
+ struct delayed_work work;
+ const struct scarlett2_device_info *info;
+ int num_mux_srcs;
+ u16 scarlett2_seq;
+ u8 vol_updated;
+ u8 master_vol;
+ u8 vol[SCARLETT2_ANALOGUE_MAX];
+ u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
+ u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX];
+ u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
+ u8 buttons[SCARLETT2_BUTTON_MAX];
+ struct snd_kcontrol *master_vol_ctl;
+ struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
+ struct snd_kcontrol *button_ctls[SCARLETT2_BUTTON_MAX];
+ u8 mux[SCARLETT2_MUX_MAX];
+ u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX];
+};
+
+/*** Model-specific data ***/
+
+static const struct scarlett2_device_info s6i6_gen2_info = {
+ /* The first two analogue inputs can be switched between line
+ * and instrument levels.
+ */
+ .level_input_count = 2,
+
+ /* The first two analogue inputs have an optional pad. */
+ .pad_input_count = 2,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ "Headphones L",
+ "Headphones R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 8 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 4, 4, 4, 4, 4 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 6, 6, 6, 6, 6 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+static const struct scarlett2_device_info s18i8_gen2_info = {
+ /* The first two analogue inputs can be switched between line
+ * and instrument levels.
+ */
+ .level_input_count = 2,
+
+ /* The first four analogue inputs have an optional pad. */
+ .pad_input_count = 4,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ "Headphones 1 L",
+ "Headphones 1 R",
+ "Headphones 2 L",
+ "Headphones 2 R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 4 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 8, 6, 6, 6, 6 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ /* S/PDIF outputs aren't available at 192KHz
+ * but are included in the USB mux I/O
+ * assignment message anyway
+ */
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x200,
+ .num = { 8, 0, 0, 0, 0 },
+ .src_descr = "ADAT %d",
+ .src_num_offset = 1,
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 20, 18, 18, 14, 10 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+static const struct scarlett2_device_info s18i20_gen2_info = {
+ /* The analogue line outputs on the 18i20 can be switched
+ * between software and hardware volume control
+ */
+ .line_out_hw_vol = 1,
+
+ /* Mute and dim buttons */
+ .button_count = 2,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Headphones 1 L",
+ "Headphones 1 R",
+ "Headphones 2 L",
+ "Headphones 2 R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 6 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 8, 10, 10, 10, 10 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ /* S/PDIF outputs aren't available at 192KHz
+ * but are included in the USB mux I/O
+ * assignment message anyway
+ */
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x200,
+ .num = { 8, 8, 8, 4, 0 },
+ .src_descr = "ADAT %d",
+ .src_num_offset = 1,
+ .dst_descr = "ADAT Output %d Playback"
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 20, 18, 18, 14, 10 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+/* get the starting port index number for a given port type/direction */
+static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports,
+ int direction, int port_type)
+{
+ int i, num = 0;
+
+ for (i = 0; i < port_type; i++)
+ num += ports[i].num[direction];
+
+ return num;
+}
+
+/*** USB Interactions ***/
+
+/* Vendor-Specific Interface, Endpoint, MaxPacketSize, Interval */
+#define SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE 5
+#define SCARLETT2_USB_INTERRUPT_ENDPOINT 4
+#define SCARLETT2_USB_INTERRUPT_MAX_DATA 64
+#define SCARLETT2_USB_INTERRUPT_INTERVAL 3
+
+/* Interrupt flags for volume and mute/dim button changes */
+#define SCARLETT2_USB_INTERRUPT_VOL_CHANGE 0x400000
+#define SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE 0x200000
+
+/* Commands for sending/receiving requests/responses */
+#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ 2
+#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP 3
+
+#define SCARLETT2_USB_INIT_SEQ 0x00000000
+#define SCARLETT2_USB_GET_METER_LEVELS 0x00001001
+#define SCARLETT2_USB_SET_MIX 0x00002002
+#define SCARLETT2_USB_SET_MUX 0x00003002
+#define SCARLETT2_USB_GET_DATA 0x00800000
+#define SCARLETT2_USB_SET_DATA 0x00800001
+#define SCARLETT2_USB_DATA_CMD 0x00800002
+#define SCARLETT2_USB_CONFIG_SAVE 6
+
+#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31
+#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
+
+/* volume status is read together (matches scarlett2_config_items[]) */
+struct scarlett2_usb_volume_status {
+ /* mute & dim buttons */
+ u8 buttons[SCARLETT2_BUTTON_MAX];
+
+ u8 pad1;
+
+ /* software volume setting */
+ s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+
+ /* actual volume of output inc. dim (-18dB) */
+ s16 hw_vol[SCARLETT2_ANALOGUE_MAX];
+
+ u8 pad2[SCARLETT2_ANALOGUE_MAX];
+
+ /* sw (0) or hw (1) controlled */
+ u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
+
+ u8 pad3[6];
+
+ /* front panel volume knob */
+ s16 master_vol;
+} __packed;
+
+/* Configuration parameters that can be read and written */
+enum {
+ SCARLETT2_CONFIG_BUTTONS = 0,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1,
+ SCARLETT2_CONFIG_SW_HW_SWITCH = 2,
+ SCARLETT2_CONFIG_LEVEL_SWITCH = 3,
+ SCARLETT2_CONFIG_PAD_SWITCH = 4,
+ SCARLETT2_CONFIG_COUNT = 5
+};
+
+/* Location, size, and activation command number for the configuration
+ * parameters
+ */
+struct scarlett2_config {
+ u8 offset;
+ u8 size;
+ u8 activate;
+};
+
+static const struct scarlett2_config
+ scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = {
+ /* Mute/Dim Buttons */
+ {
+ .offset = 0x31,
+ .size = 1,
+ .activate = 2
+ },
+
+ /* Line Out Volume */
+ {
+ .offset = 0x34,
+ .size = 2,
+ .activate = 1
+ },
+
+ /* SW/HW Volume Switch */
+ {
+ .offset = 0x66,
+ .size = 1,
+ .activate = 3
+ },
+
+ /* Level Switch */
+ {
+ .offset = 0x7c,
+ .size = 1,
+ .activate = 7
+ },
+
+ /* Pad Switch */
+ {
+ .offset = 0x84,
+ .size = 1,
+ .activate = 8
+ }
+};
+
+/* proprietary request/response format */
+struct scarlett2_usb_packet {
+ u32 cmd;
+ u16 size;
+ u16 seq;
+ u32 error;
+ u32 pad;
+ u8 data[];
+};
+
+#define SCARLETT2_USB_PACKET_LEN (sizeof(struct scarlett2_usb_packet))
+
+static void scarlett2_fill_request_header(struct scarlett2_mixer_data *private,
+ struct scarlett2_usb_packet *req,
+ u32 cmd, u16 req_size)
+{
+ /* sequence must go up by 1 for each request */
+ u16 seq = private->scarlett2_seq++;
+
+ req->cmd = cpu_to_le32(cmd);
+ req->size = cpu_to_le16(req_size);
+ req->seq = cpu_to_le16(seq);
+ req->error = 0;
+ req->pad = 0;
+}
+
+/* Send a proprietary format request to the Scarlett interface */
+static int scarlett2_usb(
+ struct usb_mixer_interface *mixer, u32 cmd,
+ void *req_data, u16 req_size, void *resp_data, u16 resp_size)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size;
+ u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size;
+ struct scarlett2_usb_packet *req = NULL, *resp = NULL;
+ int err = 0;
+
+ req = kmalloc(req_buf_size, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ resp = kmalloc(resp_buf_size, GFP_KERNEL);
+ if (!resp) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ mutex_lock(&private->usb_mutex);
+
+ /* build request message and send it */
+
+ scarlett2_fill_request_header(private, req, cmd, req_size);
+
+ if (req_size)
+ memcpy(req->data, req_data, req_size);
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ 0,
+ SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE,
+ req,
+ req_buf_size);
+
+ if (err != req_buf_size) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB request result cmd %x was %d\n",
+ cmd, err);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ /* send a second message to get the response */
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ 0,
+ SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE,
+ resp,
+ resp_buf_size);
+
+ /* validate the response */
+
+ if (err != resp_buf_size) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB response result cmd %x was %d\n",
+ cmd, err);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (resp->cmd != req->cmd ||
+ resp->seq != req->seq ||
+ resp_size != le16_to_cpu(resp->size) ||
+ resp->error ||
+ resp->pad) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB invalid response; "
+ "cmd tx/rx %d/%d seq %d/%d size %d/%d "
+ "error %d pad %d\n",
+ le16_to_cpu(req->cmd), le16_to_cpu(resp->cmd),
+ le16_to_cpu(req->seq), le16_to_cpu(resp->seq),
+ resp_size, le16_to_cpu(resp->size),
+ le16_to_cpu(resp->error),
+ le16_to_cpu(resp->pad));
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (resp_size > 0)
+ memcpy(resp_data, resp->data, resp_size);
+
+unlock:
+ mutex_unlock(&private->usb_mutex);
+error:
+ kfree(req);
+ kfree(resp);
+ return err;
+}
+
+/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
+static void scarlett2_config_save(struct usb_mixer_interface *mixer)
+{
+ u32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE);
+
+ scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req, sizeof(u32),
+ NULL, 0);
+}
+
+/* Delayed work to save config */
+static void scarlett2_config_save_work(struct work_struct *work)
+{
+ struct scarlett2_mixer_data *private =
+ container_of(work, struct scarlett2_mixer_data, work.work);
+
+ scarlett2_config_save(private->mixer);
+}
+
+/* Send a USB message to set a configuration parameter (volume level,
+ * sw/hw volume switch, line/inst level switch, or pad switch)
+ */
+static int scarlett2_usb_set_config(
+ struct usb_mixer_interface *mixer,
+ int config_item_num, int index, int value)
+{
+ const struct scarlett2_config config_item =
+ scarlett2_config_items[config_item_num];
+ struct {
+ u32 offset;
+ u32 bytes;
+ s32 value;
+ } __packed req;
+ u32 req2;
+ int err;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ /* Cancel any pending NVRAM save */
+ cancel_delayed_work_sync(&private->work);
+
+ /* Send the configuration parameter data */
+ req.offset = cpu_to_le32(config_item.offset + index * config_item.size);
+ req.bytes = cpu_to_le32(config_item.size);
+ req.value = cpu_to_le32(value);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA,
+ &req, sizeof(u32) * 2 + config_item.size,
+ NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* Activate the change */
+ req2 = cpu_to_le32(config_item.activate);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req2, sizeof(req2), NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* Schedule the change to be written to NVRAM */
+ schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
+
+ return 0;
+}
+
+/* Send a USB message to get data; result placed in *buf */
+static int scarlett2_usb_get(
+ struct usb_mixer_interface *mixer,
+ int offset, void *buf, int size)
+{
+ struct {
+ u32 offset;
+ u32 size;
+ } __packed req;
+
+ req.offset = cpu_to_le32(offset);
+ req.size = cpu_to_le32(size);
+ return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA,
+ &req, sizeof(req), buf, size);
+}
+
+/* Send a USB message to get configuration parameters; result placed in *buf */
+static int scarlett2_usb_get_config(
+ struct usb_mixer_interface *mixer,
+ int config_item_num, int count, void *buf)
+{
+ const struct scarlett2_config config_item =
+ scarlett2_config_items[config_item_num];
+ int size = config_item.size * count;
+
+ return scarlett2_usb_get(mixer, config_item.offset, buf, size);
+}
+
+/* Send a USB message to get volume status; result placed in *buf */
+static int scarlett2_usb_get_volume_status(
+ struct usb_mixer_interface *mixer,
+ struct scarlett2_usb_volume_status *buf)
+{
+ return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET,
+ buf, sizeof(*buf));
+}
+
+/* Send a USB message to set the volumes for all inputs of one mix
+ * (values obtained from private->mix[])
+ */
+static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer,
+ int mix_num)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ struct {
+ u16 mix_num;
+ u16 data[SCARLETT2_INPUT_MIX_MAX];
+ } __packed req;
+
+ int i, j;
+ int num_mixer_in =
+ info->ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+
+ req.mix_num = cpu_to_le16(mix_num);
+
+ for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++)
+ req.data[i] = cpu_to_le16(
+ scarlett2_mixer_values[private->mix[j]]
+ );
+
+ return scarlett2_usb(mixer, SCARLETT2_USB_SET_MIX,
+ &req, (num_mixer_in + 1) * sizeof(u16),
+ NULL, 0);
+}
+
+/* Convert a port number index (per info->ports) to a hardware ID */
+static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports,
+ int num)
+{
+ int port_type;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ if (num < ports[port_type].num[SCARLETT2_PORT_IN])
+ return ports[port_type].id | num;
+ num -= ports[port_type].num[SCARLETT2_PORT_IN];
+ }
+
+ /* Oops */
+ return 0;
+}
+
+/* Send USB messages to set mux inputs */
+static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int rate, port_dir_rate;
+
+ static const int assignment_order[SCARLETT2_PORT_TYPE_COUNT] = {
+ SCARLETT2_PORT_TYPE_PCM,
+ SCARLETT2_PORT_TYPE_ANALOGUE,
+ SCARLETT2_PORT_TYPE_SPDIF,
+ SCARLETT2_PORT_TYPE_ADAT,
+ SCARLETT2_PORT_TYPE_MIX,
+ SCARLETT2_PORT_TYPE_NONE,
+ };
+
+ struct {
+ u16 pad;
+ u16 num;
+ u32 data[SCARLETT2_MUX_MAX];
+ } __packed req;
+
+ req.pad = 0;
+
+ /* mux settings for each rate */
+ for (rate = 0, port_dir_rate = SCARLETT2_PORT_OUT_44;
+ port_dir_rate <= SCARLETT2_PORT_OUT_176;
+ rate++, port_dir_rate++) {
+ int order_num, i, err;
+
+ req.num = cpu_to_le16(rate);
+
+ for (order_num = 0, i = 0;
+ order_num < SCARLETT2_PORT_TYPE_COUNT;
+ order_num++) {
+ int port_type = assignment_order[order_num];
+ int j = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_OUT,
+ port_type);
+ int port_id = ports[port_type].id;
+ int channel;
+
+ for (channel = 0;
+ channel < ports[port_type].num[port_dir_rate];
+ channel++, i++, j++)
+ /* lower 12 bits for the destination and
+ * next 12 bits for the source
+ */
+ req.data[i] = !port_id
+ ? 0
+ : cpu_to_le32(
+ port_id |
+ channel |
+ scarlett2_mux_src_num_to_id(
+ ports, private->mux[j]
+ ) << 12
+ );
+
+ /* skip private->mux[j] entries not output */
+ j += ports[port_type].num[SCARLETT2_PORT_OUT] -
+ ports[port_type].num[port_dir_rate];
+ }
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX,
+ &req, (i + 1) * sizeof(u32),
+ NULL, 0);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Send USB message to get meter levels */
+static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
+ u16 *levels)
+{
+ struct {
+ u16 pad;
+ u16 num_meters;
+ u32 magic;
+ } __packed req;
+ u32 resp[SCARLETT2_NUM_METERS];
+ int i, err;
+
+ req.pad = 0;
+ req.num_meters = cpu_to_le16(SCARLETT2_NUM_METERS);
+ req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER_LEVELS,
+ &req, sizeof(req), resp, sizeof(resp));
+ if (err < 0)
+ return err;
+
+ /* copy, convert to u16 */
+ for (i = 0; i < SCARLETT2_NUM_METERS; i++)
+ levels[i] = resp[i];
+
+ return 0;
+}
+
+/*** Control Functions ***/
+
+/* helper function to create a new control */
+static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer,
+ const struct snd_kcontrol_new *ncontrol,
+ int index, int channels, const char *name,
+ struct snd_kcontrol **kctl_return)
+{
+ struct snd_kcontrol *kctl;
+ struct usb_mixer_elem_info *elem;
+ int err;
+
+ elem = kzalloc(sizeof(*elem), GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->control = index;
+ elem->head.id = index;
+ elem->channels = channels;
+
+ kctl = snd_ctl_new1(ncontrol, elem);
+ if (!kctl) {
+ kfree(elem);
+ return -ENOMEM;
+ }
+ kctl->private_free = snd_usb_mixer_elem_free;
+
+ strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
+
+ err = snd_usb_mixer_add_control(&elem->head, kctl);
+ if (err < 0)
+ return err;
+
+ if (kctl_return)
+ *kctl_return = kctl;
+
+ return 0;
+}
+
+/*** Analogue Line Out Volume Controls ***/
+
+/* Update hardware volume controls after receiving notification that
+ * they have changed
+ */
+static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ struct scarlett2_usb_volume_status volume_status;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int err, i;
+
+ private->vol_updated = 0;
+
+ err = scarlett2_usb_get_volume_status(mixer, &volume_status);
+ if (err < 0)
+ return err;
+
+ private->master_vol = clamp(
+ volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+
+ for (i = 0; i < num_line_out; i++) {
+ if (private->vol_sw_hw_switch[i])
+ private->vol[i] = private->master_vol;
+ }
+
+ for (i = 0; i < private->info->button_count; i++)
+ private->buttons[i] = !!volume_status.buttons[i];
+
+ return 0;
+}
+
+static int scarlett2_volume_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_VOLUME_BIAS;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.integer.value[0] = private->master_vol;
+ return 0;
+}
+
+static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.integer.value[0] = private->vol[index];
+ return 0;
+}
+
+static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->vol[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->vol[index] = val;
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ index, val - SCARLETT2_VOLUME_BIAS);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett2_gain, -SCARLETT2_VOLUME_BIAS * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_master_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_master_volume_ctl_get,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_gain }
+};
+
+static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_volume_ctl_get,
+ .put = scarlett2_volume_ctl_put,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_gain }
+};
+
+/*** HW/SW Volume Switch Controls ***/
+
+static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "SW", "HW"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, values);
+}
+
+static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->vol_sw_hw_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->vol_sw_hw_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->vol_sw_hw_switch[index] = val;
+
+ /* Change access mode to RO (hardware controlled volume)
+ * or RW (software controlled volume)
+ */
+ if (val)
+ private->vol_ctls[index]->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ private->vol_ctls[index]->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ /* Reset volume to master volume */
+ private->vol[index] = private->master_vol;
+
+ /* Set SW volume to current HW volume */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ index, private->master_vol - SCARLETT2_VOLUME_BIAS);
+ if (err < 0)
+ goto unlock;
+
+ /* Notify of RO/RW change */
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->vol_ctls[index]->id);
+
+ /* Send SW/HW switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_sw_hw_enum_ctl_info,
+ .get = scarlett2_sw_hw_enum_ctl_get,
+ .put = scarlett2_sw_hw_enum_ctl_put,
+};
+
+/*** Line Level/Instrument Level Switch Controls ***/
+
+static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "Line", "Inst"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, values);
+}
+
+static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->level_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->level_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->level_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_level_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_level_enum_ctl_info,
+ .get = scarlett2_level_enum_ctl_get,
+ .put = scarlett2_level_enum_ctl_put,
+};
+
+/*** Pad Switch Controls ***/
+
+static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->pad_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->pad_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->pad_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_pad_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_pad_ctl_get,
+ .put = scarlett2_pad_ctl_put,
+};
+
+/*** Mute/Dim Controls ***/
+
+static int scarlett2_button_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.enumerated.item[0] = private->buttons[elem->control];
+ return 0;
+}
+
+static int scarlett2_button_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->buttons[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->buttons[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_BUTTONS,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_button_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_button_ctl_get,
+ .put = scarlett2_button_ctl_put
+};
+
+/*** Create the analogue output controls ***/
+
+static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int err, i;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ /* Add R/O HW volume control */
+ if (info->line_out_hw_vol) {
+ snprintf(s, sizeof(s), "Master HW Playback Volume");
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_master_volume_ctl,
+ 0, 1, s, &private->master_vol_ctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add volume controls */
+ for (i = 0; i < num_line_out; i++) {
+
+ /* Fader */
+ if (info->line_out_descrs[i])
+ snprintf(s, sizeof(s),
+ "Line %02d (%s) Playback Volume",
+ i + 1, info->line_out_descrs[i]);
+ else
+ snprintf(s, sizeof(s),
+ "Line %02d Playback Volume",
+ i + 1);
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_line_out_volume_ctl,
+ i, 1, s, &private->vol_ctls[i]);
+ if (err < 0)
+ return err;
+
+ /* Make the fader read-only if the SW/HW switch is set to HW */
+ if (private->vol_sw_hw_switch[i])
+ private->vol_ctls[i]->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ /* SW/HW Switch */
+ if (info->line_out_hw_vol) {
+ snprintf(s, sizeof(s),
+ "Line Out %02d Volume Control Playback Enum",
+ i + 1);
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_sw_hw_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* Add HW button controls */
+ for (i = 0; i < private->info->button_count; i++) {
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_button_ctl,
+ i, 1, scarlett2_button_names[i],
+ &private->button_ctls[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*** Create the analogue input controls ***/
+
+static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ /* Add input level (line/inst) controls */
+ for (i = 0; i < info->level_input_count; i++) {
+ snprintf(s, sizeof(s), "Line In %d Level Capture Enum", i + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add input pad controls */
+ for (i = 0; i < info->pad_input_count; i++) {
+ snprintf(s, sizeof(s), "Line In %d Pad Capture Switch", i + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*** Mixer Volume Controls ***/
+
+static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_MIXER_MAX_VALUE;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->mix[elem->control];
+ return 0;
+}
+
+static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int oval, val, num_mixer_in, mix_num, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->mix[elem->control];
+ val = ucontrol->value.integer.value[0];
+ num_mixer_in = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+ mix_num = elem->control / num_mixer_in;
+
+ if (oval == val)
+ goto unlock;
+
+ private->mix[elem->control] = val;
+ err = scarlett2_usb_set_mix(mixer, mix_num);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett2_mixer,
+ SCARLETT2_MIXER_MIN_DB * 100,
+ SCARLETT2_MIXER_MAX_DB * 100
+);
+
+static const struct snd_kcontrol_new scarlett2_mixer_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_mixer_ctl_info,
+ .get = scarlett2_mixer_ctl_get,
+ .put = scarlett2_mixer_ctl_put,
+ .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */
+ .tlv = { .p = db_scale_scarlett2_mixer }
+};
+
+static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int err, i, j;
+ int index;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ int num_inputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+ int num_outputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_IN];
+
+ for (i = 0, index = 0; i < num_outputs; i++) {
+ for (j = 0; j < num_inputs; j++, index++) {
+ snprintf(s, sizeof(s),
+ "Mix %c Input %02d Playback Volume",
+ 'A' + i, j + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl,
+ index, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*** Mux Source Selection Controls ***/
+
+static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ unsigned int item = uinfo->value.enumerated.item;
+ int items = private->num_mux_srcs;
+ int port_type;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = elem->channels;
+ uinfo->value.enumerated.items = items;
+
+ if (item >= items)
+ item = uinfo->value.enumerated.item = items - 1;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ if (item < ports[port_type].num[SCARLETT2_PORT_IN]) {
+ sprintf(uinfo->value.enumerated.name,
+ ports[port_type].src_descr,
+ item + ports[port_type].src_num_offset);
+ return 0;
+ }
+ item -= ports[port_type].num[SCARLETT2_PORT_IN];
+ }
+
+ return -EINVAL;
+}
+
+static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] = private->mux[elem->control];
+ return 0;
+}
+
+static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->mux[index];
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, private->num_mux_srcs - 1L);
+
+ if (oval == val)
+ goto unlock;
+
+ private->mux[index] = val;
+ err = scarlett2_usb_set_mux(mixer);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_mux_src_enum_ctl_info,
+ .get = scarlett2_mux_src_enum_ctl_get,
+ .put = scarlett2_mux_src_enum_ctl_put,
+};
+
+static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int port_type, channel, i;
+
+ for (i = 0, port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ for (channel = 0;
+ channel < ports[port_type].num[SCARLETT2_PORT_OUT];
+ channel++, i++) {
+ int err;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ const char *const descr = ports[port_type].dst_descr;
+
+ snprintf(s, sizeof(s) - 5, descr, channel + 1);
+ strcat(s, " Enum");
+
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_mux_src_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*** Meter Controls ***/
+
+static int scarlett2_meter_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 4095;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ u16 meter_levels[SCARLETT2_NUM_METERS];
+ int i, err;
+
+ err = scarlett2_usb_get_meter_levels(elem->head.mixer, meter_levels);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < elem->channels; i++)
+ ucontrol->value.integer.value[i] = meter_levels[i];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_meter_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .name = "",
+ .info = scarlett2_meter_ctl_info,
+ .get = scarlett2_meter_ctl_get
+};
+
+static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer)
+{
+ return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl,
+ 0, SCARLETT2_NUM_METERS,
+ "Level Meter", NULL);
+}
+
+/*** Cleanup/Suspend Callbacks ***/
+
+static void scarlett2_private_free(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ cancel_delayed_work_sync(&private->work);
+ kfree(private);
+ mixer->private_data = NULL;
+}
+
+static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ if (cancel_delayed_work_sync(&private->work))
+ scarlett2_config_save(private->mixer);
+}
+
+/*** Initialisation ***/
+
+static int scarlett2_count_mux_srcs(const struct scarlett2_ports *ports)
+{
+ int port_type, count = 0;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++)
+ count += ports[port_type].num[SCARLETT2_PORT_IN];
+
+ return count;
+}
+
+/* Default routing connects PCM outputs and inputs to Analogue,
+ * S/PDIF, then ADAT
+ */
+static void scarlett2_init_routing(u8 *mux,
+ const struct scarlett2_ports *ports)
+{
+ int i, input_num, input_count, port_type;
+ int output_num, output_count, port_type_connect_num;
+
+ static const int connect_order[] = {
+ SCARLETT2_PORT_TYPE_ANALOGUE,
+ SCARLETT2_PORT_TYPE_SPDIF,
+ SCARLETT2_PORT_TYPE_ADAT,
+ -1
+ };
+
+ /* Assign PCM inputs (routing outputs) */
+ output_num = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_OUT,
+ SCARLETT2_PORT_TYPE_PCM);
+ output_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_OUT];
+
+ for (port_type = connect_order[port_type_connect_num = 0];
+ port_type >= 0;
+ port_type = connect_order[++port_type_connect_num]) {
+ input_num = scarlett2_get_port_start_num(
+ ports, SCARLETT2_PORT_IN, port_type);
+ input_count = ports[port_type].num[SCARLETT2_PORT_IN];
+ for (i = 0;
+ i < input_count && output_count;
+ i++, output_count--)
+ mux[output_num++] = input_num++;
+ }
+
+ /* Assign PCM outputs (routing inputs) */
+ input_num = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_IN,
+ SCARLETT2_PORT_TYPE_PCM);
+ input_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_IN];
+
+ for (port_type = connect_order[port_type_connect_num = 0];
+ port_type >= 0;
+ port_type = connect_order[++port_type_connect_num]) {
+ output_num = scarlett2_get_port_start_num(
+ ports, SCARLETT2_PORT_OUT, port_type);
+ output_count = ports[port_type].num[SCARLETT2_PORT_OUT];
+ for (i = 0;
+ i < output_count && input_count;
+ i++, input_count--)
+ mux[output_num++] = input_num++;
+ }
+}
+
+/* Initialise private data, routing, sequence number */
+static int scarlett2_init_private(struct usb_mixer_interface *mixer,
+ const struct scarlett2_device_info *info)
+{
+ struct scarlett2_mixer_data *private =
+ kzalloc(sizeof(struct scarlett2_mixer_data), GFP_KERNEL);
+
+ if (!private)
+ return -ENOMEM;
+
+ mutex_init(&private->usb_mutex);
+ mutex_init(&private->data_mutex);
+ INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
+ private->info = info;
+ private->num_mux_srcs = scarlett2_count_mux_srcs(info->ports);
+ private->scarlett2_seq = 0;
+ private->mixer = mixer;
+ mixer->private_data = private;
+ mixer->private_free = scarlett2_private_free;
+ mixer->private_suspend = scarlett2_private_suspend;
+
+ /* Setup default routing */
+ scarlett2_init_routing(private->mux, info->ports);
+
+ /* Initialise the sequence number used for the proprietary commands */
+ return scarlett2_usb(mixer, SCARLETT2_USB_INIT_SEQ, NULL, 0, NULL, 0);
+}
+
+/* Read line-in config and line-out volume settings on start */
+static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ u8 level_switches[SCARLETT2_LEVEL_SWITCH_MAX];
+ u8 pad_switches[SCARLETT2_PAD_SWITCH_MAX];
+ struct scarlett2_usb_volume_status volume_status;
+ int err, i;
+
+ if (info->level_input_count) {
+ err = scarlett2_usb_get_config(
+ mixer,
+ SCARLETT2_CONFIG_LEVEL_SWITCH,
+ info->level_input_count,
+ level_switches);
+ if (err < 0)
+ return err;
+ for (i = 0; i < info->level_input_count; i++)
+ private->level_switch[i] = level_switches[i];
+ }
+
+ if (info->pad_input_count) {
+ err = scarlett2_usb_get_config(
+ mixer,
+ SCARLETT2_CONFIG_PAD_SWITCH,
+ info->pad_input_count,
+ pad_switches);
+ if (err < 0)
+ return err;
+ for (i = 0; i < info->pad_input_count; i++)
+ private->pad_switch[i] = pad_switches[i];
+ }
+
+ err = scarlett2_usb_get_volume_status(mixer, &volume_status);
+ if (err < 0)
+ return err;
+
+ private->master_vol = clamp(
+ volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+
+ for (i = 0; i < num_line_out; i++) {
+ int volume;
+
+ private->vol_sw_hw_switch[i] =
+ info->line_out_hw_vol
+ && volume_status.sw_hw_switch[i];
+
+ volume = private->vol_sw_hw_switch[i]
+ ? volume_status.master_vol
+ : volume_status.sw_vol[i];
+ volume = clamp(volume + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+ private->vol[i] = volume;
+ }
+
+ for (i = 0; i < info->button_count; i++)
+ private->buttons[i] = !!volume_status.buttons[i];
+
+ return 0;
+}
+
+/* Notify on volume change */
+static void scarlett2_mixer_interrupt_vol_change(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int i;
+
+ private->vol_updated = 1;
+
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->master_vol_ctl->id);
+
+ for (i = 0; i < num_line_out; i++) {
+ if (!private->vol_sw_hw_switch[i])
+ continue;
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->vol_ctls[i]->id);
+ }
+}
+
+/* Notify on button change */
+static void scarlett2_mixer_interrupt_button_change(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int i;
+
+ private->vol_updated = 1;
+
+ for (i = 0; i < private->info->button_count; i++)
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->button_ctls[i]->id);
+}
+
+/* Interrupt callback */
+static void scarlett2_mixer_interrupt(struct urb *urb)
+{
+ struct usb_mixer_interface *mixer = urb->context;
+ int len = urb->actual_length;
+ int ustatus = urb->status;
+ u32 data;
+
+ if (ustatus != 0)
+ goto requeue;
+
+ if (len == 8) {
+ data = le32_to_cpu(*(u32 *)urb->transfer_buffer);
+ if (data & SCARLETT2_USB_INTERRUPT_VOL_CHANGE)
+ scarlett2_mixer_interrupt_vol_change(mixer);
+ if (data & SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE)
+ scarlett2_mixer_interrupt_button_change(mixer);
+ } else {
+ usb_audio_err(mixer->chip,
+ "scarlett mixer interrupt length %d\n", len);
+ }
+
+requeue:
+ if (ustatus != -ENOENT &&
+ ustatus != -ECONNRESET &&
+ ustatus != -ESHUTDOWN) {
+ urb->dev = mixer->chip->dev;
+ usb_submit_urb(urb, GFP_ATOMIC);
+ }
+}
+
+static int scarlett2_mixer_status_create(struct usb_mixer_interface *mixer)
+{
+ struct usb_device *dev = mixer->chip->dev;
+ unsigned int pipe = usb_rcvintpipe(dev,
+ SCARLETT2_USB_INTERRUPT_ENDPOINT);
+ void *transfer_buffer;
+
+ if (mixer->urb) {
+ usb_audio_err(mixer->chip,
+ "%s: mixer urb already in use!\n", __func__);
+ return 0;
+ }
+
+ if (snd_usb_pipe_sanity_check(dev, pipe))
+ return -EINVAL;
+
+ mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mixer->urb)
+ return -ENOMEM;
+
+ transfer_buffer = kmalloc(SCARLETT2_USB_INTERRUPT_MAX_DATA, GFP_KERNEL);
+ if (!transfer_buffer)
+ return -ENOMEM;
+
+ usb_fill_int_urb(mixer->urb, dev, pipe,
+ transfer_buffer, SCARLETT2_USB_INTERRUPT_MAX_DATA,
+ scarlett2_mixer_interrupt, mixer,
+ SCARLETT2_USB_INTERRUPT_INTERVAL);
+
+ return usb_submit_urb(mixer->urb, GFP_KERNEL);
+}
+
+/* Entry point */
+int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer)
+{
+ const struct scarlett2_device_info *info;
+ int err;
+
+ /* only use UAC_VERSION_2 */
+ if (!mixer->protocol)
+ return 0;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x1235, 0x8203):
+ info = &s6i6_gen2_info;
+ break;
+ case USB_ID(0x1235, 0x8204):
+ info = &s18i8_gen2_info;
+ break;
+ case USB_ID(0x1235, 0x8201):
+ info = &s18i20_gen2_info;
+ break;
+ default: /* device not (yet) supported */
+ return -EINVAL;
+ }
+
+ if (!(mixer->chip->setup & SCARLETT2_ENABLE)) {
+ usb_audio_err(mixer->chip,
+ "Focusrite Scarlett Gen 2 Mixer Driver disabled; "
+ "use options snd_usb_audio device_setup=1 "
+ "to enable and report any issues to g@b4.vu");
+ return 0;
+ }
+
+ /* Initialise private data, routing, sequence number */
+ err = scarlett2_init_private(mixer, info);
+ if (err < 0)
+ return err;
+
+ /* Read volume levels and controls from the interface */
+ err = scarlett2_read_configs(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the analogue output controls */
+ err = scarlett2_add_line_out_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the analogue input controls */
+ err = scarlett2_add_line_in_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the input, output, and mixer mux input selections */
+ err = scarlett2_add_mux_enums(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the matrix mixer controls */
+ err = scarlett2_add_mixer_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the level meter controls */
+ err = scarlett2_add_meter_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ /* Set up the interrupt polling if there are hardware buttons */
+ if (info->button_count) {
+ err = scarlett2_mixer_status_create(mixer);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/usb/mixer_scarlett_gen2.h b/sound/usb/mixer_scarlett_gen2.h
new file mode 100644
index 000000000000..52e1dad77afd
--- /dev/null
+++ b/sound/usb/mixer_scarlett_gen2.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_MIXER_SCARLETT_GEN2_H
+#define __USB_MIXER_SCARLETT_GEN2_H
+
+int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer);
+
+#endif /* __USB_MIXER_SCARLETT_GEN2_H */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 75b96929f76c..33cd26763c0e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -339,6 +339,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ep = 0x81;
ifnum = 2;
goto add_sync_ep_from_ifnum;
+ case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */
case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */
ep = 0x81;
ifnum = 1;
@@ -456,6 +457,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
}
ep = get_endpoint(alts, 1)->bEndpointAddress;
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ get_endpoint(alts, 0)->bSynchAddress != 0 &&
((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
dev_err(&dev->dev,
diff --git a/sound/usb/power.c b/sound/usb/power.c
index bd303a1ba1b7..606a2cb23eab 100644
--- a/sound/usb/power.c
+++ b/sound/usb/power.c
@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p;
int i;
+ if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
+ continue;
for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index e918ce346027..70c338f3ae24 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3534,5 +3534,62 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}
}
},
+{
+ /*
+ * PIONEER DJ DDJ-SX3
+ * PCM is 12 channels out, 10 channels in @ 44.1 fixed
+ * interface 0, vendor class alt setting 1 for endpoints 5 and 0x86
+ * The feedback for the output is the input.
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0023),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 12,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x05,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x86,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC|
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 78858918cbc1..25faf2d3c639 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd)
return -ENODEV;
+ if (!(injd && snd_usb_validate_midi_desc(injd)) ||
+ !(outjd && snd_usb_validate_midi_desc(outjd)))
+ return -ENODEV;
if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL)))
@@ -1563,7 +1566,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
struct usb_interface *iface;
/* Playback Designs */
- if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
+ USB_ID_PRODUCT(chip->usb_id) < 0x0110) {
switch (fp->altsetting) {
case 1:
fp->dsd_dop = true;
@@ -1580,9 +1584,6 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
/* XMOS based USB DACs */
switch (chip->usb_id) {
case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
- case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
- case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */
- case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
if (fp->altsetting == 2)
@@ -1596,7 +1597,6 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
- case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
@@ -1651,9 +1651,12 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
* from XMOS/Thesycon
*/
switch (USB_ID_VENDOR(chip->usb_id)) {
- case 0x20b1: /* XMOS based devices */
case 0x152a: /* Thesycon devices */
+ case 0x20b1: /* XMOS based devices */
+ case 0x22d9: /* Oppo */
+ case 0x23ba: /* Playback Designs */
case 0x25ce: /* Mytek devices */
+ case 0x278b: /* Rotel? */
case 0x2ab6: /* T+A devices */
if (fp->dsd_raw)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index e852c7fd6109..11785f9652ad 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -28,6 +28,14 @@
#include "power.h"
#include "media.h"
+static void audioformat_free(struct audioformat *fp)
+{
+ list_del(&fp->list); /* unlink for avoiding double-free */
+ kfree(fp->rate_table);
+ kfree(fp->chmap);
+ kfree(fp);
+}
+
/*
* free a substream
*/
@@ -37,11 +45,8 @@ static void free_substream(struct snd_usb_substream *subs)
if (!subs->num_formats)
return; /* not initialized */
- list_for_each_entry_safe(fp, n, &subs->fmt_list, list) {
- kfree(fp->rate_table);
- kfree(fp->chmap);
- kfree(fp);
- }
+ list_for_each_entry_safe(fp, n, &subs->fmt_list, list)
+ audioformat_free(fp);
kfree(subs->rate_list.list);
kfree(subs->str_pd);
snd_media_stream_delete(subs);
@@ -627,16 +632,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/
static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id, bool uac23)
+ int terminal_id, int protocol)
{
struct uac2_input_terminal_descriptor *term = NULL;
- size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) :
- sizeof(struct uac_input_terminal_descriptor);
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) {
- if (term->bLength < minlen)
+ if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
@@ -647,7 +650,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+ int terminal_id, int protocol)
{
/* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;
@@ -655,8 +658,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) {
- if (term->bLength >= sizeof(*term) &&
- term->bTerminalID == terminal_id)
+ if (!snd_usb_validate_audio_desc(term, protocol))
+ continue;
+ if (term->bTerminalID == terminal_id)
return term;
}
@@ -731,7 +735,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- false);
+ protocol);
if (iterm) {
num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig);
@@ -767,7 +771,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ protocol);
if (input_term) {
clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels))
@@ -776,7 +780,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ protocol);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -832,8 +837,7 @@ found_clock:
/* ok, let's parse further... */
if (snd_usb_parse_audio_format(chip, fp, format,
fmt, stream) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
@@ -1002,14 +1006,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ UAC_VERSION_3);
if (input_term) {
clock = input_term->bCSourceID;
goto found_clock;
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ UAC_VERSION_3);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -1043,9 +1048,7 @@ found_clock:
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd) {
- kfree(fp->chmap);
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
pd->pd_id = (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
@@ -1064,9 +1067,7 @@ found_clock:
/* ok, let's parse further... */
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
kfree(pd);
- kfree(fp->chmap);
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
}
@@ -1077,7 +1078,9 @@ found_clock:
return fp;
}
-int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
+static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
+ int iface_no,
+ bool *has_non_pcm, bool non_pcm)
{
struct usb_device *dev;
struct usb_interface *iface;
@@ -1178,6 +1181,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
else if (IS_ERR(fp))
return PTR_ERR(fp);
+ if (fp->fmt_type != UAC_FORMAT_TYPE_I)
+ *has_non_pcm = true;
+ if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) {
+ audioformat_free(fp);
+ kfree(pd);
+ fp = NULL;
+ pd = NULL;
+ continue;
+ }
+
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
if (protocol == UAC_VERSION_3)
err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd);
@@ -1185,11 +1198,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
- list_del(&fp->list); /* unlink for avoiding double-free */
+ audioformat_free(fp);
kfree(pd);
- kfree(fp->rate_table);
- kfree(fp->chmap);
- kfree(fp);
return err;
}
/* try to set the interface... */
@@ -1200,3 +1210,23 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
return 0;
}
+int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
+{
+ int err;
+ bool has_non_pcm = false;
+
+ /* parse PCM formats */
+ err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, false);
+ if (err < 0)
+ return err;
+
+ if (has_non_pcm) {
+ /* parse non-PCM formats */
+ err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, true);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
diff --git a/sound/usb/validate.c b/sound/usb/validate.c
new file mode 100644
index 000000000000..3c8f73a0eb12
--- /dev/null
+++ b/sound/usb/validate.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Validation of USB-audio class descriptors
+//
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+#include <linux/usb/midi.h>
+#include "usbaudio.h"
+#include "helper.h"
+
+struct usb_desc_validator {
+ unsigned char protocol;
+ unsigned char type;
+ bool (*func)(const void *p, const struct usb_desc_validator *v);
+ size_t size;
+};
+
+#define UAC_VERSION_ALL (unsigned char)(-1)
+
+/* UAC1 only */
+static bool validate_uac1_header(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac1_ac_header_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bInCollection;
+}
+
+/* for mixer unit; covering all UACs */
+static bool validate_mixer_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_mixer_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d) || !d->bNrInPins)
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ /* We can't determine the bitmap size only from this unit descriptor,
+ * so just check with the remaining length.
+ * The actual bitmap is checked at mixer unit parser.
+ */
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 2 + 1; /* wChannelConfig, iChannelNames */
+ /* bmControls[n*m] */
+ len += 1; /* iMixer */
+ break;
+ case UAC_VERSION_2:
+ len += 4 + 1; /* bmChannelConfig, iChannelNames */
+ /* bmMixerControls[n*m] */
+ len += 1 + 1; /* bmControls, iMixer */
+ break;
+ case UAC_VERSION_3:
+ len += 2; /* wClusterDescrID */
+ /* bmMixerControls[n*m] */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+/* both for processing and extension units; covering all UACs */
+static bool validate_processing_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_processing_unit_descriptor *d = p;
+ const unsigned char *hdr = p;
+ size_t len, m;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = d->bLength < sizeof(*d) + d->bNrInPins;
+ if (d->bLength < len)
+ return false;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ /* bNrChannels, wChannelConfig, iChannelNames, bControlSize */
+ len += 1 + 2 + 1 + 1;
+ if (d->bLength < len) /* bControlSize */
+ return false;
+ m = hdr[len];
+ len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */
+ break;
+ case UAC_VERSION_2:
+ /* bNrChannels, bmChannelConfig, iChannelNames */
+ len += 1 + 4 + 1;
+ if (v->type == UAC2_PROCESSING_UNIT_V2)
+ len += 2; /* bmControls -- 2 bytes for PU */
+ else
+ len += 1; /* bmControls -- 1 byte for EU */
+ len += 1; /* iProcessing */
+ break;
+ case UAC_VERSION_3:
+ /* wProcessingDescrStr, bmControls */
+ len += 2 + 4;
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ if (v->type == UAC1_EXTENSION_UNIT)
+ return true; /* OK */
+ switch (d->wProcessType) {
+ case UAC_PROCESS_UP_DOWNMIX:
+ case UAC_PROCESS_DOLBY_PROLOGIC:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_2:
+ if (v->type == UAC2_EXTENSION_UNIT_V2)
+ return true; /* OK */
+ switch (d->wProcessType) {
+ case UAC2_PROCESS_UP_DOWNMIX:
+ case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 4; /* bNrModes, daModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_3:
+ if (v->type == UAC3_EXTENSION_UNIT) {
+ len += 2; /* wClusterDescrID */
+ break;
+ }
+ switch (d->wProcessType) {
+ case UAC3_PROCESS_UP_DOWNMIX:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */
+ break;
+ case UAC3_PROCESS_MULTI_FUNCTION:
+ len += 2 + 4; /* wClusterDescrID, bmAlgorighms */
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ return true;
+}
+
+/* both for selector and clock selector units; covering all UACs */
+static bool validate_selector_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_selector_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 1; /* iSelector */
+ break;
+ case UAC_VERSION_2:
+ len += 1 + 1; /* bmControls, iSelector */
+ break;
+ case UAC_VERSION_3:
+ len += 4 + 2; /* bmControls, wSelectorDescrStr */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+static bool validate_uac1_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d) || !d->bControlSize)
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + d->bControlSize + 1;
+}
+
+static bool validate_uac2_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac2_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + 4 + 1;
+}
+
+static bool validate_uac3_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac3_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + wFeatureDescrStr */
+ return d->bLength >= sizeof(*d) + 4 + 2;
+}
+
+static bool validate_midi_out_jack(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct usb_midi_out_jack_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bNrInputPins * 2;
+}
+
+#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) }
+#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) }
+
+static struct usb_desc_validator audio_validators[] = {
+ /* UAC1 */
+ FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header),
+ FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL,
+ struct uac_input_terminal_descriptor),
+ FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL,
+ struct uac1_output_terminal_descriptor),
+ FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit),
+ FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit),
+
+ /* UAC2 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor),
+ FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL,
+ struct uac2_input_terminal_descriptor),
+ FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL,
+ struct uac2_output_terminal_descriptor),
+ FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit),
+ /* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit),
+ FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE,
+ struct uac_clock_source_descriptor),
+ FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER,
+ struct uac_clock_multiplier_descriptor),
+ /* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */
+
+ /* UAC3 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor),
+ FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL,
+ struct uac3_input_terminal_descriptor),
+ FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL,
+ struct uac3_output_terminal_descriptor),
+ /* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit),
+ /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE,
+ struct uac3_clock_source_descriptor),
+ FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER,
+ struct uac3_clock_multiplier_descriptor),
+ /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */
+ /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */
+ { } /* terminator */
+};
+
+static struct usb_desc_validator midi_validators[] = {
+ FIXED(UAC_VERSION_ALL, USB_MS_HEADER,
+ struct usb_ms_header_descriptor),
+ FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK,
+ struct usb_midi_in_jack_descriptor),
+ FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK,
+ validate_midi_out_jack),
+ { } /* terminator */
+};
+
+
+/* Validate the given unit descriptor, return true if it's OK */
+static bool validate_desc(unsigned char *hdr, int protocol,
+ const struct usb_desc_validator *v)
+{
+ if (hdr[1] != USB_DT_CS_INTERFACE)
+ return true; /* don't care */
+
+ for (; v->type; v++) {
+ if (v->type == hdr[2] &&
+ (v->protocol == UAC_VERSION_ALL ||
+ v->protocol == protocol)) {
+ if (v->func)
+ return v->func(hdr, v);
+ /* check for the fixed size */
+ return hdr[0] >= v->size;
+ }
+ }
+
+ return true; /* not matching, skip validation */
+}
+
+bool snd_usb_validate_audio_desc(void *p, int protocol)
+{
+ return validate_desc(p, protocol, audio_validators);
+}
+
+bool snd_usb_validate_midi_desc(void *p)
+{
+ return validate_desc(p, UAC_VERSION_1, midi_validators);
+}
+