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

Takashi Iwai tiwai at suse.de
Thu Nov 29 09:02:07 CET 2012


At Wed, 28 Nov 2012 23:55:33 +0100,
Eldad Zack wrote:
> 
> 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;

Better to rename for avoiding misunderstanding.
"direction" sounds like the direction of this substream.

> +	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;
> +	}

This sanity check would suit better where the implicit_fb is checked.
It doesn't have to be performed at each time.

And, the whole restriction you introduced is only for implicit fb
case, no?

> +
> +	/*
> +	 * 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) {

Cleaner to use list_for_each_entry().
(Yes, there are other codes using list_for_each() there, but remember
 how old the usb-audio driver code is :)

> +		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;
> +			}

Can be simplified like

		if (!sync_channels || fp->channels == sub->channels) {
			sync_channels = fp->channels;
			sync_fp = fp;
		}

But, I wonder whether you don't have to check other parameters such as
rate, format, etc?


thanks,

Takashi

> +		}
> +	}
> +
> +	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