All of lore.kernel.org
 help / color / mirror / Atom feed
* ALSA: usb-mixer: Add support for UAC2 devices
@ 2010-03-11 20:13 Daniel Mack
  2010-03-11 20:13 ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
  2010-03-12 11:46 ` ALSA: usb-mixer: Add support for UAC2 devices Takashi Iwai
  0 siblings, 2 replies; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

This patch series adds support for mixer interfaces in USB audio class
v2 devices. Successfully tested on a XMOS L1 eval kit. They apply on
top of the topic/usb branch.

There's a number significant differences between v1 and v2
implementations, which the patches itself may explain best :)

I moved most quirks out into a seperate file, and hope you agree that
it's cleaner that way. Most descriptors are now parsed with structs
which makes the code more readable. However, there are quite some
descriptors with variable field lengths so they can't be mapped into
structs. I introduces some macros to access these fields.

Again, please test this with v1 devices, as I can't be entirely
confident that I didn't break anything in the transition.

Thanks,
Daniel


[PATCH 1/6] linux/usb/audio.h: split header
[PATCH 2/6] ALSA: usb-mixer: use defines from audio.h
[PATCH 3/6] ALSA: usb-mixer: factor out quirks
[PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch]
[PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs
[PATCH 6/6] ALSA: usb-mixer: Add support for Audio Class v2.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] linux/usb/audio.h: split header
  2010-03-11 20:13 ALSA: usb-mixer: Add support for UAC2 devices Daniel Mack
@ 2010-03-11 20:13 ` Daniel Mack
  2010-03-11 20:13   ` [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h Daniel Mack
  2010-03-11 20:17   ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
  2010-03-12 11:46 ` ALSA: usb-mixer: Add support for UAC2 devices Takashi Iwai
  1 sibling, 2 replies; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

- Split the audio.h file in two to clearly denote the differences
  between the standards.
- Add many more defines to audio-v2.h. Most of them are not currently
  used.
- Replaced a magic value with a proper define

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 include/linux/usb/audio-v2.h |  319 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/audio.h    |   50 +------
 sound/usb/card.c             |    3 +-
 sound/usb/endpoint.c         |    1 +
 sound/usb/format.c           |    1 +
 sound/usb/pcm.c              |    5 +-
 6 files changed, 333 insertions(+), 46 deletions(-)
 create mode 100644 include/linux/usb/audio-v2.h

diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
new file mode 100644
index 0000000..3b8560d
--- /dev/null
+++ b/include/linux/usb/audio-v2.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices in version 2.0.
+ * Comments below reference relevant sections of the documents contained
+ * in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
+ */
+
+#ifndef __LINUX_USB_AUDIO_V2_H
+#define __LINUX_USB_AUDIO_V2_H
+
+#include <linux/types.h>
+
+/* v1.0 and v2.0 of this standard have many things in common. For the rest
+ * of the definitions, please refer to audio.h */
+
+/* 4.7.2.1 Clock Source Descriptor */
+
+struct uac_clock_source_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bmAttributes;
+	__u8 bmControls;
+	__u8 bAssocTerminal;
+	__u8 iClockSource;
+} __attribute__((packed));
+
+/* 4.7.2.2 Clock Source Descriptor */
+
+struct uac_clock_selector_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bNrInPins;
+	__u8 bmControls;
+	__u8 baCSourceID[];
+} __attribute__((packed));
+
+/* 4.9.2 Class-Specific AS Interface Descriptor */
+
+struct uac_as_header_descriptor_v2 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalLink;
+	__u8 bmControls;
+	__u8 bFormatType;
+	__u32 bmFormats;
+	__u8 bNrChannels;
+	__u32 bmChannelConfig;
+	__u8 iChannelNames;
+} __attribute__((packed));
+
+
+/* A.7 Audio Function Category Codes */
+#define UAC2_FUNCTION_SUBCLASS_UNDEFINED	0x00
+#define UAC2_FUNCTION_DESKTOP_SPEAKER		0x01
+#define UAC2_FUNCTION_HOME_THEATER		0x02
+#define UAC2_FUNCTION_MICROPHONE		0x03
+#define UAC2_FUNCTION_HEADSET			0x04
+#define UAC2_FUNCTION_TELEPHONE			0x05
+#define UAC2_FUNCTION_CONVERTER			0x06
+#define UAC2_FUNCTION_SOUND_RECORDER		0x07
+#define UAC2_FUNCTION_IO_BOX			0x08
+#define UAC2_FUNCTION_MUSICAL_INSTRUMENT	0x09
+#define UAC2_FUNCTION_PRO_AUDIO			0x0a
+#define UAC2_FUNCTION_AUDIO_VIDEO		0x0b
+#define UAC2_FUNCTION_CONTROL_PANEL		0x0c
+#define UAC2_FUNCTION_OTHER			0xff
+
+/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_EFFECT_UNIT			0x07
+#define UAC2_PROCESSING_UNIT_V2		0x08
+#define UAC2_EXTENSION_UNIT_V2		0x09
+#define UAC2_CLOCK_SOURCE		0x0a
+#define UAC2_CLOCK_SELECTOR		0x0b
+#define UAC2_CLOCK_MULTIPLIER		0x0c
+#define UAC2_SAMPLE_RATE_CONVERTER	0x0d
+
+/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_ENCODER			0x03
+#define UAC2_DECODER			0x04
+
+/* A.11 Effect Unit Effect Types */
+#define UAC2_EFFECT_UNDEFINED		0x00
+#define UAC2_EFFECT_PARAM_EQ		0x01
+#define UAC2_EFFECT_REVERB		0x02
+#define UAC2_EFFECT_MOD_DELAY		0x03
+#define UAC2_EFFECT_DYN_RANGE_COMP	0x04
+
+/* A.12 Processing Unit Process Types */
+#define UAC2_PROCESS_UNDEFINED		0x00
+#define UAC2_PROCESS_UP_DOWNMIX		0x01
+#define UAC2_PROCESS_DOLBY_PROLOCIC	0x02
+#define UAC2_PROCESS_STEREO_EXTENDER	0x03
+
+/* A.14 Audio Class-Specific Request Codes */
+#define UAC2_CS_CUR			0x01
+#define UAC2_CS_RANGE			0x02
+
+/* A.15 Encoder Type Codes */
+#define UAC2_ENCODER_UNDEFINED		0x00
+#define UAC2_ENCODER_OTHER		0x01
+#define UAC2_ENCODER_MPEG		0x02
+#define UAC2_ENCODER_AC3		0x03
+#define UAC2_ENCODER_WMA		0x04
+#define UAC2_ENCODER_DTS		0x05
+
+/* A.16 Decoder Type Codes */
+#define UAC2_DECODER_UNDEFINED		0x00
+#define UAC2_DECODER_OTHER		0x01
+#define UAC2_DECODER_MPEG		0x02
+#define UAC2_DECODER_AC3		0x03
+#define UAC2_DECODER_WMA		0x04
+#define UAC2_DECODER_DTS		0x05
+
+/* A.17.1 Clock Source Control Selectors */
+#define UAC2_CS_UNDEFINED		0x00
+#define UAC2_CS_CONTROL_SAM_FREQ	0x01
+#define UAC2_CS_CONTROL_CLOCK_VALID	0x02
+
+/* A.17.2 Clock Selector Control Selectors */
+#define UAC2_CX_UNDEFINED		0x00
+#define UAC2_CX_CLOCK_SELECTOR		0x01
+
+/* A.17.3 Clock Multiplier Control Selectors */
+#define UAC2_CM_UNDEFINED		0x00
+#define UAC2_CM_NUMERATOR		0x01
+#define UAC2_CM_DENOMINTATOR		0x02
+
+/* A.17.4 Terminal Control Selectors */
+#define UAC2_TE_UNDEFINED		0x00
+#define UAC2_TE_COPY_PROTECT		0x01
+#define UAC2_TE_CONNECTOR		0x02
+#define UAC2_TE_OVERLOAD		0x03
+#define UAC2_TE_CLUSTER			0x04
+#define UAC2_TE_UNDERFLOW		0x05
+#define UAC2_TE_OVERFLOW		0x06
+#define UAC2_TE_LATENCY			0x07
+
+/* A.17.5 Mixer Control Selectors */
+#define UAC2_MU_UNDEFINED		0x00
+#define UAC2_MU_MIXER			0x01
+#define UAC2_MU_CLUSTER			0x02
+#define UAC2_MU_UNDERFLOW		0x03
+#define UAC2_MU_OVERFLOW		0x04
+#define UAC2_MU_LATENCY			0x05
+
+/* A.17.6 Selector Control Selectors */
+#define UAC2_SU_UNDEFINED		0x00
+#define UAC2_SU_SELECTOR		0x01
+#define UAC2_SU_LATENCY			0x02
+
+/* A.17.7 Feature Unit Control Selectors */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_FU_INPUT_GAIN		0x0b
+#define UAC2_FU_INPUT_GAIN_PAD		0x0c
+#define UAC2_FU_PHASE_INVERTER		0x0d
+#define UAC2_FU_UNDERFLOW		0x0e
+#define UAC2_FU_OVERFLOW		0x0f
+#define UAC2_FU_LATENCY			0x10
+
+/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
+#define UAC2_PE_UNDEFINED		0x00
+#define UAC2_PE_ENABLE			0x01
+#define UAC2_PE_CENTERFREQ		0x02
+#define UAC2_PE_QFACTOR			0x03
+#define UAC2_PE_GAIN			0x04
+#define UAC2_PE_UNDERFLOW		0x05
+#define UAC2_PE_OVERFLOW		0x06
+#define UAC2_PE_LATENCY			0x07
+
+/* A.17.8.2 Reverberation Effect Unit Control Selectors */
+#define UAC2_RV_UNDEFINED		0x00
+#define UAC2_RV_ENABLE			0x01
+#define UAC2_RV_TYPE			0x02
+#define UAC2_RV_LEVEL			0x03
+#define UAC2_RV_TIME			0x04
+#define UAC2_RV_FEEDBACK		0x05
+#define UAC2_RV_PREDELAY		0x06
+#define UAC2_RV_DENSITY			0x07
+#define UAC2_RV_HIFREQ_ROLLOFF		0x08
+#define UAC2_RV_UNDERFLOW		0x09
+#define UAC2_RV_OVERFLOW		0x0a
+#define UAC2_RV_LATENCY			0x0b
+
+/* A.17.8.3 Modulation Delay Effect Control Selectors */
+#define UAC2_MD_UNDEFINED		0x00
+#define UAC2_MD_ENABLE			0x01
+#define UAC2_MD_BALANCE			0x02
+#define UAC2_MD_RATE			0x03
+#define UAC2_MD_DEPTH			0x04
+#define UAC2_MD_TIME			0x05
+#define UAC2_MD_FEEDBACK		0x06
+#define UAC2_MD_UNDERFLOW		0x07
+#define UAC2_MD_OVERFLOW		0x08
+#define UAC2_MD_LATENCY			0x09
+
+/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
+#define UAC2_DR_UNDEFINED		0x00
+#define UAC2_DR_ENABLE			0x01
+#define UAC2_DR_COMPRESSION_RATE	0x02
+#define UAC2_DR_MAXAMPL			0x03
+#define UAC2_DR_THRESHOLD		0x04
+#define UAC2_DR_ATTACK_TIME		0x05
+#define UAC2_DR_RELEASE_TIME		0x06
+#define UAC2_DR_UNDEFLOW		0x07
+#define UAC2_DR_OVERFLOW		0x08
+#define UAC2_DR_LATENCY			0x09
+
+/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
+#define UAC2_UD_UNDEFINED		0x00
+#define UAC2_UD_ENABLE			0x01
+#define UAC2_UD_MODE_SELECT		0x02
+#define UAC2_UD_CLUSTER			0x03
+#define UAC2_UD_UNDERFLOW		0x04
+#define UAC2_UD_OVERFLOW		0x05
+#define UAC2_UD_LATENCY			0x06
+
+/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
+#define UAC2_DP_UNDEFINED		0x00
+#define UAC2_DP_ENABLE			0x01
+#define UAC2_DP_MODE_SELECT		0x02
+#define UAC2_DP_CLUSTER			0x03
+#define UAC2_DP_UNDERFFLOW		0x04
+#define UAC2_DP_OVERFLOW		0x05
+#define UAC2_DP_LATENCY			0x06
+
+/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
+#define UAC2_ST_EXT_UNDEFINED		0x00
+#define UAC2_ST_EXT_ENABLE		0x01
+#define UAC2_ST_EXT_WIDTH		0x02
+#define UAC2_ST_EXT_UNDEFLOW		0x03
+#define UAC2_ST_EXT_OVERFLOW		0x04
+#define UAC2_ST_EXT_LATENCY		0x05
+
+/* A.17.10 Extension Unit Control Selectors */
+#define UAC2_XU_UNDEFINED		0x00
+#define UAC2_XU_ENABLE			0x01
+#define UAC2_XU_CLUSTER			0x02
+#define UAC2_XU_UNDERFLOW		0x03
+#define UAC2_XU_OVERFLOW		0x04
+#define UAC2_XU_LATENCY			0x05
+
+/* A.17.11 AudioStreaming Interface Control Selectors */
+#define UAC2_AS_UNDEFINED		0x00
+#define UAC2_AS_ACT_ALT_SETTING		0x01
+#define UAC2_AS_VAL_ALT_SETTINGS	0x02
+#define UAC2_AS_AUDIO_DATA_FORMAT	0x03
+
+/* A.17.12 Encoder Control Selectors */
+#define UAC2_EN_UNDEFINED		0x00
+#define UAC2_EN_BIT_RATE		0x01
+#define UAC2_EN_QUALITY			0x02
+#define UAC2_EN_VBR			0x03
+#define UAC2_EN_TYPE			0x04
+#define UAC2_EN_UNDERFLOW		0x05
+#define UAC2_EN_OVERFLOW		0x06
+#define UAC2_EN_ENCODER_ERROR		0x07
+#define UAC2_EN_PARAM1			0x08
+#define UAC2_EN_PARAM2			0x09
+#define UAC2_EN_PARAM3			0x0a
+#define UAC2_EN_PARAM4			0x0b
+#define UAC2_EN_PARAM5			0x0c
+#define UAC2_EN_PARAM6			0x0d
+#define UAC2_EN_PARAM7			0x0e
+#define UAC2_EN_PARAM8			0x0f
+
+/* A.17.13.1 MPEG Decoder Control Selectors */
+#define UAC2_MPEG_UNDEFINED		0x00
+#define UAC2_MPEG_DUAL_CHANNEL		0x01
+#define UAC2_MPEG_SECOND_STEREO		0x02
+#define UAC2_MPEG_MULTILINGUAL		0x03
+#define UAC2_MPEG_DYN_RANGE		0x04
+#define UAC2_MPEG_SCALING		0x05
+#define UAC2_MPEG_HILO_SCALING		0x06
+#define UAC2_MPEG_UNDERFLOW		0x07
+#define UAC2_MPEG_OVERFLOW		0x08
+#define UAC2_MPEG_DECODER_ERROR		0x09
+
+/* A17.13.2 AC3 Decoder Control Selectors */
+#define UAC2_AC3_UNDEFINED		0x00
+#define UAC2_AC3_MODE			0x01
+#define UAC2_AC3_DYN_RANGE		0x02
+#define UAC2_AC3_SCALING		0x03
+#define UAC2_AC3_HILO_SCALING		0x04
+#define UAC2_AC3_UNDERFLOW		0x05
+#define UAC2_AC3_OVERFLOW		0x06
+#define UAC2_AC3_DECODER_ERROR		0x07
+
+/* A17.13.3 WMA Decoder Control Selectors */
+#define UAC2_WMA_UNDEFINED		0x00
+#define UAC2_WMA_UNDERFLOW		0x01
+#define UAC2_WMA_OVERFLOW		0x02
+#define UAC2_WMA_DECODER_ERROR		0x03
+
+/* A17.13.4 DTS Decoder Control Selectors */
+#define UAC2_DTS_UNDEFINED		0x00
+#define UAC2_DTS_UNDERFLOW		0x01
+#define UAC2_DTS_OVERFLOW		0x02
+#define UAC2_DTS_DECODER_ERROR		0x03
+
+/* A17.14 Endpoint Control Selectors */
+#define UAC2_EP_CS_UNDEFINED		0x00
+#define UAC2_EP_CS_PITCH		0x01
+#define UAC2_EP_CS_DATA_OVERRUN		0x02
+#define UAC2_EP_CS_DATA_UNDERRUN	0x03
+
+#endif /* __LINUX_USB_AUDIO_V2_H */
+
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index 4d3e450..cdad728 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -13,6 +13,9 @@
  * Comments below reference relevant sections of that document:
  *
  * http://www.usb.org/developers/devclass_docs/audio10.pdf
