[media] pwc: convert to video_ioctl2

Tested with a Logitech QuickCam Pro 4000.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 4aa70fa..780af5f 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -151,8 +151,6 @@
 static ssize_t pwc_video_read(struct file *file, char __user *buf,
 			  size_t count, loff_t *ppos);
 static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
-static long  pwc_video_ioctl(struct file *file,
-			    unsigned int ioctlnr, unsigned long arg);
 static int  pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
 
 static const struct v4l2_file_operations pwc_fops = {
@@ -162,7 +160,7 @@
 	.read =		pwc_video_read,
 	.poll =		pwc_video_poll,
 	.mmap =		pwc_video_mmap,
-	.unlocked_ioctl = pwc_video_ioctl,
+	.unlocked_ioctl = video_ioctl2,
 };
 static struct video_device pwc_template = {
 	.name =		"Philips Webcam",	/* Filled in later */
@@ -1378,23 +1376,6 @@
 	return 0;
 }
 
-static long pwc_video_ioctl(struct file *file,
-			   unsigned int cmd, unsigned long arg)
-{
-	struct video_device *vdev = file->private_data;
-	struct pwc_device *pdev;
-	long r = -ENODEV;
-
-	if (!vdev)
-		goto out;
-	pdev = video_get_drvdata(vdev);
-
-	if (!pdev->unplugged)
-		r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl);
-out:
-	return r;
-}
-
 static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct video_device *vdev = file->private_data;
@@ -1744,6 +1725,7 @@
 	memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
 	pdev->vdev->parent = &intf->dev;
 	pdev->vdev->lock = &pdev->modlock;
+	pdev->vdev->ioctl_ops = &pwc_ioctl_ops;
 	strcpy(pdev->vdev->name, name);
 	video_set_drvdata(pdev->vdev, pdev);
 
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 8a7e52c..68a5313 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -341,606 +341,554 @@
 
 }
 
