[alsa-devel] Buffer size for ALSA USB PCM audio

Takashi Iwai tiwai at suse.de
Mon Jul 29 12:03:32 CEST 2013


At Thu, 25 Jul 2013 10:50:49 -0400 (EDT),
Alan Stern wrote:
> 
> On Thu, 25 Jul 2013, Takashi Iwai wrote:
> 
> > > Besides, what's the reason for allocating 10 URBs if you're not going 
> > > to submit more than 2 of them at a time?
> > 
> > I don't know how you deduced 10 urbs in your example,
> 
> I made it up.  :-)
> 
> >  but in general,
> > ep->nurbs is calculated so that the driver can hold at least two
> > ep->periods (i.e. double buffer).  The USB audio driver has
> > essentially two buffers: an internal hardware buffer via URBs and an
> > intermediate buffer via vmalloc.  The latter is exposed to user-space
> > and its content is copied to the URBs appropriately via complete
> > callbacks.
> > 
> > Due to this design, we just need two periods for URB buffers,
> > ideally, no matter how many periods are used in the latter buffer
> > specified by user.  That's why no buffer_size is needed in ep->nurbs 
> > estimation.  The actual calculation is, however, a bit more
> > complicated to achieve enough fine-grained updates but yet not too big
> > buffer size.  I guess this can be simplified / improved.
> 
> Ah, that makes sense.  Thanks for the explanation.
> 
> The existing code has a problem: Under some conditions the URB queue
> will be too short.  EHCI requires at least 10 microframes on the queue
> at all times (even when an URB is completing and is therefore no longer
> on the queue) to avoid underruns.  Well, 9 microframes is probably good
> enough, but I'll use 10 to be safe.  A typical arrangement of 2 URBs
> each with 8 packets each (at 1 packet/uframe) violates this constraint,
> and I have seen underruns in practice.
> 
> The patch below fixes this problem and drastically simplifies the 
> calculation.  How does it look?

Looks good through a quick glance.  But I'd rather like to let review
Clemens and Daniel as I already forgot the old voodoo logic of the
current driver code :)


Thanks!

Takashi


> 
> Alan Stern
> 
> 
> 
> Index: usb-3.10/sound/usb/endpoint.c
> ===================================================================
> --- usb-3.10.orig/sound/usb/endpoint.c
> +++ usb-3.10/sound/usb/endpoint.c
> @@ -575,6 +575,7 @@ static int data_ep_set_params(struct snd
>  			      struct snd_usb_endpoint *sync_ep)
>  {
>  	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
> +	unsigned int min_queued_packs, max_packs;
>  	int is_playback = usb_pipeout(ep->pipe);
>  	int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
>  
> @@ -608,11 +609,21 @@ static int data_ep_set_params(struct snd
>  	else
>  		ep->curpacksize = maxsize;
>  
> -	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
> +	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) {
>  		packs_per_ms = 8 >> ep->datainterval;
> -	else
> +
> +		/* high speed needs 10 USB uframes on the queue at all times */
> +		min_queued_packs = DIV_ROUND_UP(10, 8 / packs_per_ms);
> +		max_packs = MAX_PACKS_HS;
> +	} else {
>  		packs_per_ms = 1;
>  
> +		/* full speed needs one USB frame on the queue at all times */
> +		min_queued_packs = 1;
> +		max_packs = MAX_PACKS;
> +	}
> +	max_packs = min(max_packs, MAX_QUEUE * packs_per_ms);
> +
>  	if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
>  		urb_packs = max(ep->chip->nrpacks, 1);
>  		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
> @@ -625,42 +636,18 @@ static int data_ep_set_params(struct snd
>  	if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
>  		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
>  
> -	/* decide how many packets to be used */
> -	if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
> -		unsigned int minsize, maxpacks;
> -		/* determine how small a packet can be */
> -		minsize = (ep->freqn >> (16 - ep->datainterval))
> -			  * (frame_bits >> 3);
> -		/* with sync from device, assume it can be 12% lower */
> -		if (sync_ep)
> -			minsize -= minsize >> 3;
> -		minsize = max(minsize, 1u);
> -		total_packs = (period_bytes + minsize - 1) / minsize;
> -		/* we need at least two URBs for queueing */
> -		if (total_packs < 2) {
> -			total_packs = 2;
> -		} else {
> -			/* and we don't want too long a queue either */
> -			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
> -			total_packs = min(total_packs, maxpacks);
> -		}
> -	} else {
> -		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
> -			urb_packs >>= 1;
> -		total_packs = MAX_URBS * urb_packs;
> -	}
> -
> -	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
> -	if (ep->nurbs > MAX_URBS) {
> -		/* too much... */
> -		ep->nurbs = MAX_URBS;
> -		total_packs = MAX_URBS * urb_packs;
> -	} else if (ep->nurbs < 2) {
> -		/* too little - we need at least two packets
> -		 * to ensure contiguous playback/capture
> -		 */
> -		ep->nurbs = 2;
> +	/* each URB must fit into one period */
> +	urb_packs = min(urb_packs, period_bytes / maxsize);
> +	urb_packs = max(1u, urb_packs);
> +
> +	/* at least one more URB than the minimum queue size */
> +	ep->nurbs = 1 + DIV_ROUND_UP(min_queued_packs, urb_packs);
> +
> +	if (ep->nurbs * urb_packs > max_packs) {
> +		/* too much -- URBs have too many packets */
> +		urb_packs = max_packs / ep->nurbs;
>  	}
> +	total_packs = ep->nurbs * urb_packs;
>  
>  	/* allocate and initialize data urbs */
>  	for (i = 0; i < ep->nurbs; i++) {
> 


More information about the Alsa-devel mailing list