diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2019-08-07 11:16:03 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2019-08-07 11:16:03 +0000 |
commit | e3f6ec995096c10ff93830ce635c6a597e289ba3 (patch) | |
tree | e0537707297787ac2dd0217b0e5e47ca9f93e2bf /sys | |
parent | c8e115e4c4779b69c6e2622c0c5f34841d630671 (diff) |
Add support for integrated USB cameras with two functions, as
seen on a Lenovo X395 which combines normal webcam functionality
with another IR camera, by parsing the USB interface association
descriptors. This fixes the symptom of uvideo(4) complaining
about too many headers and not providing any functionality at all.
ok jan@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/uvideo.c | 135 |
1 files changed, 126 insertions, 9 deletions
diff --git a/sys/dev/usb/uvideo.c b/sys/dev/usb/uvideo.c index c90c563d569..a3652d1b164 100644 --- a/sys/dev/usb/uvideo.c +++ b/sys/dev/usb/uvideo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.c,v 1.202 2019/08/07 11:14:16 patrick Exp $ */ +/* $OpenBSD: uvideo.c,v 1.203 2019/08/07 11:16:02 patrick Exp $ */ /* * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> @@ -61,8 +61,8 @@ int uvideo_debug = 1; struct uvideo_softc { struct device sc_dev; struct usbd_device *sc_udev; + int sc_iface; int sc_nifaces; - struct usbd_interface **sc_ifaces; struct device *sc_videodev; @@ -503,25 +503,67 @@ uvideo_attach(struct device *parent, struct device *self, void *aux) { struct uvideo_softc *sc = (struct uvideo_softc *)self; struct usb_attach_arg *uaa = aux; + usb_interface_assoc_descriptor_t *iad; usb_interface_descriptor_t *id; + const usb_descriptor_t *desc; + struct usbd_desc_iter iter; int i; sc->sc_udev = uaa->device; - sc->sc_nifaces = uaa->nifaces; - /* - * Claim all video interfaces. Interfaces must be claimed during - * attach, during attach hooks is too late. - */ - for (i = 0; i < sc->sc_nifaces; i++) { + + /* Find the first unclaimed video interface. */ + for (i = 0; i < uaa->nifaces; i++) { if (usbd_iface_claimed(sc->sc_udev, i)) continue; id = usbd_get_interface_descriptor(&sc->sc_udev->ifaces[i]); if (id == NULL) continue; if (id->bInterfaceClass == UICLASS_VIDEO) - usbd_claim_iface(sc->sc_udev, i); + break; + } + if (i == uaa->nifaces) { + printf("%s: can't find video interface\n", DEVNAME(sc)); + return; + } + + /* Find out which interface association we belong to. */ + usbd_desc_iter_init(sc->sc_udev, &iter); + desc = usbd_desc_iter_next(&iter); + while (desc) { + if (desc->bDescriptorType != UDESC_IFACE_ASSOC) { + desc = usbd_desc_iter_next(&iter); + continue; + } + iad = (usb_interface_assoc_descriptor_t *)desc; + if (i >= iad->bFirstInterface && + i < iad->bFirstInterface + iad->bInterfaceCount) + break; + desc = usbd_desc_iter_next(&iter); + } + if (desc == NULL) { + printf("%s: can't find interface assoc descriptor\n", + DEVNAME(sc)); + return; } + /* + * Claim all interfaces of our association. Interfaces must be + * claimed during attach, during attach hooks is too late. + */ + for (i = iad->bFirstInterface; + i < iad->bFirstInterface + iad->bInterfaceCount; i++) { + if (usbd_iface_claimed(sc->sc_udev, i)) { + printf("%s: interface already claimed\n", + DEVNAME(sc)); + return; + } + usbd_claim_iface(sc->sc_udev, i); + } + + /* Remember our association by saving the first interface. */ + sc->sc_iface = iad->bFirstInterface; + sc->sc_nifaces = iad->bInterfaceCount; + /* maybe the device has quirks */ sc->sc_quirk = uvideo_lookup(uaa->vendor, uaa->product); @@ -615,6 +657,7 @@ uvideo_vc_parse_desc(struct uvideo_softc *sc) { struct usbd_desc_iter iter; const usb_descriptor_t *desc; + usb_interface_descriptor_t *id; int vc_header_found; usbd_status error; @@ -625,6 +668,18 @@ uvideo_vc_parse_desc(struct uvideo_softc *sc) usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; if (desc->bDescriptorType != UDESC_CS_INTERFACE) { desc = usbd_desc_iter_next(&iter); continue; @@ -827,6 +882,18 @@ uvideo_vs_parse_desc(struct uvideo_softc *sc, usb_config_descriptor_t *cdesc) usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; if (desc->bDescriptorType != UDESC_CS_INTERFACE) { desc = usbd_desc_iter_next(&iter); continue; @@ -911,12 +978,25 @@ uvideo_vs_parse_desc_format(struct uvideo_softc *sc) { struct usbd_desc_iter iter; const usb_descriptor_t *desc; + usb_interface_descriptor_t *id; DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; if (desc->bDescriptorType != UDESC_CS_INTERFACE) { desc = usbd_desc_iter_next(&iter); continue; @@ -1053,6 +1133,7 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc) { struct usbd_desc_iter iter; const usb_descriptor_t *desc; + usb_interface_descriptor_t *id; usbd_status error; DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); @@ -1060,6 +1141,18 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc) usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; if (desc->bDescriptorType == UDESC_CS_INTERFACE && desc->bLength > sizeof(struct usb_video_frame_desc) && (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG || @@ -1147,6 +1240,18 @@ uvideo_vs_parse_desc_alt(struct uvideo_softc *sc, int vs_nr, int iface, int numa usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; /* find video stream interface */ if (desc->bDescriptorType != UDESC_INTERFACE) goto next; @@ -1217,6 +1322,18 @@ uvideo_vs_set_alt(struct uvideo_softc *sc, struct usbd_interface *ifaceh, usbd_desc_iter_init(sc->sc_udev, &iter); desc = usbd_desc_iter_next(&iter); while (desc) { + /* Skip all interfaces until we found our first. */ + if (desc->bDescriptorType == UDESC_INTERFACE) { + id = (usb_interface_descriptor_t *)desc; + if (id->bInterfaceNumber == sc->sc_iface) + break; + } + desc = usbd_desc_iter_next(&iter); + } + while (desc) { + /* Crossed device function boundary. */ + if (desc->bDescriptorType == UDESC_IFACE_ASSOC) + break; /* find video stream interface */ if (desc->bDescriptorType != UDESC_INTERFACE) goto next; |