On Thu Nov 13 23:04:00 2025 +0200, Ricardo Ribalda wrote:
> Some devices, such as the Grandstream GUV3100 and the LSK Meeting Eye
> for Business & Home, exhibit entity ID collisions between units and
> streaming output terminals.
>
> The UVC specification requires unit and terminal IDs to be unique, and
> uses the ID to reference entities:
>
> - In control requests, to identify the target entity
> - In the UVC units and terminals descriptors' bSourceID field, to
> identify source entities
> - In the UVC input header descriptor's bTerminalLink, to identify the
> terminal associated with a streaming interface
>
> Entity ID collisions break accessing controls and make the graph
> description in the UVC descriptors ambiguous. However, collisions where
> one of the entities is a streaming output terminal and the other entity
> is not a streaming terminal are less severe. Streaming output terminals
> have no controls, and, as they are the final entity in pipelines, they
> are never referenced in descriptors as source entities. They are
> referenced by ID only from innput header descriptors, which by
> definition only reference streaming terminals.
>
> For these reasons, we can work around the collision by giving streaming
> output terminals their own ID namespace. Do so by setting bit
> UVC_TERM_OUTPUT (15) in the uvc_entity.id field, which is normally never
> set as the ID is a 8-bit value.
>
> This ID change doesn't affect the entity name in the media controller
> graph as the name isn't constructed from the ID, so there should not be
> any impact on the uAPI.
>
> Although this change handles some ID collisions automagically, keep
> printing an error in uvc_alloc_new_entity() when a camera has invalid
> descriptors. Hopefully this message will help vendors fix their invalid
> descriptors.
>
> This new method of handling ID collisions includes a revert of commit
> 758dbc756aad ("media: uvcvideo: Use heuristic to find stream entity")
> that attempted to fix the problem urgently due to regression reports.
>
> Suggested-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Ricardo Ribalda <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Tested-by: Lili Orosz <[email protected]>
> Co-developed-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Laurent Pinchart <[email protected]>
> Link:
> https://patch.msgid.link/[email protected]
> Signed-off-by: Hans Verkuil <[email protected]>
Patch committed.
Thanks,
Hans Verkuil
drivers/media/usb/uvc/uvc_driver.c | 54 +++++++++++++++++++++++---------------
drivers/media/usb/uvc/uvcvideo.h | 3 ++-
2 files changed, 35 insertions(+), 22 deletions(-)
---
diff --git a/drivers/media/usb/uvc/uvc_driver.c
b/drivers/media/usb/uvc/uvc_driver.c
index ee4f54d68349..aa3e8d295e0f 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -165,28 +165,17 @@ static struct uvc_entity *uvc_entity_by_reference(struct
uvc_device *dev,
return NULL;
}
-static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+static struct uvc_streaming *uvc_stream_for_terminal(struct uvc_device *dev,
+ struct uvc_entity *term)
{
- struct uvc_streaming *stream, *last_stream;
- unsigned int count = 0;
+ u16 id = UVC_HARDWARE_ENTITY_ID(term->id);
+ struct uvc_streaming *stream;
list_for_each_entry(stream, &dev->streams, list) {
- count += 1;
- last_stream = stream;
if (stream->header.bTerminalLink == id)
return stream;
}
- /*
- * If the streaming entity is referenced by an invalid ID, notify the
- * user and use heuristics to guess the correct entity.
- */
- if (count == 1 && id == UVC_INVALID_ENTITY_ID) {
- dev_warn(&dev->intf->dev,
- "UVC non compliance: Invalid USB header. The streaming
entity has an invalid ID, guessing the correct one.");
- return last_stream;
- }
-
return NULL;
}
@@ -823,10 +812,12 @@ static struct uvc_entity *uvc_alloc_new_entity(struct
uvc_device *dev, u16 type,
}
/* Per UVC 1.1+ spec 3.7.2, the ID is unique. */
- if (uvc_entity_by_id(dev, id)) {
- dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n",
id);
+ if (uvc_entity_by_id(dev, UVC_HARDWARE_ENTITY_ID(id)))
+ dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n",
+ UVC_HARDWARE_ENTITY_ID(id));
+
+ if (uvc_entity_by_id(dev, id))
id = UVC_INVALID_ENTITY_ID;
- }
extra_size = roundup(extra_size, sizeof(*entity->pads));
if (num_pads)
@@ -982,6 +973,7 @@ static int uvc_parse_standard_control(struct uvc_device
*dev,
struct usb_host_interface *alts = dev->intf->cur_altsetting;
unsigned int i, n, p, len;
const char *type_name;
+ unsigned int id;
u16 type;
switch (buffer[2]) {
@@ -1120,8 +1112,28 @@ static int uvc_parse_standard_control(struct uvc_device
*dev,
return 0;
}
+ id = buffer[3];
+
+ /*
+ * Some devices, such as the Grandstream GUV3100, exhibit entity
+ * ID collisions between units and streaming output terminals.
+ * Move streaming output terminals to their own ID namespace by
+ * setting bit UVC_TERM_OUTPUT (15), above the ID's 8-bit value.
+ * The bit is ignored in uvc_stream_for_terminal() when looking
+ * up the streaming interface for the terminal.
+ *
+ * This hack is safe to enable unconditionally, as the ID is not
+ * used for any other purpose (streaming output terminals have
+ * no controls and are never referenced as sources in UVC
+ * descriptors). Other types output terminals can have controls,
+ * so limit usage of this separate namespace to streaming output
+ * terminals.
+ */
+ if (type & UVC_TT_STREAMING)
+ id |= UVC_TERM_OUTPUT;
+
term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT,
- buffer[3], 1, 0);
+ id, 1, 0);
if (IS_ERR(term))
return PTR_ERR(term);
@@ -2118,8 +2130,8 @@ static int uvc_register_terms(struct uvc_device *dev,
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;
- stream = uvc_stream_by_id(dev, term->id);
- if (stream == NULL) {
+ stream = uvc_stream_for_terminal(dev, term);
+ if (!stream) {
dev_info(&dev->intf->dev,
"No streaming interface found for terminal
%u.",
term->id);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index d583425893a5..8480d65ecb85 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -41,7 +41,8 @@
#define UVC_EXT_GPIO_UNIT 0x7ffe
#define UVC_EXT_GPIO_UNIT_ID 0x100
-#define UVC_INVALID_ENTITY_ID 0xffff
+#define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff)
+#define UVC_INVALID_ENTITY_ID 0xffff
/* ------------------------------------------------------------------------
* Driver specific constants.
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]