[alsa-devel] [FT C400, PATCH RFC, v4 02/10] usb-audio: correct sync ep init

Eldad Zack eldad at fogrefinery.com
Wed Nov 28 23:55:33 CET 2012


Commit 947d299686aa9cc8aecf749d54e8475c6e498956
"ALSA: snd-usb: properly initialize the sync endpoint" introduced a
regression for devices that has a sync endpoint with a channel
number different than the data endpoint channel.
Due to a different channel and period bytes count, attempting to
initialize the sync endpoint will fail. With xhci it yields:

cannot submit urb 0, error -90: internal error

With this patch, if a sync endpoint has multiple audioformats, an
audioformat with a matching number of channels is preferred. Otherwise,
an audioformat will be picked randomly (i.e., first audioformat with a
non-zero channel count) and the period bytes count is adjusted
accordingly.

Cc: Jeffrey Barish <jeff_barish at earthlink.net>
Cc: Daniel Mack <zonque at gmail.com>
Signed-off-by: Eldad Zack <eldad at fogrefinery.com>
---
 sound/usb/pcm.c |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index ff8cbbf..2158943 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -438,6 +438,76 @@ add_sync_ep:
 }
 
 /*
+ * Configure the sync ep using the rate and pcm format of the data ep.
+ */
+static int configure_sync_endpoint(struct snd_usb_substream *subs)
+{
+	int ret;
+	struct list_head *p;
+	struct audioformat *sync_fp;
+	int sync_channels = 0;
+	int sync_period_bytes = subs->period_bytes;
+	int direction = subs->direction ^ 1;
+	struct snd_usb_substream *sync_subs =
+		&subs->stream->substream[direction];
+
+	if (sync_subs->ep_num != subs->sync_endpoint->ep_num) {
+		snd_printk(KERN_ERR "%s: cannot find the sync ep (direction %d, found %x, wanted %x).\n",
+			__func__, direction,
+				subs->stream->substream[direction].ep_num,
+				subs->sync_endpoint->ep_num);
+		return -EINVAL;
+	}
+
+	/*
+	 * Try to find a format with a number of channels matching
+	 * the data substream. If no such format was found, pick one
+	 * with non zero channel number at random.
+	 */
+	list_for_each(p, &sync_subs->fmt_list) {
+		struct audioformat *fp;
+		fp = list_entry(p, struct audioformat, list);
+
+		if (sync_channels == 0) {
+			sync_fp = fp;
+			sync_channels = fp->channels;
+		} else {
+			if (fp->channels == subs->channels) {
+				sync_channels = fp->channels;
+				sync_fp = fp;
+			}
+		}
+	}
+
+	if (sync_channels < 1) {
+		snd_printk(KERN_ERR "%s: no valid audioformat for sync ep %x found\n",
+			__func__, sync_subs->ep_num);
+		return -EINVAL;
+	}
+
+	/*
+	 * Recalculated the period bytes if channel number differ between
+	 * data and sync ep.
+	 */
+	if (sync_channels != subs->channels) {
+		sync_period_bytes = (subs->period_bytes / subs->channels) *
+			sync_channels;
+		snd_printdd(KERN_INFO "%s: sync ep period bytes recalc %d -> %d\n",
+			__func__, subs->period_bytes, sync_period_bytes);
+	}
+
+	ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+					  subs->pcm_format,
+					  sync_channels,
+					  sync_period_bytes,
+					  subs->cur_rate,
+					  sync_fp,
+					  NULL);
+
+	return ret;
+}
+
+/*
  * configure endpoint params
  *
  * called  during initial setup and upon resume
@@ -459,13 +529,8 @@ static int configure_endpoint(struct snd_usb_substream *subs)
 		return ret;
 
 	if (subs->sync_endpoint)
-		ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
-						  subs->pcm_format,
-						  subs->channels,
-						  subs->period_bytes,
-						  subs->cur_rate,
-						  subs->cur_audiofmt,
-						  NULL);
+		ret = configure_sync_endpoint(subs);
+
 	return ret;
 }
 
-- 
1.7.8.6



More information about the Alsa-devel mailing list