Re: [PATCH v6 5/5] usb: gadget: f_uac2: send reasonably sized packets

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Aug 27, 2014 at 10:39 PM, Daniel Mack <zonque@xxxxxxxxx> wrote:
> The UAC2 function driver currently responds to all packets at all times
> with wMaxPacketSize packets. That results in way too fast audio
> playback as the function driver (which is in fact supposed to define
> the audio stream pace) delivers as fast as it can.
>
> Fix this by sizing each packet correctly with the following steps:
>
>  a) Set the packet's size by dividing the nominal data rate by the
>     playback endpoint's interval.
>
>  b) If there is a residual value from the calculation in a), add
>     it to a accumulator to keep track of it across packets.
>
>  c) If the accumulator has gathered at least the number of bytes
>     that are needed for one sample frame, increase the packet size.
>
> This way, the packet size calculation will get rid of any kind of
> imprecision that would otherwise occur with a simple division over
> time.
>
> Some of the variables that are needed while processing each packet
> are pre-computed for performance reasons.
>
> Signed-off-by: Daniel Mack <zonque@xxxxxxxxx>
> ---
>  drivers/usb/gadget/function/f_uac2.c | 69 +++++++++++++++++++++++++++++++++---
>  1 file changed, 65 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
> index 246a778..a5a27a5 100644
> --- a/drivers/usb/gadget/function/f_uac2.c
> +++ b/drivers/usb/gadget/function/f_uac2.c
> @@ -92,6 +92,15 @@ struct snd_uac2_chip {
>
>         struct snd_card *card;
>         struct snd_pcm *pcm;
> +
> +       /* timekeeping for the playback endpoint */
> +       unsigned int p_interval;
> +       unsigned int p_residue;
> +
> +       /* pre-calculated values for playback iso completion */
> +       unsigned int p_pktsize;
> +       unsigned int p_pktsize_residue;
> +       unsigned int p_framesize;
>  };
>
I admire Alan's simple algo. I couldn't have matched that.

However I am not sure how worth is the implementation if it requires 3
more members to avoid calculations simple enough to cause no
noticeable overhead. Once every millisecond is perfectly bearable IMO.
5 members in uac2 structure for only playback, look ugly.

regards,
-jassi


>
>  #define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
> @@ -191,8 +200,29 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
>
>         spin_lock_irqsave(&prm->lock, flags);
>
> -       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +               /*
> +                * For each IN packet, take the quotient of the current data
> +                * rate and the endpoint's interval as the base packet size.
> +                * If there is a residue from this division, add it to the
> +                * residue accumulator.
> +                */
> +               req->length = uac2->p_pktsize;
> +               uac2->p_residue += uac2->p_pktsize_residue;
> +
> +               /*
> +                * Whenever there are more bytes in the accumulator than we
> +                * need to add one more sample frame, increase this packet's
> +                * size and decrease the accumulator.
> +                */
> +               if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
> +                       req->length += uac2->p_framesize;
> +                       uac2->p_residue -= uac2->p_framesize *
> +                                          uac2->p_interval;
> +               }
> +
>                 req->actual = req->length;
> +       }
>
>         pending = prm->hw_ptr % prm->period_size;
>         pending += req->actual;
> @@ -346,6 +376,7 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
>         c_srate = opts->c_srate;
>         p_chmask = opts->p_chmask;
>         c_chmask = opts->c_chmask;
> +       uac2->p_residue = 0;
>
>         runtime->hw = uac2_pcm_hardware;
>
> @@ -1077,7 +1108,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>         struct usb_request *req;
>         struct usb_ep *ep;
>         struct uac2_rtd_params *prm;
> -       int i;
> +       int req_len, i;
>
>         /* No i/f has more than 2 alt settings */
>         if (alt > 1) {
> @@ -1099,11 +1130,41 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>                 prm = &uac2->c_prm;
>                 config_ep_by_speed(gadget, fn, ep);
>                 agdev->as_out_alt = alt;
> +               req_len = prm->max_psize;
>         } else if (intf == agdev->as_in_intf) {
> +               struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
> +               unsigned int factor, rate;
> +               struct usb_endpoint_descriptor *ep_desc;
> +
>                 ep = agdev->in_ep;
>                 prm = &uac2->p_prm;
>                 config_ep_by_speed(gadget, fn, ep);
>                 agdev->as_in_alt = alt;
> +
> +               /* pre-calculate the playback endpoint's interval */
> +               if (gadget->speed == USB_SPEED_FULL) {
> +                       ep_desc = &fs_epin_desc;
> +                       factor = 1000;
> +               } else {
> +                       ep_desc = &hs_epin_desc;
> +                       factor = 125;
> +               }
> +
> +               /* pre-compute some values for iso_complete() */
> +               uac2->p_framesize = opts->p_ssize *
> +                                   num_channels(opts->p_chmask);
> +               rate = opts->p_srate * uac2->p_framesize;
> +               uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
> +               uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
> +                                       prm->max_psize);
> +
> +               if (uac2->p_pktsize < prm->max_psize)
> +                       uac2->p_pktsize_residue = rate % uac2->p_interval;
> +               else
> +                       uac2->p_pktsize_residue = 0;
> +
> +               req_len = uac2->p_pktsize;
> +               uac2->p_residue = 0;
>         } else {
>                 dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
>                 return -EINVAL;
> @@ -1128,9 +1189,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
>
>                         req->zero = 0;
>                         req->context = &prm->ureq[i];
> -                       req->length = prm->max_psize;
> +                       req->length = req_len;
>                         req->complete = agdev_iso_complete;
> -                       req->buf = prm->rbuf + i * req->length;
> +                       req->buf = prm->rbuf + i * prm->max_psize;
>                 }
>
>                 if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
> --
> 2.1.0
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux