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 pre-calculating the size of each packet to meet the
requested sample rate and format. This won't be 100% accurate, but
that's acceptable. Audio applications have to adopt to the stream's
rate anyway. The important thing here is to make f_uac2 operate at
least rougly in the range of speed that is expected by the host.

Signed-off-by: Daniel Mack <[email protected]>
---
 drivers/usb/gadget/function/f_uac2.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/function/f_uac2.c 
b/drivers/usb/gadget/function/f_uac2.c
index efe8add..610a2f1 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -92,6 +92,8 @@ struct snd_uac2_chip {
 
        struct snd_card *card;
        struct snd_pcm *pcm;
+
+       unsigned int c_pktsize;
 };
 
 #define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
@@ -187,7 +189,7 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request 
*req)
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                src = prm->dma_area + prm->hw_ptr;
-               req->actual = req->length;
+               req->length = req->actual = uac2->c_pktsize;
                dst = req->buf;
        } else {
                dst = prm->dma_area + prm->hw_ptr;
@@ -1046,6 +1048,28 @@ err:
        return -EINVAL;
 }
 
+static void
+afunc_set_c_pktsize(struct usb_gadget *gadget, struct audio_dev *agdev)
+{
+       struct usb_endpoint_descriptor *ep_desc;
+       unsigned int rate, factor, interval;
+       struct f_uac2_opts *opts =
+               container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
+
+       if (gadget->speed == USB_SPEED_FULL) {
+               ep_desc = &fs_epin_desc;
+               factor = 1000;
+       } else {
+               ep_desc = &hs_epin_desc;
+               factor = 125;
+       }
+
+       interval = (1 << (ep_desc->bInterval - 1)) * factor;
+       rate = opts->c_srate * opts->c_ssize * num_channels(opts->c_chmask);
+       agdev->uac2.c_pktsize =
+               min_t(unsigned int, rate / interval, ep_desc->wMaxPacketSize);
+}
+
 static int
 afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 {
@@ -1084,6 +1108,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, 
unsigned alt)
                prm = &uac2->p_prm;
                config_ep_by_speed(gadget, fn, ep);
                agdev->as_in_alt = alt;
+               afunc_set_c_pktsize(gadget, agdev);
        } else {
                dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return -EINVAL;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to