Simplify ioctl handling by using video_ioctl2.

Signed-off-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>

---

Changes since v1:

- Don't memset cap to 0 in uvc_ioctl_querycap
---
 drivers/media/usb/uvc/uvc_driver.c |    1 +
 drivers/media/usb/uvc/uvc_v4l2.c   | 1010 ++++++++++++++++++++----------------
 drivers/media/usb/uvc/uvcvideo.h   |    2 +-
 3 files changed, 559 insertions(+), 454 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c 
b/drivers/media/usb/uvc/uvc_driver.c
index a327f3d..30163432 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1760,6 +1760,7 @@ static int uvc_register_video(struct uvc_device *dev,
         */
        vdev->v4l2_dev = &dev->vdev;
        vdev->fops = &uvc_fops;
+       vdev->ioctl_ops = &uvc_ioctl_ops;
        vdev->release = uvc_release;
        vdev->prio = &stream->chain->prio;
        if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 378ae02..a16fe21 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -551,553 +551,635 @@ static int uvc_v4l2_release(struct file *file)
        return 0;
 }
 
-static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int uvc_ioctl_querycap(struct file *file, void *fh,
+                             struct v4l2_capability *cap)
 {
        struct video_device *vdev = video_devdata(file);
        struct uvc_fh *handle = file->private_data;
        struct uvc_video_chain *chain = handle->chain;
        struct uvc_streaming *stream = handle->stream;
-       long ret = 0;
 
-       switch (cmd) {
-       /* Query capabilities */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = arg;
-
-               memset(cap, 0, sizeof *cap);
-               strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
-               strlcpy(cap->card, vdev->name, sizeof cap->card);
-               usb_make_path(stream->dev->udev,
-                             cap->bus_info, sizeof(cap->bus_info));
-               cap->version = LINUX_VERSION_CODE;
-               cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
-                                 | chain->caps;
-               if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
-                                        | V4L2_CAP_STREAMING;
-               else
-                       cap->device_caps = V4L2_CAP_VIDEO_OUTPUT
-                                        | V4L2_CAP_STREAMING;
-               break;
-       }
+       strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver));
+       strlcpy(cap->card, vdev->name, sizeof(cap->card));
+       usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
+       cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+                         | chain->caps;
+       if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       else
+               cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
 
-       /* Priority */
-       case VIDIOC_G_PRIORITY:
-               *(u32 *)arg = v4l2_prio_max(vdev->prio);
-               break;
+       return 0;
+}
 
-       case VIDIOC_S_PRIORITY:
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
+                             struct v4l2_fmtdesc *fmt)
+{
+       struct uvc_format *format;
+       enum v4l2_buf_type type = fmt->type;
+       __u32 index = fmt->index;
 
-               return v4l2_prio_change(vdev->prio, &handle->vfh.prio,
-                                       *(u32 *)arg);
+       if (fmt->type != stream->type || fmt->index >= stream->nformats)
+               return -EINVAL;
 
-       /* Get, Set & Query control */
-       case VIDIOC_QUERYCTRL:
-               return uvc_query_v4l2_ctrl(chain, arg);
+       memset(fmt, 0, sizeof(*fmt));
+       fmt->index = index;
+       fmt->type = type;
+
+       format = &stream->format[fmt->index];
+       fmt->flags = 0;
+       if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+               fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+       strlcpy(fmt->description, format->name, sizeof(fmt->description));
+       fmt->description[sizeof(fmt->description) - 1] = 0;
+       fmt->pixelformat = format->fcc;
+       return 0;
+}
 
-       case VIDIOC_G_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-               struct v4l2_ext_control xctrl;
+static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
+                                     struct v4l2_fmtdesc *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-               memset(&xctrl, 0, sizeof xctrl);
-               xctrl.id = ctrl->id;
+       return uvc_ioctl_enum_fmt(stream, fmt);
+}
 
-               ret = uvc_ctrl_begin(chain);
-               if (ret < 0)
-                       return ret;
+static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
+                                     struct v4l2_fmtdesc *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-               ret = uvc_ctrl_get(chain, &xctrl);
-               uvc_ctrl_rollback(handle);
-               if (ret >= 0)
-                       ctrl->value = xctrl.value;
-               break;
-       }
+       return uvc_ioctl_enum_fmt(stream, fmt);
+}
 
-       case VIDIOC_S_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-               struct v4l2_ext_control xctrl;
+static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
+                                  struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+       return uvc_v4l2_get_format(stream, fmt);
+}
 
-               memset(&xctrl, 0, sizeof xctrl);
-               xctrl.id = ctrl->id;
-               xctrl.value = ctrl->value;
+static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
+                                  struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-               ret = uvc_ctrl_begin(chain);
-               if (ret < 0)
-                       return ret;
+       return uvc_v4l2_get_format(stream, fmt);
+}
 
-               ret = uvc_ctrl_set(chain, &xctrl);
-               if (ret < 0) {
-                       uvc_ctrl_rollback(handle);
-                       return ret;
-               }
-               ret = uvc_ctrl_commit(handle, &xctrl, 1);
-               if (ret == 0)
-                       ctrl->value = xctrl.value;
-               break;
-       }
+static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
+                                  struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-       case VIDIOC_QUERYMENU:
-               return uvc_query_v4l2_menu(chain, arg);
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
 
-       case VIDIOC_G_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *ctrls = arg;
-               struct v4l2_ext_control *ctrl = ctrls->controls;
-               unsigned int i;
+       return uvc_v4l2_set_format(stream, fmt);
+}
 
-               ret = uvc_ctrl_begin(chain);
-               if (ret < 0)
-                       return ret;
+static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
+                                  struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-                       ret = uvc_ctrl_get(chain, ctrl);
-                       if (ret < 0) {
-                               uvc_ctrl_rollback(handle);
-                               ctrls->error_idx = i;
-                               return ret;
-                       }
-               }
-               ctrls->error_idx = 0;
-               ret = uvc_ctrl_rollback(handle);
-               break;
-       }
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
 
-       case VIDIOC_S_EXT_CTRLS:
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
-               /* Fall through */
-       case VIDIOC_TRY_EXT_CTRLS:
-       {
-               struct v4l2_ext_controls *ctrls = arg;
-               struct v4l2_ext_control *ctrl = ctrls->controls;
-               unsigned int i;
-
-               ret = uvc_ctrl_begin(chain);
-               if (ret < 0)
-                       return ret;
+       return uvc_v4l2_set_format(stream, fmt);
+}
 
-               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-                       ret = uvc_ctrl_set(chain, ctrl);
-                       if (ret < 0) {
-                               uvc_ctrl_rollback(handle);
-                               ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS
-                                                ? ctrls->count : i;
-                               return ret;
-                       }
-               }
+static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
+                                    struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       struct uvc_streaming_control probe;
 
-               ctrls->error_idx = 0;
+       return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
+}
 
-               if (cmd == VIDIOC_S_EXT_CTRLS)
-                       ret = uvc_ctrl_commit(handle,
-                                             ctrls->controls, ctrls->count);
-               else
-                       ret = uvc_ctrl_rollback(handle);
-               break;
-       }
+static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
+                                    struct v4l2_format *fmt)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       struct uvc_streaming_control probe;
 
-       /* Get, Set & Enum input */
-       case VIDIOC_ENUMINPUT:
-       {
-               const struct uvc_entity *selector = chain->selector;
-               struct v4l2_input *input = arg;
-               struct uvc_entity *iterm = NULL;
-               u32 index = input->index;
-               int pin = 0;
-
-               if (selector == NULL ||
-                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
-                       if (index != 0)
-                               return -EINVAL;
-                       list_for_each_entry(iterm, &chain->entities, chain) {
-                               if (UVC_ENTITY_IS_ITERM(iterm))
-                                       break;
-                       }
-                       pin = iterm->id;
-               } else if (index < selector->bNrInPins) {
-                       pin = selector->baSourceID[index];
-                       list_for_each_entry(iterm, &chain->entities, chain) {
-                               if (!UVC_ENTITY_IS_ITERM(iterm))
-                                       continue;
-                               if (iterm->id == pin)
-                                       break;
-                       }
-               }
+       return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
+}
 
-               if (iterm == NULL || iterm->id != pin)
-                       return -EINVAL;
+static int uvc_ioctl_reqbufs(struct file *file, void *fh,
+                            struct v4l2_requestbuffers *rb)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-               memset(input, 0, sizeof *input);
-               input->index = index;
-               strlcpy(input->name, iterm->name, sizeof input->name);
-               if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
-                       input->type = V4L2_INPUT_TYPE_CAMERA;
-               break;
-       }
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
 
-       case VIDIOC_G_INPUT:
-       {
-               u8 input;
+       mutex_lock(&stream->mutex);
+       ret = uvc_alloc_buffers(&stream->queue, rb);
+       mutex_unlock(&stream->mutex);
+       if (ret < 0)
+               return ret;
 
-               if (chain->selector == NULL ||
-                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
-                       *(int *)arg = 0;
-                       break;
-               }
+       if (ret == 0)
+               uvc_dismiss_privileges(handle);
 
-               ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
-                       chain->selector->id, chain->dev->intfnum,
-                       UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
-               if (ret < 0)
-                       return ret;
+       return 0;
+}
 
-               *(int *)arg = input - 1;
-               break;
-       }
+static int uvc_ioctl_querybuf(struct file *file, void *fh,
+                             struct v4l2_buffer *buf)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-       case VIDIOC_S_INPUT:
-       {
-               u32 input = *(u32 *)arg + 1;
+       if (!uvc_has_privileges(handle))
+               return -EBUSY;
 
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+       return uvc_query_buffer(&stream->queue, buf);
+}
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
+static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-               if (chain->selector == NULL ||
-                   (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
-                       if (input != 1)
-                               return -EINVAL;
-                       break;
-               }
+       if (!uvc_has_privileges(handle))
+               return -EBUSY;
 
-               if (input == 0 || input > chain->selector->bNrInPins)
-                       return -EINVAL;
+       return uvc_queue_buffer(&stream->queue, buf);
+}
 
-               return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
-                       chain->selector->id, chain->dev->intfnum,
-                       UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
-       }
+static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer 
*buf)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
 
-       /* Try, Get, Set & Enum format */
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc *fmt = arg;
-               struct uvc_format *format;
-               enum v4l2_buf_type type = fmt->type;
-               __u32 index = fmt->index;
+       if (!uvc_has_privileges(handle))
+               return -EBUSY;
 
-               if (fmt->type != stream->type ||
-                   fmt->index >= stream->nformats)
-                       return -EINVAL;
+       return uvc_dequeue_buffer(&stream->queue, buf,
+                                 file->f_flags & O_NONBLOCK);
+}
 
-               memset(fmt, 0, sizeof(*fmt));
-               fmt->index = index;
-               fmt->type = type;
-
-               format = &stream->format[fmt->index];
-               fmt->flags = 0;
-               if (format->flags & UVC_FMT_FLAG_COMPRESSED)
-                       fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
-               strlcpy(fmt->description, format->name,
-                       sizeof fmt->description);
-               fmt->description[sizeof fmt->description - 1] = 0;
-               fmt->pixelformat = format->fcc;
-               break;
-       }
+static int uvc_ioctl_create_bufs(struct file *file, void *fh,
+                                 struct v4l2_create_buffers *cb)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-       case VIDIOC_TRY_FMT:
-       {
-               struct uvc_streaming_control probe;
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
 
-               return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
-       }
+       return uvc_create_buffers(&stream->queue, cb);
+}
 
-       case VIDIOC_S_FMT:
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+static int uvc_ioctl_streamon(struct file *file, void *fh,
+                             enum v4l2_buf_type type)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
+       if (type != stream->type)
+               return -EINVAL;
+
+       if (!uvc_has_privileges(handle))
+               return -EBUSY;
+
+       mutex_lock(&stream->mutex);
+       ret = uvc_video_enable(stream, 1);
+       mutex_unlock(&stream->mutex);
 
-               return uvc_v4l2_set_format(stream, arg);
+       return ret;
+}
 
-       case VIDIOC_G_FMT:
-               return uvc_v4l2_get_format(stream, arg);
+static int uvc_ioctl_streamoff(struct file *file, void *fh,
+                              enum v4l2_buf_type type)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
 
-       /* Frame size enumeration */
-       case VIDIOC_ENUM_FRAMESIZES:
-       {
-               struct v4l2_frmsizeenum *fsize = arg;
-               struct uvc_format *format = NULL;
-               struct uvc_frame *frame;
-               int i;
+       if (type != stream->type)
+               return -EINVAL;
 
-               /* Look for the given pixel format */
-               for (i = 0; i < stream->nformats; i++) {
-                       if (stream->format[i].fcc ==
-                                       fsize->pixel_format) {
-                               format = &stream->format[i];
-                               break;
-                       }
-               }
-               if (format == NULL)
-                       return -EINVAL;
+       if (!uvc_has_privileges(handle))
+               return -EBUSY;
 
-               if (fsize->index >= format->nframes)
-                       return -EINVAL;
+       mutex_lock(&stream->mutex);
+       ret = uvc_video_enable(stream, 0);
+       mutex_unlock(&stream->mutex);
 
-               frame = &format->frame[fsize->index];
-               fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-               fsize->discrete.width = frame->wWidth;
-               fsize->discrete.height = frame->wHeight;
-               break;
-       }
+       return ret;
+}
 
-       /* Frame interval enumeration */
-       case VIDIOC_ENUM_FRAMEINTERVALS:
-       {
-               struct v4l2_frmivalenum *fival = arg;
-               struct uvc_format *format = NULL;
-               struct uvc_frame *frame = NULL;
-               int i;
-
-               /* Look for the given pixel format and frame size */
-               for (i = 0; i < stream->nformats; i++) {
-                       if (stream->format[i].fcc ==
-                                       fival->pixel_format) {
-                               format = &stream->format[i];
-                               break;
-                       }
-               }
-               if (format == NULL)
+static int uvc_ioctl_enum_input(struct file *file, void *fh,
+                               struct v4l2_input *input)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       const struct uvc_entity *selector = chain->selector;
+       struct uvc_entity *iterm = NULL;
+       u32 index = input->index;
+       int pin = 0;
+
+       if (selector == NULL ||
+           (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+               if (index != 0)
                        return -EINVAL;
-
-               for (i = 0; i < format->nframes; i++) {
-                       if (format->frame[i].wWidth == fival->width &&
-                           format->frame[i].wHeight == fival->height) {
-                               frame = &format->frame[i];
+               list_for_each_entry(iterm, &chain->entities, chain) {
+                       if (UVC_ENTITY_IS_ITERM(iterm))
                                break;
-                       }
                }
-               if (frame == NULL)
-                       return -EINVAL;
-
-               if (frame->bFrameIntervalType) {
-                       if (fival->index >= frame->bFrameIntervalType)
-                               return -EINVAL;
-
-                       fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
-                       fival->discrete.numerator =
-                               frame->dwFrameInterval[fival->index];
-                       fival->discrete.denominator = 10000000;
-                       uvc_simplify_fraction(&fival->discrete.numerator,
-                               &fival->discrete.denominator, 8, 333);
-               } else {
-                       fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
-                       fival->stepwise.min.numerator =
-                               frame->dwFrameInterval[0];
-                       fival->stepwise.min.denominator = 10000000;
-                       fival->stepwise.max.numerator =
-                               frame->dwFrameInterval[1];
-                       fival->stepwise.max.denominator = 10000000;
-                       fival->stepwise.step.numerator =
-                               frame->dwFrameInterval[2];
-                       fival->stepwise.step.denominator = 10000000;
-                       uvc_simplify_fraction(&fival->stepwise.min.numerator,
-                               &fival->stepwise.min.denominator, 8, 333);
-                       uvc_simplify_fraction(&fival->stepwise.max.numerator,
-                               &fival->stepwise.max.denominator, 8, 333);
-                       uvc_simplify_fraction(&fival->stepwise.step.numerator,
-                               &fival->stepwise.step.denominator, 8, 333);
+               pin = iterm->id;
+       } else if (index < selector->bNrInPins) {
+               pin = selector->baSourceID[index];
+               list_for_each_entry(iterm, &chain->entities, chain) {
+                       if (!UVC_ENTITY_IS_ITERM(iterm))
+                               continue;
+                       if (iterm->id == pin)
+                               break;
                }
-               break;
        }
 
-       /* Get & Set streaming parameters */
-       case VIDIOC_G_PARM:
-               return uvc_v4l2_get_streamparm(stream, arg);
+       if (iterm == NULL || iterm->id != pin)
+               return -EINVAL;
 
-       case VIDIOC_S_PARM:
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+       memset(input, 0, sizeof(*input));
+       input->index = index;
+       strlcpy(input->name, iterm->name, sizeof(input->name));
+       if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
+               input->type = V4L2_INPUT_TYPE_CAMERA;
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
+       return 0;
+}
 
-               return uvc_v4l2_set_streamparm(stream, arg);
+static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       int ret;
+       u8 i;
 
-       /* Cropping and scaling */
-       case VIDIOC_CROPCAP:
-       {
-               struct v4l2_cropcap *ccap = arg;
+       if (chain->selector == NULL ||
+           (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+               *input = 0;
+               return 0;
+       }
 
-               if (ccap->type != stream->type)
-                       return -EINVAL;
+       ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
+                            chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,
+                            &i, 1);
+       if (ret < 0)
+               return ret;
 
-               ccap->bounds.left = 0;
-               ccap->bounds.top = 0;
+       *input = i - 1;
+       return 0;
+}
 
-               mutex_lock(&stream->mutex);
-               ccap->bounds.width = stream->cur_frame->wWidth;
-               ccap->bounds.height = stream->cur_frame->wHeight;
-               mutex_unlock(&stream->mutex);
+static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       int ret;
+       u32 i;
 
-               ccap->defrect = ccap->bounds;
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
 
-               ccap->pixelaspect.numerator = 1;
-               ccap->pixelaspect.denominator = 1;
-               break;
+       if (chain->selector == NULL ||
+           (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+               if (input)
+                       return -EINVAL;
+               return 0;
        }
 
-       case VIDIOC_G_CROP:
-       case VIDIOC_S_CROP:
-               return -ENOTTY;
+       if (input >= chain->selector->bNrInPins)
+               return -EINVAL;
 
-       /* Buffers & streaming */
-       case VIDIOC_REQBUFS:
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+       i = input + 1;
+       return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
+                             chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
+                             &i, 1);
+}
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
+static int uvc_ioctl_queryctrl(struct file *file, void *fh,
+                              struct v4l2_queryctrl *qc)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
 
-               mutex_lock(&stream->mutex);
-               ret = uvc_alloc_buffers(&stream->queue, arg);
-               mutex_unlock(&stream->mutex);
-               if (ret < 0)
-                       return ret;
+       return uvc_query_v4l2_ctrl(chain, qc);
+}
 
-               if (ret == 0)
-                       uvc_dismiss_privileges(handle);
+static int uvc_ioctl_g_ctrl(struct file *file, void *fh,
+                           struct v4l2_control *ctrl)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       struct v4l2_ext_control xctrl;
+       int ret;
 
-               ret = 0;
-               break;
+       memset(&xctrl, 0, sizeof(xctrl));
+       xctrl.id = ctrl->id;
 
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer *buf = arg;
+       ret = uvc_ctrl_begin(chain);
+       if (ret < 0)
+               return ret;
 
-               if (!uvc_has_privileges(handle))
-                       return -EBUSY;
+       ret = uvc_ctrl_get(chain, &xctrl);
+       uvc_ctrl_rollback(handle);
+       if (ret < 0)
+               return ret;
 
-               return uvc_query_buffer(&stream->queue, buf);
-       }
+       ctrl->value = xctrl.value;
+       return 0;
+}
 
