[media] saa7164: buffer crc checks and ensure we use the memcpy func

Buffer crc checks and ensure we use the correct PCIe IO memcpy func

Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c
index 0760891..b75157d 100644
--- a/drivers/media/video/saa7164/saa7164-buffer.c
+++ b/drivers/media/video/saa7164/saa7164-buffer.c
@@ -113,6 +113,7 @@
 	buf->flags = SAA7164_BUFFER_FREE;
 	buf->pos = 0;
 	buf->actual_size = params->pitch * params->numberoflines;
+	buf->crc = 0;
 	/* TODO: arg len is being ignored */
 	buf->pci_size = SAA7164_PT_ENTRIES * 0x1000;
 	buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000;
@@ -129,8 +130,9 @@
 		goto fail2;
 
 	/* init the buffers to a known pattern, easier during debugging */
-	memset(buf->cpu, 0xff, buf->pci_size);
-	memset(buf->pt_cpu, 0xff, buf->pt_size);
+	memset_io(buf->cpu, 0xff, buf->pci_size);
+	buf->crc = crc32(0, buf->cpu, buf->actual_size);
+	memset_io(buf->pt_cpu, 0xff, buf->pt_size);
 
 	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p\n",
 		__func__, buf);
@@ -296,6 +298,7 @@
 
 	buf->actual_size = len;
 	buf->pos = 0;
+	buf->crc = 0;
 
 	dprintk(DBGLVL_BUF, "%s()   allocated user buffer @ 0x%p\n",
 		__func__, buf);
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c
index 49414c8..ccc6100 100644
--- a/drivers/media/video/saa7164/saa7164-bus.c
+++ b/drivers/media/video/saa7164/saa7164-bus.c
@@ -217,28 +217,28 @@
 			dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
 
 			/* Split the msg into pieces as the ring wraps */
-			memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem);
-			memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem,
+			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem);
+			memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem,
 				sizeof(*msg) - space_rem);
 
-			memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
+			memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
 				buf, msg->size);
 
 		} else if (space_rem == sizeof(*msg)) {
 			dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
 
 			/* Additional data at the beginning of the ring */
-			memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
-			memcpy(bus->m_pdwSetRing, buf, msg->size);
+			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+			memcpy_toio(bus->m_pdwSetRing, buf, msg->size);
 
 		} else {
 			/* Additional data wraps around the ring */
-			memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
 			if (msg->size > 0) {
-				memcpy(bus->m_pdwSetRing + curr_swp +
+				memcpy_toio(bus->m_pdwSetRing + curr_swp +
 					sizeof(*msg), buf, space_rem -
 					sizeof(*msg));
-				memcpy(bus->m_pdwSetRing, (u8 *)buf +
+				memcpy_toio(bus->m_pdwSetRing, (u8 *)buf +
 					space_rem - sizeof(*msg),
 					bytes_to_write - space_rem);
 			}
@@ -250,8 +250,8 @@
 		dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
 
 		/* The ring buffer doesn't wrap, two simple copies */
-		memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
-		memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
+		memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+		memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
 			msg->size);
 	}
 
@@ -343,19 +343,19 @@
 		new_grp -= bus->m_dwSizeGetRing;
 		space_rem = bus->m_dwSizeGetRing - curr_grp;
 
-		memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
-		memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
+		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
+		memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
 			bytes_to_read - space_rem);
 
 	} else {
 		/* No wrapping */
-		memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
+		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
 	}
 
 	/* No need to update the read positions, because this was a peek */
 	/* If the caller specifically want to peek, return */
 	if (peekonly) {
-		memcpy(msg, &msg_tmp, sizeof(*msg));
+		memcpy_fromio(msg, &msg_tmp, sizeof(*msg));
 		goto peekout;
 	}
 
@@ -401,24 +401,24 @@
 
 		if (space_rem < sizeof(*msg)) {
 			/* msg wraps around the ring */
-			memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem);
-			memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing,
+			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem);
+			memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing,
 				sizeof(*msg) - space_rem);
 			if (buf)
-				memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) -
+				memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
 					space_rem, buf_size);
 
 		} else if (space_rem == sizeof(*msg)) {
-			memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 			if (buf)
 				memcpy(buf, bus->m_pdwGetRing, buf_size);
 		} else {
 			/* Additional data wraps around the ring */
-			memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 			if (buf) {
-				memcpy(buf, bus->m_pdwGetRing + curr_grp +
+				memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
 					sizeof(*msg), space_rem - sizeof(*msg));
-				memcpy(buf + space_rem - sizeof(*msg),
+				memcpy_fromio(buf + space_rem - sizeof(*msg),
 					bus->m_pdwGetRing, bytes_to_read -
 					space_rem);
 			}
@@ -427,9 +427,9 @@
 
 	} else {
 		/* No wrapping */
-		memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+		memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 		if (buf)
-			memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
+			memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
 				buf_size);
 	}
 
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index e96bbe4..79e1a2e 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -72,6 +72,28 @@
 
 #define INT_SIZE 16
 
+void saa7164_dumphex16FF(struct saa7164_dev *dev, u8 *buf, int len)
+{
+	int i;
+	u8 tmp[16];
+	memset(&tmp[0], 0xff, sizeof(tmp));
+
+	printk(KERN_INFO "--------------------> "
+		"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+	for (i = 0; i < len; i += 16) {
+		if (memcmp(&tmp, buf + i, sizeof(tmp)) != 0) {
+			printk(KERN_INFO "         [0x%08x] "
+				"%02x %02x %02x %02x %02x %02x %02x %02x "
+				"%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+			*(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
+			*(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
+			*(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
+			*(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
+		}
+	}
+}
+
 static void saa7164_ts_verifier(struct saa7164_buffer *buf)
 {
 	struct saa7164_port *port = buf->port;
@@ -216,6 +238,7 @@
 	struct saa7164_user_buffer *ubuf;
 	struct list_head *c, *n;
 	int wp, rp, i = 0;
+	u32 crc, ok = 0;
 	u8 *p;
 
 	port->last_svc_msecs_diff = port->last_svc_msecs;
@@ -277,10 +300,19 @@
 			saa7164_dumphex16(dev, p + buf->actual_size - 32, 64);
 		}
 
+		if (buf->idx == wp) {
+			/* Ignore this, it's being updated currently by the dma engine */
+		} else
 		if (buf->idx == rp) {
+
+			crc = crc32(0, buf->cpu, buf->actual_size);
+			if (crc != port->shadow_crc[rp])
+				printk(KERN_ERR "%s crc didn't match shadow was 0x%x now 0x%x\n",
+					__func__, port->shadow_crc[rp], crc);
+
 			/* Found the buffer, deal with it */
-			dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
-				__func__, wp, rp);
+			dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d crc32: 0x%x\n",
+				__func__, wp, rp, buf->crc);
 
 			/* Validate the incoming buffer content */
 			if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
@@ -293,9 +325,23 @@
 				ubuf = list_first_entry(&port->list_buf_free.list,
 					struct saa7164_user_buffer, list);
 
-				if (ubuf->actual_size == buf->actual_size) {
-					memcpy(ubuf->data, buf->cpu,
-						ubuf->actual_size);
+				if (ubuf->actual_size >= buf->actual_size) {
+					memcpy(ubuf->data, port->shadow_buf[rp], 312 * 188);
+
+					/* Throw a new checksum on the read buffer */
+					ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+
+					if ((crc == port->shadow_crc[rp]) && (crc == ubuf->crc))
+						ok = 1;
+					else
+						ok = 0;
+
+					if (ok == 0)
+						printk(KERN_ERR
+							"rp: %d dmacrc: 0x%08x shadcrc: 0x%08x ubufcrc: 0x%08x %s\n",
+							rp, buf->crc, port->shadow_crc[rp], ubuf->crc,
+							ok ? "crcgood" : "crcbad");
+
 				} else {
 					printk(KERN_ERR "buf %p actual fails match\n", buf);
 				}
@@ -315,9 +361,21 @@
 			/* Ensure offset into buffer remains 0, fill buffer
 			 * with known bad data. */
 			saa7164_buffer_zero_offsets(port, rp);
-			memset(buf->cpu, 0xff, buf->pci_size);
+			memset_io(buf->cpu, 0xff, buf->pci_size);
+			buf->crc = crc32(0, buf->cpu, buf->actual_size);
 
-			break;
+//			break;
+		} else {
+			/* Validate all other checksums, on previous buffers - they should never change */
+			crc = crc32(0, buf->cpu, buf->actual_size);
+			if (crc != buf->crc) {
+				printk(KERN_ERR "buf[%d].crc became invalid, was 0x%x became 0x%x rp: %d wp: %d\n",
+					buf->idx, buf->crc, crc, rp, wp);
+				//saa7164_dumphex16FF(dev, (u8 *)buf->cpu, buf->actual_size);
+				saa7164_dumphex16FF(dev, (u8 *)buf->cpu, 256);
+				buf->crc = crc;
+			}
+
 		}
 
 	}
@@ -332,7 +390,6 @@
 		print_histogram = 64 + port->nr;
 	}
 }
