# HG changeset patch
# User Mauro Carvalho Chehab <mchehab@infradead.org>
# Node ID 1852c56e6e4fc4c85bdd5e1042d12cca5250dcd3
# Parent  4ca6edc0fc1fb2c27f33654b66138eac01de047c
Usbvision_v4l2 : mmap corrected to get all frames

From: Thierry MERLE <thierry.merle@free.fr>

- private ioctls UVIOCSREG/UVIOCGREG translated to the 
	VIDIOC_INT_G_REGISTER/VIDIOC_INT_S_REGISTER
- lost frame bug corrected (mmap rework)
- reset scratch buffer is no buffer is queued (prevents useless scratch 
	overflow management)

Signed-off-by: Thierry MERLE <thierry.merle@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

diff -r 4ca6edc0fc1f -r 1852c56e6e4f linux/drivers/media/video/usbvision/usbvision-core.c
--- a/linux/drivers/media/video/usbvision/usbvision-core.c	Mon Nov 13 10:35:29 2006 -0200
+++ b/linux/drivers/media/video/usbvision/usbvision-core.c	Sat Nov 18 19:12:19 2006 -0200
@@ -371,8 +371,6 @@
 #endif
 
 #include "usbvision.h"
-#include "usbvision_ioctl.h"
-
 
 #define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__
 #define EMAIL "joerg@heckenbach-aw.de"
@@ -2379,6 +2377,10 @@ static void usbvision_isocIrq(struct urb
 				scratch_reset(usbvision);
 			}
 		}
+	}
+	else {
+		PDEBUG(DBG_IRQ, "received data, but no one needs it");
+		scratch_reset(usbvision);
 	}
 
 	usbvision->timeInIrq += jiffies - startTime;
@@ -4156,41 +4158,50 @@ static int usbvision_v4l2_do_ioctl(struc
 	//	if (debug & DBG_IOCTL) v4l_printk_ioctl(cmd);
 
 	switch (cmd) {
-		case UVIOCSREG:
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+		/* ioctls to allow direct acces to the NT100x registers */
+		case VIDIOC_INT_G_REGISTER:
 		{
-			struct usbvision_reg *usbvision_reg = arg;
+			struct v4l2_register *reg = arg;
 			int errCode;
 
-			errCode = usbvision_write_reg(usbvision, usbvision_reg->addr, usbvision_reg->value);
-
+			if (reg->i2c_id != 0)
+				return -EINVAL;
+			/* NT100x has a 8-bit register space */
+			errCode = usbvision_read_reg(usbvision, reg->reg&0xff);
 			if (errCode < 0) {
-				err("%s: UVIOCSREG failed: error %d", __FUNCTION__, errCode);
+				err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode);
 			}
 			else {
-				PDEBUG(DBG_IOCTL, "UVIOCSREG addr=0x%02X, value=0x%02X",
-							usbvision_reg->addr, usbvision_reg->value);
+				reg->val=(unsigned char)errCode;
+				PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X",
+							(unsigned int)reg->reg, reg->val);
+				errCode = 0; // No error
+			}
+			return errCode;
+		}
+		case VIDIOC_INT_S_REGISTER:
+		{
+			struct v4l2_register *reg = arg;
+			int errCode;
+
+			if (reg->i2c_id != 0)
+				return -EINVAL;
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val);
+			if (errCode < 0) {
+				err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode);
+			}
+			else {
+				PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X",
+							(unsigned int)reg->reg, reg->val);
 				errCode = 0;
 			}
-			return errCode;
-		}
-		case UVIOCGREG:
-		{
-			struct usbvision_reg *usbvision_reg = arg;
-			int errCode;
-
-			errCode = usbvision_read_reg(usbvision, usbvision_reg->addr);
-
-			if (errCode < 0) {
-				err("%s: UVIOCGREG failed: error %d", __FUNCTION__, errCode);
-			}
-			else {
-				usbvision_reg->value=(unsigned char)errCode;
-				PDEBUG(DBG_IOCTL, "UVIOCGREG addr=0x%02X, value=0x%02X",
-							usbvision_reg->addr, usbvision_reg->value);
-				errCode = 0; // No error
-			}
-			return errCode;
-		}
+			return 0;
+		}
+#endif
 		case VIDIOC_QUERYCAP:
 		{
 			struct v4l2_capability *vc=arg;
@@ -4487,12 +4498,9 @@ static int usbvision_v4l2_do_ioctl(struc
 			if(frame->grabstate == FrameState_Unused)
 				vb->flags |= V4L2_BUF_FLAG_MAPPED;
 			vb->memory = V4L2_MEMORY_MMAP;
-			if(vb->index == 0) {
-				vb->m.offset = 0;
-			}
-			else {
-				vb->m.offset = MAX_FRAME_SIZE;
-			}
+
+			vb->m.offset = vb->index*MAX_FRAME_SIZE;
+
 			vb->memory = V4L2_MEMORY_MMAP;
 			vb->field = V4L2_FIELD_NONE;
 			vb->length = MAX_FRAME_SIZE;
@@ -4962,57 +4970,54 @@ static ssize_t usbvision_v4l2_read(struc
 	return count;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
-int usbvision_v4l2_mmap(struct video_device *dev, const char *adr, unsigned long size)
-{
-	struct usb_usbvision *usbvision = (struct usb_usbvision *) dev;
-	unsigned long start = (unsigned long) adr;
-
-#else
 static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
 {
+	unsigned long size = vma->vm_end - vma->vm_start,
+		start = vma->vm_start;
+	void *pos;
+	u32 i;
+
 	struct video_device *dev = video_devdata(file);
 	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
-	unsigned long start = vma->vm_start;
-	unsigned long size  = vma->vm_end-vma->vm_start;
-#endif
-
-	unsigned long page, pos;
 
 	if (!USBVISION_IS_OPERATIONAL(usbvision))
 		return -EFAULT;
 
-	if (size > (((USBVISION_NUMFRAMES * usbvision->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (!(vma->vm_flags & VM_WRITE) ||
+	    size != PAGE_ALIGN(usbvision->max_frame_size)) {
 		return -EINVAL;
-
-	pos = (unsigned long) usbvision->fbuf;
+	}
+
+	for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+		if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff)
+			break;
+	}
+	if (i == USBVISION_NUMFRAMES) {
+		PDEBUG(DBG_FUNC, "mmap: user supplied mapping address is out of range");
+		return -EINVAL;
+	}
+
+	/* VM_IO is eventually going to replace PageReserved altogether */
+	vma->vm_flags |= VM_IO;
+	vma->vm_flags |= VM_RESERVED;	/* avoid to swap out this VMA */
+
+	pos = usbvision->frame[i].data;
 	while (size > 0) {
 
-// Really ugly....
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)                                     //Compatibility for 2.4.x kernels
-		page = usbvision_kvirt_to_pa(pos);
-		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+		unsigned long page = vmalloc_to_pfn(pos);
+		if (remap_pfn_range(vma, start, page, PAGE_SIZE,
+				    vma->vm_page_prot)) {
+			PDEBUG(DBG_FUNC, "mmap: rename page map failed");
+#else
+		if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+			PDEBUG(DBG_FUNC, "mmap: vm_insert_page failed");
+#endif
 			return -EAGAIN;
 		}
-#else
-	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)			   //Compatibility for 2.6.10+ kernels
-		page = vmalloc_to_pfn((void *)pos);
-		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
-			return -EAGAIN;
-		}
-	#else                                                                      //Compatibility for 2.6.0 - 2.6.9 kernels
-		page = usbvision_kvirt_to_pa(pos);
-		if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
-			return -EAGAIN;
-		}
-	#endif
-#endif
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
-		if (size > PAGE_SIZE)
-			size -= PAGE_SIZE;
-		else
-			size = 0;
+		size -= PAGE_SIZE;
 	}
 
 	return 0;
