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;
}
}
/* 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)
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]) {
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);
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);