-
 static void saa7164_work_cmdhandler(struct work_struct *w)
 {
 	struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
@@ -354,7 +411,11 @@
 static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
-	int wp, rp;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct list_head *c, *n;
+	int wp, rp, i = 0;
+	u8 *p;
 
 	/* Find the current write point from the hardware */
 	wp = saa7164_readl(port->bufcounter);
@@ -400,7 +461,48 @@
 		port->last_irq_wp,
 		port->last_irq_rp
 		);
+	/* Find the used buffer, shadow copy it before we've
+	 * acked the interrupt.
+	 */
+//	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
 
+		buf = list_entry(c, struct saa7164_buffer, list);
+		if (i++ > port->hwcfg.buffercount) {
+			printk(KERN_ERR "%s() illegal i count %d\n",
+				__func__, i);
+			break;
+		}
+
+		p = (u8 *)buf->cpu;
+		if (	(*(p + buf->actual_size + 0) != 0xff) ||
+			(*(p + buf->actual_size + 1) != 0xff) ||
+			(*(p + buf->actual_size + 2) != 0xff) ||
+			(*(p + buf->actual_size + 3) != 0xff) ||
+			(*(p + buf->actual_size + 0x10) != 0xff) ||
+			(*(p + buf->actual_size + 0x11) != 0xff) ||
+			(*(p + buf->actual_size + 0x12) != 0xff) ||
+			(*(p + buf->actual_size + 0x13) != 0xff) )
+		{
+			printk(KERN_ERR "buf %p failed guard check\n", buf);
+			saa7164_dumphex16(dev, p + buf->actual_size - 32, 64);
+		}
+
+		if (buf->idx == rp) {
+
+			memcpy_fromio(port->shadow_buf[rp], buf->cpu, 312 * 188);
+			port->shadow_crc[rp] = crc32(0, port->shadow_buf[rp], 312 * 188);
+
+			buf->crc = crc32(0, buf->cpu, 312 * 188);
+
+			if (port->shadow_crc[rp] != buf->crc)
+				printk(KERN_ERR "%s() crc check failed 0x%x vs 0x%x\n",
+					__func__, port->shadow_crc[rp], buf->crc);
+			break;
+		}
+
+	}
+//	mutex_unlock(&port->dmaqueue_lock);
 	schedule_work(&port->workenc);
 
 	return 0;
@@ -693,10 +795,10 @@
  */
 static void saa7164_get_descriptors(struct saa7164_dev *dev)
 {
-	memcpy(&dev->hwdesc, dev->bmmio, sizeof(tmComResHWDescr_t));
-	memcpy(&dev->intfdesc, dev->bmmio + sizeof(tmComResHWDescr_t),
+	memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(tmComResHWDescr_t));
+	memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(tmComResHWDescr_t),
 		sizeof(tmComResInterfaceDescr_t));
