lkml.org 
[lkml]   [2021]   [Jan]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH] media: uvcvideo: initial UVC 1.5 region of interest support
Date
This patch implements parts of UVC 1.5 Region of Interest (ROI)
control, using the uvcvideo selection API.

There are several things to mention here.

First, UVC 1.5 defines CT_DIGITAL_WINDOW_CONTROL; and ROI rectangle
coordinates "must be within the current Digital Window as specified
by the CT_WINDOW control." (4.2.2.1.20 Digital Region of Interest
(ROI) Control.) This is not entirely clear if we need to implement
CT_DIGITAL_WINDOW_CONTROL. ROI is naturally limited by:
a) ROI GET_MIN and GET_MAX rectangles
b) current image crop
c) stream->cur_frame->wWidth/stream->cur_frame->wHeight

Second, ROI control is a compound data type:
Control Selector CT_REGION_OF_INTEREST_CONTROL
Mandatory Requests SET_CUR, GET_CUR, GET_MIN, GET_MAX, GET_DEF
wLength 10
Offset Field Size
0 wROI_Top 2
2 wROI_Left 2
4 wROI_Bottom 2
6 wROI_Right 2
8 bmAutoControls 2 (Bitmap)

While uvc_control_mapping can handle only s32 data at the moment:
->get() returns s32 value, ->set() accepts s32 value; while v4l2_ctrl
maximum/minimum/default_value can hold only s64 values.

Therefore ROI control handling is split into two parts:
a) bmAutoControls is handled via uvc_control_mapping as V4L2_CTRL_TYPE_MENU
b) ROI rectangle (SET_CUR, GET_CUR, GET_DEF) handling is implemented
by the means of selection API.

Third, the patch adds new selection targets in order to handle ROI
control requests, but it doesn't implement all of the requests.
Namely, SEL_TGT_BOUNDS for ROI implements GET_MAX (that is maximal
ROI rectangle area). GET_MIN is not implemented ( as of now) since
it's not very clear if user space would need such information.

Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
drivers/media/usb/uvc/uvc_ctrl.c | 32 +++++++
drivers/media/usb/uvc/uvc_v4l2.c | 143 ++++++++++++++++++++++++++++-
include/uapi/linux/usb/video.h | 1 +
include/uapi/linux/v4l2-common.h | 10 ++
include/uapi/linux/v4l2-controls.h | 1 +
5 files changed, 184 insertions(+), 3 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b3dde98499f4..4e55a0922f15 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -355,6 +355,15 @@ static const struct uvc_control_info uvc_ctrls[] = {
.flags = UVC_CTRL_FLAG_GET_CUR
| UVC_CTRL_FLAG_AUTO_UPDATE,
},
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_REGION_OF_INTEREST_CONTROL,
+ .index = 21,
+ .size = 10,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_DEF
+ },
};

static const struct uvc_menu_info power_line_frequency_controls[] = {
@@ -370,6 +379,17 @@ static const struct uvc_menu_info exposure_auto_controls[] = {
{ 8, "Aperture Priority Mode" },
};

+static struct uvc_menu_info roi_auto_controls[] = {
+ { 0, "Auto Exposure" },
+ { 1, "Auto Iris" },
+ { 2, "Auto White Balance" },
+ { 3, "Auto Focus" },
+ { 4, "Auto Face Detect" },
+ { 5, "Auto Detect and Track" },
+ { 6, "Image Stabilization" },
+ { 7, "Higher Quality" },
+};
+
static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
u8 query, const u8 *data)
{
@@ -753,6 +773,18 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
},
+ {
+ .id = V4L2_CID_REGION_OF_INTEREST_CONTROLS,
+ .name = "Region of Interest (controls)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_REGION_OF_INTEREST_CONTROL,
+ .size = 16,
+ .offset = 64,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_BITMASK,
+ .menu_info = roi_auto_controls,
+ .menu_count = ARRAY_SIZE(roi_auto_controls),
+ },
};

/* ------------------------------------------------------------------------
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 252136cc885c..71b4577196e5 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1139,14 +1139,60 @@ static int uvc_ioctl_querymenu(struct file *file, void *fh,
return uvc_query_v4l2_menu(chain, qm);
}

-static int uvc_ioctl_g_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
+/* UVC 1.5 ROI rectangle is half the size of v4l2_rect */
+struct uvc_roi_rect {
+ __u16 top;
+ __u16 left;
+ __u16 bottom;
+ __u16 right;
+};
+
+static int uvc_ioctl_g_roi_target(struct file *file, void *fh,
+ struct v4l2_selection *sel)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
+ struct uvc_roi_rect *roi;
+ u8 query;
+ int ret;

- if (sel->type != stream->type)
+ switch (sel->target) {
+ case V4L2_SEL_TGT_ROI_DEFAULT:
+ query = UVC_GET_DEF;
+ break;
+ case V4L2_SEL_TGT_ROI_CURRENT:
+ query = UVC_GET_CUR;
+ break;
+ case V4L2_SEL_TGT_ROI_BOUNDS:
+ query = UVC_GET_MAX;
+ break;
+ default:
return -EINVAL;
+ }
+
+ roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+ if (!roi)
+ return -ENOMEM;
+
+ ret = uvc_query_ctrl(stream->dev, query, 1, stream->dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+ sizeof(struct uvc_roi_rect));
+ if (!ret) {
+ sel->r.left = roi->left;
+ sel->r.top = roi->top;
+ sel->r.width = roi->right;
+ sel->r.height = roi->bottom;
+ }
+
+ kfree(roi);
+ return ret;
+}
+
+static int uvc_ioctl_g_sel_target(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;

switch (sel->target) {
case V4L2_SEL_TGT_CROP_DEFAULT:
@@ -1173,6 +1219,96 @@ static int uvc_ioctl_g_selection(struct file *file, void *fh,
return 0;
}

+static int uvc_ioctl_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (sel->type != stream->type)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ return uvc_ioctl_g_sel_target(file, fh, sel);
+ case V4L2_SEL_TGT_ROI_CURRENT:
+ case V4L2_SEL_TGT_ROI_DEFAULT:
+ case V4L2_SEL_TGT_ROI_BOUNDS:
+ return uvc_ioctl_g_roi_target(file, fh, sel);
+ }
+
+ return -EINVAL;
+}
+
+static bool validate_roi_bounds(struct uvc_streaming *stream,
+ struct v4l2_selection *sel)
+{
+ bool ok = true;
+
+ if (sel->r.left > USHRT_MAX || sel->r.top > USHRT_MAX ||
+ sel->r.width > USHRT_MAX || sel->r.height > USHRT_MAX)
+ return false;
+
+ /* perhaps also can test against ROI GET_MAX */
+
+ mutex_lock(&stream->mutex);
+ if ((u16)sel->r.width > stream->cur_frame->wWidth)
+ ok = false;
+ if ((u16)sel->r.height > stream->cur_frame->wHeight)
+ ok = false;
+ mutex_unlock(&stream->mutex);
+
+ return ok;
+}
+
+static int uvc_ioctl_s_roi(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_roi_rect *roi;
+ int ret;
+
+ if (!validate_roi_bounds(stream, sel))
+ return -E2BIG;
+
+ roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+ if (!roi)
+ return -ENOMEM;
+
+ roi->left = sel->r.left;
+ roi->top = sel->r.top;
+ roi->right = sel->r.width;
+ roi->bottom = sel->r.height;
+
+ ret = uvc_query_ctrl(stream->dev, UVC_SET_CUR, 1, stream->dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+ sizeof(struct uvc_roi_rect));
+
+ kfree(roi);
+ return ret;
+}
+
+static int uvc_ioctl_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (sel->type != stream->type)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_ROI:
+ return uvc_ioctl_s_roi(file, fh, sel);
+ }
+
+ return -EINVAL;
+}
+
static int uvc_ioctl_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
@@ -1533,6 +1669,7 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
.vidioc_querymenu = uvc_ioctl_querymenu,
.vidioc_g_selection = uvc_ioctl_g_selection,
+ .vidioc_s_selection = uvc_ioctl_s_selection,
.vidioc_g_parm = uvc_ioctl_g_parm,
.vidioc_s_parm = uvc_ioctl_s_parm,
.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index d854cb19c42c..c87624962896 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -104,6 +104,7 @@
#define UVC_CT_ROLL_ABSOLUTE_CONTROL 0x0f
#define UVC_CT_ROLL_RELATIVE_CONTROL 0x10
#define UVC_CT_PRIVACY_CONTROL 0x11
+#define UVC_CT_REGION_OF_INTEREST_CONTROL 0x14

/* A.9.5. Processing Unit Control Selectors */
#define UVC_PU_CONTROL_UNDEFINED 0x00
diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h
index 7d21c1634b4d..60d5228f9113 100644
--- a/include/uapi/linux/v4l2-common.h
+++ b/include/uapi/linux/v4l2-common.h
@@ -70,6 +70,16 @@
#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002
/* Native frame size */
#define V4L2_SEL_TGT_NATIVE_SIZE 0x0003
+
+/* Current Region of Interest area */
+#define V4L2_SEL_TGT_ROI_CURRENT 0x0004
+/* Default Region of Interest area */
+#define V4L2_SEL_TGT_ROI_DEFAULT 0x0005
+/* Region of Interest bounds */
+#define V4L2_SEL_TGT_ROI_BOUNDS 0x0006
+/* Set Region of Interest area */
+#define V4L2_SEL_TGT_ROI 0x0007
+
/* Current composing area */
#define V4L2_SEL_TGT_COMPOSE 0x0100
/* Default composing area */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 039c0d7add1b..cbc1635046a0 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -976,6 +976,7 @@ enum v4l2_auto_focus_range {

#define V4L2_CID_PAN_SPEED (V4L2_CID_CAMERA_CLASS_BASE+32)
#define V4L2_CID_TILT_SPEED (V4L2_CID_CAMERA_CLASS_BASE+33)
+#define V4L2_CID_REGION_OF_INTEREST_CONTROLS (V4L2_CID_CAMERA_CLASS_BASE+34)

#define V4L2_CID_CAMERA_ORIENTATION (V4L2_CID_CAMERA_CLASS_BASE+34)
#define V4L2_CAMERA_ORIENTATION_FRONT 0
--
2.30.0.365.g02bc693789-goog
\
 
 \ /
  Last update: 2021-02-01 04:37    [W:0.048 / U:0.040 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site