aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/media/usb/uvc/uvc_ctrl.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2023-01-03 15:36:19 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2023-01-15 23:45:11 +0200
commit9f582f0418ed1c18f92c9e4628075d6ec9a7d9fb (patch)
treeaaab655efd50ea6e8163381031022e752800def1 /drivers/media/usb/uvc/uvc_ctrl.c
parentmedia: uvcvideo: Factor out usb_string() calls (diff)
downloadwireguard-linux-9f582f0418ed1c18f92c9e4628075d6ec9a7d9fb.tar.xz
wireguard-linux-9f582f0418ed1c18f92c9e4628075d6ec9a7d9fb.zip
media: uvcvideo: Check for INACTIVE in uvc_ctrl_is_accessible()
Check for inactive controls in uvc_ctrl_is_accessible(). Use the new value for the master_id controls if present, otherwise use the existing value to determine if it is OK to set the control. Doing this here avoids attempting to set an inactive control, which will return an error from the USB device, which returns an invalid errorcode. This fixes: warn: v4l2-test-controls.cpp(483): s_ctrl returned EIO   warn: v4l2-test-controls.cpp(483): s_ctrl returned EIO test VIDIOC_G/S_CTRL: OK   warn: v4l2-test-controls.cpp(739): s_ext_ctrls returned EIO   warn: v4l2-test-controls.cpp(739): s_ext_ctrls returned EIO   warn: v4l2-test-controls.cpp(816): s_ext_ctrls returned EIO test VIDIOC_G/S/TRY_EXT_CTRLS: OK Tested with: v4l2-ctl -c auto_exposure=1 OK v4l2-ctl -c exposure_time_absolute=251 OK v4l2-ctl -c auto_exposure=3 OK v4l2-ctl -c exposure_time_absolute=251 VIDIOC_S_EXT_CTRLS: failed: Input/output error exposure_time_absolute: Input/output error ERROR v4l2-ctl -c auto_exposure=3,exposure_time_absolute=251,auto_exposure=1 v4l2-ctl -C auto_exposure,exposure_time_absolute   auto_exposure: 1 exposure_time_absolute: 251 Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'drivers/media/usb/uvc/uvc_ctrl.c')
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c42
1 files changed, 41 insertions, 1 deletions
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index c95a2229f4fa..6f5aaaf09ee0 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1085,11 +1085,28 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
return 0;
}
+/*
+ * Check if control @v4l2_id can be accessed by the given control @ioctl
+ * (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS).
+ *
+ * For set operations on slave controls, check if the master's value is set to
+ * manual, either in the others controls set in the same ioctl call, or from
+ * the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set
+ * both the master and slave control, such as for instance setting
+ * auto_exposure=1, exposure_time_absolute=251.
+ */
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
- bool read)
+ const struct v4l2_ext_controls *ctrls,
+ unsigned long ioctl)
{
+ struct uvc_control_mapping *master_map = NULL;
+ struct uvc_control *master_ctrl = NULL;
struct uvc_control_mapping *mapping;
struct uvc_control *ctrl;
+ bool read = ioctl == VIDIOC_G_EXT_CTRLS;
+ s32 val;
+ int ret;
+ int i;
if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
return -EACCES;
@@ -1104,6 +1121,29 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
return -EACCES;
+ if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
+ return 0;
+
+ /*
+ * Iterate backwards in cases where the master control is accessed
+ * multiple times in the same ioctl. We want the last value.
+ */
+ for (i = ctrls->count - 1; i >= 0; i--) {
+ if (ctrls->controls[i].id == mapping->master_id)
+ return ctrls->controls[i].value ==
+ mapping->master_manual ? 0 : -EACCES;
+ }
+
+ __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
+ &master_ctrl, 0);
+
+ if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+ return 0;
+
+ ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+ if (ret >= 0 && val != mapping->master_manual)
+ return -EACCES;
+
return 0;
}