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:
parent
2ecedf5dc7
commit
2b7792c598
4 changed files with 661 additions and 81 deletions
|
@ -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 */
|
||||
|
|
|
@ -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
137
sound/usb/badd.c
Normal 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
|
||||
};
|
||||
|
|
@ -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 ||
|
||||
|
|
Loading…
Add table
Reference in a new issue