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 <zonque@xxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html