summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function/f_uac2.c
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2014-08-27 19:09:06 +0200
committerFelipe Balbi <balbi@ti.com>2014-09-02 09:17:07 -0500
commitec9e43138f1219966850477e056f6eb7fbcc4fa4 (patch)
treefd4d1c21dd175c27ec868cdfefe817d7cb863627 /drivers/usb/gadget/function/f_uac2.c
parent254b3bf68b65ac8f82da1c7e0c1a2bb17012aa7d (diff)
usb: gadget: f_uac2: handle partial dma area wrap
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(). In preparation to an implementation for 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 <zonque@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/function/f_uac2.c')
-rw-r--r--drivers/usb/gadget/function/f_uac2.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 9c8831d74f5b..246a7784e012 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -163,8 +163,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;
@@ -191,26 +191,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->actual = req->length;
- 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__);