alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls
@ 2010-05-31 11:35 Daniel Mack
  2010-05-31 11:35 ` [PATCH 2/9] ALSA: usb-audio: support partially write-protected UAC2 controls Daniel Mack
                   ` (8 more replies)
  0 siblings, 9 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

Introduce two new static inline functions for a more readable parsing
of UAC2 bmaControls.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 include/linux/usb/audio-v2.h |   10 ++++++++++
 sound/usb/mixer.c            |    8 ++++----
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
index 92f1d99..8f91be3 100644
--- a/include/linux/usb/audio-v2.h
+++ b/include/linux/usb/audio-v2.h
@@ -18,6 +18,16 @@
 /* v1.0 and v2.0 of this standard have many things in common. For the rest
  * of the definitions, please refer to audio.h */
 
+static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
+{
+	return (bmControls >> (control * 2)) & 0x1;
+}
+
+static inline bool uac2_control_is_writeable(u32 bmControls, u8 control)
+{
+	return (bmControls >> (control * 2)) & 0x2;
+}
+
 /* 4.7.2.1 Clock Source Descriptor */
 
 struct uac_clock_source_descriptor {
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 03ce971..43d6417 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1188,9 +1188,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 
 			for (j = 0; j < channels; j++) {
 				unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
-				if (mask & (1 << (i * 2))) {
+				if (uac2_control_is_readable(mask, i)) {
 					ch_bits |= (1 << j);
-					if (~mask & (1 << ((i * 2) + 1)))
+					if (!uac2_control_is_writeable(mask, i))
 						ch_read_only |= (1 << j);
 				}
 			}
@@ -1198,9 +1198,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 			/* 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))
+			if (uac2_control_is_readable(master_bits, i))
 				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
-						  ~master_bits & (1 << ((i * 2) + 1)));
+						  !uac2_control_is_writeable(master_bits, i));
 		}
 	}
 
-- 
1.7.1

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

* [PATCH 2/9] ALSA: usb-audio: support partially write-protected UAC2 controls
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 3/9] include/linux/usb/audio-v2.h: add more UAC2 details Daniel Mack
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

So far, UAC2 controls are marked read-only if any of the channels are
marked read-only in the descriptors. Change this behaviour and

 - mark them writeable unless all channels are read-only
 - store the read-only mask in usb_mixer_elem_info and
 - check the mask again in set_cur_mix_value(), and bail out for
   write-protected channels.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/mixer.c |   30 ++++++++++++++++++++++++------
 sound/usb/mixer.h |    2 ++
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 43d6417..9149a84 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -462,6 +462,16 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
 			     int index, int value)
 {
 	int err;
+	unsigned int read_only = (channel == 0) ?
+		cval->master_readonly :
+		cval->ch_readonly & (1 << (channel - 1));
+
+	if (read_only) {
+		snd_printdd(KERN_INFO "%s(): channel %d of control %d is read_only\n",
+			    __func__, channel, cval->control);
+		return 0;
+	}
+
 	err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
 			    value);
 	if (err < 0)
@@ -958,7 +968,7 @@ 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,
-			      int read_only)
+			      int readonly_mask)
 {
 	struct uac_feature_unit_descriptor *desc = raw_desc;
 	unsigned int len = 0;
@@ -989,20 +999,25 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 	cval->control = control;
 	cval->cmask = ctl_mask;
 	cval->val_type = audio_feature_info[control-1].type;
-	if (ctl_mask == 0)
+	if (ctl_mask == 0) {
 		cval->channels = 1;	/* master channel */
-	else {
+		cval->master_readonly = readonly_mask;
+	} else {
 		int i, c = 0;
 		for (i = 0; i < 16; i++)
 			if (ctl_mask & (1 << i))
 				c++;
 		cval->channels = c;
+		cval->ch_readonly = readonly_mask;
 	}
 
 	/* get min/max values */
 	get_min_max(cval, 0);
 
-	if (read_only)
+	/* if all channels in the mask are marked read-only, make the control
+	 * read-only. set_cur_mix_value() will check the mask again and won't
+	 * issue write commands to read-only channels. */
+	if (cval->channels == readonly_mask)
 		kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
 	else
 		kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
@@ -1195,9 +1210,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
 				}
 			}
 
-			/* FIXME: the whole unit is read-only if any of the channels is marked read-only */
+			/* NOTE: build_feature_ctl() will mark the control read-only if all channels
+			 * are marked read-only in the descriptors. Otherwise, the control will be
+			 * reported as writeable, but the driver will not actually issue a write
+			 * command for read-only channels */
 			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);