-long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
 {
 	struct video_device *vdev = video_devdata(file);
-	struct pwc_device *pdev;
-	DECLARE_WAITQUEUE(wait, current);
+	struct pwc_device *pdev = video_drvdata(file);
 
-	if (vdev == NULL)
-		return -EFAULT;
-	pdev = video_get_drvdata(vdev);
-	if (pdev == NULL)
-		return -EFAULT;
-
-#ifdef CONFIG_USB_PWC_DEBUG
-	if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) {
-		v4l_printk_ioctl(cmd);
-		printk("\n");
-	}
-#endif
-
-
-	switch (cmd) {
-		/* V4L2 Layer */
-		case VIDIOC_QUERYCAP:
-		{
-		    struct v4l2_capability *cap = arg;
-
-		    PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\
-				       "try to use the v4l2 layer\n");
-		    strcpy(cap->driver,PWC_NAME);
-		    strlcpy(cap->card, vdev->name, sizeof(cap->card));
-		    usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info));
-		    cap->version = PWC_VERSION_CODE;
-		    cap->capabilities =
-			V4L2_CAP_VIDEO_CAPTURE	|
-			V4L2_CAP_STREAMING	|
-			V4L2_CAP_READWRITE;
-		    return 0;
-		}
-
-		case VIDIOC_ENUMINPUT:
-		{
-		    struct v4l2_input *i = arg;
-
-		    if ( i->index )	/* Only one INPUT is supported */
-			  return -EINVAL;
-
-		    memset(i, 0, sizeof(struct v4l2_input));
-		    strcpy(i->name, "usb");
-		    return 0;
-		}
-
-		case VIDIOC_G_INPUT:
-		{
-		    int *i = arg;
-		    *i = 0;	/* Only one INPUT is supported */
-		    return 0;
-		}
-		case VIDIOC_S_INPUT:
-		{
-			int *i = arg;
-
-			if ( *i ) {	/* Only one INPUT is supported */
-				PWC_DEBUG_IOCTL("Only one input source is"\
-					" supported with this webcam.\n");
-				return -EINVAL;
-			}
-			return 0;
-		}
-
-		/* TODO: */
-		case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *c = arg;
-			int i;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id);
-			for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) {
-				if (pwc_controls[i].id == c->id) {
-					PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
-					memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl));
-					return 0;
-				}
-			}
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n");
-
-			return -EINVAL;
-		}
-		case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *c = arg;
-			int ret;
-
-			switch (c->id)
-			{
-				case V4L2_CID_BRIGHTNESS:
-					c->value = pwc_get_brightness(pdev);
-					if (c->value<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_CONTRAST:
-					c->value = pwc_get_contrast(pdev);
-					if (c->value<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_SATURATION:
-					ret = pwc_get_saturation(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_GAMMA:
-					c->value = pwc_get_gamma(pdev);
-					if (c->value<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_RED_BALANCE:
-					ret = pwc_get_red_gain(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					c->value >>= 8;
-					return 0;
-				case V4L2_CID_BLUE_BALANCE:
-					ret = pwc_get_blue_gain(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					c->value >>= 8;
-					return 0;
-				case V4L2_CID_AUTO_WHITE_BALANCE:
-					ret = pwc_get_awb(pdev);
-					if (ret<0)
-						return -EINVAL;
-					c->value = (ret == PWC_WB_MANUAL)?0:1;
-					return 0;
-				case V4L2_CID_GAIN:
-					ret = pwc_get_agc(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					c->value >>= 8;
-					return 0;
-				case V4L2_CID_AUTOGAIN:
-					ret = pwc_get_agc(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					c->value = (c->value < 0)?1:0;
-					return 0;
-				case V4L2_CID_EXPOSURE:
-					ret = pwc_get_shutter_speed(pdev, &c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_COLOUR_MODE:
-					ret = pwc_get_colour_mode(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_AUTOCONTOUR:
-					ret = pwc_get_contour(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					c->value=(c->value == -1?1:0);
-					return 0;
-				case V4L2_CID_PRIVATE_CONTOUR:
-					ret = pwc_get_contour(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					c->value >>= 10;
-					return 0;
-				case V4L2_CID_PRIVATE_BACKLIGHT:
-					ret = pwc_get_backlight(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_FLICKERLESS:
-					ret = pwc_get_flicker(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					c->value=(c->value?1:0);
-					return 0;
-				case V4L2_CID_PRIVATE_NOISE_REDUCTION:
-					ret = pwc_get_dynamic_noise(pdev, &c->value);
-					if (ret < 0)
-						return -EINVAL;
-					return 0;
-
-				case V4L2_CID_PRIVATE_SAVE_USER:
-				case V4L2_CID_PRIVATE_RESTORE_USER:
-				case V4L2_CID_PRIVATE_RESTORE_FACTORY:
-					return -EINVAL;
-			}
-			return -EINVAL;
-		}
-		case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *c = arg;
-			int ret;
-
-			switch (c->id)
-			{
-				case V4L2_CID_BRIGHTNESS:
-					c->value <<= 9;
-					ret = pwc_set_brightness(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_CONTRAST:
-					c->value <<= 10;
-					ret = pwc_set_contrast(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_SATURATION:
-					ret = pwc_set_saturation(pdev, c->value);
-					if (ret<0)
-					  return -EINVAL;
-					return 0;
-				case V4L2_CID_GAMMA:
-					c->value <<= 11;
-					ret = pwc_set_gamma(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_RED_BALANCE:
-					c->value <<= 8;
-					ret = pwc_set_red_gain(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_BLUE_BALANCE:
-					c->value <<= 8;
-					ret = pwc_set_blue_gain(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_AUTO_WHITE_BALANCE:
-					c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO;
-					ret = pwc_set_awb(pdev, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_EXPOSURE:
-					c->value <<= 8;
-					ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_AUTOGAIN:
-					/* autogain off means nothing without a gain */
-					if (c->value == 0)
-						return 0;
-					ret = pwc_set_agc(pdev, c->value, 0);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_GAIN:
-					c->value <<= 8;
-					ret = pwc_set_agc(pdev, 0, c->value);
-					if (ret<0)
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_SAVE_USER:
-					if (pwc_save_user(pdev))
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_RESTORE_USER:
-					if (pwc_restore_user(pdev))
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_RESTORE_FACTORY:
-					if (pwc_restore_factory(pdev))
-						return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_COLOUR_MODE:
-					ret = pwc_set_colour_mode(pdev, c->value);
-					if (ret < 0)
-					  return -EINVAL;
-					return 0;
-				case V4L2_CID_PRIVATE_AUTOCONTOUR:
-				  c->value=(c->value == 1)?-1:0;
-				  ret = pwc_set_contour(pdev, c->value);
-				  if (ret < 0)
-				    return -EINVAL;
-				  return 0;
-				case V4L2_CID_PRIVATE_CONTOUR:
-				  c->value <<= 10;
-				  ret = pwc_set_contour(pdev, c->value);
-				  if (ret < 0)
-				    return -EINVAL;
-				  return 0;
-				case V4L2_CID_PRIVATE_BACKLIGHT:
-				  ret = pwc_set_backlight(pdev, c->value);
-				  if (ret < 0)
-				    return -EINVAL;
-				  return 0;
-				case V4L2_CID_PRIVATE_FLICKERLESS:
-				  ret = pwc_set_flicker(pdev, c->value);
-				  if (ret < 0)
-				    return -EINVAL;
-				case V4L2_CID_PRIVATE_NOISE_REDUCTION:
-				  ret = pwc_set_dynamic_noise(pdev, c->value);
-				  if (ret < 0)
-				    return -EINVAL;
-				  return 0;
-
-			}
-			return -EINVAL;
-		}
-
-		case VIDIOC_ENUM_FMT:
-		{
-			struct v4l2_fmtdesc *f = arg;
-			int index;
-
-			if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-			      return -EINVAL;
-
-			/* We only support two format: the raw format, and YUV */
-			index = f->index;
-			memset(f,0,sizeof(struct v4l2_fmtdesc));
-			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-			f->index = index;
-			switch(index)
-			{
-				case 0:
-					/* RAW format */
-					f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2;
-					f->flags = V4L2_FMT_FLAG_COMPRESSED;
-					strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description));
-					break;
-				case 1:
-					f->pixelformat = V4L2_PIX_FMT_YUV420;
-					strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description));
-					break;
-				default:
-					return -EINVAL;
-			}
-			return 0;
-		}
-
-		case VIDIOC_G_FMT:
-		{
-			struct v4l2_format *f = arg;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y);
-			if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-			      return -EINVAL;
-
-			pwc_vidioc_fill_fmt(pdev, f);
-
-			return 0;
-		}
-
-		case VIDIOC_TRY_FMT:
-			return pwc_vidioc_try_fmt(pdev, arg);
-
-		case VIDIOC_S_FMT:
-			return pwc_vidioc_set_fmt(pdev, arg);
-
-		case VIDIOC_G_STD:
-		{
-			v4l2_std_id *std = arg;
-			*std = V4L2_STD_UNKNOWN;
-			return 0;
-		}
-
-		case VIDIOC_S_STD:
-		{
-			v4l2_std_id *std = arg;
-			if (*std != V4L2_STD_UNKNOWN)
-				return -EINVAL;
-			return 0;
-		}
-
-		case VIDIOC_ENUMSTD:
-		{
-			struct v4l2_standard *std = arg;
-			if (std->index != 0)
-				return -EINVAL;
-			std->id = V4L2_STD_UNKNOWN;
-			strlcpy(std->name, "webcam", sizeof(std->name));
-			return 0;
-		}
-
-		case VIDIOC_REQBUFS:
-		{
-			struct v4l2_requestbuffers *rb = arg;
-			int nbuffers;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count);
-			if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-				return -EINVAL;
-			if (rb->memory != V4L2_MEMORY_MMAP)
-				return -EINVAL;
-
-			nbuffers = rb->count;
-			if (nbuffers < 2)
-				nbuffers = 2;
-			else if (nbuffers > pwc_mbufs)
-				nbuffers = pwc_mbufs;
-			/* Force to use our # of buffers */
-			rb->count = pwc_mbufs;
-			return 0;
-		}
-
-		case VIDIOC_QUERYBUF:
-		{
-			struct v4l2_buffer *buf = arg;
-			int index;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index);
-			if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-				PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
-				return -EINVAL;
-			}
-			if (buf->memory != V4L2_MEMORY_MMAP) {
-				PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n");
-				return -EINVAL;
-			}
-			index = buf->index;
-			if (index < 0 || index >= pwc_mbufs) {
-				PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
-				return -EINVAL;
-			}
-
-			memset(buf, 0, sizeof(struct v4l2_buffer));
-			buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-			buf->index = index;
-			buf->m.offset = index * pdev->len_per_image;
-			if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
-				buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
-			else
-				buf->bytesused = pdev->view.size;
-			buf->field = V4L2_FIELD_NONE;
-			buf->memory = V4L2_MEMORY_MMAP;
-			//buf->flags = V4L2_BUF_FLAG_MAPPED;
-			buf->length = pdev->len_per_image;
-
-			PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index);
-			PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset);
-			PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused);
-
-			return 0;
-		}
-
-		case VIDIOC_QBUF:
-		{
-			struct v4l2_buffer *buf = arg;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index);
-			if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-				return -EINVAL;
-			if (buf->memory != V4L2_MEMORY_MMAP)
-				return -EINVAL;
-			if (buf->index >= pwc_mbufs)
-				return -EINVAL;
-
-			buf->flags |= V4L2_BUF_FLAG_QUEUED;
-			buf->flags &= ~V4L2_BUF_FLAG_DONE;
-
-			return 0;
-		}
-
-		case VIDIOC_DQBUF:
-		{
-			struct v4l2_buffer *buf = arg;
-			int ret;
-
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
-
-			if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-				return -EINVAL;
-
-			/* Add ourselves to the frame wait-queue.
-
-			   FIXME: needs auditing for safety.
-			   QUESTION: In what respect? I think that using the
-				     frameq is safe now.
-			 */
-			add_wait_queue(&pdev->frameq, &wait);
-			while (pdev->full_frames == NULL) {
-				if (pdev->error_status) {
-					remove_wait_queue(&pdev->frameq, &wait);
-					set_current_state(TASK_RUNNING);
-					return -pdev->error_status;
-				}
-
-				if (signal_pending(current)) {
-					remove_wait_queue(&pdev->frameq, &wait);
-					set_current_state(TASK_RUNNING);
-					return -ERESTARTSYS;
-				}
-				mutex_unlock(&pdev->modlock);
-				schedule();
-				set_current_state(TASK_INTERRUPTIBLE);
-				mutex_lock(&pdev->modlock);
-			}
-			remove_wait_queue(&pdev->frameq, &wait);
-			set_current_state(TASK_RUNNING);
-
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
-			/* Decompress data in pdev->images[pdev->fill_image] */
-			ret = pwc_handle_frame(pdev);
-			if (ret)
-				return -EFAULT;
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
-
-			buf->index = pdev->fill_image;
-			if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
-				buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
-			else
-				buf->bytesused = pdev->view.size;
-			buf->flags = V4L2_BUF_FLAG_MAPPED;
-			buf->field = V4L2_FIELD_NONE;
-			do_gettimeofday(&buf->timestamp);
-			buf->sequence = 0;
-			buf->memory = V4L2_MEMORY_MMAP;
-			buf->m.offset = pdev->fill_image * pdev->len_per_image;
-			buf->length = pdev->len_per_image;
-			pwc_next_image(pdev);
-
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length);
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset);
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused);
-			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
-			return 0;
-
-		}
-
-		case VIDIOC_STREAMON:
-		{
-			return pwc_isoc_init(pdev);
-		}
-
-		case VIDIOC_STREAMOFF:
-		{
-			pwc_isoc_cleanup(pdev);
-			return 0;
-		}
-
-		case VIDIOC_ENUM_FRAMESIZES:
-		{
-			struct v4l2_frmsizeenum *fsize = arg;
-			unsigned int i = 0, index = fsize->index;
-
-			if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
-				for (i = 0; i < PSZ_MAX; i++) {
-					if (pdev->image_mask & (1UL << i)) {
-						if (!index--) {
-							fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-							fsize->discrete.width = pwc_image_sizes[i].x;
-							fsize->discrete.height = pwc_image_sizes[i].y;
-							return 0;
-						}
-					}
-				}
-			} else if (fsize->index == 0 &&
-				   ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
-				    (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
-
-				fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-				fsize->discrete.width = pdev->abs_max.x;
-				fsize->discrete.height = pdev->abs_max.y;
-				return 0;
-			}
-			return -EINVAL;
-		}
-
-		case VIDIOC_ENUM_FRAMEINTERVALS:
-		{
-			struct v4l2_frmivalenum *fival = arg;
-			int size = -1;
-			unsigned int i;
-
-			for (i = 0; i < PSZ_MAX; i++) {
-				if (pwc_image_sizes[i].x == fival->width &&
-				    pwc_image_sizes[i].y == fival->height) {
-					size = i;
-					break;
-				}
-			}
-
-			/* TODO: Support raw format */
-			if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
-				return -EINVAL;
-			}
-
-			i = pwc_get_fps(pdev, fival->index, size);
-			if (!i)
-				return -EINVAL;
-
-			fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
-			fival->discrete.numerator = 1;
-			fival->discrete.denominator = i;
-
-			return 0;
-		}
-
-		default:
-			return pwc_ioctl(pdev, cmd, arg);
-	} /* ..switch */
+	strcpy(cap->driver, PWC_NAME);
+	strlcpy(cap->card, vdev->name, sizeof(cap->card));
+	usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
+	cap->version = PWC_VERSION_CODE;
+	cap->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE	|
+		V4L2_CAP_STREAMING	|
+		V4L2_CAP_READWRITE;
 	return 0;
 }
 
