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>
|
#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_16 0x0060
|
||||||
#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090
|
#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090
|
||||||
#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0
|
#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0
|
||||||
|
@ -43,6 +47,8 @@
|
||||||
#define NUM_CHANNELS_STEREO 2
|
#define NUM_CHANNELS_STEREO 2
|
||||||
#define BADD_CH_CONFIG_MONO 0
|
#define BADD_CH_CONFIG_MONO 0
|
||||||
#define BADD_CH_CONFIG_STEREO 3
|
#define BADD_CH_CONFIG_STEREO 3
|
||||||
|
#define CLUSTER_ID_MONO 0x0001
|
||||||
|
#define CLUSTER_ID_STEREO 0x0002
|
||||||
|
|
||||||
#define FULL_ADC_PROFILE 0x01
|
#define FULL_ADC_PROFILE 0x01
|
||||||
|
|
||||||
|
@ -69,4 +75,98 @@
|
||||||
#define UAC_BIDIR_TERMINAL_HEADSET 0x0402
|
#define UAC_BIDIR_TERMINAL_HEADSET 0x0402
|
||||||
#define UAC_BIDIR_TERMINAL_SPEAKERPHONE 0x0403
|
#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 */
|
#endif /* __LINUX_USB_AUDIO_V3_H */
|
||||||
|
|
|
@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \
|
||||||
pcm.o \
|
pcm.o \
|
||||||
proc.o \
|
proc.o \
|
||||||
quirks.o \
|
quirks.o \
|
||||||
stream.o
|
stream.o \
|
||||||
|
badd.o
|
||||||
|
|
||||||
snd-usbmidi-lib-objs := midi.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.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
|
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
|
@ -184,6 +185,17 @@ static void *find_audio_control_unit(struct mixer_build *state,
|
||||||
/* we just parse the header */
|
/* we just parse the header */
|
||||||
struct uac_feature_unit_descriptor *hdr = NULL;
|
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,
|
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
|
||||||
USB_DT_CS_INTERFACE)) != NULL) {
|
USB_DT_CS_INTERFACE)) != NULL) {
|
||||||
if (hdr->bLength >= 4 &&
|
if (hdr->bLength >= 4 &&
|
||||||
|
@ -717,7 +729,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
term->channels = d->bNrChannels;
|
term->channels = d->bNrChannels;
|
||||||
term->chconfig = le16_to_cpu(d->wChannelConfig);
|
term->chconfig = le16_to_cpu(d->wChannelConfig);
|
||||||
term->name = d->iTerminal;
|
term->name = d->iTerminal;
|
||||||
} else { /* UAC_VERSION_2 */
|
} else if (state->mixer->protocol == UAC_VERSION_2) {
|
||||||
struct uac2_input_terminal_descriptor *d = p1;
|
struct uac2_input_terminal_descriptor *d = p1;
|
||||||
|
|
||||||
/* call recursively to verify that the
|
/* 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->channels = d->bNrChannels;
|
||||||
term->chconfig = le32_to_cpu(d->bmChannelConfig);
|
term->chconfig = le32_to_cpu(d->bmChannelConfig);
|
||||||
term->name = d->iTerminal;
|
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;
|
return 0;
|
||||||
case UAC_FEATURE_UNIT: {
|
case UAC_FEATURE_UNIT: {
|
||||||
|
@ -751,28 +781,61 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case UAC_SELECTOR_UNIT:
|
case UAC_SELECTOR_UNIT:
|
||||||
case UAC2_CLOCK_SELECTOR: {
|
/* 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;
|
struct uac_selector_unit_descriptor *d = p1;
|
||||||
/* call recursively to retrieve the channel info */
|
/* call recursively to retrieve channel info */
|
||||||
err = check_input_term(state, d->baSourceID[0], term);
|
err = check_input_term(state,
|
||||||
|
d->baSourceID[0], term);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
/* virtual type */
|
||||||
|
term->type = d->bDescriptorSubtype << 16;
|
||||||
term->id = id;
|
term->id = id;
|
||||||
term->name = uac_selector_unit_iSelector(d);
|
term->name = uac_selector_unit_iSelector(d);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case UAC1_PROCESSING_UNIT:
|
case UAC1_PROCESSING_UNIT:
|
||||||
case UAC1_EXTENSION_UNIT:
|
case UAC1_EXTENSION_UNIT:
|
||||||
/* UAC2_PROCESSING_UNIT_V2 */
|
/* UAC2_PROCESSING_UNIT_V2 */
|
||||||
/* UAC2_EFFECT_UNIT */
|
/* UAC2_EFFECT_UNIT */
|
||||||
|
/* UAC3_FEATURE_UNIT_V3 */
|
||||||
case UAC2_EXTENSION_UNIT_V2: {
|
case UAC2_EXTENSION_UNIT_V2: {
|
||||||
|
if (state->mixer->protocol == UAC_VERSION_3) {
|
||||||
|
struct uac_feature_unit_descriptor *d = p1;
|
||||||
|
|
||||||
|
id = d->bSourceID;
|
||||||
|
} else {
|
||||||
struct uac_processing_unit_descriptor *d = p1;
|
struct uac_processing_unit_descriptor *d = p1;
|
||||||
|
|
||||||
if (state->mixer->protocol == UAC_VERSION_2 &&
|
if (state->mixer->protocol == UAC_VERSION_2 &&
|
||||||
hdr[2] == UAC2_EFFECT_UNIT) {
|
hdr[2] == UAC2_EFFECT_UNIT) {
|
||||||
/* UAC2/UAC1 unit IDs overlap here in an
|
/* UAC2/UAC1 unit IDs overlap here in an
|
||||||
* uncompatible way. Ignore this unit for now.
|
* uncompatible way. Ignore this unit
|
||||||
|
* for now.
|
||||||
*/
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -781,12 +844,19 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
id = d->baSourceID[0];
|
id = d->baSourceID[0];
|
||||||
break; /* continue to parse */
|
break; /* continue to parse */
|
||||||
}
|
}
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
/* virtual type */
|
||||||
term->channels = uac_processing_unit_bNrChannels(d);
|
term->type = d->bDescriptorSubtype << 16;
|
||||||
term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
|
term->channels =
|
||||||
term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case UAC2_CLOCK_SOURCE: {
|
case UAC2_CLOCK_SOURCE: {
|
||||||
struct uac_clock_source_descriptor *d = p1;
|
struct uac_clock_source_descriptor *d = p1;
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
|
@ -1232,12 +1302,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
struct usb_feature_control_info *ctl_info;
|
struct usb_feature_control_info *ctl_info;
|
||||||
unsigned int len = 0;
|
unsigned int len = 0;
|
||||||
int mapped_name = 0;
|
int mapped_name = 0;
|
||||||
int nameid = uac_feature_unit_iFeature(desc);
|
int nameid;
|
||||||
struct snd_kcontrol *kctl;
|
struct snd_kcontrol *kctl;
|
||||||
struct usb_mixer_elem_info *cval;
|
struct usb_mixer_elem_info *cval;
|
||||||
const struct usbmix_name_map *map;
|
const struct usbmix_name_map *map;
|
||||||
unsigned int range;
|
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 */
|
control++; /* change from zero-based to 1-based value */
|
||||||
|
|
||||||
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
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];
|
ctl_info = &audio_feature_info[control-1];
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (state->mixer->protocol == UAC_VERSION_1)
|
||||||
cval->val_type = ctl_info->type;
|
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 ?
|
cval->val_type = ctl_info->type_uac2 >= 0 ?
|
||||||
ctl_info->type_uac2 : ctl_info->type;
|
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);
|
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
|
* parse a feature unit
|
||||||
*
|
*
|
||||||
|
@ -1412,7 +1544,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
unitid);
|
unitid);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (state->mixer->protocol == UAC_VERSION_2) {
|
||||||
struct uac2_feature_unit_descriptor *ftr = _ftr;
|
struct uac2_feature_unit_descriptor *ftr = _ftr;
|
||||||
csize = 4;
|
csize = 4;
|
||||||
channels = (hdr->bLength - 6) / 4 - 1;
|
channels = (hdr->bLength - 6) / 4 - 1;
|
||||||
|
@ -1423,11 +1555,114 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
unitid);
|
unitid);
|
||||||
return -EINVAL;
|
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 */
|
/* parse the source unit */
|
||||||
if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
|
if (state->mixer->protocol != UAC_VERSION_3) {
|
||||||
|
err = parse_audio_unit(state, hdr->bSourceID);
|
||||||
|
if (err < 0)
|
||||||
return err;
|
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 */
|
/* determine the input source type and name */
|
||||||
err = check_input_term(state, hdr->bSourceID, &iterm);
|
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,
|
build_feature_ctl(state, _ftr, 0, i, &iterm,
|
||||||
unitid, 0);
|
unitid, 0);
|
||||||
}
|
}
|
||||||
} else { /* UAC_VERSION_2 */
|
} else { /* UAC_VERSION_2 or UAC_VERSION_3*/
|
||||||
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
||||||
unsigned int ch_bits = 0;
|
unsigned int ch_bits = 0;
|
||||||
unsigned int ch_read_only = 0;
|
unsigned int ch_read_only = 0;
|
||||||
|
@ -1599,6 +1834,12 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
||||||
int input_pins, num_ins, num_outs;
|
int input_pins, num_ins, num_outs;
|
||||||
int pin, ich, err;
|
int pin, ich, err;
|
||||||
|
|
||||||
|
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) ||
|
if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
|
||||||
!(num_outs = uac_mixer_unit_bNrChannels(desc))) {
|
!(num_outs = uac_mixer_unit_bNrChannels(desc))) {
|
||||||
usb_audio_err(state->chip,
|
usb_audio_err(state->chip,
|
||||||
|
@ -1606,6 +1847,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
||||||
unitid);
|
unitid);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
num_ins = 0;
|
num_ins = 0;
|
||||||
ich = 0;
|
ich = 0;
|
||||||
|
@ -1624,9 +1866,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
||||||
int och, ich_has_controls = 0;
|
int och, ich_has_controls = 0;
|
||||||
|
|
||||||
for (och = 0; och < num_outs; och++) {
|
for (och = 0; och < num_outs; och++) {
|
||||||
__u8 *c = uac_mixer_unit_bmControls(desc,
|
__u8 *c = NULL;
|
||||||
state->mixer->protocol);
|
|
||||||
|
|
||||||
|
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)) {
|
if (check_matrix_bitmap(c, ich, och, num_outs)) {
|
||||||
ich_has_controls = 1;
|
ich_has_controls = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -2134,16 +2381,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||||
case UAC_MIXER_UNIT:
|
case UAC_MIXER_UNIT:
|
||||||
return parse_audio_mixer_unit(state, unitid, p1);
|
return parse_audio_mixer_unit(state, unitid, p1);
|
||||||
case UAC_SELECTOR_UNIT:
|
case UAC_SELECTOR_UNIT:
|
||||||
|
/* UAC3_MIXER_UNIT_V3 has the same value */
|
||||||
case UAC2_CLOCK_SELECTOR:
|
case UAC2_CLOCK_SELECTOR:
|
||||||
|
/* 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);
|
return parse_audio_selector_unit(state, unitid, p1);
|
||||||
case UAC_FEATURE_UNIT:
|
case UAC_FEATURE_UNIT:
|
||||||
return parse_audio_feature_unit(state, unitid, p1);
|
return parse_audio_feature_unit(state, unitid, p1);
|
||||||
case UAC1_PROCESSING_UNIT:
|
case UAC1_PROCESSING_UNIT:
|
||||||
/* UAC2_EFFECT_UNIT has the same value */
|
/* UAC2_EFFECT_UNIT has the same value */
|
||||||
|
/* UAC3_FEATURE_UNIT_V3 has the same value */
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (state->mixer->protocol == UAC_VERSION_1)
|
||||||
return parse_audio_processing_unit(state, unitid, p1);
|
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 */
|
return 0; /* FIXME - effect units not implemented yet */
|
||||||
|
else
|
||||||
|
return parse_audio_feature_unit(state, unitid, p1);
|
||||||
case UAC1_EXTENSION_UNIT:
|
case UAC1_EXTENSION_UNIT:
|
||||||
/* UAC2_PROCESSING_UNIT_V2 has the same value */
|
/* UAC2_PROCESSING_UNIT_V2 has the same value */
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (state->mixer->protocol == UAC_VERSION_1)
|
||||||
|
@ -2178,6 +2437,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
|
||||||
return 0;
|
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
|
* 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)
|
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||||
{
|
{
|
||||||
struct mixer_build state;
|
struct mixer_build state;
|
||||||
int err;
|
int err = -EINVAL;
|
||||||
const struct usbmix_ctl_map *map;
|
const struct usbmix_ctl_map *map;
|
||||||
void *p;
|
|
||||||
|
|
||||||
memset(&state, 0, sizeof(state));
|
memset(&state, 0, sizeof(state));
|
||||||
state.chip = mixer->chip;
|
state.chip = mixer->chip;
|
||||||
|
@ -2206,46 +2481,110 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
p = NULL;
|
p = NULL;
|
||||||
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
|
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
|
||||||
mixer->hostif->extralen,
|
mixer->hostif->extralen, p,
|
||||||
p, UAC_OUTPUT_TERMINAL)) != NULL) {
|
UAC_OUTPUT_TERMINAL)) != NULL) {
|
||||||
if (mixer->protocol == UAC_VERSION_1) {
|
if (mixer->protocol == UAC_VERSION_1) {
|
||||||
struct uac1_output_terminal_descriptor *desc = p;
|
struct uac1_output_terminal_descriptor *desc =
|
||||||
|
p;
|
||||||
|
|
||||||
if (desc->bLength < sizeof(*desc))
|
if (desc->bLength < sizeof(*desc))
|
||||||
continue; /* invalid descriptor? */
|
continue; /* invalid descriptor? */
|
||||||
/* mark terminal ID as visited */
|
/* mark terminal ID as visited */
|
||||||
set_bit(desc->bTerminalID, state.unitbitmap);
|
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||||
state.oterm.id = desc->bTerminalID;
|
state.oterm.id = desc->bTerminalID;
|
||||||
state.oterm.type = le16_to_cpu(desc->wTerminalType);
|
state.oterm.type =
|
||||||
|
le16_to_cpu(desc->wTerminalType);
|
||||||
state.oterm.name = desc->iTerminal;
|
state.oterm.name = desc->iTerminal;
|
||||||
err = parse_audio_unit(&state, desc->bSourceID);
|
err = parse_audio_unit(&state, desc->bSourceID);
|
||||||
if (err < 0 && err != -EINVAL)
|
if (err < 0 && err != -EINVAL)
|
||||||
return err;
|
return err;
|
||||||
} else { /* UAC_VERSION_2 */
|
} else { /* UAC_VERSION_2 */
|
||||||
struct uac2_output_terminal_descriptor *desc = p;
|
struct uac2_output_terminal_descriptor *desc =
|
||||||
|
p;
|
||||||
|
|
||||||
if (desc->bLength < sizeof(*desc))
|
if (desc->bLength < sizeof(*desc))
|
||||||
continue; /* invalid descriptor? */
|
continue; /* invalid descriptor? */
|
||||||
/* mark terminal ID as visited */
|
/* mark terminal ID as visited */
|
||||||
set_bit(desc->bTerminalID, state.unitbitmap);
|
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||||
state.oterm.id = desc->bTerminalID;
|
state.oterm.id = desc->bTerminalID;
|
||||||
state.oterm.type = le16_to_cpu(desc->wTerminalType);
|
state.oterm.type =
|
||||||
|
le16_to_cpu(desc->wTerminalType);
|
||||||
state.oterm.name = desc->iTerminal;
|
state.oterm.name = desc->iTerminal;
|
||||||
err = parse_audio_unit(&state, desc->bSourceID);
|
err = parse_audio_unit(&state, desc->bSourceID);
|
||||||
if (err < 0 && err != -EINVAL)
|
if (err < 0 && err != -EINVAL)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For UAC2, use the same approach to also add the
|
* For UAC2, use the same approach to also add
|
||||||
* clock selectors
|
* the clock selectors
|
||||||
*/
|
*/
|
||||||
err = parse_audio_unit(&state, desc->bCSourceID);
|
err = parse_audio_unit(&state,
|
||||||
|
desc->bCSourceID);
|
||||||
if (err < 0 && err != -EINVAL)
|
if (err < 0 && err != -EINVAL)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2478,6 +2817,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
||||||
case UAC_VERSION_2:
|
case UAC_VERSION_2:
|
||||||
mixer->protocol = UAC_VERSION_2;
|
mixer->protocol = UAC_VERSION_2;
|
||||||
break;
|
break;
|
||||||
|
case UAC_VERSION_3:
|
||||||
|
mixer->protocol = UAC_VERSION_3;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
|
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
|
||||||
|
|
Loading…
Add table
Reference in a new issue