-       case VIDIOC_CREATE_BUFS:
-       {
-               struct v4l2_create_buffers *cb = arg;
+static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
+                           struct v4l2_control *ctrl)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       struct v4l2_ext_control xctrl;
+       int ret;
 
-               ret = uvc_acquire_privileges(handle);
-               if (ret < 0)
-                       return ret;
+       memset(&xctrl, 0, sizeof(xctrl));
+       xctrl.id = ctrl->id;
+       xctrl.value = ctrl->value;
 
-               return uvc_create_buffers(&stream->queue, cb);
+       ret = uvc_ctrl_begin(chain);
+       if (ret < 0)
+               return ret;
+
+       ret = uvc_ctrl_set(chain, &xctrl);
+       if (ret < 0) {
+               uvc_ctrl_rollback(handle);
+               return ret;
        }
 
-       case VIDIOC_QBUF:
-               if (!uvc_has_privileges(handle))
-                       return -EBUSY;
+       ret = uvc_ctrl_commit(handle, &xctrl, 1);
+       if (ret < 0)
+               return ret;
 
-               return uvc_queue_buffer(&stream->queue, arg);
+       ctrl->value = xctrl.value;
+       return 0;
+}
 
-       case VIDIOC_DQBUF:
-               if (!uvc_has_privileges(handle))
-                       return -EBUSY;
+static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
+                                struct v4l2_ext_controls *ctrls)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+       struct v4l2_ext_control *ctrl = ctrls->controls;
+       unsigned int i;
+       int ret;
 