+ *
+ * Types and defines in this file are either specific to version 1.0 of
+ * this standard or common for newer versions.
  */
 
 #ifndef __LINUX_USB_AUDIO_H
@@ -20,14 +23,15 @@
 
 #include <linux/types.h>
 
+/* bInterfaceProtocol values to denote the version of the standard used */
+#define UAC_VERSION_1			0x00
+#define UAC_VERSION_2			0x20
+
 /* A.2 Audio Interface Subclass Codes */
 #define USB_SUBCLASS_AUDIOCONTROL	0x01
 #define USB_SUBCLASS_AUDIOSTREAMING	0x02
 #define USB_SUBCLASS_MIDISTREAMING	0x03
 
-#define UAC_VERSION_1			0x00
-#define UAC_VERSION_2			0x20
-
 /* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
 #define UAC_HEADER			0x01
 #define UAC_INPUT_TERMINAL		0x02
@@ -38,15 +42,6 @@
 #define UAC_PROCESSING_UNIT_V1		0x07
 #define UAC_EXTENSION_UNIT_V1		0x08
 
-/* UAC v2.0 types */
-#define UAC_EFFECT_UNIT			0x07
-#define UAC_PROCESSING_UNIT_V2		0x08
-#define UAC_EXTENSION_UNIT_V2		0x09
-#define UAC_CLOCK_SOURCE		0x0a
-#define UAC_CLOCK_SELECTOR		0x0b
-#define UAC_CLOCK_MULTIPLIER		0x0c
-#define UAC_SAMPLE_RATE_CONVERTER	0x0d
-
 /* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
 #define UAC_AS_GENERAL			0x01
 #define UAC_FORMAT_TYPE			0x02
@@ -78,10 +73,6 @@
 
 #define UAC_GET_STAT			0xff
 
-/* Audio class v2.0 handles all the parameter calls differently */
-#define UAC2_CS_CUR			0x01
-#define UAC2_CS_RANGE			0x02
-
 /* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
 #define UAC_MS_HEADER			0x01
 #define UAC_MIDI_IN_JACK		0x02
@@ -200,19 +191,6 @@ struct uac_as_header_descriptor_v1 {
 	__le16 wFormatTag;		/* The Audio Data Format */
 } __attribute__ ((packed));
 
-struct uac_as_header_descriptor_v2 {
-	__u8 bLength;
-	__u8 bDescriptorType;
-	__u8 bDescriptorSubtype;
-	__u8 bTerminalLink;
-	__u8 bmControls;
-	__u8 bFormatType;
-	__u32 bmFormats;
-	__u8 bNrChannels;
-	__u32 bmChannelConfig;
-	__u8 iChannelNames;
-} __attribute__((packed));
-
 #define UAC_DT_AS_HEADER_SIZE		7
 
 /* Formats - A.1.1 Audio Data Format Type I Codes */
@@ -277,7 +255,6 @@ struct uac_format_type_i_ext_descriptor {
 	__u8 bSideBandProtocol;
 } __attribute__((packed));
 
-
 /* Formats - Audio Data Format Type I Codes */
 
 #define UAC_FORMAT_TYPE_II_MPEG	0x1001
@@ -336,19 +313,6 @@ struct uac_iso_endpoint_descriptor {
 #define UAC_EP_CS_ATTR_PITCH_CONTROL	0x02
 #define UAC_EP_CS_ATTR_FILL_MAX		0x80
 
-/* Audio class v2.0: CLOCK_SOURCE descriptor */
-
-struct uac_clock_source_descriptor {
-	__u8 bLength;
-	__u8 bDescriptorType;
-	__u8 bDescriptorSubtype;
-	__u8 bClockID;
-	__u8 bmAttributes;
-	__u8 bmControls;
-	__u8 bAssocTerminal;
-	__u8 iClockSource;
-} __attribute__((packed));
-
 /* A.10.2 Feature Unit Control Selectors */
 
 struct uac_feature_unit_descriptor {
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 426aabc..78d12ff 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -45,6 +45,7 @@
 #include <linux/moduleparam.h>
 #include <linux/mutex.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/info.h>
@@ -250,7 +251,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 		 * clock selectors and sample rate conversion units. */
 
 		cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
-						NULL, UAC_CLOCK_SOURCE);
+						NULL, UAC2_CLOCK_SOURCE);
 
 		if (!cs) {
 			snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 91850f8..b1309cd 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
diff --git a/sound/usb/format.c b/sound/usb/format.c
index b613e0a..0e04efe 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index e0f3f87..630e220 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -215,7 +216,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 	data[3] = rate >> 24;
 	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
 				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
-				   0x0100, chip->clock_id << 8,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
 				   data, sizeof(data), 1000)) < 0) {
 		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
 			   dev->devnum, iface, fmt->altsetting, rate);
@@ -223,7 +224,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
 	}
 	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
 				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-				   0x0100, chip->clock_id << 8,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
 				   data, sizeof(data), 1000)) < 0) {
 		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
 			   dev->devnum, iface, fmt->altsetting);
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h
  2010-03-11 20:13 ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
@ 2010-03-11 20:13   ` Daniel Mack
  2010-03-11 20:13     ` [PATCH 3/6] ALSA: usb-mixer: factor out quirks Daniel Mack
  2010-03-11 20:17   ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
  1 sibling, 1 reply; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

No need for the private enum.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 sound/usb/format.c        |    6 ++++--
 sound/usb/usbmixer.c      |   27 ++++++---------------------
 sound/usb/usbmixer_maps.c |    4 ++--
 3 files changed, 12 insertions(+), 25 deletions(-)

diff --git a/sound/usb/format.c b/sound/usb/format.c
index 0e04efe..fcadedd 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -218,7 +218,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
 	/* get the number of sample rates first by only fetching 2 bytes */
 	ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-			      0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);
+			      UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+			      tmp, sizeof(tmp), 1000);
 
 	if (ret < 0) {
 		snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
@@ -236,7 +237,8 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
 	/* now get the full information */
 	ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
 			       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-			       0x0100, chip->clock_id << 8, data, data_size, 1000);
