diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 22:49:23 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 22:49:23 +0300 |
commit | 2183a58803c2bbd87c2d0057eed6779ec4718d4d (patch) | |
tree | 910860a2f0c1f22efe840428f11077a5bd478933 /drivers/media/usb/uvc | |
parent | e28870f9b3e92cd3570925089c6bb789c2603bc4 (diff) | |
parent | 71947828caef0c83d4245f7d1eaddc799b4ff1d1 (diff) | |
download | linux-2183a58803c2bbd87c2d0057eed6779ec4718d4d.tar.xz |
Merge tag 'media/v3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Two new dvb frontend drivers: mn88472 and mn88473
- A new driver for some PCIe DVBSky cards
- A new remote controller driver: meson-ir
- One LIRC staging driver got rewritten and promoted to mainstream:
igorplugusb
- A new tuner driver (m88rs6000t)
- The old omap2 media driver got removed from staging. This driver
uses an old DMA API and it is likely broken on recent kernels.
Nobody cared enough to fix it
- Media bus format moved to a separate header, as DRM will also use the
definitions there
- mem2mem_testdev were renamed to vim2m, in order to use the same
naming convention taken by the other virtual test driver (vivid)
- Added a new driver for coda SoC (coda-jpeg)
- The cx88 driver got converted to use videobuf2 core
- Make DMABUF export buffer to work with DMA Scatter/Gather and Vmalloc
cores
- Lots of other fixes, improvements and cleanups on the drivers.
* tag 'media/v3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (384 commits)
[media] mn88473: One function call less in mn88473_init() after error
[media] mn88473: Remove uneeded check before release_firmware()
[media] lirc_zilog: Deletion of unnecessary checks before vfree()
[media] MAINTAINERS: Add myself as img-ir maintainer
[media] img-ir: Don't set driver's module owner
[media] img-ir: Depend on METAG or MIPS or COMPILE_TEST
[media] img-ir/hw: Drop [un]register_decoder declarations
[media] img-ir/hw: Fix potential deadlock stopping timer
[media] img-ir/hw: Always read data to clear buffer
[media] redrat3: ensure dma is setup properly
[media] ddbridge: remove unneeded check before dvb_unregister_device()
[media] si2157: One function call less in si2157_init() after error
[media] tuners: remove uneeded checks before release_firmware()
[media] arm: omap2: rx51-peripherals: fix build warning
[media] stv090x: add an extra protetion against buffer overflow
[media] stv090x: Remove an unreachable code
[media] stv090x: Some whitespace cleanups
[media] em28xx: checkpatch cleanup: whitespaces/new lines cleanups
[media] si2168: add support for firmware files in new format
[media] si2168: debug printout for firmware version
...
Diffstat (limited to 'drivers/media/usb/uvc')
-rw-r--r-- | drivers/media/usb/uvc/uvc_driver.c | 51 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_queue.c | 161 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_v4l2.c | 1009 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvc_video.c | 23 | ||||
-rw-r--r-- | drivers/media/usb/uvc/uvcvideo.h | 12 |
5 files changed, 703 insertions, 553 deletions
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 7c8322d4fc63..6a4b0b8cd270 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -331,6 +331,7 @@ static int uvc_parse_format(struct uvc_device *dev, struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; + unsigned int width_multiplier = 1; unsigned int interval; unsigned int i, n; __u8 ftype; @@ -366,6 +367,20 @@ static int uvc_parse_format(struct uvc_device *dev, } format->bpp = buffer[21]; + + /* Some devices report a format that doesn't match what they + * really send. + */ + if (dev->quirks & UVC_QUIRK_FORCE_Y8) { + if (format->fcc == V4L2_PIX_FMT_YUYV) { + strlcpy(format->name, "Greyscale 8-bit (Y8 )", + sizeof(format->name)); + format->fcc = V4L2_PIX_FMT_GREY; + format->bpp = 8; + width_multiplier = 2; + } + } + if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { ftype = UVC_VS_FRAME_UNCOMPRESSED; } else { @@ -474,7 +489,8 @@ static int uvc_parse_format(struct uvc_device *dev, frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; - frame->wWidth = get_unaligned_le16(&buffer[5]); + frame->wWidth = get_unaligned_le16(&buffer[5]) + * width_multiplier; frame->wHeight = get_unaligned_le16(&buffer[7]); frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); @@ -1623,12 +1639,12 @@ static void uvc_delete(struct uvc_device *dev) { struct list_head *p, *n; - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + if (dev->vdev.dev) v4l2_device_unregister(&dev->vdev); #ifdef CONFIG_MEDIA_CONTROLLER @@ -1718,6 +1734,11 @@ static int uvc_register_video(struct uvc_device *dev, struct video_device *vdev; int ret; + /* Initialize the video buffers queue. */ + ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); + if (ret) + return ret; + /* Initialize the streaming interface with default streaming * parameters. */ @@ -1744,6 +1765,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) @@ -1991,14 +2013,13 @@ static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); struct uvc_streaming *stream; + int ret = 0; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == UVC_SC_VIDEOCONTROL) { - int ret = 0; - if (reset) { ret = uvc_ctrl_restore_values(dev); if (ret < 0) @@ -2014,8 +2035,13 @@ static int __uvc_resume(struct usb_interface *intf, int reset) } list_for_each_entry(stream, &dev->streams, list) { - if (stream->intf == intf) - return uvc_video_resume(stream, reset); + if (stream->intf == intf) { + ret = uvc_video_resume(stream, reset); + if (ret < 0) + uvc_queue_streamoff(&stream->queue, + stream->queue.queue.type); + return ret; + } } uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " @@ -2504,6 +2530,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, + /* Oculus VR Positional Tracker DK2 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x2833, + .idProduct = 0x0201, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FORCE_Y8 }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 6e92d2080255..cc960723b926 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -36,6 +36,34 @@ * the driver. */ +static inline struct uvc_streaming * +uvc_queue_to_stream(struct uvc_video_queue *queue) +{ + return container_of(queue, struct uvc_streaming, queue); +} + +/* + * Return all queued buffers to videobuf2 in the requested state. + * + * This function must be called with the queue spinlock held. + */ +static void uvc_queue_return_buffers(struct uvc_video_queue *queue, + enum uvc_buffer_state state) +{ + enum vb2_buffer_state vb2_state = state == UVC_BUF_STATE_ERROR + ? VB2_BUF_STATE_ERROR + : VB2_BUF_STATE_QUEUED; + + while (!list_empty(&queue->irqqueue)) { + struct uvc_buffer *buf = list_first_entry(&queue->irqqueue, + struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = state; + vb2_buffer_done(&buf->buf, vb2_state); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 queue operations */ @@ -45,8 +73,7 @@ static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int sizes[], void *alloc_ctxs[]) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - struct uvc_streaming *stream = - container_of(queue, struct uvc_streaming, queue); + struct uvc_streaming *stream = uvc_queue_to_stream(queue); /* Make sure the image size is large enough. */ if (fmt && fmt->fmt.pix.sizeimage < stream->ctrl.dwMaxVideoFrameSize) @@ -109,8 +136,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) static void uvc_buffer_finish(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_streaming *stream = - container_of(queue, struct uvc_streaming, queue); + struct uvc_streaming *stream = uvc_queue_to_stream(queue); struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); if (vb->state == VB2_BUF_STATE_DONE) @@ -131,6 +157,39 @@ static void uvc_wait_finish(struct vb2_queue *vq) mutex_lock(&queue->mutex); } +static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_streaming *stream = uvc_queue_to_stream(queue); + unsigned long flags; + int ret; + + queue->buf_used = 0; + + ret = uvc_video_enable(stream, 1); + if (ret == 0) + return 0; + + spin_lock_irqsave(&queue->irqlock, flags); + uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&queue->irqlock, flags); + + return ret; +} + +static void uvc_stop_streaming(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_streaming *stream = uvc_queue_to_stream(queue); + unsigned long flags; + + uvc_video_enable(stream, 0); + + spin_lock_irqsave(&queue->irqlock, flags); + uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR); + spin_unlock_irqrestore(&queue->irqlock, flags); +} + static struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, @@ -138,6 +197,8 @@ static struct vb2_ops uvc_queue_qops = { .buf_finish = uvc_buffer_finish, .wait_prepare = uvc_wait_prepare, .wait_finish = uvc_wait_finish, + .start_streaming = uvc_start_streaming, + .stop_streaming = uvc_stop_streaming, }; int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, @@ -165,12 +226,19 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, return 0; } +void uvc_queue_release(struct uvc_video_queue *queue) +{ + mutex_lock(&queue->mutex); + vb2_queue_release(&queue->queue); + mutex_unlock(&queue->mutex); +} + /* ----------------------------------------------------------------------------- * V4L2 queue operations */ -int uvc_alloc_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb) +int uvc_request_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) { int ret; @@ -181,13 +249,6 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, return ret ? ret : rb->count; } -void uvc_free_buffers(struct uvc_video_queue *queue) -{ - mutex_lock(&queue->mutex); - vb2_queue_release(&queue->queue); - mutex_unlock(&queue->mutex); -} - int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { int ret; @@ -234,6 +295,28 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, return ret; } +int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_streamon(&queue->queue, type); + mutex_unlock(&queue->mutex); + + return ret; +} + +int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_streamoff(&queue->queue, type); + mutex_unlock(&queue->mutex); + + return ret; +} + int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) { int ret; @@ -289,49 +372,6 @@ int uvc_queue_allocated(struct uvc_video_queue *queue) } /* - * Enable or disable the video buffers queue. - * - * The queue must be enabled before starting video acquisition and must be - * disabled after stopping it. This ensures that the video buffers queue - * state can be properly initialized before buffers are accessed from the - * interrupt handler. - * - * Enabling the video queue returns -EBUSY if the queue is already enabled. - * - * Disabling the video queue cancels the queue and removes all buffers from - * the main queue. - * - * This function can't be called from interrupt context. Use - * uvc_queue_cancel() instead. - */ -int uvc_queue_enable(struct uvc_video_queue *queue, int enable) -{ - unsigned long flags; - int ret; - - mutex_lock(&queue->mutex); - if (enable) { - ret = vb2_streamon(&queue->queue, queue->queue.type); - if (ret < 0) - goto done; - - queue->buf_used = 0; - } else { - ret = vb2_streamoff(&queue->queue, queue->queue.type); - if (ret < 0) - goto done; - - spin_lock_irqsave(&queue->irqlock, flags); - INIT_LIST_HEAD(&queue->irqqueue); - spin_unlock_irqrestore(&queue->irqlock, flags); - } - -done: - mutex_unlock(&queue->mutex); - return ret; -} - -/* * Cancel the video buffers queue. * * Cancelling the queue marks all buffers on the irq queue as erroneous, @@ -345,17 +385,10 @@ done: */ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) { - struct uvc_buffer *buf; unsigned long flags; spin_lock_irqsave(&queue->irqlock, flags); - while (!list_empty(&queue->irqqueue)) { - buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, - queue); - list_del(&buf->queue); - buf->state = UVC_BUF_STATE_ERROR; - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); - } + uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR); /* This must be protected by the irqlock spinlock to avoid race * conditions between uvc_buffer_queue and the disconnection event that * could result in an interruptible wait in uvc_dequeue_buffer. Do not diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 378ae02e593b..9c5cbcf16529 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -530,10 +530,8 @@ static int uvc_v4l2_release(struct file *file) uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); /* Only free resources if this is a privileged handle. */ - if (uvc_has_privileges(handle)) { - uvc_video_enable(stream, 0); - uvc_free_buffers(&stream->queue); - } + if (uvc_has_privileges(handle)) + uvc_queue_release(&stream->queue); /* Release the file handle. */ uvc_dismiss_privileges(handle); @@ -551,553 +549,628 @@ 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_request_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 (!uvc_has_privileges(handle)) + return -EBUSY; - return uvc_v4l2_set_format(stream, arg); + mutex_lock(&stream->mutex); + ret = uvc_queue_streamon(&stream->queue, type); + mutex_unlock(&stream->mutex); - case VIDIOC_G_FMT: - return uvc_v4l2_get_format(stream, arg); + return ret; +} - /* Frame size enumeration */ - case VIDIOC_ENUM_FRAMESIZES: - { - struct v4l2_frmsizeenum *fsize = arg; - struct uvc_format *format = NULL; - struct uvc_frame *frame; - int i; +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; - /* 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); + uvc_queue_streamoff(&stream->queue, type); + 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 0; +} - /* 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; + + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; + ret = uvc_ctrl_get(chain, &xctrl); + uvc_ctrl_rollback(handle); + if (ret < 0) + return ret; - if (!uvc_has_privileges(handle)) - return -EBUSY; + ctrl->value = xctrl.value; + return 0; +} - return uvc_query_buffer(&stream->queue, buf); - } +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; - case VIDIOC_CREATE_BUFS: - { - struct v4l2_create_buffers *cb = arg; + memset(&xctrl, 0, sizeof(xctrl)); + xctrl.id = ctrl->id; + xctrl.value = ctrl->value; - ret = uvc_acquire_privileges(handle); - if (ret < 0) - return ret; + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; - return uvc_create_buffers(&stream->queue, cb); + 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; +} + +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; - case VIDIOC_DQBUF: - if (!uvc_has_privileges(handle)) - return -EBUSY; + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; - return uvc_dequeue_buffer(&stream->queue, arg, - file->f_flags & O_NONBLOCK); + 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; + } + } - case VIDIOC_STREAMON: - { - int *type = arg; + ctrls->error_idx = 0; - if (*type != stream->type) - return -EINVAL; + return uvc_ctrl_rollback(handle); +} - ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); - if (ret < 0) - return ret; +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; - if (!uvc_has_privileges(handle)) - return -EBUSY; + ret = uvc_ctrl_begin(chain); + if (ret < 0) + return ret; - mutex_lock(&stream->mutex); - ret = uvc_video_enable(stream, 1); - mutex_unlock(&stream->mutex); - if (ret < 0) + 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; + + return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true); +} - if (!uvc_has_privileges(handle)) - return -EBUSY; +static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) +{ + struct uvc_fh *handle = fh; - return uvc_video_enable(stream, 0); + return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false); +} + +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 +1178,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 +1362,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 +1423,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/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index df81b9c4faf1..9637e8b86949 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1021,6 +1021,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, uvc_video_get_ts(&ts); + buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; buf->buf.v4l2_buf.sequence = stream->sequence; buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec; buf->buf.v4l2_buf.timestamp.tv_usec = @@ -1734,19 +1735,13 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) uvc_video_clock_reset(stream); ret = uvc_commit_video(stream, &stream->ctrl); - if (ret < 0) { - uvc_queue_enable(&stream->queue, 0); + if (ret < 0) return ret; - } if (!uvc_queue_streaming(&stream->queue)) return 0; - ret = uvc_init_video(stream, GFP_NOIO); - if (ret < 0) - uvc_queue_enable(&stream->queue, 0); - - return ret; + return uvc_init_video(stream, GFP_NOIO); } /* ------------------------------------------------------------------------ @@ -1778,11 +1773,6 @@ int uvc_video_init(struct uvc_streaming *stream) atomic_set(&stream->active, 0); - /* Initialize the video buffers queue. */ - ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); - if (ret) - return ret; - /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if * they don't receive a SET_INTERFACE request before any other video @@ -1889,7 +1879,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) usb_clear_halt(stream->dev->udev, pipe); } - uvc_queue_enable(&stream->queue, 0); uvc_video_clock_cleanup(stream); return 0; } @@ -1898,10 +1887,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) if (ret < 0) return ret; - ret = uvc_queue_enable(&stream->queue, 1); - if (ret < 0) - goto error_queue; - /* Commit the streaming parameters. */ ret = uvc_commit_video(stream, &stream->ctrl); if (ret < 0) @@ -1916,8 +1901,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) error_video: usb_set_interface(stream->dev->udev, stream->intfnum, 0); error_commit: - uvc_queue_enable(&stream->queue, 0); -error_queue: uvc_video_clock_cleanup(stream); return ret; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 864ada740360..f0a04b532ede 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -148,6 +148,7 @@ #define UVC_QUIRK_PROBE_DEF 0x00000100 #define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200 #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400 +#define UVC_QUIRK_FORCE_Y8 0x00000800 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -579,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) @@ -623,9 +623,9 @@ extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); /* Video buffers queue management. */ extern int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int drop_corrupted); -extern int uvc_alloc_buffers(struct uvc_video_queue *queue, +extern void uvc_queue_release(struct uvc_video_queue *queue); +extern int uvc_request_buffers(struct uvc_video_queue *queue, struct v4l2_requestbuffers *rb); -extern void uvc_free_buffers(struct uvc_video_queue *queue); extern int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf); extern int uvc_create_buffers(struct uvc_video_queue *queue, @@ -634,7 +634,10 @@ extern int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf); extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf, int nonblocking); -extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern int uvc_queue_streamon(struct uvc_video_queue *queue, + enum v4l2_buf_type type); +extern int uvc_queue_streamoff(struct uvc_video_queue *queue, + enum v4l2_buf_type type); extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); @@ -653,6 +656,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 */ |