* [PATCH 0/3] media: uvcvideo: live pan/tilt position on the OBSBOT Tiny 2
@ 2026-08-28 15:25 Michael Jordan
2026-08-28 15:25 ` [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile Michael Jordan
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Michael Jordan @ 2026-08-28 15:25 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Ricardo Ribalda
Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-kernel,
Michael Jordan
This replaces "[PATCH] media: uvcvideo: query pan/tilt position from the
device on every read" [1], following the review there. The approach in
that patch (a driver-side VOLATILE flag with a separate live buffer) is
dropped: as Ricardo pointed out, a control that carries AUTO_UPDATE
already gets re-read from the device on every VIDIOC_G_EXT_CTRLS, so the
problem on this camera reduces to its firmware clearing the AUTOUPDATE
bit in GET_INFO, and the fix reduces to a flags fixup for the device.
Patch 1 reports V4L2_CTRL_FLAG_VOLATILE (with EXECUTE_ON_WRITE for
writable controls) for AUTO_UPDATE controls, which the driver never did.
Patch 2 renames uvc_ctrl_fixup_xu_info() to uvc_ctrl_fixup_flags() and
calls it from uvc_ctrl_get_flags(), so the per-device flags table can
correct standard controls as well as XU ones. No functional change for
the devices already listed.
Patch 3 adds the OBSBOT Tiny 2 entry, restoring AUTO_UPDATE on its
CT_PANTILT_ABSOLUTE control.
Tested on an OBSBOT Tiny 2 (3564:fef8): pan_absolute and tilt_absolute
now report volatile/execute-on-write, and polling VIDIOC_G_CTRL during
a commanded 60 degree pan returns 0, 21600, 46800, 90000, 115200,
158400, 183600, 216000 -- the live position -- where a stock module
returns the commanded value from the first read. Reads also follow the
gimbal when it is moved by hand. The vendor was asked to fix the
firmware on 2026-08-04 (ticket #8220); no fix so far.
[1] https://lore.kernel.org/linux-media/20260725212332.64927-1-jordan.mymail@gmail.com/
Michael Jordan (3):
media: uvcvideo: report AUTO_UPDATE controls as volatile
media: uvcvideo: generalise the XU flags fixup to all controls
media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt
drivers/media/usb/uvc/uvc_ctrl.c | 107 +++++++++++++++++++------------
1 file changed, 66 insertions(+), 41 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile
2026-08-28 15:25 [PATCH 0/3] media: uvcvideo: live pan/tilt position on the OBSBOT Tiny 2 Michael Jordan
@ 2026-08-28 15:25 ` Michael Jordan
2026-08-31 9:36 ` Ricardo Ribalda
2026-08-28 15:25 ` [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls Michael Jordan
2026-08-28 15:25 ` [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt Michael Jordan
2 siblings, 1 reply; 9+ messages in thread
From: Michael Jordan @ 2026-08-28 15:25 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Ricardo Ribalda
Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-kernel,
Michael Jordan
A control with UVC_CTRL_FLAG_AUTO_UPDATE is one whose value the device
changes on its own: the driver never trusts its cached value for it,
re-reading the device on every VIDIOC_G_EXT_CTRLS (the rollback at the
end of the ioctl runs uvc_ctrl_commit_entity(), which clears ctrl->loaded
for these controls) and re-reading it after each write. That is exactly
what V4L2_CTRL_FLAG_VOLATILE describes to userspace, but the driver never
reported it, so applications had no way to know that the value they read
can change under them and that a fresh read is worth issuing.
Report V4L2_CTRL_FLAG_VOLATILE for AUTO_UPDATE controls. The uAPI
documents writes to a volatile control as ignored unless
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE is also set, and this driver sends every
write of a writable control to the device, so report EXECUTE_ON_WRITE
alongside it whenever the control is settable.
Suggested-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
---
drivers/media/usb/uvc/uvc_ctrl.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 3ca108b83..aceb26310 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1840,6 +1840,17 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) &&
(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
+ if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE) {
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ /*
+ * Writes to a volatile control are documented to be ignored
+ * unless EXECUTE_ON_WRITE is also reported. The driver sends
+ * every write of a writable control to the device, so report
+ * the flag accordingly.
+ */
+ if (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+ }
if (mapping->master_id)
__uvc_find_control(ctrl->entity, mapping->master_id,
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls
2026-08-28 15:25 [PATCH 0/3] media: uvcvideo: live pan/tilt position on the OBSBOT Tiny 2 Michael Jordan
2026-08-28 15:25 ` [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile Michael Jordan
@ 2026-08-28 15:25 ` Michael Jordan
2026-08-31 9:35 ` Ricardo Ribalda
2026-08-28 15:25 ` [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt Michael Jordan
2 siblings, 1 reply; 9+ messages in thread
From: Michael Jordan @ 2026-08-28 15:25 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Ricardo Ribalda
Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-kernel,
Michael Jordan
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@chromium.org>
Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt
2026-08-28 15:25 [PATCH 0/3] media: uvcvideo: live pan/tilt position on the OBSBOT Tiny 2 Michael Jordan
2026-08-28 15:25 ` [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile Michael Jordan
2026-08-28 15:25 ` [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls Michael Jordan
@ 2026-08-28 15:25 ` Michael Jordan
2026-08-31 9:40 ` Ricardo Ribalda
2 siblings, 1 reply; 9+ messages in thread
From: Michael Jordan @ 2026-08-28 15:25 UTC (permalink / raw)
To: Laurent Pinchart, Hans de Goede, Ricardo Ribalda
Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-kernel,
Michael Jordan
The OBSBOT Tiny 2 (3564:fef8) answers GET_INFO for CT_PANTILT_ABSOLUTE
(entity 1, selector 0x0d) with 0x03 -- GET and SET capable, but with the
AUTOUPDATE bit clear. It returns the same 0x03 for every Camera Terminal
control queried, so the firmware is not computing the byte per control.
uvc_ctrl_get_flags() takes the flags from that byte, so it clears the
UVC_CTRL_FLAG_AUTO_UPDATE that the static uvc_ctrls[] entry sets for
this control. Without AUTO_UPDATE nothing clears ctrl->loaded after the
first read, so uvcvideo serves the control from its cache indefinitely:
VIDIOC_G_CTRL returns the last value the host commanded, never the
actuator's live position. On a motorised PTZ camera the position keeps
changing during a move, and changes on its own under the camera's
autonomous subject tracking, so userspace cannot observe it at all.
Add a flags fixup entry restoring AUTO_UPDATE, alongside the flags the
control already has, for this camera's pan/tilt control. With
AUTO_UPDATE restored, the rollback at the end of every
VIDIOC_G_EXT_CTRLS runs uvc_ctrl_commit_entity(), which clears
ctrl->loaded, so the next read re-queries the device and reports the
live position. The fixup replaces info->flags wholesale rather than
OR-ing, so the entry spells out the full flag set for the control.
Tested on an OBSBOT Tiny 2: without this, a read taken while the gimbal
is moving (or after the gimbal is moved by hand) returns a stale value;
with it, VIDIOC_G_EXT_CTRLS tracks the physical position on both axes.
The vendor has been asked to fix the firmware (support ticket #8220,
2026-08-04); no fix is available at the time of writing.
lsusb -v (device descriptor and the Camera Terminal):
Bus 003 Device 006: ID 3564:fef8 Remo Tech Co., Ltd. OBSBOT Tiny 2
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.10
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2 [unknown]
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x3564 Remo Tech Co., Ltd.
idProduct 0xfef8 OBSBOT Tiny 2
bcdDevice 4.09
iManufacturer 1 Remo Tech Co., Ltd.
iProduct 2 OBSBOT Tiny 2
iSerial 0
bNumConfigurations 1
[...]
VideoControl Interface Descriptor:
bLength 18
bDescriptorType 36
bDescriptorSubtype 2 (INPUT_TERMINAL)
bTerminalID 1
wTerminalType 0x0201 Camera Sensor
bAssocTerminal 0
iTerminal 0
wObjectiveFocalLengthMin 0
wObjectiveFocalLengthMax 0
wOcularFocalLength 0
bControlSize 3
bmControls 0x00023e3e
Auto-Exposure Mode
Auto-Exposure Priority
Exposure Time (Absolute)
Exposure Time (Relative)
Focus (Absolute)
Zoom (Absolute)
Zoom (Relative)
PanTilt (Absolute)
PanTilt (Relative)
Roll (Absolute)
Focus, Auto
Suggested-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
---
drivers/media/usb/uvc/uvc_ctrl.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b16a5cc0d..379ee51bd 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -2876,6 +2876,15 @@ static void uvc_ctrl_fixup_flags(struct uvc_device *dev,
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 },
+ /*
+ * OBSBOT Tiny 2: GET_INFO on CT_PANTILT_ABSOLUTE_CONTROL is a
+ * stub that reports GET|SET only, clearing the AUTO_UPDATE the
+ * driver's own control table sets for this control.
+ */
+ { { USB_DEVICE(0x3564, 0xfef8) }, 1,
+ UVC_CT_PANTILT_ABSOLUTE_CONTROL,
+ UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_RANGE |
+ UVC_CTRL_FLAG_RESTORE | UVC_CTRL_FLAG_AUTO_UPDATE },
};
unsigned int i;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls
2026-08-28 15:25 ` [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls Michael Jordan
@ 2026-08-31 9:35 ` Ricardo Ribalda
0 siblings, 0 replies; 9+ messages in thread
From: Ricardo Ribalda @ 2026-08-31 9:35 UTC (permalink / raw)
To: Michael Jordan
Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Hans Verkuil, linux-media, linux-kernel
Hi Michael
Thanks for your patch
On Fri, 28 Aug 2026 at 17:26, Michael Jordan <jordan.mymail@gmail.com> wrote:
>
> 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@chromium.org>
> Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
> ---
> 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
> */
>
What about making uvc_ctrl_fixup_flags return bool:
true if it applied a patch.
That way we can move it to the beggining of uvc_ctrl_flags, even
before the kmalloc and return early.
> +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
>
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile
2026-08-28 15:25 ` [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile Michael Jordan
@ 2026-08-31 9:36 ` Ricardo Ribalda
0 siblings, 0 replies; 9+ messages in thread
From: Ricardo Ribalda @ 2026-08-31 9:36 UTC (permalink / raw)
To: Michael Jordan
Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Hans Verkuil, linux-media, linux-kernel
Hi Michael
On Fri, 28 Aug 2026 at 17:26, Michael Jordan <jordan.mymail@gmail.com> wrote:
>
> A control with UVC_CTRL_FLAG_AUTO_UPDATE is one whose value the device
> changes on its own: the driver never trusts its cached value for it,
> re-reading the device on every VIDIOC_G_EXT_CTRLS (the rollback at the
> end of the ioctl runs uvc_ctrl_commit_entity(), which clears ctrl->loaded
> for these controls) and re-reading it after each write. That is exactly
> what V4L2_CTRL_FLAG_VOLATILE describes to userspace, but the driver never
> reported it, so applications had no way to know that the value they read
> can change under them and that a fresh read is worth issuing.
>
> Report V4L2_CTRL_FLAG_VOLATILE for AUTO_UPDATE controls. The uAPI
> documents writes to a volatile control as ignored unless
> V4L2_CTRL_FLAG_EXECUTE_ON_WRITE is also set, and this driver sends every
> write of a writable control to the device, so report EXECUTE_ON_WRITE
> alongside it whenever the control is settable.
>
> Suggested-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 3ca108b83..aceb26310 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1840,6 +1840,17 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) &&
> (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
> v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> + if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE) {
> + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> + /*
> + * Writes to a volatile control are documented to be ignored
> + * unless EXECUTE_ON_WRITE is also reported. The driver sends
> + * every write of a writable control to the device, so report
> + * the flag accordingly.
> + */
> + if (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)
> + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
> + }
>
> if (mapping->master_id)
> __uvc_find_control(ctrl->entity, mapping->master_id,
> --
> 2.43.0
>
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt
2026-08-28 15:25 ` [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt Michael Jordan
@ 2026-08-31 9:40 ` Ricardo Ribalda
2026-08-31 9:42 ` Ricardo Ribalda
0 siblings, 1 reply; 9+ messages in thread
From: Ricardo Ribalda @ 2026-08-31 9:40 UTC (permalink / raw)
To: Michael Jordan
Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Hans Verkuil, linux-media, linux-kernel
Hi Michael
Thanks for the patch, and for contacting the vendor.
Could you reply to this patch with the full output of lsusb -v instead
of a cropped one?
We keep that info for future reference.
Thanks!
On Fri, 28 Aug 2026 at 17:26, Michael Jordan <jordan.mymail@gmail.com> wrote:
>
> The OBSBOT Tiny 2 (3564:fef8) answers GET_INFO for CT_PANTILT_ABSOLUTE
> (entity 1, selector 0x0d) with 0x03 -- GET and SET capable, but with the
> AUTOUPDATE bit clear. It returns the same 0x03 for every Camera Terminal
> control queried, so the firmware is not computing the byte per control.
> uvc_ctrl_get_flags() takes the flags from that byte, so it clears the
> UVC_CTRL_FLAG_AUTO_UPDATE that the static uvc_ctrls[] entry sets for
> this control. Without AUTO_UPDATE nothing clears ctrl->loaded after the
> first read, so uvcvideo serves the control from its cache indefinitely:
> VIDIOC_G_CTRL returns the last value the host commanded, never the
> actuator's live position. On a motorised PTZ camera the position keeps
> changing during a move, and changes on its own under the camera's
> autonomous subject tracking, so userspace cannot observe it at all.
>
> Add a flags fixup entry restoring AUTO_UPDATE, alongside the flags the
> control already has, for this camera's pan/tilt control. With
> AUTO_UPDATE restored, the rollback at the end of every
> VIDIOC_G_EXT_CTRLS runs uvc_ctrl_commit_entity(), which clears
> ctrl->loaded, so the next read re-queries the device and reports the
> live position. The fixup replaces info->flags wholesale rather than
> OR-ing, so the entry spells out the full flag set for the control.
>
> Tested on an OBSBOT Tiny 2: without this, a read taken while the gimbal
> is moving (or after the gimbal is moved by hand) returns a stale value;
> with it, VIDIOC_G_EXT_CTRLS tracks the physical position on both axes.
>
> The vendor has been asked to fix the firmware (support ticket #8220,
> 2026-08-04); no fix is available at the time of writing.
>
> lsusb -v (device descriptor and the Camera Terminal):
>
> Bus 003 Device 006: ID 3564:fef8 Remo Tech Co., Ltd. OBSBOT Tiny 2
> Device Descriptor:
> bLength 18
> bDescriptorType 1
> bcdUSB 2.10
> bDeviceClass 239 Miscellaneous Device
> bDeviceSubClass 2 [unknown]
> bDeviceProtocol 1 Interface Association
> bMaxPacketSize0 64
> idVendor 0x3564 Remo Tech Co., Ltd.
> idProduct 0xfef8 OBSBOT Tiny 2
> bcdDevice 4.09
> iManufacturer 1 Remo Tech Co., Ltd.
> iProduct 2 OBSBOT Tiny 2
> iSerial 0
> bNumConfigurations 1
> [...]
> VideoControl Interface Descriptor:
> bLength 18
> bDescriptorType 36
> bDescriptorSubtype 2 (INPUT_TERMINAL)
> bTerminalID 1
> wTerminalType 0x0201 Camera Sensor
> bAssocTerminal 0
> iTerminal 0
> wObjectiveFocalLengthMin 0
> wObjectiveFocalLengthMax 0
> wOcularFocalLength 0
> bControlSize 3
> bmControls 0x00023e3e
> Auto-Exposure Mode
> Auto-Exposure Priority
> Exposure Time (Absolute)
> Exposure Time (Relative)
> Focus (Absolute)
> Zoom (Absolute)
> Zoom (Relative)
> PanTilt (Absolute)
> PanTilt (Relative)
> Roll (Absolute)
> Focus, Auto
>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Suggested-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
> ---
> drivers/media/usb/uvc/uvc_ctrl.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index b16a5cc0d..379ee51bd 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -2876,6 +2876,15 @@ static void uvc_ctrl_fixup_flags(struct uvc_device *dev,
> 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 },
> + /*
> + * OBSBOT Tiny 2: GET_INFO on CT_PANTILT_ABSOLUTE_CONTROL is a
> + * stub that reports GET|SET only, clearing the AUTO_UPDATE the
> + * driver's own control table sets for this control.
> + */
> + { { USB_DEVICE(0x3564, 0xfef8) }, 1,
> + UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> + UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_RANGE |
> + UVC_CTRL_FLAG_RESTORE | UVC_CTRL_FLAG_AUTO_UPDATE },
> };
>
> unsigned int i;
> --
> 2.43.0
>
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt
2026-08-31 9:40 ` Ricardo Ribalda
@ 2026-08-31 9:42 ` Ricardo Ribalda
2026-09-02 0:25 ` Michael Jordan
0 siblings, 1 reply; 9+ messages in thread
From: Ricardo Ribalda @ 2026-08-31 9:42 UTC (permalink / raw)
To: Michael Jordan
Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Hans Verkuil, linux-media, linux-kernel
Hi again.
Sashiko [1] has pointed out that there might be other controls that
will benefit from the UVC_CTRL_FLAG_AUTO_UPDATE.
Can you double check if that is the case? Feel free to add is a v2 or
as a follow-up patch.
Thanks!
[1] https://sashiko.dev/#/patchset/20260828152557.653475-1-jordan.mymail%40gmail.com
On Mon, 31 Aug 2026 at 11:40, Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> Hi Michael
>
> Thanks for the patch, and for contacting the vendor.
> Could you reply to this patch with the full output of lsusb -v instead
> of a cropped one?
>
> We keep that info for future reference.
>
> Thanks!
>
> On Fri, 28 Aug 2026 at 17:26, Michael Jordan <jordan.mymail@gmail.com> wrote:
> >
> > The OBSBOT Tiny 2 (3564:fef8) answers GET_INFO for CT_PANTILT_ABSOLUTE
> > (entity 1, selector 0x0d) with 0x03 -- GET and SET capable, but with the
> > AUTOUPDATE bit clear. It returns the same 0x03 for every Camera Terminal
> > control queried, so the firmware is not computing the byte per control.
> > uvc_ctrl_get_flags() takes the flags from that byte, so it clears the
> > UVC_CTRL_FLAG_AUTO_UPDATE that the static uvc_ctrls[] entry sets for
> > this control. Without AUTO_UPDATE nothing clears ctrl->loaded after the
> > first read, so uvcvideo serves the control from its cache indefinitely:
> > VIDIOC_G_CTRL returns the last value the host commanded, never the
> > actuator's live position. On a motorised PTZ camera the position keeps
> > changing during a move, and changes on its own under the camera's
> > autonomous subject tracking, so userspace cannot observe it at all.
> >
> > Add a flags fixup entry restoring AUTO_UPDATE, alongside the flags the
> > control already has, for this camera's pan/tilt control. With
> > AUTO_UPDATE restored, the rollback at the end of every
> > VIDIOC_G_EXT_CTRLS runs uvc_ctrl_commit_entity(), which clears
> > ctrl->loaded, so the next read re-queries the device and reports the
> > live position. The fixup replaces info->flags wholesale rather than
> > OR-ing, so the entry spells out the full flag set for the control.
> >
> > Tested on an OBSBOT Tiny 2: without this, a read taken while the gimbal
> > is moving (or after the gimbal is moved by hand) returns a stale value;
> > with it, VIDIOC_G_EXT_CTRLS tracks the physical position on both axes.
> >
> > The vendor has been asked to fix the firmware (support ticket #8220,
> > 2026-08-04); no fix is available at the time of writing.
> >
> > lsusb -v (device descriptor and the Camera Terminal):
> >
> > Bus 003 Device 006: ID 3564:fef8 Remo Tech Co., Ltd. OBSBOT Tiny 2
> > Device Descriptor:
> > bLength 18
> > bDescriptorType 1
> > bcdUSB 2.10
> > bDeviceClass 239 Miscellaneous Device
> > bDeviceSubClass 2 [unknown]
> > bDeviceProtocol 1 Interface Association
> > bMaxPacketSize0 64
> > idVendor 0x3564 Remo Tech Co., Ltd.
> > idProduct 0xfef8 OBSBOT Tiny 2
> > bcdDevice 4.09
> > iManufacturer 1 Remo Tech Co., Ltd.
> > iProduct 2 OBSBOT Tiny 2
> > iSerial 0
> > bNumConfigurations 1
> > [...]
> > VideoControl Interface Descriptor:
> > bLength 18
> > bDescriptorType 36
> > bDescriptorSubtype 2 (INPUT_TERMINAL)
> > bTerminalID 1
> > wTerminalType 0x0201 Camera Sensor
> > bAssocTerminal 0
> > iTerminal 0
> > wObjectiveFocalLengthMin 0
> > wObjectiveFocalLengthMax 0
> > wOcularFocalLength 0
> > bControlSize 3
> > bmControls 0x00023e3e
> > Auto-Exposure Mode
> > Auto-Exposure Priority
> > Exposure Time (Absolute)
> > Exposure Time (Relative)
> > Focus (Absolute)
> > Zoom (Absolute)
> > Zoom (Relative)
> > PanTilt (Absolute)
> > PanTilt (Relative)
> > Roll (Absolute)
> > Focus, Auto
> >
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > Suggested-by: Ricardo Ribalda <ribalda@chromium.org>
> > Signed-off-by: Michael Jordan <jordan.mymail@gmail.com>
> > ---
> > drivers/media/usb/uvc/uvc_ctrl.c | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index b16a5cc0d..379ee51bd 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -2876,6 +2876,15 @@ static void uvc_ctrl_fixup_flags(struct uvc_device *dev,
> > 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 },
> > + /*
> > + * OBSBOT Tiny 2: GET_INFO on CT_PANTILT_ABSOLUTE_CONTROL is a
> > + * stub that reports GET|SET only, clearing the AUTO_UPDATE the
> > + * driver's own control table sets for this control.
> > + */
> > + { { USB_DEVICE(0x3564, 0xfef8) }, 1,
> > + UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> > + UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_RANGE |
> > + UVC_CTRL_FLAG_RESTORE | UVC_CTRL_FLAG_AUTO_UPDATE },
> > };
> >
> > unsigned int i;
> > --
> > 2.43.0
> >
>
>
> --
> Ricardo Ribalda
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt
2026-08-31 9:42 ` Ricardo Ribalda
@ 2026-09-02 0:25 ` Michael Jordan
0 siblings, 0 replies; 9+ messages in thread
From: Michael Jordan @ 2026-09-02 0:25 UTC (permalink / raw)
To: Ricardo Ribalda
Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Hans Verkuil, linux-media, linux-kernel, Michael Jordan
Hi Ricardo,
On Mon, 31 Aug 2026 11:42:12 +0200, Ricardo Ribalda <ribalda@chromium.org> wrote:
> > Could you reply to this patch with the full output of lsusb -v instead
> > of a cropped one?
Full output at the end of this mail.
> Sashiko [1] has pointed out that there might be other controls that
> will benefit from the UVC_CTRL_FLAG_AUTO_UPDATE.
>
> Can you double check if that is the case? Feel free to add is a v2 or
> as a follow-up patch.
I checked every control the camera exposes, in two steps: dumped the
GET_INFO capability byte for each control, then tested on the hardware
which values the firmware actually changes on its own and reports
through GET_CUR.
Two more controls need the fixup, both verified live:
- CT_ZOOM_ABSOLUTE (GET_INFO 0x03): the camera's AI subject framing
zooms autonomously, and GET_CUR reports it live -- I watched it move
0..71% with the host issuing no zoom request, matching the zoom value
the vendor's own status protocol reports.
- CT_PANTILT_RELATIVE (GET_INFO 0x03): GET_CUR reports the actual
current speed during a relative move: a commanded 80 reads back as
78, then the deceleration ramp as the gimbal approaches the end
stop, then 0 once stopped. (It reports the magnitude only, for
either direction of travel.) Without AUTO_UPDATE the cache reports
the last written speed forever.
The camera's other AUTO_UPDATE-flagged controls turned out not to
benefit:
- Exposure, white balance and focus: the autos work (verified by
forcing a wrong manual value and watching the image correct itself
once auto is re-enabled), but GET_CUR just echoes the last SET_CUR;
this firmware never reports the auto-chosen value, so AUTO_UPDATE
would add USB traffic for no benefit.
- Hue: the camera has no auto-hue, so nothing updates it.
- CT_ZOOM_RELATIVE: the firmware gets this one right (GET_INFO 0x0f,
AUTOUPDATE set).
- CT_ROLL_ABSOLUTE: GET_INFO 0x01, read-only, and not mapped.
This also corrects my own commit message: the capability byte is not
the same 0x03 for every control (zoom relative reports 0x0f, roll
0x01), so the firmware does compute it per control -- it is just wrong
for the PTZ controls that need AUTOUPDATE most. v2 follows with the
wording fixed and the two entries added.
lsusb -v:
Bus 003 Device 006: ID 3564:fef8 Remo Tech Co., Ltd. OBSBOT Tiny 2
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.10
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2 [unknown]
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x3564 Remo Tech Co., Ltd.
idProduct 0xfef8 OBSBOT Tiny 2
bcdDevice 4.09
iManufacturer 1 Remo Tech Co., Ltd.
iProduct 2 OBSBOT Tiny 2
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x03d2
bNumInterfaces 4
bConfigurationValue 1
iConfiguration 4 OBSBOT Multifunction
bmAttributes 0xc0
Self Powered
MaxPower 2mA
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 14 Video
bFunctionSubClass 3 Video Interface Collection
bFunctionProtocol 0
iFunction 5 OBSBOT Tiny 2 StreamCamera
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 14 Video
bInterfaceSubClass 1 Video Control
bInterfaceProtocol 0
iInterface 5 OBSBOT Tiny 2 StreamCamera
VideoControl Interface Descriptor:
bLength 13
bDescriptorType 36
bDescriptorSubtype 1 (HEADER)
bcdUVC 1.00
wTotalLength 0x0050
dwClockFrequency 48.000000MHz
bInCollection 1
baInterfaceNr( 0) 1
VideoControl Interface Descriptor:
bLength 18
bDescriptorType 36
bDescriptorSubtype 2 (INPUT_TERMINAL)
bTerminalID 1
wTerminalType 0x0201 Camera Sensor
bAssocTerminal 0
iTerminal 0
wObjectiveFocalLengthMin 0
wObjectiveFocalLengthMax 0
wOcularFocalLength 0
bControlSize 3
bmControls 0x00023e3e
Auto-Exposure Mode
Auto-Exposure Priority
Exposure Time (Absolute)
Exposure Time (Relative)
Focus (Absolute)
Zoom (Absolute)
Zoom (Relative)
PanTilt (Absolute)
PanTilt (Relative)
Roll (Absolute)
Focus, Auto
VideoControl Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 5 (PROCESSING_UNIT)
Warning: Descriptor too short
bUnitID 3
bSourceID 1
wMaxMultiplier 400
bControlSize 2
bmControls 0x0000f7df
Brightness
Contrast
Hue
Saturation
Sharpness
White Balance Temperature
White Balance Component
Backlight Compensation
Gain
Power Line Frequency
White Balance Temperature, Auto
White Balance Component, Auto
Digital Multiplier
Digital Multiplier Limit
iProcessing 0
bmVideoStandards 0x1d
None
PAL - 625/50
SECAM - 625/50
NTSC - 625/50
VideoControl Interface Descriptor:
bLength 29
bDescriptorType 36
bDescriptorSubtype 6 (EXTENSION_UNIT)
bUnitID 2
guidExtensionCode {9a1e7291-6843-4683-6d92-39bc7906ee49}
bNumControls 19
bNrInPins 1
baSourceID( 0) 3
bControlSize 4
bmControls( 0) 0xff
bmControls( 1) 0xff
bmControls( 2) 0x04
bmControls( 3) 0x00
iExtension 0
VideoControl Interface Descriptor:
bLength 9
bDescriptorType 36
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
bTerminalID 7
wTerminalType 0x0101 USB Streaming
bAssocTerminal 0
bSourceID 2
iTerminal 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0010 1x 16 bytes
bInterval 8
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 14 Video
bInterfaceSubClass 2 Video Streaming
bInterfaceProtocol 0
iInterface 6 Video Streaming
VideoStreaming Interface Descriptor:
bLength 16
bDescriptorType 36
bDescriptorSubtype 1 (INPUT_HEADER)
bNumFormats 3
wTotalLength 0x02e0
bEndpointAddress 0x81 EP 1 IN
bmInfo 0
bTerminalLink 7
bStillCaptureMethod 0
bTriggerSupport 0
bTriggerUsage 0
bControlSize 1
bmaControls( 0) 4
bmaControls( 1) 0
bmaControls( 2) 4
VideoStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 6 (FORMAT_MJPEG)
bFormatIndex 1
bNumFrameDescriptors 5
bFlags 0
Fixed-size samples: No
bDefaultFrameIndex 1
bAspectRatioX 0
bAspectRatioY 0
bmInterlaceFlags 0x00
Interlaced stream or variable: No
Fields per frame: 1 fields
Field 1 first: No
Field pattern: Field 1 only
bCopyProtect 0
VideoStreaming Interface Descriptor:
bLength 62
bDescriptorType 36
bDescriptorSubtype 7 (FRAME_MJPEG)
bFrameIndex 1
bmCapabilities 0x00
Still image unsupported
wWidth 1920
wHeight 1080
dwMinBitRate 995328000
dwMaxBitRate 1990656000
dwMaxVideoFrameBufferSize 4147200
dwDefaultFrameInterval 333333
bFrameIntervalType 9
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 166833
dwFrameInterval( 2) 200000
dwFrameInterval( 3) 333333
dwFrameInterval( 4) 333666
dwFrameInterval( 5) 400000
dwFrameInterval( 6) 416666
dwFrameInterval( 7) 500000
dwFrameInterval( 8) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 7 (FRAME_MJPEG)
bFrameIndex 2
bmCapabilities 0x00
Still image unsupported
wWidth 3840
wHeight 2160
dwMinBitRate 1327104000
dwMaxBitRate 1327104000
dwMaxVideoFrameBufferSize 16588800
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwFrameInterval( 0) 333333
dwFrameInterval( 1) 333666
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 62
bDescriptorType 36
bDescriptorSubtype 7 (FRAME_MJPEG)
bFrameIndex 3
bmCapabilities 0x00
Still image unsupported
wWidth 1280
wHeight 720
dwMinBitRate 221184000
dwMaxBitRate 884736000
dwMaxVideoFrameBufferSize 1843200
dwDefaultFrameInterval 333333
bFrameIntervalType 9
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 166833
dwFrameInterval( 2) 200000
dwFrameInterval( 3) 333333
dwFrameInterval( 4) 333666
dwFrameInterval( 5) 400000
dwFrameInterval( 6) 416666
dwFrameInterval( 7) 500000
dwFrameInterval( 8) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 7 (FRAME_MJPEG)
bFrameIndex 4
bmCapabilities 0x00
Still image unsupported
wWidth 1280
wHeight 960
dwMinBitRate 294912000
dwMaxBitRate 1179648000
dwMaxVideoFrameBufferSize 2457600
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 333333
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 7 (FRAME_MJPEG)
bFrameIndex 5
bmCapabilities 0x00
Still image unsupported
wWidth 1920
wHeight 1440
dwMinBitRate 663552000
dwMaxBitRate 1327104000
dwMaxVideoFrameBufferSize 5529600
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 333333
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 6
bDescriptorType 36
bDescriptorSubtype 13 (COLORFORMAT)
bColorPrimaries 1 (BT.709,sRGB)
bTransferCharacteristics 1 (BT.709)
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
VideoStreaming Interface Descriptor:
bLength 27
bDescriptorType 36
bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)
bFormatIndex 2
bNumFrameDescriptors 2
guidFormat {32595559-0000-0010-8000-00aa00389b71}
bBitsPerPixel 16
bDefaultFrameIndex 1
bAspectRatioX 0
bAspectRatioY 0
bmInterlaceFlags 0x00
Interlaced stream or variable: No
Fields per frame: 2 fields
Field 1 first: No
Field pattern: Field 1 only
bCopyProtect 0
VideoStreaming Interface Descriptor:
bLength 46
bDescriptorType 36
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
bFrameIndex 1
bmCapabilities 0x00
Still image unsupported
wWidth 640
wHeight 360
dwMinBitRate 55296000
dwMaxBitRate 110592000
dwMaxVideoFrameBufferSize 460800
dwDefaultFrameInterval 333333
bFrameIntervalType 5
dwFrameInterval( 0) 333333
dwFrameInterval( 1) 400000
dwFrameInterval( 2) 416666
dwFrameInterval( 3) 500000
dwFrameInterval( 4) 666666
VideoStreaming Interface Descriptor:
bLength 42
bDescriptorType 36
bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)
bFrameIndex 2
bmCapabilities 0x00
Still image unsupported
wWidth 640
wHeight 480
dwMinBitRate 73728000
dwMaxBitRate 122880000
dwMaxVideoFrameBufferSize 614400
dwDefaultFrameInterval 400000
bFrameIntervalType 4
dwFrameInterval( 0) 400000
dwFrameInterval( 1) 416666
dwFrameInterval( 2) 500000
dwFrameInterval( 3) 666666
VideoStreaming Interface Descriptor:
bLength 6
bDescriptorType 36
bDescriptorSubtype 13 (COLORFORMAT)
bColorPrimaries 1 (BT.709,sRGB)
bTransferCharacteristics 1 (BT.709)
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
VideoStreaming Interface Descriptor:
bLength 28
bDescriptorType 36
bDescriptorSubtype 16 (FORMAT_FRAME_BASED)
bFormatIndex 3
bNumFrameDescriptors 5
guidFormat {34363248-0000-0010-8000-00aa00389b71}
bBitsPerPixel 16
bDefaultFrameIndex 1
bAspectRatioX 0
bAspectRatioY 0
bmInterlaceFlags 0x00
Interlaced stream or variable: No
Fields per frame: 2 fields
Field 1 first: No
Field pattern: Field 1 only
bCopyProtect 0
bVariableSize 1
VideoStreaming Interface Descriptor:
bLength 62
bDescriptorType 36
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
bFrameIndex 1
bmCapabilities 0x00
Still image unsupported
wWidth 1920
wHeight 1080
dwMinBitRate 497664000
dwMaxBitRate 1990656000
dwDefaultFrameInterval 333333
bFrameIntervalType 9
dwBytesPerLine 0
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 166833
dwFrameInterval( 2) 200000
dwFrameInterval( 3) 333333
dwFrameInterval( 4) 333666
dwFrameInterval( 5) 400000
dwFrameInterval( 6) 416666
dwFrameInterval( 7) 500000
dwFrameInterval( 8) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
bFrameIndex 2
bmCapabilities 0x00
Still image unsupported
wWidth 3840
wHeight 2160
dwMinBitRate 1327104000
dwMaxBitRate 1327104000
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwBytesPerLine 0
dwFrameInterval( 0) 333333
dwFrameInterval( 1) 333666
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 62
bDescriptorType 36
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
bFrameIndex 4
bmCapabilities 0x00
Still image unsupported
wWidth 1280
wHeight 720
dwMinBitRate 221184000
dwMaxBitRate 884736000
dwDefaultFrameInterval 333333
bFrameIntervalType 9
dwBytesPerLine 0
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 166833
dwFrameInterval( 2) 200000
dwFrameInterval( 3) 333333
dwFrameInterval( 4) 333666
dwFrameInterval( 5) 400000
dwFrameInterval( 6) 416666
dwFrameInterval( 7) 500000
dwFrameInterval( 8) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
bFrameIndex 5
bmCapabilities 0x00
Still image unsupported
wWidth 1280
wHeight 960
dwMinBitRate 294912000
dwMaxBitRate 1179648000
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwBytesPerLine 0
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 333333
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 50
bDescriptorType 36
bDescriptorSubtype 17 (FRAME_FRAME_BASED)
bFrameIndex 6
bmCapabilities 0x00
Still image unsupported
wWidth 1920
wHeight 1440
dwMinBitRate 663552000
dwMaxBitRate 1327104000
dwDefaultFrameInterval 333333
bFrameIntervalType 6
dwBytesPerLine 0
dwFrameInterval( 0) 166666
dwFrameInterval( 1) 333333
dwFrameInterval( 2) 400000
dwFrameInterval( 3) 416666
dwFrameInterval( 4) 500000
dwFrameInterval( 5) 666666
VideoStreaming Interface Descriptor:
bLength 6
bDescriptorType 36
bDescriptorSubtype 13 (COLORFORMAT)
bColorPrimaries 1 (BT.709,sRGB)
bTransferCharacteristics 1 (BT.709)
bMatrixCoefficients 4 (SMPTE 170M (BT.601))
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 2
bInterfaceCount 2
bFunctionClass 1 Audio
bFunctionSubClass 0 [unknown]
bFunctionProtocol 0
iFunction 8 OBSBOT Tiny2 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 0
bInterfaceClass 1 Audio
bInterfaceSubClass 1 Control Device
bInterfaceProtocol 0
iInterface 8 OBSBOT Tiny2 Audio
AudioControl Interface Descriptor:
bLength 9
bDescriptorType 36
bDescriptorSubtype 1 (HEADER)
bcdADC 1.00
wTotalLength 0x0027
bInCollection 1
baInterfaceNr(0) 3
AudioControl Interface Descriptor:
bLength 12
bDescriptorType 36
bDescriptorSubtype 2 (INPUT_TERMINAL)
bTerminalID 3
wTerminalType 0x0201 Microphone
bAssocTerminal 0
bNrChannels 2
wChannelConfig 0x0003
Left Front (L)
Right Front (R)
iChannelNames 13 Capture Channels
iTerminal 12 OBSBOT Tiny2 Microphone
AudioControl Interface Descriptor:
bLength 9
bDescriptorType 36
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
bTerminalID 4
wTerminalType 0x0101 USB Streaming
bAssocTerminal 0
bSourceID 5
iTerminal 14 Capture Output terminal
AudioControl Interface Descriptor:
bLength 9
bDescriptorType 36
bDescriptorSubtype 6 (FEATURE_UNIT)
bUnitID 5
bSourceID 3
bControlSize 2
bmaControls(0) 0x0003
Mute Control
Volume Control
iFeature 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 0
bNumEndpoints 0
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 17 OBSBOT Tiny2 Microphone
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 1
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 18 Capture Active
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 4
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 2
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x00c0 1x 192 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
Binary Object Store Descriptor:
bLength 5
bDescriptorType 15
wTotalLength 0x0016
bNumDeviceCaps 2
USB 2.0 Extension Device Capability:
bLength 7
bDescriptorType 16
bDevCapabilityType 2
bmAttributes 0x00000006
BESL Link Power Management (LPM) Supported
SuperSpeed USB Device Capability:
bLength 10
bDescriptorType 16
bDevCapabilityType 3
bmAttributes 0x00
wSpeedsSupported 0x000f
Device can operate at Low Speed (1Mbps)
Device can operate at Full Speed (12Mbps)
Device can operate at High Speed (480Mbps)
Device can operate at SuperSpeed (5Gbps)
bFunctionalitySupport 1
Lowest fully-functional device speed is Full Speed (12Mbps)
bU1DevExitLat 1 micro seconds
bU2DevExitLat 500 micro seconds
Device Status: 0x0001
Self Powered
Best regards,
Michael
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-02 0:25 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28 15:25 [PATCH 0/3] media: uvcvideo: live pan/tilt position on the OBSBOT Tiny 2 Michael Jordan
2026-08-28 15:25 ` [PATCH 1/3] media: uvcvideo: report AUTO_UPDATE controls as volatile Michael Jordan
2026-08-31 9:36 ` Ricardo Ribalda
2026-08-28 15:25 ` [PATCH 2/3] media: uvcvideo: generalise the XU flags fixup to all controls Michael Jordan
2026-08-31 9:35 ` Ricardo Ribalda
2026-08-28 15:25 ` [PATCH 3/3] media: uvcvideo: fix up missing AUTO_UPDATE on OBSBOT Tiny 2 pan/tilt Michael Jordan
2026-08-31 9:40 ` Ricardo Ribalda
2026-08-31 9:42 ` Ricardo Ribalda
2026-09-02 0:25 ` Michael Jordan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox