瀏覽代碼

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>
Daniel Mack 11 年之前
父節點
當前提交
ec9e43138f
共有 1 個文件被更改,包括 23 次插入9 次删除
  1. 23 9
      drivers/usb/gadget/function/f_uac2.c

+ 23 - 9
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 pending;
 	unsigned long flags;
 	unsigned long flags;
+	unsigned int hw_ptr;
 	bool update_alsa = false;
 	bool update_alsa = false;
-	unsigned char *src, *dst;
 	int status = req->status;
 	int status = req->status;
 	struct uac2_req *ur = req->context;
 	struct uac2_req *ur = req->context;
 	struct snd_pcm_substream *substream;
 	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);
 	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;
 		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 = prm->hw_ptr % prm->period_size;
 	pending += req->actual;
 	pending += req->actual;
 	if (pending >= prm->period_size)
 	if (pending >= prm->period_size)
 		update_alsa = true;
 		update_alsa = true;
 
 
+	hw_ptr = prm->hw_ptr;
 	prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
 	prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
 
 
 	spin_unlock_irqrestore(&prm->lock, flags);
 	spin_unlock_irqrestore(&prm->lock, flags);
 
 
 	/* Pack USB load in ALSA ring buffer */
 	/* 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:
 exit:
 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
 		dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
 		dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);