On 13/08/15(Thu) 19:41, ludovic coues wrote:
> 2015-08-13 15:11 GMT+02:00 Martin Pieuchot <m...@openbsd.org>:
> >
> > With this diff I get the following error on my x220:
> >
> > uvideo_vs_negotiation: uvideo0: no frame descriptors found!
> >
> > I have this camera:
> >
> > uvideo0 at uhub3 port 6 configuration 1 interface 0 "Chicony Electronics
> > Co., Ltd. Integrated Camera" rev 2.00/8.54 addr 3
> >
> > And the descriptor dump:
> >
> 
> I haven't any clue yet on why that happen.
> But that dump show that the diff is wrong. I have assumed bControlSize
> to be always 3 for usb_video_input_terminal_desc and
> usb_video_vc_processing_desc, as it's a constant in the spec 1.5 .
> Obviously, that the value can be less than 3 with device following the
> spec 1.00
> 

I found the problem with the patch. Somehow, I forget to update some
section of the code to work with the new structure. More exactly, the
struct usb_video_frame_desc have gained 3 byte but the underlying data
can be 2 byte shorter than structure size if the device have fixed frame
rate. The size of the data was often compared to the size of the
structure. When updating the code, most of the test have been adjusted
by I missed a couple of them. One of these missed test is at line 1076
revision 1.181. As the test returned false for usb video device
returning a discrete range of less than 3 frame rate, the driver didn't
parse frame descriptor in these case.

Following patch should work better.
I made 2 patch this time. First patch if for the merged struct, second
is for usb_video_frame_desc.
Index: sys/dev/usb/uvideo.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.c,v
retrieving revision 1.181
diff -u -p -r1.181 uvideo.c
--- sys/dev/usb/uvideo.c        9 Jul 2015 14:58:32 -0000       1.181
+++ sys/dev/usb/uvideo.c        23 Jul 2015 14:31:18 -0000
@@ -84,8 +84,8 @@ struct uvideo_softc {
 
        int                                      sc_nframes;
        struct usb_video_probe_commit            sc_desc_probe;
-       struct usb_video_header_desc_all         sc_desc_vc_header;
-       struct usb_video_input_header_desc_all   sc_desc_vs_input_header;
+       struct usb_video_header_desc            *sc_desc_vc_header;
+       struct usb_video_input_header_desc      *sc_desc_vs_input_header;
 
 #define UVIDEO_MAX_PU                           8
        int                                      sc_desc_vc_pu_num;
@@ -694,16 +693,15 @@ uvideo_vc_parse_desc_header(struct uvide
 {
        struct usb_video_header_desc *d;
 
-       d = (struct usb_video_header_desc *)(uint8_t *)desc;
+       d = (struct usb_video_header_desc *)desc;
 
        if (d->bInCollection == 0) {
                printf("%s: no VS interface found!\n",
                    DEVNAME(sc));
                return (USBD_INVAL);
        }
-       
-       sc->sc_desc_vc_header.fix = d;
-       sc->sc_desc_vc_header.baInterfaceNr = (uByte *)(d + 1);
+
+       sc->sc_desc_vc_header = d;
 
        return (USBD_NORMAL_COMPLETION);
 }
@@ -838,7 +836,7 @@ uvideo_vs_parse_desc(struct uvideo_softc
        DPRINTF(1, "%s: number of total interfaces=%d\n",
            DEVNAME(sc), sc->sc_nifaces);
        DPRINTF(1, "%s: number of VS interfaces=%d\n",
-           DEVNAME(sc), sc->sc_desc_vc_header.fix->bInCollection);
+           DEVNAME(sc), sc->sc_desc_vc_header->bInCollection);
 
        usbd_desc_iter_init(sc->sc_udev, &iter);
        desc = usbd_desc_iter_next(&iter);
@@ -874,8 +872,8 @@ uvideo_vs_parse_desc(struct uvideo_softc
                return (error);
 
        /* parse interface collection */
-       for (i = 0; i < sc->sc_desc_vc_header.fix->bInCollection; i++) {
-               iface = sc->sc_desc_vc_header.baInterfaceNr[i];
+       for (i = 0; i < sc->sc_desc_vc_header->bInCollection; i++) {
+               iface = sc->sc_desc_vc_header->baInterfaceNr[i];
 
                id = usbd_get_interface_descriptor(&sc->sc_udev->ifaces[iface]);
                if (id == NULL) {
@@ -916,8 +914,7 @@ uvideo_vs_parse_desc_input_header(struct
                return (USBD_INVAL);
        }
 
-       sc->sc_desc_vs_input_header.fix = d;
-       sc->sc_desc_vs_input_header.bmaControls = (uByte *)(d + 1);
+       sc->sc_desc_vs_input_header = d;
 
        return (USBD_NORMAL_COMPLETION);
 }
@@ -1500,12 +1498,12 @@ uvideo_vs_negotiation(struct uvideo_soft
                 * Some UVC 1.00 devices return dwMaxVideoFrameSize = 0.
                 * If so, fix it by format/frame descriptors.
                 */
-               hd = sc->sc_desc_vc_header.fix;
+               hd = sc->sc_desc_vc_header;
                if (UGETDW(pc->dwMaxVideoFrameSize) == 0 &&
                    UGETW(hd->bcdUVC) < 0x0110 ) {
                        DPRINTF(1, "%s: dwMaxVideoFrameSize == 0, fixed\n",
                            DEVNAME(sc));
-                       USETDW(pc->dwMaxVideoFrameSize, 
+                       USETDW(pc->dwMaxVideoFrameSize,
                            UGETDW(frame->dwMaxVideoFrameBufferSize));
                }
        }
 
Index: sys/dev/usb/uvideo.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.h,v
retrieving revision 1.57
diff -u -p -r1.57 uvideo.h
--- sys/dev/usb/uvideo.h        9 Jul 2015 14:58:32 -0000       1.57
+++ sys/dev/usb/uvideo.h        23 Jul 2015 14:31:18 -0000
@@ -162,13 +162,9 @@ struct usb_video_header_desc {
        uWord   wTotalLength;
        uDWord  dwClockFrequency;
        uByte   bInCollection;
+       uByte   baInterfaceNr[1];
 } __packed;
 
-struct usb_video_header_desc_all {
-       struct usb_video_header_desc    *fix;
-       uByte                           *baInterfaceNr;
-};
-
 /* Table 3-4: Input Terminal Descriptor */
 struct usb_video_input_terminal_desc {
        uByte   bLength;
@@ -255,13 +251,9 @@ struct usb_video_input_header_desc {
        uByte   bTriggerSupport;
        uByte   bTriggerUsage;
        uByte   bControlSize;
+       uByte   bmaControls[1];
 } __packed;
 
-struct usb_video_input_header_desc_all {
-       struct usb_video_input_header_desc      *fix;
-       uByte                                   *bmaControls;
-};
-
 /* Table 3-18: Color Matching Descriptor */
 struct usb_video_color_matching_descr {
        uByte   bLength;
Index: sys/dev/usb/uvideo.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.c,v
retrieving revision 1.181
diff -u -p -r1.181 uvideo.c
--- sys/dev/usb/uvideo.c        9 Jul 2015 14:58:32 -0000       1.181
+++ sys/dev/usb/uvideo.c        15 Aug 2015 09:59:29 -0000
@@ -668,8 +668,7 @@ uvideo_vc_parse_desc(struct uvideo_softc
                        break;
                case UDESCSUB_VC_PROCESSING_UNIT:
                        /* XXX do correct length calculation */
-                       if (desc->bLength <
-                           sizeof(struct usb_video_frame_desc)) {
+                       if (desc->bLength < UVIDEO_FRAME_DESC_LENGTH) {
                                (void)uvideo_vc_parse_desc_pu(sc, desc);
                        }
                        break;
@@ -1073,9 +1070,10 @@ uvideo_vs_parse_desc_frame(struct uvideo
        desc = usbd_desc_iter_next(&iter);
        while (desc) {
                if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
-                   desc->bLength > sizeof(struct usb_video_frame_desc) &&
+                   desc->bLength > UVIDEO_FRAME_DESC_LENGTH &&
                    (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ||
-                   desc->bDescriptorSubtype == 
UDESCSUB_VS_FRAME_UNCOMPRESSED)) {
+                   desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED)
+                   ) {
                        error = uvideo_vs_parse_desc_frame_sub(sc, desc);
                        if (error != USBD_NORMAL_COMPLETION)
                                return (error);
@@ -1090,8 +1088,8 @@ usbd_status
 uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *sc,
     const usb_descriptor_t *desc)
 {
-       struct usb_video_frame_desc *fd = 
-           (struct usb_video_frame_desc *)(uint8_t *)desc;
+       /* Descriptor is variable sized */
+       struct usb_video_frame_desc *fd = (void *)desc;
        int fmtidx, frame_num;
        uint32_t fbuf_size;
 
@@ -1356,11 +1354,10 @@ uvideo_vs_negotiation(struct uvideo_soft
        struct uvideo_format_group *fmtgrp;
        struct usb_video_header_desc *hd;
        struct usb_video_frame_desc *frame;
-       uint8_t *p, *cur;
        uint8_t probe_data[34];
        uint32_t frame_ival, nivals, min, max, step, diff;
        usbd_status error;
-       int i, ival_bytes, changed = 0;
+       int i, cur, ival_bytes, changed = 0;
 
        pc = (struct usb_video_probe_commit *)probe_data;
 
@@ -1384,19 +1381,15 @@ uvideo_vs_negotiation(struct uvideo_soft
        if (sc->sc_frame_rate != 0) {
                frame_ival = 10000000 / sc->sc_frame_rate;
                /* find closest matching interval the device supports */
-               p = (uint8_t *)fmtgrp->frame_cur;
-               p += sizeof(struct usb_video_frame_desc);
+               frame = fmtgrp->frame_cur;
                nivals = fmtgrp->frame_cur->bFrameIntervalType;
                ival_bytes = fmtgrp->frame_cur->bLength -
-                   sizeof(struct usb_video_frame_desc);
+                   UVIDEO_FRAME_DESC_LENGTH;
                if (!nivals && (ival_bytes >= sizeof(uDWord) * 3)) {
                        /* continuous */
-                       min = UGETDW(p);
-                       p += sizeof(uDWord);
-                       max = UGETDW(p);
-                       p += sizeof(uDWord);
-                       step = UGETDW(p);
-                       p += sizeof(uDWord);
+                       min = UGETDW(frame->continuous.dwMinFrameInterval);
+                       max = UGETDW(frame->continuous.dwMaxFrameInterval);
+                       step = UGETDW(frame->continuous.dwFrameIntervalStep);
                        if (frame_ival <= min)
                                frame_ival = min;
                        else if (frame_ival >= max)
@@ -1408,24 +1401,26 @@ uvideo_vs_negotiation(struct uvideo_soft
                        }
                } else if (nivals > 0 && ival_bytes >= sizeof(uDWord)) {
                        /* discrete */
-                       cur = p;
+                       cur = 0;
                        min = UINT_MAX;
                        for (i = 0; i < nivals; i++) {
                                if (ival_bytes < sizeof(uDWord)) {
                                        /* short descriptor ? */
                                        break;
                                }
-                               diff = abs(UGETDW(p) - frame_ival);
+                               diff = abs(
+                                   UGETDW(frame->discrete.dwFrameInterval[i])
+                                   - frame_ival);
                                if (diff < min) {
                                        min = diff;
-                                       cur = p;
+                                       cur = i;
                                        if (diff == 0)
                                                break;
                                }
-                               p += sizeof(uDWord);
                                ival_bytes -= sizeof(uDWord);
                        }
-                       frame_ival = UGETDW(cur);
+                       frame_ival =
+                           UGETDW(frame->discrete.dwFrameInterval[cur]);
                } else {
                        DPRINTF(1, "%s: %s: bad frame ival descriptor\n",
                            DEVNAME(sc), __func__);
@@ -2334,8 +2329,7 @@ uvideo_dump_desc_all(struct uvideo_softc
                        case UDESCSUB_VC_PROCESSING_UNIT:
                                printf("bDescriptorSubtype=0x%02x",
                                    desc->bDescriptorSubtype);
-                               if (desc->bLength >
-                                   sizeof(struct usb_video_frame_desc)) {
+                               if (desc->bLength > UVIDEO_FRAME_DESC_LENGTH) {
                                        printf(" (UDESCSUB_VS_FRAME_"
                                            "UNCOMPRESSED)\n");
                                        uvideo_dump_desc_frame(sc, desc);
@@ -2364,8 +2358,7 @@ uvideo_dump_desc_all(struct uvideo_softc
                                printf("bDescriptorSubtype=0x%02x",
                                    desc->bDescriptorSubtype);
                                printf(" (UDESCSUB_VS_FRAME_MJPEG)\n");
-                               if (desc->bLength >
-                                   sizeof(struct usb_video_frame_desc)) {
+                               if (desc->bLength > UVIDEO_FRAME_DESC_LENGTH) {
                                        printf("|\n");
                                        uvideo_dump_desc_frame(sc, desc);
                                }
@@ -2629,10 +2622,10 @@ void
 uvideo_dump_desc_frame(struct uvideo_softc *sc, const usb_descriptor_t *desc)
 {
        struct usb_video_frame_desc *d;
-       uint8_t *p;
-       int length, i;
+       int i;
 
-       d = (struct usb_video_frame_desc *)(uint8_t *)desc;
+       /* Descriptor is variable sized */
+       d = (void*) desc;
 
        printf("bLength=%d\n", d->bLength);
        printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
@@ -2649,33 +2642,23 @@ uvideo_dump_desc_frame(struct uvideo_sof
            UGETDW(d->dwDefaultFrameInterval));
        printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType);
 
-       p = (uint8_t *)d;
-       p += sizeof(struct usb_video_frame_desc);
-
-       if (!d->bFrameIntervalType) {
+       if (d->bFrameIntervalType == 0) {
                /* continuous */
-               if (d->bLength < (sizeof(struct usb_video_frame_desc) +
-                   sizeof(uDWord) * 3)) {
-                       printf("invalid frame descriptor length\n");
-               } else {
-                       printf("dwMinFrameInterval = %d\n", UGETDW(p));
-                       p += sizeof(uDWord);
-                       printf("dwMaxFrameInterval = %d\n", UGETDW(p));
-                       p += sizeof(uDWord);
-                       printf("dwFrameIntervalStep = %d\n", UGETDW(p));
-                       p += sizeof(uDWord);
-               }
+               printf("dwMinFrameInterval = %d\n",
+                       UGETDW(d->continuous.dwMinFrameInterval));
+               printf("dwMaxFrameInterval = %d\n",
+                       UGETDW(d->continuous.dwMaxFrameInterval));
+               printf("dwFrameIntervalStep = %d\n",
+                       UGETDW(d->continuous.dwFrameIntervalStep));
        } else {
                /* discrete */
-               length = d->bLength - sizeof(struct usb_video_frame_desc);
                for (i = 0; i < d->bFrameIntervalType; i++) {
-                       if (length <= 0) {
+                       if (d->bLength < UVIDEO_FRAME_DESC_LENGTH + i) {
                                printf("frame descriptor ended early\n");
                                break;
                        }
-                       printf("dwFrameInterval = %d\n", UGETDW(p));
-                       p += sizeof(uDWord);
-                       length -= sizeof(uDWord);
+                       printf("dwFrameInterval = %d\n",
+                               UGETDW(d->discrete.dwFrameInterval[i]));
                }
        }
 }
@@ -2924,7 +2906,6 @@ uvideo_enum_fivals(void *v, struct v4l2_
        int idx;
        struct uvideo_format_group *fmtgrp = NULL;
        struct usb_video_frame_desc *frame = NULL;
-       uint8_t *p;
 
        for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
                if (sc->sc_fmtgrp[idx].pixelformat == fivals->pixel_format) {
@@ -2945,33 +2926,29 @@ uvideo_enum_fivals(void *v, struct v4l2_
        if (frame == NULL)
                return (EINVAL);
 
-       /* byte-wise pointer to start of frame intervals */
-       p = (uint8_t *)frame;
-       p += sizeof(struct usb_video_frame_desc);
-
        if (frame->bFrameIntervalType == 0) {
                if (fivals->index != 0)
                        return (EINVAL);
                fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE;
-               fivals->stepwise.min.numerator = UGETDW(p);
+               fivals->stepwise.min.numerator =
+                       UGETDW(frame->continuous.dwMinFrameInterval);
                fivals->stepwise.min.denominator = 10000000;
-               p += sizeof(uDWord);
-               fivals->stepwise.max.numerator = UGETDW(p);
+               fivals->stepwise.max.numerator =
+                       UGETDW(frame->continuous.dwMaxFrameInterval);
                fivals->stepwise.max.denominator = 10000000;
-               p += sizeof(uDWord);
-               fivals->stepwise.step.numerator = UGETDW(p);
+               fivals->stepwise.step.numerator =
+                       UGETDW(frame->continuous.dwFrameIntervalStep);
                fivals->stepwise.step.denominator = 10000000;
-               p += sizeof(uDWord);
        } else {
                if (fivals->index >= frame->bFrameIntervalType)
                        return (EINVAL);
-               p += sizeof(uDWord) * fivals->index;
-               if (p > frame->bLength + (uint8_t *)frame) {
+               if (fivals->index + UVIDEO_FRAME_DESC_LENGTH > frame->bLength){
                        printf("%s: frame desc too short?\n", __func__);
                        return (EINVAL);
                }
                fivals->type = V4L2_FRMIVAL_TYPE_DISCRETE;
-               fivals->discrete.numerator = UGETDW(p);
+               fivals->discrete.numerator =
+                       UGETDW(frame->discrete.dwFrameInterval[fivals->index]);
                fivals->discrete.denominator = 10000000;
        }
 
Index: sys/dev/usb/uvideo.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.h,v
retrieving revision 1.57
diff -u -p -r1.57 uvideo.h
--- sys/dev/usb/uvideo.h        9 Jul 2015 14:58:32 -0000       1.57
+++ sys/dev/usb/uvideo.h        15 Aug 2015 09:59:29 -0000
@@ -357,7 +349,17 @@ struct usb_video_frame_desc {
        uDWord  dwMaxVideoFrameBufferSize;
        uDWord  dwDefaultFrameInterval;
        uByte   bFrameIntervalType;
-       /* uDWord ivals[]; frame intervals, length varies */
+#define UVIDEO_FRAME_DESC_LENGTH 26 /* That's the size up to this point */
+       union {
+               struct {
+                       uDWord  dwMinFrameInterval;
+                       uDWord  dwMaxFrameInterval;
+                       uDWord  dwFrameIntervalStep;
+               } continuous;
+               struct {
+                       uDWord  dwFrameInterval[1];
+               } discrete;
+       };
 } __packed;
 
 /*

Reply via email to