+static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	if (i->index)	/* Only one INPUT is supported */
+		return -EINVAL;
+
+	strcpy(i->name, "usb");
+	return 0;
+}
+
+static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int pwc_s_input(struct file *file, void *fh, unsigned int i)
+{
+	return i ? -EINVAL : 0;
+}
+
+static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+{
+	int i;
+
+	for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) {
+		if (pwc_controls[i].id == c->id) {
+			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
+			memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl));
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value = pwc_get_brightness(pdev);
+		if (c->value < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_CONTRAST:
+		c->value = pwc_get_contrast(pdev);
+		if (c->value < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_SATURATION:
+		ret = pwc_get_saturation(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_GAMMA:
+		c->value = pwc_get_gamma(pdev);
+		if (c->value < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_RED_BALANCE:
+		ret = pwc_get_red_gain(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value >>= 8;
+		return 0;
+	case V4L2_CID_BLUE_BALANCE:
+		ret = pwc_get_blue_gain(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value >>= 8;
+		return 0;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = pwc_get_awb(pdev);
+		if (ret < 0)
+			return -EINVAL;
+		c->value = (ret == PWC_WB_MANUAL) ? 0 : 1;
+		return 0;
+	case V4L2_CID_GAIN:
+		ret = pwc_get_agc(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value >>= 8;
+		return 0;
+	case V4L2_CID_AUTOGAIN:
+		ret = pwc_get_agc(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value = (c->value < 0) ? 1 : 0;
+		return 0;
+	case V4L2_CID_EXPOSURE:
+		ret = pwc_get_shutter_speed(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_COLOUR_MODE:
+		ret = pwc_get_colour_mode(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_AUTOCONTOUR:
+		ret = pwc_get_contour(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value = (c->value == -1 ? 1 : 0);
+		return 0;
+	case V4L2_CID_PRIVATE_CONTOUR:
+		ret = pwc_get_contour(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value >>= 10;
+		return 0;
+	case V4L2_CID_PRIVATE_BACKLIGHT:
+		ret = pwc_get_backlight(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_FLICKERLESS:
+		ret = pwc_get_flicker(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		c->value = (c->value ? 1 : 0);
+		return 0;
+	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
+		ret = pwc_get_dynamic_noise(pdev, &c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+
+	case V4L2_CID_PRIVATE_SAVE_USER:
+	case V4L2_CID_PRIVATE_RESTORE_USER:
+	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value <<= 9;
+		ret = pwc_set_brightness(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_CONTRAST:
+		c->value <<= 10;
+		ret = pwc_set_contrast(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_SATURATION:
+		ret = pwc_set_saturation(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_GAMMA:
+		c->value <<= 11;
+		ret = pwc_set_gamma(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_RED_BALANCE:
+		c->value <<= 8;
+		ret = pwc_set_red_gain(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_BLUE_BALANCE:
+		c->value <<= 8;
+		ret = pwc_set_blue_gain(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO;
+		ret = pwc_set_awb(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_EXPOSURE:
+		c->value <<= 8;
+		ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_AUTOGAIN:
+		/* autogain off means nothing without a gain */
+		if (c->value == 0)
+			return 0;
+		ret = pwc_set_agc(pdev, c->value, 0);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_GAIN:
+		c->value <<= 8;
+		ret = pwc_set_agc(pdev, 0, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_SAVE_USER:
+		if (pwc_save_user(pdev))
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_RESTORE_USER:
+		if (pwc_restore_user(pdev))
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
+		if (pwc_restore_factory(pdev))
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_COLOUR_MODE:
+		ret = pwc_set_colour_mode(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_AUTOCONTOUR:
+		c->value = (c->value == 1) ? -1 : 0;
+		ret = pwc_set_contour(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_CONTOUR:
+		c->value <<= 10;
+		ret = pwc_set_contour(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_BACKLIGHT:
+		ret = pwc_set_backlight(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+	case V4L2_CID_PRIVATE_FLICKERLESS:
+		ret = pwc_set_flicker(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
+		ret = pwc_set_dynamic_noise(pdev, c->value);
+		if (ret < 0)
+			return -EINVAL;
+		return 0;
+
+	}
+	return -EINVAL;
+}
+
+static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	/* We only support two format: the raw format, and YUV */
+	switch (f->index) {
+	case 0:
+		/* RAW format */
+		f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+		strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description));
+		break;
+	case 1:
+		f->pixelformat = V4L2_PIX_FMT_YUV420;
+		strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description));
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
+			pdev->image.x, pdev->image.y);
+	pwc_vidioc_fill_fmt(pdev, f);
+	return 0;
+}
+
+static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	return pwc_vidioc_try_fmt(pdev, f);
+}
+
+static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	return pwc_vidioc_set_fmt(pdev, f);
+}
+
+static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+	int nbuffers;
+
+	PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
+	if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (rb->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+
+	nbuffers = rb->count;
+	if (nbuffers < 2)
+		nbuffers = 2;
+	else if (nbuffers > pwc_mbufs)
+		nbuffers = pwc_mbufs;
+	/* Force to use our # of buffers */
+	rb->count = pwc_mbufs;
+	return 0;
+}
+
+static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+	int index;
+
+	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
+		return -EINVAL;
+	}
+	index = buf->index;
+	if (index < 0 || index >= pwc_mbufs) {
+		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
+		return -EINVAL;
+	}
+
+	buf->m.offset = index * pdev->len_per_image;
+	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
+		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
+	else
+		buf->bytesused = pdev->view.size;
+	buf->field = V4L2_FIELD_NONE;
+	buf->memory = V4L2_MEMORY_MMAP;
+	/*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
+	buf->length = pdev->len_per_image;
+
+	PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index);
+	PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
+	PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
+
+	return 0;
+}
+
+static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index);
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (buf->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+	if (buf->index >= pwc_mbufs)
+		return -EINVAL;
+
+	buf->flags |= V4L2_BUF_FLAG_QUEUED;
+	buf->flags &= ~V4L2_BUF_FLAG_DONE;
+
+	return 0;
+}
+
+static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct pwc_device *pdev = video_drvdata(file);
+	int ret;
+
+	PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
+
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	add_wait_queue(&pdev->frameq, &wait);
+	while (pdev->full_frames == NULL) {
+		if (pdev->error_status) {
+			remove_wait_queue(&pdev->frameq, &wait);
+			set_current_state(TASK_RUNNING);
+			return -pdev->error_status;
+		}
+
+		if (signal_pending(current)) {
+			remove_wait_queue(&pdev->frameq, &wait);
+			set_current_state(TASK_RUNNING);
+			return -ERESTARTSYS;
+		}
+		mutex_unlock(&pdev->modlock);
+		schedule();
+		set_current_state(TASK_INTERRUPTIBLE);
+		mutex_lock(&pdev->modlock);
+	}
+	remove_wait_queue(&pdev->frameq, &wait);
+	set_current_state(TASK_RUNNING);
+
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
+	/* Decompress data in pdev->images[pdev->fill_image] */
+	ret = pwc_handle_frame(pdev);
+	if (ret)
+		return -EFAULT;
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
+
+	buf->index = pdev->fill_image;
+	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
+		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
+	else
+		buf->bytesused = pdev->view.size;
+	buf->flags = V4L2_BUF_FLAG_MAPPED;
+	buf->field = V4L2_FIELD_NONE;
+	do_gettimeofday(&buf->timestamp);
+	buf->sequence = 0;
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->m.offset = pdev->fill_image * pdev->len_per_image;
+	buf->length = pdev->len_per_image;
+	pwc_next_image(pdev);
+
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
+	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
+	return 0;
+
+}
+
+static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	return pwc_isoc_init(pdev);
+}
+
+static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	pwc_isoc_cleanup(pdev);
+	return 0;
+}
+
+static int pwc_enum_framesizes(struct file *file, void *fh,
+					 struct v4l2_frmsizeenum *fsize)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+	unsigned int i = 0, index = fsize->index;
+
+	if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
+		for (i = 0; i < PSZ_MAX; i++) {
+			if (pdev->image_mask & (1UL << i)) {
+				if (!index--) {
+					fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+					fsize->discrete.width = pwc_image_sizes[i].x;
+					fsize->discrete.height = pwc_image_sizes[i].y;
+					return 0;
+				}
+			}
+		}
+	} else if (fsize->index == 0 &&
+			((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
+			 (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
+
+		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+		fsize->discrete.width = pdev->abs_max.x;
+		fsize->discrete.height = pdev->abs_max.y;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int pwc_enum_frameintervals(struct file *file, void *fh,
+					   struct v4l2_frmivalenum *fival)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+	int size = -1;
+	unsigned int i;
+
+	for (i = 0; i < PSZ_MAX; i++) {
+		if (pwc_image_sizes[i].x == fival->width &&
+				pwc_image_sizes[i].y == fival->height) {
+			size = i;
+			break;
+		}
+	}
+
+	/* TODO: Support raw format */
+	if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
+		return -EINVAL;
+
+	i = pwc_get_fps(pdev, fival->index, size);
+	if (!i)
+		return -EINVAL;
+
+	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	fival->discrete.numerator = 1;
+	fival->discrete.denominator = i;
+
+	return 0;
+}
+
+static long pwc_default(struct file *file, void *fh, int cmd, void *arg)
+{
+	struct pwc_device *pdev = video_drvdata(file);
+
+	return pwc_ioctl(pdev, cmd, arg);
+}
+
+const struct v4l2_ioctl_ops pwc_ioctl_ops = {
+	.vidioc_querycap		    = pwc_querycap,
+	.vidioc_enum_input		    = pwc_enum_input,
+	.vidioc_g_input			    = pwc_g_input,
+	.vidioc_s_input			    = pwc_s_input,
+	.vidioc_enum_fmt_vid_cap	    = pwc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		    = pwc_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		    = pwc_s_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		    = pwc_try_fmt_vid_cap,
+	.vidioc_queryctrl		    = pwc_queryctrl,
+	.vidioc_g_ctrl			    = pwc_g_ctrl,
+	.vidioc_s_ctrl			    = pwc_s_ctrl,
+	.vidioc_reqbufs			    = pwc_reqbufs,
+	.vidioc_querybuf		    = pwc_querybuf,
+	.vidioc_qbuf			    = pwc_qbuf,
+	.vidioc_dqbuf			    = pwc_dqbuf,
+	.vidioc_streamon		    = pwc_streamon,
+	.vidioc_streamoff		    = pwc_streamoff,
+	.vidioc_enum_framesizes		    = pwc_enum_framesizes,
+	.vidioc_enum_frameintervals	    = pwc_enum_frameintervals,
+	.vidioc_default		    = pwc_default,
+};
+
+
 /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 16bbc6d..e947766 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -339,8 +339,7 @@
 /* Private ioctl()s; see pwc-ioctl.h */
 extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
 
-/** Functions in pwc-v4l.c */
-extern long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg);
+extern const struct v4l2_ioctl_ops pwc_ioctl_ops;
 
 /** pwc-uncompress.c */
 /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */