2021-03-16 21:18:23

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 00/13] uvcvideo: Fix v4l2-compliance errors

v4l2-compliance -m /dev/media0 -a -f
Total for uvcvideo device /dev/media0: 8, Succeeded: 6, Failed: 2, Warnings: 0
Total for uvcvideo device /dev/video0: 54, Succeeded: 50, Failed: 4, Warnings: 2
Total for uvcvideo device /dev/video1: 46, Succeeded: 46, Failed: 0, Warnings: 0
Grand Total for uvcvideo device /dev/media0: 108, Succeeded: 102,
Failed: 6, Warnings: 2

After fixing all of them we go down to:

Total for uvcvideo device /dev/media0: 8, Succeeded: 8, Failed: 0, Warnings: 0
Total for uvcvideo device /dev/video0: 54, Succeeded: 54, Failed: 0, Warnings: 0
Total for uvcvideo device /dev/video1: 46, Succeeded: 46, Failed: 0, Warnings: 0
Grand Total for uvcvideo device /dev/media0: 108, Succeeded: 108,
Failed: 0, Warnings: 0

YES, NO MORE WARNINGS :)

Note that we depend on:
https://patchwork.linuxtv.org/project/linux-media/patch/[email protected]/

With Hans patch we can also pass v4l2-compliance -s.

Changelog from v4 (Thanks to Hans and Laurent)
- Use framework names for controls
- Check the control flags before accessing them
- Drop meta headers instead of returning error
- Fix error_idx handling in v4l2-ioctl

Hans Verkuil (1):
uvc: use vb2 ioctl and fop helpers

Ricardo Ribalda (12):
media: v4l2-ioctl: Fix check_ext_ctrls
media: uvcvideo: Set capability in s_param
media: uvcvideo: Return -EIO for control errors
media: uvcvideo: Check controls flags before accessing them
media: uvcvideo: refactor __uvc_ctrl_add_mapping
media: uvcvideo: Add support for V4L2_CTRL_TYPE_CTRL_CLASS
media: uvcvideo: Use dev->name for querycap()
media: uvcvideo: Set unique vdev name based in type
media: uvcvideo: Increase the size of UVC_METADATA_BUF_SIZE
media: uvcvideo: Return -EACCES to inactive controls
media: uvcvideo: Use control names from framework
media: v4l2-ioctl: Set error_idx to the right value

drivers/media/usb/uvc/uvc_ctrl.c | 209 +++++++++++----
drivers/media/usb/uvc/uvc_driver.c | 22 +-
drivers/media/usb/uvc/uvc_metadata.c | 8 +-
drivers/media/usb/uvc/uvc_queue.c | 143 ----------
drivers/media/usb/uvc/uvc_v4l2.c | 373 ++++++---------------------
drivers/media/usb/uvc/uvc_video.c | 13 +-
drivers/media/usb/uvc/uvcvideo.h | 41 +--
drivers/media/v4l2-core/v4l2-ioctl.c | 40 ++-
8 files changed, 303 insertions(+), 546 deletions(-)

--
2.31.0.rc2.261.g7f71774620-goog


2021-03-16 21:18:37

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 04/13] media: uvcvideo: Check controls flags before accessing them

We can figure out if reading/writing a set of controls can fail without
accessing them by checking their flags.

This way we can honor the API closer:

If an error is found when validating the list of controls passed with
VIDIOC_G_EXT_CTRLS, then error_idx shall be set to ctrls->count to
indicate to userspace that no actual hardware was touched.

Fixes v4l2-compliance:
Control ioctls (Input 0):
warn: v4l2-test-controls.cpp(765): g_ext_ctrls(0) invalid error_idx 0
fail: v4l2-test-controls.cpp(645): invalid error index write only control
test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL

Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_v4l2.c | 69 +++++++++++++++++++++++---------
1 file changed, 51 insertions(+), 18 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 157310c0ca87..e956d833ed84 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1040,31 +1040,54 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
return 0;
}

-static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
- struct v4l2_ext_controls *ctrls)
+static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
+ struct v4l2_ext_controls *ctrls, bool read)
{
- struct uvc_fh *handle = fh;
- struct uvc_video_chain *chain = handle->chain;
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
- int ret;
+ int ret = 0;

- if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
- for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- struct v4l2_queryctrl qc = { .id = ctrl->id };
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ struct v4l2_queryctrl qc = { .id = ctrl->id };

- ret = uvc_query_v4l2_ctrl(chain, &qc);
- if (ret < 0) {
- ctrls->error_idx = i;
- return ret;
- }
+ ret = uvc_query_v4l2_ctrl(chain, &qc);
+ if (ret < 0)
+ break;

+ if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
ctrl->value = qc.default_value;
+ continue;
}

- return 0;
+ if (qc.flags & V4L2_CTRL_FLAG_WRITE_ONLY && read) {
+ ret = -EACCES;
+ break;
+ }
+
+ if (qc.flags & V4L2_CTRL_FLAG_READ_ONLY && !read) {
+ ret = -EACCES;
+ break;
+ }
}

+ ctrls->error_idx = ctrls->count;
+
+ return ret;
+}
+
+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;
+
+ ret = uvc_ctrl_check_access(chain, ctrls, true);
+ if (ret < 0 || ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return ret;
+
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
@@ -1092,10 +1115,6 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
unsigned int i;
int ret;

- /* Default value cannot be changed */
- if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
- return -EINVAL;
-
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
@@ -1121,6 +1140,16 @@ static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
struct v4l2_ext_controls *ctrls)
{
struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ int ret;
+
+ /* Default value cannot be changed */
+ if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return -EINVAL;
+
+ ret = uvc_ctrl_check_access(chain, ctrls, false);
+ if (ret < 0)
+ return ret;

return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
}
@@ -1130,6 +1159,10 @@ static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
{
struct uvc_fh *handle = fh;

+ /* Default value cannot be changed */
+ if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return -EINVAL;
+
return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
}

--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:18:43

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 09/13] media: uvcvideo: Increase the size of UVC_METADATA_BUF_SIZE

Hans has discovered that in his test device, for the H264 format
bytesused goes up to about 570, for YUYV it will actually go up
to a bit over 5000 bytes, and for MJPG up to about 2706 bytes.

We should also, according to V4L2_META_FMT_UVC docs, drop headers when
the buffer is full.

Credit-to: Hans Verkuil <[email protected]>
Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_video.c | 8 +++++---
drivers/media/usb/uvc/uvcvideo.h | 2 +-
2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 25fd8aa23529..ea2903dc3252 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1244,11 +1244,13 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
if (!meta_buf || length == 2)
return;

+ /*
+ * According to V4L2_META_FMT_UVC docs, we should drop headers when
+ * the buffer is full.
+ */
if (meta_buf->length - meta_buf->bytesused <
- length + sizeof(meta->ns) + sizeof(meta->sof)) {
- meta_buf->error = 1;
+ length + sizeof(meta->ns) + sizeof(meta->sof))
return;
- }

has_pts = mem[1] & UVC_STREAM_PTS;
has_scr = mem[1] & UVC_STREAM_SCR;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index b81d3f65e52e..a26bbec8d37b 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -527,7 +527,7 @@ struct uvc_stats_stream {
unsigned int max_sof; /* Maximum STC.SOF value */
};

-#define UVC_METADATA_BUF_SIZE 1024
+#define UVC_METADATA_BUF_SIZE 10240

/**
* struct uvc_copy_op: Context structure to schedule asynchronous memcpy
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:18:54

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 13/13] uvc: use vb2 ioctl and fop helpers

From: Hans Verkuil <[email protected]>

When uvc was written the vb2 ioctl and file operation helpers didn't exist.

This patch switches uvc over to those helpers, which removes a lot of boilerplate
code and simplifies VIDIOC_G/S_PRIORITY handling and allows us to drop the
'privileges' scheme, since that's now handled inside the vb2 helpers.

This makes it possible for uvc to pass the v4l2-compliance streaming tests.

Signed-off-by: Hans Verkuil <[email protected]>
---
drivers/media/usb/uvc/uvc_driver.c | 7 +-
drivers/media/usb/uvc/uvc_metadata.c | 8 +-
drivers/media/usb/uvc/uvc_queue.c | 143 -------------
drivers/media/usb/uvc/uvc_v4l2.c | 288 ++-------------------------
drivers/media/usb/uvc/uvcvideo.h | 32 ---
5 files changed, 25 insertions(+), 453 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 76ab6acecbc9..1a38ea31e218 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1911,7 +1911,6 @@ static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
INIT_LIST_HEAD(&chain->entities);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
- v4l2_prio_init(&chain->prio);

return chain;
}
@@ -2181,7 +2180,7 @@ int uvc_register_video_device(struct uvc_device *dev,
vdev->fops = fops;
vdev->ioctl_ops = ioctl_ops;
vdev->release = uvc_release;
- vdev->prio = &stream->chain->prio;
+ vdev->queue = &queue->queue;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
else
@@ -2546,8 +2545,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
if (stream->intf == intf) {
ret = uvc_video_resume(stream, reset);
if (ret < 0)
- uvc_queue_streamoff(&stream->queue,
- stream->queue.queue.type);
+ vb2_streamoff(&stream->queue.queue,
+ stream->queue.queue.type);
return ret;
}
}
diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c
index b6279ad7ac84..849d8e5ab75d 100644
--- a/drivers/media/usb/uvc/uvc_metadata.c
+++ b/drivers/media/usb/uvc/uvc_metadata.c
@@ -96,7 +96,7 @@ static int uvc_meta_v4l2_set_format(struct file *file, void *fh,
*/
mutex_lock(&stream->mutex);

- if (uvc_queue_allocated(&stream->queue))
+ if (vb2_is_busy(&stream->meta.queue.queue))
ret = -EBUSY;
else
stream->meta.format = fmt->dataformat;
@@ -164,12 +164,6 @@ int uvc_meta_register(struct uvc_streaming *stream)

stream->meta.format = V4L2_META_FMT_UVC;

- /*
- * The video interface queue uses manual locking and thus does not set
- * the queue pointer. Set it manually here.
- */
- vdev->queue = &queue->queue;
-
return uvc_register_video_device(dev, stream, vdev, queue,
V4L2_BUF_TYPE_META_CAPTURE,
&uvc_meta_fops, &uvc_meta_ioctl_ops);
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 21a907d32bb7..437e48b32480 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -247,153 +247,10 @@ 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_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_reqbufs(&queue->queue, rb);
- mutex_unlock(&queue->mutex);
-
- return ret ? ret : rb->count;
-}
-
-int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_querybuf(&queue->queue, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *cb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_create_bufs(&queue->queue, cb);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_qbuf(&queue->queue, mdev, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_expbuf(&queue->queue, exp);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
- int nonblocking)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
- mutex_unlock(&queue->mutex);
-
- 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)
-{
- return vb2_mmap(&queue->queue, vma);
-}
-
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
-{
- return vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
-}
-#endif
-
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait)
-{
- __poll_t ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_poll(&queue->queue, file, wait);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
/* -----------------------------------------------------------------------------
*
*/

-/*
- * Check if buffers have been allocated.
- */
-int uvc_queue_allocated(struct uvc_video_queue *queue)
-{
- int allocated;
-
- mutex_lock(&queue->mutex);
- allocated = vb2_is_busy(&queue->queue);
- mutex_unlock(&queue->mutex);
-
- return allocated;
-}
-
/*
* Cancel the video buffers queue.
*
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 74d862c358fd..a5454bcc3feb 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -352,17 +352,13 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream,
return ret;

mutex_lock(&stream->mutex);
-
- if (uvc_queue_allocated(&stream->queue)) {
+ if (vb2_is_busy(&stream->queue.queue)) {
ret = -EBUSY;
- goto done;
+ } else {
+ stream->ctrl = probe;
+ stream->cur_format = format;
+ stream->cur_frame = frame;
}
-
- stream->ctrl = probe;
- stream->cur_format = format;
- stream->cur_frame = frame;
-
-done:
mutex_unlock(&stream->mutex);
return ret;
}
@@ -489,62 +485,6 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
return 0;
}

-/* ------------------------------------------------------------------------
- * Privilege management
- */
-
-/*
- * Privilege management is the multiple-open implementation basis. The current
- * implementation is completely transparent for the end-user and doesn't
- * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
- * Those ioctls enable finer control on the device (by making possible for a
- * user to request exclusive access to a device), but are not mature yet.
- * Switching to the V4L2 priority mechanism might be considered in the future
- * if this situation changes.
- *
- * Each open instance of a UVC device can either be in a privileged or
- * unprivileged state. Only a single instance can be in a privileged state at
- * a given time. Trying to perform an operation that requires privileges will
- * automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance or when
- * freeing the video buffers using VIDIOC_REQBUFS.
- *
- * Operations that require privileges are:
- *
- * - VIDIOC_S_INPUT
- * - VIDIOC_S_PARM
- * - VIDIOC_S_FMT
- * - VIDIOC_REQBUFS
- */
-static int uvc_acquire_privileges(struct uvc_fh *handle)
-{
- /* Always succeed if the handle is already privileged. */
- if (handle->state == UVC_HANDLE_ACTIVE)
- return 0;
-
- /* Check if the device already has a privileged handle. */
- if (atomic_inc_return(&handle->stream->active) != 1) {
- atomic_dec(&handle->stream->active);
- return -EBUSY;
- }
-
- handle->state = UVC_HANDLE_ACTIVE;
- return 0;
-}
-
-static void uvc_dismiss_privileges(struct uvc_fh *handle)
-{
- if (handle->state == UVC_HANDLE_ACTIVE)
- atomic_dec(&handle->stream->active);
-
- handle->state = UVC_HANDLE_PASSIVE;
-}
-
-static int uvc_has_privileges(struct uvc_fh *handle)
-{
- return handle->state == UVC_HANDLE_ACTIVE;
-}
-
/* ------------------------------------------------------------------------
* V4L2 file operations
*/
@@ -587,7 +527,6 @@ static int uvc_v4l2_open(struct file *file)
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
handle->stream = stream;
- handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;

return 0;
@@ -600,15 +539,8 @@ static int uvc_v4l2_release(struct file *file)

uvc_dbg(stream->dev, CALLS, "%s\n", __func__);

- /* Only free resources if this is a privileged handle. */
- if (uvc_has_privileges(handle))
- uvc_queue_release(&stream->queue);
-
/* Release the file handle. */
- uvc_dismiss_privileges(handle);
- v4l2_fh_del(&handle->vfh);
- v4l2_fh_exit(&handle->vfh);
- kfree(handle);
+ vb2_fop_release(file);
file->private_data = NULL;

mutex_lock(&stream->dev->lock);
@@ -701,11 +633,6 @@ static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
{
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_format(stream, fmt);
}
@@ -715,11 +642,6 @@ static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
{
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_format(stream, fmt);
}
@@ -744,124 +666,6 @@ static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}

