diff options
author | Marcus Glocker <mglocker@cvs.openbsd.org> | 2008-07-10 04:49:13 +0000 |
---|---|---|
committer | Marcus Glocker <mglocker@cvs.openbsd.org> | 2008-07-10 04:49:13 +0000 |
commit | a3adbba510bb615e50beb6ea4ef173d0a81ee8ce (patch) | |
tree | 90ade3cdad0ec43a33d63c0e069435aa7fb5dac0 /sys | |
parent | c942e12914a46a641ebd3cce5d060557232e2b6c (diff) |
Make cams with Sonix chipset work by avoiding excessive alternate
interface switching, which kind of crashed the device.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/uvideo.c | 128 |
1 files changed, 75 insertions, 53 deletions
diff --git a/sys/dev/usb/uvideo.c b/sys/dev/usb/uvideo.c index 7be0065cae5..330cfc2bcfa 100644 --- a/sys/dev/usb/uvideo.c +++ b/sys/dev/usb/uvideo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.c,v 1.48 2008/07/07 18:07:51 mglocker Exp $ */ +/* $OpenBSD: uvideo.c,v 1.49 2008/07/10 04:49:12 mglocker Exp $ */ /* * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> @@ -89,7 +89,7 @@ int uvideo_vs_set_alt(struct uvideo_softc *, usbd_interface_handle, int); int uvideo_desc_len(const usb_descriptor_t *, int, int, int, int); -usbd_status uvideo_vs_negotation(struct uvideo_softc *); +usbd_status uvideo_vs_negotation(struct uvideo_softc *, int); usbd_status uvideo_vs_set_probe(struct uvideo_softc *, uint8_t *); usbd_status uvideo_vs_get_probe(struct uvideo_softc *, uint8_t *); usbd_status uvideo_vs_set_commit(struct uvideo_softc *, uint8_t *); @@ -359,8 +359,13 @@ uvideo_attach(struct device *parent, struct device *self, void *aux) if (error != USBD_NORMAL_COMPLETION) return; - /* do device negotation */ - error = uvideo_vs_negotation(sc); + /* set default video stream interface */ + error = usbd_set_interface(sc->sc_vs_curr->ifaceh, 0); + if (error != USBD_NORMAL_COMPLETION) + return; + + /* do device negotation without commit */ + error = uvideo_vs_negotation(sc, 0); if (error != USBD_NORMAL_COMPLETION) return; @@ -709,37 +714,38 @@ uvideo_vs_parse_desc_alt(struct uvideo_softc *sc, struct usb_attach_arg *uaa, int vs_nr, int iface, int numalts) { struct uvideo_vs_iface *vs; + usbd_desc_iter_t iter; + const usb_descriptor_t *desc; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; - int i; - usbd_status error; vs = &sc->sc_vs_coll[vs_nr]; - for (i = 0; i < numalts; i++) { - error = usbd_set_interface(uaa->ifaces[iface], i); - if (error) { - printf("%s: could not set alternate interface %d!\n", - DEVNAME(sc), i); - return (USBD_INVAL); - } - - id = usbd_get_interface_descriptor(uaa->ifaces[iface]); - if (id == NULL) - continue; - + usb_desc_iter_init(sc->sc_udev, &iter); + desc = usb_desc_iter_next(&iter); + while (desc) { + /* find video stream interface */ + if (desc->bDescriptorType != UDESC_INTERFACE) + goto next; + id = (usb_interface_descriptor_t *)(uint8_t *)desc; + if (id->bInterfaceNumber != iface) + goto next; DPRINTF(1, "%s: bAlternateSetting=0x%02x, ", DEVNAME(sc), id->bAlternateSetting); - - ed = usbd_interface2endpoint_descriptor(uaa->ifaces[iface], 0); - if (ed == NULL) { + if (id->bNumEndpoints == 0) { DPRINTF(1, "no endpoint descriptor\n"); - continue; + goto next; } + /* jump to corresponding endpoint descriptor */ + desc = usb_desc_iter_next(&iter); + if (desc->bDescriptorType != UDESC_ENDPOINT) + goto next; + ed = (usb_endpoint_descriptor_t *)(uint8_t *)desc; DPRINTF(1, "bEndpointAddress=0x%02x, ", ed->bEndpointAddress); DPRINTF(1, "wMaxPacketSize=%d\n", UGETW(ed->wMaxPacketSize)); + /* save endpoint with largest bandwidth */ if (UGETW(ed->wMaxPacketSize) > vs->max_packet_size) { vs->ifaceh = uaa->ifaces[iface]; vs->endpoint = ed->bEndpointAddress; @@ -748,14 +754,8 @@ uvideo_vs_parse_desc_alt(struct uvideo_softc *sc, struct usb_attach_arg *uaa, vs->max_packet_size = UGETW(ed->wMaxPacketSize); vs->iface = iface; } - } - - /* switch back to default interface, otherwise negotation could fail */ - error = usbd_set_interface(uaa->ifaces[iface], 0); - if (error) { - printf("%s: could not set alternate interface 0!\n", - DEVNAME(sc)); - return (USBD_INVAL); +next: + desc = usb_desc_iter_next(&iter); } return (USBD_NORMAL_COMPLETION); @@ -765,42 +765,57 @@ int uvideo_vs_set_alt(struct uvideo_softc *sc, usbd_interface_handle ifaceh, int max_packet_size) { + usbd_desc_iter_t iter; + const usb_descriptor_t *desc; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; usbd_status error; - for (i = 0; i < sc->sc_vs_curr->numalts; i++) { - error = usbd_set_interface(ifaceh, i); - if (error) { - printf("%s: could not set alternate interface %d!\n", - DEVNAME(sc), i); - return (USBD_INVAL); - } - - id = usbd_get_interface_descriptor(ifaceh); - if (id == NULL) - continue; - - ed = usbd_interface2endpoint_descriptor(ifaceh, 0); - if (ed == NULL) - continue; + i = 0; + usb_desc_iter_init(sc->sc_udev, &iter); + desc = usb_desc_iter_next(&iter); + while (desc) { + /* find video stream interface */ + if (desc->bDescriptorType != UDESC_INTERFACE) + goto next; + id = (usb_interface_descriptor_t *)(uint8_t *)desc; + if (id->bInterfaceNumber != sc->sc_vs_curr->iface) + goto next; + if (id->bNumEndpoints == 0) + goto next; + + /* jump to corresponding endpoint descriptor */ + desc = usb_desc_iter_next(&iter); + if (desc->bDescriptorType != UDESC_ENDPOINT) + goto next; + ed = (usb_endpoint_descriptor_t *)(uint8_t *)desc; + i++; + /* save endpoint with requested bandwidth */ if (UGETW(ed->wMaxPacketSize) == max_packet_size) { sc->sc_vs_curr->endpoint = ed->bEndpointAddress; sc->sc_vs_curr->curalt = id->bAlternateSetting; sc->sc_vs_curr->max_packet_size = UGETW(ed->wMaxPacketSize); - DPRINTF(1, "%s: set alternate iface to ", DEVNAME(sc)); DPRINTF(1, "bAlternateSetting=0x%02x\n", id->bAlternateSetting); - - return (USBD_NORMAL_COMPLETION); + break; } +next: + desc = usb_desc_iter_next(&iter); } - return (USBD_INVAL); + /* set alternate video stream interface */ + error = usbd_set_interface(ifaceh, i); + if (error) { + printf("%s: could not set alternate interface %d!\n", + DEVNAME(sc), i); + return (USBD_INVAL); + } + + return (USBD_NORMAL_COMPLETION); } /* @@ -840,7 +855,7 @@ uvideo_desc_len(const usb_descriptor_t *desc, } usbd_status -uvideo_vs_negotation(struct uvideo_softc *sc) +uvideo_vs_negotation(struct uvideo_softc *sc, int commit) { struct usb_video_probe_commit *pc; uint8_t probe_data[34]; @@ -872,9 +887,11 @@ uvideo_vs_negotation(struct uvideo_softc *sc) return (error); /* commit */ - error = uvideo_vs_set_commit(sc, probe_data); - if (error != USBD_NORMAL_COMPLETION) - return (error); + if (commit) { + error = uvideo_vs_set_commit(sc, probe_data); + if (error != USBD_NORMAL_COMPLETION) + return (error); + } /* save a copy of probe commit */ bcopy(pc, &sc->sc_desc_probe, sizeof(sc->sc_desc_probe)); @@ -1093,6 +1110,11 @@ uvideo_vs_open(struct uvideo_softc *sc) DPRINTF(1, "%s: %s\n", DEVNAME(sc), __func__); + /* do device negotation with commit */ + error = uvideo_vs_negotation(sc, 1); + if (error != USBD_NORMAL_COMPLETION) + return (error); + error = uvideo_vs_set_alt(sc, sc->sc_vs_curr->ifaceh, UGETDW(sc->sc_desc_probe.dwMaxPayloadTransferSize)); if (error != USBD_NORMAL_COMPLETION) { |