-               return uvc_dequeue_buffer(&stream->queue, arg,
-                       file->f_flags & O_NONBLOCK);
+       ret = uvc_ctrl_begin(chain);
+       if (ret < 0)
+               return ret;
 
-       case VIDIOC_STREAMON:
-       {
-               int *type = arg;
+       for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+               ret = uvc_ctrl_get(chain, ctrl);
+               if (ret < 0) {
+                       uvc_ctrl_rollback(handle);
+                       ctrls->error_idx = i;
+                       return ret;
+               }
+       }
 
-               if (*type != stream->type)
-                       return -EINVAL;
+       ctrls->error_idx = 0;
 
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+       return uvc_ctrl_rollback(handle);
+}
 
-               if (!uvc_has_privileges(handle))
-                       return -EBUSY;
+static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
+                                    struct v4l2_ext_controls *ctrls,
+                                    bool commit)
+{
+       struct v4l2_ext_control *ctrl = ctrls->controls;
+       struct uvc_video_chain *chain = handle->chain;
+       unsigned int i;
+       int ret;
 
-               mutex_lock(&stream->mutex);
-               ret = uvc_video_enable(stream, 1);
-               mutex_unlock(&stream->mutex);
-               if (ret < 0)
+       ret = uvc_ctrl_begin(chain);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+               ret = uvc_ctrl_set(chain, ctrl);
+               if (ret < 0) {
+                       uvc_ctrl_rollback(handle);
+                       ctrls->error_idx = commit ? ctrls->count : i;
                        return ret;
-               break;
+               }
        }
 
-       case VIDIOC_STREAMOFF:
-       {
-               int *type = arg;
+       ctrls->error_idx = 0;
 
-               if (*type != stream->type)
-                       return -EINVAL;
+       if (commit)
+               return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);
+       else
+               return uvc_ctrl_rollback(handle);
+}
 
-               ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
-               if (ret < 0)
-                       return ret;
+static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
+                                struct v4l2_ext_controls *ctrls)
+{
+       struct uvc_fh *handle = fh;
 
-               if (!uvc_has_privileges(handle))
-                       return -EBUSY;
+       return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
+}
+
+static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
+                                  struct v4l2_ext_controls *ctrls)
+{
+       struct uvc_fh *handle = fh;
+
+       return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
+}
 
-               return uvc_video_enable(stream, 0);
+static int uvc_ioctl_querymenu(struct file *file, void *fh,
+                              struct v4l2_querymenu *qm)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
+
+       return uvc_query_v4l2_menu(chain, qm);
+}
+
+static int uvc_ioctl_cropcap(struct file *file, void *fh,
+                            struct v4l2_cropcap *ccap)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+
+       if (ccap->type != stream->type)
+               return -EINVAL;
+
+       ccap->bounds.left = 0;
+       ccap->bounds.top = 0;
+       mutex_lock(&stream->mutex);
+       ccap->bounds.width = stream->cur_frame->wWidth;
+       ccap->bounds.height = stream->cur_frame->wHeight;
+       mutex_unlock(&stream->mutex);
+
+       ccap->defrect = ccap->bounds;
+
+       ccap->pixelaspect.numerator = 1;
+       ccap->pixelaspect.denominator = 1;
+       return 0;
+}
+
+static int uvc_ioctl_g_parm(struct file *file, void *fh,
+                           struct v4l2_streamparm *parm)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+
+       return uvc_v4l2_get_streamparm(stream, parm);
+}
+
+static int uvc_ioctl_s_parm(struct file *file, void *fh,
+                           struct v4l2_streamparm *parm)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       int ret;
+
+       ret = uvc_acquire_privileges(handle);
+       if (ret < 0)
+               return ret;
+
+       return uvc_v4l2_set_streamparm(stream, parm);
+}
+
+static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
+                                    struct v4l2_frmsizeenum *fsize)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       struct uvc_format *format = NULL;
+       struct uvc_frame *frame;
+       int i;
+
+       /* Look for the given pixel format */
+       for (i = 0; i < stream->nformats; i++) {
+               if (stream->format[i].fcc == fsize->pixel_format) {
+                       format = &stream->format[i];
+                       break;
+               }
        }
