From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Yunke Cao <yunkec@google.com>
Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl>,
Nicolas Dufresne <nicolas@ndufresne.ca>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Tomasz Figa <tfiga@chromium.org>,
Sergey Senozhatsky <senozhatsky@chromium.org>,
Ricardo Ribalda <ribalda@chromium.org>,
linux-media@vger.kernel.org
Subject: Re: [PATCH v7 3/7] media: uvcvideo: Add support for compound controls
Date: Wed, 24 Aug 2022 22:56:47 +0300 [thread overview]
Message-ID: <YwaCf1mR7SGCfnAR@pendragon.ideasonboard.com> (raw)
In-Reply-To: <20220628075705.2278044-4-yunkec@google.com>
Hi Yunke,
Thank you for the patch.
On Tue, Jun 28, 2022 at 04:57:01PM +0900, Yunke Cao wrote:
> Supports getting/setting current value.
> Supports getting default value.
> Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 279 ++++++++++++++++++++++++++-----
> drivers/media/usb/uvc/uvcvideo.h | 4 +
> 2 files changed, 238 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 772d9d28a520..508ee04afbcd 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -815,6 +815,34 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> }
> }
>
> +/* Extract the byte array specified by mapping->offset and mapping->size
> + * stored at 'data' to the output array 'data_out'.
> + */
Please use the
/*
* multi
* line
*/
style instead of
/* multi
* line
*/
The uvcvideo driver has recently switched to the former in commit
699b9a86a3f0 ("media: uvcvideo: Fix comment blocks style").
> +static int uvc_get_array(struct uvc_control_mapping *mapping, const u8 *data,
> + u8 *data_out)
> +{
> + // Only supports byte-aligned data.
And no C++-style comments either please.
> + if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> + return -EINVAL;
> +
> + memcpy(data_out, data + mapping->offset / 8, mapping->size / 8);
> + return 0;
> +}
> +
> +/* Copy the byte array 'data_in' to the destination specified by mapping->offset
> + * and mapping->size stored at 'data'.
> + */
> +static int uvc_set_array(struct uvc_control_mapping *mapping, const u8 *data_in,
> + u8 *data)
> +{
> + // Only supports byte-aligned data.
> + if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> + return -EINVAL;
> +
> + memcpy(data + mapping->offset / 8, data_in, mapping->size / 8);
> + return 0;
> +}
> +
Could you add here a
static bool
uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
{
return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
}
and use it below ? That would be clearer I think.
> /* ------------------------------------------------------------------------
> * Terminal and unit management
> */
> @@ -831,7 +859,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
>
> static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> struct uvc_control_mapping **mapping, struct uvc_control **control,
> - int next)
> + int next, int next_compound)
> {
> struct uvc_control *ctrl;
> struct uvc_control_mapping *map;
> @@ -846,14 +874,18 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> continue;
>
> list_for_each_entry(map, &ctrl->info.mappings, list) {
> - if ((map->id == v4l2_id) && !next) {
> + if (map->id == v4l2_id && !next && !next_compound) {
> *control = ctrl;
> *mapping = map;
> return;
> }
>
> if ((*mapping == NULL || (*mapping)->id > map->id) &&
> - (map->id > v4l2_id) && next) {
> + (map->id > v4l2_id) &&
> + ((map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> + next) ||
> + (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> + next_compound))) {
> *control = ctrl;
> *mapping = map;
> }
This could become for instance
if ((*mapping == NULL || (*mapping)->id > map->id) &&
(map->id > v4l2_id) &&
((!uvc_ctrl_mapping_is_compound(map) && next) ||
(uvc_ctrl_mapping_is_compound(map) && next_compound))) {
*control = ctrl;
*mapping = map;
}
> @@ -867,6 +899,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> struct uvc_control *ctrl = NULL;
> struct uvc_entity *entity;
> int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> + int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
>
> *mapping = NULL;
>
> @@ -875,12 +908,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>
> /* Find the control. */
> list_for_each_entry(entity, &chain->entities, chain) {
> - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> - if (ctrl && !next)
> + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> + next_compound);
> + if (ctrl && !next && !next_compound)
> return ctrl;
> }
>
> - if (ctrl == NULL && !next)
> + if (!ctrl && !next && !next_compound)
> uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
> v4l2_id);
>
> @@ -943,6 +977,39 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
> return 0;
> }
>
> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> + struct uvc_control *ctrl)
> +{
I would declare
u8 data;
here, and
> + int ret = 0;
> +
> + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> + return -EACCES;
> +
> + if (ctrl->loaded)
> + return 0;
> +
set
data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
here, and
> + if (ctrl->entity->get_cur) {
> + ret = ctrl->entity->get_cur(chain->dev,
> + ctrl->entity,
> + ctrl->info.selector,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
improve indentation here
ret = ctrl->entity->get_cur(chain->dev, ctrl->entity,
ctrl->info.selector, data,
ctrl->info.size);
> + } else {
and also use data here
> + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> + ctrl->entity->id, chain->dev->intfnum,
> + ctrl->info.selector,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> + ctrl->info.size);
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
ctrl->entity->id, chain->dev->intfnum,
ctrl->info.selector, data,
ctrl->info.size);
> + }
> +
> + if (ret < 0)
> + return ret;
> +
> + ctrl->loaded = 1;
> +
> + return ret;
> +}
> +
> static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
> const u8 *data)
> {
> @@ -963,35 +1030,19 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
> return value;
> }
>
> -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> - struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
> - s32 *value)
> +static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> + struct uvc_control *ctrl,
> + struct uvc_control_mapping *mapping,
> + s32 *value)
> {
> int ret;
>
> - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> - return -EACCES;
> -
> - if (!ctrl->loaded) {
> - if (ctrl->entity->get_cur) {
> - ret = ctrl->entity->get_cur(chain->dev,
> - ctrl->entity,
> - ctrl->info.selector,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> - ctrl->info.size);
> - } else {
> - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> - ctrl->entity->id,
> - chain->dev->intfnum,
> - ctrl->info.selector,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> - ctrl->info.size);
> - }
> - if (ret < 0)
> - return ret;
> + if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> + return -EINVAL;
>
> - ctrl->loaded = 1;
> - }
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (ret < 0)
> + return ret;
>
> *value = __uvc_ctrl_get_value(mapping,
> uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> @@ -999,6 +1050,57 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> return 0;
> }
>
> +static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
I don't like this name much. One option would be to rename the
__uvc_ctrl_get_compound() function below to
__uvc_ctrl_get_compound_cur() and call this function
__uvc_ctrl_get_compound(). Better options are likely possible.
> + struct uvc_control *ctrl,
> + int id,
> + struct v4l2_ext_control *xctrl)
> +{
> + int ret, size;
size is never negative, make it an unsigned int.
> + u8 *data;
> +
> + if (WARN_ON(!mapping->size % 8))
> + return -EINVAL;
This duplicates a check in the get_array function. Let's move all those
checks to the function that adds mappings.
> +
> + size = mapping->size / 8;
> + if (xctrl->size < size) {
> + xctrl->size = size;
> + return -ENOSPC;
> + }
> +
> + data = kmalloc(size, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = mapping->get_array(mapping, uvc_ctrl_data(ctrl, id), data);
> + if (ret < 0)
> + goto out;
> +
> + ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> +
> +out:
> + kfree(data);
> + return ret;
> +}
> +
> +static int __uvc_ctrl_get_compound(struct uvc_video_chain *chain,
> + struct uvc_control *ctrl,
> + struct uvc_control_mapping *mapping,
> + struct v4l2_ext_control *xctrl)
> +{
> + int ret;
> +
> + if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> + return -EINVAL;
> +
> + ret = __uvc_ctrl_load_cur(chain, ctrl);
> + if (ret < 0)
> + return ret;
> +
> + return __uvc_ctrl_get_compound_to_user(mapping, ctrl,
> + UVC_CTRL_DATA_CURRENT,
> + xctrl);
> +}
> +
> static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> u32 found_id)
> {
> @@ -1102,10 +1204,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>
> if (mapping->master_id)
> __uvc_find_control(ctrl->entity, mapping->master_id,
> - &master_map, &master_ctrl, 0);
> + &master_map, &master_ctrl, 0, 0);
> if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> - s32 val;
> - int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> + int ret;
> + s32 val = 0;
Move ret after val.
> +
> + if (master_map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> + return -EINVAL;
> +
> + ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
> if (ret < 0)
> return ret;
>
> @@ -1113,6 +1220,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
> }
>
> + if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> + v4l2_ctrl->default_value = 0;
> + v4l2_ctrl->minimum = 0;
> + v4l2_ctrl->maximum = 0;
> + v4l2_ctrl->step = 0;
> + return 0;
> + }
> +
> if (!ctrl->cached) {
> int ret = uvc_ctrl_populate_cache(chain, ctrl);
> if (ret < 0)
> @@ -1346,11 +1462,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> s32 val = 0;
>
> - __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> + __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
> if (ctrl == NULL)
> return;
>
> - if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
> + if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> + __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
> changes |= V4L2_EVENT_CTRL_CH_VALUE;
>
> uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> @@ -1517,7 +1634,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
> u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> s32 val = 0;
>
> - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> + if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> + __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
> changes |= V4L2_EVENT_CTRL_CH_VALUE;
>
> uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
> @@ -1647,7 +1765,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
>
> for (i = 0; i < ctrls->count; i++) {
> __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> - &ctrl_found, 0);
> + &ctrl_found, 0, 0);
> if (uvc_control == ctrl_found)
> return i;
> }
> @@ -1694,11 +1812,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> if (ctrl == NULL)
> return -EINVAL;
>
> - return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
> + if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> + return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> + else
> + return __uvc_ctrl_get_compound(chain, ctrl, mapping, xctrl);
> }
>
> -int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> - struct v4l2_ext_control *xctrl)
> +int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> + struct v4l2_ext_control *xctrl)
> {
> struct v4l2_queryctrl qc = { .id = xctrl->id };
> int ret = uvc_query_v4l2_ctrl(chain, &qc);
> @@ -1710,6 +1831,56 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> return 0;
> }
>
> +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> + struct v4l2_ext_control *xctrl)
> +{
> + struct uvc_control *ctrl;
> + struct uvc_control_mapping *mapping;
> + int ret;
> +
> + if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
> + return -EACCES;
> +
This check comes a bit out of the blue, I think it belongs to another
patch, with a commit message that clearly explain the rationale.
> + ctrl = uvc_find_control(chain, xctrl->id, &mapping);
> + if (!ctrl)
> + return -EINVAL;
And here, you're duplicating the uvc_find_control() call from
uvc_query_v4l2_ctrl(), called in __uvc_ctrl_get_fixed_std().
Furthermore, you're not holding the ctrl_mutex lock here, which I think
is wrong.
I'd like to see a patch before this one that refactors what needs to be
refactored first, and then the introduction of compound types support
without any extra change like this. Splitting code out to
__uvc_ctrl_load_cur() could also be moved to a preparatory patch to
simplify this one, it's quite hard to review.
> +
> + if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> + return __uvc_ctrl_get_fixed_std(chain, xctrl);
> +
> + if (!ctrl->cached) {
> + ret = uvc_ctrl_populate_cache(chain, ctrl);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> + xctrl);
> +}
> +
> +int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> + struct v4l2_ext_control *xctrl,
> + struct uvc_control *ctrl)
> +{
> + int ret;
> + u8 *data;
Please move ret after data.
> +
> + data = kmalloc(xctrl->size, GFP_KERNEL);
A limit on the size is needed to avoid denial of service attacks.
> + if (!data)
> + return -ENOMEM;
> +
> + ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> + if (ret < 0)
> + goto out;
> +
> + ret = mapping->set_array(mapping, data,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
ret = mapping->set_array(mapping, data,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
The V4L2 control model is that the kernel can update the value of a
control if the requested value is out of range or otherwise not
acceptable. You should here copy the data back to the user.
> +
> +out:
> + kfree(data);
> + return ret;
> +}
> +
> int uvc_ctrl_set(struct uvc_fh *handle,
> struct v4l2_ext_control *xctrl)
> {
> @@ -1820,8 +1991,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> ctrl->info.size);
> }
>
> - mapping->set(mapping, value,
> - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> + if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES) {
> + mapping->set(mapping, value,
> + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> + } else {
> + ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> + if (ret < 0)
> + return ret;
> + }
>
> if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> ctrl->handle = handle;
> @@ -2220,10 +2397,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> return -ENOMEM;
> }
>
> - if (map->get == NULL)
> + if (!map->get && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> map->get = uvc_get_le_value;
> - if (map->set == NULL)
> + if (!map->set && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> map->set = uvc_set_le_value;
> + if (!map->get_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> + map->get_array = uvc_get_array;
> + if (!map->set_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> + map->set_array = uvc_set_array;
>
> for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> @@ -2233,6 +2414,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> }
> }
>
> + if (map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> + WARN_ON(!map->get || !map->set))
> + return -EINVAL;
> +
> + if (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> + WARN_ON(!map->get_array || !map->set_array))
> + return -EINVAL;
> +
Can this happen, given that you set them above if they're null ?
> list_add_tail(&map->list, &ctrl->info.mappings);
> uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
> uvc_map_get_name(map), ctrl->info.entity,
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index ba028ba7c34e..2f9b75faae83 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -262,8 +262,12 @@ struct uvc_control_mapping {
>
> s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
> const u8 *data);
> + int (*get_array)(struct uvc_control_mapping *mapping, const u8 *data,
> + u8 *data_out);
> void (*set)(struct uvc_control_mapping *mapping, s32 value,
> u8 *data);
> + int (*set_array)(struct uvc_control_mapping *mapping, const u8 *data_in,
> + u8 *data);
Wouldn't those operations be better names get_compound and set_compound ?
> };
>
> struct uvc_control {
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2022-08-24 19:57 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-28 7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
2022-06-28 7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2022-08-24 8:40 ` Laurent Pinchart
2022-08-24 8:50 ` Hans Verkuil
2022-08-24 8:51 ` Hans Verkuil
2022-08-25 6:15 ` Yunke Cao
2022-06-28 7:57 ` [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value Yunke Cao
2022-08-24 16:24 ` Laurent Pinchart
2022-06-28 7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
2022-06-30 6:42 ` Ricardo Ribalda
2022-08-24 19:56 ` Laurent Pinchart [this message]
2022-08-29 5:39 ` Yunke Cao
2022-06-28 7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
2022-06-30 6:42 ` Ricardo Ribalda
2022-08-24 20:36 ` Laurent Pinchart
2022-08-29 5:48 ` Yunke Cao
2022-09-28 8:13 ` Ricardo Ribalda
2022-06-28 7:57 ` [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2022-08-24 16:20 ` Laurent Pinchart
2022-09-20 10:01 ` Yunke Cao
2022-09-30 7:59 ` Hans Verkuil
2022-06-28 7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2022-06-30 6:41 ` Ricardo Ribalda
2022-08-24 20:03 ` Laurent Pinchart
2022-09-20 7:52 ` Yunke Cao
2022-06-28 7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2022-06-30 6:41 ` Ricardo Ribalda
2022-08-24 20:17 ` Laurent Pinchart
2022-09-20 7:46 ` Yunke Cao
2022-07-14 23:25 ` [PATCH v7 0/7] media: Implement " Yunke Cao
2022-07-15 0:52 ` Laurent Pinchart
2022-09-27 4:32 ` Yunke Cao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YwaCf1mR7SGCfnAR@pendragon.ideasonboard.com \
--to=laurent.pinchart@ideasonboard.com \
--cc=hverkuil-cisco@xs4all.nl \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=nicolas@ndufresne.ca \
--cc=ribalda@chromium.org \
--cc=senozhatsky@chromium.org \
--cc=tfiga@chromium.org \
--cc=yunkec@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.