Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

media: uvcvideo: Create an ID namespace for streaming output terminals

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 <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>

authored by

Ricardo Ribalda and committed by
Hans Verkuil
3d9f32e0 e3b5b77e

+35 -22
+33 -21
drivers/media/usb/uvc/uvc_driver.c
··· 165 165 return NULL; 166 166 } 167 167 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) 169 170 { 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; 172 173 173 174 list_for_each_entry(stream, &dev->streams, list) { 174 - count += 1; 175 - last_stream = stream; 176 175 if (stream->header.bTerminalLink == id) 177 176 return stream; 178 - } 179 - 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 177 } 189 178 190 179 return NULL; ··· 812 823 } 813 824 814 825 /* Per UVC 1.1+ spec 3.7.2, the ID is unique. */ 815 - if (uvc_entity_by_id(dev, id)) { 816 - dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", id); 826 + if (uvc_entity_by_id(dev, UVC_HARDWARE_ENTITY_ID(id))) 827 + dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", 828 + UVC_HARDWARE_ENTITY_ID(id)); 829 + 830 + if (uvc_entity_by_id(dev, id)) 817 831 id = UVC_INVALID_ENTITY_ID; 818 - } 819 832 820 833 extra_size = roundup(extra_size, sizeof(*entity->pads)); 821 834 if (num_pads) ··· 973 982 struct usb_host_interface *alts = dev->intf->cur_altsetting; 974 983 unsigned int i, n, p, len; 975 984 const char *type_name; 985 + unsigned int id; 976 986 u16 type; 977 987 978 988 switch (buffer[2]) { ··· 1112 1120 return 0; 1113 1121 } 1114 1122 1123 + id = buffer[3]; 1124 + 1125 + /* 1126 + * Some devices, such as the Grandstream GUV3100, exhibit entity 1127 + * ID collisions between units and streaming output terminals. 1128 + * Move streaming output terminals to their own ID namespace by 1129 + * setting bit UVC_TERM_OUTPUT (15), above the ID's 8-bit value. 1130 + * The bit is ignored in uvc_stream_for_terminal() when looking 1131 + * up the streaming interface for the terminal. 1132 + * 1133 + * This hack is safe to enable unconditionally, as the ID is not 1134 + * used for any other purpose (streaming output terminals have 1135 + * no controls and are never referenced as sources in UVC 1136 + * descriptors). Other types output terminals can have controls, 1137 + * so limit usage of this separate namespace to streaming output 1138 + * terminals. 1139 + */ 1140 + if (type & UVC_TT_STREAMING) 1141 + id |= UVC_TERM_OUTPUT; 1142 + 1115 1143 term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT, 1116 - buffer[3], 1, 0); 1144 + id, 1, 0); 1117 1145 if (IS_ERR(term)) 1118 1146 return PTR_ERR(term); 1119 1147 ··· 2130 2118 if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 2131 2119 continue; 2132 2120 2133 - stream = uvc_stream_by_id(dev, term->id); 2134 - if (stream == NULL) { 2121 + stream = uvc_stream_for_terminal(dev, term); 2122 + if (!stream) { 2135 2123 dev_info(&dev->intf->dev, 2136 2124 "No streaming interface found for terminal %u.", 2137 2125 term->id);
+2 -1
drivers/media/usb/uvc/uvcvideo.h
··· 41 41 #define UVC_EXT_GPIO_UNIT 0x7ffe 42 42 #define UVC_EXT_GPIO_UNIT_ID 0x100 43 43 44 - #define UVC_INVALID_ENTITY_ID 0xffff 44 + #define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff) 45 + #define UVC_INVALID_ENTITY_ID 0xffff 45 46 46 47 /* ------------------------------------------------------------------------ 47 48 * Driver specific constants.