
From: Stelian Pop <stelian@popies.net>

Signed-off-by: Stelian Pop <stelian@popies.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/Documentation/video4linux/meye.txt |   21 -
 25-akpm/drivers/media/video/meye.c         |  562 ++++++++++++++++++++++++++++-
 25-akpm/drivers/media/video/meye.h         |    3 
 25-akpm/include/linux/meye.h               |    7 
 4 files changed, 576 insertions(+), 17 deletions(-)

diff -puN Documentation/video4linux/meye.txt~meye-add-v4l2-support Documentation/video4linux/meye.txt
--- 25/Documentation/video4linux/meye.txt~meye-add-v4l2-support	Thu Nov  4 15:04:46 2004
+++ 25-akpm/Documentation/video4linux/meye.txt	Thu Nov  4 15:04:46 2004
@@ -46,6 +46,8 @@ module argument syntax (<param>=<value> 
 module or meye.<param>=<value> on the kernel boot line when meye is
 statically linked into the kernel). Those options are:
 
+	forcev4l1:	force use of V4L1 API instead of V4L2
+
 	gbuffers:	number of capture buffers, default is 2 (32 max)
 
 	gbufsize:	size of each capture buffer, default is 614400
@@ -78,8 +80,9 @@ Usage:
 Private API:
 ------------
 
-	The driver supports frame grabbing with the video4linux API, so
-	all video4linux tools (like xawtv) should work with this driver.
+	The driver supports frame grabbing with the video4linux API
+	(either v4l1 or v4l2), so all video4linux tools (like xawtv)
+	should work with this driver.
 
 	Besides the video4linux interface, the driver has a private interface
 	for accessing the Motion Eye extended parameters (camera sharpness,
@@ -121,13 +124,7 @@ Private API:
 Bugs / Todo:
 ------------
 
-	- overlay output is not supported (although the camera is capable of).
-	  	(it should not be too hard to to it, provided we found how...)
-		
-	- mjpeg hardware playback doesn't work (depends on overlay...)
-
-	- rewrite the driver to use some common video4linux API for snapshot
-	  and mjpeg capture. Unfortunately, video4linux1 does not permit it,
-	  the BUZ API seems to be targeted to TV cards only. The video4linux 2
-	  API may be an option, if it goes into the kernel (maybe 2.5 
-	  material ?).
+	- the driver could be much cleaned up by removing the v4l1 support.
+	  However, this means all v4l1-only applications will stop working.
+
+	- 'motioneye' still uses the meye private v4l1 API extensions.
diff -puN drivers/media/video/meye.c~meye-add-v4l2-support drivers/media/video/meye.c
--- 25/drivers/media/video/meye.c~meye-add-v4l2-support	Thu Nov  4 15:04:46 2004
+++ 25-akpm/drivers/media/video/meye.c	Thu Nov  4 15:04:46 2004
@@ -46,6 +46,11 @@ MODULE_DESCRIPTION("v4l/v4l2 driver for 
 MODULE_LICENSE("GPL");
 MODULE_VERSION(MEYE_DRIVER_VERSION);
 
+/* force usage of V4L1 API */
+static int forcev4l1; /* = 0 */
+module_param(forcev4l1, int, 0644);
+MODULE_PARM_DESC(forcev4l1, "force use of V4L1 instead of V4L2");
+
 /* number of grab buffers */
 static unsigned int gbuffers = 2;
 module_param(gbuffers, int, 0444);
@@ -781,6 +786,8 @@ static irqreturn_t meye_irq(int irq, voi
 {
 	u32 v;
 	int reqnr;
+	static int sequence = 0;
+
 	v = mchip_read(MCHIP_MM_INTA);
 
 	if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT &&
@@ -802,6 +809,8 @@ again:
 				      mchip_hsize() * mchip_vsize() * 2);
 		meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2;
 		meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+		do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+		meye.grab_buffer[reqnr].sequence = sequence++;
 		kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int));
 		wake_up_interruptible(&meye.proc_list);
 	} else {
@@ -820,6 +829,8 @@ again:
 		       size);
 		meye.grab_buffer[reqnr].size = size;
 		meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+		do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+		meye.grab_buffer[reqnr].sequence = sequence++;
 		kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int));
 		wake_up_interruptible(&meye.proc_list);
 	}
