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

Commit abea5c0b authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 429f51e3 80723d03
Loading
Loading
Loading
Loading
+172 −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.
 *
 * This file holds USB constants and structures defined
 * by the USB Device Class Definition for Audio Devices in version 3.0.
 * Comments below reference relevant sections of the documents contained
 * in http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip
 */

#ifndef __LINUX_USB_AUDIO_V3_H
#define __LINUX_USB_AUDIO_V3_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_24	0x0090
#define BADD_MAXPSIZE_SYNC_STEREO_16	0x00c0
#define BADD_MAXPSIZE_SYNC_STEREO_24	0x0120

#define BADD_MAXPSIZE_ASYNC_MONO_16	0x0062
#define BADD_MAXPSIZE_ASYNC_MONO_24	0x0093
#define BADD_MAXPSIZE_ASYNC_STEREO_16	0x00c4
#define BADD_MAXPSIZE_ASYNC_STEREO_24	0x0126

#define BIT_RES_16_BIT		0x10
#define BIT_RES_24_BIT		0x18

#define SUBSLOTSIZE_16_BIT	0x02
#define SUBSLOTSIZE_24_BIT	0x03

#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 CLUSTER_ID_MONO		0x0001
#define CLUSTER_ID_STEREO	0x0002

#define FULL_ADC_PROFILE	0x01

/* BADD Profile IDs */
#define PROF_GENERIC_IO		0x20
#define PROF_HEADPHONE		0x21
#define PROF_SPEAKER		0x22
#define PROF_MICROPHONE		0x23
#define PROF_HEADSET		0x24
#define PROF_HEADSET_ADAPTER	0x25
#define PROF_SPEAKERPHONE	0x26

/* BADD Entity IDs */
#define BADD_OUT_TERM_ID_BAOF	0x03
#define BADD_OUT_TERM_ID_BAIF	0x06
#define BADD_IN_TERM_ID_BAOF	0x01
#define BADD_IN_TERM_ID_BAIF	0x04
#define BADD_FU_ID_BAOF		0x02
#define BADD_FU_ID_BAIF		0x05
#define BADD_CLOCK_SOURCE	0x09
#define BADD_FU_ID_BAIOF	0x07
#define BADD_MU_ID_BAIOF	0x08

#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 */
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
/* bInterfaceProtocol values to denote the version of the standard used */
#define UAC_VERSION_1			0x00
#define UAC_VERSION_2			0x20
#define UAC_VERSION_3			0x30

/* A.2 Audio Interface Subclass Codes */
#define USB_SUBCLASS_AUDIOCONTROL	0x01
+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
};
+38 −22
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/module.h>
#include <linux/usb/audio-v3.h>

#include <sound/control.h>
#include <sound/core.h>
@@ -285,9 +286,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
	struct usb_host_interface *host_iface;
	struct usb_interface_descriptor *altsd;
	struct usb_interface *usb_iface;
	void *control_header;
	int i, protocol;
	int rest_bytes;

	usb_iface = usb_ifnum_to_if(dev, ctrlif);
	if (!usb_iface) {
@@ -303,19 +302,34 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
		return -EINVAL;
	}

	control_header = snd_usb_find_csint_desc(host_iface->extra,
						 host_iface->extralen,
						 NULL, UAC_HEADER);
	altsd = get_iface_desc(host_iface);
	protocol = altsd->bInterfaceProtocol;

	/*
	 * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces;
	 * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces
	 */
	switch (protocol) {
	default:
		dev_warn(&dev->dev,
			 "unknown interface protocol %#02x, assuming v1\n",
			 protocol);
		/* fall through */

	case UAC_VERSION_1: {
		void *control_header;
		struct uac1_ac_header_descriptor *h1;
		int rest_bytes;

		control_header = snd_usb_find_csint_desc(host_iface->extra,
					host_iface->extralen, NULL, UAC_HEADER);
		if (!control_header) {
			dev_err(&dev->dev, "cannot find UAC_HEADER\n");
			return -EINVAL;
		}

	rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
		control_header;
		rest_bytes = (void *)(host_iface->extra +
				host_iface->extralen) -	control_header;

		/* just to be sure -- this shouldn't hit at all */
		if (rest_bytes <= 0) {
@@ -323,16 +337,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
			return -EINVAL;
		}

	switch (protocol) {
	default:
		dev_warn(&dev->dev,
			 "unknown interface protocol %#02x, assuming v1\n",
			 protocol);
		/* fall through */

	case UAC_VERSION_1: {
		struct uac1_ac_header_descriptor *h1 = control_header;

		h1 = control_header;
		if (rest_bytes < sizeof(*h1)) {
			dev_err(&dev->dev, "too short v1 buffer descriptor\n");
			return -EINVAL;
@@ -359,7 +364,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
		break;
	}

	case UAC_VERSION_2: {
	case UAC_VERSION_2:
	case UAC_VERSION_3: {
		struct usb_interface_assoc_descriptor *assoc =
						usb_iface->intf_assoc;
		if (!assoc) {
@@ -378,7 +384,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
		}

		if (!assoc) {
			dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
			dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n",
					protocol);
			return -EINVAL;
		}

@@ -624,6 +631,15 @@ static int usb_audio_probe(struct usb_interface *intf,
	struct usb_host_interface *alts;
	int ifnum;
	u32 id;
	struct usb_interface_assoc_descriptor *assoc;

	assoc = intf->intf_assoc;
	if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO &&
	    assoc->bFunctionProtocol == UAC_VERSION_3 &&
	    assoc->bFunctionSubClass == FULL_ADC_PROFILE) {
		dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n");
		return -EINVAL;
	}

	alts = &intf->altsetting[0];
	ifnum = get_iface_desc(alts)->bInterfaceNumber;
Loading