Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80723d03 authored by Ajay Agarwal's avatar Ajay Agarwal Committed by Mayank Rana
Browse files

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: default avatarAjay Agarwal <ajaya@codeaurora.org>
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent b9fa3f08
Loading
Loading
Loading
Loading
+104 −4
Original line number Diff line number Diff line
@@ -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
@@ -43,6 +47,8 @@
#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 */
+2 −1
Original line number Diff line number Diff line
@@ -15,7 +15,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

sound/usb/badd.c

0 → 100644
+137 −0
Original line number Diff line number Diff line
/*
 * 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
};
+424 −77
Original line number Diff line number Diff line
@@ -51,6 +51,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>
@@ -185,6 +186,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 &&
@@ -730,7 +742,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
@@ -747,6 +759,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: {
@@ -764,28 +794,61 @@ static int check_input_term(struct mixer_build *state, int id,
			return 0;
		}
		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;
			/* call recursively to retrieve the channel info */
			err = check_input_term(state, d->baSourceID[0], term);
				/* call recursively to retrieve channel info */
				err = check_input_term(state,
							d->baSourceID[0], term);
				if (err < 0)
					return err;
			term->type = d->bDescriptorSubtype << 16; /* virtual type */
				/* 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: {
			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;

				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.
					 * uncompatible way. Ignore this unit
					 * for now.
					 */
					return 0;
				}
@@ -794,12 +857,19 @@ static int check_input_term(struct mixer_build *state, int id,
					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);
				/* 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;
			}
			break;
		}
		case UAC2_CLOCK_SOURCE: {
			struct uac_clock_source_descriptor *d = p1;
			term->type = d->bDescriptorSubtype << 16; /* virtual type */
@@ -1245,12 +1315,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) {
@@ -1271,7 +1347,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;

@@ -1459,6 +1535,62 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
	return 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
 *
@@ -1496,7 +1628,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
				      unitid);
			return -EINVAL;
		}
	} else {
	} else if (state->mixer->protocol == UAC_VERSION_2) {
		struct uac2_feature_unit_descriptor *ftr = _ftr;
		if (hdr->bLength < 6) {
			usb_audio_err(state->chip,
@@ -1513,11 +1645,118 @@ 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;

		default:
			usb_audio_err(state->chip, "Invalid unit %u\n", unitid);
			return -EINVAL;
		}
	}

	/* 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;
	} 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);
@@ -1571,7 +1810,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;
@@ -1689,13 +1928,21 @@ 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))) {
	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 {
		input_pins = desc->bNrInPins;
		num_outs = uac_mixer_unit_bNrChannels(desc);
		if (desc->bLength < 11 || !input_pins || !num_outs) {
			usb_audio_err(state->chip,
				      "invalid MIXER UNIT descriptor %d\n",
				      unitid);
			return -EINVAL;
		}
	}

	num_ins = 0;
	ich = 0;
@@ -1714,9 +1961,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;
@@ -2232,16 +2484,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
	case UAC2_CLOCK_SOURCE:
		return parse_clock_source_unit(state, unitid, p1);
	case UAC_SELECTOR_UNIT:
	/*   UAC3_MIXER_UNIT_V3 has the same value */
	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);
	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)
@@ -2279,6 +2543,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
 *
@@ -2287,9 +2568,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;
@@ -2307,46 +2587,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;
		while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
					    mixer->hostif->extralen,
					    p, UAC_OUTPUT_TERMINAL)) != NULL) {
						mixer->hostif->extralen, p,
						UAC_OUTPUT_TERMINAL)) != NULL) {
			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))
					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.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;
				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.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
				 * For UAC2, use the same approach to also add
				 * the clock selectors
				 */
			err = parse_audio_unit(&state, desc->bCSourceID);
				err = parse_audio_unit(&state,
							desc->bCSourceID);
				if (err < 0 && err != -EINVAL)
					return err;
			}
		}
	}

	return 0;
}
@@ -2586,6 +2930,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 ||