-	memcpy(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
+	memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
 		sizeof(tmComResBusDescr_t));
 
 	if (dev->hwdesc.bLength != sizeof(tmComResHWDescr_t)) {
@@ -742,6 +844,7 @@
 static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
 {
 	struct saa7164_port *port = 0;
+	int i;
 
 	if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
 		BUG();
@@ -780,6 +883,18 @@
 	saa7164_histogram_reset(&port->poll_interval,
 		"encoder poll() intervals");
 
+	if (port->type == SAA7164_MPEG_ENCODER) {
+		for (i = 0; i < 8; i ++) {
+			port->shadow_buf[i] = kzalloc(312 * 188, GFP_KERNEL);
+			if (port->shadow_buf[i] == 0)
+				printk(KERN_ERR "%s() shadow_buf ENOMEM\n", __func__);
+			else {
+				memset(port->shadow_buf[i], 0xff, 312 * 188);
+				port->shadow_crc[i] = crc32(0, port->shadow_buf[i], 312 * 188);
+			}
+		}
+	}
+
 	return 0;
 }
 
@@ -1057,6 +1172,8 @@
 static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
 {
 	struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
+	struct saa7164_port *port;
+	int i;
 
 	saa7164_histogram_print(&dev->ports[ SAA7164_PORT_ENC1 ],
 		&dev->ports[ SAA7164_PORT_ENC1 ].irq_interval);
@@ -1071,6 +1188,22 @@
 
 	saa7164_shutdown(dev);
 
+	port = &dev->ports[ SAA7164_PORT_ENC1 ];
+	if (port->type == SAA7164_MPEG_ENCODER) {
+		for (i = 0; i < 8; i ++) {
+			kfree(port->shadow_buf[i]);
+			port->shadow_buf[i] = 0;
+		}
+	}
+	port = &dev->ports[ SAA7164_PORT_ENC2 ];
+	if (port->type == SAA7164_MPEG_ENCODER) {
+		for (i = 0; i < 8; i ++) {
+			kfree(port->shadow_buf[i]);
+			port->shadow_buf[i] = 0;
+		}
+	}
+
+
 	if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
 		saa7164_dvb_unregister(&dev->ports[ SAA7164_PORT_TS1 ]);
 
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index c61907d..f3ecdc9 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -865,6 +865,7 @@
 
 	/* Configure the encoder with any cache values */
 	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
 
 	saa7164_buffer_cfg_port(port);
 
@@ -1006,11 +1007,19 @@
 {
 	struct saa7164_user_buffer *buf = 0;
 	struct saa7164_dev *dev = port->dev;
+	u32 crc;
 
 	mutex_lock(&port->dmaqueue_lock);
 	if (!list_empty(&port->list_buf_used.list)) {
 		buf = list_first_entry(&port->list_buf_used.list,
 			struct saa7164_user_buffer, list);
+
+		crc = crc32(0, buf->data, buf->actual_size);
+		if (crc != buf->crc) {
+			printk(KERN_ERR "%s() buf %p crc became invalid, was 0x%x became 0x%x\n", __func__,
+				buf, buf->crc, crc);
+		}
+
 	}
 	mutex_unlock(&port->dmaqueue_lock);
 
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 796d21d..a8a29e5 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -50,6 +50,7 @@
 #include <linux/kdev_t.h>
 #include <linux/version.h>
 #include <linux/mutex.h>
+#include <linux/crc32.h>
 
 #include <media/tuner.h>
 #include <media/tveeprom.h>
@@ -194,6 +195,8 @@
 	u8  *data;
 	u32 pos;
 	u32 actual_size;
+
+	u32 crc;
 };
 
 struct saa7164_fw_status {
@@ -282,12 +285,13 @@
 
 	/* A block of page align PCI memory */
 	u32 pci_size;	/* PCI allocation size in bytes */
-	u64 *cpu;	/* Virtual address */
+	u64 __iomem *cpu;	/* Virtual address */
 	dma_addr_t dma;	/* Physical address */
+	u32 crc;	/* Checksum for the entire buffer data */
 
 	/* A page table that splits the block into a number of entries */
 	u32 pt_size;		/* PCI allocation size in bytes */
-	u64 *pt_cpu;		/* Virtual address */
+	u64 __iomem *pt_cpu;		/* Virtual address */
 	dma_addr_t pt_dma;	/* Physical address */
 
 	/* Encoder fops */
@@ -386,6 +390,9 @@
 	u32 a_cc_errors;
 	u8 last_v_cc;
 	u8 last_a_cc;
+
+	u8 *shadow_buf[8];
+	u32 shadow_crc[8];
 };
 
 struct saa7164_dev {
@@ -536,7 +543,6 @@
 extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
 extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
 
-
 /* ----------------------------------------------------------- */
 /* saa7164-encoder.c                                            */
 int saa7164_encoder_register(struct saa7164_port *port);