aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video/uvc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r--drivers/media/video/uvc/Kconfig1
-rw-r--r--drivers/media/video/uvc/Makefile2
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c19
-rw-r--r--drivers/media/video/uvc/uvc_debugfs.c136
-rw-r--r--drivers/media/video/uvc/uvc_driver.c41
-rw-r--r--drivers/media/video/uvc/uvc_isight.c10
-rw-r--r--drivers/media/video/uvc/uvc_queue.c564
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c236
-rw-r--r--drivers/media/video/uvc/uvc_video.c653
-rw-r--r--drivers/media/video/uvc/uvcvideo.h128
10 files changed, 1280 insertions, 510 deletions
diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig
index 2956a7637219..6c197da531b2 100644
--- a/drivers/media/video/uvc/Kconfig
+++ b/drivers/media/video/uvc/Kconfig
@@ -1,5 +1,6 @@
config USB_VIDEO_CLASS
tristate "USB Video Class (UVC)"
+ select VIDEOBUF2_VMALLOC
---help---
Support for the USB Video Class (UVC). Currently only video
input devices, such as webcams, are supported.
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile
index 2071ca8a2f03..c26d12fdb8f4 100644
--- a/drivers/media/video/uvc/Makefile
+++ b/drivers/media/video/uvc/Makefile
@@ -1,5 +1,5 @@
uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
- uvc_status.o uvc_isight.o
+ uvc_status.o uvc_isight.o uvc_debugfs.o
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
uvcvideo-objs += uvc_entity.o
endif
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index 254d32688843..0efd3b10b353 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -878,8 +878,21 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
chain->dev->intfnum, ctrl->info.selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
ctrl->info.size);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ if (UVC_ENTITY_TYPE(ctrl->entity) !=
+ UVC_VC_EXTENSION_UNIT)
+ return ret;
+
+ /* GET_RES is mandatory for XU controls, but some
+ * cameras still choke on it. Ignore errors and set the
+ * resolution value to zero.
+ */
+ uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES,
+ "UVC non compliance - GET_RES failed on "
+ "an XU control. Enabling workaround.\n");
+ memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
+ ctrl->info.size);
+ }
}
ctrl->cached = 1;
@@ -1861,7 +1874,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
if (ncontrols == 0)
continue;
- entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
+ entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
GFP_KERNEL);
if (entity->controls == NULL)
return -ENOMEM;
diff --git a/drivers/media/video/uvc/uvc_debugfs.c b/drivers/media/video/uvc/uvc_debugfs.c
new file mode 100644
index 000000000000..14561a5abb79
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_debugfs.c
@@ -0,0 +1,136 @@
+/*
+ * uvc_debugfs.c -- USB Video Class driver - Debugging support
+ *
+ * Copyright (C) 2011
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "uvcvideo.h"
+
+/* -----------------------------------------------------------------------------
+ * Statistics
+ */
+
+#define UVC_DEBUGFS_BUF_SIZE 1024
+
+struct uvc_debugfs_buffer {
+ size_t count;
+ char data[UVC_DEBUGFS_BUF_SIZE];
+};
+
+static int uvc_debugfs_stats_open(struct inode *inode, struct file *file)
+{
+ struct uvc_streaming *stream = inode->i_private;
+ struct uvc_debugfs_buffer *buf;
+
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data));
+
+ file->private_data = buf;
+ return 0;
+}
+
+static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct uvc_debugfs_buffer *buf = file->private_data;
+
+ return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data,
+ buf->count);
+}
+
+static int uvc_debugfs_stats_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations uvc_debugfs_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = uvc_debugfs_stats_open,
+ .llseek = no_llseek,
+ .read = uvc_debugfs_stats_read,
+ .release = uvc_debugfs_stats_release,
+};
+
+/* -----------------------------------------------------------------------------
+ * Global and stream initialization/cleanup
+ */
+
+static struct dentry *uvc_debugfs_root_dir;
+
+int uvc_debugfs_init_stream(struct uvc_streaming *stream)
+{
+ struct usb_device *udev = stream->dev->udev;
+ struct dentry *dent;
+ char dir_name[32];
+
+ if (uvc_debugfs_root_dir == NULL)
+ return -ENODEV;
+
+ sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum);
+
+ dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
+ if (IS_ERR_OR_NULL(dent)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs %s "
+ "directory.\n", dir_name);
+ return -ENODEV;
+ }
+
+ stream->debugfs_dir = dent;
+
+ dent = debugfs_create_file("stats", 0444, stream->debugfs_dir,
+ stream, &uvc_debugfs_stats_fops);
+ if (IS_ERR_OR_NULL(dent)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n");
+ uvc_debugfs_cleanup_stream(stream);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream)
+{
+ if (stream->debugfs_dir == NULL)
+ return;
+
+ debugfs_remove_recursive(stream->debugfs_dir);
+ stream->debugfs_dir = NULL;
+}
+
+int uvc_debugfs_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("uvcvideo", usb_debug_root);
+ if (IS_ERR_OR_NULL(dir)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs directory\n");
+ return -ENODATA;
+ }
+
+ uvc_debugfs_root_dir = dir;
+ return 0;
+}
+
+void uvc_debugfs_cleanup(void)
+{
+ if (uvc_debugfs_root_dir != NULL)
+ debugfs_remove_recursive(uvc_debugfs_root_dir);
+}
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 656d4c9e3b9f..1d131720b6d7 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -23,6 +23,7 @@
* codec can't handle MJPEG data.
*/
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -32,7 +33,6 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/version.h>
-#include <asm/atomic.h>
#include <asm/unaligned.h>
#include <media/v4l2-common.h>
@@ -1675,6 +1675,8 @@ static void uvc_unregister_video(struct uvc_device *dev)
video_unregister_device(stream->vdev);
stream->vdev = NULL;
+
+ uvc_debugfs_cleanup_stream(stream);
}
/* Decrement the stream count and call uvc_delete explicitly if there
@@ -1700,6 +1702,8 @@ static int uvc_register_video(struct uvc_device *dev,
return ret;
}
+ uvc_debugfs_init_stream(stream);
+
/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL) {
@@ -2033,6 +2037,15 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
* though they are compliant.
*/
static struct usb_device_id uvc_ids[] = {
+ /* LogiLink Wireless Webcam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0416,
+ .idProduct = 0xa91a,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Genius eFace 2025 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2126,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Dell XPS m1530 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x2640,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2396,17 +2418,24 @@ struct uvc_driver uvc_driver = {
static int __init uvc_init(void)
{
- int result;
+ int ret;
+
+ uvc_debugfs_init();
- result = usb_register(&uvc_driver.driver);
- if (result == 0)
- printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
- return result;
+ ret = usb_register(&uvc_driver.driver);
+ if (ret < 0) {
+ uvc_debugfs_cleanup();
+ return ret;
+ }
+
+ printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
+ return 0;
}
static void __exit uvc_cleanup(void)
{
usb_deregister(&uvc_driver.driver);
+ uvc_debugfs_cleanup();
}
module_init(uvc_init);
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c
index 74bbe8f18f3e..8510e7259e76 100644
--- a/drivers/media/video/uvc/uvc_isight.c
+++ b/drivers/media/video/uvc/uvc_isight.c
@@ -74,7 +74,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
* Empty buffers (bytesused == 0) don't trigger end of frame detection
* as it doesn't make sense to return an empty buffer.
*/
- if (is_header && buf->buf.bytesused != 0) {
+ if (is_header && buf->bytesused != 0) {
buf->state = UVC_BUF_STATE_DONE;
return -EAGAIN;
}
@@ -83,13 +83,13 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
* contain no data.
*/
if (!is_header) {
- maxlen = buf->buf.length - buf->buf.bytesused;
- mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ maxlen = buf->length - buf->bytesused;
+ mem = buf->mem + buf->bytesused;
nbytes = min(len, maxlen);
memcpy(mem, data, nbytes);
- buf->buf.bytesused += nbytes;
+ buf->bytesused += nbytes;
- if (len > maxlen || buf->buf.bytesused == buf->buf.length) {
+ if (len > maxlen || buf->bytesused == buf->length) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete "
"(overflow).\n");
buf->state = UVC_BUF_STATE_DONE;
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 677691c44500..8f54e24e3f35 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
@@ -19,7 +20,7 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
-#include <linux/atomic.h>
+#include <media/videobuf2-vmalloc.h>
#include "uvcvideo.h"
@@ -29,467 +30,211 @@
* Video queues is initialized by uvc_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
- * Video buffer allocation and freeing are performed by uvc_alloc_buffers and
- * uvc_free_buffers respectively. The former acquires the video queue lock,
- * while the later must be called with the lock held (so that allocation can
- * free previously allocated buffers). Trying to free buffers that are mapped
- * to user space will return -EBUSY.
- *
- * Video buffers are managed using two queues. However, unlike most USB video
- * drivers that use an in queue and an out queue, we use a main queue to hold
- * all queued buffers (both 'empty' and 'done' buffers), and an irq queue to
- * hold empty buffers. This design (copied from video-buf) minimizes locking
- * in interrupt, as only one queue is shared between interrupt and user
- * contexts.
- *
- * Use cases
- * ---------
- *
- * Unless stated otherwise, all operations that modify the irq buffers queue
- * are protected by the irq spinlock.
- *
- * 1. The user queues the buffers, starts streaming and dequeues a buffer.
- *
- * The buffers are added to the main and irq queues. Both operations are
- * protected by the queue lock, and the later is protected by the irq
- * spinlock as well.
- *
- * The completion handler fetches a buffer from the irq queue and fills it
- * with video data. If no buffer is available (irq queue empty), the handler
- * returns immediately.
- *
- * When the buffer is full, the completion handler removes it from the irq
- * queue, marks it as done (UVC_BUF_STATE_DONE) and wakes its wait queue.
- * At that point, any process waiting on the buffer will be woken up. If a
- * process tries to dequeue a buffer after it has been marked done, the
- * dequeing will succeed immediately.
- *
- * 2. Buffers are queued, user is waiting on a buffer and the device gets
- * disconnected.
- *
- * When the device is disconnected, the kernel calls the completion handler
- * with an appropriate status code. The handler marks all buffers in the
- * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
- * that any process waiting on a buffer gets woken up.
- *
- * Waking up up the first buffer on the irq list is not enough, as the
- * process waiting on the buffer might restart the dequeue operation
- * immediately.
- *
+ * Video buffers are managed by videobuf2. The driver uses a mutex to protect
+ * the videobuf2 queue operations by serializing calls to videobuf2 and a
+ * spinlock to protect the IRQ queue that holds the buffers to be processed by
+ * the driver.
*/
-void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
- int drop_corrupted)
-{
- mutex_init(&queue->mutex);
- spin_lock_init(&queue->irqlock);
- INIT_LIST_HEAD(&queue->mainqueue);
- INIT_LIST_HEAD(&queue->irqqueue);
- queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
- queue->type = type;
-}
-
-/*
- * Free the video buffers.
- *
- * This function must be called with the queue lock held.
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
*/
-static int __uvc_free_buffers(struct uvc_video_queue *queue)
+
+static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- unsigned int i;
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream =
+ container_of(queue, struct uvc_streaming, queue);
- for (i = 0; i < queue->count; ++i) {
- if (queue->buffer[i].vma_use_count != 0)
- return -EBUSY;
- }
+ if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
+ *nbuffers = UVC_MAX_VIDEO_BUFFERS;
- if (queue->count) {
- uvc_queue_cancel(queue, 0);
- INIT_LIST_HEAD(&queue->mainqueue);
- vfree(queue->mem);
- queue->count = 0;
- }
+ *nplanes = 1;
+
+ sizes[0] = stream->ctrl.dwMaxVideoFrameSize;
return 0;
}
-int uvc_free_buffers(struct uvc_video_queue *queue)
+static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
- int ret;
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
- mutex_lock(&queue->mutex);
- ret = __uvc_free_buffers(queue);
- mutex_unlock(&queue->mutex);
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+ return -EINVAL;
+ }
- return ret;
-}
+ if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
+ return -ENODEV;
-/*
- * Allocate the video buffers.
- *
- * Pages are reserved to make sure they will not be swapped, as they will be
- * filled in the URB completion handler.
- *
- * Buffers will be individually mapped, so they must all be page aligned.
- */
-int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
- unsigned int buflength)
-{
- unsigned int bufsize = PAGE_ALIGN(buflength);
- unsigned int i;
- void *mem = NULL;
- int ret;
+ buf->state = UVC_BUF_STATE_QUEUED;
+ buf->error = 0;
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ buf->length = vb2_plane_size(vb, 0);
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ buf->bytesused = 0;
+ else
+ buf->bytesused = vb2_get_plane_payload(vb, 0);
- if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
- nbuffers = UVC_MAX_VIDEO_BUFFERS;
+ return 0;
+}
- mutex_lock(&queue->mutex);
+static void uvc_buffer_queue(struct vb2_buffer *vb)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+ unsigned long flags;
- if ((ret = __uvc_free_buffers(queue)) < 0)
- goto done;
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+ list_add_tail(&buf->queue, &queue->irqqueue);
+ } else {
+ /* If the device is disconnected return the buffer to userspace
+ * directly. The next QBUF call will fail with -ENODEV.
+ */
+ buf->state = UVC_BUF_STATE_ERROR;
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+ }
- /* Bail out if no buffers should be allocated. */
- if (nbuffers == 0)
- goto done;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
- /* Decrement the number of buffers until allocation succeeds. */
- for (; nbuffers > 0; --nbuffers) {
- mem = vmalloc_32(nbuffers * bufsize);
- if (mem != NULL)
- break;
- }
+static int 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_buffer *buf = container_of(vb, struct uvc_buffer, buf);
- if (mem == NULL) {
- ret = -ENOMEM;
- goto done;
- }
+ uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
+ return 0;
+}
- for (i = 0; i < nbuffers; ++i) {
- memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
- queue->buffer[i].buf.index = i;
- queue->buffer[i].buf.m.offset = i * bufsize;
- queue->buffer[i].buf.length = buflength;
- queue->buffer[i].buf.type = queue->type;
- queue->buffer[i].buf.field = V4L2_FIELD_NONE;
- queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
- queue->buffer[i].buf.flags = 0;
- init_waitqueue_head(&queue->buffer[i].wait);
- }
+static struct vb2_ops uvc_queue_qops = {
+ .queue_setup = uvc_queue_setup,
+ .buf_prepare = uvc_buffer_prepare,
+ .buf_queue = uvc_buffer_queue,
+ .buf_finish = uvc_buffer_finish,
+};
- queue->mem = mem;
- queue->count = nbuffers;
- queue->buf_size = bufsize;
- ret = nbuffers;
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+ int drop_corrupted)
+{
+ queue->queue.type = type;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.drv_priv = queue;
+ queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
+ queue->queue.ops = &uvc_queue_qops;
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ vb2_queue_init(&queue->queue);
-done:
- mutex_unlock(&queue->mutex);
- return ret;
+ mutex_init(&queue->mutex);
+ spin_lock_init(&queue->irqlock);
+ INIT_LIST_HEAD(&queue->irqqueue);
+ queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
}
-/*
- * Check if buffers have been allocated.
+/* -----------------------------------------------------------------------------
+ * V4L2 queue operations
*/
-int uvc_queue_allocated(struct uvc_video_queue *queue)
+
+int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
{
- int allocated;
+ int ret;
mutex_lock(&queue->mutex);
- allocated = queue->count != 0;
+ ret = vb2_reqbufs(&queue->queue, rb);
mutex_unlock(&queue->mutex);
- return allocated;
+ return ret ? ret : rb->count;
}
-static void __uvc_query_buffer(struct uvc_buffer *buf,
- struct v4l2_buffer *v4l2_buf)
+void uvc_free_buffers(struct uvc_video_queue *queue)
{
- memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
-
- if (buf->vma_use_count)
- v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
-
- switch (buf->state) {
- case UVC_BUF_STATE_ERROR:
- case UVC_BUF_STATE_DONE:
- v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
- break;
- case UVC_BUF_STATE_QUEUED:
- case UVC_BUF_STATE_ACTIVE:
- case UVC_BUF_STATE_READY:
- v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
- break;
- case UVC_BUF_STATE_IDLE:
- default:
- break;
- }
+ 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 *v4l2_buf)
+int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
- int ret = 0;
+ int ret;
mutex_lock(&queue->mutex);
- if (v4l2_buf->index >= queue->count) {
- ret = -EINVAL;
- goto done;
- }
-
- __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
-
-done:
+ ret = vb2_querybuf(&queue->queue, buf);
mutex_unlock(&queue->mutex);
+
return ret;
}
-/*
- * Queue a video buffer. Attempting to queue a buffer that has already been
- * queued will return -EINVAL.
- */
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf)
+int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
- struct uvc_buffer *buf;
- unsigned long flags;
- int ret = 0;
-
- uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
-
- if (v4l2_buf->type != queue->type ||
- v4l2_buf->memory != V4L2_MEMORY_MMAP) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
- "and/or memory (%u).\n", v4l2_buf->type,
- v4l2_buf->memory);
- return -EINVAL;
- }
+ int ret;
mutex_lock(&queue->mutex);
- if (v4l2_buf->index >= queue->count) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
- ret = -EINVAL;
- goto done;
- }
-
- buf = &queue->buffer[v4l2_buf->index];
- if (buf->state != UVC_BUF_STATE_IDLE) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
- "(%u).\n", buf->state);
- ret = -EINVAL;
- goto done;
- }
-
- if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- v4l2_buf->bytesused > buf->buf.length) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
- ret = -EINVAL;
- goto done;
- }
-
- spin_lock_irqsave(&queue->irqlock, flags);
- if (queue->flags & UVC_QUEUE_DISCONNECTED) {
- spin_unlock_irqrestore(&queue->irqlock, flags);
- ret = -ENODEV;
- goto done;
- }
- buf->state = UVC_BUF_STATE_QUEUED;
- if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- buf->buf.bytesused = 0;
- else
- buf->buf.bytesused = v4l2_buf->bytesused;
-
- list_add_tail(&buf->stream, &queue->mainqueue);
- list_add_tail(&buf->queue, &queue->irqqueue);
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
+ ret = vb2_qbuf(&queue->queue, buf);
mutex_unlock(&queue->mutex);
- return ret;
-}
-static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
-{
- if (nonblocking) {
- return (buf->state != UVC_BUF_STATE_QUEUED &&
- buf->state != UVC_BUF_STATE_ACTIVE &&
- buf->state != UVC_BUF_STATE_READY)
- ? 0 : -EAGAIN;
- }
-
- return wait_event_interruptible(buf->wait,
- buf->state != UVC_BUF_STATE_QUEUED &&
- buf->state != UVC_BUF_STATE_ACTIVE &&
- buf->state != UVC_BUF_STATE_READY);
+ return ret;
}
-/*
- * Dequeue a video buffer. If nonblocking is false, block until a buffer is
- * available.
- */
-int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking)
+int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
+ int nonblocking)
{
- struct uvc_buffer *buf;
- int ret = 0;
-
- if (v4l2_buf->type != queue->type ||
- v4l2_buf->memory != V4L2_MEMORY_MMAP) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
- "and/or memory (%u).\n", v4l2_buf->type,
- v4l2_buf->memory);
- return -EINVAL;
- }
+ int ret;
mutex_lock(&queue->mutex);
- if (list_empty(&queue->mainqueue)) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
- ret = -EINVAL;
- goto done;
- }
-
- buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
- if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
- goto done;
-
- uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
- buf->buf.index, buf->state, buf->buf.bytesused);
-
- switch (buf->state) {
- case UVC_BUF_STATE_ERROR:
- uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
- "(transmission error).\n");
- ret = -EIO;
- case UVC_BUF_STATE_DONE:
- buf->state = UVC_BUF_STATE_IDLE;
- break;
-
- case UVC_BUF_STATE_IDLE:
- case UVC_BUF_STATE_QUEUED:
- case UVC_BUF_STATE_ACTIVE:
- case UVC_BUF_STATE_READY:
- default:
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
- "(driver bug?).\n", buf->state);
- ret = -EINVAL;
- goto done;
- }
-
- list_del(&buf->stream);
- __uvc_query_buffer(buf, v4l2_buf);
-
-done:
+ ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
mutex_unlock(&queue->mutex);
- return ret;
-}
-/*
- * VMA operations.
- */
-static void uvc_vm_open(struct vm_area_struct *vma)
-{
- struct uvc_buffer *buffer = vma->vm_private_data;
- buffer->vma_use_count++;
-}
-
-static void uvc_vm_close(struct vm_area_struct *vma)
-{
- struct uvc_buffer *buffer = vma->vm_private_data;
- buffer->vma_use_count--;
+ return ret;
}
-static const struct vm_operations_struct uvc_vm_ops = {
- .open = uvc_vm_open,
- .close = uvc_vm_close,
-};
-
-/*
- * Memory-map a video buffer.
- *
- * This function implements video buffers memory mapping and is intended to be
- * used by the device mmap handler.
- */
int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
{
- struct uvc_buffer *uninitialized_var(buffer);
- struct page *page;
- unsigned long addr, start, size;
- unsigned int i;
- int ret = 0;
-
- start = vma->vm_start;
- size = vma->vm_end - vma->vm_start;
+ int ret;
mutex_lock(&queue->mutex);
+ ret = vb2_mmap(&queue->queue, vma);
+ mutex_unlock(&queue->mutex);
- for (i = 0; i < queue->count; ++i) {
- buffer = &queue->buffer[i];
- if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
-
- if (i == queue->count || PAGE_ALIGN(size) != queue->buf_size) {
- ret = -EINVAL;
- goto done;
- }
-
- /*
- * VM_IO marks the area as being an mmaped region for I/O to a
- * device. It also prevents the region from being core dumped.
- */
- vma->vm_flags |= VM_IO;
-
- addr = (unsigned long)queue->mem + buffer->buf.m.offset;
-#ifdef CONFIG_MMU
- while (size > 0) {
- page = vmalloc_to_page((void *)addr);
- if ((ret = vm_insert_page(vma, start, page)) < 0)
- goto done;
-
- start += PAGE_SIZE;
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-#endif
+ return ret;
+}
- vma->vm_ops = &uvc_vm_ops;
- vma->vm_private_data = buffer;
- uvc_vm_open(vma);
+unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait)
+{
+ unsigned int ret;
-done:
+ mutex_lock(&queue->mutex);
+ ret = vb2_poll(&queue->queue, file, wait);
mutex_unlock(&queue->mutex);
+
return ret;
}
-/*
- * Poll the video queue.
+/* -----------------------------------------------------------------------------
*
- * This function implements video queue polling and is intended to be used by
- * the device poll handler.
*/
-unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait)
+
+/*
+ * Check if buffers have been allocated.
+ */
+int uvc_queue_allocated(struct uvc_video_queue *queue)
{
- struct uvc_buffer *buf;
- unsigned int mask = 0;
+ int allocated;
mutex_lock(&queue->mutex);
- if (list_empty(&queue->mainqueue)) {
- mask |= POLLERR;
- goto done;
- }
- buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
-
- poll_wait(file, &buf->wait, wait);
- if (buf->state == UVC_BUF_STATE_DONE ||
- buf->state == UVC_BUF_STATE_ERROR) {
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- mask |= POLLIN | POLLRDNORM;
- else
- mask |= POLLOUT | POLLWRNORM;
- }
-
-done:
+ allocated = vb2_is_busy(&queue->queue);
mutex_unlock(&queue->mutex);
- return mask;
+
+ return allocated;
}
#ifndef CONFIG_MMU
@@ -515,7 +260,7 @@ unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
ret = -EINVAL;
goto done;
}
- ret = (unsigned long)queue->mem + buffer->buf.m.offset;
+ ret = (unsigned long)buf->mem;
done:
mutex_unlock(&queue->mutex);
return ret;
@@ -540,27 +285,24 @@ done:
*/
int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
{
- unsigned int i;
- int ret = 0;
+ unsigned long flags;
+ int ret;
mutex_lock(&queue->mutex);
if (enable) {
- if (uvc_queue_streaming(queue)) {
- ret = -EBUSY;
+ ret = vb2_streamon(&queue->queue, queue->queue.type);
+ if (ret < 0)
goto done;
- }
- queue->flags |= UVC_QUEUE_STREAMING;
+
queue->buf_used = 0;
} else {
- uvc_queue_cancel(queue, 0);
- INIT_LIST_HEAD(&queue->mainqueue);
-
- for (i = 0; i < queue->count; ++i) {
- queue->buffer[i].error = 0;
- queue->buffer[i].state = UVC_BUF_STATE_IDLE;
- }
+ ret = vb2_streamoff(&queue->queue, queue->queue.type);
+ if (ret < 0)
+ goto done;
- queue->flags &= ~UVC_QUEUE_STREAMING;
+ spin_lock_irqsave(&queue->irqlock, flags);
+ INIT_LIST_HEAD(&queue->irqqueue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
}
done:
@@ -591,12 +333,12 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
queue);
list_del(&buf->queue);
buf->state = UVC_BUF_STATE_ERROR;
- wake_up(&buf->wait);
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
}
/* This must be protected by the irqlock spinlock to avoid race
- * conditions between uvc_queue_buffer and the disconnection event that
+ * conditions between uvc_buffer_queue and the disconnection event that
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
- * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
+ * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED
* state outside the queue code.
*/
if (disconnect)
@@ -613,14 +355,12 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
- buf->buf.bytesused = 0;
+ vb2_set_plane_payload(&buf->buf, 0, 0);
return buf;
}
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
- buf->error = 0;
- buf->state = UVC_BUF_STATE_DONE;
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
@@ -628,7 +368,9 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
nextbuf = NULL;
spin_unlock_irqrestore(&queue->irqlock, flags);
- wake_up(&buf->wait);
+ buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
+ vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
+
return nextbuf;
}
-
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index dadf11f704dc..ff2cdddf9bc6 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
@@ -58,6 +59,15 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
break;
case V4L2_CTRL_TYPE_MENU:
+ /* Prevent excessive memory consumption, as well as integer
+ * overflows.
+ */
+ if (xmap->menu_count == 0 ||
+ xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
+ ret = -EINVAL;
+ goto done;
+ }
+
size = xmap->menu_count * sizeof(*map->menu_info);
map->menu_info = kmalloc(size, GFP_KERNEL);
if (map->menu_info == NULL) {
@@ -513,10 +523,7 @@ static int uvc_v4l2_release(struct file *file)
/* Only free resources if this is a privileged handle. */
if (uvc_has_privileges(handle)) {
uvc_video_enable(stream, 0);
-
- if (uvc_free_buffers(&stream->queue) < 0)
- uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "
- "free buffers.\n");
+ uvc_free_buffers(&stream->queue);
}
/* Release the file handle. */
@@ -914,19 +921,11 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Buffers & streaming */
case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
-
- if (rb->type != stream->type ||
- rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
mutex_lock(&stream->mutex);
- ret = uvc_alloc_buffers(&stream->queue, rb->count,
- stream->ctrl.dwMaxVideoFrameSize);
+ ret = uvc_alloc_buffers(&stream->queue, arg);
mutex_unlock(&stream->mutex);
if (ret < 0)
return ret;
@@ -934,18 +933,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret == 0)
uvc_dismiss_privileges(handle);
- rb->count = ret;
ret = 0;
break;
- }
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *buf = arg;
- if (buf->type != stream->type)
- return -EINVAL;
-
if (!uvc_has_privileges(handle))
return -EBUSY;
@@ -1019,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
default:
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
- return -EINVAL;
+ return -ENOTTY;
}
return ret;
@@ -1037,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file,
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
+#ifdef CONFIG_COMPAT
+struct uvc_xu_control_mapping32 {
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ __u32 v4l2_type;
+ __u32 data_type;
+
+ compat_caddr_t menu_info;
+ __u32 menu_count;
+
+ __u32 reserved[4];
+};
+
+static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
+ const struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
+ __get_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ memset(kp->reserved, 0, sizeof(kp->reserved));
+
+ if (kp->menu_count == 0) {
+ kp->menu_info = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
+ if (kmenus == NULL)
+ return -EFAULT;
+ kp->menu_info = kmenus;
+
+ if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
+ struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus = kp->menu_info;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
+ __put_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ __clear_user(up->reserved, sizeof(up->reserved));
+
+ if (kp->menu_count == 0)
+ return 0;
+
+ if (get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+struct uvc_xu_control_query32 {
+ __u8 unit;
+ __u8 selector;
+ __u8 query;
+ __u16 size;
+ compat_caddr_t data;
+};
+
+static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
+ const struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0) {
+ kp->data = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ kdata = compat_alloc_user_space(kp->size);
+ if (kdata == NULL)
+ return -EFAULT;
+ kp->data = kdata;
+
+ if (copy_in_user(kdata, udata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
+ struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata = kp->data;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0)
+ return 0;
+
+ if (get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ if (copy_in_user(udata, kdata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
+#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
+
+static long uvc_v4l2_compat_ioctl32(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ union {
+ struct uvc_xu_control_mapping xmap;
+ struct uvc_xu_control_query xqry;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ mm_segment_t old_fs;
+ long ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP32:
+ cmd = UVCIOC_CTRL_MAP;
+ ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY32:
+ cmd = UVCIOC_CTRL_QUERY;
+ ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+
+ if (ret < 0)
+ return ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP:
+ ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY:
+ ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
+ break;
+ }
+
+ return ret;
+}
+#endif
+
static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
@@ -1083,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = {
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.unlocked_ioctl = uvc_v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
+#endif
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index b015e8e5e8b0..b76b0ac0958f 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -351,25 +351,565 @@ done:
return ret;
}
-int uvc_commit_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *probe)
+static int uvc_commit_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe)
{
return uvc_set_video_ctrl(stream, probe, 0);
}
+/* -----------------------------------------------------------------------------
+ * Clocks and timestamps
+ */
+
+static void
+uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
+ const __u8 *data, int len)
+{
+ struct uvc_clock_sample *sample;
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ unsigned long flags;
+ struct timespec ts;
+ u16 host_sof;
+ u16 dev_sof;
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size)
+ return;
+
+ /* Extract the timestamps:
+ *
+ * - store the frame PTS in the buffer structure
+ * - if the SCR field is present, retrieve the host SOF counter and
+ * kernel timestamps and store them with the SCR STC and SOF fields
+ * in the ring buffer
+ */
+ if (has_pts && buf != NULL)
+ buf->pts = get_unaligned_le32(&data[2]);
+
+ if (!has_scr)
+ return;
+
+ /* To limit the amount of data, drop SCRs with an SOF identical to the
+ * previous one.
+ */
+ dev_sof = get_unaligned_le16(&data[header_size - 2]);
+ if (dev_sof == stream->clock.last_sof)
+ return;
+
+ stream->clock.last_sof = dev_sof;
+
+ host_sof = usb_get_current_frame_number(stream->dev->udev);
+ ktime_get_ts(&ts);
+
+ /* The UVC specification allows device implementations that can't obtain
+ * the USB frame number to keep their own frame counters as long as they
+ * match the size and frequency of the frame number associated with USB
+ * SOF tokens. The SOF values sent by such devices differ from the USB
+ * SOF tokens by a fixed offset that needs to be estimated and accounted
+ * for to make timestamp recovery as accurate as possible.
+ *
+ * The offset is estimated the first time a device SOF value is received
+ * as the difference between the host and device SOF values. As the two
+ * SOF values can differ slightly due to transmission delays, consider
+ * that the offset is null if the difference is not higher than 10 ms
+ * (negative differences can not happen and are thus considered as an
+ * offset). The video commit control wDelay field should be used to
+ * compute a dynamic threshold instead of using a fixed 10 ms value, but
+ * devices don't report reliable wDelay values.
+ *
+ * See uvc_video_clock_host_sof() for an explanation regarding why only
+ * the 8 LSBs of the delta are kept.
+ */
+ if (stream->clock.sof_offset == (u16)-1) {
+ u16 delta_sof = (host_sof - dev_sof) & 255;
+ if (delta_sof >= 10)
+ stream->clock.sof_offset = delta_sof;
+ else
+ stream->clock.sof_offset = 0;
+ }
+
+ dev_sof = (dev_sof + stream->clock.sof_offset) & 2047;
+
+ spin_lock_irqsave(&stream->clock.lock, flags);
+
+ sample = &stream->clock.samples[stream->clock.head];
+ sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
+ sample->dev_sof = dev_sof;
+ sample->host_sof = host_sof;
+ sample->host_ts = ts;
+
+ /* Update the sliding window head and count. */
+ stream->clock.head = (stream->clock.head + 1) % stream->clock.size;
+
+ if (stream->clock.count < stream->clock.size)
+ stream->clock.count++;
+
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
+static void uvc_video_clock_reset(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ clock->head = 0;
+ clock->count = 0;
+ clock->last_sof = -1;
+ clock->sof_offset = -1;
+}
+
+static int uvc_video_clock_init(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ spin_lock_init(&clock->lock);
+ clock->size = 32;
+
+ clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
+ GFP_KERNEL);
+ if (clock->samples == NULL)
+ return -ENOMEM;
+
+ uvc_video_clock_reset(stream);
+
+ return 0;
+}
+
+static void uvc_video_clock_cleanup(struct uvc_streaming *stream)
+{
+ kfree(stream->clock.samples);
+ stream->clock.samples = NULL;
+}
+
+/*
+ * uvc_video_clock_host_sof - Return the host SOF value for a clock sample
+ *
+ * Host SOF counters reported by usb_get_current_frame_number() usually don't
+ * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame
+ * schedule window. They can be limited to 8, 9 or 10 bits depending on the host
+ * controller and its configuration.
+ *
+ * We thus need to recover the SOF value corresponding to the host frame number.
+ * As the device and host frame numbers are sampled in a short interval, the
+ * difference between their values should be equal to a small delta plus an
+ * integer multiple of 256 caused by the host frame number limited precision.
+ *
+ * To obtain the recovered host SOF value, compute the small delta by masking
+ * the high bits of the host frame counter and device SOF difference and add it
+ * to the device SOF value.
+ */
+static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample)
+{
+ /* The delta value can be negative. */
+ s8 delta_sof;
+
+ delta_sof = (sample->host_sof - sample->dev_sof) & 255;
+
+ return (sample->dev_sof + delta_sof) & 2047;
+}
+
+/*
+ * uvc_video_clock_update - Update the buffer timestamp
+ *
+ * This function converts the buffer PTS timestamp to the host clock domain by
+ * going through the USB SOF clock domain and stores the result in the V4L2
+ * buffer timestamp field.
+ *
+ * The relationship between the device clock and the host clock isn't known.
+ * However, the device and the host share the common USB SOF clock which can be
+ * used to recover that relationship.
+ *
+ * The relationship between the device clock and the USB SOF clock is considered
+ * to be linear over the clock samples sliding window and is given by
+ *
+ * SOF = m * PTS + p
+ *
+ * Several methods to compute the slope (m) and intercept (p) can be used. As
+ * the clock drift should be small compared to the sliding window size, we
+ * assume that the line that goes through the points at both ends of the window
+ * is a good approximation. Naming those points P1 and P2, we get
+ *
+ * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS
+ * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1)
+ *
+ * or
+ *
+ * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1)
+ *
+ * to avoid loosing precision in the division. Similarly, the host timestamp is
+ * computed with
+ *
+ * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2)
+ *
+ * SOF values are coded on 11 bits by USB. We extend their precision with 16
+ * decimal bits, leading to a 11.16 coding.
+ *
+ * TODO: To avoid surprises with device clock values, PTS/STC timestamps should
+ * be normalized using the nominal device clock frequency reported through the
+ * UVC descriptors.
+ *
+ * Both the PTS/STC and SOF counters roll over, after a fixed but device
+ * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the
+ * sliding window size is smaller than the rollover period, differences computed
+ * on unsigned integers will produce the correct result. However, the p term in
+ * the linear relations will be miscomputed.
+ *
+ * To fix the issue, we subtract a constant from the PTS and STC values to bring
+ * PTS to half the 32 bit STC range. The sliding window STC values then fit into
+ * the 32 bit range without any rollover.
+ *
+ * Similarly, we add 2048 to the device SOF values to make sure that the SOF
+ * computed by (1) will never be smaller than 0. This offset is then compensated
+ * by adding 2048 to the SOF values used in (2). However, this doesn't prevent
+ * rollovers between (1) and (2): the SOF value computed by (1) can be slightly
+ * lower than 4096, and the host SOF counters can have rolled over to 2048. This
+ * case is handled by subtracting 2048 from the SOF value if it exceeds the host
+ * SOF value at the end of the sliding window.
+ *
+ * Finally we subtract a constant from the host timestamps to bring the first
+ * timestamp of the sliding window to 1s.
+ */
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf)
+{
+ struct uvc_clock *clock = &stream->clock;
+ struct uvc_clock_sample *first;
+ struct uvc_clock_sample *last;
+ unsigned long flags;
+ struct timespec ts;
+ u32 delta_stc;
+ u32 y1, y2;
+ u32 x1, x2;
+ u32 mean;
+ u32 sof;
+ u32 div;
+ u32 rem;
+ u64 y;
+
+ spin_lock_irqsave(&clock->lock, flags);
+
+ if (clock->count < clock->size)
+ goto done;
+
+ first = &clock->samples[clock->head];
+ last = &clock->samples[(clock->head - 1) % clock->size];
+
+ /* First step, PTS to SOF conversion. */
+ delta_stc = buf->pts - (1UL << 31);
+ x1 = first->dev_stc - delta_stc;
+ x2 = last->dev_stc - delta_stc;
+ if (x1 == x2)
+ goto done;
+
+ y1 = (first->dev_sof + 2048) << 16;
+ y2 = (last->dev_sof + 2048) << 16;
+ if (y2 < y1)
+ y2 += 2048 << 16;
+
+ y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ sof = y;
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
+ "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
+ stream->dev->name, buf->pts,
+ y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ x1, x2, y1, y2, clock->sof_offset);
+
+ /* Second step, SOF to host clock conversion. */
+ x1 = (uvc_video_clock_host_sof(first) + 2048) << 16;
+ x2 = (uvc_video_clock_host_sof(last) + 2048) << 16;
+ if (x2 < x1)
+ x2 += 2048 << 16;
+ if (x1 == x2)
+ goto done;
+
+ ts = timespec_sub(last->host_ts, first->host_ts);
+ y1 = NSEC_PER_SEC;
+ y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec;
+
+ /* Interpolated and host SOF timestamps can wrap around at slightly
+ * different times. Handle this by adding or removing 2048 to or from
+ * the computed SOF value to keep it close to the SOF samples mean
+ * value.
+ */
+ mean = (x1 + x2) / 2;
+ if (mean - (1024 << 16) > sof)
+ sof += 2048 << 16;
+ else if (sof > mean + (1024 << 16))
+ sof -= 2048 << 16;
+
+ y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ div = div_u64_rem(y, NSEC_PER_SEC, &rem);
+ ts.tv_sec = first->host_ts.tv_sec - 1 + div;
+ ts.tv_nsec = first->host_ts.tv_nsec + rem;
+ if (ts.tv_nsec >= NSEC_PER_SEC) {
+ ts.tv_sec++;
+ ts.tv_nsec -= NSEC_PER_SEC;
+ }
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu "
+ "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
+ stream->dev->name,
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
+ v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec,
+ x1, first->host_sof, first->dev_sof,
+ x2, last->host_sof, last->dev_sof, y1, y2);
+
+ /* Update the V4L2 buffer. */
+ v4l2_buf->timestamp.tv_sec = ts.tv_sec;
+ v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+done:
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
/* ------------------------------------------------------------------------
- * Video codecs
+ * Stream statistics
*/
-/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
-#define UVC_STREAM_EOH (1 << 7)
-#define UVC_STREAM_ERR (1 << 6)
-#define UVC_STREAM_STI (1 << 5)
-#define UVC_STREAM_RES (1 << 4)
-#define UVC_STREAM_SCR (1 << 3)
-#define UVC_STREAM_PTS (1 << 2)
-#define UVC_STREAM_EOF (1 << 1)
-#define UVC_STREAM_FID (1 << 0)
+static void uvc_video_stats_decode(struct uvc_streaming *stream,
+ const __u8 *data, int len)
+{
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ u16 uninitialized_var(scr_sof);
+ u32 uninitialized_var(scr_stc);
+ u32 uninitialized_var(pts);
+
+ if (stream->stats.stream.nb_frames == 0 &&
+ stream->stats.frame.nb_packets == 0)
+ ktime_get_ts(&stream->stats.stream.start_ts);
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size || data[0] < header_size) {
+ stream->stats.frame.nb_invalid++;
+ return;
+ }
+
+ /* Extract the timestamps. */
+ if (has_pts)
+ pts = get_unaligned_le32(&data[2]);
+
+ if (has_scr) {
+ scr_stc = get_unaligned_le32(&data[header_size - 6]);
+ scr_sof = get_unaligned_le16(&data[header_size - 2]);
+ }
+
+ /* Is PTS constant through the whole frame ? */
+ if (has_pts && stream->stats.frame.nb_pts) {
+ if (stream->stats.frame.pts != pts) {
+ stream->stats.frame.nb_pts_diffs++;
+ stream->stats.frame.last_pts_diff =
+ stream->stats.frame.nb_packets;
+ }
+ }
+
+ if (has_pts) {
+ stream->stats.frame.nb_pts++;
+ stream->stats.frame.pts = pts;
+ }
+
+ /* Do all frames have a PTS in their first non-empty packet, or before
+ * their first empty packet ?
+ */
+ if (stream->stats.frame.size == 0) {
+ if (len > header_size)
+ stream->stats.frame.has_initial_pts = has_pts;
+ if (len == header_size && has_pts)
+ stream->stats.frame.has_early_pts = true;
+ }
+
+ /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
+ if (has_scr && stream->stats.frame.nb_scr) {
+ if (stream->stats.frame.scr_stc != scr_stc)
+ stream->stats.frame.nb_scr_diffs++;
+ }
+
+ if (has_scr) {
+ /* Expand the SOF counter to 32 bits and store its value. */
+ if (stream->stats.stream.nb_frames > 0 ||
+ stream->stats.frame.nb_scr > 0)
+ stream->stats.stream.scr_sof_count +=
+ (scr_sof - stream->stats.stream.scr_sof) % 2048;
+ stream->stats.stream.scr_sof = scr_sof;
+
+ stream->stats.frame.nb_scr++;
+ stream->stats.frame.scr_stc = scr_stc;
+ stream->stats.frame.scr_sof = scr_sof;
+
+ if (scr_sof < stream->stats.stream.min_sof)
+ stream->stats.stream.min_sof = scr_sof;
+ if (scr_sof > stream->stats.stream.max_sof)
+ stream->stats.stream.max_sof = scr_sof;
+ }
+
+ /* Record the first non-empty packet number. */
+ if (stream->stats.frame.size == 0 && len > header_size)
+ stream->stats.frame.first_data = stream->stats.frame.nb_packets;
+
+ /* Update the frame size. */
+ stream->stats.frame.size += len - header_size;
+
+ /* Update the packets counters. */
+ stream->stats.frame.nb_packets++;
+ if (len > header_size)
+ stream->stats.frame.nb_empty++;
+
+ if (data[1] & UVC_STREAM_ERR)
+ stream->stats.frame.nb_errors++;
+}
+
+static void uvc_video_stats_update(struct uvc_streaming *stream)
+{
+ struct uvc_stats_frame *frame = &stream->stats.frame;
+
+ uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
+ "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
+ "last pts/stc/sof %u/%u/%u\n",
+ stream->sequence, frame->first_data,
+ frame->nb_packets - frame->nb_empty, frame->nb_packets,
+ frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
+ frame->has_early_pts ? "" : "!",
+ frame->has_initial_pts ? "" : "!",
+ frame->nb_scr_diffs, frame->nb_scr,
+ frame->pts, frame->scr_stc, frame->scr_sof);
+
+ stream->stats.stream.nb_frames++;
+ stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
+ stream->stats.stream.nb_empty += stream->stats.frame.nb_empty;
+ stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
+ stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
+
+ if (frame->has_early_pts)
+ stream->stats.stream.nb_pts_early++;
+ if (frame->has_initial_pts)
+ stream->stats.stream.nb_pts_initial++;
+ if (frame->last_pts_diff <= frame->first_data)
+ stream->stats.stream.nb_pts_constant++;
+ if (frame->nb_scr >= frame->nb_packets - frame->nb_empty)
+ stream->stats.stream.nb_scr_count_ok++;
+ if (frame->nb_scr_diffs + 1 == frame->nb_scr)
+ stream->stats.stream.nb_scr_diffs_ok++;
+
+ memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
+}
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size)
+{
+ unsigned int scr_sof_freq;
+ unsigned int duration;
+ struct timespec ts;
+ size_t count = 0;
+
+ ts.tv_sec = stream->stats.stream.stop_ts.tv_sec
+ - stream->stats.stream.start_ts.tv_sec;
+ ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec
+ - stream->stats.stream.start_ts.tv_nsec;
+ if (ts.tv_nsec < 0) {
+ ts.tv_sec--;
+ ts.tv_nsec += 1000000000;
+ }
+
+ /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF
+ * frequency this will not overflow before more than 1h.
+ */
+ duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ if (duration != 0)
+ scr_sof_freq = stream->stats.stream.scr_sof_count * 1000
+ / duration;
+ else
+ scr_sof_freq = 0;
+
+ count += scnprintf(buf + count, size - count,
+ "frames: %u\npackets: %u\nempty: %u\n"
+ "errors: %u\ninvalid: %u\n",
+ stream->stats.stream.nb_frames,
+ stream->stats.stream.nb_packets,
+ stream->stats.stream.nb_empty,
+ stream->stats.stream.nb_errors,
+ stream->stats.stream.nb_invalid);
+ count += scnprintf(buf + count, size - count,
+ "pts: %u early, %u initial, %u ok\n",
+ stream->stats.stream.nb_pts_early,
+ stream->stats.stream.nb_pts_initial,
+ stream->stats.stream.nb_pts_constant);
+ count += scnprintf(buf + count, size - count,
+ "scr: %u count ok, %u diff ok\n",
+ stream->stats.stream.nb_scr_count_ok,
+ stream->stats.stream.nb_scr_diffs_ok);
+ count += scnprintf(buf + count, size - count,
+ "sof: %u <= sof <= %u, freq %u.%03u kHz\n",
+ stream->stats.stream.min_sof,
+ stream->stats.stream.max_sof,
+ scr_sof_freq / 1000, scr_sof_freq % 1000);
+
+ return count;
+}
+
+static void uvc_video_stats_start(struct uvc_streaming *stream)
+{
+ memset(&stream->stats, 0, sizeof(stream->stats));
+ stream->stats.stream.min_sof = 2048;
+}
+
+static void uvc_video_stats_stop(struct uvc_streaming *stream)
+{
+ ktime_get_ts(&stream->stats.stream.stop_ts);
+}
+
+/* ------------------------------------------------------------------------
+ * Video codecs
+ */
/* Video payload decoding is handled by uvc_video_decode_start(),
* uvc_video_decode_data() and uvc_video_decode_end().
@@ -416,14 +956,9 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* - bHeaderLength value must be at least 2 bytes (see above)
* - bHeaderLength value can't be larger than the packet size.
*/
- if (len < 2 || data[0] < 2 || data[0] > len)
+ if (len < 2 || data[0] < 2 || data[0] > len) {
+ stream->stats.frame.nb_invalid++;
return -EINVAL;
-
- /* Skip payloads marked with the error bit ("error frames"). */
- if (data[1] & UVC_STREAM_ERR) {
- uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit "
- "set).\n");
- return -ENODATA;
}
fid = data[1] & UVC_STREAM_FID;
@@ -431,8 +966,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
/* Increase the sequence number regardless of any buffer states, so
* that discontinuous sequence numbers always indicate lost frames.
*/
- if (stream->last_fid != fid)
+ if (stream->last_fid != fid) {
stream->sequence++;
+ if (stream->sequence)
+ uvc_video_stats_update(stream);
+ }
+
+ uvc_video_clock_decode(stream, buf, data, len);
+ uvc_video_stats_decode(stream, data, len);
/* Store the payload FID bit and return immediately when the buffer is
* NULL.
@@ -442,6 +983,13 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
return -ENODATA;
}
+ /* Mark the buffer as bad if the error bit is set. */
+ if (data[1] & UVC_STREAM_ERR) {
+ uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
+ "set).\n");
+ buf->error = 1;
+ }
+
/* Synchronize to the input stream by waiting for the FID bit to be
* toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
* stream->last_fid is initialized to -1, so the first isochronous
@@ -467,9 +1015,10 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
else
ktime_get_real_ts(&ts);
- buf->buf.sequence = stream->sequence;
- buf->buf.timestamp.tv_sec = ts.tv_sec;
- buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ buf->buf.v4l2_buf.sequence = stream->sequence;
+ buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+ buf->buf.v4l2_buf.timestamp.tv_usec =
+ ts.tv_nsec / NSEC_PER_USEC;
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
@@ -490,7 +1039,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* avoids detecting end of frame conditions at FID toggling if the
* previous payload had the EOF bit set.
*/
- if (fid != stream->last_fid && buf->buf.bytesused != 0) {
+ if (fid != stream->last_fid && buf->bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
"toggled).\n");
buf->state = UVC_BUF_STATE_READY;
@@ -505,7 +1054,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
static void uvc_video_decode_data(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
- struct uvc_video_queue *queue = &stream->queue;
unsigned int maxlen, nbytes;
void *mem;
@@ -513,11 +1061,11 @@ static void uvc_video_decode_data(struct uvc_streaming *stream,
return;
/* Copy the video data to the buffer. */
- maxlen = buf->buf.length - buf->buf.bytesused;
- mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ maxlen = buf->length - buf->bytesused;
+ mem = buf->mem + buf->bytesused;
nbytes = min((unsigned int)len, maxlen);
memcpy(mem, data, nbytes);
- buf->buf.bytesused += nbytes;
+ buf->bytesused += nbytes;
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
@@ -530,7 +1078,7 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
/* Mark the buffer as done if the EOF marker is set. */
- if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) {
+ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
if (data[0] == len)
uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
@@ -568,8 +1116,8 @@ static int uvc_video_encode_data(struct uvc_streaming *stream,
void *mem;
/* Copy video data to the URB buffer. */
- mem = queue->mem + buf->buf.m.offset + queue->buf_used;
- nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
+ mem = buf->mem + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size,
nbytes);
memcpy(data, mem, nbytes);
@@ -624,7 +1172,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
urb->iso_frame_desc[i].actual_length);
if (buf->state == UVC_BUF_STATE_READY) {
- if (buf->buf.length != buf->buf.bytesused &&
+ if (buf->length != buf->bytesused &&
!(stream->cur_format->flags &
UVC_FMT_FLAG_COMPRESSED))
buf->error = 1;
@@ -724,12 +1272,12 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
stream->bulk.payload_size += ret;
len -= ret;
- if (buf->buf.bytesused == stream->queue.buf_used ||
+ if (buf->bytesused == stream->queue.buf_used ||
stream->bulk.payload_size == stream->bulk.max_payload_size) {
- if (buf->buf.bytesused == stream->queue.buf_used) {
+ if (buf->bytesused == stream->queue.buf_used) {
stream->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_READY;
- buf->buf.sequence = ++stream->sequence;
+ buf->buf.v4l2_buf.sequence = ++stream->sequence;
uvc_queue_next_buffer(&stream->queue, buf);
stream->last_fid ^= UVC_STREAM_FID;
}
@@ -870,6 +1418,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
struct urb *urb;
unsigned int i;
+ uvc_video_stats_stop(stream);
+
for (i = 0; i < UVC_URBS; ++i) {
urb = stream->urb[i];
if (urb == NULL)
@@ -1009,6 +1559,8 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
stream->bulk.skip_payload = 0;
stream->bulk.payload_size = 0;
+ uvc_video_stats_start(stream);
+
if (intf->num_altsetting > 1) {
struct usb_host_endpoint *best_ep = NULL;
unsigned int best_psize = 3 * 1024;
@@ -1133,6 +1685,8 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset)
stream->frozen = 0;
+ uvc_video_clock_reset(stream);
+
ret = uvc_commit_video(stream, &stream->ctrl);
if (ret < 0) {
uvc_queue_enable(&stream->queue, 0);
@@ -1269,20 +1823,35 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
uvc_uninit_video(stream, 1);
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
uvc_queue_enable(&stream->queue, 0);
+ uvc_video_clock_cleanup(stream);
return 0;
}
- ret = uvc_queue_enable(&stream->queue, 1);
+ ret = uvc_video_clock_init(stream);
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) {
- uvc_queue_enable(&stream->queue, 0);
- return ret;
- }
+ if (ret < 0)
+ goto error_commit;
- return uvc_init_video(stream, GFP_KERNEL);
-}
+ ret = uvc_init_video(stream, GFP_KERNEL);
+ if (ret < 0)
+ goto error_video;
+ return 0;
+
+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/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 4c1392ebcd4b..67f88d85bb16 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -13,6 +13,7 @@
#include <linux/videodev2.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
/* --------------------------------------------------------------------------
* UVC constants
@@ -113,6 +114,7 @@
/* Maximum allowed number of control mappings per device */
#define UVC_MAX_CONTROL_MAPPINGS 1024
+#define UVC_MAX_CONTROL_MENU_ENTRIES 32
/* Devices quirks */
#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
@@ -319,35 +321,30 @@ enum uvc_buffer_state {
};
struct uvc_buffer {
- unsigned long vma_use_count;
- struct list_head stream;
-
- /* Touched by interrupt handler. */
- struct v4l2_buffer buf;
+ struct vb2_buffer buf;
struct list_head queue;
- wait_queue_head_t wait;
+
enum uvc_buffer_state state;
unsigned int error;
+
+ void *mem;
+ unsigned int length;
+ unsigned int bytesused;
+
+ u32 pts;
};
-#define UVC_QUEUE_STREAMING (1 << 0)
-#define UVC_QUEUE_DISCONNECTED (1 << 1)
-#define UVC_QUEUE_DROP_CORRUPTED (1 << 2)
+#define UVC_QUEUE_DISCONNECTED (1 << 0)
+#define UVC_QUEUE_DROP_CORRUPTED (1 << 1)
struct uvc_video_queue {
- enum v4l2_buf_type type;
+ struct vb2_queue queue;
+ struct mutex mutex; /* Protects queue */
- void *mem;
unsigned int flags;
-
- unsigned int count;
- unsigned int buf_size;
unsigned int buf_used;
- struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
- struct mutex mutex; /* protects buffers and mainqueue */
- spinlock_t irqlock; /* protects irqqueue */
- struct list_head mainqueue;
+ spinlock_t irqlock; /* Protects irqqueue */
struct list_head irqqueue;
};
@@ -362,6 +359,51 @@ struct uvc_video_chain {
struct mutex ctrl_mutex; /* Protects ctrl.info */
};
+struct uvc_stats_frame {
+ unsigned int size; /* Number of bytes captured */
+ unsigned int first_data; /* Index of the first non-empty packet */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts; /* Number of packets with a PTS timestamp */
+ unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */
+ unsigned int last_pts_diff; /* Index of the last PTS difference */
+ bool has_initial_pts; /* Whether the first non-empty packet has a PTS */
+ bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */
+ u32 pts; /* PTS of the last packet */
+
+ unsigned int nb_scr; /* Number of packets with a SCR timestamp */
+ unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */
+ u16 scr_sof; /* SCR.SOF of the last packet */
+ u32 scr_stc; /* SCR.STC of the last packet */
+};
+
+struct uvc_stats_stream {
+ struct timespec start_ts; /* Stream start timestamp */
+ struct timespec stop_ts; /* Stream stop timestamp */
+
+ unsigned int nb_frames; /* Number of frames */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts_constant; /* Number of frames with constant PTS */
+ unsigned int nb_pts_early; /* Number of frames with early PTS */
+ unsigned int nb_pts_initial; /* Number of frames with initial PTS */
+
+ unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */
+ unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */
+ unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */
+ unsigned int scr_sof; /* STC.SOF of the last packet */
+ unsigned int min_sof; /* Minimum STC.SOF value */
+ unsigned int max_sof; /* Maximum STC.SOF value */
+};
+
struct uvc_streaming {
struct list_head list;
struct uvc_device *dev;
@@ -387,6 +429,7 @@ struct uvc_streaming {
*/
struct mutex mutex;
+ /* Buffers queue. */
unsigned int frozen : 1;
struct uvc_video_queue queue;
void (*decode) (struct urb *urb, struct uvc_streaming *video,
@@ -408,6 +451,32 @@ struct uvc_streaming {
__u32 sequence;
__u8 last_fid;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct {
+ struct uvc_stats_frame frame;
+ struct uvc_stats_stream stream;
+ } stats;
+
+ /* Timestamps support. */
+ struct uvc_clock {
+ struct uvc_clock_sample {
+ u32 dev_stc;
+ u16 dev_sof;
+ struct timespec host_ts;
+ u16 host_sof;
+ } *samples;
+
+ unsigned int head;
+ unsigned int count;
+ unsigned int size;
+
+ u16 last_sof;
+ u16 sof_offset;
+
+ spinlock_t lock;
+ } clock;
};
enum uvc_device_state {
@@ -479,9 +548,12 @@ struct uvc_driver {
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
#define UVC_TRACE_VIDEO (1 << 10)
+#define UVC_TRACE_STATS (1 << 11)
+#define UVC_TRACE_CLOCK (1 << 12)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
+#define UVC_WARN_XU_GET_RES 2
extern unsigned int uvc_clock_param;
extern unsigned int uvc_no_drop_param;
@@ -516,8 +588,8 @@ extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
extern void 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,
- unsigned int nbuffers, unsigned int buflength);
-extern int uvc_free_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_queue_buffer(struct uvc_video_queue *queue,
@@ -539,7 +611,7 @@ extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
extern int uvc_queue_allocated(struct uvc_video_queue *queue);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
- return queue->flags & UVC_QUEUE_STREAMING;
+ return vb2_is_streaming(&queue->queue);
}
/* V4L2 interface */
@@ -556,10 +628,11 @@ extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
extern int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe);
-extern int uvc_commit_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size);
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf);
/* Status */
extern int uvc_status_init(struct uvc_device *dev);
@@ -612,4 +685,13 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf);
+/* debugfs and statistics */
+int uvc_debugfs_init(void);
+void uvc_debugfs_cleanup(void);
+int uvc_debugfs_init_stream(struct uvc_streaming *stream);
+void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size);
+
#endif