[alsa-devel] [PATCH sound-unstable] usb-audio: sync ep init fix for audioformat mistmatch

Takashi Iwai tiwai at suse.de
Mon Dec 3 09:58:39 CET 2012


At Sat,  1 Dec 2012 23:50:26 +0100,
Eldad Zack wrote:
> 
> Commit 947d299686aa9cc8aecf749d54e8475c6e498956 , "ALSA: snd-usb:
> properly initialize the sync endpoint", while correcting the
> initialization of the sync endpoint when opening just the data
> endpoint, prevents devices that has a sync endpoint with a channel
> number different than the data endpoint channel from functioning.
> Due to a different channel and period bytes count. attempting to
> initialize the sync endpoint will fail at the usb host driver:
> (example, when using xhci:)
> 
>  cannot submit urb 0, error -90: internal error
> 
> With this patch, if a sync endpoint has multiple audioformats, a
> matching audioformat is perferred. An audioformat must be found
> with at least one channel and support the requested sample rate
> and PCM format, otherwise the stream will not be opened.
> 
> If the number of channels differ between the selected audioformat
> and the requested format, adjust the period bytes count accordingly.
> It is safe to perform the calculation on the basis of the channel
> count, since the PCM audio format and the rate must be supported by
> the selected audioformat.
> 
> 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 |  106 +++++++++++++++++++++++++++++++++++++++++++++++++++----
>  1 files changed, 99 insertions(+), 7 deletions(-)
> 
> diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
> index 5d3cf85..e539a5a 100644
> --- a/sound/usb/pcm.c
> +++ b/sound/usb/pcm.c
> @@ -451,6 +451,103 @@ add_sync_ep:
>  }
>  
>  /*
> + * Return the score of matching two audioformats.
> + * Veto the audioformat if:
> + * - It has no channels for some reason.
> + * - Requested PCM format is not supported.
> + * - Requested sample rate is not supported.
> + */
> +static int match_endpoint_audioformats(struct audioformat *fp,
> +	struct audioformat *match, int rate,
> +	snd_pcm_format_t pcm_format)
> +{
> +	int i;
> +	int score = 0;
> +
> +	if (fp->channels < 1) {
> +		snd_printdd("%s: (fmt @%p) no channels\n", __func__, fp);
> +		return 0;
> +	}
> +
> +	if (!(fp->formats && pcm_format)) {
> +		snd_printdd("%s: (fmt @%p) no match for format %d\n", __func__,
> +			fp, pcm_format);
> +		return 0;
> +	}
> +
> +	for (i = 0; i < 4; i++) {

Use fp->nr_rates instead of 4.
Otherwise looks good to me.


thanks,

Takashi


> +		if (fp->rate_table[i] == rate) {
> +			score++;
> +			break;
> +		}
> +	}
> +	if (!score) {
> +		snd_printdd("%s: (fmt @%p) no match for rate %d\n", __func__,
> +			fp, rate);
> +		return 0;
> +	}
> +
> +	if (fp->channels == match->channels)
> +		score++;
> +
> +	snd_printdd("%s: (fmt @%p) score %d\n", __func__, fp, score);
> +
> +	return score;
> +}
> +
> +/*
> + * 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 audioformat *fp;
> +	struct audioformat *sync_fp = NULL;
> +	int cur_score = 0;
> +	int sync_period_bytes = subs->period_bytes;
> +	struct snd_usb_substream *sync_subs =
> +		&subs->stream->substream[subs->direction ^ 1];
> +
> +	/* Try to find the best matching audioformat. */
> +	list_for_each_entry(fp, &sync_subs->fmt_list, list) {
> +		int score = match_endpoint_audioformats(fp, subs->cur_audiofmt,
> +			subs->cur_rate, subs->pcm_format);
> +
> +		if (score > cur_score) {
> +			sync_fp = fp;
> +			cur_score = score;
> +		}
> +	}
> +
> +	if (unlikely(sync_fp == NULL)) {
> +		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 audioformat.
> +	 */
> +	if (sync_fp->channels != subs->channels) {
> +		sync_period_bytes = (subs->period_bytes / subs->channels) *
> +			sync_fp->channels;
> +		snd_printdd("%s: adjusted sync ep period bytes (%d -> %d)\n",
> +			__func__, subs->period_bytes, sync_period_bytes);
> +	}
> +
> +	ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
> +					  subs->pcm_format,
> +					  sync_fp->channels,
> +					  sync_period_bytes,
> +					  subs->cur_rate,
> +					  sync_fp,
> +					  NULL);
> +
> +	return ret;
> +}
> +
> +/*
>   * configure endpoint params
>   *
>   * called  during initial setup and upon resume
> @@ -472,13 +569,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