@@ -1132,10 +1143,519 @@ static int meye_do_ioctl(struct inode *i
 		break;
 	}
 
+	case VIDIOC_QUERYCAP: {
+		struct v4l2_capability *cap = arg;
+
+		if (forcev4l1)
+			return -EINVAL;
+
+		memset(cap, 0, sizeof(*cap));
+		strcpy(cap->driver, "meye");
+		strcpy(cap->card, "meye");
+		sprintf(cap->bus_info, "PCI:%s", meye.mchip_dev->slot_name);
+		cap->version = (MEYE_DRIVER_MAJORVERSION << 8) +
+			       MEYE_DRIVER_MINORVERSION;
+		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+				    V4L2_CAP_STREAMING;
+		break;
+	}
+
+	case VIDIOC_ENUMINPUT: {
+		struct v4l2_input *i = arg;
+
+		if (i->index != 0)
+			return -EINVAL;
+		memset(i, 0, sizeof(*i));
+		i->index = 0;
+		strcpy(i->name, "Camera");
+		i->type = V4L2_INPUT_TYPE_CAMERA;
+		break;
+	}
+
+	case VIDIOC_G_INPUT: {
+		int *i = arg;
+
+		*i = 0;
+		break;
+	}
+
+	case VIDIOC_S_INPUT: {
+		int *i = arg;
+
+		if (*i != 0)
+			return -EINVAL;
+		break;
+	}
+
+	case VIDIOC_QUERYCTRL: {
+		struct v4l2_queryctrl *c = arg;
+
+		switch (c->id) {
+
+		case V4L2_CID_BRIGHTNESS:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Brightness");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 32;
+			c->flags = 0;
+			break;
+		case V4L2_CID_HUE:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Hue");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 32;
+			c->flags = 0;
+			break;
+		case V4L2_CID_CONTRAST:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Contrast");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 32;
+			c->flags = 0;
+			break;
+		case V4L2_CID_SATURATION:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Saturation");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 32;
+			c->flags = 0;
+			break;
+		case V4L2_CID_AGC:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Agc");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 48;
+			c->flags = 0;
+			break;
+		case V4L2_CID_SHARPNESS:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Sharpness");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 32;
+			c->flags = 0;
+			break;
+		case V4L2_CID_PICTURE:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Picture");
+			c->minimum = 0;
+			c->maximum = 63;
+			c->step = 1;
+			c->default_value = 0;
+			c->flags = 0;
+			break;
+		case V4L2_CID_JPEGQUAL:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "JPEG quality");
+			c->minimum = 0;
+			c->maximum = 10;
+			c->step = 1;
+			c->default_value = 8;
+			c->flags = 0;
+			break;
+		case V4L2_CID_FRAMERATE:
+			c->type = V4L2_CTRL_TYPE_INTEGER;
+			strcpy(c->name, "Framerate");
+			c->minimum = 0;
+			c->maximum = 31;
+			c->step = 1;
+			c->default_value = 0;
+			c->flags = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_S_CTRL: {
+		struct v4l2_control *c = arg;
+
+		down(&meye.lock);
+		switch (c->id) {
+
+		case V4L2_CID_BRIGHTNESS:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value);
+			break;
+		case V4L2_CID_HUE:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERAHUE, c->value);
+			break;
+		case V4L2_CID_CONTRAST:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERACOLOR, c->value);
+			break;
+		case V4L2_CID_SATURATION:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERACOLOR, c->value);
+			break;
+		case V4L2_CID_AGC:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERAAGC, c->value);
+			break;
+		case V4L2_CID_SHARPNESS:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERASHARPNESS, c->value);
+			break;
+		case V4L2_CID_PICTURE:
+			sonypi_camera_command(
+				SONYPI_COMMAND_SETCAMERAPICTURE, c->value);
+			break;
+		case V4L2_CID_JPEGQUAL:
+			meye.params.quality = c->value;
+			break;
+		case V4L2_CID_FRAMERATE:
+			meye.params.framerate = c->value;
+			break;
+		default:
+			up(&meye.lock);
+			return -EINVAL;
+		}
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_G_CTRL: {
+		struct v4l2_control *c = arg;
+
+		down(&meye.lock);
+		switch (c->id) {
+		case V4L2_CID_BRIGHTNESS:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERABRIGHTNESS, 0);
+			break;
+		case V4L2_CID_HUE:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERAHUE, 0);
+			break;
+		case V4L2_CID_CONTRAST:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERACOLOR, 0);
+			break;
+		case V4L2_CID_SATURATION:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERACOLOR, 0);
+			break;
+		case V4L2_CID_AGC:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERAAGC, 0);
+			break;
+		case V4L2_CID_SHARPNESS:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERASHARPNESS, 0);
+			break;
+		case V4L2_CID_PICTURE:
+			c->value = sonypi_camera_command(
+				SONYPI_COMMAND_GETCAMERAPICTURE, 0);
+			break;
+		case V4L2_CID_JPEGQUAL:
+			c->value = meye.params.quality;
+			break;
+		case V4L2_CID_FRAMERATE:
+			c->value = meye.params.framerate;
+			break;
+		default:
+			up(&meye.lock);
+			return -EINVAL;
+		}
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_ENUM_FMT: {
+		struct v4l2_fmtdesc *f = arg;
+
+		if (f->index > 1)
+			return -EINVAL;
+		if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (f->index == 0) {
+			/* standard YUV 422 capture */
+			memset(f, 0, sizeof(*f));
+			f->index = 0;
+			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			f->flags = 0;
+			strcpy(f->description, "YUV422");
+			f->pixelformat = V4L2_PIX_FMT_YUYV;
+		} else {
+			/* compressed MJPEG capture */
+			memset(f, 0, sizeof(*f));
+			f->index = 1;
+			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			f->flags = V4L2_FMT_FLAG_COMPRESSED;
+			strcpy(f->description, "MJPEG");
+			f->pixelformat = V4L2_PIX_FMT_MJPEG;
+		}
+		break;
+	}
+
+	case VIDIOC_TRY_FMT: {
+		struct v4l2_format *f = arg;
+
+		if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+		    f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+			return -EINVAL;
+		if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+		    f->fmt.pix.field != V4L2_FIELD_NONE)
+			return -EINVAL;
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+		if (f->fmt.pix.width <= 320) {
+			f->fmt.pix.width = 320;
+			f->fmt.pix.height = 240;
+		} else {
+			f->fmt.pix.width = 640;
+			f->fmt.pix.height = 480;
+		}
+		f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+		f->fmt.pix.sizeimage = f->fmt.pix.height *
+				       f->fmt.pix.bytesperline;
+		f->fmt.pix.colorspace = 0;
+		f->fmt.pix.priv = 0;
+		break;
+	}
+
+	case VIDIOC_G_FMT: {
+		struct v4l2_format *f = arg;
+
+		if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		switch (meye.mchip_mode) {
+		case MCHIP_HIC_MODE_CONT_OUT:
+		default:
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+			break;
+		case MCHIP_HIC_MODE_CONT_COMP:
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+			break;
+		}
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+		f->fmt.pix.width = mchip_hsize();
+		f->fmt.pix.height = mchip_vsize();
+		f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+		f->fmt.pix.sizeimage = f->fmt.pix.height *
+				       f->fmt.pix.bytesperline;
+		f->fmt.pix.colorspace = 0;
+		f->fmt.pix.priv = 0;
+		break;
+	}
+
+	case VIDIOC_S_FMT: {
+		struct v4l2_format *f = arg;
+
+		if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+		    f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+			return -EINVAL;
+		if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+		    f->fmt.pix.field != V4L2_FIELD_NONE)
+			return -EINVAL;
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+		down(&meye.lock);
+		if (f->fmt.pix.width <= 320) {
+			f->fmt.pix.width = 320;
+			f->fmt.pix.height = 240;
+			meye.params.subsample = 1;
+		} else {
+			f->fmt.pix.width = 640;
+			f->fmt.pix.height = 480;
+			meye.params.subsample = 0;
+		}
+		switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_YUYV:
+			meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+			break;
+		case V4L2_PIX_FMT_MJPEG:
+			meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+			break;
+		}
+		up(&meye.lock);
+		f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+		f->fmt.pix.sizeimage = f->fmt.pix.height *
+				       f->fmt.pix.bytesperline;
+		f->fmt.pix.colorspace = 0;
+		f->fmt.pix.priv = 0;
+
+		break;
+	}
+
+	case VIDIOC_REQBUFS: {
+		struct v4l2_requestbuffers *req = arg;
+		int i;
+
+		if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (req->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+		if (meye.grab_fbuffer && req->count == gbuffers) {
+			/* already allocated, no modifications */
+			break;
+		}
+		down(&meye.lock);
+		if (meye.grab_fbuffer) {
+			for (i = 0; i < gbuffers; i++)
+				if (meye.vma_use_count[i]) {
+					up(&meye.lock);
+					return -EINVAL;
+				}
+			rvfree(meye.grab_fbuffer, gbuffers * gbufsize);
+			meye.grab_fbuffer = NULL;
+		}
+		gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS));
+		req->count = gbuffers;
+		meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize);
+		if (!meye.grab_fbuffer) {
+			printk(KERN_ERR "meye: v4l framebuffer allocation"
+					" failed\n");
+			up(&meye.lock);
+			return -ENOMEM;
+		}
+		for (i = 0; i < gbuffers; i++)
+			meye.vma_use_count[i] = 0;
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_QUERYBUF: {
+		struct v4l2_buffer *buf = arg;
+		int index = buf->index;
+
+		if (index < 0 || index >= gbuffers)
+			return -EINVAL;
+		memset(buf, 0, sizeof(*buf));
+		buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf->index = index;
+		buf->bytesused = meye.grab_buffer[index].size;
+		buf->flags = V4L2_BUF_FLAG_MAPPED;
+		if (meye.grab_buffer[index].state == MEYE_BUF_USING)
+			buf->flags |= V4L2_BUF_FLAG_QUEUED;
+		if (meye.grab_buffer[index].state == MEYE_BUF_DONE)
+			buf->flags |= V4L2_BUF_FLAG_DONE;
+		buf->field = V4L2_FIELD_NONE;
+		buf->timestamp = meye.grab_buffer[index].timestamp;
+		buf->sequence = meye.grab_buffer[index].sequence;
+		buf->memory = V4L2_MEMORY_MMAP;
+		buf->m.offset = index * gbufsize;
+		buf->length = gbufsize;
+		break;
+	}
+
+	case VIDIOC_QBUF: {
+		struct v4l2_buffer *buf = arg;
+
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (buf->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+		if (buf->index < 0 || buf->index >= gbuffers)
+			return -EINVAL;
+		if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED)
+			return -EINVAL;
+		down(&meye.lock);
+		buf->flags |= V4L2_BUF_FLAG_QUEUED;
+		buf->flags &= ~V4L2_BUF_FLAG_DONE;
+		meye.grab_buffer[buf->index].state = MEYE_BUF_USING;
+		kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int));
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_DQBUF: {
+		struct v4l2_buffer *buf = arg;
+		int reqnr;
+
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (buf->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+
+		down(&meye.lock);
+		if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) {
+			up(&meye.lock);
+			return -EAGAIN;
+		}
+		if (wait_event_interruptible(meye.proc_list,
+					     kfifo_len(meye.doneq) != 0) < 0) {
+			up(&meye.lock);
+			return -EINTR;
+		}
+		if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr,
+			       sizeof(int))) {
+			up(&meye.lock);
+			return -EBUSY;
+		}
+		if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) {
+			up(&meye.lock);
+			return -EINVAL;
+		}
+		buf->index = reqnr;
+		buf->bytesused = meye.grab_buffer[reqnr].size;
+		buf->flags = V4L2_BUF_FLAG_MAPPED;
+		buf->field = V4L2_FIELD_NONE;
+		buf->timestamp = meye.grab_buffer[reqnr].timestamp;
+		buf->sequence = meye.grab_buffer[reqnr].sequence;
+		buf->memory = V4L2_MEMORY_MMAP;
+		buf->m.offset = reqnr * gbufsize;
+		buf->length = gbufsize;
+		meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED;
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_STREAMON: {
+		down(&meye.lock);
+		switch (meye.mchip_mode) {
+		case MCHIP_HIC_MODE_CONT_OUT:
+			mchip_continuous_start();
+			break;
+		case MCHIP_HIC_MODE_CONT_COMP:
+			mchip_cont_compression_start();
+			break;
+		default:
+			up(&meye.lock);
+			return -EINVAL;
+		}
+		up(&meye.lock);
+		break;
+	}
+
+	case VIDIOC_STREAMOFF: {
+		int i;
+
+		down(&meye.lock);
+		mchip_hic_stop();
+		kfifo_reset(meye.grabq);
+		kfifo_reset(meye.doneq);
+		for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+			meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+		up(&meye.lock);
+		break;
+	}
+
+	/*
+	 * XXX what about private snapshot ioctls ?
+	 * Do they need to be converted to V4L2 ?
+	*/
+
 	default:
 		return -ENOIOCTLCMD;