+				build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only);
 			if (uac2_control_is_readable(master_bits, i))
 				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
 						  !uac2_control_is_writeable(master_bits, i));
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 1301238..a7cf100 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -34,6 +34,8 @@ struct usb_mixer_elem_info {
 	unsigned int id;
 	unsigned int control;	/* CS or ICN (high byte) */
 	unsigned int cmask; /* channel mask bitmap: 0 = master */
+	unsigned int ch_readonly;
+	unsigned int master_readonly;
 	int channels;
 	int val_type;
 	int min, max, res;
-- 
1.7.1

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

* [PATCH 3/9] include/linux/usb/audio-v2.h: add more UAC2 details
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
  2010-05-31 11:35 ` [PATCH 2/9] ALSA: usb-audio: support partially write-protected UAC2 controls Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 4/9] ALSA: usb-audio: fix selector unit string index accessor Daniel Mack
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

Also, remove the 'bmControl' field from uac_clock_selector_descriptor,
which was at the wrong offset. This struct is currently unused.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 include/linux/usb/audio-v2.h |   21 ++++++++++++++++++++-
 1 files changed, 20 insertions(+), 1 deletions(-)

diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
index 8f91be3..383b94b 100644
--- a/include/linux/usb/audio-v2.h
+++ b/include/linux/usb/audio-v2.h
@@ -41,6 +41,13 @@ struct uac_clock_source_descriptor {
 	__u8 iClockSource;
 } __attribute__((packed));
 
+/* bmAttribute fields */
+#define UAC_CLOCK_SOURCE_TYPE_EXT	0x0
+#define UAC_CLOCK_SOURCE_TYPE_INT_FIXED	0x1
+#define UAC_CLOCK_SOURCE_TYPE_INT_VAR	0x2
+#define UAC_CLOCK_SOURCE_TYPE_INT_PROG	0x3
+#define UAC_CLOCK_SOURCE_SYNCED_TO_SOF	(1 << 2)
+
 /* 4.7.2.2 Clock Source Descriptor */
 
 struct uac_clock_selector_descriptor {
@@ -49,8 +56,20 @@ struct uac_clock_selector_descriptor {
 	__u8 bDescriptorSubtype;
 	__u8 bClockID;
 	__u8 bNrInPins;
-	__u8 bmControls;
 	__u8 baCSourceID[];
+	/* bmControls, bAssocTerminal and iClockSource omitted */
+} __attribute__((packed));
+
+/* 4.7.2.3 Clock Multiplier Descriptor */
+
+struct uac_clock_multiplier_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bCSourceID;
+	__u8 bmControls;
+	__u8 iClockMultiplier;
 } __attribute__((packed));
 
 /* 4.7.2.4 Input terminal descriptor */
-- 
1.7.1

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

* [PATCH 4/9] ALSA: usb-audio: fix selector unit string index accessor
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
  2010-05-31 11:35 ` [PATCH 2/9] ALSA: usb-audio: support partially write-protected UAC2 controls Daniel Mack
  2010-05-31 11:35 ` [PATCH 3/9] include/linux/usb/audio-v2.h: add more UAC2 details Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices Daniel Mack
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

This is another regression from the UAC2 code refactoring.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 include/linux/usb/audio.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index 5d646c3..ed5cf92 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -244,7 +244,7 @@ struct uac_selector_unit_descriptor {
 static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
 {
 	__u8 *raw = (__u8 *) desc;
-	return raw[9 + desc->bLength - 1];
+	return raw[desc->bLength - 1];
 }
 
 /* 4.3.2.5 Feature Unit Descriptor */
-- 
1.7.1

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

* [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (2 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 4/9] ALSA: usb-audio: fix selector unit string index accessor Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 12:51   ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 6/9] ALSA: usb-audio: unify constants from specification Daniel Mack
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

Audio devices which comply to the UAC2 standard can export complex clock
topologies in its descriptors and set up links between them.

The entities that are defined are

 - clock sources, which define the end-leafs.
 - clock selectors, which act as switch to select one out of many
   possible clocks sources.
 - clock multipliers, which have an input clock source, and act as clock
   source again. They can be used to derive one clock from another.

All sample rate changes, clock validity queries and the like must go to
clock source elements, while clock selectors and multipliers can be used
as terminal clock source.

The following patch adds a parser for these elements and functions to
iterate over the tree and find the leaf nodes (clock sources).

The samplerate set functions were moved to the new clock.c file.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/Makefile   |    3 +-
 sound/usb/card.c     |   18 +---
 sound/usb/card.h     |    1 +
 sound/usb/clock.c    |  310 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sound/usb/clock.h    |   12 ++
 sound/usb/endpoint.c |   57 +++++++++-
 sound/usb/format.c   |   16 ++-
 sound/usb/pcm.c      |   98 +----------------
 sound/usb/usbaudio.h |    5 +-
 9 files changed, 395 insertions(+), 125 deletions(-)
 create mode 100644 sound/usb/clock.c
 create mode 100644 sound/usb/clock.h

diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e7ac7f4..1e362bf 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,7 +11,8 @@ snd-usb-audio-objs := 	card.o \
 			endpoint.o \
 			urb.o \
 			pcm.o \
-			helper.o
+			helper.o \
+			clock.o
 
 snd-usbmidi-lib-objs := midi.o
 
diff --git a/sound/usb/card.c b/sound/usb/card.c
index da1346b..7a8ac1d 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -236,7 +236,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 	}
 
 	case UAC_VERSION_2: {
-		struct uac_clock_source_descriptor *cs;
 		struct usb_interface_assoc_descriptor *assoc =
 			usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
 
@@ -245,21 +244,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 			return -EINVAL;
 		}
 
-		/* FIXME: for now, we expect there is at least one clock source
-		 * descriptor and we always take the first one.
-		 * We should properly support devices with multiple clock sources,
-		 * clock selectors and sample rate conversion units. */
-
-		cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
-						NULL, UAC2_CLOCK_SOURCE);
-
-		if (!cs) {
-			snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
-			return -EINVAL;
-		}
-
-		chip->clock_id = cs->bClockID;
-
 		for (i = 0; i < assoc->bInterfaceCount; i++) {
 			int intf = assoc->bFirstInterface + i;
 
@@ -481,6 +465,8 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 			goto __error;
 	}
 
+	chip->ctrl_intf = alts;
+
 	if (err > 0) {
 		/* create normal USB audio interfaces */
 		if (snd_usb_create_streams(chip, ifnum) < 0 ||
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ed92420..1febf2f 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -25,6 +25,7 @@ struct audioformat {
 	unsigned int rate_min, rate_max;	/* min/max rates */
 	unsigned int nr_rates;		/* number of rate table entries */
 	unsigned int *rate_table;	/* rate table */
+	unsigned char clock;		/* associated clock */
 };
 
 struct snd_usb_substream;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
new file mode 100644
index 0000000..94f2664
--- /dev/null
+++ b/sound/usb/clock.c
@@ -0,0 +1,310 @@
+/*
+ *   Clock domain and sample rate management functions
+ *
+ *   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/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#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>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "midi.h"
+#include "mixer.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "helper.h"
+#include "debug.h"
+#include "pcm.h"
+#include "urb.h"
+#include "format.h"
+
+static struct uac_clock_source_descriptor *
+	snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
+				  int clock_id)
+{
+	struct uac_clock_source_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_SOURCE))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static struct uac_clock_selector_descriptor *
+	snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
+				    int clock_id)
+{
+	struct uac_clock_selector_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_SELECTOR))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static struct uac_clock_multiplier_descriptor *
+	snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
+				      int clock_id)
+{
+	struct uac_clock_multiplier_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_MULTIPLIER))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
+{
+	unsigned char buf;
+	int ret;
+
+	ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+			      UAC2_CS_CUR,
+			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+			      UAC2_CX_CLOCK_SELECTOR << 8, selector_id << 8,
+			      &buf, sizeof(buf), 1000);
+
+	if (ret < 0)
+		return ret;
+
+	return buf;
+}
+
+static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
+{
+	int err;
+	unsigned char data;
+	struct usb_device *dev = chip->dev;
+
+	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+			      UAC2_CS_CONTROL_CLOCK_VALID << 8, source_id << 8,
+			      &data, sizeof(data), 1000);
+
+	if (err < 0) {
+		snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
+			   __func__, source_id);
+		return err;
+	}
+
+	return !!data;
+}
+
+/* Try to find the clock source ID of a given clock entity */
+
+static int __uac_clock_find_source(struct snd_usb_audio *chip,
+				   struct usb_host_interface *host_iface,
+				   int entity_id, unsigned long *visited)
+{
+	struct uac_clock_source_descriptor *source;
+	struct uac_clock_selector_descriptor *selector;
+	struct uac_clock_multiplier_descriptor *multiplier;
+
+	entity_id &= 0xff;
+
+	if (test_and_set_bit(entity_id, visited)) {
+		snd_printk(KERN_WARNING
+			"%s(): recursive clock topology detected, id %d.\n",
+			__func__, entity_id);
+		return -EINVAL;
+	}
+
+	/* first, see if the ID we're looking for is a clock source already */
+	source = snd_usb_find_clock_source(host_iface, entity_id);
+	if (source)
+		return source->bClockID;
+
+	selector = snd_usb_find_clock_selector(host_iface, entity_id);
+	if (selector) {
+		int ret;
+
+		/* the entity ID we are looking for is a selector.
+		 * find out what it currently selects */
+		ret = uac_clock_selector_get_val(chip, selector->bClockID);
+		if (ret < 0)
+			return ret;
+
+		if (ret >= selector->bNrInPins) {
+			printk(KERN_ERR
+				"%s(): selector reported illegal value, id %d, ret %d\n",
+				__func__, selector->bClockID, ret);
+			return -EINVAL;
+		}
+
+		return __uac_clock_find_source(chip, host_iface,
+					       selector->baCSourceID[ret],
+					       visited);
+	}
+
+	/* FIXME: multipliers only act as pass-thru element for now */
+	multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
+	if (multiplier)
+		return __uac_clock_find_source(chip, host_iface,
+					       multiplier->bCSourceID, visited);
+
+	return -EINVAL;
+}
+
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+			      struct usb_host_interface *host_iface,
+			      int entity_id)
+{
+	DECLARE_BITMAP(visited, 256);
+	memset(visited, 0, sizeof(visited));
+	return __uac_clock_find_source(chip, host_iface, entity_id, visited);
+}
+
+static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
+			      struct usb_host_interface *alts,
+			      struct audioformat *fmt, int rate)
+{
+	struct usb_device *dev = chip->dev;
+	unsigned int ep;
+	unsigned char data[3];
+	int err, crate;
+
+	ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+	/* if endpoint doesn't have sampling rate control, bail out */
+	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
+		snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
+				   dev->devnum, iface, fmt->altsetting);
+		return 0;
+	}
+
+	data[0] = rate;
+	data[1] = rate >> 8;
+	data[2] = rate >> 16;
+	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
+			   dev->devnum, iface, fmt->altsetting, rate, ep);
+		return err;
+	}
+
+	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
+				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
+			   dev->devnum, iface, fmt->altsetting, ep);
+		return 0; /* some devices don't support reading */
+	}
+
+	crate = data[0] | (data[1] << 8) | (data[2] << 16);
+	if (crate != rate) {
+		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+		// runtime->rate = crate;
+	}
+
+	return 0;
+}
+
+static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+			      struct usb_host_interface *alts,
+			      struct audioformat *fmt, int rate)
+{
+	struct usb_device *dev = chip->dev;
+	unsigned char data[4];
+	int err, crate;
+	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
+
+	if (clock < 0)
+		return clock;
+
+	if (!uac_clock_source_is_valid(chip, clock)) {
+		snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
+			   dev->devnum, iface, fmt->altsetting, clock);
+		return -ENXIO;
+	}
+
+	data[0] = rate;
+	data[1] = rate >> 8;
+	data[2] = rate >> 16;
+	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,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 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);
+		return err;
+	}
+
+	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
+			   dev->devnum, iface, fmt->altsetting);
+		return err;
+	}
+
+	crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+	if (crate != rate)
+		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+
+	return 0;
+}
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+			     struct usb_host_interface *alts,
+			     struct audioformat *fmt, int rate)
+{
+	struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+	switch (altsd->bInterfaceProtocol) {
+	case UAC_VERSION_1:
+		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
+
+	case UAC_VERSION_2:
+		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+	}
+
+	return -EINVAL;
+}
+
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
new file mode 100644
index 0000000..beb2536
--- /dev/null
+++ b/sound/usb/clock.h
@@ -0,0 +1,12 @@
+#ifndef __USBAUDIO_CLOCK_H
+#define __USBAUDIO_CLOCK_H
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+			     struct usb_host_interface *alts,
+			     struct audioformat *fmt, int rate);
+
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+			      struct usb_host_interface *host_iface,
+			      int entity_id);
+
+#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 28ee1ce..9593b91 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -190,6 +190,38 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
 	return attributes;
 }
 
+static struct uac2_input_terminal_descriptor *
+	snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+					       int terminal_id)
+{
+	struct uac2_input_terminal_descriptor *term = NULL;
+
+	while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+					       ctrl_iface->extralen,
+					       term, UAC_INPUT_TERMINAL))) {
+		if (term->bTerminalID == terminal_id)
+			return term;
+	}
+
+	return NULL;
+}
+
+static struct uac2_output_terminal_descriptor *
+	snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+						int terminal_id)
+{
+	struct uac2_output_terminal_descriptor *term = NULL;
+
+	while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+					       ctrl_iface->extralen,
+					       term, UAC_OUTPUT_TERMINAL))) {
+		if (term->bTerminalID == terminal_id)
+			return term;
+	}
+
+	return NULL;
+}
+
 int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 {
 	struct usb_device *dev;
@@ -199,7 +231,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 	int i, altno, err, stream;
 	int format = 0, num_channels = 0;
 	struct audioformat *fp = NULL;
-	int num, protocol;
+	int num, protocol, clock = 0;
 	struct uac_format_type_i_continuous_descriptor *fmt;
 
 	dev = chip->dev;
@@ -263,6 +295,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 		}
 
 		case UAC_VERSION_2: {
+			struct uac2_input_terminal_descriptor *input_term;
+			struct uac2_output_terminal_descriptor *output_term;
 			struct uac_as_header_descriptor_v2 *as =
 				snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
 
@@ -281,7 +315,25 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 			num_channels = as->bNrChannels;
 			format = le32_to_cpu(as->bmFormats);
 
-			break;
+			/* lookup the terminal associated to this interface
+			 * to extract the clock */
+			input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+									    as->bTerminalLink);
+			if (input_term) {
+				clock = input_term->bCSourceID;
+				break;
+			}
+
+			output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+									      as->bTerminalLink);
+			if (output_term) {
+				clock = output_term->bCSourceID;
+				break;
+			}
+
+			snd_printk(KERN_ERR "%d:%u:%d : bogus bTerminalLink %d\n",
+				   dev->devnum, iface_no, altno, as->bTerminalLink);
+			continue;
 		}
 
 		default:
@@ -338,6 +390,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 			fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
 					* (fp->maxpacksize & 0x7ff);
 		fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
+		fp->clock = clock;
 
 		/* some quirks for attributes here */
 
diff --git a/sound/usb/format.c b/sound/usb/format.c
index fe29d61..5367cd1 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -29,6 +29,7 @@
 #include "quirks.h"
 #include "helper.h"
 #include "debug.h"
+#include "clock.h"
 
 /*
  * parse the audio format type I descriptor
@@ -215,15 +216,17 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
 	struct usb_device *dev = chip->dev;
 	unsigned char tmp[2], *data;
 	int i, nr_rates, data_size, ret = 0;
+	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
 
 	/* 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,
-			      UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+			      UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
 			      tmp, sizeof(tmp), 1000);
 
 	if (ret < 0) {
-		snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
+		snd_printk(KERN_ERR "%s(): unable to retrieve number of sample rates (clock %d)\n",
+				__func__, clock);
 		goto err;
 	}
 
@@ -237,12 +240,13 @@ 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,
-			       UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
-			       data, data_size, 1000);
+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+			      UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
+			      data, data_size, 1000);
 
 	if (ret < 0) {
-		snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
+		snd_printk(KERN_ERR "%s(): unable to retrieve sample rate range (clock %d)\n",
+				__func__, clock);
 		ret = -EINVAL;
 		goto err_free;
 	}
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 056587d..4568298 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -31,6 +31,7 @@
 #include "urb.h"
 #include "helper.h"
 #include "pcm.h"
+#include "clock.h"
 
 /*
  * return the current pcm pointer.  just based on the hwptr_done value.
@@ -181,103 +182,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
 	return -EINVAL;
 }
 
-static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
-			      struct usb_host_interface *alts,
-			      struct audioformat *fmt, int rate)
-{
-	struct usb_device *dev = chip->dev;
-	unsigned int ep;
-	unsigned char data[3];
-	int err, crate;
-
-	ep = get_endpoint(alts, 0)->bEndpointAddress;
-	/* if endpoint doesn't have sampling rate control, bail out */
-	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
-		snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
-				   dev->devnum, iface, fmt->altsetting);
-		return 0;
-	}
-
-	data[0] = rate;
-	data[1] = rate >> 8;
-	data[2] = rate >> 16;
-	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-				   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-				   data, sizeof(data), 1000)) < 0) {
-		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
-			   dev->devnum, iface, fmt->altsetting, rate, ep);
-		return err;
-	}
-	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
-				   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
-				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-				   data, sizeof(data), 1000)) < 0) {
-		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
-			   dev->devnum, iface, fmt->altsetting, ep);
-		return 0; /* some devices don't support reading */
-	}
-	crate = data[0] | (data[1] << 8) | (data[2] << 16);
-	if (crate != rate) {
-		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-		// runtime->rate = crate;
-	}
-
-	return 0;
-}
-
-static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
-			      struct usb_host_interface *alts,
-			      struct audioformat *fmt, int rate)
-{
-	struct usb_device *dev = chip->dev;
-	unsigned char data[4];
-	int err, crate;
-
-	data[0] = rate;
-	data[1] = rate >> 8;
-	data[2] = rate >> 16;
-	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,
-				   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);
-		return err;
-	}
-	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
-				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-				   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);
-		return err;
-	}
-	crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
-	if (crate != rate)
-		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-
-	return 0;
-}
-
-int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
-			     struct usb_host_interface *alts,
-			     struct audioformat *fmt, int rate)
-{
-	struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
-	switch (altsd->bInterfaceProtocol) {
-	case UAC_VERSION_1:
-		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
-
-	case UAC_VERSION_2:
-		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
-	}
-
-	return -EINVAL;
-}
-
 /*
  * find a matching format and set up the interface
  */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 06ebf24..24d3319 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -40,9 +40,6 @@ struct snd_usb_audio {
 	int num_interfaces;
 	int num_suspended_intf;
 
-	/* for audio class v2 */
-	int clock_id;
-
 	struct list_head pcm_list;	/* list of pcm streams */
 	int pcm_devs;
 
@@ -53,6 +50,8 @@ struct snd_usb_audio {
 	int setup;			/* from the 'device_setup' module param */
 	int nrpacks;			/* from the 'nrpacks' module param */
 	int async_unlink;		/* from the 'async_unlink' module param */
+
+	struct usb_host_interface *ctrl_intf;	/* the audio control interface */
 };
 
 /*
-- 
1.7.1

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

* [PATCH 6/9] ALSA: usb-audio: unify constants from specification
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (3 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 7/9] ALSA: usb-audio: add UAC2 sepecific Feature Unit controls Daniel Mack
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

Move more definitions from private enums to appropriate header files.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 include/linux/usb/audio.h |   88 ++++++++++++++++++++++++++++-----------
 sound/usb/mixer.c         |   99 +++++++++++++++------------------------------
 sound/usb/mixer_maps.c    |    4 +-
 3 files changed, 98 insertions(+), 93 deletions(-)

diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index ed5cf92..c51200c 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -47,6 +47,15 @@
 #define UAC_FORMAT_TYPE			0x02
 #define UAC_FORMAT_SPECIFIC		0x03
 
+/* A.7 Processing Unit Process Types */
+#define UAC_PROCESS_UNDEFINED		0x00
+#define UAC_PROCESS_UP_DOWNMIX		0x01
+#define UAC_PROCESS_DOLBY_PROLOGIC	0x02
+#define UAC_PROCESS_STEREO_EXTENDER	0x03
+#define UAC_PROCESS_REVERB		0x04
+#define UAC_PROCESS_CHORUS		0x05
+#define UAC_PROCESS_DYN_RANGE_COMP	0x06
+
 /* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */
 #define UAC_EP_GENERAL			0x01
 
@@ -73,6 +82,60 @@
 
 #define UAC_GET_STAT			0xff
 
+/* A.10 Control Selector Codes */
+
+/* A.10.1 Terminal Control Selectors */
+#define UAC_TERM_COPY_PROTECT		0x01
+
+/* A.10.2 Feature Unit Control Selectors */
+#define UAC_FU_MUTE			0x01
+#define UAC_FU_VOLUME			0x02
+#define UAC_FU_BASS			0x03
+#define UAC_FU_MID			0x04
+#define UAC_FU_TREBLE			0x05
+#define UAC_FU_GRAPHIC_EQUALIZER	0x06
+#define UAC_FU_AUTOMATIC_GAIN		0x07
+#define UAC_FU_DELAY			0x08
+#define UAC_FU_BASS_BOOST		0x09
+#define UAC_FU_LOUDNESS			0x0a
+
+#define UAC_CONTROL_BIT(CS)	(1 << ((CS) - 1))
+
+/* A.10.3.1 Up/Down-mix Processing Unit Controls Selectors */
+#define UAC_UD_ENABLE			0x01
+#define UAC_UD_MODE_SELECT		0x02
+
+/* A.10.3.2 Dolby Prologic (tm) Processing Unit Controls Selectors */
+#define UAC_DP_ENABLE			0x01
+#define UAC_DP_MODE_SELECT		0x02
+
+/* A.10.3.3 3D Stereo Extender Processing Unit Control Selectors */
+#define UAC_3D_ENABLE			0x01
+#define UAC_3D_SPACE			0x02
+
+/* A.10.3.4 Reverberation Processing Unit Control Selectors */
+#define UAC_REVERB_ENABLE		0x01
+#define UAC_REVERB_LEVEL		0x02
+#define UAC_REVERB_TIME			0x03
+#define UAC_REVERB_FEEDBACK		0x04
+
+/* A.10.3.5 Chorus Processing Unit Control Selectors */
+#define UAC_CHORUS_ENABLE		0x01
+#define UAC_CHORUS_LEVEL		0x02
+#define UAC_CHORUS_RATE			0x03
+#define UAC_CHORUS_DEPTH		0x04
+
+/* A.10.3.6 Dynamic Range Compressor Unit Control Selectors */
+#define UAC_DCR_ENABLE			0x01
+#define UAC_DCR_RATE			0x02
+#define UAC_DCR_MAXAMPL			0x03
+#define UAC_DCR_THRESHOLD		0x04
+#define UAC_DCR_ATTACK_TIME		0x05
+#define UAC_DCR_RELEASE_TIME		0x06
+
+/* A.10.4 Extension Unit Control Selectors */
+#define UAC_XU_ENABLE			0x01
+
 /* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
 #define UAC_MS_HEADER			0x01
 #define UAC_MIDI_IN_JACK		0x02
@@ -463,31 +526,6 @@ struct uac_iso_endpoint_descriptor {
 #define UAC_EP_CS_ATTR_PITCH_CONTROL	0x02
 #define UAC_EP_CS_ATTR_FILL_MAX		0x80
 
-/* A.10.2 Feature Unit Control Selectors */
-
-#define UAC_FU_CONTROL_UNDEFINED	0x00
-#define UAC_MUTE_CONTROL		0x01
-#define UAC_VOLUME_CONTROL		0x02
-#define UAC_BASS_CONTROL		0x03
-#define UAC_MID_CONTROL			0x04
-#define UAC_TREBLE_CONTROL		0x05
-#define UAC_GRAPHIC_EQUALIZER_CONTROL	0x06
-#define UAC_AUTOMATIC_GAIN_CONTROL	0x07
-#define UAC_DELAY_CONTROL		0x08
-#define UAC_BASS_BOOST_CONTROL		0x09
-#define UAC_LOUDNESS_CONTROL		0x0a
-
-#define UAC_FU_MUTE		(1 << (UAC_MUTE_CONTROL - 1))
-#define UAC_FU_VOLUME		(1 << (UAC_VOLUME_CONTROL - 1))
-#define UAC_FU_BASS		(1 << (UAC_BASS_CONTROL - 1))
-#define UAC_FU_MID		(1 << (UAC_MID_CONTROL - 1))
-#define UAC_FU_TREBLE		(1 << (UAC_TREBLE_CONTROL - 1))
-#define UAC_FU_GRAPHIC_EQ	(1 << (UAC_GRAPHIC_EQUALIZER_CONTROL - 1))
-#define UAC_FU_AUTO_GAIN	(1 << (UAC_AUTOMATIC_GAIN_CONTROL - 1))
-#define UAC_FU_DELAY		(1 << (UAC_DELAY_CONTROL - 1))
-#define UAC_FU_BASS_BOOST	(1 << (UAC_BASS_BOOST_CONTROL - 1))
-#define UAC_FU_LOUDNESS		(1 << (UAC_LOUDNESS_CONTROL - 1))
-
 /* status word format (3.7.1.1) */
 
 #define UAC1_STATUS_TYPE_ORIG_MASK		0x0f
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 9149a84..2442819 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -78,39 +78,6 @@ enum {
 	USB_MIXER_U16,
 };
 
-enum {
-	USB_PROC_UPDOWN = 1,
-	USB_PROC_UPDOWN_SWITCH = 1,
-	USB_PROC_UPDOWN_MODE_SEL = 2,
-
-	USB_PROC_PROLOGIC = 2,
-	USB_PROC_PROLOGIC_SWITCH = 1,
-	USB_PROC_PROLOGIC_MODE_SEL = 2,
-
-	USB_PROC_3DENH = 3,
-	USB_PROC_3DENH_SWITCH = 1,
-	USB_PROC_3DENH_SPACE = 2,
-
-	USB_PROC_REVERB = 4,
-	USB_PROC_REVERB_SWITCH = 1,
-	USB_PROC_REVERB_LEVEL = 2,
-	USB_PROC_REVERB_TIME = 3,
-	USB_PROC_REVERB_DELAY = 4,
-
-	USB_PROC_CHORUS = 5,
-	USB_PROC_CHORUS_SWITCH = 1,
-	USB_PROC_CHORUS_LEVEL = 2,
-	USB_PROC_CHORUS_RATE = 3,
-	USB_PROC_CHORUS_DEPTH = 4,
-
-	USB_PROC_DCR = 6,
-	USB_PROC_DCR_SWITCH = 1,
-	USB_PROC_DCR_RATIO = 2,
-	USB_PROC_DCR_MAX_AMP = 3,
-	USB_PROC_DCR_THRESHOLD = 4,
-	USB_PROC_DCR_ATTACK = 5,
-	USB_PROC_DCR_RELEASE = 6,
-};
 
 /*E-mu 0202(0404) eXtension Unit(XU) control*/
 enum {
@@ -980,7 +947,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 
 	control++; /* change from zero-based to 1-based value */
 
-	if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
+	if (control == UAC_FU_GRAPHIC_EQUALIZER) {
 		/* FIXME: not supported yet */
 		return;
 	}
@@ -1036,8 +1003,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 				kctl->id.name, sizeof(kctl->id.name));
 
 	switch (control) {
-	case UAC_MUTE_CONTROL:
-	case UAC_VOLUME_CONTROL:
+	case UAC_FU_MUTE:
+	case UAC_FU_VOLUME:
 		/* 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
@@ -1064,9 +1031,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 				len = append_ctl_name(kctl, " Playback");
 			}
 		}
-		append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
+		append_ctl_name(kctl, control == UAC_FU_MUTE ?
 				" Switch" : " Volume");
-		if (control == UAC_VOLUME_CONTROL) {
+		if (control == UAC_FU_VOLUME) {
 			kctl->tlv.c = mixer_vol_tlv;
 			kctl->vd[0].access |= 
 				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
@@ -1165,7 +1132,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 &= ~UAC_FU_VOLUME;
+		master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
 		break;
 	}
 	if (channels > 0)
@@ -1410,51 +1377,51 @@ struct procunit_info {
 };
 
 static struct procunit_value_info updown_proc_info[] = {
-	{ USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
+	{ UAC_UD_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
 	{ 0 }
 };
 static struct procunit_value_info prologic_proc_info[] = {
-	{ USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
+	{ UAC_DP_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_DP_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
 	{ 0 }
 };
 static struct procunit_value_info threed_enh_proc_info[] = {
-	{ USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 },
+	{ UAC_3D_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_3D_SPACE, "Spaciousness", USB_MIXER_U8 },
 	{ 0 }
 };
 static struct procunit_value_info reverb_proc_info[] = {
-	{ USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
-	{ USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 },
-	{ USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 },
+	{ UAC_REVERB_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
+	{ UAC_REVERB_TIME, "Time", USB_MIXER_U16 },
+	{ UAC_REVERB_FEEDBACK, "Feedback", USB_MIXER_U8 },
 	{ 0 }
 };
 static struct procunit_value_info chorus_proc_info[] = {
-	{ USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
-	{ USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
-	{ USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
+	{ UAC_CHORUS_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
+	{ UAC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
+	{ UAC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
 	{ 0 }
 };
 static struct procunit_value_info dcr_proc_info[] = {
-	{ USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN },
-	{ USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 },
-	{ USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 },
-	{ USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
-	{ USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 },
-	{ USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 },
+	{ UAC_DCR_ENABLE, "Switch", USB_MIXER_BOOLEAN },
+	{ UAC_DCR_RATE, "Ratio", USB_MIXER_U16 },
+	{ UAC_DCR_MAXAMPL, "Max Amp", USB_MIXER_S16 },
+	{ UAC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
+	{ UAC_DCR_ATTACK_TIME, "Attack Time", USB_MIXER_U16 },
+	{ UAC_DCR_RELEASE_TIME, "Release Time", USB_MIXER_U16 },
 	{ 0 }
 };
 
 static struct procunit_info procunits[] = {
-	{ USB_PROC_UPDOWN, "Up Down", updown_proc_info },
-	{ USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info },
-	{ USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info },
-	{ USB_PROC_REVERB, "Reverb", reverb_proc_info },
-	{ USB_PROC_CHORUS, "Chorus", chorus_proc_info },
-	{ USB_PROC_DCR, "DCR", dcr_proc_info },
+	{ UAC_PROCESS_UP_DOWNMIX, "Up Down", updown_proc_info },
+	{ UAC_PROCESS_DOLBY_PROLOGIC, "Dolby Prologic", prologic_proc_info },
+	{ UAC_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", threed_enh_proc_info },
+	{ UAC_PROCESS_REVERB, "Reverb", reverb_proc_info },
+	{ UAC_PROCESS_CHORUS, "Chorus", chorus_proc_info },
+	{ UAC_PROCESS_DYN_RANGE_COMP, "DCR", dcr_proc_info },
 	{ 0 },
 };
 /*
@@ -1542,7 +1509,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
 		cval->channels = 1;
 
 		/* get min/max values */
-		if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
+		if (type == UAC_PROCESS_UP_DOWNMIX && cval->control == UAC_UD_MODE_SELECT) {
 			__u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
 			/* FIXME: hard-coded */
 			cval->min = 1;
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index d93fc89..f1324c4 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_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", UAC_BASS_CONTROL }, /* FU */
-	{ 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
+	{ 18, "Tone Control - Bass", UAC_FU_BASS }, /* FU */
+	{ 18, "Tone Control - Treble", UAC_FU_TREBLE }, /* FU */
 	{ 18, "Master Playback" }, /* FU; others */
 	/* 19: OT speaker */
 	/* 20: OT headphone */
-- 
1.7.1

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

* [PATCH 7/9] ALSA: usb-audio: add UAC2 sepecific Feature Unit controls
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (4 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 6/9] ALSA: usb-audio: unify constants from specification Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 8/9] ALSA: usb-audio: clean up find_audio_control_unit() Daniel Mack
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

The bits to enable them are always 0 for UAC1 devices, so no additional
checks are required.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/mixer.c |   16 ++++++++++------
 1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 2442819..8be6bf2 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -686,16 +686,20 @@ struct usb_feature_control_info {
 };
 
 static struct usb_feature_control_info audio_feature_info[] = {
-	{ "Mute",		USB_MIXER_INV_BOOLEAN },
-	{ "Volume",		USB_MIXER_S16 },
+	{ "Mute",			USB_MIXER_INV_BOOLEAN },
+	{ "Volume",			USB_MIXER_S16 },
 	{ "Tone Control - Bass",	USB_MIXER_S8 },
 	{ "Tone Control - Mid",		USB_MIXER_S8 },
 	{ "Tone Control - Treble",	USB_MIXER_S8 },
 	{ "Graphic Equalizer",		USB_MIXER_S8 }, /* FIXME: not implemeted yet */
-	{ "Auto Gain Control",	USB_MIXER_BOOLEAN },
-	{ "Delay Control",	USB_MIXER_U16 },
-	{ "Bass Boost",		USB_MIXER_BOOLEAN },
-	{ "Loudness",		USB_MIXER_BOOLEAN },
+	{ "Auto Gain Control",		USB_MIXER_BOOLEAN },
+	{ "Delay Control",		USB_MIXER_U16 },
+	{ "Bass Boost",			USB_MIXER_BOOLEAN },
+	{ "Loudness",			USB_MIXER_BOOLEAN },
+	/* UAC2 specific */
+	{ "Input Gain Control",		USB_MIXER_U16 },
+	{ "Input Gain Pad Control",	USB_MIXER_BOOLEAN },
+	{ "Phase Inverter Control",	USB_MIXER_BOOLEAN },
 };
 
 
-- 
1.7.1

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

* [PATCH 8/9] ALSA: usb-audio: clean up find_audio_control_unit()
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (5 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 7/9] ALSA: usb-audio: add UAC2 sepecific Feature Unit controls Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 11:35 ` [PATCH 9/9] ALSA: usb-audio: export UAC2 clock selectors as mixer controls Daniel Mack
  2010-05-31 16:35 ` [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Takashi Iwai
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

Use a struct to parse the audio units, and return usable descriptors
for all types. There's no need to limit the result set, except for some
kind of sanity check.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/mixer.c |   20 +++++++++++---------
 1 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 8be6bf2..cb34536 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -165,22 +165,24 @@ 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)
 {
-	unsigned char *p;
+	/* we just parse the header */
+	struct uac_feature_unit_descriptor *hdr = NULL;
 
-	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] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
-			return p;
+	while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
+					USB_DT_CS_INTERFACE)) != NULL) {
+		if (hdr->bLength >= 4 &&
+		    hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
+		    hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
+		    hdr->bUnitID == unit)
+			return hdr;
 	}
+
 	return NULL;
 }
 
-
 /*
  * copy a string with the given id
  */
-- 
1.7.1

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

* [PATCH 9/9] ALSA: usb-audio: export UAC2 clock selectors as mixer controls
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (6 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 8/9] ALSA: usb-audio: clean up find_audio_control_unit() Daniel Mack
@ 2010-05-31 11:35 ` Daniel Mack
  2010-05-31 16:35 ` [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Takashi Iwai
  8 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 11:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

The UAC2 clock selectors are fortunately compatible with UAC1 audio
selector units, so we can simply reuse the same approach to get all the
linked units.

Requests to this control need a different CS value though.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/mixer.c |   42 +++++++++++++++++++++++++++++++++++-------
 1 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index cb34536..a060d00 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -313,8 +313,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
 			      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);
+		snd_printk(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;
 	}
 
@@ -610,6 +610,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
  */
 static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
 {
+	int err;
 	void *p1;
 
 	memset(term, 0, sizeof(*term));
@@ -630,6 +631,11 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
 				term->channels = d->bNrChannels;
 				term->chconfig = le32_to_cpu(d->bmChannelConfig);
 				term->name = d->iTerminal;
+
+				/* call recursively to get the clock selectors */
+				err = check_input_term(state, d->bCSourceID, term);
+				if (err < 0)
+					return err;
 			}
 			return 0;
 		case UAC_FEATURE_UNIT: {
@@ -646,7 +652,8 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
 			term->name = uac_mixer_unit_iMixer(d);
 			return 0;
 		}
-		case UAC_SELECTOR_UNIT: {
+		case UAC_SELECTOR_UNIT:
+		case UAC2_CLOCK_SELECTOR: {
 			struct uac_selector_unit_descriptor *d = p1;
 			/* call recursively to retrieve the channel info */
 			if (check_input_term(state, d->baSourceID[0], term) < 0)
@@ -669,6 +676,13 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
 			term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
 			return 0;
 		}
+		case UAC2_CLOCK_SOURCE: {
+			struct uac_clock_source_descriptor *d = p1;
+			term->type = d->bDescriptorSubtype << 16; /* virtual type */
+			term->id = id;
+			term->name = d->iClockSource;
+			return 0;
+		}
 		default:
 			return -ENODEV;
 		}
@@ -1610,7 +1624,7 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
 	struct usb_mixer_elem_info *cval = kcontrol->private_data;
 	int val, err;
 
-	err = get_cur_ctl_value(cval, 0, &val);
+	err = get_cur_ctl_value(cval, cval->control << 8, &val);
 	if (err < 0) {
 		if (cval->mixer->ignore_ctl_error) {
 			ucontrol->value.enumerated.item[0] = 0;
@@ -1629,7 +1643,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
 	struct usb_mixer_elem_info *cval = kcontrol->private_data;
 	int val, oval, err;
 
-	err = get_cur_ctl_value(cval, 0, &oval);
+	err = get_cur_ctl_value(cval, cval->control << 8, &oval);
 	if (err < 0) {
 		if (cval->mixer->ignore_ctl_error)
 			return 0;
@@ -1638,7 +1652,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
 	val = ucontrol->value.enumerated.item[0];
 	val = get_abs_value(cval, val);
 	if (val != oval) {
-		set_cur_ctl_value(cval, 0, val);
+		set_cur_ctl_value(cval, cval->control << 8, val);
 		return 1;
 	}
 	return 0;
@@ -1720,6 +1734,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
 	cval->res = 1;
 	cval->initialized = 1;
 
+	if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
+		cval->control = UAC2_CX_CLOCK_SELECTOR;
+	else
+		cval->control = 0;
+
 	namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
 	if (! namelist) {
 		snd_printk(KERN_ERR "cannot malloc\n");
@@ -1769,7 +1788,9 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
 		if (! len)
 			strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name));
 
-		if ((state->oterm.type & 0xff00) == 0x0100)
+		if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
+			append_ctl_name(kctl, " Clock Source");
+		else if ((state->oterm.type & 0xff00) == 0x0100)
 			append_ctl_name(kctl, " Capture Source");
 		else
 			append_ctl_name(kctl, " Playback Source");
@@ -1803,10 +1824,12 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
 
 	switch (p1[2]) {
 	case UAC_INPUT_TERMINAL:
+	case UAC2_CLOCK_SOURCE:
 		return 0; /* NOP */
 	case UAC_MIXER_UNIT:
 		return parse_audio_mixer_unit(state, unitid, p1);
 	case UAC_SELECTOR_UNIT:
+	case UAC2_CLOCK_SELECTOR:
 		return parse_audio_selector_unit(state, unitid, p1);
 	case UAC_FEATURE_UNIT:
 		return parse_audio_feature_unit(state, unitid, p1);
@@ -1903,6 +1926,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 			err = parse_audio_unit(&state, desc->bSourceID);
 			if (err < 0)
 				return err;
+
+			/* for UAC2, use the same approach to also add the clock selectors */
+			err = parse_audio_unit(&state, desc->bCSourceID);
+			if (err < 0)
+				return err;
 		}
 	}
 
-- 
1.7.1

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

* Re: [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices
  2010-05-31 11:35 ` [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices Daniel Mack
@ 2010-05-31 12:51   ` Daniel Mack
  0 siblings, 0 replies; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 12:51 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, gregkh, clemens

On Mon, May 31, 2010 at 01:35:40PM +0200, Daniel Mack wrote:
> Audio devices which comply to the UAC2 standard can export complex clock
> topologies in its descriptors and set up links between them.
> 
> The entities that are defined are
> 
>  - clock sources, which define the end-leafs.
>  - clock selectors, which act as switch to select one out of many
>    possible clocks sources.
>  - clock multipliers, which have an input clock source, and act as clock
>    source again. They can be used to derive one clock from another.
> 
> All sample rate changes, clock validity queries and the like must go to
> clock source elements, while clock selectors and multipliers can be used
> as terminal clock source.
> 
> The following patch adds a parser for these elements and functions to
> iterate over the tree and find the leaf nodes (clock sources).
> 
> The samplerate set functions were moved to the new clock.c file.
> 
> Signed-off-by: Daniel Mack <daniel@caiaq.de>

Sorry, forgot to squash one hunk into this one.
New version of the patch is below.

Thanks,
Daniel


>From 39575e78644a2083682371b578f438d59ae612f5 Mon Sep 17 00:00:00 2001
From: Daniel Mack <daniel@caiaq.de>
Date: Thu, 27 May 2010 17:07:21 +0200
Subject: [PATCH] ALSA: usb-audio: parse clock topology of UAC2 devices

Audio devices which comply to the UAC2 standard can export complex clock
topologies in its descriptors and set up links between them.

The entities that are defined are

 - clock sources, which define the end-leafs.
 - clock selectors, which act as switch to select one out of many
   possible clocks sources.
 - clock multipliers, which have an input clock source, and act as clock
   source again. They can be used to derive one clock from another.

All sample rate changes, clock validity queries and the like must go to
clock source elements, while clock selectors and multipliers can be used
as terminal clock source.

The following patch adds a parser for these elements and functions to
iterate over the tree and find the leaf nodes (clock sources).

The samplerate set functions were moved to the new clock.c file.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
 sound/usb/Makefile   |    3 +-
 sound/usb/card.c     |   18 +---
 sound/usb/card.h     |    1 +
 sound/usb/clock.c    |  311 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sound/usb/clock.h    |   12 ++
 sound/usb/endpoint.c |   57 +++++++++-
 sound/usb/format.c   |   16 ++-
 sound/usb/pcm.c      |   98 +----------------
 sound/usb/usbaudio.h |    5 +-
 9 files changed, 396 insertions(+), 125 deletions(-)
 create mode 100644 sound/usb/clock.c
 create mode 100644 sound/usb/clock.h

diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e7ac7f4..1e362bf 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,7 +11,8 @@ snd-usb-audio-objs := 	card.o \
 			endpoint.o \
 			urb.o \
 			pcm.o \
-			helper.o
+			helper.o \
+			clock.o
 
 snd-usbmidi-lib-objs := midi.o
 
diff --git a/sound/usb/card.c b/sound/usb/card.c
index da1346b..7a8ac1d 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -236,7 +236,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 	}
 
 	case UAC_VERSION_2: {
-		struct uac_clock_source_descriptor *cs;
 		struct usb_interface_assoc_descriptor *assoc =
 			usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
 
@@ -245,21 +244,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 			return -EINVAL;
 		}
 
-		/* FIXME: for now, we expect there is at least one clock source
-		 * descriptor and we always take the first one.
-		 * We should properly support devices with multiple clock sources,
-		 * clock selectors and sample rate conversion units. */
-
-		cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
-						NULL, UAC2_CLOCK_SOURCE);
-
-		if (!cs) {
-			snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
-			return -EINVAL;
-		}
-
-		chip->clock_id = cs->bClockID;
-
 		for (i = 0; i < assoc->bInterfaceCount; i++) {
 			int intf = assoc->bFirstInterface + i;
 
@@ -481,6 +465,8 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 			goto __error;
 	}
 
+	chip->ctrl_intf = alts;
+
 	if (err > 0) {
 		/* create normal USB audio interfaces */
 		if (snd_usb_create_streams(chip, ifnum) < 0 ||
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ed92420..1febf2f 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -25,6 +25,7 @@ struct audioformat {
 	unsigned int rate_min, rate_max;	/* min/max rates */
 	unsigned int nr_rates;		/* number of rate table entries */
 	unsigned int *rate_table;	/* rate table */
+	unsigned char clock;		/* associated clock */
 };
 
 struct snd_usb_substream;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
new file mode 100644
index 0000000..b7aadd6
--- /dev/null
+++ b/sound/usb/clock.c
@@ -0,0 +1,311 @@
+/*
+ *   Clock domain and sample rate management functions
+ *
+ *   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/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#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>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "midi.h"
+#include "mixer.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "helper.h"
+#include "debug.h"
+#include "pcm.h"
+#include "urb.h"
+#include "format.h"
+
+static struct uac_clock_source_descriptor *
+	snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
+				  int clock_id)
+{
+	struct uac_clock_source_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_SOURCE))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static struct uac_clock_selector_descriptor *
+	snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
+				    int clock_id)
+{
+	struct uac_clock_selector_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_SELECTOR))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static struct uac_clock_multiplier_descriptor *
+	snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
+				      int clock_id)
+{
+	struct uac_clock_multiplier_descriptor *cs = NULL;
+
+	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
+					     ctrl_iface->extralen,
+					     cs, UAC2_CLOCK_MULTIPLIER))) {
+		if (cs->bClockID == clock_id)
+			return cs;
+	}
+
+	return NULL;
+}
+
+static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
+{
+	unsigned char buf;
+	int ret;
+
+	ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+			      UAC2_CS_CUR,
+			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+			      UAC2_CX_CLOCK_SELECTOR << 8, selector_id << 8,
+			      &buf, sizeof(buf), 1000);
+
+	if (ret < 0)
+		return ret;
+
+	return buf;
+}
+
+static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
+{
+	int err;
+	unsigned char data;
+	struct usb_device *dev = chip->dev;
+
+	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+			      UAC2_CS_CONTROL_CLOCK_VALID << 8, source_id << 8,
+			      &data, sizeof(data), 1000);
+
+	if (err < 0) {
+		snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
+			   __func__, source_id);
+		return err;
+	}
+
+	return !!data;
+}
+
+/* Try to find the clock source ID of a given clock entity */
+
+static int __uac_clock_find_source(struct snd_usb_audio *chip,
+				   struct usb_host_interface *host_iface,
+				   int entity_id, unsigned long *visited)
+{
+	struct uac_clock_source_descriptor *source;
+	struct uac_clock_selector_descriptor *selector;
+	struct uac_clock_multiplier_descriptor *multiplier;
+
+	entity_id &= 0xff;
+
+	if (test_and_set_bit(entity_id, visited)) {
+		snd_printk(KERN_WARNING
+			"%s(): recursive clock topology detected, id %d.\n",
+			__func__, entity_id);
+		return -EINVAL;
+	}
+
+	/* first, see if the ID we're looking for is a clock source already */
+	source = snd_usb_find_clock_source(host_iface, entity_id);
+	if (source)
+		return source->bClockID;
+
+	selector = snd_usb_find_clock_selector(host_iface, entity_id);
+	if (selector) {
+		int ret;
+
+		/* the entity ID we are looking for is a selector.
+		 * find out what it currently selects */
+		ret = uac_clock_selector_get_val(chip, selector->bClockID);
+		if (ret < 0)
+			return ret;
+
+		if (ret > selector->bNrInPins || ret < 1) {
+			printk(KERN_ERR
+				"%s(): selector reported illegal value, id %d, ret %d\n",
+				__func__, selector->bClockID, ret);
+
+			return -EINVAL;
+		}
+
+		return __uac_clock_find_source(chip, host_iface,
+					       selector->baCSourceID[ret-1],
+					       visited);
+	}
+
+	/* FIXME: multipliers only act as pass-thru element for now */
+	multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
+	if (multiplier)
+		return __uac_clock_find_source(chip, host_iface,
+					       multiplier->bCSourceID, visited);
+
+	return -EINVAL;
+}
+
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+			      struct usb_host_interface *host_iface,
+			      int entity_id)
+{
+	DECLARE_BITMAP(visited, 256);
+	memset(visited, 0, sizeof(visited));
+	return __uac_clock_find_source(chip, host_iface, entity_id, visited);
+}
+
+static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
+			      struct usb_host_interface *alts,
+			      struct audioformat *fmt, int rate)
+{
+	struct usb_device *dev = chip->dev;
+	unsigned int ep;
+	unsigned char data[3];
+	int err, crate;
+
+	ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+	/* if endpoint doesn't have sampling rate control, bail out */
+	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
+		snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
+				   dev->devnum, iface, fmt->altsetting);
+		return 0;
+	}
+
+	data[0] = rate;
+	data[1] = rate >> 8;
+	data[2] = rate >> 16;
+	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
+			   dev->devnum, iface, fmt->altsetting, rate, ep);
+		return err;
+	}
+
+	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
+				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
+			   dev->devnum, iface, fmt->altsetting, ep);
+		return 0; /* some devices don't support reading */
+	}
+
+	crate = data[0] | (data[1] << 8) | (data[2] << 16);
+	if (crate != rate) {
+		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+		// runtime->rate = crate;
+	}
+
+	return 0;
+}
+
+static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+			      struct usb_host_interface *alts,
+			      struct audioformat *fmt, int rate)
+{
+	struct usb_device *dev = chip->dev;
+	unsigned char data[4];
+	int err, crate;
+	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
+
+	if (clock < 0)
+		return clock;
+
+	if (!uac_clock_source_is_valid(chip, clock)) {
+		snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
+			   dev->devnum, iface, fmt->altsetting, clock);
+		return -ENXIO;
+	}
+
+	data[0] = rate;
+	data[1] = rate >> 8;
+	data[2] = rate >> 16;
+	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,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 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);
+		return err;
+	}
+
+	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+				   UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
+				   data, sizeof(data), 1000)) < 0) {
+		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
+			   dev->devnum, iface, fmt->altsetting);
+		return err;
+	}
+
+	crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+	if (crate != rate)
+		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+
+	return 0;
+}
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+			     struct usb_host_interface *alts,
+			     struct audioformat *fmt, int rate)
+{
+	struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+	switch (altsd->bInterfaceProtocol) {
+	case UAC_VERSION_1:
+		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
+
+	case UAC_VERSION_2:
+		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+	}
+
+	return -EINVAL;
+}
+
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
new file mode 100644
index 0000000..beb2536
--- /dev/null
+++ b/sound/usb/clock.h
@@ -0,0 +1,12 @@
+#ifndef __USBAUDIO_CLOCK_H
+#define __USBAUDIO_CLOCK_H
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+			     struct usb_host_interface *alts,
+			     struct audioformat *fmt, int rate);
+
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+			      struct usb_host_interface *host_iface,
+			      int entity_id);
+
+#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 28ee1ce..9593b91 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -190,6 +190,38 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
 	return attributes;
 }
 
+static struct uac2_input_terminal_descriptor *
+	snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+					       int terminal_id)
+{
+	struct uac2_input_terminal_descriptor *term = NULL;
+
+	while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+					       ctrl_iface->extralen,
+					       term, UAC_INPUT_TERMINAL))) {
+		if (term->bTerminalID == terminal_id)
+			return term;
+	}
+
+	return NULL;
+}
+
+static struct uac2_output_terminal_descriptor *
+	snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+						int terminal_id)
+{
+	struct uac2_output_terminal_descriptor *term = NULL;
+
+	while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+					       ctrl_iface->extralen,
+					       term, UAC_OUTPUT_TERMINAL))) {
+		if (term->bTerminalID == terminal_id)
+			return term;
+	}
+
+	return NULL;
+}
+
 int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 {
 	struct usb_device *dev;
@@ -199,7 +231,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 	int i, altno, err, stream;
 	int format = 0, num_channels = 0;
 	struct audioformat *fp = NULL;
-	int num, protocol;
+	int num, protocol, clock = 0;
 	struct uac_format_type_i_continuous_descriptor *fmt;
 
 	dev = chip->dev;
@@ -263,6 +295,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 		}
 
 		case UAC_VERSION_2: {
+			struct uac2_input_terminal_descriptor *input_term;
+			struct uac2_output_terminal_descriptor *output_term;
 			struct uac_as_header_descriptor_v2 *as =
 				snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
 
@@ -281,7 +315,25 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 			num_channels = as->bNrChannels;
 			format = le32_to_cpu(as->bmFormats);
 
-			break;
+			/* lookup the terminal associated to this interface
+			 * to extract the clock */
+			input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+									    as->bTerminalLink);
+			if (input_term) {
+				clock = input_term->bCSourceID;
+				break;
+			}
+
+			output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+									      as->bTerminalLink);
+			if (output_term) {
+				clock = output_term->bCSourceID;
+				break;
+			}
+
+			snd_printk(KERN_ERR "%d:%u:%d : bogus bTerminalLink %d\n",
+				   dev->devnum, iface_no, altno, as->bTerminalLink);
+			continue;
 		}
 
 		default:
@@ -338,6 +390,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 			fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
 					* (fp->maxpacksize & 0x7ff);
 		fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
+		fp->clock = clock;
 
 		/* some quirks for attributes here */
 
diff --git a/sound/usb/format.c b/sound/usb/format.c
index fe29d61..5367cd1 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -29,6 +29,7 @@
 #include "quirks.h"
 #include "helper.h"
 #include "debug.h"
+#include "clock.h"
 
 /*
  * parse the audio format type I descriptor
@@ -215,15 +216,17 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
 	struct usb_device *dev = chip->dev;
 	unsigned char tmp[2], *data;
 	int i, nr_rates, data_size, ret = 0;
+	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
 
 	/* 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,
-			      UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+			      UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
 			      tmp, sizeof(tmp), 1000);
 
 	if (ret < 0) {
-		snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
+		snd_printk(KERN_ERR "%s(): unable to retrieve number of sample rates (clock %d)\n",
+				__func__, clock);
 		goto err;
 	}
 
@@ -237,12 +240,13 @@ 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,
-			       UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
-			       data, data_size, 1000);
+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+			      UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
+			      data, data_size, 1000);
 
 	if (ret < 0) {
-		snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
+		snd_printk(KERN_ERR "%s(): unable to retrieve sample rate range (clock %d)\n",
+				__func__, clock);
 		ret = -EINVAL;
 		goto err_free;
 	}
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 056587d..4568298 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -31,6 +31,7 @@
 #include "urb.h"
 #include "helper.h"
 #include "pcm.h"
+#include "clock.h"
 
 /*
  * return the current pcm pointer.  just based on the hwptr_done value.
@@ -181,103 +182,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
 	return -EINVAL;
 }
 
-static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
-			      struct usb_host_interface *alts,
-			      struct audioformat *fmt, int rate)
-{
-	struct usb_device *dev = chip->dev;
-	unsigned int ep;
-	unsigned char data[3];
-	int err, crate;
-
-	ep = get_endpoint(alts, 0)->bEndpointAddress;
-	/* if endpoint doesn't have sampling rate control, bail out */
-	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
-		snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
-				   dev->devnum, iface, fmt->altsetting);
-		return 0;
-	}
-
-	data[0] = rate;
-	data[1] = rate >> 8;
-	data[2] = rate >> 16;
-	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-				   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-				   data, sizeof(data), 1000)) < 0) {
-		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
-			   dev->devnum, iface, fmt->altsetting, rate, ep);
-		return err;
-	}
-	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
-				   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
-				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-				   data, sizeof(data), 1000)) < 0) {
-		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
-			   dev->devnum, iface, fmt->altsetting, ep);
-		return 0; /* some devices don't support reading */
-	}
-	crate = data[0] | (data[1] << 8) | (data[2] << 16);
-	if (crate != rate) {
-		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-		// runtime->rate = crate;
-	}
-
-	return 0;
-}
-
-static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
-			      struct usb_host_interface *alts,
-			      struct audioformat *fmt, int rate)
-{
-	struct usb_device *dev = chip->dev;
-	unsigned char data[4];
-	int err, crate;
-
-	data[0] = rate;
-	data[1] = rate >> 8;
-	data[2] = rate >> 16;
-	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,
-				   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);
-		return err;
-	}
-	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
-				   USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-				   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);
-		return err;
-	}
-	crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
-	if (crate != rate)
-		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-
-	return 0;
-}
-
-int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
-			     struct usb_host_interface *alts,
-			     struct audioformat *fmt, int rate)
-{
-	struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
-	switch (altsd->bInterfaceProtocol) {
-	case UAC_VERSION_1:
-		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
-
-	case UAC_VERSION_2:
-		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
-	}
-
-	return -EINVAL;
-}
-
 /*
  * find a matching format and set up the interface
  */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 06ebf24..24d3319 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -40,9 +40,6 @@ struct snd_usb_audio {
 	int num_interfaces;
 	int num_suspended_intf;
 
-	/* for audio class v2 */
-	int clock_id;
-
 	struct list_head pcm_list;	/* list of pcm streams */
 	int pcm_devs;
 
@@ -53,6 +50,8 @@ struct snd_usb_audio {
 	int setup;			/* from the 'device_setup' module param */
 	int nrpacks;			/* from the 'nrpacks' module param */
 	int async_unlink;		/* from the 'async_unlink' module param */
+
+	struct usb_host_interface *ctrl_intf;	/* the audio control interface */
 };
 
 /*
-- 
1.7.1

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

* Re: [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls
  2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
                   ` (7 preceding siblings ...)
  2010-05-31 11:35 ` [PATCH 9/9] ALSA: usb-audio: export UAC2 clock selectors as mixer controls Daniel Mack
@ 2010-05-31 16:35 ` Takashi Iwai
  2010-05-31 16:47   ` Daniel Mack
  8 siblings, 1 reply; 13+ messages in thread
From: Takashi Iwai @ 2010-05-31 16:35 UTC (permalink / raw)
  To: Daniel Mack; +Cc: alsa-devel, gregkh, clemens

At Mon, 31 May 2010 13:35:36 +0200,
Daniel Mack wrote:
> 
> Introduce two new static inline functions for a more readable parsing
> of UAC2 bmaControls.
> 
> Signed-off-by: Daniel Mack <daniel@caiaq.de>

Thanks!  Now I applied all patches for 2.6.35-rc2.

But, basically I don't want to touch too much after this.
So, please avoid refactoring patches for 2.6.35.  If any, prepare a
separate one for 2.6.36, and let's concentrate on fixes during
2.6.35-rc.


Takashi

> ---
>  include/linux/usb/audio-v2.h |   10 ++++++++++
>  sound/usb/mixer.c            |    8 ++++----
>  2 files changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
> index 92f1d99..8f91be3 100644
> --- a/include/linux/usb/audio-v2.h
> +++ b/include/linux/usb/audio-v2.h
> @@ -18,6 +18,16 @@
>  /* v1.0 and v2.0 of this standard have many things in common. For the rest
>   * of the definitions, please refer to audio.h */
>  
> +static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
> +{
> +	return (bmControls >> (control * 2)) & 0x1;
> +}
> +
> +static inline bool uac2_control_is_writeable(u32 bmControls, u8 control)
> +{
> +	return (bmControls >> (control * 2)) & 0x2;
> +}
> +
>  /* 4.7.2.1 Clock Source Descriptor */
>  
>  struct uac_clock_source_descriptor {
> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
> index 03ce971..43d6417 100644
> --- a/sound/usb/mixer.c
> +++ b/sound/usb/mixer.c
> @@ -1188,9 +1188,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
>  
>  			for (j = 0; j < channels; j++) {
>  				unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
> -				if (mask & (1 << (i * 2))) {
> +				if (uac2_control_is_readable(mask, i)) {
>  					ch_bits |= (1 << j);
> -					if (~mask & (1 << ((i * 2) + 1)))
> +					if (!uac2_control_is_writeable(mask, i))
>  						ch_read_only |= (1 << j);
>  				}
>  			}
> @@ -1198,9 +1198,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
>  			/* 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))
> +			if (uac2_control_is_readable(master_bits, i))
>  				build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
> -						  ~master_bits & (1 << ((i * 2) + 1)));
> +						  !uac2_control_is_writeable(master_bits, i));
>  		}
>  	}
>  
> -- 
> 1.7.1
> 

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

* Re: [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls
  2010-05-31 16:35 ` [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Takashi Iwai
@ 2010-05-31 16:47   ` Daniel Mack
  2010-05-31 20:35     ` Takashi Iwai
  0 siblings, 1 reply; 13+ messages in thread
From: Daniel Mack @ 2010-05-31 16:47 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, gregkh, clemens

On Mon, May 31, 2010 at 06:35:22PM +0200, Takashi Iwai wrote:
> At Mon, 31 May 2010 13:35:36 +0200,
> Daniel Mack wrote:
> > 
> > Introduce two new static inline functions for a more readable parsing
> > of UAC2 bmaControls.
> > 
> > Signed-off-by: Daniel Mack <daniel@caiaq.de>
> 
> Thanks!  Now I applied all patches for 2.6.35-rc2.
> 
> But, basically I don't want to touch too much after this.
> So, please avoid refactoring patches for 2.6.35.  If any, prepare a
> separate one for 2.6.36, and let's concentrate on fixes during
> 2.6.35-rc.

Yes, I'm aware of this, and I'm not planning to move this code around
much in the next weeks, except for bug fixing.

In general, UAC2 devices are now almost fully supported. There are some
special controllers missing like graphical equalizers, but that can be
done when devices appear which really implement such things.

Happy testing :)

Thanks,
Daniel

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

* Re: [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls
  2010-05-31 16:47   ` Daniel Mack
@ 2010-05-31 20:35     ` Takashi Iwai
  0 siblings, 0 replies; 13+ messages in thread
From: Takashi Iwai @ 2010-05-31 20:35 UTC (permalink / raw)
  To: Daniel Mack; +Cc: alsa-devel, gregkh, clemens

At Mon, 31 May 2010 18:47:16 +0200,
Daniel Mack wrote:
> 
> On Mon, May 31, 2010 at 06:35:22PM +0200, Takashi Iwai wrote:
> > At Mon, 31 May 2010 13:35:36 +0200,
> > Daniel Mack wrote:
> > > 
> > > Introduce two new static inline functions for a more readable parsing
> > > of UAC2 bmaControls.
> > > 
> > > Signed-off-by: Daniel Mack <daniel@caiaq.de>
> > 
> > Thanks!  Now I applied all patches for 2.6.35-rc2.
> > 
> > But, basically I don't want to touch too much after this.
> > So, please avoid refactoring patches for 2.6.35.  If any, prepare a
> > separate one for 2.6.36, and let's concentrate on fixes during
> > 2.6.35-rc.
> 
> Yes, I'm aware of this, and I'm not planning to move this code around
> much in the next weeks, except for bug fixing.

That's helpful.

> In general, UAC2 devices are now almost fully supported. There are some
> special controllers missing like graphical equalizers, but that can be
> done when devices appear which really implement such things.
> 
> Happy testing :)

Great, thanks for the info update.


Takashi

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

end of thread, other threads:[~2010-05-31 20:35 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-31 11:35 [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Daniel Mack
2010-05-31 11:35 ` [PATCH 2/9] ALSA: usb-audio: support partially write-protected UAC2 controls Daniel Mack
2010-05-31 11:35 ` [PATCH 3/9] include/linux/usb/audio-v2.h: add more UAC2 details Daniel Mack
2010-05-31 11:35 ` [PATCH 4/9] ALSA: usb-audio: fix selector unit string index accessor Daniel Mack
2010-05-31 11:35 ` [PATCH 5/9] ALSA: usb-audio: parse clock topology of UAC2 devices Daniel Mack
2010-05-31 12:51   ` Daniel Mack
2010-05-31 11:35 ` [PATCH 6/9] ALSA: usb-audio: unify constants from specification Daniel Mack
2010-05-31 11:35 ` [PATCH 7/9] ALSA: usb-audio: add UAC2 sepecific Feature Unit controls Daniel Mack
2010-05-31 11:35 ` [PATCH 8/9] ALSA: usb-audio: clean up find_audio_control_unit() Daniel Mack
2010-05-31 11:35 ` [PATCH 9/9] ALSA: usb-audio: export UAC2 clock selectors as mixer controls Daniel Mack
2010-05-31 16:35 ` [PATCH 1/9] ALSA: usb-audio: UAC2: clean up parsing of bmaControls Takashi Iwai
2010-05-31 16:47   ` Daniel Mack
2010-05-31 20:35     ` Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).