Commit 9b31ea80 authored by Ricardo Ribalda's avatar Ricardo Ribalda Committed by Mauro Carvalho Chehab
Browse files

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

Reviewed-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarRicardo Ribalda <ribalda@chromium.org>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 866c6bdd
Loading
Loading
Loading
Loading
+90 −0
Original line number Diff line number Diff line
@@ -357,6 +357,11 @@ static const struct uvc_control_info uvc_ctrls[] = {
	},
};

static const u32 uvc_control_classes[] = {
	V4L2_CID_CAMERA_CLASS,
	V4L2_CID_USER_CLASS,
};

static const struct uvc_menu_info power_line_frequency_controls[] = {
	{ 0, "Disabled" },
	{ 1, "50 Hz" },
@@ -1024,6 +1029,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_classes); i++) {
		if (!(chain->ctrl_class_bitmap & BIT(i)))
			continue;
		if (!find_next) {
			if (uvc_control_classes[i] == req_id)
				return i;
			continue;
		}
		if (uvc_control_classes[i] > req_id &&
		    uvc_control_classes[i] < 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_classes[idx];
	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 +1175,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 +1493,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 +1531,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 +1652,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 +1674,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 +2143,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 +2167,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_classes); i++) {
		if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
						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);
+1 −0
Original line number Diff line number Diff line
@@ -476,6 +476,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 {