From 9a2fe9b801f585baccf8352d82839dcd54b300cf Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Wed, 21 Mar 2018 02:03:59 +0200 Subject: ALSA: usb: initial USB Audio Device Class 3.0 support Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol Reviewed-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 337 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 228 insertions(+), 109 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 06b22624ab7a..1c02f7373eda 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state, USB_DT_CS_INTERFACE)) != NULL) { if (hdr->bLength >= 4 && hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL && - hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER && + hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER && hdr->bUnitID == unit) return hdr; } @@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, validx += cval->idx_off; + if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2/3 */ val_len = uac2_ctl_value_size(cval->val_type); /* FIXME */ @@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { + int protocol = state->mixer->protocol; int err; void *p1; @@ -730,16 +733,104 @@ static int check_input_term(struct mixer_build *state, int id, while ((p1 = find_audio_control_unit(state, id)) != NULL) { unsigned char *hdr = p1; term->id = id; - switch (hdr[2]) { - case UAC_INPUT_TERMINAL: - if (state->mixer->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; + + 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; + + /* 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; + + id = d->bSourceID; + break; /* continue to parse */ + } + case UAC_MIXER_UNIT: { + struct uac_mixer_unit_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 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 = d->bDescriptorSubtype << 16; /* virtual type */ + term->id = id; + term->name = uac_selector_unit_iSelector(d); + return 0; + } + case UAC1_PROCESSING_UNIT: + case UAC1_EXTENSION_UNIT: + /* UAC2_PROCESSING_UNIT_V2 */ + /* UAC2_EFFECT_UNIT */ + 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; + } + + if (d->bNrInPins) { + id = d->baSourceID[0]; + break; /* continue to parse */ + } + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + 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->type = d->bDescriptorSubtype << 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 */ @@ -752,71 +843,31 @@ static int check_input_term(struct mixer_build *state, int id, * 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; - id = d->bSourceID; - break; /* continue to parse */ - } - case UAC_MIXER_UNIT: { - struct uac_mixer_unit_descriptor *d = p1; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_mixer_unit_bNrChannels(d); - term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->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 = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = uac_selector_unit_iSelector(d); - return 0; - } - case UAC1_PROCESSING_UNIT: - case UAC1_EXTENSION_UNIT: - /* UAC2_PROCESSING_UNIT_V2 */ - /* UAC2_EFFECT_UNIT */ - case UAC2_EXTENSION_UNIT_V2: { - struct uac_processing_unit_descriptor *d = p1; - - if (state->mixer->protocol == UAC_VERSION_2 && - hdr[2] == UAC2_EFFECT_UNIT) { - /* UAC2/UAC1 unit IDs overlap here in an - * uncompatible way. Ignore this unit for now. - */ + + /* REVISIT: UAC3 IT doesn't have channels/cfg */ + term->channels = 0; + term->chconfig = 0; + + term->name = le16_to_cpu(d->wTerminalDescrStr); return 0; } + case UAC3_FEATURE_UNIT: { + struct uac3_feature_unit_descriptor *d = p1; - if (d->bNrInPins) { - id = d->baSourceID[0]; + id = d->bSourceID; break; /* continue to parse */ } - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_processing_unit_bNrChannels(d); - term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); - term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); - return 0; - } - case UAC2_CLOCK_SOURCE: { - struct uac_clock_source_descriptor *d = p1; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = d->iClockSource; - return 0; - } - default: - return -ENODEV; + case UAC3_CLOCK_SOURCE: { + struct uac3_clock_source_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + term->id = id; + term->name = le16_to_cpu(d->wClockSourceStr); + return 0; + } + default: + return -ENODEV; + } } } return -ENODEV; @@ -1423,7 +1474,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, * The only property of this unit we are interested in is the * clock source validity. If that isn't readable, just bail out. */ - if (!uac2_control_is_readable(hdr->bmControls, + if (!uac_v2v3_control_is_readable(hdr->bmControls, ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) return 0; @@ -1439,7 +1490,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, cval->val_type = USB_MIXER_BOOLEAN; cval->control = UAC2_CS_CONTROL_CLOCK_VALID; - if (uac2_control_is_writeable(hdr->bmControls, + if (uac_v2v3_control_is_writeable(hdr->bmControls, ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); else { @@ -1502,7 +1553,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } - } else { + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_feature_unit_descriptor *ftr = _ftr; if (hdr->bLength < 6) { usb_audio_err(state->chip, @@ -1519,6 +1570,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, 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 */ @@ -1577,7 +1646,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); } - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2/3 */ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsigned int ch_bits = 0; unsigned int ch_read_only = 0; @@ -1587,9 +1656,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); - if (uac2_control_is_readable(mask, i)) { + if (uac_v2v3_control_is_readable(mask, i)) { ch_bits |= (1 << j); - if (!uac2_control_is_writeable(mask, i)) + if (!uac_v2v3_control_is_writeable(mask, i)) ch_read_only |= (1 << j); } } @@ -1610,9 +1679,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, if (ch_bits & 1) build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only); - if (uac2_control_is_readable(master_bits, i)) + if (uac_v2v3_control_is_readable(master_bits, i)) build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, - !uac2_control_is_writeable(master_bits, i)); + !uac_v2v3_control_is_writeable(master_bits, i)); } } @@ -2220,6 +2289,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, static int parse_audio_unit(struct mixer_build *state, int unitid) { unsigned char *p1; + int protocol = state->mixer->protocol; if (test_and_set_bit(unitid, state->unitbitmap)) return 0; /* the unit already visited */ @@ -2230,36 +2300,61 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) return -EINVAL; } - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - return 0; /* NOP */ - 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 (state->mixer->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 (state->mixer->protocol == UAC_VERSION_1) + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + switch (p1[2]) { + case UAC_INPUT_TERMINAL: + return 0; /* NOP */ + 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); - else /* UAC_VERSION_2 */ + 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 0; /* NOP */ + 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_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 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; + 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; + } } } @@ -2330,7 +2425,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bSourceID); if (err < 0 && err != -EINVAL) return err; - } else { /* UAC_VERSION_2 */ + } else if (mixer->protocol == UAC_VERSION_2) { struct uac2_output_terminal_descriptor *desc = p; if (desc->bLength < sizeof(*desc)) @@ -2351,6 +2446,27 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bCSourceID); if (err < 0 && err != -EINVAL) return err; + } 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; + state.oterm.type = le16_to_cpu(desc->wTerminalType); + state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr); + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; + + /* + * For UAC3, use the same approach to also add the + * clock selectors + */ + err = parse_audio_unit(&state, desc->bCSourceID); + if (err < 0 && err != -EINVAL) + return err; } } @@ -2597,6 +2713,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, case UAC_VERSION_2: mixer->protocol = UAC_VERSION_2; break; + case UAC_VERSION_3: + mixer->protocol = UAC_VERSION_3; + break; } if ((err = snd_usb_mixer_controls(mixer)) < 0 || -- cgit v1.2.2 From 21e9b3e931f78497b19b1f8f3d59d19412c1a28f Mon Sep 17 00:00:00 2001 From: Andrew Chant Date: Thu, 22 Mar 2018 14:39:55 -0700 Subject: ALSA: usb-audio: fix uac control query argument This patch fixes code readability and should have no functional change. Correct uac control query functions to account for the 1-based indexing of USB Audio Class control identifiers. The function parameter, u8 control, should be the constant defined in audio-v2.h to identify the control to be checked for readability or writeability. This patch fixes all callers that had adjusted, and makes explicit the mapping between audio_feature_info[] array index and the associated control identifier. Signed-off-by: Andrew Chant Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 69 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 26 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1c02f7373eda..3075ac50a391 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -879,26 +879,27 @@ static int check_input_term(struct mixer_build *state, int id, /* feature unit control information */ struct usb_feature_control_info { + int control; const char *name; int type; /* data type for uac1 */ int type_uac2; /* data type for uac2 if different from uac1, else -1 */ }; static struct usb_feature_control_info audio_feature_info[] = { - { "Mute", USB_MIXER_INV_BOOLEAN, -1 }, - { "Volume", USB_MIXER_S16, -1 }, - { "Tone Control - Bass", USB_MIXER_S8, -1 }, - { "Tone Control - Mid", USB_MIXER_S8, -1 }, - { "Tone Control - Treble", USB_MIXER_S8, -1 }, - { "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemeted yet */ - { "Auto Gain Control", USB_MIXER_BOOLEAN, -1 }, - { "Delay Control", USB_MIXER_U16, USB_MIXER_U32 }, - { "Bass Boost", USB_MIXER_BOOLEAN, -1 }, - { "Loudness", USB_MIXER_BOOLEAN, -1 }, + { UAC_FU_MUTE, "Mute", USB_MIXER_INV_BOOLEAN, -1 }, + { UAC_FU_VOLUME, "Volume", USB_MIXER_S16, -1 }, + { UAC_FU_BASS, "Tone Control - Bass", USB_MIXER_S8, -1 }, + { UAC_FU_MID, "Tone Control - Mid", USB_MIXER_S8, -1 }, + { UAC_FU_TREBLE, "Tone Control - Treble", USB_MIXER_S8, -1 }, + { UAC_FU_GRAPHIC_EQUALIZER, "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemented yet */ + { UAC_FU_AUTOMATIC_GAIN, "Auto Gain Control", USB_MIXER_BOOLEAN, -1 }, + { UAC_FU_DELAY, "Delay Control", USB_MIXER_U16, USB_MIXER_U32 }, + { UAC_FU_BASS_BOOST, "Bass Boost", USB_MIXER_BOOLEAN, -1 }, + { UAC_FU_LOUDNESS, "Loudness", USB_MIXER_BOOLEAN, -1 }, /* UAC2 specific */ - { "Input Gain Control", USB_MIXER_S16, -1 }, - { "Input Gain Pad Control", USB_MIXER_S16, -1 }, - { "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, + { UAC2_FU_INPUT_GAIN, "Input Gain Control", USB_MIXER_S16, -1 }, + { UAC2_FU_INPUT_GAIN_PAD, "Input Gain Pad Control", USB_MIXER_S16, -1 }, + { UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, }; /* private_free callback */ @@ -1293,6 +1294,17 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl, strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name)); } +static struct usb_feature_control_info *get_feature_control_info(int control) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(audio_feature_info); ++i) { + if (audio_feature_info[i].control == control) + return &audio_feature_info[i]; + } + return NULL; +} + static void build_feature_ctl(struct mixer_build *state, void *raw_desc, unsigned int ctl_mask, int control, struct usb_audio_term *iterm, int unitid, @@ -1308,8 +1320,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, const struct usbmix_name_map *map; unsigned int range; - control++; /* change from zero-based to 1-based value */ - if (control == UAC_FU_GRAPHIC_EQUALIZER) { /* FIXME: not supported yet */ return; @@ -1325,7 +1335,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = control; cval->cmask = ctl_mask; - ctl_info = &audio_feature_info[control-1]; + + ctl_info = get_feature_control_info(control); + if (!ctl_info) + return; if (state->mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; else /* UAC_VERSION_2 */ @@ -1475,7 +1488,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, * clock source validity. If that isn't readable, just bail out. */ if (!uac_v2v3_control_is_readable(hdr->bmControls, - ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) + UAC2_CS_CONTROL_CLOCK_VALID)) return 0; cval = kzalloc(sizeof(*cval), GFP_KERNEL); @@ -1491,7 +1504,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, cval->control = UAC2_CS_CONTROL_CLOCK_VALID; if (uac_v2v3_control_is_writeable(hdr->bmControls, - ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) + UAC2_CS_CONTROL_CLOCK_VALID)) kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); else { cval->master_readonly = 1; @@ -1625,6 +1638,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, /* check all control types */ for (i = 0; i < 10; i++) { unsigned int ch_bits = 0; + int control = audio_feature_info[i].control; + for (j = 0; j < channels; j++) { unsigned int mask; @@ -1640,25 +1655,26 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, * (for ease of programming). */ if (ch_bits & 1) - build_feature_ctl(state, _ftr, ch_bits, i, + build_feature_ctl(state, _ftr, ch_bits, control, &iterm, unitid, 0); if (master_bits & (1 << i)) - build_feature_ctl(state, _ftr, 0, i, &iterm, - unitid, 0); + build_feature_ctl(state, _ftr, 0, control, + &iterm, unitid, 0); } } else { /* UAC_VERSION_2/3 */ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsigned int ch_bits = 0; unsigned int ch_read_only = 0; + int control = audio_feature_info[i].control; for (j = 0; j < channels; j++) { unsigned int mask; mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); - if (uac_v2v3_control_is_readable(mask, i)) { + if (uac_v2v3_control_is_readable(mask, control)) { ch_bits |= (1 << j); - if (!uac_v2v3_control_is_writeable(mask, i)) + if (!uac_v2v3_control_is_writeable(mask, control)) ch_read_only |= (1 << j); } } @@ -1677,11 +1693,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, * (for ease of programming). */ if (ch_bits & 1) - build_feature_ctl(state, _ftr, ch_bits, i, + build_feature_ctl(state, _ftr, ch_bits, control, &iterm, unitid, ch_read_only); - if (uac_v2v3_control_is_readable(master_bits, i)) + if (uac_v2v3_control_is_readable(master_bits, control)) build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, - !uac_v2v3_control_is_writeable(master_bits, i)); + !uac_v2v3_control_is_writeable(master_bits, + control)); } } -- cgit v1.2.2 From 5a222e8494524d04cc8de63726644759e5b132f7 Mon Sep 17 00:00:00 2001 From: Andrew Chant Date: Fri, 23 Mar 2018 19:25:23 -0700 Subject: ALSA: usb-audio: UAC2 jack detection This implements UAC2 jack detection support, presenting jack status as a boolean read-only mono mixer. The presence of any channel in the UAC2_TE_CONNECTOR control for a terminal will result in the mixer saying the jack is connected. Mixer naming follows the convention in sound/core/ctljack.c, terminating the mixer with " Jack". For additional clues as to which jack is being presented, the name is prefixed with " - Input Jack" or " - Output Jack" depending on if it's an input or output terminal. This is required because terminal names are ambiguous between inputs and outputs and often duplicated - Bidirectional terminal types (0x400 -> 0x4FF) "... may be used separately for input only or output only. These types require two Terminal descriptors. Both have the same type." (quote from "USB Device Class Definition for Terminal Types") Since bidirectional terminal types are common for headphone adapters, this distinguishes between two otherwise identically-named jack controls. Tested with a UAC2 audio device with connector control capability. Signed-off-by: Andrew Chant Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3075ac50a391..97ed2155205d 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1235,6 +1235,21 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, return changed; } +/* get the current value from a mixer element */ +static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + int val, err; + + err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); + if (err < 0) + return filter_error(cval, err); + val = (val != 0); + ucontrol->value.integer.value[0] = val; + return 0; +} + static struct snd_kcontrol_new usb_feature_unit_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later manually */ @@ -1252,6 +1267,16 @@ static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = { .put = NULL, }; +/* A UAC connector mixer control */ +static struct snd_kcontrol_new usb_connector_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", /* will be filled later manually */ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = mixer_ctl_connector_get, + .put = NULL, +}; + /* * This symbol is exported in order to allow the mixer quirks to * hook up to the standard feature unit control mechanism @@ -1464,6 +1489,54 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_add_control(&cval->head, kctl); } +static void get_connector_control_name(struct mixer_build *state, + struct usb_audio_term *term, + bool is_input, char *name, int name_size) +{ + int name_len = get_term_name(state, term, name, name_size, 0); + + if (name_len == 0) + strlcpy(name, "Unknown", name_size); + + /* + * sound/core/ctljack.c has a convention of naming jack controls + * by ending in " Jack". Make it slightly more useful by + * indicating Input or Output after the terminal name. + */ + if (is_input) + strlcat(name, " - Input Jack", name_size); + else + strlcat(name, " - Output Jack", name_size); +} + +/* Build a mixer control for a UAC connector control (jack-detect) */ +static void build_connector_control(struct mixer_build *state, + struct usb_audio_term *term, bool is_input) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *cval; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id); + cval->control = UAC2_TE_CONNECTOR; + cval->val_type = USB_MIXER_BOOLEAN; + cval->channels = 1; /* report true if any channel is connected */ + cval->min = 0; + cval->max = 1; + kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); + if (!kctl) { + usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + kfree(cval); + return; + } + get_connector_control_name(state, term, is_input, kctl->id.name, + sizeof(kctl->id.name)); + kctl->private_free = snd_usb_mixer_elem_free; + snd_usb_mixer_add_control(&cval->head, kctl); +} + static int parse_clock_source_unit(struct mixer_build *state, int unitid, void *_ftr) { @@ -1770,6 +1843,23 @@ static void build_mixer_unit_ctl(struct mixer_build *state, snd_usb_mixer_add_control(&cval->head, kctl); } +static int parse_audio_input_terminal(struct mixer_build *state, int unitid, + void *raw_desc) +{ + struct usb_audio_term iterm; + struct uac2_input_terminal_descriptor *d = raw_desc; + + check_input_term(state, d->bTerminalID, &iterm); + if (state->mixer->protocol == UAC_VERSION_2) { + /* Check for jack detection. */ + if (uac_v2v3_control_is_readable(d->bmControls, + UAC2_TE_CONNECTOR)) { + build_connector_control(state, &iterm, true); + } + } + return 0; +} + /* * parse a mixer unit */ @@ -2320,7 +2410,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { switch (p1[2]) { case UAC_INPUT_TERMINAL: - return 0; /* NOP */ + return parse_audio_input_terminal(state, unitid, p1); case UAC_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC2_CLOCK_SOURCE: @@ -2463,6 +2553,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bCSourceID); if (err < 0 && err != -EINVAL) return err; + + if (uac_v2v3_control_is_readable(desc->bmControls, + UAC2_TE_CONNECTOR)) { + build_connector_control(&state, &state.oterm, + false); + } } else { /* UAC_VERSION_3 */ struct uac3_output_terminal_descriptor *desc = p; -- cgit v1.2.2 From 568fa7e087ef98bc85b5aa31ea7c9252c1305c1f Mon Sep 17 00:00:00 2001 From: Andrew Chant Date: Fri, 23 Mar 2018 19:25:24 -0700 Subject: ALSA: usb-audio: update clock valid control Make the "clock valid" control a global control instead of a mixer so that it doesn't appear in mixer applications. Additionally, remove the check for writeability prohibited by spec, and Use common code to read the control value. Tested with a UAC2 Audio device that presents a clock validity control. The control still shows up in /proc usbmixer but not in alsamixer. Signed-off-by: Andrew Chant Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 97ed2155205d..04dab6f65535 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1235,9 +1235,9 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, return changed; } -/* get the current value from a mixer element */ -static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* get the boolean value from the master channel of a UAC control */ +static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *cval = kcontrol->private_data; int val, err; @@ -1267,13 +1267,16 @@ static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = { .put = NULL, }; -/* A UAC connector mixer control */ -static struct snd_kcontrol_new usb_connector_ctl_ro = { +/* + * A control which shows the boolean value from reading a UAC control on + * the master channel. + */ +static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "", /* will be filled later manually */ .access = SNDRV_CTL_ELEM_ACCESS_READ, .info = snd_ctl_boolean_mono_info, - .get = mixer_ctl_connector_get, + .get = mixer_ctl_master_bool_get, .put = NULL, }; @@ -1520,12 +1523,18 @@ static void build_connector_control(struct mixer_build *state, if (!cval) return; snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id); + /* + * The first byte from reading the UAC2_TE_CONNECTOR control returns the + * number of channels connected. This boolean ctl will simply report + * if any channels are connected or not. + * (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block) + */ cval->control = UAC2_TE_CONNECTOR; cval->val_type = USB_MIXER_BOOLEAN; cval->channels = 1; /* report true if any channel is connected */ cval->min = 0; cval->max = 1; - kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); + kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); if (!kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); kfree(cval); @@ -1576,13 +1585,9 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, cval->val_type = USB_MIXER_BOOLEAN; cval->control = UAC2_CS_CONTROL_CLOCK_VALID; - if (uac_v2v3_control_is_writeable(hdr->bmControls, - UAC2_CS_CONTROL_CLOCK_VALID)) - kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); - else { - cval->master_readonly = 1; - kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); - } + cval->master_readonly = 1; + /* From UAC2 5.2.5.1.2 "Only the get request is supported." */ + kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); if (!kctl) { kfree(cval); -- cgit v1.2.2 From 623760257b39632ffc5051fc88167b546dcfa791 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 27 Mar 2018 15:30:01 +0100 Subject: ALSA: usb-audio: fix memory leak on cval With the current exit return path of the ctl_info allocation failure cval is not being freed resulting in a memory leak. Fix this by kfree'ing it on the return. Detected by CoverityScan, CID#1466878 ("Resource Leak") Fixes: 21e9b3e931f7 ("ALSA: usb-audio: fix uac control query argument") Signed-off-by: Colin Ian King Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 04dab6f65535..301ad61ed426 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1365,8 +1365,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, cval->cmask = ctl_mask; ctl_info = get_feature_control_info(control); - if (!ctl_info) + if (!ctl_info) { + kfree(cval); return; + } if (state->mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; else /* UAC_VERSION_2 */ -- cgit v1.2.2