Re: [PATCHv3 5/6] media: uvcvideo: add UVC 1.5 ROI control

From: Ricardo Ribalda
Date: Tue Mar 23 2021 - 12:17:40 EST


Hi Sergey

On Fri, Mar 19, 2021 at 6:54 AM Sergey Senozhatsky
<senozhatsky@xxxxxxxxxxxx> wrote:
>
> This patch implements UVC 1.5 Region of Interest (ROI) control.
>
> Note that, UVC 1.5 defines CT_DIGITAL_WINDOW_CONTROL controls
> and mentions that 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).
>
> It's is not entirely clear if we need to implement WINDOW_CONTROL.
> ROI is naturally limited by GET_MIN and GET_MAX rectangles.
>
> Another thing to note is that ROI support is implemented as
> V4L2 selection target: selection rectangle represents ROI
> rectangle and selection flags represent ROI auto-controls.
> User-space is required to set valid values for both rectangle
> and auto-controls every time SET_CUR is issued.
>
> Usage example:
>
> struct v4l2_selection roi = {0, };
>
> roi.target = V4L2_SEL_TGT_ROI;
> roi.r.left = 0;
> roi.r.top = 0;
> roi.r.width = 42;
> roi.r.height = 42;
> roi.flags = V4L2_SEL_FLAG_ROI_AUTO_EXPOSURE;
>
> ioctl(fd, VIDIOC_S_SELECTION, &roi);
>
> Signed-off-by: Sergey Senozhatsky <senozhatsky@xxxxxxxxxxxx>
> ---
> drivers/media/usb/uvc/uvc_v4l2.c | 147 ++++++++++++++++++++++++++++++-
> include/uapi/linux/usb/video.h | 1 +
> 2 files changed, 145 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 252136cc885c..d0fe6c33fab6 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1139,14 +1139,66 @@ 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;
> + __u16 auto_controls;
> +} __packed;
> +
> +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:
> + query = UVC_GET_CUR;
> + break;
> + case V4L2_SEL_TGT_ROI_DEFAULT:
> + query = UVC_GET_DEF;
> + break;
> + case V4L2_SEL_TGT_ROI_BOUNDS_MIN:
> + query = UVC_GET_MAX;
> + break;
> + case V4L2_SEL_TGT_ROI_BOUNDS_MAX:
> + 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) {
> + /* ROI left, top, right, bottom are global coordinates. */
> + sel->r.left = roi->left;
> + sel->r.top = roi->top;
> + sel->r.width = roi->right - roi->left + 1;
> + sel->r.height = roi->bottom - roi->top + 1;
> + sel->flags = roi->auto_controls;
> + }
> +
> + 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 +1225,94 @@ 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:
> + case V4L2_SEL_TGT_ROI_DEFAULT:
> + case V4L2_SEL_TGT_ROI_BOUNDS_MIN:
> + case V4L2_SEL_TGT_ROI_BOUNDS_MAX:
> + return uvc_ioctl_g_roi_target(file, fh, sel);
> + }
> +
> + return -EINVAL;
> +}
> +
> +static bool validate_roi_bounds(struct uvc_streaming *stream,
> + struct v4l2_selection *sel)
> +{
> + if (sel->r.left > USHRT_MAX ||
> + sel->r.top > USHRT_MAX ||
> + (sel->r.width + sel->r.left) > USHRT_MAX ||
> + (sel->r.height + sel->r.top) > USHRT_MAX ||
> + !sel->r.width || !sel->r.height)
> + return false;
> +
> + if (sel->flags > V4L2_SEL_FLAG_ROI_AUTO_HIGHER_QUALITY)
> + return false;

Is it not allowed V4L2_SEL_FLAG_ROI_AUTO_IRIS |
V4L2_SEL_FLAG_ROI_AUTO_HIGHER_QUALITY ?

> +
> + return true;
> +}
> +
> +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;

Not sure if this is the correct approach or if we should convert the
value to the closest valid...


> +
> + roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
> + if (!roi)
> + return -ENOMEM;
> +
> + /* ROI left, top, right, bottom are global coordinates. */
> + roi->left = sel->r.left;
> + roi->top = sel->r.top;
> + roi->right = sel->r.width + sel->r.left - 1;
> + roi->bottom = sel->r.height + sel->r.top - 1;
> + roi->auto_controls = sel->flags;
> +
> + 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 +1673,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
> --
> 2.31.0.rc2.261.g7f71774620-goog
>


--
Ricardo Ribalda