Commit e0ccdef9 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Clean up check_input_term()

The primary changes in this patch are cleanups of __check_input_term()
and move to a non-nested switch-case block by evaluating the pair of
UAC version and the unit type, as we've done for parse_audio_unit().
Also each parser is split into the function for readability.

Now, a slight behavior change by this cleanup is the handling of
processing and extension units.  Formerly we've dealt with them
differently between UAC1/2 and UAC3; the latter returns an error if no
input sources are available, while the former continues to parse.

In this patch, unify the behavior in all cases: when input sources are
available, it parses recursively, then override the type and the id,
as well as channel information if not provided yet.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 744f51e8
...@@ -758,224 +758,242 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, ...@@ -758,224 +758,242 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
} }
/* /*
* parse the source unit recursively until it reaches to a terminal * Parse Input Terminal Unit
* or a branched unit.
*/ */
static int __check_input_term(struct mixer_build *state, int id, 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; int err;
void *p1;
unsigned char *hdr;
memset(term, 0, sizeof(*term)); /* call recursively to verify the referenced clock entity */
for (;;) { err = __check_input_term(state, d->bCSourceID, term);
/* a loop in the terminal chain? */ if (err < 0)
if (test_and_set_bit(id, state->termbitmap)) return err;
return -EINVAL;
p1 = find_audio_control_unit(state, id); /* save input term properties after recursion,
if (!p1) * to ensure they are not overriden by the recursion calls
break; */
if (!snd_usb_validate_audio_desc(p1, protocol)) term->id = id;
break; /* bad descriptor */ 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; static int parse_term_uac3_iterm_unit(struct mixer_build *state,
term->id = id; 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) { /* call recursively to verify the referenced clock entity */
switch (hdr[2]) { err = __check_input_term(state, d->bCSourceID, term);
case UAC_INPUT_TERMINAL: if (err < 0)
if (protocol == UAC_VERSION_1) { return err;
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, /* save input term properties after recursion,
* to ensure they are not overriden by the * to ensure they are not overriden by the recursion calls
* recursion calls */ */
term->id = id; term->id = id;
term->type = le16_to_cpu(d->wTerminalType); 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; err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
break; /* continue to parse */ if (err < 0)
} return err;
case UAC_MIXER_UNIT: { term->channels = err;
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;
}
if (d->bNrInPins) { /* REVISIT: UAC3 IT doesn't have channels cfg */
id = d->baSourceID[0]; term->chconfig = 0;
break; /* continue to parse */
}
if (!term->type)
term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
term->channels = uac_processing_unit_bNrChannels(d); term->name = le16_to_cpu(d->wTerminalDescrStr);
term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); return 0;
term->name = uac_processing_unit_iProcessing(d, protocol); }
return 0;
}
case UAC2_CLOCK_SOURCE: {
struct uac_clock_source_descriptor *d = p1;
term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ static int parse_term_mixer_unit(struct mixer_build *state,
term->id = id; struct usb_audio_term *term,
term->name = d->iClockSource; void *p1, int id)
return 0; {
} struct uac_mixer_unit_descriptor *d = p1;
default: int protocol = state->mixer->protocol;
return -ENODEV; int err;
}
} 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;
/* save input term properties after recursion, err = uac_mixer_unit_get_channels(state, d);
* to ensure they are not overriden by the if (err <= 0)
* recursion calls */ return err;
term->id = id;
term->type = le16_to_cpu(d->wTerminalType);
err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
if (err < 0) term->channels = err;
return err; if (protocol != UAC_VERSION_3) {
term->channels = err; term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
term->name = uac_mixer_unit_iMixer(d);
}
return 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;
/* REVISIT: UAC3 IT doesn't have channels cfg */ /* call recursively to retrieve the channel info */
term->chconfig = 0; 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;
}
term->name = le16_to_cpu(d->wTerminalDescrStr); static int parse_term_proc_unit(struct mixer_build *state,
return 0; struct usb_audio_term *term,
} void *p1, int id, int vtype)
case UAC3_FEATURE_UNIT: { {
struct uac3_feature_unit_descriptor *d = p1; struct uac_processing_unit_descriptor *d = p1;
int protocol = state->mixer->protocol;
int err;
id = d->bSourceID; if (d->bNrInPins) {
break; /* continue to parse */ /* call recursively to retrieve the channel info */
} err = __check_input_term(state, d->baSourceID[0], term);
case UAC3_CLOCK_SOURCE: { if (err < 0)
struct uac3_clock_source_descriptor *d = p1; return err;
}
term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ term->type = vtype << 16; /* virtual type */
term->id = id; term->id = id;
term->name = le16_to_cpu(d->wClockSourceStr);
return 0;
}
case UAC3_MIXER_UNIT: {
struct uac_mixer_unit_descriptor *d = p1;
err = uac_mixer_unit_get_channels(state, d); if (protocol == UAC_VERSION_3)
if (err <= 0) return 0;
return err;
term->channels = err; if (!term->channels) {
term->type = UAC3_MIXER_UNIT << 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;
}
return 0; static int parse_term_uac2_clock_source(struct mixer_build *state,
} struct usb_audio_term *term,
case UAC3_SELECTOR_UNIT: void *p1, int id)
case UAC3_CLOCK_SELECTOR: { {
struct uac_selector_unit_descriptor *d = p1; struct uac_clock_source_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 */
return 0; term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
} term->id = id;
case UAC3_PROCESSING_UNIT: { term->name = d->iClockSource;
struct uac_processing_unit_descriptor *d = p1; return 0;
}
if (!d->bNrInPins) static int parse_term_uac3_clock_source(struct mixer_build *state,
return -EINVAL; struct usb_audio_term *term,
void *p1, int id)
{
struct uac3_clock_source_descriptor *d = p1;
/* call recursively to retrieve the channel info */ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
err = __check_input_term(state, d->baSourceID[0], term); term->id = id;
if (err < 0) term->name = le16_to_cpu(d->wClockSourceStr);
return err; return 0;
}
term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ #define PTYPE(a, b) ((a) << 8 | (b))
term->id = id;
term->name = 0; /* TODO: UAC3 Class-specific strings */
return 0; /*
} * parse the source unit recursively until it reaches to a terminal
default: * or a branched unit.
return -ENODEV; */
} 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; return -ENODEV;
...@@ -2712,7 +2730,6 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) ...@@ -2712,7 +2730,6 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return 0; /* skip invalid unit */ return 0; /* skip invalid unit */
} }
#define PTYPE(a, b) ((a) << 8 | (b))
switch (PTYPE(protocol, p1[2])) { switch (PTYPE(protocol, p1[2])) {
case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment