V4L/DVB: em28xx: rework buffer pointer tracking for offset to start of video

Rework the logic for tracking the amount of data copied to the VBI buffer, to
address problem found where the video lines are several bytes shifted to the
right (and the leading pixels in the first line rendered are garbage).  This
would occur because the copy function would advance the pointer when detecting
headers, but the caller would not adjust the length actually copied.

This work was sponsored by EyeMagnet Limited.

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 0fe2011..b6ac99d 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -203,12 +203,6 @@
 	if (dma_q->pos + len > buf->vb.size)
 		len = buf->vb.size - dma_q->pos;
 
-	if (p[0] != 0x88 && p[0] != 0x22) {
-		em28xx_isocdbg("frame is not complete\n");
-		len += 4;
-	} else
-		p += 4;
-
 	startread = p;
 	remain = len;
 
@@ -309,14 +303,6 @@
 	if (dma_q->pos + len > buf->vb.size)
 		len = buf->vb.size - dma_q->pos;
 
-	if ((p[0] == 0x33 && p[1] == 0x95) ||
-	    (p[0] == 0x88 && p[1] == 0x88)) {
-		/* Header field, advance past it */
-		p += 4;
-	} else {
-		len += 4;
-	}
-
 	startread = p;
 
 	startwrite = outp + dma_q->pos;
@@ -507,8 +493,15 @@
 
 			dma_q->pos = 0;
 		}
-		if (buf != NULL)
+		if (buf != NULL) {
+			if (p[0] != 0x88 && p[0] != 0x22) {
+				em28xx_isocdbg("frame is not complete\n");
+				len += 4;
+			} else {
+				p += 4;
+			}
 			em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+		}
 	}
 	return rc;
 }
@@ -555,8 +548,7 @@
 				continue;
 		}
 
-		len = urb->iso_frame_desc[i].actual_length - 4;
-
+		len = urb->iso_frame_desc[i].actual_length;
 		if (urb->iso_frame_desc[i].actual_length <= 0) {
 			/* em28xx_isocdbg("packet %d is empty",i); - spammy */
 			continue;
@@ -577,6 +569,17 @@
 			dev->vbi_read = 0;
 			em28xx_isocdbg("VBI START HEADER!!!\n");
 			dev->cur_field = p[2];
+			p += 4;
+			len -= 4;
+		} else if (p[0] == 0x88 && p[1] == 0x88 &&
+			   p[2] == 0x88 && p[3] == 0x88) {
+			/* continuation */
+			p += 4;
+			len -= 4;
+		} else if (p[0] == 0x22 && p[1] == 0x5a) {
+			/* start video */
+			p += 4;
+			len -= 4;
 		}
 
 		vbi_size = dev->vbi_width * dev->vbi_height;
@@ -631,9 +634,6 @@
 
 		if (dev->capture_type == 1) {
 			dev->capture_type = 2;
-			em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
-				       len, (p[2] & 1) ? "odd" : "even");
-
 			if (dev->progressive || !(dev->cur_field & 1)) {
 				if (buf != NULL)
 					buffer_filled(dev, dma_q, buf);
@@ -652,8 +652,25 @@
 
 			dma_q->pos = 0;
 		}
-		if (buf != NULL && dev->capture_type == 2)
-			em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+
+		if (buf != NULL && dev->capture_type == 2) {
+			if (len > 4 && p[0] == 0x88 && p[1] == 0x88 &&
+			    p[2] == 0x88 && p[3] == 0x88) {
+				p += 4;
+				len -= 4;
+			}
+			if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) {
+				em28xx_isocdbg("Video frame %d, len=%i, %s\n",
+					       p[2], len, (p[2] & 1) ?
+					       "odd" : "even");
+				p += 4;
+				len -= 4;
+			}
+
+			if (len > 0)
+				em28xx_copy_video(dev, dma_q, buf, p, outp,
+						  len);
+		}
 	}
 	return rc;
 }