Re: [PATCH v3 2/4] media: uvcvideo: generalise the XU flags fixup to all controls

From: Ricardo Ribalda

Date: Mon Sep 28 2026 - 13:58:07 EST


Hi Michael


On Mon, 28 Sept 2026 at 17:24, Michael Jordan <jordan.mymail@xxxxxxxxx> wrote:
>
> uvc_ctrl_fixup_xu_info() overrides the flags of controls whose GET_INFO
> reply is known to be wrong, but is only called for extension unit
> controls. Standard controls can have the same problem.
>
> Rename it to uvc_ctrl_fixup_flags() and call it from
> uvc_ctrl_get_flags(), which handles all controls. When an entry
> matches, skip the GET_INFO request, as its result would be overridden.
>
> Suggested-by: Ricardo Ribalda <ribalda@xxxxxxxxxxxx>
> Reviewed-by: Ricardo Ribalda <ribalda@xxxxxxxxxxxx>
> Reviewed-by: Hans de Goede <johannes.goede@xxxxxxxxxxxxxxxx>
> Assisted-by: Claude:claude-fable-5

nit: I think that the current way is to not mention the model version

Assisted-by: LLM

https://docs.kernel.org/process/coding-assistants.html#attribution

If you need to respin please fix it, but maybe Hans or Laurent fix it for you.

Regards!
> Signed-off-by: Michael Jordan <jordan.mymail@xxxxxxxxx>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 91 ++++++++++++++++++--------------
> 1 file changed, 50 insertions(+), 41 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index aceb263103e9..64c90c380d3d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -2852,6 +2852,48 @@ int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl)
> * Dynamic controls
> */
>
> +static bool uvc_ctrl_fixup_flags(struct uvc_device *dev,
> + const struct uvc_control *ctrl,
> + struct uvc_control_info *info)
> +{
> + struct uvc_ctrl_fixup {
> + struct usb_device_id id;
> + u8 entity;
> + u8 selector;
> + u8 flags;
> + };
> +
> + static const struct uvc_ctrl_fixup fixups[] = {
> + { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1,
> + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> + UVC_CTRL_FLAG_AUTO_UPDATE },
> + { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1,
> + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> + UVC_CTRL_FLAG_AUTO_UPDATE },
> + { { USB_DEVICE(0x046d, 0x0994) }, 9, 1,
> + UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> + UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> + UVC_CTRL_FLAG_AUTO_UPDATE },
> + };
> +
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
> + if (!usb_match_one_id(dev->intf, &fixups[i].id))
> + continue;
> +
> + if (fixups[i].entity == ctrl->entity->id &&
> + fixups[i].selector == info->selector) {
> + info->flags = fixups[i].flags;
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> /*
> * Retrieve flags for a given control
> */
> @@ -2862,6 +2904,14 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
> u8 *data;
> int ret;
>
> + /*
> + * Some devices report bogus capabilities through GET_INFO. If the
> + * fixup table covers this control, take the flags from the table and
> + * skip the query altogether.
> + */
> + if (uvc_ctrl_fixup_flags(dev, ctrl, info))
> + return 0;
> +
> data = kmalloc(1, GFP_KERNEL);
> if (data == NULL)
> return -ENOMEM;
> @@ -2893,45 +2943,6 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
> return ret;
> }
>
> -static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev,
> - const struct uvc_control *ctrl, struct uvc_control_info *info)
> -{
> - struct uvc_ctrl_fixup {
> - struct usb_device_id id;
> - u8 entity;
> - u8 selector;
> - u8 flags;
> - };
> -
> - static const struct uvc_ctrl_fixup fixups[] = {
> - { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1,
> - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> - UVC_CTRL_FLAG_AUTO_UPDATE },
> - { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1,
> - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> - UVC_CTRL_FLAG_AUTO_UPDATE },
> - { { USB_DEVICE(0x046d, 0x0994) }, 9, 1,
> - UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
> - UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
> - UVC_CTRL_FLAG_AUTO_UPDATE },
> - };
> -
> - unsigned int i;
> -
> - for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
> - if (!usb_match_one_id(dev->intf, &fixups[i].id))
> - continue;
> -
> - if (fixups[i].entity == ctrl->entity->id &&
> - fixups[i].selector == info->selector) {
> - info->flags = fixups[i].flags;
> - return;
> - }
> - }
> -}
> -
> /*
> * Query control information (size and flags) for XU controls.
> */
> @@ -2972,8 +2983,6 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
> goto done;
> }
>
> - uvc_ctrl_fixup_xu_info(dev, ctrl, info);
> -
> uvc_dbg(dev, CONTROL,
> "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }\n",
> info->entity, info->selector, info->size,
> --
> 2.43.0
>


--
Ricardo Ribalda