summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/usb/audio-v2.h4
-rw-r--r--include/linux/usb/audio-v3.h395
-rw-r--r--include/uapi/linux/usb/audio.h1
-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
11 files changed, 1246 insertions, 194 deletions
diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
index 3119d0ace7aa..2db83a191e78 100644
--- a/include/linux/usb/audio-v2.h
+++ b/include/linux/usb/audio-v2.h
@@ -34,12 +34,12 @@
*
*/
-static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
+static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control)
{
return (bmControls >> (control * 2)) & 0x1;
}
-static inline bool uac2_control_is_writeable(u32 bmControls, u8 control)
+static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control)
{
return (bmControls >> (control * 2)) & 0x2;
}
diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h
new file mode 100644
index 000000000000..a8959aaba0ae
--- /dev/null
+++ b/include/linux/usb/audio-v3.h
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
+ * This file holds USB constants and structures defined
+ * by the USB DEVICE CLASS DEFINITION FOR AUDIO DEVICES Release 3.0.
+ */
+
+#ifndef __LINUX_USB_AUDIO_V3_H
+#define __LINUX_USB_AUDIO_V3_H
+
+#include <linux/types.h>
+
+/*
+ * v1.0, v2.0 and v3.0 of this standard have many things in common. For the rest
+ * of the definitions, please refer to audio.h and audio-v2.h
+ */
+
+/* All High Capability descriptors have these 2 fields at the beginning */
+struct uac3_hc_descriptor_header {
+ __le16 wLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __le16 wDescriptorID;
+} __attribute__ ((packed));
+
+/* 4.3.1 CLUSTER DESCRIPTOR HEADER */
+struct uac3_cluster_header_descriptor {
+ __le16 wLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __le16 wDescriptorID;
+ __u8 bNrChannels;
+} __attribute__ ((packed));
+
+/* 4.3.2.1 SEGMENTS */
+struct uac3_cluster_segment_descriptor {
+ __le16 wLength;
+ __u8 bSegmentType;
+ /* __u8[0]; segment-specific data */
+} __attribute__ ((packed));
+
+/* 4.3.2.1.1 END SEGMENT */
+struct uac3_cluster_end_segment_descriptor {
+ __le16 wLength;
+ __u8 bSegmentType; /* Constant END_SEGMENT */
+} __attribute__ ((packed));
+
+/* 4.3.2.1.3.1 INFORMATION SEGMENT */
+struct uac3_cluster_information_segment_descriptor {
+ __le16 wLength;
+ __u8 bSegmentType;
+ __u8 bChPurpose;
+ __u8 bChRelationship;
+ __u8 bChGroupID;
+} __attribute__ ((packed));
+
+/* 4.5.2 CLASS-SPECIFIC AC INTERFACE DESCRIPTOR */
+struct uac3_ac_header_descriptor {
+ __u8 bLength; /* 10 */
+ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */
+ __u8 bDescriptorSubtype; /* HEADER descriptor subtype */
+ __u8 bCategory;
+
+ /* includes Clock Source, Unit, Terminal, and Power Domain desc. */
+ __le16 wTotalLength;
+
+ __le32 bmControls;
+} __attribute__ ((packed));
+
+/* 4.5.2.1 INPUT TERMINAL DESCRIPTOR */
+struct uac3_input_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __le16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bCSourceID;
+ __le32 bmControls;
+ __le16 wClusterDescrID;
+ __le16 wExTerminalDescrID;
+ __le16 wConnectorsDescrID;
+ __le16 wTerminalDescrStr;
+} __attribute__((packed));
+
+/* 4.5.2.2 OUTPUT TERMINAL DESCRIPTOR */
+struct uac3_output_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __le16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bSourceID;
+ __u8 bCSourceID;
+ __le32 bmControls;
+ __le16 wExTerminalDescrID;
+ __le16 wConnectorsDescrID;
+ __le16 wTerminalDescrStr;
+} __attribute__((packed));
+
+/* 4.5.2.7 FEATURE UNIT DESCRIPTOR */
+struct uac3_feature_unit_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bUnitID;
+ __u8 bSourceID;
+ /* bmaControls is actually u32,
+ * but u8 is needed for the hybrid parser */
+ __u8 bmaControls[0]; /* variable length */
+ /* wFeatureDescrStr omitted */
+} __attribute__((packed));
+
+#define UAC3_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 4)
+
+/* As above, but more useful for defining your own descriptors */
+#define DECLARE_UAC3_FEATURE_UNIT_DESCRIPTOR(ch) \
+struct uac3_feature_unit_descriptor_##ch { \
+ __u8 bLength; \
+ __u8 bDescriptorType; \
+ __u8 bDescriptorSubtype; \
+ __u8 bUnitID; \
+ __u8 bSourceID; \
+ __le32 bmaControls[ch + 1]; \
+ __le16 wFeatureDescrStr; \
+} __attribute__ ((packed))
+
+/* 4.5.2.12 CLOCK SOURCE DESCRIPTOR */
+struct uac3_clock_source_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bClockID;
+ __u8 bmAttributes;
+ __le32 bmControls;
+ __u8 bReferenceTerminal;
+ __le16 wClockSourceStr;
+} __attribute__((packed));
+
+/* bmAttribute fields */
+#define UAC3_CLOCK_SOURCE_TYPE_EXT 0x0
+#define UAC3_CLOCK_SOURCE_TYPE_INT 0x1
+#define UAC3_CLOCK_SOURCE_ASYNC (0 << 2)
+#define UAC3_CLOCK_SOURCE_SYNCED_TO_SOF (1 << 1)
+
+/* 4.5.2.13 CLOCK SELECTOR DESCRIPTOR */
+struct uac3_clock_selector_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bClockID;
+ __u8 bNrInPins;
+ __u8 baCSourceID[];
+ /* bmControls and wCSelectorDescrStr omitted */
+} __attribute__((packed));
+
+/* 4.5.2.14 CLOCK MULTIPLIER DESCRIPTOR */
+struct uac3_clock_multiplier_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bClockID;
+ __u8 bCSourceID;
+ __le32 bmControls;
+ __le16 wCMultiplierDescrStr;
+} __attribute__((packed));
+
+/* 4.5.2.15 POWER DOMAIN DESCRIPTOR */
+struct uac3_power_domain_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bPowerDomainID;
+ __le16 waRecoveryTime1;
+ __le16 waRecoveryTime2;
+ __u8 bNrEntities;
+ __u8 baEntityID[];
+ /* wPDomainDescrStr omitted */
+} __attribute__((packed));
+
+/* As above, but more useful for defining your own descriptors */
+#define DECLARE_UAC3_POWER_DOMAIN_DESCRIPTOR(n) \
+struct uac3_power_domain_descriptor_##n { \
+ __u8 bLength; \
+ __u8 bDescriptorType; \
+ __u8 bDescriptorSubtype; \
+ __u8 bPowerDomainID; \
+ __le16 waRecoveryTime1; \
+ __le16 waRecoveryTime2; \
+ __u8 bNrEntities; \
+ __u8 baEntityID[n]; \
+ __le16 wPDomainDescrStr; \
+} __attribute__ ((packed))
+
+/* 4.7.2 CLASS-SPECIFIC AS INTERFACE DESCRIPTOR */
+struct uac3_as_header_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalLink;
+ __le32 bmControls;
+ __le16 wClusterDescrID;
+ __le64 bmFormats;
+ __u8 bSubslotSize;
+ __u8 bBitResolution;
+ __le16 bmAuxProtocols;
+ __u8 bControlSize;
+} __attribute__((packed));
+
+#define UAC3_FORMAT_TYPE_I_RAW_DATA (1 << 6)
+
+/* 4.8.1.2 CLASS-SPECIFIC AS ISOCHRONOUS AUDIO DATA ENDPOINT DESCRIPTOR */
+struct uac3_iso_endpoint_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __le32 bmControls;
+ __u8 bLockDelayUnits;
+ __le16 wLockDelay;
+} __attribute__((packed));
+
+/* 6.1 INTERRUPT DATA MESSAGE */
+struct uac3_interrupt_data_msg {
+ __u8 bInfo;
+ __u8 bSourceType;
+ __le16 wValue;
+ __le16 wIndex;
+} __attribute__((packed));
+
+/* A.2 AUDIO AUDIO FUNCTION SUBCLASS CODES */
+#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00
+#define UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 0x01
+/* BADD profiles */
+#define UAC3_FUNCTION_SUBCLASS_GENERIC_IO 0x20
+#define UAC3_FUNCTION_SUBCLASS_HEADPHONE 0x21
+#define UAC3_FUNCTION_SUBCLASS_SPEAKER 0x22
+#define UAC3_FUNCTION_SUBCLASS_MICROPHONE 0x23
+#define UAC3_FUNCTION_SUBCLASS_HEADSET 0x24
+#define UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER 0x25
+#define UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE 0x26
+
+/* A.7 AUDIO FUNCTION CATEGORY CODES */
+#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00
+#define UAC3_FUNCTION_DESKTOP_SPEAKER 0x01
+#define UAC3_FUNCTION_HOME_THEATER 0x02
+#define UAC3_FUNCTION_MICROPHONE 0x03
+#define UAC3_FUNCTION_HEADSET 0x04
+#define UAC3_FUNCTION_TELEPHONE 0x05
+#define UAC3_FUNCTION_CONVERTER 0x06
+#define UAC3_FUNCTION_SOUND_RECORDER 0x07
+#define UAC3_FUNCTION_IO_BOX 0x08
+#define UAC3_FUNCTION_MUSICAL_INSTRUMENT 0x09
+#define UAC3_FUNCTION_PRO_AUDIO 0x0a
+#define UAC3_FUNCTION_AUDIO_VIDEO 0x0b
+#define UAC3_FUNCTION_CONTROL_PANEL 0x0c
+#define UAC3_FUNCTION_HEADPHONE 0x0d
+#define UAC3_FUNCTION_GENERIC_SPEAKER 0x0e
+#define UAC3_FUNCTION_HEADSET_ADAPTER 0x0f
+#define UAC3_FUNCTION_SPEAKERPHONE 0x10
+#define UAC3_FUNCTION_OTHER 0xff
+
+/* A.8 AUDIO CLASS-SPECIFIC DESCRIPTOR TYPES */
+#define UAC3_CS_UNDEFINED 0x20
+#define UAC3_CS_DEVICE 0x21
+#define UAC3_CS_CONFIGURATION 0x22
+#define UAC3_CS_STRING 0x23
+#define UAC3_CS_INTERFACE 0x24
+#define UAC3_CS_ENDPOINT 0x25
+#define UAC3_CS_CLUSTER 0x26
+
+/* A.10 CLUSTER DESCRIPTOR SEGMENT TYPES */
+#define UAC3_SEGMENT_UNDEFINED 0x00
+#define UAC3_CLUSTER_DESCRIPTION 0x01
+#define UAC3_CLUSTER_VENDOR_DEFINED 0x1F
+#define UAC3_CHANNEL_INFORMATION 0x20
+#define UAC3_CHANNEL_AMBISONIC 0x21
+#define UAC3_CHANNEL_DESCRIPTION 0x22
+#define UAC3_CHANNEL_VENDOR_DEFINED 0xFE
+#define UAC3_END_SEGMENT 0xFF
+
+/* A.11 CHANNEL PURPOSE DEFINITIONS */
+#define UAC3_PURPOSE_UNDEFINED 0x00
+#define UAC3_PURPOSE_GENERIC_AUDIO 0x01
+#define UAC3_PURPOSE_VOICE 0x02
+#define UAC3_PURPOSE_SPEECH 0x03
+#define UAC3_PURPOSE_AMBIENT 0x04
+#define UAC3_PURPOSE_REFERENCE 0x05
+#define UAC3_PURPOSE_ULTRASONIC 0x06
+#define UAC3_PURPOSE_VIBROKINETIC 0x07
+#define UAC3_PURPOSE_NON_AUDIO 0xFF
+
+/* A.12 CHANNEL RELATIONSHIP DEFINITIONS */
+#define UAC3_CH_RELATIONSHIP_UNDEFINED 0x00
+#define UAC3_CH_MONO 0x01
+#define UAC3_CH_LEFT 0x02
+#define UAC3_CH_RIGHT 0x03
+#define UAC3_CH_ARRAY 0x04
+#define UAC3_CH_PATTERN_X 0x20
+#define UAC3_CH_PATTERN_Y 0x21
+#define UAC3_CH_PATTERN_A 0x22
+#define UAC3_CH_PATTERN_B 0x23
+#define UAC3_CH_PATTERN_M 0x24
+#define UAC3_CH_PATTERN_S 0x25
+#define UAC3_CH_FRONT_LEFT 0x80
+#define UAC3_CH_FRONT_RIGHT 0x81
+#define UAC3_CH_FRONT_CENTER 0x82
+#define UAC3_CH_FRONT_LEFT_OF_CENTER 0x83
+#define UAC3_CH_FRONT_RIGHT_OF_CENTER 0x84
+#define UAC3_CH_FRONT_WIDE_LEFT 0x85
+#define UAC3_CH_FRONT_WIDE_RIGHT 0x86
+#define UAC3_CH_SIDE_LEFT 0x87
+#define UAC3_CH_SIDE_RIGHT 0x88
+#define UAC3_CH_SURROUND_ARRAY_LEFT 0x89
+#define UAC3_CH_SURROUND_ARRAY_RIGHT 0x8A
+#define UAC3_CH_BACK_LEFT 0x8B
+#define UAC3_CH_BACK_RIGHT 0x8C
+#define UAC3_CH_BACK_CENTER 0x8D
+#define UAC3_CH_BACK_LEFT_OF_CENTER 0x8E
+#define UAC3_CH_BACK_RIGHT_OF_CENTER 0x8F
+#define UAC3_CH_BACK_WIDE_LEFT 0x90
+#define UAC3_CH_BACK_WIDE_RIGHT 0x91
+#define UAC3_CH_TOP_CENTER 0x92
+#define UAC3_CH_TOP_FRONT_LEFT 0x93
+#define UAC3_CH_TOP_FRONT_RIGHT 0x94
+#define UAC3_CH_TOP_FRONT_CENTER 0x95
+#define UAC3_CH_TOP_FRONT_LOC 0x96
+#define UAC3_CH_TOP_FRONT_ROC 0x97
+#define UAC3_CH_TOP_FRONT_WIDE_LEFT 0x98
+#define UAC3_CH_TOP_FRONT_WIDE_RIGHT 0x99
+#define UAC3_CH_TOP_SIDE_LEFT 0x9A
+#define UAC3_CH_TOP_SIDE_RIGHT 0x9B
+#define UAC3_CH_TOP_SURR_ARRAY_LEFT 0x9C
+#define UAC3_CH_TOP_SURR_ARRAY_RIGHT 0x9D
+#define UAC3_CH_TOP_BACK_LEFT 0x9E
+#define UAC3_CH_TOP_BACK_RIGHT 0x9F
+#define UAC3_CH_TOP_BACK_CENTER 0xA0
+#define UAC3_CH_TOP_BACK_LOC 0xA1
+#define UAC3_CH_TOP_BACK_ROC 0xA2
+#define UAC3_CH_TOP_BACK_WIDE_LEFT 0xA3
+#define UAC3_CH_TOP_BACK_WIDE_RIGHT 0xA4
+#define UAC3_CH_BOTTOM_CENTER 0xA5
+#define UAC3_CH_BOTTOM_FRONT_LEFT 0xA6
+#define UAC3_CH_BOTTOM_FRONT_RIGHT 0xA7
+#define UAC3_CH_BOTTOM_FRONT_CENTER 0xA8
+#define UAC3_CH_BOTTOM_FRONT_LOC 0xA9
+#define UAC3_CH_BOTTOM_FRONT_ROC 0xAA
+#define UAC3_CH_BOTTOM_FRONT_WIDE_LEFT 0xAB
+#define UAC3_CH_BOTTOM_FRONT_WIDE_RIGHT 0xAC
+#define UAC3_CH_BOTTOM_SIDE_LEFT 0xAD
+#define UAC3_CH_BOTTOM_SIDE_RIGHT 0xAE
+#define UAC3_CH_BOTTOM_SURR_ARRAY_LEFT 0xAF
+#define UAC3_CH_BOTTOM_SURR_ARRAY_RIGHT 0xB0
+#define UAC3_CH_BOTTOM_BACK_LEFT 0xB1
+#define UAC3_CH_BOTTOM_BACK_RIGHT 0xB2
+#define UAC3_CH_BOTTOM_BACK_CENTER 0xB3
+#define UAC3_CH_BOTTOM_BACK_LOC 0xB4
+#define UAC3_CH_BOTTOM_BACK_ROC 0xB5
+#define UAC3_CH_BOTTOM_BACK_WIDE_LEFT 0xB6
+#define UAC3_CH_BOTTOM_BACK_WIDE_RIGHT 0xB7
+#define UAC3_CH_LOW_FREQUENCY_EFFECTS 0xB8
+#define UAC3_CH_LFE_LEFT 0xB9
+#define UAC3_CH_LFE_RIGHT 0xBA
+#define UAC3_CH_HEADPHONE_LEFT 0xBB
+#define UAC3_CH_HEADPHONE_RIGHT 0xBC
+
+/* A.15 AUDIO CLASS-SPECIFIC AC INTERFACE DESCRIPTOR SUBTYPES */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC3_EXTENDED_TERMINAL 0x04
+#define UAC3_MIXER_UNIT 0x05
+#define UAC3_SELECTOR_UNIT 0x06
+#define UAC3_FEATURE_UNIT 0x07
+#define UAC3_EFFECT_UNIT 0x08
+#define UAC3_PROCESSING_UNIT 0x09
+#define UAC3_EXTENSION_UNIT 0x0a
+#define UAC3_CLOCK_SOURCE 0x0b
+#define UAC3_CLOCK_SELECTOR 0x0c
+#define UAC3_CLOCK_MULTIPLIER 0x0d
+#define UAC3_SAMPLE_RATE_CONVERTER 0x0e
+#define UAC3_CONNECTORS 0x0f
+#define UAC3_POWER_DOMAIN 0x10
+
+/* A.22 AUDIO CLASS-SPECIFIC REQUEST CODES */
+/* see audio-v2.h for the rest, which is identical to v2 */
+#define UAC3_CS_REQ_INTEN 0x04
+#define UAC3_CS_REQ_STRING 0x05
+#define UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR 0x06
+
+/* A.23.1 AUDIOCONTROL INTERFACE CONTROL SELECTORS */
+#define UAC3_AC_CONTROL_UNDEFINED 0x00
+#define UAC3_AC_ACTIVE_INTERFACE_CONTROL 0x01
+#define UAC3_AC_POWER_DOMAIN_CONTROL 0x02
+
+#endif /* __LINUX_USB_AUDIO_V3_H */
diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h
index da3315ed1bcd..3a78e7145689 100644
--- a/include/uapi/linux/usb/audio.h
+++ b/include/uapi/linux/usb/audio.h
@@ -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
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;
}
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return 0; /* NOP */
- case UAC_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC2_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR:
- 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 */
- if (state->mixer->protocol == UAC_VERSION_1)
- return parse_audio_processing_unit(state, unitid, p1);
- else
- return 0; /* FIXME - effect units not implemented yet */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 has the same value */
- if (state->mixer->protocol == UAC_VERSION_1)
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ switch (p1[2]) {
+ case UAC_INPUT_TERMINAL:
+ return 0; /* NOP */
+ case UAC_MIXER_UNIT:
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC2_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
+ case UAC_SELECTOR_UNIT:
+ case UAC2_CLOCK_SELECTOR:
+ 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 */
+ if (protocol == UAC_VERSION_1)
+ return parse_audio_processing_unit(state, unitid, p1);
+ else
+ return 0; /* FIXME - effect units not implemented yet */
+ case UAC1_EXTENSION_UNIT:
+ /* UAC2_PROCESSING_UNIT_V2 has the same value */
+ if (protocol == UAC_VERSION_1)
+ return parse_audio_extension_unit(state, unitid, p1);
+ else /* UAC_VERSION_2 */
+ return parse_audio_processing_unit(state, unitid, p1);
+ case UAC2_EXTENSION_UNIT_V2:
return parse_audio_extension_unit(state, unitid, p1);
- else /* UAC_VERSION_2 */
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+ return -EINVAL;
+ }
+ } else { /* UAC_VERSION_3 */
+ switch (p1[2]) {
+ case UAC_INPUT_TERMINAL:
+ return 0; /* NOP */
+ case UAC3_MIXER_UNIT:
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC3_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
+ case UAC3_CLOCK_SELECTOR:
+ return parse_audio_selector_unit(state, unitid, p1);
+ case UAC3_FEATURE_UNIT:
+ return parse_audio_feature_unit(state, unitid, p1);
+ case UAC3_EFFECT_UNIT:
+ return 0; /* FIXME - effect units not implemented yet */
+ case UAC3_PROCESSING_UNIT:
return parse_audio_processing_unit(state, unitid, p1);
- case UAC2_EXTENSION_UNIT_V2:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
+ case UAC3_EXTENSION_UNIT:
+ return parse_audio_extension_unit(state, unitid, p1);
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+ return -EINVAL;
+ }
}
}
@@ -2330,7 +2425,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
err = parse_audio_unit(&state, desc->bSourceID);
if (err < 0 && err != -EINVAL)
return err;
- } else { /* UAC_VERSION_2 */
+ } else if (mixer->protocol == UAC_VERSION_2) {
struct uac2_output_terminal_descriptor *desc = p;
if (desc->bLength < sizeof(*desc))
@@ -2351,6 +2446,27 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
err = parse_audio_unit(&state, desc->bCSourceID);
if (err < 0 && err != -EINVAL)
return err;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_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.name = le16_to_cpu(desc->wTerminalDescrStr);
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+
+ /*
+ * For UAC3, use the same approach to also add the
+ * clock selectors
+ */
+ err = parse_audio_unit(&state, desc->bCSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
}
}
@@ -2597,6 +2713,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 ||
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index dbbe854745ab..6a8f5843334e 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.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>
@@ -311,6 +312,153 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
return chmap;
}
+/* UAC3 device stores channels information in Cluster Descriptors */
+static struct
+snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
+ *cluster)
+{
+ unsigned int channels = cluster->bNrChannels;
+ struct snd_pcm_chmap_elem *chmap;
+ void *p = cluster;
+ int len, c;
+
+ if (channels > ARRAY_SIZE(chmap->map))
+ return NULL;
+
+ chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
+ if (!chmap)
+ return NULL;
+
+ len = le16_to_cpu(cluster->wLength);
+ c = 0;
+ p += sizeof(struct uac3_cluster_header_descriptor);
+
+ while (((p - (void *)cluster) < len) && (c < channels)) {
+ struct uac3_cluster_segment_descriptor *cs_desc = p;
+ u16 cs_len;
+ u8 cs_type;
+
+ cs_len = le16_to_cpu(cs_desc->wLength);
+ cs_type = cs_desc->bSegmentType;
+
+ if (cs_type == UAC3_CHANNEL_INFORMATION) {
+ struct uac3_cluster_information_segment_descriptor *is = p;
+ unsigned char map;
+
+ /*
+ * TODO: this conversion is not complete, update it
+ * after adding UAC3 values to asound.h
+ */
+ switch (is->bChPurpose) {
+ case UAC3_CH_MONO:
+ map = SNDRV_CHMAP_MONO;
+ break;
+ case UAC3_CH_LEFT:
+ case UAC3_CH_FRONT_LEFT:
+ case UAC3_CH_HEADPHONE_LEFT:
+ map = SNDRV_CHMAP_FL;
+ break;
+ case UAC3_CH_RIGHT:
+ case UAC3_CH_FRONT_RIGHT:
+ case UAC3_CH_HEADPHONE_RIGHT:
+ map = SNDRV_CHMAP_FR;
+ break;
+ case UAC3_CH_FRONT_CENTER:
+ map = SNDRV_CHMAP_FC;
+ break;
+ case UAC3_CH_FRONT_LEFT_OF_CENTER:
+ map = SNDRV_CHMAP_FLC;
+ break;
+ case UAC3_CH_FRONT_RIGHT_OF_CENTER:
+ map = SNDRV_CHMAP_FRC;
+ break;
+ case UAC3_CH_SIDE_LEFT:
+ map = SNDRV_CHMAP_SL;
+ break;
+ case UAC3_CH_SIDE_RIGHT:
+ map = SNDRV_CHMAP_SR;
+ break;
+ case UAC3_CH_BACK_LEFT:
+ map = SNDRV_CHMAP_RL;
+ break;
+ case UAC3_CH_BACK_RIGHT:
+ map = SNDRV_CHMAP_RR;
+ break;
+ case UAC3_CH_BACK_CENTER:
+ map = SNDRV_CHMAP_RC;
+ break;
+ case UAC3_CH_BACK_LEFT_OF_CENTER:
+ map = SNDRV_CHMAP_RLC;
+ break;
+ case UAC3_CH_BACK_RIGHT_OF_CENTER:
+ map = SNDRV_CHMAP_RRC;
+ break;
+ case UAC3_CH_TOP_CENTER:
+ map = SNDRV_CHMAP_TC;
+ break;
+ case UAC3_CH_TOP_FRONT_LEFT:
+ map = SNDRV_CHMAP_TFL;
+ break;
+ case UAC3_CH_TOP_FRONT_RIGHT:
+ map = SNDRV_CHMAP_TFR;
+ break;
+ case UAC3_CH_TOP_FRONT_CENTER:
+ map = SNDRV_CHMAP_TFC;
+ break;
+ case UAC3_CH_TOP_FRONT_LOC:
+ map = SNDRV_CHMAP_TFLC;
+ break;
+ case UAC3_CH_TOP_FRONT_ROC:
+ map = SNDRV_CHMAP_TFRC;
+ break;
+ case UAC3_CH_TOP_SIDE_LEFT:
+ map = SNDRV_CHMAP_TSL;
+ break;
+ case UAC3_CH_TOP_SIDE_RIGHT:
+ map = SNDRV_CHMAP_TSR;
+ break;
+ case UAC3_CH_TOP_BACK_LEFT:
+ map = SNDRV_CHMAP_TRL;
+ break;
+ case UAC3_CH_TOP_BACK_RIGHT:
+ map = SNDRV_CHMAP_TRR;
+ break;
+ case UAC3_CH_TOP_BACK_CENTER:
+ map = SNDRV_CHMAP_TRC;
+ break;
+ case UAC3_CH_BOTTOM_CENTER:
+ map = SNDRV_CHMAP_BC;
+ break;
+ case UAC3_CH_LOW_FREQUENCY_EFFECTS:
+ map = SNDRV_CHMAP_LFE;
+ break;
+ case UAC3_CH_LFE_LEFT:
+ map = SNDRV_CHMAP_LLFE;
+ break;
+ case UAC3_CH_LFE_RIGHT:
+ map = SNDRV_CHMAP_RLFE;
+ break;
+ case UAC3_CH_RELATIONSHIP_UNDEFINED:
+ default:
+ map = SNDRV_CHMAP_UNKNOWN;
+ break;
+ }
+ chmap->map[c++] = map;
+ }
+ p += cs_len;
+ }
+
+ if (channels < c)
+ pr_err("%s: channel number mismatch\n", __func__);
+
+ chmap->channels = channels;
+
+ for (; c < channels; c++)
+ chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
+
+ return chmap;
+}
+
/*
* add this endpoint to the chip instance.
* if a stream with the same endpoint already exists, append to it.
@@ -461,10 +609,11 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
return NULL;
}
-static struct uac2_output_terminal_descriptor *
- snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+static void *
+snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+ int terminal_id)
{
+ /* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
@@ -484,10 +633,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
int i, altno, err, stream;
- unsigned int format = 0, num_channels = 0;
+ u64 format = 0;
+ unsigned int num_channels = 0;
struct audioformat *fp = NULL;
int num, protocol, clock = 0;
- struct uac_format_type_i_continuous_descriptor *fmt;
+ struct uac_format_type_i_continuous_descriptor *fmt = NULL;
+ struct snd_pcm_chmap_elem *chmap_v3 = NULL;
unsigned int chconfig;
dev = chip->dev;
@@ -624,38 +775,158 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
iface_no, altno, as->bTerminalLink);
continue;
}
- }
- /* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
- if (!fmt) {
+ case UAC_VERSION_3: {
+ struct uac3_input_terminal_descriptor *input_term;
+ struct uac3_output_terminal_descriptor *output_term;
+ struct uac3_as_header_descriptor *as;
+ struct uac3_cluster_header_descriptor *cluster;
+ struct uac3_hc_descriptor_header hc_header;
+ u16 cluster_id, wLength;
+
+ as = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen,
+ NULL, UAC_AS_GENERAL);
+
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ continue;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ continue;
+ }
+
+ cluster_id = le16_to_cpu(as->wClusterDescrID);
+ if (!cluster_id) {
+ dev_err(&dev->dev,
+ "%u:%d : no cluster descriptor\n",
+ iface_no, altno);
+ continue;
+ }
+
+ /*
+ * Get number of channels and channel map through
+ * High Capability Cluster Descriptor
+ *
+ * First step: get High Capability header and
+ * read size of Cluster Descriptor
+ */
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0),
+ UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ cluster_id,
+ snd_usb_ctrl_intf(chip),
+ &hc_header, sizeof(hc_header));
+ if (err < 0)
+ return err;
+ else if (err != sizeof(hc_header)) {
+ dev_err(&dev->dev,
+ "%u:%d : can't get High Capability descriptor\n",
+ iface_no, altno);
+ return -EIO;
+ }
+
+ /*
+ * Second step: allocate needed amount of memory
+ * and request Cluster Descriptor
+ */
+ wLength = le16_to_cpu(hc_header.wLength);
+ cluster = kzalloc(wLength, GFP_KERNEL);
+ if (!cluster)
+ return -ENOMEM;
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0),
+ UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ cluster_id,
+ snd_usb_ctrl_intf(chip),
+ cluster, wLength);
+ if (err < 0) {
+ kfree(cluster);
+ return err;
+ } else if (err != wLength) {
+ dev_err(&dev->dev,
+ "%u:%d : can't get Cluster Descriptor\n",
+ iface_no, altno);
+ kfree(cluster);
+ return -EIO;
+ }
+
+ num_channels = cluster->bNrChannels;
+ chmap_v3 = convert_chmap_v3(cluster);
+
+ kfree(cluster);
+
+ format = le64_to_cpu(as->bmFormats);
+
+ /* lookup the terminal associated to this interface
+ * to extract the clock */
+ input_term = snd_usb_find_input_terminal_descriptor(
+ chip->ctrl_intf,
+ as->bTerminalLink);
+
+ if (input_term) {
+ clock = input_term->bCSourceID;
+ break;
+ }
+
+ output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (output_term) {
+ clock = output_term->bCSourceID;
+ break;
+ }
+
dev_err(&dev->dev,
- "%u:%d : no UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
+ "%u:%d : bogus bTerminalLink %d\n",
+ iface_no, altno, as->bTerminalLink);
continue;
}
- if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
- ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
- continue;
}
- /*
- * Blue Microphones workaround: The last altsetting is identical
- * with the previous one, except for a larger packet size, but
- * is actually a mislabeled two-channel setting; ignore it.
- */
- if (fmt->bNrChannels == 1 &&
- fmt->bSubframeSize == 2 &&
- altno == 2 && num == 3 &&
- fp && fp->altsetting == 1 && fp->channels == 1 &&
- fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
- protocol == UAC_VERSION_1 &&
- le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ /* get format type */
+ fmt = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen,
+ NULL, UAC_FORMAT_TYPE);
+ if (!fmt) {
+ dev_err(&dev->dev,
+ "%u:%d : no UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
+ if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
+ || ((protocol == UAC_VERSION_2) &&
+ (fmt->bLength < 6))) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
+
+ /*
+ * Blue Microphones workaround: The last altsetting is
+ * identical with the previous one, except for a larger
+ * packet size, but is actually a mislabeled two-channel
+ * setting; ignore it.
+ */
+ if (fmt->bNrChannels == 1 &&
+ fmt->bSubframeSize == 2 &&
+ altno == 2 && num == 3 &&
+ fp && fp->altsetting == 1 && fp->channels == 1 &&
+ fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+ protocol == UAC_VERSION_1 &&
+ le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
- continue;
+ continue;
+ }
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (!fp)
@@ -681,17 +952,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
snd_usb_audioformat_attributes_quirk(chip, fp, stream);
/* ok, let's parse further... */
- if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
- fp = NULL;
- continue;
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ if (snd_usb_parse_audio_format(chip, fp, format,
+ fmt, stream) < 0) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ fp = NULL;
+ continue;
+ }
+ } else {
+ struct uac3_as_header_descriptor *as;
+
+ as = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen,
+ NULL, UAC_AS_GENERAL);
+
+ if (snd_usb_parse_audio_format_v3(chip, fp, as,
+ stream) < 0) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ fp = NULL;
+ continue;
+ }
}
/* Create chmap */
if (fp->channels != num_channels)
chconfig = 0;
- fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
+
+ if (protocol == UAC_VERSION_3)
+ fp->chmap = chmap_v3;
+ else
+ fp->chmap = convert_chmap(fp->channels, chconfig,
+ protocol);
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
OpenPOWER on IntegriCloud