Skip to content
/ linux Public

Commit 37914ce

Browse files
ribaldaSasha Levin
authored andcommitted
media: uvcvideo: Create an ID namespace for streaming output terminals
[ Upstream commit 3d9f32e ] 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 758dbc7 ("media: uvcvideo: Use heuristic to find stream entity") that attempted to fix the problem urgently due to regression reports. Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Lili Orosz <lily@floofy.city> Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: https://patch.msgid.link/20251113210400.28618-1-laurent.pinchart@ideasonboard.com Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 8829746 commit 37914ce

File tree

2 files changed

+35
-22
lines changed

2 files changed

+35
-22
lines changed

drivers/media/usb/uvc/uvc_driver.c

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -165,28 +165,17 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
165165
return NULL;
166166
}
167167

168-
static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
168+
static struct uvc_streaming *uvc_stream_for_terminal(struct uvc_device *dev,
169+
struct uvc_entity *term)
169170
{
170-
struct uvc_streaming *stream, *last_stream;
171-
unsigned int count = 0;
171+
u16 id = UVC_HARDWARE_ENTITY_ID(term->id);
172+
struct uvc_streaming *stream;
172173

173174
list_for_each_entry(stream, &dev->streams, list) {
174-
count += 1;
175-
last_stream = stream;
176175
if (stream->header.bTerminalLink == id)
177176
return stream;
178177
}
179178

180-
/*
181-
* If the streaming entity is referenced by an invalid ID, notify the
182-
* user and use heuristics to guess the correct entity.
183-
*/
184-
if (count == 1 && id == UVC_INVALID_ENTITY_ID) {
185-
dev_warn(&dev->intf->dev,
186-
"UVC non compliance: Invalid USB header. The streaming entity has an invalid ID, guessing the correct one.");
187-
return last_stream;
188-
}
189-
190179
return NULL;
191180
}
192181

@@ -823,10 +812,12 @@ static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
823812
}
824813

825814
/* Per UVC 1.1+ spec 3.7.2, the ID is unique. */
826-
if (uvc_entity_by_id(dev, id)) {
827-
dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", id);
815+
if (uvc_entity_by_id(dev, UVC_HARDWARE_ENTITY_ID(id)))
816+
dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n",
817+
UVC_HARDWARE_ENTITY_ID(id));
818+
819+
if (uvc_entity_by_id(dev, id))
828820
id = UVC_INVALID_ENTITY_ID;
829-
}
830821

831822
extra_size = roundup(extra_size, sizeof(*entity->pads));
832823
if (num_pads)
@@ -982,6 +973,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
982973
struct usb_host_interface *alts = dev->intf->cur_altsetting;
983974
unsigned int i, n, p, len;
984975
const char *type_name;
976+
unsigned int id;
985977
u16 type;
986978

987979
switch (buffer[2]) {
@@ -1120,8 +1112,28 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
11201112
return 0;
11211113
}
11221114

1115+
id = buffer[3];
1116+
1117+
/*
1118+
* Some devices, such as the Grandstream GUV3100, exhibit entity
1119+
* ID collisions between units and streaming output terminals.
1120+
* Move streaming output terminals to their own ID namespace by
1121+
* setting bit UVC_TERM_OUTPUT (15), above the ID's 8-bit value.
1122+
* The bit is ignored in uvc_stream_for_terminal() when looking
1123+
* up the streaming interface for the terminal.
1124+
*
1125+
* This hack is safe to enable unconditionally, as the ID is not
1126+
* used for any other purpose (streaming output terminals have
1127+
* no controls and are never referenced as sources in UVC
1128+
* descriptors). Other types output terminals can have controls,
1129+
* so limit usage of this separate namespace to streaming output
1130+
* terminals.
1131+
*/
1132+
if (type & UVC_TT_STREAMING)
1133+
id |= UVC_TERM_OUTPUT;
1134+
11231135
term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT,
1124-
buffer[3], 1, 0);
1136+
id, 1, 0);
11251137
if (IS_ERR(term))
11261138
return PTR_ERR(term);
11271139

@@ -2118,8 +2130,8 @@ static int uvc_register_terms(struct uvc_device *dev,
21182130
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
21192131
continue;
21202132

2121-
stream = uvc_stream_by_id(dev, term->id);
2122-
if (stream == NULL) {
2133+
stream = uvc_stream_for_terminal(dev, term);
2134+
if (!stream) {
21232135
dev_info(&dev->intf->dev,
21242136
"No streaming interface found for terminal %u.",
21252137
term->id);

drivers/media/usb/uvc/uvcvideo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
#define UVC_EXT_GPIO_UNIT 0x7ffe
4242
#define UVC_EXT_GPIO_UNIT_ID 0x100
4343

44-
#define UVC_INVALID_ENTITY_ID 0xffff
44+
#define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff)
45+
#define UVC_INVALID_ENTITY_ID 0xffff
4546

4647
/* ------------------------------------------------------------------------
4748
* Driver specific constants.

0 commit comments

Comments
 (0)