Linux Media Controller development
 help / color / mirror / Atom feed
* [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