[PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls
From: Michael Jordan
Date: Fri Aug 28 2026 - 11:27:38 EST
uvc_ctrl_fixup_xu_info() holds a per-device table of controls whose
GET_INFO reply is wrong, and overrides the flags for them. It only runs
from uvc_ctrl_fill_xu_info(), so it can only correct extension unit
controls, but standard controls suffer from the same class of firmware
bug: a device can report a wrong capability byte for a Camera Terminal
or Processing Unit control just as easily.
Rename it to uvc_ctrl_fixup_flags() and call it from
uvc_ctrl_get_flags(), where the flags are derived from GET_INFO for every
control, standard and XU alike. Call it whether or not the GET_INFO
request succeeded, so the table has the last word in both cases. The
call in uvc_ctrl_fill_xu_info() is dropped, as it now runs from
uvc_ctrl_get_flags() which that function calls.
No functional change for the devices already in the table: their
entries are XU controls, and were matched by entity and selector before
as they are now.
Suggested-by: Ricardo Ribalda <ribalda@xxxxxxxxxxxx>
Signed-off-by: Michael Jordan <jordan.mymail@xxxxxxxxx>
---
drivers/media/usb/uvc/uvc_ctrl.c | 87 +++++++++++++++++---------------
1 file changed, 46 insertions(+), 41 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index aceb26310..b16a5cc0d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -2852,6 +2852,46 @@ int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl)
* Dynamic controls
*/
+static void 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;
+ }
+ }
+}
+
/*
* Retrieve flags for a given control
*/
@@ -2889,49 +2929,16 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
UVC_CTRL_FLAG_ASYNCHRONOUS : 0);
}
+ /*
+ * Some devices report bogus capabilities through GET_INFO. Let the
+ * fixup table have the last word, whether or not GET_INFO succeeded.
+ */
+ uvc_ctrl_fixup_flags(dev, ctrl, info);
+
kfree(data);
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 +2979,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