sound: usb: Add support for parsing AudioControl intf for BADD devices

BADD 3.0 devices support three types of topologies;
Basic I/P, Basic O/P, and BASIC I/P. Accordingly, various
units and terminals have to be parsed which are not
exposed by the device and host must be able to figure
out various class-specific descriptors based on the
profile ID of the device. This patch adds this logic
to build various units and terminals of the AudioControl
interface of a BADD device.

Change-Id: Ib52f884133cdf6e0ec95f49095c14f7d005a5356
Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org>
This commit is contained in:
Ajay Agarwal 2017-02-07 18:41:34 +05:30
parent 2ecedf5dc7
commit 2b7792c598
4 changed files with 661 additions and 81 deletions

View file

@ -21,6 +21,10 @@
#include <linux/types.h>
#define UAC3_MIXER_UNIT_V3 0x05
#define UAC3_FEATURE_UNIT_V3 0x07
#define UAC3_CLOCK_SOURCE 0x0b
#define BADD_MAXPSIZE_SYNC_MONO_16 0x0060
#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090
#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0
@ -39,10 +43,12 @@
#define BADD_SAMPLING_RATE 48000
#define NUM_CHANNELS_MONO 1
#define NUM_CHANNELS_STEREO 2
#define BADD_CH_CONFIG_MONO 0
#define BADD_CH_CONFIG_STEREO 3
#define NUM_CHANNELS_MONO 1
#define NUM_CHANNELS_STEREO 2
#define BADD_CH_CONFIG_MONO 0
#define BADD_CH_CONFIG_STEREO 3
#define CLUSTER_ID_MONO 0x0001
#define CLUSTER_ID_STEREO 0x0002
#define FULL_ADC_PROFILE 0x01
@ -69,4 +75,98 @@
#define UAC_BIDIR_TERMINAL_HEADSET 0x0402
#define UAC_BIDIR_TERMINAL_SPEAKERPHONE 0x0403
#define NUM_BADD_DESCS 7
struct uac3_input_terminal_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bTerminalID;
__u16 wTerminalType;
__u8 bAssocTerminal;
__u8 bCSourceID;
__u32 bmControls;
__u16 wClusterDescrID;
__u16 wExTerminalDescrID;
__u16 wConnectorsDescrID;
__u16 wTerminalDescrStr;
} __packed;
#define UAC3_DT_INPUT_TERMINAL_SIZE 0x14
extern struct uac3_input_terminal_descriptor badd_baif_in_term_desc;
extern struct uac3_input_terminal_descriptor badd_baof_in_term_desc;
struct uac3_output_terminal_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bTerminalID;
__u16 wTerminalType;
__u8 bAssocTerminal;
__u8 bSourceID;
__u8 bCSourceID;
__u32 bmControls;
__u16 wExTerminalDescrID;
__u16 wConnectorsDescrID;
__u16 wTerminalDescrStr;
} __packed;
#define UAC3_DT_OUTPUT_TERMINAL_SIZE 0x13
extern struct uac3_output_terminal_descriptor badd_baif_out_term_desc;
extern struct uac3_output_terminal_descriptor badd_baof_out_term_desc;
extern __u8 monoControls[];
extern __u8 stereoControls[];
extern __u8 badd_mu_src_ids[];
struct uac3_mixer_unit_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bUnitID;
__u8 bNrInPins;
__u8 *baSourceID;
__u16 wClusterDescrID;
__u8 bmMixerControls;
__u32 bmControls;
__u16 wMixerDescrStr;
} __packed;
#define UAC3_DT_MIXER_UNIT_SIZE 0x10
extern struct uac3_mixer_unit_descriptor badd_baiof_mu_desc;
struct uac3_feature_unit_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bUnitID;
__u8 bSourceID;
__u8 *bmaControls;
__u16 wFeatureDescrStr;
} __packed;
extern struct uac3_feature_unit_descriptor badd_baif_fu_desc;
extern struct uac3_feature_unit_descriptor badd_baof_fu_desc;
extern struct uac3_feature_unit_descriptor badd_baiof_fu_desc;
struct uac3_clock_source_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bClockID;
__u8 bmAttributes;
__u32 bmControls;
__u8 bReferenceTerminal;
__u16 wClockSourceStr;
} __packed;
#define UAC3_DT_CLOCK_SRC_SIZE 0x0c
extern struct uac3_clock_source_descriptor badd_clock_desc;
extern void *badd_desc_list[];
#endif /* __LINUX_USB_AUDIO_V3_H */

View file

@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \
pcm.o \
proc.o \
quirks.o \
stream.o
stream.o \
badd.o
snd-usbmidi-lib-objs := midi.o

137
sound/usb/badd.c Normal file
View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
struct uac3_input_terminal_descriptor badd_baif_in_term_desc = {
.bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
.bTerminalID = BADD_IN_TERM_ID_BAIF,
.bCSourceID = BADD_CLOCK_SOURCE,
.wExTerminalDescrID = 0x0000,
.wTerminalDescrStr = 0x0000
};
struct uac3_input_terminal_descriptor badd_baof_in_term_desc = {
.bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
.bTerminalID = BADD_IN_TERM_ID_BAOF,
.wTerminalType = UAC_TERMINAL_STREAMING,
.bAssocTerminal = 0x00,
.bCSourceID = BADD_CLOCK_SOURCE,
.bmControls = 0x00000000,
.wExTerminalDescrID = 0x0000,
.wConnectorsDescrID = 0x0000,
.wTerminalDescrStr = 0x0000
};
struct uac3_output_terminal_descriptor badd_baif_out_term_desc = {
.bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
.bTerminalID = BADD_OUT_TERM_ID_BAIF,
.wTerminalType = UAC_TERMINAL_STREAMING,
.bAssocTerminal = 0x00, /* No associated terminal */
.bSourceID = BADD_FU_ID_BAIF,
.bCSourceID = BADD_CLOCK_SOURCE,
.bmControls = 0x00000000, /* No controls */
.wExTerminalDescrID = 0x0000,
.wConnectorsDescrID = 0x0000,
.wTerminalDescrStr = 0x0000
};
struct uac3_output_terminal_descriptor badd_baof_out_term_desc = {
.bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
.bTerminalID = BADD_OUT_TERM_ID_BAOF,
.bSourceID = BADD_FU_ID_BAOF,
.bCSourceID = BADD_CLOCK_SOURCE,
.wExTerminalDescrID = 0x0000,
.wTerminalDescrStr = 0x0000
};
__u8 monoControls[] = {
0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00};
__u8 stereoControls[] = {
0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00
};
__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF};
struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = {
.bLength = UAC3_DT_MIXER_UNIT_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC3_MIXER_UNIT_V3,
.bUnitID = BADD_MU_ID_BAIOF,
.bNrInPins = 0x02,
.baSourceID = badd_mu_src_ids,
.bmMixerControls = 0x00,
.bmControls = 0x00000000,
.wMixerDescrStr = 0x0000
};
struct uac3_feature_unit_descriptor badd_baif_fu_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
.bUnitID = BADD_FU_ID_BAIF,
.bSourceID = BADD_IN_TERM_ID_BAIF,
.wFeatureDescrStr = 0x0000
};
struct uac3_feature_unit_descriptor badd_baof_fu_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
.bUnitID = BADD_FU_ID_BAOF,
.wFeatureDescrStr = 0x0000
};
struct uac3_feature_unit_descriptor badd_baiof_fu_desc = {
.bLength = 0x0f,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
.bUnitID = BADD_FU_ID_BAIOF,
.bSourceID = BADD_IN_TERM_ID_BAIF,
.bmaControls = monoControls,
.wFeatureDescrStr = 0x0000
};
struct uac3_clock_source_descriptor badd_clock_desc = {
.bLength = UAC3_DT_CLOCK_SRC_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC3_CLOCK_SOURCE,
.bClockID = BADD_CLOCK_SOURCE,
.bmControls = 0x00000001,
.bReferenceTerminal = 0x00,
.wClockSourceStr = 0x0000
};
void *badd_desc_list[] = {
&badd_baif_in_term_desc,
&badd_baof_in_term_desc,
&badd_baiof_mu_desc,
&badd_baif_fu_desc,
&badd_baof_fu_desc,
&badd_baiof_fu_desc,
&badd_clock_desc
};