+       if (format == NULL)
+               return -EINVAL;
 
-       case VIDIOC_SUBSCRIBE_EVENT:
-       {
-               struct v4l2_event_subscription *sub = arg;
+       if (fsize->index >= format->nframes)
+               return -EINVAL;
 
-               switch (sub->type) {
-               case V4L2_EVENT_CTRL:
-                       return v4l2_event_subscribe(&handle->vfh, sub, 0,
-                                                   &uvc_ctrl_sub_ev_ops);
-               default:
-                       return -EINVAL;
+       frame = &format->frame[fsize->index];
+       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+       fsize->discrete.width = frame->wWidth;
+       fsize->discrete.height = frame->wHeight;
+       return 0;
+}
+
+static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
+                                        struct v4l2_frmivalenum *fival)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_streaming *stream = handle->stream;
+       struct uvc_format *format = NULL;
+       struct uvc_frame *frame = NULL;
+       int i;
+
+       /* Look for the given pixel format and frame size */
+       for (i = 0; i < stream->nformats; i++) {
+               if (stream->format[i].fcc == fival->pixel_format) {
+                       format = &stream->format[i];
+                       break;
                }
        }
+       if (format == NULL)
+               return -EINVAL;
 
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-               return v4l2_event_unsubscribe(&handle->vfh, arg);
+       for (i = 0; i < format->nframes; i++) {
+               if (format->frame[i].wWidth == fival->width &&
+                   format->frame[i].wHeight == fival->height) {
+                       frame = &format->frame[i];
+                       break;
+               }
+       }
+       if (frame == NULL)
+               return -EINVAL;
 
-       case VIDIOC_DQEVENT:
-               return v4l2_event_dequeue(&handle->vfh, arg,
-                                         file->f_flags & O_NONBLOCK);
+       if (frame->bFrameIntervalType) {
+               if (fival->index >= frame->bFrameIntervalType)
+                       return -EINVAL;
 
-       /* Analog video standards make no sense for digital cameras. */
-       case VIDIOC_ENUMSTD:
-       case VIDIOC_QUERYSTD:
-       case VIDIOC_G_STD:
-       case VIDIOC_S_STD:
+               fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+               fival->discrete.numerator =
+                       frame->dwFrameInterval[fival->index];
+               fival->discrete.denominator = 10000000;
+               uvc_simplify_fraction(&fival->discrete.numerator,
+                       &fival->discrete.denominator, 8, 333);
+       } else {
+               fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+               fival->stepwise.min.numerator = frame->dwFrameInterval[0];
+               fival->stepwise.min.denominator = 10000000;
+               fival->stepwise.max.numerator = frame->dwFrameInterval[1];
+               fival->stepwise.max.denominator = 10000000;
+               fival->stepwise.step.numerator = frame->dwFrameInterval[2];
+               fival->stepwise.step.denominator = 10000000;
+               uvc_simplify_fraction(&fival->stepwise.min.numerator,
+                       &fival->stepwise.min.denominator, 8, 333);
+               uvc_simplify_fraction(&fival->stepwise.max.numerator,
+                       &fival->stepwise.max.denominator, 8, 333);
+               uvc_simplify_fraction(&fival->stepwise.step.numerator,
+                       &fival->stepwise.step.denominator, 8, 333);
+       }
 
-       case VIDIOC_OVERLAY:
+       return 0;
+}
 
-       case VIDIOC_ENUMAUDIO:
-       case VIDIOC_ENUMAUDOUT:
+static int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,
+                                    const struct v4l2_event_subscription *sub)
+{
+       switch (sub->type) {
+       case V4L2_EVENT_CTRL:
+               return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops);
+       default:
+               return -EINVAL;
+       }
+}
 
-       case VIDIOC_ENUMOUTPUT:
-               uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
-               return -ENOTTY;
+static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
+                             unsigned int cmd, void *arg)
+{
+       struct uvc_fh *handle = fh;
+       struct uvc_video_chain *chain = handle->chain;
 
+       switch (cmd) {
+       /* Dynamic controls. */
        case UVCIOC_CTRL_MAP:
                return uvc_ioctl_ctrl_map(chain, arg);
 
@@ -1105,23 +1187,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, 
unsigned int cmd, void *arg)
                return uvc_xu_ctrl_query(chain, arg);
 
        default:
-               uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
                return -ENOTTY;
        }
-
-       return ret;
-}
-
-static long uvc_v4l2_ioctl(struct file *file,
-                    unsigned int cmd, unsigned long arg)
-{
-       if (uvc_trace_param & UVC_TRACE_IOCTL) {
-               uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
-               v4l_printk_ioctl(NULL, cmd);
-               printk(")\n");
-       }
-
-       return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
 }
 
 #ifdef CONFIG_COMPAT
@@ -1304,7 +1371,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
 
        old_fs = get_fs();
        set_fs(KERNEL_DS);
-       ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+       ret = video_ioctl2(file, cmd, (unsigned long)&karg);
        set_fs(old_fs);
 
        if (ret < 0)
@@ -1365,11 +1432,48 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct 
file *file,
 }
 #endif
 
+const struct v4l2_ioctl_ops uvc_ioctl_ops = {
+       .vidioc_querycap = uvc_ioctl_querycap,
+       .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
+       .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
+       .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
+       .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
+       .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
+       .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
+       .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
+       .vidioc_reqbufs = uvc_ioctl_reqbufs,
+       .vidioc_querybuf = uvc_ioctl_querybuf,
+       .vidioc_qbuf = uvc_ioctl_qbuf,
+       .vidioc_dqbuf = uvc_ioctl_dqbuf,
+       .vidioc_create_bufs = uvc_ioctl_create_bufs,
+       .vidioc_streamon = uvc_ioctl_streamon,
+       .vidioc_streamoff = uvc_ioctl_streamoff,
+       .vidioc_enum_input = uvc_ioctl_enum_input,
+       .vidioc_g_input = uvc_ioctl_g_input,
+       .vidioc_s_input = uvc_ioctl_s_input,
+       .vidioc_queryctrl = uvc_ioctl_queryctrl,
+       .vidioc_g_ctrl = uvc_ioctl_g_ctrl,
+       .vidioc_s_ctrl = uvc_ioctl_s_ctrl,
+       .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
+       .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
+       .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
+       .vidioc_querymenu = uvc_ioctl_querymenu,
+       .vidioc_cropcap = uvc_ioctl_cropcap,
+       .vidioc_g_parm = uvc_ioctl_g_parm,
+       .vidioc_s_parm = uvc_ioctl_s_parm,
+       .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
+       .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
+       .vidioc_subscribe_event = uvc_ioctl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+       .vidioc_default = uvc_ioctl_default,
+};
+
 const struct v4l2_file_operations uvc_fops = {
        .owner          = THIS_MODULE,
        .open           = uvc_v4l2_open,
        .release        = uvc_v4l2_release,
-       .unlocked_ioctl = uvc_v4l2_ioctl,
+       .unlocked_ioctl = video_ioctl2,
 #ifdef CONFIG_COMPAT
        .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
 #endif
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 2600c96..53db7ed 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -580,7 +580,6 @@ struct uvc_driver {
 #define UVC_TRACE_FORMAT       (1 << 3)
 #define UVC_TRACE_CAPTURE      (1 << 4)
 #define UVC_TRACE_CALLS                (1 << 5)
-#define UVC_TRACE_IOCTL                (1 << 6)
 #define UVC_TRACE_FRAME                (1 << 7)
 #define UVC_TRACE_SUSPEND      (1 << 8)
 #define UVC_TRACE_STATUS       (1 << 9)
@@ -654,6 +653,7 @@ static inline int uvc_queue_streaming(struct 
uvc_video_queue *queue)
 }
 
 /* V4L2 interface */
+extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
 extern const struct v4l2_file_operations uvc_fops;
 
 /* Media controller */
-- 
2.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to