Message ID | 20210319055342.127308-6-senozhatsky@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | media: uvcvideo: implement UVC 1.5 ROI | expand |
Hi Sergey On Fri, Mar 19, 2021 at 6:54 AM Sergey Senozhatsky <senozhatsky@chromium.org> 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@chromium.org> > --- > 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 >
On (21/03/23 17:16), Ricardo Ribalda wrote: [..] > > +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 ? Good question. I don't know. Depends on what HIGHER_QUALITY can stand for (UVC doesn't specify). But overall it seems like features there are mutually exclusive. E.g. AUTO_FACE_DETECT and AUTO_DETECT_AND_TRACK. I think it'll be better to replace this with if (sel->flags > USHRT_MAX) return false; so that we don't let overflow happen and accidentally enable/disable some of the features. > > + > > + 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... Well, at this point we know that ROI rectangle dimensions are out of sane value range. I'd rather tell user-space about integer overflow. Looking for the closest ROI rectangle that suffice can be rather tricky. It may sounds like we can just use BOUNDARIES_MAX, but this is what Firmware D returns for GET_MAX ioctl(V4L2_SEL_TGT_ROI_BOUNDS_MAX) 0, 0, 65535, 65535 -ss
On Wed, Mar 24, 2021 at 11:01 AM Sergey Senozhatsky <senozhatsky@chromium.org> wrote: > > On (21/03/23 17:16), Ricardo Ribalda wrote: > [..] > > > +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 ? > > Good question. > > I don't know. Depends on what HIGHER_QUALITY can stand for (UVC doesn't > specify). But overall it seems like features there are mutually > exclusive. E.g. AUTO_FACE_DETECT and AUTO_DETECT_AND_TRACK. > > > I think it'll be better to replace this with > > if (sel->flags > USHRT_MAX) > return false; > > so that we don't let overflow happen and accidentally enable/disable > some of the features. > > > > + > > > + 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... > > Well, at this point we know that ROI rectangle dimensions are out of > sane value range. I'd rather tell user-space about integer overflow. Adjusting the rectangle to something supported by the hardware is mentioned explicitly in the V4L2 API documentation and is what drivers have to implement. Returning an error on invalid value is not a correct behavior here (and similarly for many other operations, e.g. S_FMT). https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/vidioc-g-selection.html > > Looking for the closest ROI rectangle that suffice can be rather > tricky. It may sounds like we can just use BOUNDARIES_MAX, but this > is what Firmware D returns for GET_MAX > > ioctl(V4L2_SEL_TGT_ROI_BOUNDS_MAX) > > 0, 0, 65535, 65535 Perhaps the frame size would be the correct bounds? Best regards, Tomasz
On (21/03/24 11:14), Tomasz Figa wrote: > > > > +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... > > > > Well, at this point we know that ROI rectangle dimensions are out of > > sane value range. I'd rather tell user-space about integer overflow. > > Adjusting the rectangle to something supported by the hardware is > mentioned explicitly in the V4L2 API documentation and is what drivers > have to implement. Returning an error on invalid value is not a > correct behavior here (and similarly for many other operations, e.g. > S_FMT). Well, in this particular case we are talking about user-space that wants to set ROI rectangle that is knowingly violates device's GET_MAX and overflows UVC ROI rectangle u16 value range. That's a clear bug in user-space. Do we want to pretend that user-space does the correct thing and fixup stuff behind the scenes? > > Looking for the closest ROI rectangle that suffice can be rather > > tricky. It may sounds like we can just use BOUNDARIES_MAX, but this > > is what Firmware D returns for GET_MAX > > > > ioctl(V4L2_SEL_TGT_ROI_BOUNDS_MAX) > > > > 0, 0, 65535, 65535 > > Perhaps the frame size would be the correct bounds? I can check that.
On Wed, Mar 24, 2021 at 11:31 AM Sergey Senozhatsky <senozhatsky@chromium.org> wrote: > > On (21/03/24 11:14), Tomasz Figa wrote: > > > > > +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... > > > > > > Well, at this point we know that ROI rectangle dimensions are out of > > > sane value range. I'd rather tell user-space about integer overflow. > > > > Adjusting the rectangle to something supported by the hardware is > > mentioned explicitly in the V4L2 API documentation and is what drivers > > have to implement. Returning an error on invalid value is not a > > correct behavior here (and similarly for many other operations, e.g. > > S_FMT). > > Well, in this particular case we are talking about user-space that wants > to set ROI rectangle that is knowingly violates device's GET_MAX and > overflows UVC ROI rectangle u16 value range. That's a clear bug in user-space. > Do we want to pretend that user-space does the correct thing and fixup > stuff behind the scenes? > That's how the API is defined. There is a valid use case for this - you don't need to run QUERY_CTRL if all you need is setting the biggest possible rectangle, just set it to (0, 0), (INT_MAX, INT_MAX). > > > Looking for the closest ROI rectangle that suffice can be rather > > > tricky. It may sounds like we can just use BOUNDARIES_MAX, but this > > > is what Firmware D returns for GET_MAX > > > > > > ioctl(V4L2_SEL_TGT_ROI_BOUNDS_MAX) > > > > > > 0, 0, 65535, 65535 > > > > Perhaps the frame size would be the correct bounds? > > I can check that.
On (21/03/24 11:34), Tomasz Figa wrote: > On Wed, Mar 24, 2021 at 11:31 AM Sergey Senozhatsky > <senozhatsky@chromium.org> wrote: [..] > > > Adjusting the rectangle to something supported by the hardware is > > > mentioned explicitly in the V4L2 API documentation and is what drivers > > > have to implement. Returning an error on invalid value is not a > > > correct behavior here (and similarly for many other operations, e.g. > > > S_FMT). > > > > Well, in this particular case we are talking about user-space that wants > > to set ROI rectangle that is knowingly violates device's GET_MAX and > > overflows UVC ROI rectangle u16 value range. That's a clear bug in user-space. > > Do we want to pretend that user-space does the correct thing and fixup > > stuff behind the scenes? > > > > That's how the API is defined. There is a valid use case for this - > you don't need to run QUERY_CTRL if all you need is setting the > biggest possible rectangle, just set it to (0, 0), (INT_MAX, INT_MAX). I guess in our case we need to talk about rectangle,auto-controls tuple that we send to firmware rect { (0, 0), (INT_MAX, INT_MAX) } auto-controls { INT_MAX } For ROI user-space also must provide valid auto-controls value, which normally requires GET_MIN/GET_MAX discovery. v4l2 selection API mentions only rectangle adjustments and errnos like -ERANGE also mention "It is not possible to adjust struct v4l2_rect r rectangle to satisfy all constraints given in the flags argument". So in case when auto-controls is out of supported range (out of GET_MIN, GET_MAX range) there is no way for us to tell user-space that auto-controls is wrong. We probably need silently pick up the first supported value, but not sure how well this will work out in the end.
On Wed, Mar 24, 2021 at 11:52 AM Sergey Senozhatsky <senozhatsky@chromium.org> wrote: > > On (21/03/24 11:34), Tomasz Figa wrote: > > On Wed, Mar 24, 2021 at 11:31 AM Sergey Senozhatsky > > <senozhatsky@chromium.org> wrote: > [..] > > > > Adjusting the rectangle to something supported by the hardware is > > > > mentioned explicitly in the V4L2 API documentation and is what drivers > > > > have to implement. Returning an error on invalid value is not a > > > > correct behavior here (and similarly for many other operations, e.g. > > > > S_FMT). > > > > > > Well, in this particular case we are talking about user-space that wants > > > to set ROI rectangle that is knowingly violates device's GET_MAX and > > > overflows UVC ROI rectangle u16 value range. That's a clear bug in user-space. > > > Do we want to pretend that user-space does the correct thing and fixup > > > stuff behind the scenes? > > > > > > > That's how the API is defined. There is a valid use case for this - > > you don't need to run QUERY_CTRL if all you need is setting the > > biggest possible rectangle, just set it to (0, 0), (INT_MAX, INT_MAX). > > I guess in our case we need to talk about rectangle,auto-controls tuple > that we send to firmware > > rect { > (0, 0), (INT_MAX, INT_MAX) > } > auto-controls { > INT_MAX > } > > For ROI user-space also must provide valid auto-controls value, which > normally requires GET_MIN/GET_MAX discovery. > > v4l2 selection API mentions only rectangle adjustments and errnos like > -ERANGE also mention "It is not possible to adjust struct v4l2_rect r > rectangle to satisfy all constraints given in the flags argument". > > So in case when auto-controls is out of supported range (out of > GET_MIN, GET_MAX range) there is no way for us to tell user-space that > auto-controls is wrong. We probably need silently pick up the first > supported value, but not sure how well this will work out in the end. Shouldn't the autocontrol selection be done via a separate bitmask control rather than some custom flags in the selection API? Best regards, Tomasz
On (21/03/24 12:00), Tomasz Figa wrote: [..] > > I guess in our case we need to talk about rectangle,auto-controls tuple > > that we send to firmware > > > > rect { > > (0, 0), (INT_MAX, INT_MAX) > > } > > auto-controls { > > INT_MAX > > } > > > > For ROI user-space also must provide valid auto-controls value, which > > normally requires GET_MIN/GET_MAX discovery. > > > > v4l2 selection API mentions only rectangle adjustments and errnos like > > -ERANGE also mention "It is not possible to adjust struct v4l2_rect r > > rectangle to satisfy all constraints given in the flags argument". > > > > So in case when auto-controls is out of supported range (out of > > GET_MIN, GET_MAX range) there is no way for us to tell user-space that > > auto-controls is wrong. We probably need silently pick up the first > > supported value, but not sure how well this will work out in the end. > > Shouldn't the autocontrol selection be done via a separate bitmask > control rather than some custom flags in the selection API? That selection must be done before we send ROI to the firmware. Firmware H that I have supports split controls - we can send ROI::rectangle and ROI::autocontrols separately. But other firmwares don't tolerate such a thing and by the time we issue uvc_query_ctrl(stream->dev, UVC_SET_CUR UVC_CT_REGION_OF_INTEREST_CONTROL roi, + sizeof(struct uvc_roi_rect)) roi rectangle should be of size 5 * u16 and contain values that firmware will accept, including autocontrols.
On (21/03/24 12:05), Sergey Senozhatsky wrote: > > > For ROI user-space also must provide valid auto-controls value, which > > > normally requires GET_MIN/GET_MAX discovery. > > > > > > v4l2 selection API mentions only rectangle adjustments and errnos like > > > -ERANGE also mention "It is not possible to adjust struct v4l2_rect r > > > rectangle to satisfy all constraints given in the flags argument". > > > > > > So in case when auto-controls is out of supported range (out of > > > GET_MIN, GET_MAX range) there is no way for us to tell user-space that > > > auto-controls is wrong. We probably need silently pick up the first > > > supported value, but not sure how well this will work out in the end. > > > > Shouldn't the autocontrol selection be done via a separate bitmask > > control rather than some custom flags in the selection API? > > That selection must be done before we send ROI to the firmware. > Firmware H that I have supports split controls - we can send > ROI::rectangle and ROI::autocontrols separately. But other > firmwares don't tolerate such a thing and by the time we issue > > uvc_query_ctrl(stream->dev, > UVC_SET_CUR > UVC_CT_REGION_OF_INTEREST_CONTROL > roi, > + sizeof(struct uvc_roi_rect)) > > roi rectangle should be of size 5 * u16 and contain values that firmware ^^^ roi structure > will accept, including autocontrols.
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; + + 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; + + 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
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@chromium.org> --- drivers/media/usb/uvc/uvc_v4l2.c | 147 ++++++++++++++++++++++++++++++- include/uapi/linux/usb/video.h | 1 + 2 files changed, 145 insertions(+), 3 deletions(-)