+			       UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+			       data, data_size, 1000);
 
 	if (ret < 0) {
 		snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 5c05683..ab8f0f0 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -136,21 +136,6 @@ struct usb_mixer_elem_info {
 	u8 initialized;
 };
 
-
-enum {
-	USB_FEATURE_NONE = 0,
-	USB_FEATURE_MUTE = 1,
-	USB_FEATURE_VOLUME,
-	USB_FEATURE_BASS,
-	USB_FEATURE_MID,
-	USB_FEATURE_TREBLE,
-	USB_FEATURE_GEQ,
-	USB_FEATURE_AGC,
-	USB_FEATURE_DELAY,
-	USB_FEATURE_BASSBOOST,
-	USB_FEATURE_LOUDNESS
-};
-
 enum {
 	USB_MIXER_BOOLEAN,
 	USB_MIXER_INV_BOOLEAN,
@@ -954,7 +939,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 
 	control++; /* change from zero-based to 1-based value */
 
-	if (control == USB_FEATURE_GEQ) {
+	if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
 		/* FIXME: not supported yet */
 		return;
 	}
@@ -1001,8 +986,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 				kctl->id.name, sizeof(kctl->id.name));
 
 	switch (control) {
-	case USB_FEATURE_MUTE:
-	case USB_FEATURE_VOLUME:
+	case UAC_MUTE_CONTROL:
+	case UAC_VOLUME_CONTROL:
 		/* determine the control name.  the rule is:
 		 * - if a name id is given in descriptor, use it.
 		 * - if the connected input can be determined, then use the name
@@ -1029,9 +1014,9 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 				len = append_ctl_name(kctl, " Playback");
 			}
 		}
-		append_ctl_name(kctl, control == USB_FEATURE_MUTE ?
+		append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
 				" Switch" : " Volume");
-		if (control == USB_FEATURE_VOLUME) {
+		if (control == UAC_VOLUME_CONTROL) {
 			kctl->tlv.c = mixer_vol_tlv;
 			kctl->vd[0].access |= 
 				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
@@ -1120,7 +1105,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 		snd_printk(KERN_INFO
 			   "usbmixer: master volume quirk for PCM2702 chip\n");
 		/* disable non-functional volume control */
-		master_bits &= ~(1 << (USB_FEATURE_VOLUME - 1));
+		master_bits &= ~UAC_FU_VOLUME;
 		break;
 	}
 	if (channels > 0)
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index 79e903a..d93fc89 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = {
 	/* 16: MU (w/o controls) */
 	{ 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
 	{ 17, "Channel Routing", 2 },	/* PU: mode select */
-	{ 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
-	{ 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
+	{ 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */
+	{ 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
 	{ 18, "Master Playback" }, /* FU; others */
 	/* 19: OT speaker */
 	/* 20: OT headphone */
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/6] ALSA: usb-mixer: factor out quirks
  2010-03-11 20:13   ` [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h Daniel Mack
@ 2010-03-11 20:13     ` Daniel Mack
  2010-03-11 20:13       ` [PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch] Daniel Mack
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

Move all non-standard mixer controls and vendor-specific extensions to a
separate file. Some structs need to be exported now.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 sound/usb/Makefile       |    1 +
 sound/usb/mixer_quirks.c |  411 ++++++++++++++++++++++++++++++++++++++++++++
 sound/usb/mixer_quirks.h |   13 ++
 sound/usb/quirks.c       |    1 +
 sound/usb/usbmixer.c     |  425 ++--------------------------------------------
 sound/usb/usbmixer.h     |   45 +++++-
 6 files changed, 480 insertions(+), 416 deletions(-)
 create mode 100644 sound/usb/mixer_quirks.c
 create mode 100644 sound/usb/mixer_quirks.h

diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 0758d8d..744024a 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -4,6 +4,7 @@
 
 snd-usb-audio-objs := 	card.o \
 			usbmixer.o \
+			mixer_quirks.o \
 			proc.o \
 			quirks.o \
 			format.o \
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
new file mode 100644
index 0000000..d2f4dcd
--- /dev/null
+++ b/sound/usb/mixer_quirks.c
@@ -0,0 +1,411 @@
+/*
+ *   USB Audio Driver for ALSA
+ *
+ *   Quirks and vendor-specific extensions for mixer interfaces
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by
+ *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ *   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
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+#include "usbaudio.h"
+#include "usbmixer.h"
+#include "mixer_quirks.h"
+#include "helper.h"
+
+/*
+ * Sound Blaster remote control configuration
+ *
+ * format of remote control data:
+ * Extigy:       xx 00
+ * Audigy 2 NX:  06 80 xx 00 00 00
+ * Live! 24-bit: 06 80 xx yy 22 83
+ */
+static const struct rc_config {
+	u32 usb_id;
+	u8  offset;
+	u8  length;
+	u8  packet_length;
+	u8  min_packet_length; /* minimum accepted length of the URB result */
+	u8  mute_mixer_id;
+	u32 mute_code;
+} rc_configs[] = {
+	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
+	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
+	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
+	{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
+};
+
+static void snd_usb_soundblaster_remote_complete(struct urb *urb)
+{
+	struct usb_mixer_interface *mixer = urb->context;
+	const struct rc_config *rc = mixer->rc_cfg;
+	u32 code;
+
+	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
+		return;
+
+	code = mixer->rc_buffer[rc->offset];
+	if (rc->length == 2)
+		code |= mixer->rc_buffer[rc->offset + 1] << 8;
+
+	/* the Mute button actually changes the mixer control */
+	if (code == rc->mute_code)
+		snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
+	mixer->rc_code = code;
+	wmb();
+	wake_up(&mixer->rc_waitq);
+}
+
+static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
+				     long count, loff_t *offset)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+	int err;
+	u32 rc_code;
+
+	if (count != 1 && count != 4)
+		return -EINVAL;
+	err = wait_event_interruptible(mixer->rc_waitq,
+				       (rc_code = xchg(&mixer->rc_code, 0)) != 0);
+	if (err == 0) {
+		if (count == 1)
+			err = put_user(rc_code, buf);
+		else
+			err = put_user(rc_code, (u32 __user *)buf);
+	}
+	return err < 0 ? err : count;
+}
+
+static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
+					    poll_table *wait)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+
+	poll_wait(file, &mixer->rc_waitq, wait);
+	return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
+}
+
+static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
+{
+	struct snd_hwdep *hwdep;
+	int err, len, i;
+
+	for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
+		if (rc_configs[i].usb_id == mixer->chip->usb_id)
+			break;
+	if (i >= ARRAY_SIZE(rc_configs))
+		return 0;
+	mixer->rc_cfg = &rc_configs[i];
+
+	len = mixer->rc_cfg->packet_length;
+
+	init_waitqueue_head(&mixer->rc_waitq);
+	err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
+	if (err < 0)
+		return err;
+	snprintf(hwdep->name, sizeof(hwdep->name),
+		 "%s remote control", mixer->chip->card->shortname);
+	hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
+	hwdep->private_data = mixer;
+	hwdep->ops.read = snd_usb_sbrc_hwdep_read;
+	hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+	hwdep->exclusive = 1;
+
+	mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!mixer->rc_urb)
+		return -ENOMEM;
+	mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
+	if (!mixer->rc_setup_packet) {
+		usb_free_urb(mixer->rc_urb);
+		mixer->rc_urb = NULL;
+		return -ENOMEM;
+	}
+	mixer->rc_setup_packet->bRequestType =
+		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
+	mixer->rc_setup_packet->wValue = cpu_to_le16(0);
+	mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
+	mixer->rc_setup_packet->wLength = cpu_to_le16(len);
+	usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
+			     usb_rcvctrlpipe(mixer->chip->dev, 0),
+			     (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+			     snd_usb_soundblaster_remote_complete, mixer);
+	return 0;
+}
+
+#define snd_audigy2nx_led_info		snd_ctl_boolean_mono_info
+
+static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+	return 0;
+}
+
+static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	int value = ucontrol->value.integer.value[0];
+	int err, changed;
+
+	if (value > 1)
+		return -EINVAL;
+	changed = value != mixer->audigy2nx_leds[index];
+	err = snd_usb_ctl_msg(mixer->chip->dev,
+			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+			      value, index + 2, NULL, 0, 100);
+	if (err < 0)
+		return err;
+	mixer->audigy2nx_leds[index] = value;
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "CMSS LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 0,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Power LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 1,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Dolby Digital LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 2,
+	},
+};
+
+static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+{
+	int i, err;
+
+	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+		if (i > 1 && /* Live24ext has 2 LEDs only */
+			(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+			 mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
+			break; 
+		err = snd_ctl_add(mixer->chip->card,
+				  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+		if (err < 0)
+			return err;
+	}
+	mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
+	return 0;
+}
+
+static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
+				    struct snd_info_buffer *buffer)
+{
+	static const struct sb_jack {
+		int unitid;
+		const char *name;
+	}  jacks_audigy2nx[] = {
+		{4,  "dig in "},
+		{7,  "line in"},
+		{19, "spk out"},
+		{20, "hph out"},
+		{-1, NULL}
+	}, jacks_live24ext[] = {
+		{4,  "line in"}, /* &1=Line, &2=Mic*/
+		{3,  "hph out"}, /* headphones */
+		{0,  "RC     "}, /* last command, 6 bytes see rc_config above */
+		{-1, NULL}
+	};
+	const struct sb_jack *jacks;
+	struct usb_mixer_interface *mixer = entry->private_data;
+	int i, err;
+	u8 buf[3];
+
+	snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
+		jacks = jacks_audigy2nx;
+	else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+		 mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+		jacks = jacks_live24ext;
+	else
+		return;
+
+	for (i = 0; jacks[i].name; ++i) {
+		snd_iprintf(buffer, "%s: ", jacks[i].name);
+		err = snd_usb_ctl_msg(mixer->chip->dev,
+				      usb_rcvctrlpipe(mixer->chip->dev, 0),
+				      UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
+				      USB_RECIP_INTERFACE, 0,
+				      jacks[i].unitid << 8, buf, 3, 100);
+		if (err == 3 && (buf[0] == 3 || buf[0] == 6))
+			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+		else
+			snd_iprintf(buffer, "?\n");
+	}
+}
+
+static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
+	return 0;
+}
+
+static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+	u8 old_status, new_status;
+	int err, changed;
+
+	old_status = mixer->xonar_u1_status;
+	if (ucontrol->value.integer.value[0])
+		new_status = old_status | 0x02;
+	else
+		new_status = old_status & ~0x02;
+	changed = new_status != old_status;
+	err = snd_usb_ctl_msg(mixer->chip->dev,
+			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+			      50, 0, &new_status, 1, 100);
+	if (err < 0)
+		return err;
+	mixer->xonar_u1_status = new_status;
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Digital Playback Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = snd_xonar_u1_switch_get,
+	.put = snd_xonar_u1_switch_put,
+};
+
+static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
+{
+	int err;
+
+	err = snd_ctl_add(mixer->chip->card,
+			  snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
+	if (err < 0)
+		return err;
+	mixer->xonar_u1_status = 0x05;
+	return 0;
+}
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+			       unsigned char samplerate_id)
+{
+	struct usb_mixer_interface *mixer;
+	struct usb_mixer_elem_info *cval;
+	int unitid = 12; /* SamleRate ExtensionUnit ID */
+
+	list_for_each_entry(mixer, &chip->mixer_list, list) {
+		cval = mixer->id_elems[unitid];
+		if (cval) {
+			snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
+						    cval->control << 8,
+						    samplerate_id);
+			snd_usb_mixer_notify_id(mixer, unitid);
+		}
+		break;
+	}
+}
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+{
+	int err;
+	struct snd_info_entry *entry;
+
+	if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+		return err;
+
+	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
+	    mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+	    mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
+		if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+			return err;
+		if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry))
+			snd_info_set_text_ops(entry, mixer,
+					      snd_audigy2nx_proc_read);
+	}
+
+	if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
+	    mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
+		err = snd_xonar_u1_controls_create(mixer);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+				    int unitid)
+{
+	if (!mixer->rc_cfg)
+		return;
+	/* unit ids specific to Extigy/Audigy 2 NX: */
+	switch (unitid) {
+	case 0: /* remote control */
+		mixer->rc_urb->dev = mixer->chip->dev;
+		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
+		break;
+	case 4: /* digital in jack */
+	case 7: /* line in jacks */
+	case 19: /* speaker out jacks */
+	case 20: /* headphones out jack */
+		break;
+	/* live24ext: 4 = line-in jack */
+	case 3:	/* hp-out jack (may actuate Mute) */
+		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+		    mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
+		break;
+	default:
+		snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+		break;
+	}
+}
+
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
new file mode 100644
index 0000000..bdbfab0
--- /dev/null
+++ b/sound/usb/mixer_quirks.h
@@ -0,0 +1,13 @@
+#ifndef SND_USB_MIXER_QUIRKS_H
+#define SND_USB_MIXER_QUIRKS_H
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer);
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+			       unsigned char samplerate_id);
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+				    int unitid);
+
+#endif /* SND_USB_MIXER_QUIRKS_H */
+
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 0c0b23b..a82cfed 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -25,6 +25,7 @@
 #include "usbaudio.h"
 #include "card.h"
 #include "usbmixer.h"
+#include "mixer_quirks.h"
 #include "midi.h"
 #include "quirks.h"
 #include "helper.h"
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index ab8f0f0..ec2436e 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -43,60 +43,10 @@
 #include "usbaudio.h"
 #include "usbmixer.h"
 #include "helper.h"
-
-/*
- */
-
-/* ignore error from controls - for debugging */
-/* #define IGNORE_CTL_ERROR */
-
-/*
- * Sound Blaster remote control configuration
- *
- * format of remote control data:
- * Extigy:       xx 00
- * Audigy 2 NX:  06 80 xx 00 00 00
- * Live! 24-bit: 06 80 xx yy 22 83
- */
-static const struct rc_config {
-	u32 usb_id;
-	u8  offset;
-	u8  length;
-	u8  packet_length;
-	u8  min_packet_length; /* minimum accepted length of the URB result */
-	u8  mute_mixer_id;
-	u32 mute_code;
-} rc_configs[] = {
-	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
-	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
-	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
-	{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
-};
+#include "mixer_quirks.h"
 
 #define MAX_ID_ELEMS	256
 
-struct usb_mixer_interface {
-	struct snd_usb_audio *chip;
-	unsigned int ctrlif;
-	struct list_head list;
-	unsigned int ignore_ctl_error;
-	struct urb *urb;
-	/* array[MAX_ID_ELEMS], indexed by unit id */
-	struct usb_mixer_elem_info **id_elems;
-
-	/* Sound Blaster remote control stuff */
-	const struct rc_config *rc_cfg;
-	u32 rc_code;
-	wait_queue_head_t rc_waitq;
-	struct urb *rc_urb;
-	struct usb_ctrlrequest *rc_setup_packet;
-	u8 rc_buffer[6];
-
-	u8 audigy2nx_leds[3];
-	u8 xonar_u1_status;
-};
-
-
 struct usb_audio_term {
 	int id;
 	int type;
@@ -118,24 +68,6 @@ struct mixer_build {
 	const struct usbmix_selector_map *selector_map;
 };
 
-#define MAX_CHANNELS	10	/* max logical channels */
-
-struct usb_mixer_elem_info {
-	struct usb_mixer_interface *mixer;
-	struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
-	struct snd_ctl_elem_id *elem_id;
-	unsigned int id;
-	unsigned int control;	/* CS or ICN (high byte) */
-	unsigned int cmask; /* channel mask bitmap: 0 = master */
-	int channels;
-	int val_type;
-	int min, max, res;
-	int dBmin, dBmax;
-	int cached;
-	int cache_val[MAX_CHANNELS];
-	u8 initialized;
-};
-
 enum {
 	USB_MIXER_BOOLEAN,
 	USB_MIXER_INV_BOOLEAN,
@@ -431,7 +363,8 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
  * set a mixer value
  */
 
-static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set)
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+				int request, int validx, int value_set)
 {
 	unsigned char buf[2];
 	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -455,14 +388,14 @@ static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 
 static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
 {
-	return set_ctl_value(cval, UAC_SET_CUR, validx, value);
+	return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value);
 }
 
 static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
 			     int index, int value)
 {
 	int err;
-	err = set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
+	err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
 			    value);
 	if (err < 0)
 		return err;
@@ -751,7 +684,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 			int last_valid_res = cval->res;
 
 			while (cval->res > 1) {
-				if (set_ctl_value(cval, UAC_SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0)
+				if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES,
+								(cval->control << 8) | minchn, cval->res / 2) < 0)
 					break;
 				cval->res /= 2;
 			}
@@ -1808,8 +1742,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 	return 0;
 }
 
-static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
-				    int unitid)
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
 {
 	struct usb_mixer_elem_info *info;
 
@@ -1858,34 +1791,6 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
 	}
 }
 
-static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
-					int unitid)
-{
-	if (!mixer->rc_cfg)
-		return;
-	/* unit ids specific to Extigy/Audigy 2 NX: */
-	switch (unitid) {
-	case 0: /* remote control */
-		mixer->rc_urb->dev = mixer->chip->dev;
-		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
-		break;
-	case 4: /* digital in jack */
-	case 7: /* line in jacks */
-	case 19: /* speaker out jacks */
-	case 20: /* headphones out jack */
-		break;
-	/* live24ext: 4 = line-in jack */
-	case 3:	/* hp-out jack (may actuate Mute) */
-		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-		    mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
-		break;
-	default:
-		snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
-		break;
-	}
-}
-
 static void snd_usb_mixer_status_complete(struct urb *urb)
 {
 	struct usb_mixer_interface *mixer = urb->context;
@@ -1903,7 +1808,7 @@ static void snd_usb_mixer_status_complete(struct urb *urb)
 			if (!(buf[0] & 0x40))
 				snd_usb_mixer_notify_id(mixer, buf[1]);
 			else
-				snd_usb_mixer_memory_change(mixer, buf[1]);
+				snd_usb_mixer_rc_memory_change(mixer, buf[1]);
 		}
 	}
 	if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
@@ -1947,296 +1852,6 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
 	return 0;
 }
 
-static void snd_usb_soundblaster_remote_complete(struct urb *urb)
-{
-	struct usb_mixer_interface *mixer = urb->context;
-	const struct rc_config *rc = mixer->rc_cfg;
-	u32 code;
-
-	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
-		return;
-
-	code = mixer->rc_buffer[rc->offset];
-	if (rc->length == 2)
-		code |= mixer->rc_buffer[rc->offset + 1] << 8;
-
-	/* the Mute button actually changes the mixer control */
-	if (code == rc->mute_code)
-		snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
-	mixer->rc_code = code;
-	wmb();
-	wake_up(&mixer->rc_waitq);
-}
-
-static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
-				     long count, loff_t *offset)
-{
-	struct usb_mixer_interface *mixer = hw->private_data;
-	int err;
-	u32 rc_code;
-
-	if (count != 1 && count != 4)
-		return -EINVAL;
-	err = wait_event_interruptible(mixer->rc_waitq,
-				       (rc_code = xchg(&mixer->rc_code, 0)) != 0);
-	if (err == 0) {
-		if (count == 1)
-			err = put_user(rc_code, buf);
-		else
-			err = put_user(rc_code, (u32 __user *)buf);
-	}
-	return err < 0 ? err : count;
-}
-
-static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
-					    poll_table *wait)
-{
-	struct usb_mixer_interface *mixer = hw->private_data;
-
-	poll_wait(file, &mixer->rc_waitq, wait);
-	return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
-}
-
-static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
-{
-	struct snd_hwdep *hwdep;
-	int err, len, i;
-
-	for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
-		if (rc_configs[i].usb_id == mixer->chip->usb_id)
-			break;
-	if (i >= ARRAY_SIZE(rc_configs))
-		return 0;
-	mixer->rc_cfg = &rc_configs[i];
-
-	len = mixer->rc_cfg->packet_length;
-	
-	init_waitqueue_head(&mixer->rc_waitq);
-	err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
-	if (err < 0)
-		return err;
-	snprintf(hwdep->name, sizeof(hwdep->name),
-		 "%s remote control", mixer->chip->card->shortname);
-	hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
-	hwdep->private_data = mixer;
-	hwdep->ops.read = snd_usb_sbrc_hwdep_read;
-	hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
-	hwdep->exclusive = 1;
-
-	mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!mixer->rc_urb)
-		return -ENOMEM;
-	mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
-	if (!mixer->rc_setup_packet) {
-		usb_free_urb(mixer->rc_urb);
-		mixer->rc_urb = NULL;
-		return -ENOMEM;
-	}
-	mixer->rc_setup_packet->bRequestType =
-		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
-	mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
-	mixer->rc_setup_packet->wValue = cpu_to_le16(0);
-	mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
-	mixer->rc_setup_packet->wLength = cpu_to_le16(len);
-	usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
-			     usb_rcvctrlpipe(mixer->chip->dev, 0),
-			     (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
-			     snd_usb_soundblaster_remote_complete, mixer);
-	return 0;
-}
-
-#define snd_audigy2nx_led_info		snd_ctl_boolean_mono_info
-
-static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
-
-	ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
-	return 0;
-}
-
-static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
-	int value = ucontrol->value.integer.value[0];
-	int err, changed;
-
-	if (value > 1)
-		return -EINVAL;
-	changed = value != mixer->audigy2nx_leds[index];
-	err = snd_usb_ctl_msg(mixer->chip->dev,
-			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
-			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-			      value, index + 2, NULL, 0, 100);
-	if (err < 0)
-		return err;
-	mixer->audigy2nx_leds[index] = value;
-	return changed;
-}
-
-static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "CMSS LED Switch",
-		.info = snd_audigy2nx_led_info,
-		.get = snd_audigy2nx_led_get,
-		.put = snd_audigy2nx_led_put,
-		.private_value = 0,
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Power LED Switch",
-		.info = snd_audigy2nx_led_info,
-		.get = snd_audigy2nx_led_get,
-		.put = snd_audigy2nx_led_put,
-		.private_value = 1,
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Dolby Digital LED Switch",
-		.info = snd_audigy2nx_led_info,
-		.get = snd_audigy2nx_led_get,
-		.put = snd_audigy2nx_led_put,
-		.private_value = 2,
-	},
-};
-
-static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
-{
-	int i, err;
-
-	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
-		if (i > 1 && /* Live24ext has 2 LEDs only */
-			(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-			 mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
-			break; 
-		err = snd_ctl_add(mixer->chip->card,
-				  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
-		if (err < 0)
-			return err;
-	}
-	mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
-	return 0;
-}
-
-static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
-				    struct snd_info_buffer *buffer)
-{
-	static const struct sb_jack {
-		int unitid;
-		const char *name;
-	}  jacks_audigy2nx[] = {
-		{4,  "dig in "},
-		{7,  "line in"},
-		{19, "spk out"},
-		{20, "hph out"},
-		{-1, NULL}
-	}, jacks_live24ext[] = {
-		{4,  "line in"}, /* &1=Line, &2=Mic*/
-		{3,  "hph out"}, /* headphones */
-		{0,  "RC     "}, /* last command, 6 bytes see rc_config above */
-		{-1, NULL}
-	};
-	const struct sb_jack *jacks;
-	struct usb_mixer_interface *mixer = entry->private_data;
-	int i, err;
-	u8 buf[3];
-
-	snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
-	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
-		jacks = jacks_audigy2nx;
-	else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-		 mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-		jacks = jacks_live24ext;
-	else
-		return;
-
-	for (i = 0; jacks[i].name; ++i) {
-		snd_iprintf(buffer, "%s: ", jacks[i].name);
-		err = snd_usb_ctl_msg(mixer->chip->dev,
-				      usb_rcvctrlpipe(mixer->chip->dev, 0),
-				      UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
-				      USB_RECIP_INTERFACE, 0,
-				      jacks[i].unitid << 8, buf, 3, 100);
-		if (err == 3 && (buf[0] == 3 || buf[0] == 6))
-			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
-		else
-			snd_iprintf(buffer, "?\n");
-	}
-}
-
-static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
-				   struct snd_ctl_elem_value *ucontrol)
-{
-	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-
-	ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
-	return 0;
-}
-
-static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
-				   struct snd_ctl_elem_value *ucontrol)
-{
-	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-	u8 old_status, new_status;
-	int err, changed;
-
-	old_status = mixer->xonar_u1_status;
-	if (ucontrol->value.integer.value[0])
-		new_status = old_status | 0x02;
-	else
-		new_status = old_status & ~0x02;
-	changed = new_status != old_status;
-	err = snd_usb_ctl_msg(mixer->chip->dev,
-			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
-			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-			      50, 0, &new_status, 1, 100);
-	if (err < 0)
-		return err;
-	mixer->xonar_u1_status = new_status;
-	return changed;
-}
-
-static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Digital Playback Switch",
-	.info = snd_ctl_boolean_mono_info,
-	.get = snd_xonar_u1_switch_get,
-	.put = snd_xonar_u1_switch_put,
-};
-
-static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
-{
-	int err;
-
-	err = snd_ctl_add(mixer->chip->card,
-			  snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
-	if (err < 0)
-		return err;
-	mixer->xonar_u1_status = 0x05;
-	return 0;
-}
-
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-			       unsigned char samplerate_id)
-{
-	struct usb_mixer_interface *mixer;
-	struct usb_mixer_elem_info *cval;
-	int unitid = 12; /* SamleRate ExtensionUnit ID */
-
-	list_for_each_entry(mixer, &chip->mixer_list, list) {
-		cval = mixer->id_elems[unitid];
-		if (cval) {
-			set_cur_ctl_value(cval, cval->control << 8,
-					  samplerate_id);
-			snd_usb_mixer_notify_id(mixer, unitid);
-		}
-		break;
-	}
-}
-
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 			 int ignore_error)
 {
@@ -2277,25 +1892,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 	    (err = snd_usb_mixer_status_create(mixer)) < 0)
 		goto _error;
 
-	if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
-		goto _error;
-
-	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
-	    mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-	    mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
-		if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
-			goto _error;
-		if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
-			snd_info_set_text_ops(entry, mixer,
-					      snd_audigy2nx_proc_read);
-	}
-
-	if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
-	    mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
-		err = snd_xonar_u1_controls_create(mixer);
-		if (err < 0)
-			goto _error;
-	}
+	snd_usb_mixer_apply_create_quirk(mixer);
 
 	err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
 	if (err < 0)
@@ -2316,7 +1913,7 @@ _error:
 void snd_usb_mixer_disconnect(struct list_head *p)
 {
 	struct usb_mixer_interface *mixer;
-	
+
 	mixer = list_entry(p, struct usb_mixer_interface, list);
 	usb_kill_urb(mixer->urb);
 	usb_kill_urb(mixer->rc_urb);
diff --git a/sound/usb/usbmixer.h b/sound/usb/usbmixer.h
index e199e4b..63101ae 100644
--- a/sound/usb/usbmixer.h
+++ b/sound/usb/usbmixer.h
@@ -1,11 +1,52 @@
 #ifndef __USBMIXER_H
 #define __USBMIXER_H
 
+struct usb_mixer_interface {
+	struct snd_usb_audio *chip;
+	unsigned int ctrlif;
+	struct list_head list;
+	unsigned int ignore_ctl_error;
+	struct urb *urb;
+	/* array[MAX_ID_ELEMS], indexed by unit id */
+	struct usb_mixer_elem_info **id_elems;
+
+	/* Sound Blaster remote control stuff */
+	const struct rc_config *rc_cfg;
+	u32 rc_code;
+	wait_queue_head_t rc_waitq;
+	struct urb *rc_urb;
+	struct usb_ctrlrequest *rc_setup_packet;
+	u8 rc_buffer[6];
+
+	u8 audigy2nx_leds[3];
+	u8 xonar_u1_status;
+};
+
+#define MAX_CHANNELS	10	/* max logical channels */
+
+struct usb_mixer_elem_info {
+	struct usb_mixer_interface *mixer;
+	struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
+	struct snd_ctl_elem_id *elem_id;
+	unsigned int id;
+	unsigned int control;	/* CS or ICN (high byte) */
+	unsigned int cmask; /* channel mask bitmap: 0 = master */
+	int channels;
+	int val_type;
+	int min, max, res;
+	int dBmin, dBmax;
+	int cached;
+	int cache_val[MAX_CHANNELS];
+	u8 initialized;
+};
+
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 			 int ignore_error);
 void snd_usb_mixer_disconnect(struct list_head *p);
 
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-			unsigned char samplerate_id);
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
+
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+				int request, int validx, int value_set);
 
 #endif /* __USBMIXER_H */
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch]
  2010-03-11 20:13     ` [PATCH 3/6] ALSA: usb-mixer: factor out quirks Daniel Mack
@ 2010-03-11 20:13       ` Daniel Mack
  2010-03-11 20:13         ` [PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs Daniel Mack
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

For clearer namespace, also rename usbmixer_maps.c -> mixer_maps.c

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 sound/usb/Makefile                          |    2 +-
 sound/usb/card.c                            |    2 +-
 sound/usb/{usbmixer.c => mixer.c}           |    4 ++--
 sound/usb/{usbmixer.h => mixer.h}           |    0
 sound/usb/{usbmixer_maps.c => mixer_maps.c} |    0
 sound/usb/mixer_quirks.c                    |    2 +-
 sound/usb/quirks.c                          |    2 +-
 7 files changed, 6 insertions(+), 6 deletions(-)
 rename sound/usb/{usbmixer.c => mixer.c} (99%)
 rename sound/usb/{usbmixer.h => mixer.h} (100%)
 rename sound/usb/{usbmixer_maps.c => mixer_maps.c} (100%)

diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 744024a..e7ac7f4 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -3,7 +3,7 @@
 #
 
 snd-usb-audio-objs := 	card.o \
-			usbmixer.o \
+			mixer.o \
 			mixer_quirks.o \
 			proc.o \
 			quirks.o \
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 78d12ff..0bd62a1 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -56,7 +56,7 @@
 #include "usbaudio.h"
 #include "card.h"
 #include "midi.h"
-#include "usbmixer.h"
+#include "mixer.h"
 #include "proc.h"
 #include "quirks.h"
 #include "endpoint.h"
diff --git a/sound/usb/usbmixer.c b/sound/usb/mixer.c
similarity index 99%
rename from sound/usb/usbmixer.c
rename to sound/usb/mixer.c
index ec2436e..4e7c2fd 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/mixer.c
@@ -41,7 +41,7 @@
 #include <sound/tlv.h>
 
 #include "usbaudio.h"
-#include "usbmixer.h"
+#include "mixer.h"
 #include "helper.h"
 #include "mixer_quirks.h"
 
@@ -132,7 +132,7 @@ enum {
  * if the mixer topology is too complicated and the parsed names are
  * ambiguous, add the entries in usbmixer_maps.c.
  */
-#include "usbmixer_maps.c"
+#include "mixer_maps.c"
 
 static const struct usbmix_name_map *
 find_map(struct mixer_build *state, int unitid, int control)
diff --git a/sound/usb/usbmixer.h b/sound/usb/mixer.h
similarity index 100%
rename from sound/usb/usbmixer.h
rename to sound/usb/mixer.h
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/mixer_maps.c
similarity index 100%
rename from sound/usb/usbmixer_maps.c
rename to sound/usb/mixer_maps.c
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index d2f4dcd..56b6659 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -35,7 +35,7 @@
 #include <sound/info.h>
 
 #include "usbaudio.h"
-#include "usbmixer.h"
+#include "mixer.h"
 #include "mixer_quirks.h"
 #include "helper.h"
 
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index a82cfed..d4ced64 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -24,7 +24,7 @@
 
 #include "usbaudio.h"
 #include "card.h"
-#include "usbmixer.h"
+#include "mixer.h"
 #include "mixer_quirks.h"
 #include "midi.h"
 #include "quirks.h"
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs
  2010-03-11 20:13       ` [PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch] Daniel Mack
@ 2010-03-11 20:13         ` Daniel Mack
  2010-03-11 20:13           ` [PATCH 6/6] ALSA: usb-mixer: Add support for Audio Class v2.0 Daniel Mack
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

Introduce a number of new structs for mixer, selector, feature and
processing units and some static inline helpers to access fields which
have dynamic offsets. Use them in mixer.c to parse the descriptors. This
is necessary for the upcoming audio v2 parsers.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 include/linux/usb/audio.h |  129 +++++++++++++++++++++++++++++++++++++++++----
 sound/usb/mixer.c         |   87 ++++++++++++++++--------------
 2 files changed, 166 insertions(+), 50 deletions(-)

diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index cdad728..bc78a83 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -181,6 +181,125 @@ struct uac_feature_unit_descriptor_##ch {			\
 	__u8  iFeature;						\
 } __attribute__ ((packed))
 
+/* 4.3.2.3 Mixer Unit Descriptor */
+struct uac_mixer_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bNrInPins;
+	__u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor *desc)
+{
+	return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u16 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc)
+{
+	return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+		desc->baSourceID[desc->bNrInPins + 1];
+}
+
+static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc)
+{
+	return desc->baSourceID[desc->bNrInPins + 3];
+}
+
+static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc)
+{
+	return &desc->baSourceID[desc->bNrInPins + 4];
+}
+
+static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
+{
+	__u8 *raw = (__u8 *) desc;
+	return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.4 Selector Unit Descriptor */
+struct uac_selector_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUintID;
+	__u8 bNrInPins;
+	__u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
+{
+	__u8 *raw = (__u8 *) desc;
+	return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.5 Feature Unit Descriptor */
+struct uac_feature_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__u8 bControlSize;
+	__u8 bmaControls[0]; /* variable length */
+} __attribute__((packed));
+
+static inline __u8 uac_feature_unit_iFeature(struct uac_feature_unit_descriptor *desc)
+{
+	__u8 *raw = (__u8 *) desc;
+	return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.6 Processing Unit Descriptors */
+struct uac_processing_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u16 wProcessType;
+	__u8 bNrInPins;
+	__u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_descriptor *desc)
+{
+	return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u16 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc)
+{
+	return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+		desc->baSourceID[desc->bNrInPins + 1];
+}
+
+static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc)
+{
+	return desc->baSourceID[desc->bNrInPins + 3];
+}
+
+static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc)
+{
+	return desc->baSourceID[desc->bNrInPins + 4];
+}
+
+static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc)
+{
+	return &desc->baSourceID[desc->bNrInPins + 5];
+}
+
+static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc)
+{
+	__u8 control_size = uac_processing_unit_bControlSize(desc);
+	return desc->baSourceID[desc->bNrInPins + control_size];
+}
+
+static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc)
+{
+	__u8 control_size = uac_processing_unit_bControlSize(desc);
+	return &desc->baSourceID[desc->bNrInPins + control_size + 1];
+}
+
 /* 4.5.2 Class-Specific AS Interface Descriptor */
 struct uac_as_header_descriptor_v1 {
 	__u8  bLength;			/* in bytes: 7 */
@@ -315,16 +434,6 @@ struct uac_iso_endpoint_descriptor {
 
 /* A.10.2 Feature Unit Control Selectors */
 
-struct uac_feature_unit_descriptor {
-	__u8 bLength;
-	__u8 bDescriptorType;
-	__u8 bDescriptorSubtype;
-	__u8 bUnitID;
-	__u8 bSourceID;
-	__u8 bControlSize;
-	__u8 controls[0]; /* variable length */
-} __attribute__((packed));
-
 #define UAC_FU_CONTROL_UNDEFINED	0x00
 #define UAC_MUTE_CONTROL		0x01
 #define UAC_VOLUME_CONTROL		0x02
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4e7c2fd..994b038 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -860,13 +860,14 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
 	return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
 }
 
-static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 			      unsigned int ctl_mask, int control,
 			      struct usb_audio_term *iterm, int unitid)
 {
+	struct uac_feature_unit_descriptor *desc = raw_desc;
 	unsigned int len = 0;
 	int mapped_name = 0;
-	int nameid = desc[desc[0] - 1];
+	int nameid = uac_feature_unit_iFeature(desc);
 	struct snd_kcontrol *kctl;
 	struct usb_mixer_elem_info *cval;
 	const struct usbmix_name_map *map;
@@ -1032,7 +1033,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 
 	channels = (ftr->bLength - 7) / csize - 1;
 
-	master_bits = snd_usb_combine_bytes(ftr->controls, csize);
+	master_bits = snd_usb_combine_bytes(ftr->bmaControls, csize);
 	/* master configuration quirks */
 	switch (state->chip->usb_id) {
 	case USB_ID(0x08bb, 0x2702):
@@ -1043,14 +1044,14 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 		break;
 	}
 	if (channels > 0)
-		first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize);
+		first_ch_bits = snd_usb_combine_bytes(ftr->bmaControls + csize, csize);
 	else
 		first_ch_bits = 0;
 	/* check all control types */
 	for (i = 0; i < 10; i++) {
 		unsigned int ch_bits = 0;
 		for (j = 0; j < channels; j++) {
-			unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize);
+			unsigned int mask = snd_usb_combine_bytes(ftr->bmaControls + csize * (j+1), csize);
 			if (mask & (1 << i))
 				ch_bits |= (1 << j);
 		}
@@ -1075,13 +1076,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
  * input channel number (zero based) is given in control field instead.
  */
 
-static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_mixer_unit_ctl(struct mixer_build *state,
+				 struct uac_mixer_unit_descriptor *desc,
 				 int in_pin, int in_ch, int unitid,
 				 struct usb_audio_term *iterm)
 {
 	struct usb_mixer_elem_info *cval;
-	unsigned int input_pins = desc[4];
-	unsigned int num_outs = desc[5 + input_pins];
+	unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
 	unsigned int i, len;
 	struct snd_kcontrol *kctl;
 	const struct usbmix_name_map *map;
@@ -1099,7 +1100,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 	cval->control = in_ch + 1; /* based on 1 */
 	cval->val_type = USB_MIXER_S16;
 	for (i = 0; i < num_outs; i++) {
-		if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) {
+		if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), in_ch, i, num_outs)) {
 			cval->cmask |= (1 << i);
 			cval->channels++;
 		}
@@ -1132,18 +1133,19 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 /*
  * parse a mixer unit
  */
-static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
+	struct uac_mixer_unit_descriptor *desc = raw_desc;
 	struct usb_audio_term iterm;
 	int input_pins, num_ins, num_outs;
 	int pin, ich, err;
 
-	if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) {
+	if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) {
 		snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid);
 		return -EINVAL;
 	}
 	/* no bmControls field (e.g. Maya44) -> ignore */
-	if (desc[0] <= 10 + input_pins) {
+	if (desc->bLength <= 10 + input_pins) {
 		snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid);
 		return 0;
 	}
@@ -1151,10 +1153,10 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
 	num_ins = 0;
 	ich = 0;
 	for (pin = 0; pin < input_pins; pin++) {
-		err = parse_audio_unit(state, desc[5 + pin]);
+		err = parse_audio_unit(state, desc->baSourceID[pin]);
 		if (err < 0)
 			return err;
-		err = check_input_term(state, desc[5 + pin], &iterm);
+		err = check_input_term(state, desc->baSourceID[pin], &iterm);
 		if (err < 0)
 			return err;
 		num_ins += iterm.channels;
@@ -1162,7 +1164,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
 			int och, ich_has_controls = 0;
 
 			for (och = 0; och < num_outs; ++och) {
-				if (check_matrix_bitmap(desc + 9 + input_pins,
+				if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc),
 							ich, och, num_outs)) {
 					ich_has_controls = 1;
 					break;
@@ -1323,9 +1325,10 @@ static struct procunit_info extunits[] = {
 /*
  * build a processing/extension unit
  */
-static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name)
+static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name)
 {
-	int num_ins = dsc[6];
+	struct uac_processing_unit_descriptor *desc = raw_desc;
+	int num_ins = desc->bNrInPins;
 	struct usb_mixer_elem_info *cval;
 	struct snd_kcontrol *kctl;
 	int i, err, nameid, type, len;
@@ -1340,17 +1343,17 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 		0, NULL, default_value_info
 	};
 
-	if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) {
+	if (desc->bLength < 13 || desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc)) {
 		snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
 		return -EINVAL;
 	}
 
 	for (i = 0; i < num_ins; i++) {
-		if ((err = parse_audio_unit(state, dsc[7 + i])) < 0)
+		if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
 			return err;
 	}
 
-	type = combine_word(&dsc[4]);
+	type = le16_to_cpu(desc->wProcessType);
 	for (info = list; info && info->type; info++)
 		if (info->type == type)
 			break;
@@ -1358,8 +1361,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 		info = &default_info;
 
 	for (valinfo = info->values; valinfo->control; valinfo++) {
-		/* FIXME: bitmap might be longer than 8bit */
-		if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
+		__u8 *controls = uac_processing_unit_bmControls(desc);
+
+		if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
 			continue;
 		map = find_map(state, unitid, valinfo->control);
 		if (check_ignored_ctl(map))
@@ -1377,9 +1381,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 
 		/* get min/max values */
 		if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
+			__u8 *control_spec = uac_processing_unit_specific(desc);
 			/* FIXME: hard-coded */
 			cval->min = 1;
-			cval->max = dsc[15];
+			cval->max = control_spec[0];
 			cval->res = 1;
 			cval->initialized = 1;
 		} else {
@@ -1409,7 +1414,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 		else if (info->name)
 			strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
 		else {
-			nameid = dsc[12 + num_ins + dsc[11 + num_ins]];
+			nameid = uac_processing_unit_iProcessing(desc);
 			len = 0;
 			if (nameid)
 				len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
@@ -1428,14 +1433,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 }
 
 
-static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-	return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit");
+	return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit");
 }
 
-static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-	return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
+	/* Note that we parse extension units with processing unit descriptors.
+	 * That's ok as the layout is the same */
+	return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit");
 }
 
 
@@ -1537,9 +1544,9 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
 /*
  * parse a selector unit
  */
-static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-	unsigned int num_ins = desc[4];
+	struct uac_selector_unit_descriptor *desc = raw_desc;
 	unsigned int i, nameid, len;
 	int err;
 	struct usb_mixer_elem_info *cval;
@@ -1547,17 +1554,17 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	const struct usbmix_name_map *map;
 	char **namelist;
 
-	if (! num_ins || desc[0] < 5 + num_ins) {
+	if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) {
 		snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid);
 		return -EINVAL;
 	}
 
-	for (i = 0; i < num_ins; i++) {
-		if ((err = parse_audio_unit(state, desc[5 + i])) < 0)
+	for (i = 0; i < desc->bNrInPins; i++) {
+		if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
 			return err;
 	}
 
-	if (num_ins == 1) /* only one ? nonsense! */
+	if (desc->bNrInPins == 1) /* only one ? nonsense! */
 		return 0;
 
 	map = find_map(state, unitid, 0);
@@ -1574,18 +1581,18 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	cval->val_type = USB_MIXER_U8;
 	cval->channels = 1;
 	cval->min = 1;
-	cval->max = num_ins;
+	cval->max = desc->bNrInPins;
 	cval->res = 1;
 	cval->initialized = 1;
 
-	namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL);
+	namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
 	if (! namelist) {
 		snd_printk(KERN_ERR "cannot malloc\n");
 		kfree(cval);
 		return -ENOMEM;
 	}
 #define MAX_ITEM_NAME_LEN	64
-	for (i = 0; i < num_ins; i++) {
+	for (i = 0; i < desc->bNrInPins; i++) {
 		struct usb_audio_term iterm;
 		len = 0;
 		namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
@@ -1599,7 +1606,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 		}
 		len = check_mapped_selector_name(state, unitid, i, namelist[i],
 						 MAX_ITEM_NAME_LEN);
-		if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0)
+		if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
 			len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
 		if (! len)
 			sprintf(namelist[i], "Input %d", i);
@@ -1615,7 +1622,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	kctl->private_value = (unsigned long)namelist;
 	kctl->private_free = usb_mixer_selector_elem_free;
 
-	nameid = desc[desc[0] - 1];
+	nameid = uac_selector_unit_iSelector(desc);
 	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
 	if (len)
 		;
@@ -1634,7 +1641,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	}
 
 	snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
-		    cval->id, kctl->id.name, num_ins);
+		    cval->id, kctl->id.name, desc->bNrInPins);
 	if ((err = add_control_to_empty(state, kctl)) < 0)
 		return err;
 
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/6] ALSA: usb-mixer: Add support for Audio Class v2.0
  2010-03-11 20:13         ` [PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs Daniel Mack
@ 2010-03-11 20:13           ` Daniel Mack
  0 siblings, 0 replies; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, clemens

USB Audio Class v2.0 compliant devices have different descriptors and a
different way of setting/getting min/max/res/cur properties. This patch
adds support for them.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Cc: Takashi Iwai <tiwai@suse.de>
---
 include/linux/usb/audio-v2.h |   47 ++++++
 include/linux/usb/audio.h    |   71 +++++++---
 sound/usb/mixer.c            |  322 +++++++++++++++++++++++++++++++-----------
 sound/usb/mixer.h            |    3 +
 4 files changed, 343 insertions(+), 100 deletions(-)

diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
index 3b8560d..0952231 100644
--- a/include/linux/usb/audio-v2.h
+++ b/include/linux/usb/audio-v2.h
@@ -43,6 +43,53 @@ struct uac_clock_selector_descriptor {
 	__u8 baCSourceID[];
 } __attribute__((packed));
 
+/* 4.7.2.4 Input terminal descriptor */
+
+struct uac2_input_terminal_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bCSourceID;
+	__u8 bNrChannels;
+	__u32 bmChannelConfig;
+	__u8 iChannelNames;
+	__u16 bmControls;
+	__u8 iTerminal;
+} __attribute__((packed));
+
+/* 4.7.2.5 Output terminal descriptor */
+
+struct uac2_output_terminal_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bSourceID;
+	__u8 bCSourceID;
+	__u16 bmControls;
+	__u8 iTerminal;
+} __attribute__((packed));
+
+
+
+/* 4.7.2.8 Feature Unit Descriptor */
+
+struct uac2_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 */
+} __attribute__((packed));
+
 /* 4.9.2 Class-Specific AS Interface Descriptor */
 
 struct uac_as_header_descriptor_v2 {
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index bc78a83..905a87c 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -196,20 +196,33 @@ static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor *
 	return desc->baSourceID[desc->bNrInPins];
 }
 
-static inline __u16 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc)
+static inline __u32 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc,
+						  int protocol)
 {
-	return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
-		desc->baSourceID[desc->bNrInPins + 1];
+	if (protocol == UAC_VERSION_1)
+		return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+			desc->baSourceID[desc->bNrInPins + 1];
+	else
+		return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+			(desc->baSourceID[desc->bNrInPins + 3] << 16) |
+			(desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+			(desc->baSourceID[desc->bNrInPins + 1]);
 }
 
-static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc)
+static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc,
+						int protocol)
 {
-	return desc->baSourceID[desc->bNrInPins + 3];
+	return (protocol == UAC_VERSION_1) ?
+		desc->baSourceID[desc->bNrInPins + 3] :
+		desc->baSourceID[desc->bNrInPins + 5];
 }
 
-static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc)
+static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
+					      int protocol)
 {
-	return &desc->baSourceID[desc->bNrInPins + 4];
+	return (protocol == UAC_VERSION_1) ?
+		&desc->baSourceID[desc->bNrInPins + 4] :
+		&desc->baSourceID[desc->bNrInPins + 6];
 }
 
 static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
@@ -267,36 +280,54 @@ static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_de
 	return desc->baSourceID[desc->bNrInPins];
 }
 
-static inline __u16 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc)
+static inline __u32 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc,
+						       int protocol)
 {
-	return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
-		desc->baSourceID[desc->bNrInPins + 1];
+	if (protocol == UAC_VERSION_1)
+		return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+			desc->baSourceID[desc->bNrInPins + 1];
+	else
+		return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+			(desc->baSourceID[desc->bNrInPins + 3] << 16) |
+			(desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+			(desc->baSourceID[desc->bNrInPins + 1]);
 }
 
-static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc)
+static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc,
+						     int protocol)
 {
-	return desc->baSourceID[desc->bNrInPins + 3];
+	return (protocol == UAC_VERSION_1) ?
+		desc->baSourceID[desc->bNrInPins + 3] :
+		desc->baSourceID[desc->bNrInPins + 5];
 }
 
-static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc)
+static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
+						    int protocol)
 {
-	return desc->baSourceID[desc->bNrInPins + 4];
+	return (protocol == UAC_VERSION_1) ?
+		desc->baSourceID[desc->bNrInPins + 4] :
+		desc->baSourceID[desc->bNrInPins + 6];
 }
 
-static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc)
+static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
+						   int protocol)
 {
-	return &desc->baSourceID[desc->bNrInPins + 5];
+	return (protocol == UAC_VERSION_1) ?
+		&desc->baSourceID[desc->bNrInPins + 5] :
+		&desc->baSourceID[desc->bNrInPins + 7];
 }
 
-static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc)
+static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
+						   int protocol)
 {
-	__u8 control_size = uac_processing_unit_bControlSize(desc);
+	__u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
 	return desc->baSourceID[desc->bNrInPins + control_size];
 }
 
-static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc)
+static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc,
+						 int protocol)
 {
-	__u8 control_size = uac_processing_unit_bControlSize(desc);
+	__u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
 	return &desc->baSourceID[desc->bNrInPins + control_size + 1];
 }
 
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 994b038..1deef62 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -33,6 +33,7 @@
 #include <linux/string.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
@@ -197,6 +198,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
 
 /*
  * find an audio control unit with the given unit id
+ * this doesn't return any clock related units, so they need to be handled elsewhere
  */
 static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
 {
@@ -205,7 +207,7 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
 	p = NULL;
 	while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
 				      USB_DT_CS_INTERFACE)) != NULL) {
-		if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit)
+		if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
 			return p;
 	}
 	return NULL;
@@ -302,7 +304,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
  * retrieve a mixer value
  */
 
-static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
 {
 	unsigned char buf[2];
 	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -324,6 +326,58 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 	return -EINVAL;
 }
 
+static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+	unsigned char buf[14]; /* enough space for one range of 4 bytes */
+	unsigned char *val;
+	int ret;
+	__u8 bRequest;
+
+	bRequest = (request == UAC_GET_CUR) ?
+		UAC2_CS_CUR : UAC2_CS_RANGE;
+
+	ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
+			      usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
+			      bRequest,
+			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+			      validx, cval->mixer->ctrlif | (cval->id << 8),
+			      buf, sizeof(buf), 1000);
+
+	if (ret < 0) {
+		snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+			    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+		return ret;
+	}
+
+	switch (request) {
+	case UAC_GET_CUR:
+		val = buf;
+		break;
+	case UAC_GET_MIN:
+		val = buf + sizeof(__u16);
+		break;
+	case UAC_GET_MAX:
+		val = buf + sizeof(__u16) * 2;
+		break;
+	case UAC_GET_RES:
+		val = buf + sizeof(__u16) * 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16)));
+
+	return 0;
+}
+
+static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+	return (cval->mixer->protocol == UAC_VERSION_1) ?
+		get_ctl_value_v1(cval, request, validx, value_ret) :
+		get_ctl_value_v2(cval, request, validx, value_ret);
+}
+
 static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value)
 {
 	return get_ctl_value(cval, UAC_GET_CUR, validx, value);
@@ -348,8 +402,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
 	err = get_cur_mix_raw(cval, channel, value);
 	if (err < 0) {
 		if (!cval->mixer->ignore_ctl_error)
-			snd_printd(KERN_ERR "cannot get current value for "
-				   "control %d ch %d: err = %d\n",
+			snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n",
 				   cval->control, channel, err);
 		return err;
 	}
@@ -367,8 +420,22 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
 				int request, int validx, int value_set)
 {
 	unsigned char buf[2];
-	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
-	int timeout = 10;
+	int val_len, timeout = 10;
+
+	if (cval->mixer->protocol == UAC_VERSION_1) {
+		val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
+	} else { /* UAC_VERSION_2 */
+		/* audio class v2 controls are always 2 bytes in size */
+		val_len = sizeof(__u16);
+
+		/* FIXME */
+		if (request != UAC_SET_CUR) {
+			snd_printdd(KERN_WARNING "RANGE setting not yet supported\n");
+			return -EINVAL;
+		}
+
+		request = UAC2_CS_CUR;
+	}
 
 	value_set = convert_bytes_value(cval, value_set);
 	buf[0] = value_set & 0xff;
@@ -564,46 +631,65 @@ 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)
 {
-	unsigned char *p1;
+	void *p1;
 
 	memset(term, 0, sizeof(*term));
 	while ((p1 = find_audio_control_unit(state, id)) != NULL) {
+		unsigned char *hdr = p1;
 		term->id = id;
-		switch (p1[2]) {
+		switch (hdr[2]) {
 		case UAC_INPUT_TERMINAL:
-			term->type = combine_word(p1 + 4);
-			term->channels = p1[7];
-			term->chconfig = combine_word(p1 + 8);
-			term->name = p1[11];
+			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;
+				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:
-			id = p1[4];
+		case UAC_FEATURE_UNIT: {
+			/* the header is the same for v1 and v2 */
+			struct uac_feature_unit_descriptor *d = p1;
+			id = d->bUnitID;
 			break; /* continue to parse */
-		case UAC_MIXER_UNIT:
-			term->type = p1[2] << 16; /* virtual type */
-			term->channels = p1[5 + p1[4]];
-			term->chconfig = combine_word(p1 + 6 + p1[4]);
-			term->name = p1[p1[0] - 1];
+		}
+		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 UAC_SELECTOR_UNIT: {
+			struct uac_selector_unit_descriptor *d = p1;
 			/* call recursively to retrieve the channel info */
-			if (check_input_term(state, p1[5], term) < 0)
+			if (check_input_term(state, d->baSourceID[0], term) < 0)
 				return -ENODEV;
-			term->type = p1[2] << 16; /* virtual type */
+			term->type = d->bDescriptorSubtype << 16; /* virtual type */
 			term->id = id;
-			term->name = p1[9 + p1[0] - 1];
+			term->name = uac_selector_unit_iSelector(d);
 			return 0;
+		}
 		case UAC_PROCESSING_UNIT_V1:
-		case UAC_EXTENSION_UNIT_V1:
-			if (p1[6] == 1) {
-				id = p1[7];
+		case UAC_EXTENSION_UNIT_V1: {
+			struct uac_processing_unit_descriptor *d = p1;
+			if (d->bNrInPins) {
+				id = d->baSourceID[0];
 				break; /* continue to parse */
 			}
-			term->type = p1[2] << 16; /* virtual type */
-			term->channels = p1[7 + p1[6]];
-			term->chconfig = combine_word(p1 + 8 + p1[6]);
-			term->name = p1[12 + p1[6] + p1[11 + p1[6]]];
+			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;
+		}
 		default:
 			return -ENODEV;
 		}
@@ -850,6 +936,15 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = {
 	.put = mixer_ctl_feature_put,
 };
 
+/* the read-only variant */
+static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "", /* will be filled later manually */
+	.info = mixer_ctl_feature_info,
+	.get = mixer_ctl_feature_get,
+	.put = NULL,
+};
+
 
 /*
  * build a feature control
@@ -862,7 +957,8 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
 
 static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 			      unsigned int ctl_mask, int control,
-			      struct usb_audio_term *iterm, int unitid)
+			      struct usb_audio_term *iterm, int unitid,
+			      int read_only)
 {
 	struct uac_feature_unit_descriptor *desc = raw_desc;
 	unsigned int len = 0;
@@ -906,7 +1002,11 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 	/* get min/max values */
 	get_min_max(cval, 0);
 
-	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+	if (read_only)
+		kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+	else
+		kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+
 	if (! kctl) {
 		snd_printk(KERN_ERR "cannot malloc kcontrol\n");
 		kfree(cval);
@@ -1016,24 +1116,34 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 	struct usb_audio_term iterm;
 	unsigned int master_bits, first_ch_bits;
 	int err, csize;
-	struct uac_feature_unit_descriptor *ftr = _ftr;
+	struct uac_feature_unit_descriptor *hdr = _ftr;
+	__u8 *bmaControls;
+
+	if (state->mixer->protocol == UAC_VERSION_1) {
+		csize = hdr->bControlSize;
+		channels = (hdr->bLength - 7) / csize - 1;
+		bmaControls = hdr->bmaControls;
+	} else {
+		struct uac2_feature_unit_descriptor *ftr = _ftr;
+		csize = 4;
+		channels = (hdr->bLength - 6) / 4;
+		bmaControls = ftr->bmaControls;
+	}
 
-	if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) {
+	if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) {
 		snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid);
 		return -EINVAL;
 	}
 
 	/* parse the source unit */
-	if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0)
+	if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
 		return err;
 
 	/* determine the input source type and name */
-	if (check_input_term(state, ftr->bSourceID, &iterm) < 0)
+	if (check_input_term(state, hdr->bSourceID, &iterm) < 0)
 		return -EINVAL;
 
-	channels = (ftr->bLength - 7) / csize - 1;
-
-	master_bits = snd_usb_combine_bytes(ftr->bmaControls, csize);
+	master_bits = snd_usb_combine_bytes(bmaControls, csize);
 	/* master configuration quirks */
 	switch (state->chip->usb_id) {
 	case USB_ID(0x08bb, 0x2702):
@@ -1044,21 +1154,54 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 		break;
 	}
 	if (channels > 0)
-		first_ch_bits = snd_usb_combine_bytes(ftr->bmaControls + csize, csize);
+		first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
 	else
 		first_ch_bits = 0;
-	/* check all control types */
-	for (i = 0; i < 10; i++) {
-		unsigned int ch_bits = 0;
-		for (j = 0; j < channels; j++) {
-			unsigned int mask = snd_usb_combine_bytes(ftr->bmaControls + csize * (j+1), csize);
-			if (mask & (1 << i))
-				ch_bits |= (1 << j);
+
+	if (state->mixer->protocol == UAC_VERSION_1) {
+		/* check all control types */
+		for (i = 0; i < 10; i++) {
+			unsigned int ch_bits = 0;
+			for (j = 0; j < channels; j++) {
+				unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+				if (mask & (1 << i))
+					ch_bits |= (1 << j);
+			}
+			/* audio class v1 controls are never read-only */
+			if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+				build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0);
+			if (master_bits & (1 << i))
+				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
+		}
+	} else { /* UAC_VERSION_2 */
+		for (i = 0; i < 30/2; i++) {
+			/* From the USB Audio spec v2.0:
+			   bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
+			   each containing a set of bit pairs. If a Control is present,
+			   it must be Host readable. If a certain Control is not
+			   present then the bit pair must be set to 0b00.
+			   If a Control is present but read-only, the bit pair must be
+			   set to 0b01. If a Control is also Host programmable, the bit
+			   pair must be set to 0b11. The value 0b10 is not allowed. */
+			unsigned int ch_bits = 0;
+			unsigned int ch_read_only = 0;
+
+			for (j = 0; j < channels; j++) {
+				unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+				if (mask & (1 << (i * 2))) {
+					ch_bits |= (1 << j);
+					if (~mask & (1 << ((i * 2) + 1)))
+						ch_read_only |= (1 << j);
+				}
+			}
+
+			/* FIXME: the whole unit is read-only if any of the channels is marked read-only */
+			if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+				build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only);
+			if (master_bits & (1 << i * 2))
+				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
+						  ~master_bits & (1 << ((i * 2) + 1)));
 		}
-		if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
-			build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid);
-		if (master_bits & (1 << i))
-			build_feature_ctl(state, _ftr, 0, i, &iterm, unitid);
 	}
 
 	return 0;
@@ -1100,7 +1243,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
 	cval->control = in_ch + 1; /* based on 1 */
 	cval->val_type = USB_MIXER_S16;
 	for (i = 0; i < num_outs; i++) {
-		if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), in_ch, i, num_outs)) {
+		if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) {
 			cval->cmask |= (1 << i);
 			cval->channels++;
 		}
@@ -1164,7 +1307,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
 			int och, ich_has_controls = 0;
 
 			for (och = 0; och < num_outs; ++och) {
-				if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc),
+				if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol),
 							ich, och, num_outs)) {
 					ich_has_controls = 1;
 					break;
@@ -1343,7 +1486,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
 		0, NULL, default_value_info
 	};
 
-	if (desc->bLength < 13 || desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc)) {
+	if (desc->bLength < 13 || desc->bLength < 13 + num_ins ||
+	    desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
 		snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
 		return -EINVAL;
 	}
@@ -1361,7 +1505,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
 		info = &default_info;
 
 	for (valinfo = info->values; valinfo->control; valinfo++) {
-		__u8 *controls = uac_processing_unit_bmControls(desc);
+		__u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol);
 
 		if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
 			continue;
@@ -1381,7 +1525,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
 
 		/* get min/max values */
 		if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
-			__u8 *control_spec = uac_processing_unit_specific(desc);
+			__u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
 			/* FIXME: hard-coded */
 			cval->min = 1;
 			cval->max = control_spec[0];
@@ -1414,7 +1558,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
 		else if (info->name)
 			strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
 		else {
-			nameid = uac_processing_unit_iProcessing(desc);
+			nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
 			len = 0;
 			if (nameid)
 				len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
@@ -1676,9 +1820,17 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
 	case UAC_FEATURE_UNIT:
 		return parse_audio_feature_unit(state, unitid, p1);
 	case UAC_PROCESSING_UNIT_V1:
-		return parse_audio_processing_unit(state, unitid, p1);
+	/*   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 UAC_EXTENSION_UNIT_V1:
-		return parse_audio_extension_unit(state, unitid, p1);
+	/*   UAC2_PROCESSING_UNIT_V2 has the same value */
+		if (state->mixer->protocol == UAC_VERSION_1)
+			return parse_audio_extension_unit(state, unitid, p1);
+		else /* UAC_VERSION_2 */
+			return parse_audio_processing_unit(state, unitid, p1);
 	default:
 		snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
 		return -EINVAL;
@@ -1711,11 +1863,11 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
  */
 static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 {
-	struct uac_output_terminal_descriptor_v1 *desc;
 	struct mixer_build state;
 	int err;
 	const struct usbmix_ctl_map *map;
 	struct usb_host_interface *hostif;
+	void *p;
 
 	hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
 	memset(&state, 0, sizeof(state));
@@ -1734,18 +1886,35 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 		}
 	}
 
-	desc = NULL;
-	while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) {
-		if (desc->bLength < 9)
-			continue; /* invalid descriptor? */
-		set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
-		state.oterm.id = desc->bTerminalID;
-		state.oterm.type = le16_to_cpu(desc->wTerminalType);
-		state.oterm.name = desc->iTerminal;
-		err = parse_audio_unit(&state, desc->bSourceID);
-		if (err < 0)
-			return err;
+	p = NULL;
+	while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
+		if (mixer->protocol == UAC_VERSION_1) {
+			struct uac_output_terminal_descriptor_v1 *desc = p;
+
+			if (desc->bLength < sizeof(*desc))
+				continue; /* invalid descriptor? */
+			set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+			state.oterm.id = desc->bTerminalID;
+			state.oterm.type = le16_to_cpu(desc->wTerminalType);
+			state.oterm.name = desc->iTerminal;
+			err = parse_audio_unit(&state, desc->bSourceID);
+			if (err < 0)
+				return err;
+		} else { /* UAC_VERSION_2 */
+			struct uac2_output_terminal_descriptor *desc = p;
+
+			if (desc->bLength < sizeof(*desc))
+				continue; /* invalid descriptor? */
+			set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+			state.oterm.id = desc->bTerminalID;
+			state.oterm.type = le16_to_cpu(desc->wTerminalType);
+			state.oterm.name = desc->iTerminal;
+			err = parse_audio_unit(&state, desc->bSourceID);
+			if (err < 0)
+				return err;
+		}
 	}
+
 	return 0;
 }
 
@@ -1868,7 +2037,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 	struct usb_mixer_interface *mixer;
 	struct snd_info_entry *entry;
 	struct usb_host_interface *host_iface;
-	int err, protocol;
+	int err;
 
 	strcpy(chip->card->mixername, "USB Mixer");
 
@@ -1886,14 +2055,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
 	}
 
 	host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
-	protocol = host_iface->desc.bInterfaceProtocol;
-
-	/* FIXME! */
-	if (protocol != UAC_VERSION_1) {
-		snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n",
-					protocol);
-		return 0;
-	}
+	mixer->protocol = host_iface->desc.bInterfaceProtocol;
 
 	if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
 	    (err = snd_usb_mixer_status_create(mixer)) < 0)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 63101ae..1301238 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -10,6 +10,9 @@ struct usb_mixer_interface {
 	/* array[MAX_ID_ELEMS], indexed by unit id */
 	struct usb_mixer_elem_info **id_elems;
 
+	/* the usb audio specification version this interface complies to */
+	int protocol;
+
 	/* Sound Blaster remote control stuff */
 	const struct rc_config *rc_cfg;
 	u32 rc_code;
-- 
1.6.6.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] linux/usb/audio.h: split header
  2010-03-11 20:13 ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
  2010-03-11 20:13   ` [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h Daniel Mack
@ 2010-03-11 20:17   ` Daniel Mack
  2010-03-12  2:55     ` Greg KH
  1 sibling, 1 reply; 10+ messages in thread
From: Daniel Mack @ 2010-03-11 20:17 UTC (permalink / raw)
  To: Greg KH; +Cc: tiwai, alsa-devel, clemens

On Thu, Mar 11, 2010 at 09:13:20PM +0100, Daniel Mack wrote:
> - Split the audio.h file in two to clearly denote the differences
>   between the standards.
> - Add many more defines to audio-v2.h. Most of them are not currently
>   used.
> - Replaced a magic value with a proper define
> 
> Signed-off-by: Daniel Mack <daniel@caiaq.de>
> Cc: Clemens Ladisch <clemens@ladisch.de>
> Cc: Takashi Iwai <tiwai@suse.de>

Sorry Greg, I forgot to Cc: you on this.

As the patch touch both the include files and audio code, it would go
thru the ALSA channel again if you don't mind.

Thanks,
Daniel


> ---
>  include/linux/usb/audio-v2.h |  319 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/usb/audio.h    |   50 +------
>  sound/usb/card.c             |    3 +-
>  sound/usb/endpoint.c         |    1 +
>  sound/usb/format.c           |    1 +
>  sound/usb/pcm.c              |    5 +-
>  6 files changed, 333 insertions(+), 46 deletions(-)
>  create mode 100644 include/linux/usb/audio-v2.h
> 
> diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
> new file mode 100644
> index 0000000..3b8560d
> --- /dev/null
> +++ b/include/linux/usb/audio-v2.h
> @@ -0,0 +1,319 @@
> +/*
> + * Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
> + *
> + * This software is distributed under the terms of the GNU General Public
> + * License ("GPL") version 2, as published by the Free Software Foundation.
> + *
> + * This file holds USB constants and structures defined
> + * by the USB Device Class Definition for Audio Devices in version 2.0.
> + * Comments below reference relevant sections of the documents contained
> + * in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
> + */
> +
> +#ifndef __LINUX_USB_AUDIO_V2_H
> +#define __LINUX_USB_AUDIO_V2_H
> +
> +#include <linux/types.h>
> +
> +/* v1.0 and v2.0 of this standard have many things in common. For the rest
> + * of the definitions, please refer to audio.h */
> +
> +/* 4.7.2.1 Clock Source Descriptor */
> +
> +struct uac_clock_source_descriptor {
> +	__u8 bLength;
> +	__u8 bDescriptorType;
> +	__u8 bDescriptorSubtype;
> +	__u8 bClockID;
> +	__u8 bmAttributes;
> +	__u8 bmControls;
> +	__u8 bAssocTerminal;
> +	__u8 iClockSource;
> +} __attribute__((packed));
> +
> +/* 4.7.2.2 Clock Source Descriptor */
> +
> +struct uac_clock_selector_descriptor {
> +	__u8 bLength;
> +	__u8 bDescriptorType;
> +	__u8 bDescriptorSubtype;
> +	__u8 bClockID;
> +	__u8 bNrInPins;
> +	__u8 bmControls;
> +	__u8 baCSourceID[];
> +} __attribute__((packed));
> +
> +/* 4.9.2 Class-Specific AS Interface Descriptor */
> +
> +struct uac_as_header_descriptor_v2 {
> +	__u8 bLength;
> +	__u8 bDescriptorType;
> +	__u8 bDescriptorSubtype;
> +	__u8 bTerminalLink;
> +	__u8 bmControls;
> +	__u8 bFormatType;
> +	__u32 bmFormats;
> +	__u8 bNrChannels;
> +	__u32 bmChannelConfig;
> +	__u8 iChannelNames;
> +} __attribute__((packed));
> +
> +
> +/* A.7 Audio Function Category Codes */
> +#define UAC2_FUNCTION_SUBCLASS_UNDEFINED	0x00
> +#define UAC2_FUNCTION_DESKTOP_SPEAKER		0x01
> +#define UAC2_FUNCTION_HOME_THEATER		0x02
> +#define UAC2_FUNCTION_MICROPHONE		0x03
> +#define UAC2_FUNCTION_HEADSET			0x04
> +#define UAC2_FUNCTION_TELEPHONE			0x05
> +#define UAC2_FUNCTION_CONVERTER			0x06
> +#define UAC2_FUNCTION_SOUND_RECORDER		0x07
> +#define UAC2_FUNCTION_IO_BOX			0x08
> +#define UAC2_FUNCTION_MUSICAL_INSTRUMENT	0x09
> +#define UAC2_FUNCTION_PRO_AUDIO			0x0a
> +#define UAC2_FUNCTION_AUDIO_VIDEO		0x0b
> +#define UAC2_FUNCTION_CONTROL_PANEL		0x0c
> +#define UAC2_FUNCTION_OTHER			0xff
> +
> +/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
> +/* see audio.h for the rest, which is identical to v1 */
> +#define UAC2_EFFECT_UNIT			0x07
> +#define UAC2_PROCESSING_UNIT_V2		0x08
> +#define UAC2_EXTENSION_UNIT_V2		0x09
> +#define UAC2_CLOCK_SOURCE		0x0a
> +#define UAC2_CLOCK_SELECTOR		0x0b
> +#define UAC2_CLOCK_MULTIPLIER		0x0c
> +#define UAC2_SAMPLE_RATE_CONVERTER	0x0d
> +
> +/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
> +/* see audio.h for the rest, which is identical to v1 */
> +#define UAC2_ENCODER			0x03
> +#define UAC2_DECODER			0x04
> +
> +/* A.11 Effect Unit Effect Types */
> +#define UAC2_EFFECT_UNDEFINED		0x00
> +#define UAC2_EFFECT_PARAM_EQ		0x01
> +#define UAC2_EFFECT_REVERB		0x02
> +#define UAC2_EFFECT_MOD_DELAY		0x03
> +#define UAC2_EFFECT_DYN_RANGE_COMP	0x04
> +
> +/* A.12 Processing Unit Process Types */
> +#define UAC2_PROCESS_UNDEFINED		0x00
> +#define UAC2_PROCESS_UP_DOWNMIX		0x01
> +#define UAC2_PROCESS_DOLBY_PROLOCIC	0x02
> +#define UAC2_PROCESS_STEREO_EXTENDER	0x03
> +
> +/* A.14 Audio Class-Specific Request Codes */
> +#define UAC2_CS_CUR			0x01
> +#define UAC2_CS_RANGE			0x02
> +
> +/* A.15 Encoder Type Codes */
> +#define UAC2_ENCODER_UNDEFINED		0x00
> +#define UAC2_ENCODER_OTHER		0x01
> +#define UAC2_ENCODER_MPEG		0x02
> +#define UAC2_ENCODER_AC3		0x03
> +#define UAC2_ENCODER_WMA		0x04
> +#define UAC2_ENCODER_DTS		0x05
> +
> +/* A.16 Decoder Type Codes */
> +#define UAC2_DECODER_UNDEFINED		0x00
> +#define UAC2_DECODER_OTHER		0x01
> +#define UAC2_DECODER_MPEG		0x02
> +#define UAC2_DECODER_AC3		0x03
> +#define UAC2_DECODER_WMA		0x04
> +#define UAC2_DECODER_DTS		0x05
> +
> +/* A.17.1 Clock Source Control Selectors */
> +#define UAC2_CS_UNDEFINED		0x00
> +#define UAC2_CS_CONTROL_SAM_FREQ	0x01
> +#define UAC2_CS_CONTROL_CLOCK_VALID	0x02
> +
> +/* A.17.2 Clock Selector Control Selectors */
> +#define UAC2_CX_UNDEFINED		0x00
> +#define UAC2_CX_CLOCK_SELECTOR		0x01
> +
> +/* A.17.3 Clock Multiplier Control Selectors */
> +#define UAC2_CM_UNDEFINED		0x00
> +#define UAC2_CM_NUMERATOR		0x01
> +#define UAC2_CM_DENOMINTATOR		0x02
> +
> +/* A.17.4 Terminal Control Selectors */
> +#define UAC2_TE_UNDEFINED		0x00
> +#define UAC2_TE_COPY_PROTECT		0x01
> +#define UAC2_TE_CONNECTOR		0x02
> +#define UAC2_TE_OVERLOAD		0x03
> +#define UAC2_TE_CLUSTER			0x04
> +#define UAC2_TE_UNDERFLOW		0x05
> +#define UAC2_TE_OVERFLOW		0x06
> +#define UAC2_TE_LATENCY			0x07
> +
> +/* A.17.5 Mixer Control Selectors */
> +#define UAC2_MU_UNDEFINED		0x00
> +#define UAC2_MU_MIXER			0x01
> +#define UAC2_MU_CLUSTER			0x02
> +#define UAC2_MU_UNDERFLOW		0x03
> +#define UAC2_MU_OVERFLOW		0x04
> +#define UAC2_MU_LATENCY			0x05
> +
> +/* A.17.6 Selector Control Selectors */
> +#define UAC2_SU_UNDEFINED		0x00
> +#define UAC2_SU_SELECTOR		0x01
> +#define UAC2_SU_LATENCY			0x02
> +
> +/* A.17.7 Feature Unit Control Selectors */
> +/* see audio.h for the rest, which is identical to v1 */
> +#define UAC2_FU_INPUT_GAIN		0x0b
> +#define UAC2_FU_INPUT_GAIN_PAD		0x0c
> +#define UAC2_FU_PHASE_INVERTER		0x0d
> +#define UAC2_FU_UNDERFLOW		0x0e
> +#define UAC2_FU_OVERFLOW		0x0f
> +#define UAC2_FU_LATENCY			0x10
> +
> +/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
> +#define UAC2_PE_UNDEFINED		0x00
> +#define UAC2_PE_ENABLE			0x01
> +#define UAC2_PE_CENTERFREQ		0x02
> +#define UAC2_PE_QFACTOR			0x03
> +#define UAC2_PE_GAIN			0x04
> +#define UAC2_PE_UNDERFLOW		0x05
> +#define UAC2_PE_OVERFLOW		0x06
> +#define UAC2_PE_LATENCY			0x07
> +
> +/* A.17.8.2 Reverberation Effect Unit Control Selectors */
> +#define UAC2_RV_UNDEFINED		0x00
> +#define UAC2_RV_ENABLE			0x01
> +#define UAC2_RV_TYPE			0x02
> +#define UAC2_RV_LEVEL			0x03
> +#define UAC2_RV_TIME			0x04
> +#define UAC2_RV_FEEDBACK		0x05
> +#define UAC2_RV_PREDELAY		0x06
> +#define UAC2_RV_DENSITY			0x07
> +#define UAC2_RV_HIFREQ_ROLLOFF		0x08
> +#define UAC2_RV_UNDERFLOW		0x09
> +#define UAC2_RV_OVERFLOW		0x0a
> +#define UAC2_RV_LATENCY			0x0b
> +
> +/* A.17.8.3 Modulation Delay Effect Control Selectors */
> +#define UAC2_MD_UNDEFINED		0x00
> +#define UAC2_MD_ENABLE			0x01
> +#define UAC2_MD_BALANCE			0x02
> +#define UAC2_MD_RATE			0x03
> +#define UAC2_MD_DEPTH			0x04
> +#define UAC2_MD_TIME			0x05
> +#define UAC2_MD_FEEDBACK		0x06
> +#define UAC2_MD_UNDERFLOW		0x07
> +#define UAC2_MD_OVERFLOW		0x08
> +#define UAC2_MD_LATENCY			0x09
> +
> +/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
> +#define UAC2_DR_UNDEFINED		0x00
> +#define UAC2_DR_ENABLE			0x01
> +#define UAC2_DR_COMPRESSION_RATE	0x02
> +#define UAC2_DR_MAXAMPL			0x03
> +#define UAC2_DR_THRESHOLD		0x04
> +#define UAC2_DR_ATTACK_TIME		0x05
> +#define UAC2_DR_RELEASE_TIME		0x06
> +#define UAC2_DR_UNDEFLOW		0x07
> +#define UAC2_DR_OVERFLOW		0x08
> +#define UAC2_DR_LATENCY			0x09
> +
> +/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
> +#define UAC2_UD_UNDEFINED		0x00
> +#define UAC2_UD_ENABLE			0x01
> +#define UAC2_UD_MODE_SELECT		0x02
> +#define UAC2_UD_CLUSTER			0x03
> +#define UAC2_UD_UNDERFLOW		0x04
> +#define UAC2_UD_OVERFLOW		0x05
> +#define UAC2_UD_LATENCY			0x06
> +
> +/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
> +#define UAC2_DP_UNDEFINED		0x00
> +#define UAC2_DP_ENABLE			0x01
> +#define UAC2_DP_MODE_SELECT		0x02
> +#define UAC2_DP_CLUSTER			0x03
> +#define UAC2_DP_UNDERFFLOW		0x04
> +#define UAC2_DP_OVERFLOW		0x05
> +#define UAC2_DP_LATENCY			0x06
> +
> +/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
> +#define UAC2_ST_EXT_UNDEFINED		0x00
> +#define UAC2_ST_EXT_ENABLE		0x01
> +#define UAC2_ST_EXT_WIDTH		0x02
> +#define UAC2_ST_EXT_UNDEFLOW		0x03
> +#define UAC2_ST_EXT_OVERFLOW		0x04
> +#define UAC2_ST_EXT_LATENCY		0x05
> +
> +/* A.17.10 Extension Unit Control Selectors */
> +#define UAC2_XU_UNDEFINED		0x00
> +#define UAC2_XU_ENABLE			0x01
> +#define UAC2_XU_CLUSTER			0x02
> +#define UAC2_XU_UNDERFLOW		0x03
> +#define UAC2_XU_OVERFLOW		0x04
> +#define UAC2_XU_LATENCY			0x05
> +
> +/* A.17.11 AudioStreaming Interface Control Selectors */
> +#define UAC2_AS_UNDEFINED		0x00
> +#define UAC2_AS_ACT_ALT_SETTING		0x01
> +#define UAC2_AS_VAL_ALT_SETTINGS	0x02
> +#define UAC2_AS_AUDIO_DATA_FORMAT	0x03
> +
> +/* A.17.12 Encoder Control Selectors */
> +#define UAC2_EN_UNDEFINED		0x00
> +#define UAC2_EN_BIT_RATE		0x01
> +#define UAC2_EN_QUALITY			0x02
> +#define UAC2_EN_VBR			0x03
> +#define UAC2_EN_TYPE			0x04
> +#define UAC2_EN_UNDERFLOW		0x05
> +#define UAC2_EN_OVERFLOW		0x06
> +#define UAC2_EN_ENCODER_ERROR		0x07
> +#define UAC2_EN_PARAM1			0x08
> +#define UAC2_EN_PARAM2			0x09
> +#define UAC2_EN_PARAM3			0x0a
> +#define UAC2_EN_PARAM4			0x0b
> +#define UAC2_EN_PARAM5			0x0c
> +#define UAC2_EN_PARAM6			0x0d
> +#define UAC2_EN_PARAM7			0x0e
> +#define UAC2_EN_PARAM8			0x0f
> +
> +/* A.17.13.1 MPEG Decoder Control Selectors */
> +#define UAC2_MPEG_UNDEFINED		0x00
> +#define UAC2_MPEG_DUAL_CHANNEL		0x01
> +#define UAC2_MPEG_SECOND_STEREO		0x02
> +#define UAC2_MPEG_MULTILINGUAL		0x03
> +#define UAC2_MPEG_DYN_RANGE		0x04
> +#define UAC2_MPEG_SCALING		0x05
> +#define UAC2_MPEG_HILO_SCALING		0x06
> +#define UAC2_MPEG_UNDERFLOW		0x07
> +#define UAC2_MPEG_OVERFLOW		0x08
> +#define UAC2_MPEG_DECODER_ERROR		0x09
> +
> +/* A17.13.2 AC3 Decoder Control Selectors */
> +#define UAC2_AC3_UNDEFINED		0x00
> +#define UAC2_AC3_MODE			0x01
> +#define UAC2_AC3_DYN_RANGE		0x02
> +#define UAC2_AC3_SCALING		0x03
> +#define UAC2_AC3_HILO_SCALING		0x04
> +#define UAC2_AC3_UNDERFLOW		0x05
> +#define UAC2_AC3_OVERFLOW		0x06
> +#define UAC2_AC3_DECODER_ERROR		0x07
> +
> +/* A17.13.3 WMA Decoder Control Selectors */
> +#define UAC2_WMA_UNDEFINED		0x00
> +#define UAC2_WMA_UNDERFLOW		0x01
> +#define UAC2_WMA_OVERFLOW		0x02
> +#define UAC2_WMA_DECODER_ERROR		0x03
> +
> +/* A17.13.4 DTS Decoder Control Selectors */
> +#define UAC2_DTS_UNDEFINED		0x00
> +#define UAC2_DTS_UNDERFLOW		0x01
> +#define UAC2_DTS_OVERFLOW		0x02
> +#define UAC2_DTS_DECODER_ERROR		0x03
> +
> +/* A17.14 Endpoint Control Selectors */
> +#define UAC2_EP_CS_UNDEFINED		0x00
> +#define UAC2_EP_CS_PITCH		0x01
> +#define UAC2_EP_CS_DATA_OVERRUN		0x02
> +#define UAC2_EP_CS_DATA_UNDERRUN	0x03
> +
> +#endif /* __LINUX_USB_AUDIO_V2_H */
> +
> diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
> index 4d3e450..cdad728 100644
> --- a/include/linux/usb/audio.h
> +++ b/include/linux/usb/audio.h
> @@ -13,6 +13,9 @@
>   * Comments below reference relevant sections of that document:
>   *
>   * http://www.usb.org/developers/devclass_docs/audio10.pdf
> + *
> + * Types and defines in this file are either specific to version 1.0 of
> + * this standard or common for newer versions.
>   */
>  
>  #ifndef __LINUX_USB_AUDIO_H
> @@ -20,14 +23,15 @@
>  
>  #include <linux/types.h>
>  
> +/* bInterfaceProtocol values to denote the version of the standard used */
> +#define UAC_VERSION_1			0x00
> +#define UAC_VERSION_2			0x20
> +
>  /* A.2 Audio Interface Subclass Codes */
>  #define USB_SUBCLASS_AUDIOCONTROL	0x01
>  #define USB_SUBCLASS_AUDIOSTREAMING	0x02
>  #define USB_SUBCLASS_MIDISTREAMING	0x03
>  
> -#define UAC_VERSION_1			0x00
> -#define UAC_VERSION_2			0x20
> -
>  /* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
>  #define UAC_HEADER			0x01
>  #define UAC_INPUT_TERMINAL		0x02
> @@ -38,15 +42,6 @@
>  #define UAC_PROCESSING_UNIT_V1		0x07
>  #define UAC_EXTENSION_UNIT_V1		0x08
>  
> -/* UAC v2.0 types */
> -#define UAC_EFFECT_UNIT			0x07
> -#define UAC_PROCESSING_UNIT_V2		0x08
> -#define UAC_EXTENSION_UNIT_V2		0x09
> -#define UAC_CLOCK_SOURCE		0x0a
> -#define UAC_CLOCK_SELECTOR		0x0b
> -#define UAC_CLOCK_MULTIPLIER		0x0c
> -#define UAC_SAMPLE_RATE_CONVERTER	0x0d
> -
>  /* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
>  #define UAC_AS_GENERAL			0x01
>  #define UAC_FORMAT_TYPE			0x02
> @@ -78,10 +73,6 @@
>  
>  #define UAC_GET_STAT			0xff
>  
> -/* Audio class v2.0 handles all the parameter calls differently */
> -#define UAC2_CS_CUR			0x01
> -#define UAC2_CS_RANGE			0x02
> -
>  /* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
>  #define UAC_MS_HEADER			0x01
>  #define UAC_MIDI_IN_JACK		0x02
> @@ -200,19 +191,6 @@ struct uac_as_header_descriptor_v1 {
>  	__le16 wFormatTag;		/* The Audio Data Format */
>  } __attribute__ ((packed));
>  
> -struct uac_as_header_descriptor_v2 {
> -	__u8 bLength;
> -	__u8 bDescriptorType;
> -	__u8 bDescriptorSubtype;
> -	__u8 bTerminalLink;
> -	__u8 bmControls;
> -	__u8 bFormatType;
> -	__u32 bmFormats;
> -	__u8 bNrChannels;
> -	__u32 bmChannelConfig;
> -	__u8 iChannelNames;
> -} __attribute__((packed));
> -
>  #define UAC_DT_AS_HEADER_SIZE		7
>  
>  /* Formats - A.1.1 Audio Data Format Type I Codes */
> @@ -277,7 +255,6 @@ struct uac_format_type_i_ext_descriptor {
>  	__u8 bSideBandProtocol;
>  } __attribute__((packed));
>  
> -
>  /* Formats - Audio Data Format Type I Codes */
>  
>  #define UAC_FORMAT_TYPE_II_MPEG	0x1001
> @@ -336,19 +313,6 @@ struct uac_iso_endpoint_descriptor {
>  #define UAC_EP_CS_ATTR_PITCH_CONTROL	0x02
>  #define UAC_EP_CS_ATTR_FILL_MAX		0x80
>  
> -/* Audio class v2.0: CLOCK_SOURCE descriptor */
> -
> -struct uac_clock_source_descriptor {
> -	__u8 bLength;
> -	__u8 bDescriptorType;
> -	__u8 bDescriptorSubtype;
> -	__u8 bClockID;
> -	__u8 bmAttributes;
> -	__u8 bmControls;
> -	__u8 bAssocTerminal;
> -	__u8 iClockSource;
> -} __attribute__((packed));
> -
>  /* A.10.2 Feature Unit Control Selectors */
>  
>  struct uac_feature_unit_descriptor {
> diff --git a/sound/usb/card.c b/sound/usb/card.c
> index 426aabc..78d12ff 100644
> --- a/sound/usb/card.c
> +++ b/sound/usb/card.c
> @@ -45,6 +45,7 @@
>  #include <linux/moduleparam.h>
>  #include <linux/mutex.h>
>  #include <linux/usb/audio.h>
> +#include <linux/usb/audio-v2.h>
>  
>  #include <sound/core.h>
>  #include <sound/info.h>
> @@ -250,7 +251,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
>  		 * clock selectors and sample rate conversion units. */
>  
>  		cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
> -						NULL, UAC_CLOCK_SOURCE);
> +						NULL, UAC2_CLOCK_SOURCE);
>  
>  		if (!cs) {
>  			snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
> diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
> index 91850f8..b1309cd 100644
> --- a/sound/usb/endpoint.c
> +++ b/sound/usb/endpoint.c
> @@ -18,6 +18,7 @@
>  #include <linux/init.h>
>  #include <linux/usb.h>
>  #include <linux/usb/audio.h>
> +#include <linux/usb/audio-v2.h>
>  
>  #include <sound/core.h>
>  #include <sound/pcm.h>
> diff --git a/sound/usb/format.c b/sound/usb/format.c
> index b613e0a..0e04efe 100644
> --- a/sound/usb/format.c
> +++ b/sound/usb/format.c
> @@ -18,6 +18,7 @@
>  #include <linux/init.h>
>  #include <linux/usb.h>
>  #include <linux/usb/audio.h>
> +#include <linux/usb/audio-v2.h>
>  
>  #include <sound/core.h>
>  #include <sound/pcm.h>
> diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
> index e0f3f87..630e220 100644
> --- a/sound/usb/pcm.c
> +++ b/sound/usb/pcm.c
> @@ -17,6 +17,7 @@
>  #include <linux/init.h>
>  #include <linux/usb.h>
>  #include <linux/usb/audio.h>
> +#include <linux/usb/audio-v2.h>
>  
>  #include <sound/core.h>
>  #include <sound/pcm.h>
> @@ -215,7 +216,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
>  	data[3] = rate >> 24;
>  	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
>  				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
> -				   0x0100, chip->clock_id << 8,
> +				   UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
>  				   data, sizeof(data), 1000)) < 0) {
>  		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
>  			   dev->devnum, iface, fmt->altsetting, rate);
> @@ -223,7 +224,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
>  	}
>  	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
>  				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
> -				   0x0100, chip->clock_id << 8,
> +				   UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
>  				   data, sizeof(data), 1000)) < 0) {
>  		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
>  			   dev->devnum, iface, fmt->altsetting);
> -- 
> 1.6.6.2
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] linux/usb/audio.h: split header
  2010-03-11 20:17   ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
@ 2010-03-12  2:55     ` Greg KH
  0 siblings, 0 replies; 10+ messages in thread
From: Greg KH @ 2010-03-12  2:55 UTC (permalink / raw)
  To: Daniel Mack; +Cc: tiwai, alsa-devel, clemens

On Thu, Mar 11, 2010 at 09:17:48PM +0100, Daniel Mack wrote:
> On Thu, Mar 11, 2010 at 09:13:20PM +0100, Daniel Mack wrote:
> > - Split the audio.h file in two to clearly denote the differences
> >   between the standards.
> > - Add many more defines to audio-v2.h. Most of them are not currently
> >   used.
> > - Replaced a magic value with a proper define
> > 
> > Signed-off-by: Daniel Mack <daniel@caiaq.de>
> > Cc: Clemens Ladisch <clemens@ladisch.de>
> > Cc: Takashi Iwai <tiwai@suse.de>
> 
> Sorry Greg, I forgot to Cc: you on this.
> 
> As the patch touch both the include files and audio code, it would go
> thru the ALSA channel again if you don't mind.

No objection from me:
	Acked-by: Greg Kroah-Hartman <gregkh@suse.de>

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: ALSA: usb-mixer: Add support for UAC2 devices
  2010-03-11 20:13 ALSA: usb-mixer: Add support for UAC2 devices Daniel Mack
  2010-03-11 20:13 ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
@ 2010-03-12 11:46 ` Takashi Iwai
  1 sibling, 0 replies; 10+ messages in thread
From: Takashi Iwai @ 2010-03-12 11:46 UTC (permalink / raw)
  To: Daniel Mack; +Cc: alsa-devel, clemens

At Thu, 11 Mar 2010 21:13:19 +0100,
Daniel Mack wrote:
> 
> This patch series adds support for mixer interfaces in USB audio class
> v2 devices. Successfully tested on a XMOS L1 eval kit. They apply on
> top of the topic/usb branch.
> 
> There's a number significant differences between v1 and v2
> implementations, which the patches itself may explain best :)
> 
> I moved most quirks out into a seperate file, and hope you agree that
> it's cleaner that way. Most descriptors are now parsed with structs
> which makes the code more readable. However, there are quite some
> descriptors with variable field lengths so they can't be mapped into
> structs. I introduces some macros to access these fields.
> 
> Again, please test this with v1 devices, as I can't be entirely
> confident that I didn't break anything in the transition.
> 
> Thanks,
> Daniel
> 
> 
> [PATCH 1/6] linux/usb/audio.h: split header
> [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h
> [PATCH 3/6] ALSA: usb-mixer: factor out quirks
> [PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch]
> [PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs
> [PATCH 6/6] ALSA: usb-mixer: Add support for Audio Class v2.0

All applied now.  Thanks!


Takashi

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2010-03-12 11:46 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-11 20:13 ALSA: usb-mixer: Add support for UAC2 devices Daniel Mack
2010-03-11 20:13 ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
2010-03-11 20:13   ` [PATCH 2/6] ALSA: usb-mixer: use defines from audio.h Daniel Mack
2010-03-11 20:13     ` [PATCH 3/6] ALSA: usb-mixer: factor out quirks Daniel Mack
2010-03-11 20:13       ` [PATCH 4/6] ALSA: usb-mixer: rename usbmixer.[ch] -> mixer.[ch] Daniel Mack
2010-03-11 20:13         ` [PATCH 5/6] ALSA: usb-mixer: parse descriptors with structs Daniel Mack
2010-03-11 20:13           ` [PATCH 6/6] ALSA: usb-mixer: Add support for Audio Class v2.0 Daniel Mack
2010-03-11 20:17   ` [PATCH 1/6] linux/usb/audio.h: split header Daniel Mack
2010-03-12  2:55     ` Greg KH
2010-03-12 11:46 ` ALSA: usb-mixer: Add support for UAC2 devices Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.