-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;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- mutex_lock(&stream->mutex);
- ret = uvc_request_buffers(&stream->queue, rb);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
-
- if (ret == 0)
- uvc_dismiss_privileges(handle);
-
- return 0;
-}
-
-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;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_query_buffer(&stream->queue, buf);
-}
-
-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 (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_queue_buffer(&stream->queue,
- stream->vdev.v4l2_dev->mdev, buf);
-}
-
-static int uvc_ioctl_expbuf(struct file *file, void *fh,
- struct v4l2_exportbuffer *exp)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_export_buffer(&stream->queue, exp);
-}
-
-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;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_dequeue_buffer(&stream->queue, buf,
- file->f_flags & O_NONBLOCK);
-}
-
-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;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_create_buffers(&stream->queue, cb);
-}
-
-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 (!uvc_has_privileges(handle))
- return -EBUSY;
-
- mutex_lock(&stream->mutex);
- ret = uvc_queue_streamon(&stream->queue, type);
- mutex_unlock(&stream->mutex);
-
- return ret;
-}
-
-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;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- mutex_lock(&stream->mutex);
- uvc_queue_streamoff(&stream->queue, type);
- mutex_unlock(&stream->mutex);
-
- return 0;
-}
-
static int uvc_ioctl_enum_input(struct file *file, void *fh,
struct v4l2_input *input)
{
@@ -929,13 +733,12 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
{
struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
struct uvc_video_chain *chain = handle->chain;
- int ret;
u32 i;

- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
+ if (vb2_is_busy(&stream->queue.queue))
+ return -EBUSY;

if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
@@ -1228,11 +1031,6 @@ static int uvc_ioctl_s_parm(struct file *file, void *fh,
{
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);
}
@@ -1500,50 +1298,6 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
}
#endif

-static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_mmap(&stream->queue, vma);
-}
-
-static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_poll(&stream->queue, file, wait);
-}
-
-#ifndef CONFIG_MMU
-static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
-}
-#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,
@@ -1554,14 +1308,15 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.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_expbuf = uvc_ioctl_expbuf,
- .vidioc_dqbuf = uvc_ioctl_dqbuf,
- .vidioc_create_bufs = uvc_ioctl_create_bufs,
- .vidioc_streamon = uvc_ioctl_streamon,
- .vidioc_streamoff = uvc_ioctl_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_input = uvc_ioctl_enum_input,
.vidioc_g_input = uvc_ioctl_g_input,
.vidioc_s_input = uvc_ioctl_s_input,
@@ -1591,11 +1346,10 @@ const struct v4l2_file_operations uvc_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
- .read = uvc_v4l2_read,
- .mmap = uvc_v4l2_mmap,
- .poll = uvc_v4l2_poll,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
#ifndef CONFIG_MMU
- .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+ .get_unmapped_area = vb2_fop_get_unmapped_area,
#endif
};

diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index dc20021f7ee0..bd28813c5649 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -477,7 +477,6 @@ struct uvc_video_chain {

struct mutex ctrl_mutex; /* Protects ctrl.info */

- struct v4l2_prio_state prio; /* V4L2 priority state */
u32 caps; /* V4L2 chain-wide caps */
u8 ctrl_class_bitmap; /* Bitmap of valid classes */
};
@@ -714,16 +713,10 @@ struct uvc_device {
struct uvc_entity *gpio_unit;
};

-enum uvc_handle_state {
- UVC_HANDLE_PASSIVE = 0,
- UVC_HANDLE_ACTIVE = 1,
-};
-
struct uvc_fh {
struct v4l2_fh vfh;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
- enum uvc_handle_state state;
};

struct uvc_driver {
@@ -788,36 +781,11 @@ struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
int drop_corrupted);
-void uvc_queue_release(struct uvc_video_queue *queue);
-int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *v4l2_cb);
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev,
- struct v4l2_buffer *v4l2_buf);
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp);
-int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking);
-int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type);
-int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type);
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
void uvc_queue_buffer_release(struct uvc_buffer *buf);
-int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma);
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait);
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff);
-#endif
-int uvc_queue_allocated(struct uvc_video_queue *queue);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:18:56

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 10/13] media: uvcvideo: Return -EACCES to inactive controls

If a control is inactive return -EACCES to let the userspace know that
the value will not be applied automatically when the control is active
again.

Signed-off-by: Ricardo Ribalda <[email protected]>
Suggested-by: Hans Verkuil <[email protected]>
---
drivers/media/usb/uvc/uvc_ctrl.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index ba14733db757..98614e1be829 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1578,6 +1578,18 @@ int uvc_ctrl_begin(struct uvc_video_chain *chain)
return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
}

+static bool uvc_ctrl_is_inactive(struct uvc_control *ctrl)
+{
+ struct uvc_control_mapping *map;
+
+ list_for_each_entry(map, &ctrl->info.mappings, list) {
+ if (map->master_id)
+ return true;
+ }
+
+ return false;
+}
+
static int uvc_ctrl_commit_entity(struct uvc_device *dev,
struct uvc_entity *entity, int rollback)
{
@@ -1621,8 +1633,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,

ctrl->dirty = 0;

- if (ret < 0)
+ if (ret < 0) {
+ if (uvc_ctrl_is_inactive(ctrl))
+ return -EACCES;
return ret;
+ }
}

return 0;
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:18:58

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 12/13] media: v4l2-ioctl: Set error_idx to the right value

If an error is found when validating the list of controls passed with
VIDIOC_G_EXT_CTRLS, then error_idx shall be set to ctrls->count to
indicate to userspace that no actual hardware was touched.

It would have been much nicer of course if error_idx could point to the
control index that failed the validation, but sadly that's not how the
API was designed.

Fixes v4l2-compliance:
Control ioctls (Input 0):
warn: v4l2-test-controls.cpp(834): error_idx should be equal to count
warn: v4l2-test-controls.cpp(855): error_idx should be equal to count
test VIDIOC_G/S/TRY_EXT_CTRLS: OK

Fixes: 6fa6f831f095 ("media: v4l2-ctrls: add core request support")
Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/v4l2-core/v4l2-ioctl.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 9406e90ff805..936ae21a0e0e 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -2294,8 +2294,12 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
vfd, vfd->v4l2_dev->mdev, p);
if (ops->vidioc_g_ext_ctrls == NULL)
return -ENOTTY;
- return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
- -EINVAL;
+
+ if (check_ext_ctrls(p, 0))
+ return ops->vidioc_g_ext_ctrls(file, fh, p);
+
+ p->error_idx = p->count;
+ return -EINVAL;
}

static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
@@ -2315,8 +2319,11 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
vfd, vfd->v4l2_dev->mdev, p);
if (ops->vidioc_s_ext_ctrls == NULL)
return -ENOTTY;
- return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
- -EINVAL;
+ if (check_ext_ctrls(p, 0))
+ return ops->vidioc_s_ext_ctrls(file, fh, p);
+
+ p->error_idx = p->count;
+ return -EINVAL;
}

static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:18:58

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 11/13] media: uvcvideo: Use control names from framework

The framework already contains a map of IDs to names, lets use it when
possible.

Signed-off-by: Ricardo Ribalda <[email protected]>
Suggested-by: Hans Verkuil <[email protected]>
---
drivers/media/usb/uvc/uvc_ctrl.c | 57 ++++++++++++--------------------
drivers/media/usb/uvc/uvc_v4l2.c | 8 ++++-
drivers/media/usb/uvc/uvcvideo.h | 2 +-
3 files changed, 30 insertions(+), 37 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 98614e1be829..efbdd49ad8ec 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -436,7 +436,6 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_BRIGHTNESS_CONTROL,
.size = 16,
@@ -446,7 +445,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_CONTRAST,
- .name = "Contrast",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_CONTRAST_CONTROL,
.size = 16,
@@ -456,7 +454,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_HUE,
- .name = "Hue",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_HUE_CONTROL,
.size = 16,
@@ -468,7 +465,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_SATURATION,
- .name = "Saturation",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_SATURATION_CONTROL,
.size = 16,
@@ -478,7 +474,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_SHARPNESS,
- .name = "Sharpness",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_SHARPNESS_CONTROL,
.size = 16,
@@ -488,7 +483,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_GAMMA,
- .name = "Gamma",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_GAMMA_CONTROL,
.size = 16,
@@ -498,7 +492,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_BACKLIGHT_COMPENSATION,
- .name = "Backlight Compensation",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
.size = 16,
@@ -508,7 +501,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_GAIN,
- .name = "Gain",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_GAIN_CONTROL,
.size = 16,
@@ -518,7 +510,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
- .name = "Power Line Frequency",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
.size = 2,
@@ -530,7 +521,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_HUE_AUTO,
- .name = "Hue, Auto",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_HUE_AUTO_CONTROL,
.size = 1,
@@ -541,7 +531,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_EXPOSURE_AUTO,
- .name = "Exposure, Auto",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_AE_MODE_CONTROL,
.size = 4,
@@ -554,7 +543,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
- .name = "Exposure, Auto Priority",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_AE_PRIORITY_CONTROL,
.size = 1,
@@ -564,7 +552,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
- .name = "Exposure (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
.size = 32,
@@ -576,7 +563,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
- .name = "White Balance Temperature, Auto",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
.size = 1,
@@ -587,7 +573,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
- .name = "White Balance Temperature",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
.size = 16,
@@ -599,7 +584,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
- .name = "White Balance Component, Auto",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
.size = 1,
@@ -611,7 +595,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_BLUE_BALANCE,
- .name = "White Balance Blue Component",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
.size = 16,
@@ -623,7 +606,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_RED_BALANCE,
- .name = "White Balance Red Component",
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
.size = 16,
@@ -635,7 +617,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_FOCUS_ABSOLUTE,
- .name = "Focus (absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
.size = 16,
@@ -647,7 +628,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_FOCUS_AUTO,
- .name = "Focus, Auto",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_FOCUS_AUTO_CONTROL,
.size = 1,
@@ -658,7 +638,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_IRIS_ABSOLUTE,
- .name = "Iris, Absolute",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
.size = 16,
@@ -668,7 +647,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_IRIS_RELATIVE,
- .name = "Iris, Relative",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_IRIS_RELATIVE_CONTROL,
.size = 8,
@@ -678,7 +656,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_ZOOM_ABSOLUTE,
- .name = "Zoom, Absolute",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
.size = 16,
@@ -688,7 +665,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_ZOOM_CONTINUOUS,
- .name = "Zoom, Continuous",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
.size = 0,
@@ -700,7 +676,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_PAN_ABSOLUTE,
- .name = "Pan (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
@@ -710,7 +685,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_TILT_ABSOLUTE,
- .name = "Tilt (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
@@ -720,7 +694,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_PAN_SPEED,
- .name = "Pan (Speed)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
.size = 16,
@@ -732,7 +705,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_TILT_SPEED,
- .name = "Tilt (Speed)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
.size = 16,
@@ -744,7 +716,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_PRIVACY,
- .name = "Privacy",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PRIVACY_CONTROL,
.size = 1,
@@ -754,7 +725,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
},
{
.id = V4L2_CID_PRIVACY,
- .name = "Privacy",
.entity = UVC_GUID_EXT_GPIO_CONTROLLER,
.selector = UVC_CT_PRIVACY_CONTROL,
.size = 1,
@@ -1076,6 +1046,20 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
return 0;
}

+static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
+{
+ const char *name;
+
+ if (map->name)
+ return map->name;
+
+ name = v4l2_ctrl_get_name(map->id);
+ if (name)
+ return name;
+
+ return "Unknown Control";
+}
+
static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
@@ -1089,7 +1073,8 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
v4l2_ctrl->id = mapping->id;
v4l2_ctrl->type = mapping->v4l2_type;
- strscpy(v4l2_ctrl->name, mapping->name, sizeof(v4l2_ctrl->name));
+ strscpy(v4l2_ctrl->name, uvc_map_get_name(mapping),
+ sizeof(v4l2_ctrl->name));
v4l2_ctrl->flags = 0;

if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
@@ -2196,7 +2181,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,

list_add_tail(&map->list, &ctrl->info.mappings);
uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
- map->name, ctrl->info.entity, ctrl->info.selector);
+ uvc_map_get_name(map), ctrl->info.entity,
+ ctrl->info.selector);

return 0;
}
@@ -2214,7 +2200,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_dbg(dev, CONTROL,
"Can't add mapping '%s', control id 0x%08x is invalid\n",
- mapping->name, mapping->id);
+ uvc_map_get_name(mapping), mapping->id);
return -EINVAL;
}

@@ -2261,7 +2247,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
if (mapping->id == map->id) {
uvc_dbg(dev, CONTROL,
"Can't add mapping '%s', control id 0x%08x already exists\n",
- mapping->name, mapping->id);
+ uvc_map_get_name(mapping), mapping->id);
ret = -EEXIST;
goto done;
}
@@ -2272,7 +2258,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
atomic_dec(&dev->nmappings);
uvc_dbg(dev, CONTROL,
"Can't add mapping '%s', maximum mappings count (%u) exceeded\n",
- mapping->name, UVC_MAX_CONTROL_MAPPINGS);
+ uvc_map_get_name(mapping), UVC_MAX_CONTROL_MAPPINGS);
ret = -ENOMEM;
goto done;
}
@@ -2481,6 +2467,7 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
list_del(&mapping->list);
kfree(mapping->menu_info);
+ kfree(mapping->name);
kfree(mapping);
}
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index d780065f3716..74d862c358fd 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -40,7 +40,13 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
return -ENOMEM;

map->id = xmap->id;
- memcpy(map->name, xmap->name, sizeof(map->name));
+ /* Non standard control id. */
+ if (v4l2_ctrl_get_name(map->id) == NULL) {
+ map->name = kmemdup(xmap->name, sizeof(xmap->name),
+ GFP_KERNEL);
+ if (!map->name)
+ return -ENOMEM;
+ }
memcpy(map->entity, xmap->entity, sizeof(map->entity));
map->selector = xmap->selector;
map->size = xmap->size;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index a26bbec8d37b..dc20021f7ee0 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -240,7 +240,7 @@ struct uvc_control_mapping {
struct list_head ev_subs;

u32 id;
- u8 name[32];
+ char *name;
u8 entity[16];
u8 selector;

--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:20:11

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 02/13] media: uvcvideo: Set capability in s_param

Fixes v4l2-compliance:

Format ioctls (Input 0):
warn: v4l2-test-formats.cpp(1339): S_PARM is supported but doesn't report V4L2_CAP_TIMEPERFRAME
fail: v4l2-test-formats.cpp(1241): node->has_frmintervals && !cap->capability

Reviewed-by: Laurent Pinchart <[email protected]>
Reviewed-by: Hans Verkuil <[email protected]>
Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_v4l2.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 252136cc885c..157310c0ca87 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -472,10 +472,13 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
uvc_simplify_fraction(&timeperframe.numerator,
&timeperframe.denominator, 8, 333);

- if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
parm->parm.capture.timeperframe = timeperframe;
- else
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ } else {
parm->parm.output.timeperframe = timeperframe;
+ parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ }

return 0;
}
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:20:35

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 03/13] media: uvcvideo: Return -EIO for control errors

The device is doing something unspected with the control. Either because
the protocol is not properly implemented or there has been a HW error.

Fixes v4l2-compliance:

Control ioctls (Input 0):
fail: v4l2-test-controls.cpp(448): s_ctrl returned an error (22)
test VIDIOC_G/S_CTRL: FAIL
fail: v4l2-test-controls.cpp(698): s_ext_ctrls returned an error (22)
test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL

Reviewed-by: Laurent Pinchart <[email protected]>
Reviewed-by: Hans Verkuil <[email protected]>
Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_video.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index f2f565281e63..25fd8aa23529 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -112,6 +112,11 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
case 5: /* Invalid unit */
case 6: /* Invalid control */
case 7: /* Invalid Request */
+ /*
+ * The firmware has not properly implemented
+ * the control or there has been a HW error.
+ */
+ return -EIO;
case 8: /* Invalid value within range */
return -EINVAL;
default: /* reserved or unknown */
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:20:41

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 08/13] media: uvcvideo: Set unique vdev name based in type

All the entities must have a unique name. We can have a descriptive and
unique name by appending the function and the entity->id.

This is even resilent to multi chain devices.

Fixes v4l2-compliance:
Media Controller ioctls:
fail: v4l2-test-media.cpp(205): v2_entity_names_set.find(key) != v2_entity_names_set.end()
test MEDIA_IOC_G_TOPOLOGY: FAIL
fail: v4l2-test-media.cpp(394): num_data_links != num_links
test MEDIA_IOC_ENUM_ENTITIES/LINKS: FAIL

Signed-off-by: Ricardo Ribalda <[email protected]>
Reviewed-by: Hans Verkuil <[email protected]>
---
drivers/media/usb/uvc/uvc_driver.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 35873cf2773d..76ab6acecbc9 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2163,6 +2163,7 @@ int uvc_register_video_device(struct uvc_device *dev,
const struct v4l2_ioctl_ops *ioctl_ops)
{
int ret;
+ const char *name;

/* Initialize the video buffers queue. */
ret = uvc_queue_init(queue, type, !uvc_no_drop_param);
@@ -2190,16 +2191,20 @@ int uvc_register_video_device(struct uvc_device *dev,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
default:
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ name = "Video Capture";
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ name = "Video Output";
break;
case V4L2_BUF_TYPE_META_CAPTURE:
vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ name = "Metadata";
break;
}

- strscpy(vdev->name, dev->name, sizeof(vdev->name));
+ snprintf(vdev->name, sizeof(vdev->name), "%s %u", name,
+ stream->header.bTerminalLink);

/*
* Set the driver data before calling video_register_device, otherwise
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 21:20:57

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 06/13] media: uvcvideo: Add support for V4L2_CTRL_TYPE_CTRL_CLASS

Create all the class controls for the device defined controls.

Fixes v4l2-compliance:
Control ioctls (Input 0):
fail: v4l2-test-controls.cpp(216): missing control class for class 00980000
fail: v4l2-test-controls.cpp(216): missing control tclass for class 009a0000
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: FAIL

Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_ctrl.c | 94 ++++++++++++++++++++++++++++++++
drivers/media/usb/uvc/uvcvideo.h | 5 ++
2 files changed, 99 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b75da65115ef..ba14733db757 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -357,6 +357,15 @@ static const struct uvc_control_info uvc_ctrls[] = {
},
};

+static const struct uvc_control_class uvc_control_class[] = {
+ {
+ .id = V4L2_CID_CAMERA_CLASS,
+ },
+ {
+ .id = V4L2_CID_USER_CLASS,
+ },
+};
+
static const struct uvc_menu_info power_line_frequency_controls[] = {
{ 0, "Disabled" },
{ 1, "50 Hz" },
@@ -1024,6 +1033,49 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
return 0;
}

+static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
+ u32 found_id)
+{
+ bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+ unsigned int i;
+
+ req_id &= V4L2_CTRL_ID_MASK;
+
+ for (i = 0; i < ARRAY_SIZE(uvc_control_class); i++) {
+ if (!(chain->ctrl_class_bitmap & BIT(i)))
+ continue;
+ if (!find_next) {
+ if (uvc_control_class[i].id == req_id)
+ return i;
+ continue;
+ }
+ if (uvc_control_class[i].id > req_id &&
+ uvc_control_class[i].id < found_id)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
+ u32 found_id, struct v4l2_queryctrl *v4l2_ctrl)
+{
+ int idx;
+
+ idx = __uvc_query_v4l2_class(chain, req_id, found_id);
+ if (idx < 0)
+ return -ENODEV;
+
+ memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
+ v4l2_ctrl->id = uvc_control_class[idx].id;
+ strscpy(v4l2_ctrl->name, v4l2_ctrl_get_name(v4l2_ctrl->id),
+ sizeof(v4l2_ctrl->name));
+ v4l2_ctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ v4l2_ctrl->flags = V4L2_CTRL_FLAG_WRITE_ONLY
+ | V4L2_CTRL_FLAG_READ_ONLY;
+ return 0;
+}
+
static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
@@ -1127,12 +1179,31 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
if (ret < 0)
return -ERESTARTSYS;

+ /* Check if the ctrl is a know class */
+ if (!(v4l2_ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
+ ret = uvc_query_v4l2_class(chain, v4l2_ctrl->id, 0, v4l2_ctrl);
+ if (!ret)
+ goto done;
+ }
+
ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL) {
ret = -EINVAL;
goto done;
}

+ /*
+ * If we're enumerating control with V4L2_CTRL_FLAG_NEXT_CTRL, check if
+ * a class should be inserted between the previous control and the one
+ * we have just found.
+ */
+ if (v4l2_ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ ret = uvc_query_v4l2_class(chain, v4l2_ctrl->id, mapping->id,
+ v4l2_ctrl);
+ if (!ret)
+ goto done;
+ }
+
ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
done:
mutex_unlock(&chain->ctrl_mutex);
@@ -1426,6 +1497,11 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
if (ret < 0)
return -ERESTARTSYS;

+ if (__uvc_query_v4l2_class(handle->chain, sev->id, 0) >= 0) {
+ ret = 0;
+ goto done;
+ }
+
ctrl = uvc_find_control(handle->chain, sev->id, &mapping);
if (ctrl == NULL) {
ret = -EINVAL;
@@ -1459,7 +1535,10 @@ static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev)
struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);

mutex_lock(&handle->chain->ctrl_mutex);
+ if (__uvc_query_v4l2_class(handle->chain, sev->id, 0) >= 0)
+ goto done;
list_del(&sev->node);
+done:
mutex_unlock(&handle->chain->ctrl_mutex);
}

@@ -1577,6 +1656,9 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
struct uvc_control *ctrl;
struct uvc_control_mapping *mapping;

+ if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
+ return -EACCES;
+
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL)
return -EINVAL;
@@ -1596,6 +1678,9 @@ int uvc_ctrl_set(struct uvc_fh *handle,
s32 max;
int ret;

+ if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
+ return -EACCES;
+
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL)
return -EINVAL;
@@ -2062,6 +2147,7 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
{
struct uvc_control_mapping *map;
unsigned int size;
+ unsigned int i;

/* Most mappings come from static kernel data and need to be duplicated.
* Mappings that come from userspace will be unnecessarily duplicated,
@@ -2085,6 +2171,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
if (map->set == NULL)
map->set = uvc_set_le_value;

+ for (i = 0; i < ARRAY_SIZE(uvc_control_class); i++) {
+ if (V4L2_CTRL_ID2WHICH(uvc_control_class[i].id) ==
+ V4L2_CTRL_ID2WHICH(map->id)) {
+ chain->ctrl_class_bitmap |= BIT(i);
+ break;
+ }
+ }
+
list_add_tail(&map->list, &ctrl->info.mappings);
uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
map->name, ctrl->info.entity, ctrl->info.selector);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 97df5ecd66c9..b81d3f65e52e 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -262,6 +262,10 @@ struct uvc_control_mapping {
u8 *data);
};

+struct uvc_control_class {
+ u32 id;
+};
+
struct uvc_control {
struct uvc_entity *entity;
struct uvc_control_info info;
@@ -475,6 +479,7 @@ struct uvc_video_chain {

struct v4l2_prio_state prio; /* V4L2 priority state */
u32 caps; /* V4L2 chain-wide caps */
+ u8 ctrl_class_bitmap; /* Bitmap of valid classes */
};

struct uvc_stats_frame {
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-16 22:53:49

by Ricardo Ribalda

[permalink] [raw]
Subject: [PATCH v5 07/13] media: uvcvideo: Use dev->name for querycap()

Use the device name for the card name instead of cap->card.

Suggested-by: Laurent Pinchart <[email protected]>
Signed-off-by: Ricardo Ribalda <[email protected]>
---
drivers/media/usb/uvc/uvc_v4l2.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index e956d833ed84..d780065f3716 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -617,13 +617,12 @@ static int uvc_v4l2_release(struct file *file)
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;

strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
- strscpy(cap->card, vdev->name, sizeof(cap->card));
+ strscpy(cap->card, handle->stream->dev->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;
--
2.31.0.rc2.261.g7f71774620-goog

2021-03-17 10:54:27

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 04/13] media: uvcvideo: Check controls flags before accessing them

On 16/03/2021 18:59, Ricardo Ribalda wrote:
> We can figure out if reading/writing a set of controls can fail without
> accessing them by checking their flags.
>
> This way we can honor the API closer:
>
> If an error is found when validating the list of controls passed with
> VIDIOC_G_EXT_CTRLS, then error_idx shall be set to ctrls->count to
> indicate to userspace that no actual hardware was touched.
>
> Fixes v4l2-compliance:
> Control ioctls (Input 0):
> warn: v4l2-test-controls.cpp(765): g_ext_ctrls(0) invalid error_idx 0
> fail: v4l2-test-controls.cpp(645): invalid error index write only control
> test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> ---
> drivers/media/usb/uvc/uvc_v4l2.c | 69 +++++++++++++++++++++++---------
> 1 file changed, 51 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 157310c0ca87..e956d833ed84 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1040,31 +1040,54 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
> return 0;
> }
>
> -static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
> - struct v4l2_ext_controls *ctrls)
> +static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
> + struct v4l2_ext_controls *ctrls, bool read)
> {
> - struct uvc_fh *handle = fh;
> - struct uvc_video_chain *chain = handle->chain;
> struct v4l2_ext_control *ctrl = ctrls->controls;
> unsigned int i;
> - int ret;
> + int ret = 0;
>
> - if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> - for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> - struct v4l2_queryctrl qc = { .id = ctrl->id };
> + for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> + struct v4l2_queryctrl qc = { .id = ctrl->id };
>
> - ret = uvc_query_v4l2_ctrl(chain, &qc);
> - if (ret < 0) {
> - ctrls->error_idx = i;
> - return ret;
> - }
> + ret = uvc_query_v4l2_ctrl(chain, &qc);

You can't call this. If I am not mistaken, this call can actually call
the hardware.

Instead you need to call the lower level function uvc_find_control() and
use ctrl->info to check for read/write only.

> + if (ret < 0)
> + break;
>
> + if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> ctrl->value = qc.default_value;

This needs to use the old code in uvc_ioctl_g_ext_ctrls() since it depends
on uvc_query_v4l2_ctrl() which accesses the hardware.

> + continue;
> }
>
> - return 0;
> + if (qc.flags & V4L2_CTRL_FLAG_WRITE_ONLY && read) {
> + ret = -EACCES;
> + break;
> + }
> +
> + if (qc.flags & V4L2_CTRL_FLAG_READ_ONLY && !read) {
> + ret = -EACCES;
> + break;
> + }
> }
>
> + ctrls->error_idx = ctrls->count;
> +
> + return ret;
> +}

So uvc_ctrl_check_access() is a good idea, but it does a bit too much.
It should just check if all controls in the list are known and check for
write/read only.

Regards,

Hans

> +
> +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;
> +
> + ret = uvc_ctrl_check_access(chain, ctrls, true);
> + if (ret < 0 || ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
> + return ret;
> +
> ret = uvc_ctrl_begin(chain);
> if (ret < 0)
> return ret;
> @@ -1092,10 +1115,6 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
> unsigned int i;
> int ret;
>
> - /* Default value cannot be changed */
> - if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
> - return -EINVAL;
> -
> ret = uvc_ctrl_begin(chain);
> if (ret < 0)
> return ret;
> @@ -1121,6 +1140,16 @@ static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
> struct v4l2_ext_controls *ctrls)
> {
> struct uvc_fh *handle = fh;
> + struct uvc_video_chain *chain = handle->chain;
> + int ret;
> +
> + /* Default value cannot be changed */
> + if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
> + return -EINVAL;
> +
> + ret = uvc_ctrl_check_access(chain, ctrls, false);
> + if (ret < 0)
> + return ret;
>
> return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
> }
> @@ -1130,6 +1159,10 @@ static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
> {
> struct uvc_fh *handle = fh;
>
> + /* Default value cannot be changed */
> + if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
> + return -EINVAL;
> +
> return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
> }
>
>

2021-03-17 10:59:26

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 07/13] media: uvcvideo: Use dev->name for querycap()

On 16/03/2021 18:59, Ricardo Ribalda wrote:
> Use the device name for the card name instead of cap->card.

You mean: 'instead of vdev->name.' ?

>
> Suggested-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> ---
> drivers/media/usb/uvc/uvc_v4l2.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index e956d833ed84..d780065f3716 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -617,13 +617,12 @@ static int uvc_v4l2_release(struct file *file)
> 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;
>
> strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
> - strscpy(cap->card, vdev->name, sizeof(cap->card));
> + strscpy(cap->card, handle->stream->dev->name, sizeof(cap->card));

I don't think this is right. I get this for the video node:

Card type : Integrated IR Camera: Integrate

and this for the corresponding metadata node:

Card type : Metadata 7

But they are the same device, so I expect the same text here.

Regards,

Hans

> usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
> cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
> | chain->caps;
>

2021-03-17 11:03:56

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 09/13] media: uvcvideo: Increase the size of UVC_METADATA_BUF_SIZE

On 16/03/2021 18:59, Ricardo Ribalda wrote:
> Hans has discovered that in his test device, for the H264 format
> bytesused goes up to about 570, for YUYV it will actually go up
> to a bit over 5000 bytes, and for MJPG up to about 2706 bytes.
>
> We should also, according to V4L2_META_FMT_UVC docs, drop headers when
> the buffer is full.
>
> Credit-to: Hans Verkuil <[email protected]>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> ---
> drivers/media/usb/uvc/uvc_video.c | 8 +++++---
> drivers/media/usb/uvc/uvcvideo.h | 2 +-
> 2 files changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index 25fd8aa23529..ea2903dc3252 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1244,11 +1244,13 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
> if (!meta_buf || length == 2)
> return;
>
> + /*
> + * According to V4L2_META_FMT_UVC docs, we should drop headers when
> + * the buffer is full.
> + */
> if (meta_buf->length - meta_buf->bytesused <
> - length + sizeof(meta->ns) + sizeof(meta->sof)) {
> - meta_buf->error = 1;
> + length + sizeof(meta->ns) + sizeof(meta->sof))
> return;
> - }
>
> has_pts = mem[1] & UVC_STREAM_PTS;
> has_scr = mem[1] & UVC_STREAM_SCR;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index b81d3f65e52e..a26bbec8d37b 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -527,7 +527,7 @@ struct uvc_stats_stream {
> unsigned int max_sof; /* Maximum STC.SOF value */
> };
>
> -#define UVC_METADATA_BUF_SIZE 1024
> +#define UVC_METADATA_BUF_SIZE 10240
>
> /**
> * struct uvc_copy_op: Context structure to schedule asynchronous memcpy
>

Reviewed-by: Hans Verkuil <[email protected]>

Thanks!

Hans

2021-03-17 11:28:57

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 10/13] media: uvcvideo: Return -EACCES to inactive controls

On 16/03/2021 19:00, Ricardo Ribalda wrote:
> If a control is inactive return -EACCES to let the userspace know that
> the value will not be applied automatically when the control is active
> again.
>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> Suggested-by: Hans Verkuil <[email protected]>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 17 ++++++++++++++++-
> 1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index ba14733db757..98614e1be829 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1578,6 +1578,18 @@ int uvc_ctrl_begin(struct uvc_video_chain *chain)
> return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
> }
>
> +static bool uvc_ctrl_is_inactive(struct uvc_control *ctrl)

This doesn't test if the control is inactive, it tests if it *might* be
inactive. To test if it is really inactive would require checking the value
of the master control.

> +{
> + struct uvc_control_mapping *map;
> +
> + list_for_each_entry(map, &ctrl->info.mappings, list) {
> + if (map->master_id)
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int uvc_ctrl_commit_entity(struct uvc_device *dev,
> struct uvc_entity *entity, int rollback)
> {
> @@ -1621,8 +1633,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
>
> ctrl->dirty = 0;
>
> - if (ret < 0)
> + if (ret < 0) {
> + if (uvc_ctrl_is_inactive(ctrl))
> + return -EACCES;

So here you assume that if setting the control failed, and if the control
might be inactive, then it probably was inactive.

I feel a bit uncomfortable by this assumption.

Regards,

Hans

> return ret;
> + }
> }
>
> return 0;
>

2021-03-17 11:30:55

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 11/13] media: uvcvideo: Use control names from framework

On 16/03/2021 19:00, Ricardo Ribalda wrote:
> The framework already contains a map of IDs to names, lets use it when
> possible.
>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> Suggested-by: Hans Verkuil <[email protected]>

Reviewed-by: Hans Verkuil <[email protected]>

Regards,

Hans

> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 57 ++++++++++++--------------------
> drivers/media/usb/uvc/uvc_v4l2.c | 8 ++++-
> drivers/media/usb/uvc/uvcvideo.h | 2 +-
> 3 files changed, 30 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 98614e1be829..efbdd49ad8ec 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -436,7 +436,6 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
> static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> {
> .id = V4L2_CID_BRIGHTNESS,
> - .name = "Brightness",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_BRIGHTNESS_CONTROL,
> .size = 16,
> @@ -446,7 +445,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_CONTRAST,
> - .name = "Contrast",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_CONTRAST_CONTROL,
> .size = 16,
> @@ -456,7 +454,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_HUE,
> - .name = "Hue",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_HUE_CONTROL,
> .size = 16,
> @@ -468,7 +465,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_SATURATION,
> - .name = "Saturation",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_SATURATION_CONTROL,
> .size = 16,
> @@ -478,7 +474,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_SHARPNESS,
> - .name = "Sharpness",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_SHARPNESS_CONTROL,
> .size = 16,
> @@ -488,7 +483,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_GAMMA,
> - .name = "Gamma",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_GAMMA_CONTROL,
> .size = 16,
> @@ -498,7 +492,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_BACKLIGHT_COMPENSATION,
> - .name = "Backlight Compensation",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
> .size = 16,
> @@ -508,7 +501,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_GAIN,
> - .name = "Gain",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_GAIN_CONTROL,
> .size = 16,
> @@ -518,7 +510,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_POWER_LINE_FREQUENCY,
> - .name = "Power Line Frequency",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> .size = 2,
> @@ -530,7 +521,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_HUE_AUTO,
> - .name = "Hue, Auto",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_HUE_AUTO_CONTROL,
> .size = 1,
> @@ -541,7 +531,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_EXPOSURE_AUTO,
> - .name = "Exposure, Auto",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_AE_MODE_CONTROL,
> .size = 4,
> @@ -554,7 +543,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
> - .name = "Exposure, Auto Priority",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_AE_PRIORITY_CONTROL,
> .size = 1,
> @@ -564,7 +552,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_EXPOSURE_ABSOLUTE,
> - .name = "Exposure (Absolute)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
> .size = 32,
> @@ -576,7 +563,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_AUTO_WHITE_BALANCE,
> - .name = "White Balance Temperature, Auto",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
> .size = 1,
> @@ -587,7 +573,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
> - .name = "White Balance Temperature",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
> .size = 16,
> @@ -599,7 +584,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_AUTO_WHITE_BALANCE,
> - .name = "White Balance Component, Auto",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
> .size = 1,
> @@ -611,7 +595,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_BLUE_BALANCE,
> - .name = "White Balance Blue Component",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> .size = 16,
> @@ -623,7 +606,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_RED_BALANCE,
> - .name = "White Balance Red Component",
> .entity = UVC_GUID_UVC_PROCESSING,
> .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> .size = 16,
> @@ -635,7 +617,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_FOCUS_ABSOLUTE,
> - .name = "Focus (absolute)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
> .size = 16,
> @@ -647,7 +628,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_FOCUS_AUTO,
> - .name = "Focus, Auto",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_FOCUS_AUTO_CONTROL,
> .size = 1,
> @@ -658,7 +638,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_IRIS_ABSOLUTE,
> - .name = "Iris, Absolute",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
> .size = 16,
> @@ -668,7 +647,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_IRIS_RELATIVE,
> - .name = "Iris, Relative",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_IRIS_RELATIVE_CONTROL,
> .size = 8,
> @@ -678,7 +656,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_ZOOM_ABSOLUTE,
> - .name = "Zoom, Absolute",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
> .size = 16,
> @@ -688,7 +665,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_ZOOM_CONTINUOUS,
> - .name = "Zoom, Continuous",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
> .size = 0,
> @@ -700,7 +676,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_PAN_ABSOLUTE,
> - .name = "Pan (Absolute)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> .size = 32,
> @@ -710,7 +685,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_TILT_ABSOLUTE,
> - .name = "Tilt (Absolute)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> .size = 32,
> @@ -720,7 +694,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_PAN_SPEED,
> - .name = "Pan (Speed)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
> .size = 16,
> @@ -732,7 +705,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_TILT_SPEED,
> - .name = "Tilt (Speed)",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
> .size = 16,
> @@ -744,7 +716,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_PRIVACY,
> - .name = "Privacy",
> .entity = UVC_GUID_UVC_CAMERA,
> .selector = UVC_CT_PRIVACY_CONTROL,
> .size = 1,
> @@ -754,7 +725,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> },
> {
> .id = V4L2_CID_PRIVACY,
> - .name = "Privacy",
> .entity = UVC_GUID_EXT_GPIO_CONTROLLER,
> .selector = UVC_CT_PRIVACY_CONTROL,
> .size = 1,
> @@ -1076,6 +1046,20 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> return 0;
> }
>
> +static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
> +{
> + const char *name;
> +
> + if (map->name)
> + return map->name;
> +
> + name = v4l2_ctrl_get_name(map->id);
> + if (name)
> + return name;
> +
> + return "Unknown Control";
> +}
> +
> static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> struct uvc_control *ctrl,
> struct uvc_control_mapping *mapping,
> @@ -1089,7 +1073,8 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
> v4l2_ctrl->id = mapping->id;
> v4l2_ctrl->type = mapping->v4l2_type;
> - strscpy(v4l2_ctrl->name, mapping->name, sizeof(v4l2_ctrl->name));
> + strscpy(v4l2_ctrl->name, uvc_map_get_name(mapping),
> + sizeof(v4l2_ctrl->name));
> v4l2_ctrl->flags = 0;
>
> if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> @@ -2196,7 +2181,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>
> list_add_tail(&map->list, &ctrl->info.mappings);
> uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
> - map->name, ctrl->info.entity, ctrl->info.selector);
> + uvc_map_get_name(map), ctrl->info.entity,
> + ctrl->info.selector);
>
> return 0;
> }
> @@ -2214,7 +2200,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> if (mapping->id & ~V4L2_CTRL_ID_MASK) {
> uvc_dbg(dev, CONTROL,
> "Can't add mapping '%s', control id 0x%08x is invalid\n",
> - mapping->name, mapping->id);
> + uvc_map_get_name(mapping), mapping->id);
> return -EINVAL;
> }
>
> @@ -2261,7 +2247,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> if (mapping->id == map->id) {
> uvc_dbg(dev, CONTROL,
> "Can't add mapping '%s', control id 0x%08x already exists\n",
> - mapping->name, mapping->id);
> + uvc_map_get_name(mapping), mapping->id);
> ret = -EEXIST;
> goto done;
> }
> @@ -2272,7 +2258,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> atomic_dec(&dev->nmappings);
> uvc_dbg(dev, CONTROL,
> "Can't add mapping '%s', maximum mappings count (%u) exceeded\n",
> - mapping->name, UVC_MAX_CONTROL_MAPPINGS);
> + uvc_map_get_name(mapping), UVC_MAX_CONTROL_MAPPINGS);
> ret = -ENOMEM;
> goto done;
> }
> @@ -2481,6 +2467,7 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
> list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
> list_del(&mapping->list);
> kfree(mapping->menu_info);
> + kfree(mapping->name);
> kfree(mapping);
> }
> }
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index d780065f3716..74d862c358fd 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -40,7 +40,13 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
> return -ENOMEM;
>
> map->id = xmap->id;
> - memcpy(map->name, xmap->name, sizeof(map->name));
> + /* Non standard control id. */
> + if (v4l2_ctrl_get_name(map->id) == NULL) {
> + map->name = kmemdup(xmap->name, sizeof(xmap->name),
> + GFP_KERNEL);
> + if (!map->name)
> + return -ENOMEM;
> + }
> memcpy(map->entity, xmap->entity, sizeof(map->entity));
> map->selector = xmap->selector;
> map->size = xmap->size;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index a26bbec8d37b..dc20021f7ee0 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -240,7 +240,7 @@ struct uvc_control_mapping {
> struct list_head ev_subs;
>
> u32 id;
> - u8 name[32];
> + char *name;
> u8 entity[16];
> u8 selector;
>
>

2021-03-17 11:41:28

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v5 12/13] media: v4l2-ioctl: Set error_idx to the right value

On 16/03/2021 19:00, Ricardo Ribalda wrote:
> If an error is found when validating the list of controls passed with
> VIDIOC_G_EXT_CTRLS, then error_idx shall be set to ctrls->count to
> indicate to userspace that no actual hardware was touched.
>
> It would have been much nicer of course if error_idx could point to the
> control index that failed the validation, but sadly that's not how the
> API was designed.
>
> Fixes v4l2-compliance:
> Control ioctls (Input 0):
> warn: v4l2-test-controls.cpp(834): error_idx should be equal to count
> warn: v4l2-test-controls.cpp(855): error_idx should be equal to count
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
>
> Fixes: 6fa6f831f095 ("media: v4l2-ctrls: add core request support")
> Signed-off-by: Ricardo Ribalda <[email protected]>
> ---
> drivers/media/v4l2-core/v4l2-ioctl.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 9406e90ff805..936ae21a0e0e 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -2294,8 +2294,12 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
> vfd, vfd->v4l2_dev->mdev, p);
> if (ops->vidioc_g_ext_ctrls == NULL)
> return -ENOTTY;
> - return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
> - -EINVAL;
> +
> + if (check_ext_ctrls(p, 0))
> + return ops->vidioc_g_ext_ctrls(file, fh, p);
> +
> + p->error_idx = p->count;
> + return -EINVAL;
> }
>
> static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
> @@ -2315,8 +2319,11 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
> vfd, vfd->v4l2_dev->mdev, p);
> if (ops->vidioc_s_ext_ctrls == NULL)
> return -ENOTTY;
> - return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
> - -EINVAL;
> + if (check_ext_ctrls(p, 0))
> + return ops->vidioc_s_ext_ctrls(file, fh, p);
> +
> + p->error_idx = p->count;
> + return -EINVAL;
> }
>
> static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
>

No, this patch is wrong. In all cases where check_ext_ctrls() is called the
code had already set error_idx to count. The real culprit is check_ext_ctrls()
that sets error_idx to the index of the failing control. But it can only do that
for the TRY ioctl.

In my review of patch 1 I mentioned that adding a bool is_get is a good idea.
I now this that it is best to pass the ioctl (G/S/TRY_EXT_CTRLS) instead and
based on that set error_idx here. In fact, if v4l_g/s_ctrl pass G/S_CTRL as the
ioctl argument to check_ext_ctrls(), then the allow_priv argument can be dropped
as well, since that can be decided based on the ioctl for which this function is
called.

Regards,

Hans