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

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

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



BADD(Basic Audio Device Definition) is a subset of UAC3
specifications. It defines standard profiles which provide
specific attributes of class descriptors. BADD audio device
provides profile id and BADD supporting host will infer
attributes from profile-id.

Currently USB Audio Host driver has support for UAC1
and UAC2 devices. To handle BADD 3.0 devices, AudioControl
and AudioStreaming Interfaces have to be properly parsed.
The Host has to derive all class-specific information based
on the profile ID that the device exposes. Add support for
parsing the AudioStreaming interfaces exposed by the device.

Change-Id: I726bbb735708fe8e395011c7228a73d5f65c092d
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent 0a1c067c
Loading
Loading
Loading
Loading
+72 −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 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 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

#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
+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;
+4 −0
Original line number Diff line number Diff line
@@ -431,6 +431,10 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,

	case UAC_VERSION_2:
		return set_sample_rate_v2(chip, iface, alts, fmt, rate);

	/* Clock rate is fixed at 48 kHz for BADD devices */
	case UAC_VERSION_3:
		return 0;
	}
}
+62 −6
Original line number Diff line number Diff line
@@ -20,6 +20,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/pcm.h>
@@ -69,6 +70,34 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
		format <<= 1;
		break;
	}

	case UAC_VERSION_3: {
		switch (fp->maxpacksize) {
		case BADD_MAXPSIZE_SYNC_MONO_16:
		case BADD_MAXPSIZE_SYNC_STEREO_16:
		case BADD_MAXPSIZE_ASYNC_MONO_16:
		case BADD_MAXPSIZE_ASYNC_STEREO_16: {
			sample_width = BIT_RES_16_BIT;
			sample_bytes = SUBSLOTSIZE_16_BIT;
			break;
		}

		case BADD_MAXPSIZE_SYNC_MONO_24:
		case BADD_MAXPSIZE_SYNC_STEREO_24:
		case BADD_MAXPSIZE_ASYNC_MONO_24:
		case BADD_MAXPSIZE_ASYNC_STEREO_24: {
			sample_width = BIT_RES_24_BIT;
			sample_bytes = SUBSLOTSIZE_24_BIT;
			break;
		}
		default:
			usb_audio_err(chip, "%u:%d : Invalid wMaxPacketSize\n",
				fp->iface, fp->altsetting);
			return pcm_formats;
		}
		format = 1 << format;
		break;
	}
	}

	if ((pcm_formats == 0) &&
@@ -364,17 +393,34 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
	return ret;
}

static int badd_set_audio_rate_v3(struct snd_usb_audio *chip,
		   struct audioformat *fp)
{
	unsigned int rate;

	fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
	if (fp->rate_table == NULL)
		return -ENOMEM;

	fp->nr_rates = 1;
	rate = BADD_SAMPLING_RATE;
	fp->rate_min = fp->rate_max = fp->rate_table[0] = rate;
	fp->rates |= snd_pcm_rate_to_rate_bit(rate);
	return 0;
}

/*
 * parse the format type I and III descriptors
 */
static int parse_audio_format_i(struct snd_usb_audio *chip,
				struct audioformat *fp, unsigned int format,
				u8 format_type,
				struct uac_format_type_i_continuous_descriptor *fmt)
{
	snd_pcm_format_t pcm_format;
	int ret;

	if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
	if (format_type == UAC_FORMAT_TYPE_III) {
		/* FIXME: the format type is really IECxxx
		 *        but we give normal PCM format to get the existing
		 *        apps working...
@@ -413,6 +459,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
		/* fp->channels is already set in this case */
		ret = parse_audio_format_rates_v2(chip, fp);
		break;
	case UAC_VERSION_3:
		ret = badd_set_audio_rate_v3(chip, fp);
		break;
	}

	if (fp->channels < 1) {
@@ -484,11 +533,18 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
			       int stream)
{
	int err;
	int format_type = -EINVAL;

	if ((fp->protocol == UAC_VERSION_1) ||
			(fp->protocol == UAC_VERSION_2))
		format_type = fmt->bFormatType;
	else
		format_type = UAC_FORMAT_TYPE_I; /* only BADD is supported */

	switch (fmt->bFormatType) {
	switch (format_type) {
	case UAC_FORMAT_TYPE_I:
	case UAC_FORMAT_TYPE_III:
		err = parse_audio_format_i(chip, fp, format, fmt);
		err = parse_audio_format_i(chip, fp, format, format_type, fmt);
		break;
	case UAC_FORMAT_TYPE_II:
		err = parse_audio_format_ii(chip, fp, format, fmt);
@@ -497,10 +553,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
		usb_audio_info(chip,
			 "%u:%d : format type %d is not supported yet\n",
			 fp->iface, fp->altsetting,
			 fmt->bFormatType);
			 format_type);
		return -ENOTSUPP;
	}
	fp->fmt_type = fmt->bFormatType;
	fp->fmt_type = format_type;
	if (err < 0)
		return err;
#if 1
@@ -511,7 +567,7 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
	if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
	    chip->usb_id == USB_ID(0x041e, 0x3020) ||
	    chip->usb_id == USB_ID(0x041e, 0x3061)) {
		if (fmt->bFormatType == UAC_FORMAT_TYPE_I &&
		if (format_type == UAC_FORMAT_TYPE_I &&
		    fp->rates != SNDRV_PCM_RATE_48000 &&
		    fp->rates != SNDRV_PCM_RATE_96000)
			return -ENOTSUPP;
Loading