summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorRuslan Bilovol <ruslan.bilovol@gmail.com>2018-03-21 02:03:59 +0200
committerTakashi Iwai <tiwai@suse.de>2018-03-21 11:46:33 +0100
commit9a2fe9b801f585baccf8352d82839dcd54b300cf (patch)
tree333de5ebcac026ce1a18da1c693cb8e9603c9a54 /sound
parentceb18f511beeb8b750f027f170eb6d901a082e9a (diff)
ALSA: usb: initial USB Audio Device Class 3.0 support
Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/usb/card.c7
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/clock.c228
-rw-r--r--sound/usb/clock.h4
-rw-r--r--sound/usb/format.c91
-rw-r--r--sound/usb/format.h6
-rw-r--r--sound/usb/mixer.c337
-rw-r--r--sound/usb/stream.c365
8 files changed, 848 insertions, 192 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 8018d56cfecc..4a1c6bb3dfa0 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -7,6 +7,7 @@
* Alan Cox (alan@lxorguk.ukuu.org.uk)
* Thomas Sailer (sailer@ife.ee.ethz.ch)
*
+ * Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,6 +45,7 @@
#include <linux/mutex.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <linux/module.h>
#include <sound/control.h>
@@ -281,7 +283,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_ifnum_to_if(dev, ctrlif)->intf_assoc;
@@ -301,7 +304,7 @@ 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 v2/v3 interfaces need an interface association\n");
return -EINVAL;
}
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ed87cc83eb47..1406292d50ec 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -22,7 +22,7 @@ struct audioformat {
unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */
- unsigned char protocol; /* UAC_VERSION_1/2 */
+ unsigned char protocol; /* UAC_VERSION_1/2/3 */
unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index eb3396ffba4c..25de7fe285d9 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -23,6 +23,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/info.h>
@@ -50,6 +51,22 @@ static struct uac_clock_source_descriptor *
return NULL;
}
+static struct uac3_clock_source_descriptor *
+ snd_usb_find_clock_source_v3(struct usb_host_interface *ctrl_iface,
+ int clock_id)
+{
+ struct uac3_clock_source_descriptor *cs = NULL;
+
+ while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+ ctrl_iface->extralen,
+ cs, UAC3_CLOCK_SOURCE))) {
+ if (cs->bClockID == clock_id)
+ return cs;
+ }
+
+ return NULL;
+}
+
static struct uac_clock_selector_descriptor *
snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
int clock_id)
@@ -69,6 +86,22 @@ static struct uac_clock_selector_descriptor *
return NULL;
}
+static struct uac3_clock_selector_descriptor *
+ snd_usb_find_clock_selector_v3(struct usb_host_interface *ctrl_iface,
+ int clock_id)
+{
+ struct uac3_clock_selector_descriptor *cs = NULL;
+
+ while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+ ctrl_iface->extralen,
+ cs, UAC3_CLOCK_SELECTOR))) {
+ if (cs->bClockID == clock_id)
+ return cs;
+ }
+
+ return NULL;
+}
+
static struct uac_clock_multiplier_descriptor *
snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
int clock_id)
@@ -85,6 +118,22 @@ static struct uac_clock_multiplier_descriptor *
return NULL;
}
+static struct uac3_clock_multiplier_descriptor *
+ snd_usb_find_clock_multiplier_v3(struct usb_host_interface *ctrl_iface,
+ int clock_id)
+{
+ struct uac3_clock_multiplier_descriptor *cs = NULL;
+
+ while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+ ctrl_iface->extralen,
+ cs, UAC3_CLOCK_MULTIPLIER))) {
+ if (cs->bClockID == clock_id)
+ return cs;
+ }
+
+ return NULL;
+}
+
static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
{
unsigned char buf;
@@ -138,19 +187,33 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
return ret;
}
-static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
+static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
+ int protocol,
+ int source_id)
{
int err;
unsigned char data;
struct usb_device *dev = chip->dev;
- struct uac_clock_source_descriptor *cs_desc =
- snd_usb_find_clock_source(chip->ctrl_intf, source_id);
-
- if (!cs_desc)
- return 0;
+ u32 bmControls;
+
+ if (protocol == UAC_VERSION_3) {
+ struct uac3_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+ bmControls = le32_to_cpu(cs_desc->bmControls);
+ } else { /* UAC_VERSION_1/2 */
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+ bmControls = cs_desc->bmControls;
+ }
/* If a clock source can't tell us whether it's valid, we assume it is */
- if (!uac2_control_is_readable(cs_desc->bmControls,
+ if (!uac_v2v3_control_is_readable(bmControls,
UAC2_CS_CONTROL_CLOCK_VALID - 1))
return 1;
@@ -170,9 +233,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
return !!data;
}
-static int __uac_clock_find_source(struct snd_usb_audio *chip,
- int entity_id, unsigned long *visited,
- bool validate)
+static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ unsigned long *visited, bool validate)
{
struct uac_clock_source_descriptor *source;
struct uac_clock_selector_descriptor *selector;
@@ -191,7 +253,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
if (source) {
entity_id = source->bClockID;
- if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
+ if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2,
+ entity_id)) {
usb_audio_err(chip,
"clock source %d is not valid, cannot use\n",
entity_id);
@@ -260,6 +323,97 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
return -EINVAL;
}
+static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ unsigned long *visited, bool validate)
+{
+ struct uac3_clock_source_descriptor *source;
+ struct uac3_clock_selector_descriptor *selector;
+ struct uac3_clock_multiplier_descriptor *multiplier;
+
+ entity_id &= 0xff;
+
+ if (test_and_set_bit(entity_id, visited)) {
+ usb_audio_warn(chip,
+ "%s(): recursive clock topology detected, id %d.\n",
+ __func__, entity_id);
+ return -EINVAL;
+ }
+
+ /* first, see if the ID we're looking for is a clock source already */
+ source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
+ if (source) {
+ entity_id = source->bClockID;
+ if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
+ entity_id)) {
+ usb_audio_err(chip,
+ "clock source %d is not valid, cannot use\n",
+ entity_id);
+ return -ENXIO;
+ }
+ return entity_id;
+ }
+
+ selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
+ if (selector) {
+ int ret, i, cur;
+
+ /* the entity ID we are looking for is a selector.
+ * find out what it currently selects */
+ ret = uac_clock_selector_get_val(chip, selector->bClockID);
+ if (ret < 0)
+ return ret;
+
+ /* Selector values are one-based */
+
+ if (ret > selector->bNrInPins || ret < 1) {
+ usb_audio_err(chip,
+ "%s(): selector reported illegal value, id %d, ret %d\n",
+ __func__, selector->bClockID, ret);
+
+ return -EINVAL;
+ }
+
+ cur = ret;
+ ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
+ visited, validate);
+ if (!validate || ret > 0 || !chip->autoclock)
+ return ret;
+
+ /* The current clock source is invalid, try others. */
+ for (i = 1; i <= selector->bNrInPins; i++) {
+ int err;
+
+ if (i == cur)
+ continue;
+
+ ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
+ visited, true);
+ if (ret < 0)
+ continue;
+
+ err = uac_clock_selector_set_val(chip, entity_id, i);
+ if (err < 0)
+ continue;
+
+ usb_audio_info(chip,
+ "found and selected valid clock source %d\n",
+ ret);
+ return ret;
+ }
+
+ return -ENXIO;
+ }
+
+ /* FIXME: multipliers only act as pass-thru element for now */
+ multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
+ entity_id);
+ if (multiplier)
+ return __uac3_clock_find_source(chip, multiplier->bCSourceID,
+ visited, validate);
+
+ return -EINVAL;
+}
+
/*
* For all kinds of sample rate settings and other device queries,
* the clock source (end-leaf) must be used. However, clock selectors,
@@ -271,12 +425,22 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
*
* Returns the clock source UnitID (>=0) on success, or an error.
*/
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
- bool validate)
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
+ int entity_id, bool validate)
{
DECLARE_BITMAP(visited, 256);
memset(visited, 0, sizeof(visited));
- return __uac_clock_find_source(chip, entity_id, visited, validate);
+
+ switch (protocol) {
+ case UAC_VERSION_2:
+ return __uac_clock_find_source(chip, entity_id, visited,
+ validate);
+ case UAC_VERSION_3:
+ return __uac3_clock_find_source(chip, entity_id, visited,
+ validate);
+ default:
+ return -EINVAL;
+ }
}
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
@@ -335,7 +499,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
return 0;
}
-static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
int altsetting, int clock)
{
struct usb_device *dev = chip->dev;
@@ -348,7 +512,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
snd_usb_ctrl_intf(chip) | (clock << 8),
&data, sizeof(data));
if (err < 0) {
- dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
+ dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
iface, altsetting, err);
return 0;
}
@@ -356,7 +520,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
return le32_to_cpu(data);
}
-static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
@@ -365,18 +529,30 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
int err, cur_rate, prev_rate;
int clock;
bool writeable;
- struct uac_clock_source_descriptor *cs_desc;
+ u32 bmControls;
- clock = snd_usb_clock_find_source(chip, fmt->clock, true);
+ clock = snd_usb_clock_find_source(chip, fmt->protocol,
+ fmt->clock, true);
if (clock < 0)
return clock;
- prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+ prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock);
if (prev_rate == rate)
return 0;
- cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
- writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
+ if (fmt->protocol == UAC_VERSION_3) {
+ struct uac3_clock_source_descriptor *cs_desc;
+
+ cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock);
+ bmControls = le32_to_cpu(cs_desc->bmControls);
+ } else {
+ struct uac_clock_source_descriptor *cs_desc;
+
+ cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
+ bmControls = cs_desc->bmControls;
+ }
+
+ writeable = uac_v2v3_control_is_writeable(bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
if (writeable) {
data = cpu_to_le32(rate);
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
@@ -386,12 +562,13 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
&data, sizeof(data));
if (err < 0) {
usb_audio_err(chip,
- "%d:%d: cannot set freq %d (v2): err %d\n",
+ "%d:%d: cannot set freq %d (v2/v3): err %d\n",
iface, fmt->altsetting, rate, err);
return err;
}
- cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+ cur_rate = get_sample_rate_v2v3(chip, iface,
+ fmt->altsetting, clock);
} else {
cur_rate = prev_rate;
}
@@ -430,7 +607,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
case UAC_VERSION_2:
- return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+ case UAC_VERSION_3:
+ return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
}
}
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
index 87557cae1a0b..076e31b79ee0 100644
--- a/sound/usb/clock.h
+++ b/sound/usb/clock.h
@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate);
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
- bool validate);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
+ int entity_id, bool validate);
#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 2c44386e5569..edbe67eeddfa 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -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>
@@ -39,11 +40,11 @@
* @dev: usb device
* @fp: audioformat record
* @format: the format tag (wFormatTag)
- * @fmt: the format type descriptor
+ * @fmt: the format type descriptor (v1/v2) or AudioStreaming descriptor (v3)
*/
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp,
- unsigned int format, void *_fmt)
+ u64 format, void *_fmt)
{
int sample_width, sample_bytes;
u64 pcm_formats = 0;
@@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
format <<= 1;
break;
}
+ case UAC_VERSION_3: {
+ struct uac3_as_header_descriptor *as = _fmt;
+
+ sample_width = as->bBitResolution;
+ sample_bytes = as->bSubslotSize;
+
+ if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
+ pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+
+ format <<= 1;
+ break;
+ }
}
if ((pcm_formats == 0) &&
@@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
}
if (format & ~0x3f) {
usb_audio_info(chip,
- "%u:%d : unsupported format bits %#x\n",
+ "%u:%d : unsupported format bits %#llx\n",
fp->iface, fp->altsetting, format);
}
@@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
/*
* parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v2).
+ * on the audioformat table (audio class v2 and v3).
*/
-static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
+static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
struct audioformat *fp)
{
struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0;
- int clock = snd_usb_clock_find_source(chip, fp->clock, false);
+ int clock = snd_usb_clock_find_source(chip, fp->protocol,
+ fp->clock, false);
if (clock < 0) {
dev_err(&dev->dev,
@@ -368,13 +382,30 @@ err:
* 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,
- struct uac_format_type_i_continuous_descriptor *fmt)
+ struct audioformat *fp, u64 format,
+ void *_fmt)
{
snd_pcm_format_t pcm_format;
+ unsigned int fmt_type;
int ret;
- if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
+ switch (fp->protocol) {
+ default:
+ case UAC_VERSION_1:
+ case UAC_VERSION_2: {
+ struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
+
+ fmt_type = fmt->bFormatType;
+ break;
+ }
+ case UAC_VERSION_3: {
+ /* fp->fmt_type is already set in this case */
+ fmt_type = fp->fmt_type;
+ break;
+ }
+ }
+
+ if (fmt_type == UAC_FORMAT_TYPE_III) {
/* FIXME: the format type is really IECxxx
* but we give normal PCM format to get the existing
* apps working...
@@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
}
fp->formats = pcm_format_to_bits(pcm_format);
} else {
- fp->formats = parse_audio_format_i_type(chip, fp, format, fmt);
+ fp->formats = parse_audio_format_i_type(chip, fp, format, _fmt);
if (!fp->formats)
return -EINVAL;
}
@@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/
switch (fp->protocol) {
default:
- case UAC_VERSION_1:
+ case UAC_VERSION_1: {
+ struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
+
fp->channels = fmt->bNrChannels;
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
break;
+ }
case UAC_VERSION_2:
+ case UAC_VERSION_3: {
/* fp->channels is already set in this case */
- ret = parse_audio_format_rates_v2(chip, fp);
+ ret = parse_audio_format_rates_v2v3(chip, fp);
break;
}
+ }
if (fp->channels < 1) {
usb_audio_err(chip,
@@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/
static int parse_audio_format_ii(struct snd_usb_audio *chip,
struct audioformat *fp,
- int format, void *_fmt)
+ u64 format, void *_fmt)
{
int brate, framesize, ret;
@@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
break;
default:
usb_audio_info(chip,
- "%u:%d : unknown format tag %#x is detected. processed as MPEG.\n",
+ "%u:%d : unknown format tag %#llx is detected. processed as MPEG.\n",
fp->iface, fp->altsetting, format);
fp->formats = SNDRV_PCM_FMTBIT_MPEG;
break;
@@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize;
- ret = parse_audio_format_rates_v2(chip, fp);
+ ret = parse_audio_format_rates_v2v3(chip, fp);
break;
}
}
@@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
}
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
- struct audioformat *fp, unsigned int format,
+ struct audioformat *fp, u64 format,
struct uac_format_type_i_continuous_descriptor *fmt,
int stream)
{
@@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
return 0;
}
+int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ struct uac3_as_header_descriptor *as,
+ int stream)
+{
+ u64 format = le64_to_cpu(as->bmFormats);
+ int err;
+
+ /*
+ * Type I format bits are D0..D6
+ * This test works because type IV is not supported
+ */
+ if (format & 0x7f)
+ fp->fmt_type = UAC_FORMAT_TYPE_I;
+ else
+ fp->fmt_type = UAC_FORMAT_TYPE_III;
+
+ err = parse_audio_format_i(chip, fp, format, as);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
diff --git a/sound/usb/format.h b/sound/usb/format.h
index 8c3ff9ce0824..e70171892f32 100644
--- a/sound/usb/format.h
+++ b/sound/usb/format.h
@@ -3,8 +3,12 @@
#define __USBAUDIO_FORMAT_H
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
- struct audioformat *fp, unsigned int format,
+ struct audioformat *fp, u64 format,
struct uac_format_type_i_continuous_descriptor *fmt,
int stream);
+int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ struct uac3_as_header_descriptor *as,
+ int stream);
#endif /* __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 06b22624ab7a..1c02f7373eda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -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>
@@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
- hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
+ hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER &&
hdr->bUnitID == unit)
return hdr;
}
@@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
validx += cval->idx_off;
+
if (cval->head.mixer->protocol == UAC_VERSION_1) {
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2/3 */
val_len = uac2_ctl_value_size(cval->val_type);
/* FIXME */
@@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
static int check_input_term(struct mixer_build *state, int id,
struct usb_audio_term *term)
{
+ int protocol = state->mixer->protocol;
int err;
void *p1;
@@ -730,16 +733,104 @@ static int check_input_term(struct mixer_build *state, int id,
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
unsigned char *hdr = p1;
term->id = id;
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL:
- if (state->mixer->protocol == UAC_VERSION_1) {
- struct uac_input_terminal_descriptor *d = p1;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le16_to_cpu(d->wChannelConfig);
- term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
- struct uac2_input_terminal_descriptor *d = p1;
+
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ switch (hdr[2]) {
+ case UAC_INPUT_TERMINAL:
+ if (protocol == UAC_VERSION_1) {
+ struct uac_input_terminal_descriptor *d = p1;
+
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le16_to_cpu(d->wChannelConfig);
+ term->name = d->iTerminal;
+ } else { /* UAC_VERSION_2 */
+ struct uac2_input_terminal_descriptor *d = p1;
+
+ /* call recursively to verify that the
+ * referenced clock entity is valid */
+ err = check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the
+ * recursion calls */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
+ }
+ return 0;
+ case UAC_FEATURE_UNIT: {
+ /* the header is the same for v1 and v2 */
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ break; /* continue to parse */
+ }
+ case UAC_MIXER_UNIT: {
+ struct uac_mixer_unit_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->channels = uac_mixer_unit_bNrChannels(d);
+ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+ term->name = uac_mixer_unit_iMixer(d);
+ 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);
+ return 0;
+ }
+ case UAC1_PROCESSING_UNIT:
+ case UAC1_EXTENSION_UNIT:
+ /* UAC2_PROCESSING_UNIT_V2 */
+ /* UAC2_EFFECT_UNIT */
+ case UAC2_EXTENSION_UNIT_V2: {
+ struct uac_processing_unit_descriptor *d = p1;
+
+ if (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 */
+ }
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->channels = uac_processing_unit_bNrChannels(d);
+ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+ term->name = uac_processing_unit_iProcessing(d, protocol);
+ return 0;
+ }
+ case UAC2_CLOCK_SOURCE: {
+ struct uac_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->id = id;
+ term->name = d->iClockSource;
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
+ } else { /* UAC_VERSION_3 */
+ switch (hdr[2]) {
+ case UAC_INPUT_TERMINAL: {
+ struct uac3_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
* referenced clock entity is valid */
@@ -752,71 +843,31 @@ static int check_input_term(struct mixer_build *state, int id,
* recursion calls */
term->id = id;
term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- }
- return 0;
- case UAC_FEATURE_UNIT: {
- /* the header is the same for v1 and v2 */
- struct uac_feature_unit_descriptor *d = p1;
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_mixer_unit_bNrChannels(d);
- term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_mixer_unit_iMixer(d);
- 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);
- return 0;
- }
- case UAC1_PROCESSING_UNIT:
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 */
- /* UAC2_EFFECT_UNIT */
- case UAC2_EXTENSION_UNIT_V2: {
- 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.
- */
+
+ /* REVISIT: UAC3 IT doesn't have channels/cfg */
+ term->channels = 0;
+ term->chconfig = 0;
+
+ term->name = le16_to_cpu(d->wTerminalDescrStr);
return 0;
}
+ case UAC3_FEATURE_UNIT: {
+ struct uac3_feature_unit_descriptor *d = p1;
- if (d->bNrInPins) {
- id = d->baSourceID[0];
+ id = d->bSourceID;
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;
- }
- case UAC2_CLOCK_SOURCE: {
- struct uac_clock_source_descriptor *d = p1;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = d->iClockSource;
- return 0;
- }
- default:
- return -ENODEV;
+ case UAC3_CLOCK_SOURCE: {
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->id = id;
+ term->name = le16_to_cpu(d->wClockSourceStr);
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
}
}
return -ENODEV;
@@ -1423,7 +1474,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
* The only property of this unit we are interested in is the
* clock source validity. If that isn't readable, just bail out.
*/
- if (!uac2_control_is_readable(hdr->bmControls,
+ if (!uac_v2v3_control_is_readable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
return 0;
@@ -1439,7 +1490,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
cval->val_type = USB_MIXER_BOOLEAN;
cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
- if (uac2_control_is_writeable(hdr->bmControls,
+ if (uac_v2v3_control_is_writeable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
else {
@@ -1502,7 +1553,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,
@@ -1519,6 +1570,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
+ } else { /* UAC_VERSION_3 */
+ struct uac3_feature_unit_descriptor *ftr = _ftr;
+
+ if (hdr->bLength < 7) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
+ csize = 4;
+ channels = (ftr->bLength - 7) / 4 - 1;
+ bmaControls = ftr->bmaControls;
+ if (hdr->bLength < 7 + csize) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
}
/* parse the source unit */
@@ -1577,7 +1646,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/3 */
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
@@ -1587,9 +1656,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
- if (uac2_control_is_readable(mask, i)) {
+ if (uac_v2v3_control_is_readable(mask, i)) {
ch_bits |= (1 << j);
- if (!uac2_control_is_writeable(mask, i))
+ if (!uac_v2v3_control_is_writeable(mask, i))
ch_read_only |= (1 << j);
}
}
@@ -1610,9 +1679,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, i,
&iterm, unitid, ch_read_only);
- if (uac2_control_is_readable(master_bits, i))
+ if (uac_v2v3_control_is_readable(master_bits, i))
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
- !uac2_control_is_writeable(master_bits, i));
+ !uac_v2v3_control_is_writeable(master_bits, i));
}
}
@@ -2220,6 +2289,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
static int parse_audio_unit(struct mixer_build *state, int unitid)
{
unsigned char *p1;
+ int protocol = state->mixer->protocol;
if (test_and_set_bit(unitid, state->unitbitmap))
return 0; /* the unit already visited */
@@ -2230,36 +2300,61 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}
- s