diff options
-rw-r--r-- | include/linux/usb/audio-v2.h | 4 | ||||
-rw-r--r-- | include/linux/usb/audio-v3.h | 395 | ||||
-rw-r--r-- | include/uapi/linux/usb/audio.h | 1 | ||||
-rw-r--r-- | sound/usb/card.c | 7 | ||||
-rw-r--r-- | sound/usb/card.h | 2 | ||||
-rw-r--r-- | sound/usb/clock.c | 228 | ||||
-rw-r--r-- | sound/usb/clock.h | 4 | ||||
-rw-r--r-- | sound/usb/format.c | 91 | ||||
-rw-r--r-- | sound/usb/format.h | 6 | ||||
-rw-r--r-- | sound/usb/mixer.c | 337 | ||||
-rw-r--r-- | sound/usb/stream.c | 365 |
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 = |