With packet sizes other than 512, payloads in the packets may wrap
around the ALSA dma buffer partially, which leads to memory corruption
and audible clicks and pops in the audio stream at the moment, because
there is no boundary check before the memcpy().

Now that we have smaller and dynamically sized packets, we have to
address such cases, and copy the payload in two steps conditionally.
The 'src' and 'dst' approach doesn't work here anymore, as different
behavior is necessary in playback and capture cases. Thus, this patch
open-codes the routine now.

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

diff --git a/drivers/usb/gadget/function/f_uac2.c 
b/drivers/usb/gadget/function/f_uac2.c
index 610a2f1..06446cc 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -159,8 +159,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request 
*req)
 {
        unsigned pending;
        unsigned long flags;
+       unsigned int hw_ptr;
        bool update_alsa = false;
-       unsigned char *src, *dst;
        int status = req->status;
        struct uac2_req *ur = req->context;
        struct snd_pcm_substream *substream;
@@ -187,26 +187,40 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request 
*req)
 
        spin_lock_irqsave(&prm->lock, flags);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               src = prm->dma_area + prm->hw_ptr;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                req->length = req->actual = uac2->c_pktsize;
-               dst = req->buf;
-       } else {
-               dst = prm->dma_area + prm->hw_ptr;
-               src = req->buf;
-       }
 
        pending = prm->hw_ptr % prm->period_size;
        pending += req->actual;
        if (pending >= prm->period_size)
                update_alsa = true;
 
+       hw_ptr = prm->hw_ptr;
        prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
 
        spin_unlock_irqrestore(&prm->lock, flags);
 
        /* Pack USB load in ALSA ring buffer */
-       memcpy(dst, src, req->actual);
+       pending = prm->dma_bytes - hw_ptr;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (unlikely(pending < req->actual)) {
+                       memcpy(req->buf, prm->dma_area + hw_ptr, pending);
+                       memcpy(req->buf + pending, prm->dma_area,
+                              req->actual - pending);
+               } else {
+                       memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
+               }
+       } else {
+               if (unlikely(pending < req->actual)) {
+                       memcpy(prm->dma_area + hw_ptr, req->buf, pending);
+                       memcpy(prm->dma_area, req->buf + pending,
+                              req->actual - pending);
+               } else {
+                       memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
+               }
+       }
+
 exit:
        if (usb_ep_queue(ep, req, GFP_ATOMIC))
                dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
-- 
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