View file

@ -50,6 +50,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/control.h>
@ -184,6 +185,17 @@ static void *find_audio_control_unit(struct mixer_build *state,
/* we just parse the header */
struct uac_feature_unit_descriptor *hdr = NULL;
if (state->mixer->protocol == UAC_VERSION_3) {
int i;
for (i = 0; i < NUM_BADD_DESCS; i++) {
hdr = (void *)badd_desc_list[i];
if (hdr->bUnitID == unit)
return hdr;
}
return NULL;
}
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
@ -717,7 +729,7 @@ static int check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le16_to_cpu(d->wChannelConfig);
term->name = d->iTerminal;
} else { /* UAC_VERSION_2 */
} else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
@ -734,6 +746,24 @@ static int check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le32_to_cpu(d->bmChannelConfig);
term->name = d->iTerminal;
} else { /* UAC_VERSION_3 */
struct uac3_input_terminal_descriptor *d = p1;
err = check_input_term(state,
d->bCSourceID, term);
if (err < 0)
return err;
term->id = id;
term->type = d->wTerminalType;
if (d->wClusterDescrID == CLUSTER_ID_MONO) {
term->channels = NUM_CHANNELS_MONO;
term->chconfig = BADD_CH_CONFIG_MONO;
} else {
term->channels = NUM_CHANNELS_STEREO;
term->chconfig = BADD_CH_CONFIG_STEREO;
}
term->name = d->wTerminalDescrStr;
}
return 0;
case UAC_FEATURE_UNIT: {
@ -751,41 +781,81 @@ static int check_input_term(struct mixer_build *state, int id,
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);
/* UAC3_MIXER_UNIT_V3 */
case UAC2_CLOCK_SELECTOR:
/* UAC3_CLOCK_SOURCE */ {
if (state->mixer->protocol == UAC_VERSION_3
&& hdr[2] == UAC3_CLOCK_SOURCE) {
struct uac3_clock_source_descriptor *d = p1;
term->type = d->bDescriptorSubtype << 16;
term->id = id;
term->name = d->wClockSourceStr;
} else if (state->mixer->protocol == UAC_VERSION_3
&& hdr[2] == UAC3_MIXER_UNIT_V3) {
struct uac3_mixer_unit_descriptor *d = p1;
term->type = d->bDescriptorSubtype << 16;
if (d->wClusterDescrID == CLUSTER_ID_MONO) {
term->channels = NUM_CHANNELS_MONO;
term->chconfig = BADD_CH_CONFIG_MONO;
} else {
term->channels = NUM_CHANNELS_STEREO;
term->chconfig = BADD_CH_CONFIG_STEREO;
}
term->name = d->wMixerDescrStr;
} else {
struct uac_selector_unit_descriptor *d = p1;
/* call recursively to retrieve channel info */
err = check_input_term(state,
d->baSourceID[0], term);
if (err < 0)
return err;
/* virtual type */
term->type = d->bDescriptorSubtype << 16;
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 */
/* UAC3_FEATURE_UNIT_V3 */
case UAC2_EXTENSION_UNIT_V2: {
struct uac_processing_unit_descriptor *d = p1;
if (state->mixer->protocol == UAC_VERSION_3) {
struct uac_feature_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.
*/
id = d->bSourceID;
} else {
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.
*/
return 0;
}
if (d->bNrInPins) {
id = d->baSourceID[0];
break; /* continue to parse */
}
/* virtual type */
term->type = d->bDescriptorSubtype << 16;
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;
}
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, state->mixer->protocol);
term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
return 0;
break;
}
case UAC2_CLOCK_SOURCE: {
struct uac_clock_source_descriptor *d = p1;
@ -1232,12 +1302,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
int nameid = uac_feature_unit_iFeature(desc);
int nameid;
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
unsigned int range;
if (state->mixer->protocol == UAC_VERSION_3)
nameid = ((struct uac3_feature_unit_descriptor *)
raw_desc)->wFeatureDescrStr;
else
nameid = uac_feature_unit_iFeature(desc);
control++; /* change from zero-based to 1-based value */
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
@ -1258,7 +1334,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
ctl_info = &audio_feature_info[control-1];
if (state->mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
else /* UAC_VERSION_2 */
else /* UAC_VERSION_2 or UAC_VERSION_3*/
cval->val_type = ctl_info->type_uac2 >= 0 ?
ctl_info->type_uac2 : ctl_info->type;
@ -1381,6 +1457,62 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl);
}
static int find_num_channels(struct mixer_build *state, int dir)
{
int num_ch = -EINVAL, num, i, j, wMaxPacketSize;
int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber;
struct usb_interface *usb_iface =
usb_ifnum_to_if(state->mixer->chip->dev, ctrlif);
struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc;
struct usb_host_interface *alts;
for (i = 0; i < assoc->bInterfaceCount; i++) {
int intf = assoc->bFirstInterface + i;
if (intf != ctrlif) {
struct usb_interface *iface =
usb_ifnum_to_if(state->mixer->chip->dev, intf);
alts = &iface->altsetting[1];
if (dir == USB_DIR_OUT &&
get_endpoint(alts, 0)->bEndpointAddress &
USB_DIR_IN)
continue;
if (dir == USB_DIR_IN &&
!(get_endpoint(alts, 0)->bEndpointAddress &
USB_DIR_IN))
continue;
num = iface->num_altsetting;
for (j = 1; j < num; j++) {
num_ch = NUM_CHANNELS_MONO;
alts = &iface->altsetting[j];
wMaxPacketSize = le16_to_cpu(
get_endpoint(alts, 0)->
wMaxPacketSize);
switch (wMaxPacketSize) {
case BADD_MAXPSIZE_SYNC_MONO_16:
case BADD_MAXPSIZE_SYNC_MONO_24:
case BADD_MAXPSIZE_ASYNC_MONO_16:
case BADD_MAXPSIZE_ASYNC_MONO_24:
break;
case BADD_MAXPSIZE_SYNC_STEREO_16:
case BADD_MAXPSIZE_SYNC_STEREO_24:
case BADD_MAXPSIZE_ASYNC_STEREO_16:
case BADD_MAXPSIZE_ASYNC_STEREO_24:
num_ch = NUM_CHANNELS_STEREO;
break;
}
if (num_ch == NUM_CHANNELS_MONO)
continue;
else
break;
}
}
}
return num_ch;
}
/*
* parse a feature unit
*
@ -1412,7 +1544,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;
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
@ -1423,11 +1555,114 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
} else {
struct usb_interface *usb_iface =
usb_ifnum_to_if(state->mixer->chip->dev,
get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
struct usb_interface_assoc_descriptor *assoc =
usb_iface->intf_assoc;
csize = 4;
switch (unitid) {
case BADD_FU_ID_BAIOF:
channels = NUM_CHANNELS_MONO;
bmaControls = monoControls;
badd_baif_in_term_desc.wClusterDescrID =
CLUSTER_ID_MONO;
break;
case BADD_FU_ID_BAOF:
switch (assoc->bFunctionSubClass) {
case PROF_HEADPHONE:
case PROF_HEADSET_ADAPTER:
channels = NUM_CHANNELS_STEREO;
bmaControls = stereoControls;
badd_baiof_mu_desc.wClusterDescrID =
CLUSTER_ID_MONO;
break;
case PROF_SPEAKERPHONE:
channels = NUM_CHANNELS_MONO;
bmaControls = monoControls;
badd_baof_in_term_desc.wClusterDescrID =
CLUSTER_ID_MONO;
break;
default:
channels = find_num_channels(state,
USB_DIR_OUT);
if (channels < 0) {
usb_audio_err(state->chip,
"unit %u: Cant find num of channels\n",
unitid);
return channels;
}
bmaControls = (channels == NUM_CHANNELS_MONO) ?
monoControls : stereoControls;
badd_baof_in_term_desc.wClusterDescrID =
(channels == NUM_CHANNELS_MONO) ?
CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
break;
}
break;
case BADD_FU_ID_BAIF:
switch (assoc->bFunctionSubClass) {
case PROF_HEADSET:
case PROF_HEADSET_ADAPTER:
case PROF_SPEAKERPHONE:
channels = NUM_CHANNELS_MONO;
bmaControls = monoControls;
badd_baif_in_term_desc.wClusterDescrID =
CLUSTER_ID_MONO;
break;
default:
channels = find_num_channels(state, USB_DIR_IN);
if (channels < 0) {
usb_audio_err(state->chip,
"unit %u: Cant find num of channels\n",
unitid);
return channels;
}
bmaControls = (channels == NUM_CHANNELS_MONO) ?
monoControls : stereoControls;
badd_baif_in_term_desc.wClusterDescrID =
(channels == NUM_CHANNELS_MONO) ?
CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
break;
}
break;
}
}
/* parse the source unit */
if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
return err;
if (state->mixer->protocol != UAC_VERSION_3) {
err = parse_audio_unit(state, hdr->bSourceID);
if (err < 0)
return err;
} else {
struct usb_interface *usb_iface =
usb_ifnum_to_if(state->mixer->chip->dev,
get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
struct usb_interface_assoc_descriptor *assoc =
usb_iface->intf_assoc;
switch (unitid) {
case BADD_FU_ID_BAOF:
switch (assoc->bFunctionSubClass) {
case PROF_HEADSET:
case PROF_HEADSET_ADAPTER:
hdr->bSourceID = BADD_MU_ID_BAIOF;
break;
default:
hdr->bSourceID = BADD_IN_TERM_ID_BAOF;
break;
}
}
err = parse_audio_unit(state, hdr->bSourceID);
if (err < 0)
return err;
}
/* determine the input source type and name */
err = check_input_term(state, hdr->bSourceID, &iterm);
@ -1481,7 +1716,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 or UAC_VERSION_3*/
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
@ -1599,12 +1834,19 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int input_pins, num_ins, num_outs;
int pin, ich, err;
if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
!(num_outs = uac_mixer_unit_bNrChannels(desc))) {
usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n",
unitid);
return -EINVAL;
if (state->mixer->protocol == UAC_VERSION_3) {
input_pins = badd_baiof_mu_desc.bNrInPins;
num_outs =
(badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ?
NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO;
} else {
if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
!(num_outs = uac_mixer_unit_bNrChannels(desc))) {
usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n",
unitid);
return -EINVAL;
}
}
num_ins = 0;
@ -1624,9 +1866,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int och, ich_has_controls = 0;
for (och = 0; och < num_outs; och++) {
__u8 *c = uac_mixer_unit_bmControls(desc,
state->mixer->protocol);
__u8 *c = NULL;
if (state->mixer->protocol == UAC_VERSION_3)
c =
&(badd_baiof_mu_desc.bmMixerControls);
else
c = uac_mixer_unit_bmControls(desc,
state->mixer->protocol);
if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
break;
@ -2134,16 +2381,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
/* UAC3_MIXER_UNIT_V3 has the same value */
case UAC2_CLOCK_SELECTOR:
return parse_audio_selector_unit(state, unitid, p1);
/* UAC3_CLOCK_SOURCE has the same value */
if (state->mixer->protocol == UAC_VERSION_3 &&
p1[2] == UAC3_CLOCK_SOURCE)
return 0; /* NOP */
else if (state->mixer->protocol == UAC_VERSION_3
&& p1[2] == UAC3_MIXER_UNIT_V3)
return parse_audio_mixer_unit(state, unitid, p1);
else
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 */
/* UAC3_FEATURE_UNIT_V3 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
else
else if (state->mixer->protocol == UAC_VERSION_2)
return 0; /* FIXME - effect units not implemented yet */
else
return parse_audio_feature_unit(state, unitid, p1);
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
@ -2178,6 +2437,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
return 0;
}
static int make_out_term(struct mixer_build state, int wTerminalType)
{
struct uac3_output_terminal_descriptor *desc = NULL;
if (wTerminalType == UAC_TERMINAL_STREAMING)
desc = &badd_baif_out_term_desc;
else {
desc = &badd_baof_out_term_desc;
desc->wTerminalType = wTerminalType;
}
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
state.oterm.type = desc->wTerminalType;
state.oterm.name = desc->wTerminalDescrStr;
return parse_audio_unit(&state, desc->bSourceID);
}
/*
* create mixer controls
*
@ -2186,9 +2462,8 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{
struct mixer_build state;
int err;
int err = -EINVAL;
const struct usbmix_ctl_map *map;
void *p;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
@ -2206,44 +2481,108 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
}
}
p = NULL;
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen,
p, UAC_OUTPUT_TERMINAL)) != NULL) {
if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;
if (mixer->protocol == UAC_VERSION_3) {
struct usb_interface *usb_iface =
usb_ifnum_to_if(mixer->chip->dev,
get_iface_desc(mixer->hostif)->bInterfaceNumber);
struct usb_interface_assoc_descriptor *assoc =
usb_iface->intf_assoc;
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 = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
switch (assoc->bFunctionSubClass) {
case PROF_GENERIC_IO: {
if (assoc->bInterfaceCount == 0x02) {
if (get_endpoint(mixer->hostif,
0)->bEndpointAddress | USB_DIR_IN)
err = make_out_term(state,
UAC_TERMINAL_STREAMING);
else
err = make_out_term(state,
UAC_OUTPUT_TERMINAL_UNDEFINED);
} else {
err = make_out_term(state,
UAC_OUTPUT_TERMINAL_UNDEFINED);
if (err < 0 && err != -EINVAL)
return err;
err = make_out_term(state,
UAC_TERMINAL_STREAMING);
}
break;
}
case PROF_HEADPHONE:
err = make_out_term(state,
UAC_OUTPUT_TERMINAL_HEADPHONES);
break;
case PROF_SPEAKER:
err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER);
break;
case PROF_MICROPHONE:
err = make_out_term(state, UAC_TERMINAL_STREAMING);
break;
case PROF_HEADSET:
case PROF_HEADSET_ADAPTER:
err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET);
if (err < 0 && err != -EINVAL)
return err;
} else { /* 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;
state.oterm.type = le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
err = make_out_term(state, UAC_TERMINAL_STREAMING);
break;
case PROF_SPEAKERPHONE:
err = make_out_term(state,
UAC_BIDIR_TERMINAL_SPEAKERPHONE);
if (err < 0 && err != -EINVAL)
return err;
err = make_out_term(state, UAC_TERMINAL_STREAMING);
break;
}
if (err < 0 && err != -EINVAL)
return err;
} else {
void *p;
/*
* For UAC2, use the same approach to also add the
* clock selectors
*/
err = parse_audio_unit(&state, desc->bCSourceID);
if (err < 0 && err != -EINVAL)
return err;
p = NULL;
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen, p,
UAC_OUTPUT_TERMINAL)) != NULL) {
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;
state.oterm.type =
le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
if (err < 0 && err != -EINVAL)
return err;
} else { /* 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;
state.oterm.type =
le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
if (err < 0 && err != -EINVAL)
return err;
/*
* For UAC2, use the same approach to also add
* the clock selectors
*/
err = parse_audio_unit(&state,
desc->bCSourceID);
if (err < 0 && err != -EINVAL)
return err;
}
}
}
@ -2478,6 +2817,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 ||