-		
-	} /* switch */
+	}
 
 	return 0;
 }
@@ -1158,9 +1678,27 @@ static unsigned int meye_poll(struct fil
 	return res;
 }
 
+static void meye_vm_open(struct vm_area_struct *vma)
+{
+	int idx = (int)vma->vm_private_data;
+	meye.vma_use_count[idx]++;
+}
+
+static void meye_vm_close(struct vm_area_struct *vma)
+{
+	int idx = (int)vma->vm_private_data;
+	meye.vma_use_count[idx]--;
+}
+
+static struct vm_operations_struct meye_vm_ops = {
+	.open		= meye_vm_open,
+	.close		= meye_vm_close,
+};
+
 static int meye_mmap(struct file *file, struct vm_area_struct *vma) {
 	unsigned long start = vma->vm_start;
-	unsigned long size  = vma->vm_end - vma->vm_start;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long page, pos;
 
 	down(&meye.lock);
@@ -1169,6 +1707,8 @@ static int meye_mmap(struct file *file, 
 		return -EINVAL;
 	}
 	if (!meye.grab_fbuffer) {
+		int i;
+
 		/* lazy allocation */
 		meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize);
 		if (!meye.grab_fbuffer) {
@@ -1176,8 +1716,10 @@ static int meye_mmap(struct file *file, 
 			up(&meye.lock);
 			return -ENOMEM;
 		}
+		for (i = 0; i < gbuffers; i++)
+			meye.vma_use_count[i] = 0;
 	}
-	pos = (unsigned long)meye.grab_fbuffer;
+	pos = (unsigned long)meye.grab_fbuffer + offset;
 
 	while (size > 0) {
 		page = vmalloc_to_pfn((void *)pos);
@@ -1187,8 +1729,18 @@ static int meye_mmap(struct file *file, 
 		}
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
-		size -= PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
+
+	vma->vm_ops = &meye_vm_ops;
+	vma->vm_flags &= ~VM_IO;	/* not I/O memory */
+	vma->vm_flags |= VM_RESERVED;	/* avoid to swap out this VMA */
+	vma->vm_private_data = (void *) (offset / gbufsize);
+	meye_vm_open(vma);
+
 	up(&meye.lock);
 	return 0;
 }
diff -puN drivers/media/video/meye.h~meye-add-v4l2-support drivers/media/video/meye.h
--- 25/drivers/media/video/meye.h~meye-add-v4l2-support	Thu Nov  4 15:04:46 2004
+++ 25-akpm/drivers/media/video/meye.h	Thu Nov  4 15:04:46 2004
@@ -279,6 +279,8 @@
 struct meye_grab_buffer {
 	int state;			/* state of buffer */
 	unsigned long size;		/* size of jpg frame */
+	struct timeval timestamp;	/* timestamp */
+	unsigned long sequence;		/* sequence number */
 };
 
 /* size of kfifos containings buffer indices */
@@ -302,6 +304,7 @@ struct meye {
 	unsigned char *grab_temp;	/* temporary buffer */
 					/* list of buffers */
 	struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS];
+	int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */
 
 	/* other */
 	struct semaphore lock;		/* semaphore for open/mmap... */
diff -puN include/linux/meye.h~meye-add-v4l2-support include/linux/meye.h
--- 25/include/linux/meye.h~meye-add-v4l2-support	Thu Nov  4 15:04:46 2004
+++ 25-akpm/include/linux/meye.h	Thu Nov  4 15:04:46 2004
@@ -56,4 +56,11 @@ struct meye_params {
 /* get a jpeg compressed snapshot */
 #define MEYEIOC_STILLJCAPT	_IOR ('v', BASE_VIDIOCPRIVATE+5, int)
 
+/* V4L2 private controls */
+#define V4L2_CID_AGC		V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_SHARPNESS	(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PICTURE	(V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_JPEGQUAL	(V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_FRAMERATE	(V4L2_CID_PRIVATE_BASE + 4)
+
 #endif
_
