while working on an update for ffmpeg, I noticed it's ability to
record from a video(4) device is now completely broken because video(4)
doesn't yet support VIDIOC_{S,G}_PARM.

so I added support for the VIDIOC_{S,G}_PARM ioctls to video(4) and
uvideo(4).

but then ffmpeg still couldn't record from a video device.  more
investigation found that uvideo(4) doesn't fill in the timetamp
for frames when using the mmap() interface.  so I added that too.

but still, recording did not work correctly.  it would work the
first time after plugging in a camera, but not any time after that.
eventually I found that the pointer to the current frame in the
mmap() buffer does not get reset when the mmap() buffer is freed
and reallocate once that was fixed, recording video from a video(4)
device then worked like it should.

so here's a diff that includes all the above mentioned changes,
plus a few more that seemed appropriate:
* the mjpeg and uncompreeesed frame descriptors are the same,
  so use a single struct, instead of two different one.  also
  unify the handling of frame descriptors.
* really quit using the frame index from the frame descriptor
  as an index to our array of frame descriptors.  this means the
  array now starts at 0 instead of 1, which makes the code less
  confusing.
* replace magic numbers with appropriate value (sizeof(struct
  usb_video_frame_desc) to be exact)
* fill in uvideo_enum_fivals(), which is used for
  VIDIOC_ENUM_FRAMEINTERVALS ioctl

please test with uvideo(4) devices to be sure this does not break
anything that is currently working.  this will be committed in pieces,
but I wanted to get this full diff out for testing purposes.  thanks.

-- 
jake...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

Index: usb/uvideo.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.c,v
retrieving revision 1.149
diff -u -p usb/uvideo.c
--- usb/uvideo.c        25 Jan 2011 20:03:36 -0000      1.149
+++ usb/uvideo.c        20 Mar 2011 21:10:57 -0000
@@ -48,6 +48,7 @@
 
 #include <dev/video_if.h>
 
+/* #define UVIDEO_DEBUG */
 #ifdef UVIDEO_DEBUG
 int uvideo_debug = 1;
 #define DPRINTF(l, x...) do { if ((l) <= uvideo_debug) printf(x); } while (0)
@@ -93,10 +94,8 @@ usbd_status  uvideo_vs_parse_desc_format_mjpeg(struct u
 usbd_status    uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc *,
                    const usb_descriptor_t *);
 usbd_status    uvideo_vs_parse_desc_frame(struct uvideo_softc *);
-usbd_status    uvideo_vs_parse_desc_frame_mjpeg(struct uvideo_softc *,
+usbd_status    uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *,
                    const usb_descriptor_t *);
-usbd_status    uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_softc *,
-                   const usb_descriptor_t *);
 usbd_status    uvideo_vs_parse_desc_alt(struct uvideo_softc *, int, int, int);
 usbd_status    uvideo_vs_set_alt(struct uvideo_softc *, usbd_interface_handle,
                    int);
@@ -152,13 +151,11 @@ void              uvideo_dump_desc_cs_endpoint(struct 
uvideo_softc
                    const usb_descriptor_t *);
 void           uvideo_dump_desc_colorformat(struct uvideo_softc *,
                    const usb_descriptor_t *);
-void           uvideo_dump_desc_frame_mjpeg(struct uvideo_softc *,
-                   const usb_descriptor_t *);
 void           uvideo_dump_desc_format_mjpeg(struct uvideo_softc *,
                    const usb_descriptor_t *);
 void           uvideo_dump_desc_format_uncompressed(struct uvideo_softc *,
                    const usb_descriptor_t *);
-void           uvideo_dump_desc_frame_uncompressed(struct uvideo_softc *,
+void           uvideo_dump_desc_frame(struct uvideo_softc *,
                    const usb_descriptor_t *);
 void           uvideo_dump_desc_processing(struct uvideo_softc *,
                    const usb_descriptor_t *);
@@ -178,6 +175,8 @@ int         uvideo_enum_fsizes(void *, struct 
v4l2_frmsizeenu
 int            uvideo_enum_fivals(void *, struct v4l2_frmivalenum *);
 int            uvideo_s_fmt(void *, struct v4l2_format *);
 int            uvideo_g_fmt(void *, struct v4l2_format *);
+int            uvideo_s_parm(void *, struct v4l2_streamparm *);
+int            uvideo_g_parm(void *, struct v4l2_streamparm *);
 int            uvideo_enum_input(void *, struct v4l2_input *);
 int            uvideo_s_input(void *, int);
 int            uvideo_reqbufs(void *, struct v4l2_requestbuffers *);
@@ -225,6 +224,8 @@ struct video_hw_if uvideo_hw_if = {
        uvideo_enum_fivals,     /* VIDIOC_ENUM_FRAMEINTERVALS */
        uvideo_s_fmt,           /* VIDIOC_S_FMT */
        uvideo_g_fmt,           /* VIDIOC_G_FMT */
+       uvideo_s_parm,          /* VIDIOC_S_PARM */
+       uvideo_g_parm,          /* VIDIOC_G_PARM */
        uvideo_enum_input,      /* VIDIOC_ENUMINPUT */
        uvideo_s_input,         /* VIDIOC_S_INPUT */
        uvideo_reqbufs,         /* VIDIOC_REQBUFS */
@@ -553,7 +554,7 @@ uvideo_attach_hook(void *arg)
 
        /* init mmap queue */
        SIMPLEQ_INIT(&sc->sc_mmap_q);
-       sc->sc_mmap_cur = 0;
+       sc->sc_mmap_cur = -1;
        sc->sc_mmap_count = 0;
 
        DPRINTF(1, "uvideo_attach: doing video_attach_mi\n");
@@ -634,7 +635,8 @@ uvideo_vc_parse_desc(struct uvideo_softc *sc)
                        break;
                case UDESCSUB_VC_PROCESSING_UNIT:
                        /* XXX do correct length calculation */
-                       if (desc->bLength < 25) {
+                       if (desc->bLength <
+                           sizeof(struct usb_video_frame_desc)) {
                                (void)uvideo_vc_parse_desc_pu(sc, desc);
                        }
                        break;
@@ -951,7 +953,6 @@ uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc 
                printf("%s: too many format descriptors found!\n", DEVNAME(sc));
                return (USBD_INVAL);
        }
-
        sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format =
            (struct uvideo_format_desc *)d;
        if (d->bDefaultFrameIndex > d->bNumFrameDescriptors ||
@@ -1037,28 +1038,14 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc)
        usb_desc_iter_init(sc->sc_udev, &iter);
        desc = usb_desc_iter_next(&iter);
        while (desc) {
-               if (desc->bDescriptorType != UDESC_CS_INTERFACE) {
-                       desc = usb_desc_iter_next(&iter);
-                       continue;
-               }
-
-               switch (desc->bDescriptorSubtype) {
-               case UDESCSUB_VS_FRAME_MJPEG:
-                       error = uvideo_vs_parse_desc_frame_mjpeg(sc, desc);
+               if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
+                   desc->bLength > sizeof(struct usb_video_frame_desc) &&
+                   (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ||
+                   desc->bDescriptorSubtype == 
UDESCSUB_VS_FRAME_UNCOMPRESSED)) {
+                       error = uvideo_vs_parse_desc_frame_sub(sc, desc);
                        if (error != USBD_NORMAL_COMPLETION)
                                return (error);
-                       break;
-               case UDESCSUB_VS_FRAME_UNCOMPRESSED:
-                       /* XXX do correct length calculation */
-                       if (desc->bLength > 25) {
-                               error =uvideo_vs_parse_desc_frame_uncompressed(
-                                   sc, desc);
-                               if (error != USBD_NORMAL_COMPLETION)
-                                       return (error);
-                       }
-                       break;
                }
-
                desc = usb_desc_iter_next(&iter);
        }
 
@@ -1066,80 +1053,34 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc)
 }
 
 usbd_status
-uvideo_vs_parse_desc_frame_mjpeg(struct uvideo_softc *sc,
+uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *sc,
     const usb_descriptor_t *desc)
 {
-       struct usb_video_frame_mjpeg_desc *d;
-       int fmtidx;
-
-       d = (struct usb_video_frame_mjpeg_desc *)(uint8_t *)desc;
-
-       if (d->bFrameIndex == UVIDEO_MAX_FRAME) {
-               printf("%s: too many MJPEG frame descriptors found!\n",
-                   DEVNAME(sc));
-               return (USBD_INVAL);
-       }
-
-       fmtidx = sc->sc_fmtgrp_idx;
-       sc->sc_fmtgrp[fmtidx].frame[d->bFrameIndex] = d;
-
-       if (sc->sc_fmtgrp[fmtidx].format_dfidx == d->bFrameIndex) {
-               sc->sc_fmtgrp[fmtidx].frame_cur =
-                   sc->sc_fmtgrp[fmtidx].frame[d->bFrameIndex];
-       }
-
-       sc->sc_fmtgrp[fmtidx].frame_num++;
-
-       if (sc->sc_fmtgrp[fmtidx].frame_num ==
-           sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors)
-               sc->sc_fmtgrp_idx++;
-
-       /* store max value */
-       if (UGETDW(d->dwMaxVideoFrameBufferSize) > sc->sc_max_fbuf_size)
-               sc->sc_max_fbuf_size = UGETDW(d->dwMaxVideoFrameBufferSize);
-
-       return (USBD_NORMAL_COMPLETION);
-}
-
-usbd_status
-uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_softc *sc,
-    const usb_descriptor_t *desc)
-{
-       struct usb_video_frame_uncompressed_desc *fd = 
-           (struct usb_video_frame_uncompressed_desc *)(uint8_t *)desc;
+       struct usb_video_frame_desc *fd = 
+           (struct usb_video_frame_desc *)(uint8_t *)desc;
        int fmtidx, frame_num;
        uint32_t fbuf_size;
 
        fmtidx = sc->sc_fmtgrp_idx;
-
-       frame_num = sc->sc_fmtgrp[fmtidx].frame_num + 1;
+       frame_num = sc->sc_fmtgrp[fmtidx].frame_num;
        if (frame_num >= UVIDEO_MAX_FRAME) {
-               printf("%s: too many UNCOMPRESSED frame descriptors found!\n",
-                   DEVNAME(sc));
+               printf("%s: too many %s frame descriptors found!\n",
+                   DEVNAME(sc),
+                   desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ?
+                   "MJPEG" : "UNCOMPRESSED");
                return (USBD_INVAL);
        }
-       sc->sc_fmtgrp[fmtidx].frame_num = frame_num;
-
-       sc->sc_fmtgrp[fmtidx].frame[frame_num] =
-           (struct usb_video_frame_mjpeg_desc *)fd;
-
+       sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd;
        if (sc->sc_fmtgrp[fmtidx].frame_cur == NULL ||
-           sc->sc_fmtgrp[fmtidx].format_dfidx == frame_num) {
-               sc->sc_fmtgrp[fmtidx].frame_cur =
-                   sc->sc_fmtgrp[fmtidx].frame[frame_num];
-       }
+           sc->sc_fmtgrp[fmtidx].format_dfidx == fd->bFrameIndex)
+               sc->sc_fmtgrp[fmtidx].frame_cur = fd;
 
-       if (sc->sc_fmtgrp[fmtidx].frame_num ==
-           sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) {
-               sc->sc_fmtgrp_idx++;
-       }
-
        /*
         * On some broken device, dwMaxVideoFrameBufferSize is not correct.
         * So fix it by frame width/height (XXX YUV2 format only).
         */
        if (sc->sc_quirk &&
-           sc->sc_quirk->flags & UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE &&
+           (sc->sc_quirk->flags & UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE) &&
            sc->sc_fmtgrp[fmtidx].pixelformat == V4L2_PIX_FMT_YUYV) {
                fbuf_size = UGETW(fd->wWidth) * UGETW(fd->wHeight) * 4;
                DPRINTF(1, "wWidth = %d, wHeight = %d\n",
@@ -1151,6 +1092,15 @@ uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_
        if (fbuf_size > sc->sc_max_fbuf_size)
                sc->sc_max_fbuf_size = fbuf_size;
 
+       /*
+        * Increment frame count.  If this is the last frame in the
+        * format group, go on to next group.
+        */
+       if (++sc->sc_fmtgrp[fmtidx].frame_num ==
+           sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) {
+               sc->sc_fmtgrp_idx++;
+       }
+
        return (USBD_NORMAL_COMPLETION);
 }
 
@@ -1338,7 +1288,7 @@ uvideo_find_res(struct uvideo_softc *sc, int idx, int 
 
        size_want = width * height;
 
-       for (i = 1; i <= sc->sc_fmtgrp[idx].frame_num; i++) {
+       for (i = 0; i < sc->sc_fmtgrp[idx].frame_num; i++) {
                w = UGETW(sc->sc_fmtgrp[idx].frame[i]->wWidth);
                h = UGETW(sc->sc_fmtgrp[idx].frame[i]->wHeight);
                size_is = w * h;
@@ -1346,13 +1296,13 @@ uvideo_find_res(struct uvideo_softc *sc, int idx, int 
                        diff = size_is - size_want;
                else
                        diff = size_want - size_is;
-               if (i == 1)
+               if (i == 0)
                        diff_best = diff;
                if (diff <= diff_best) {
                        diff_best = diff;
                        r->width = w;
                        r->height = h;
-                       r->fidx = sc->sc_fmtgrp[idx].frame[i]->bFrameIndex;
+                       r->fidx = i;
                }
                DPRINTF(1, "%s: %s: frame index %d: width=%d, height=%d\n",
                    DEVNAME(sc), __func__, i, w, h);
@@ -1363,13 +1313,19 @@ usbd_status
 uvideo_vs_negotiation(struct uvideo_softc *sc, int commit)
 {
        struct usb_video_probe_commit *pc;
+       struct uvideo_format_group *fmtgrp;
+       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;
 
        pc = (struct usb_video_probe_commit *)probe_data;
 
+       fmtgrp = sc->sc_fmtgrp_cur;
+
        /* check if the format descriptor contains frame descriptors */
-       if (sc->sc_fmtgrp_cur->frame_num == 0) {
+       if (fmtgrp->frame_num == 0) {
                printf("%s: %s: no frame descriptors found!\n",
                    __func__, DEVNAME(sc));
                return (USBD_INVAL);
@@ -1377,12 +1333,63 @@ uvideo_vs_negotiation(struct uvideo_softc *sc, int com
 
        /* set probe */
        bzero(probe_data, sizeof(probe_data));
+       /* hint that dwFrameInterval should be favored over other parameters */
        USETW(pc->bmHint, 0x1);
-       pc->bFormatIndex = sc->sc_fmtgrp_cur->format->bFormatIndex;
-       pc->bFrameIndex = sc->sc_fmtgrp_cur->format_dfidx;
+       pc->bFormatIndex = fmtgrp->format->bFormatIndex;
+       pc->bFrameIndex = fmtgrp->frame_cur->bFrameIndex;
        /* dwFrameInterval: 30fps=333333, 15fps=666666, 10fps=1000000 */
-       USETDW(pc->dwFrameInterval,
-           UGETDW(sc->sc_fmtgrp_cur->frame_cur->dwDefaultFrameInterval));
+       frame_ival = UGETDW(fmtgrp->frame_cur->dwDefaultFrameInterval);
+       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);
+               nivals = fmtgrp->frame_cur->bFrameIntervalType;
+               ival_bytes = fmtgrp->frame_cur->bLength -
+                   sizeof(struct usb_video_frame_desc);
+               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);
+                       if (frame_ival <= min)
+                               frame_ival = min;
+                       else if (frame_ival >= max)
+                               frame_ival = max;
+                       else {
+                               for (i = min; i + step/2 < frame_ival; i+= step)
+                                       ;       /* nothing */
+                               frame_ival = i;
+                       }
+               } else if (nivals > 0 && ival_bytes >= sizeof(uDWord)) {
+                       /* discrete */
+                       cur = p;
+                       min = UINT_MAX;
+                       for (i = 0; i < nivals; i++) {
+                               if (ival_bytes < sizeof(uDWord)) {
+                                       /* short descriptor ? */
+                                       break;
+                               }
+                               diff = abs(UGETDW(p) - frame_ival);
+                               if (diff < min) {
+                                       min = diff;
+                                       cur = p;
+                                       if (diff == 0)
+                                               break;
+                               }
+                               p += sizeof(uDWord);
+                               ival_bytes -= sizeof(uDWord);
+                       }
+                       frame_ival = UGETDW(cur);
+               } else {
+                       DPRINTF(1, "%s: %s: bad frame ival descriptor\n",
+                           DEVNAME(sc), __func__);
+               }
+       }
+       USETDW(pc->dwFrameInterval, frame_ival);
        error = uvideo_vs_set_probe(sc, probe_data);
        if (error != USBD_NORMAL_COMPLETION)
                return (error);
@@ -1594,6 +1601,12 @@ uvideo_vs_free_frame(struct uvideo_softc *sc)
                free(sc->sc_mmap_buffer, M_DEVBUF);
                sc->sc_mmap_buffer = NULL;
        }
+
+       while (!SIMPLEQ_EMPTY(&sc->sc_mmap_q))
+               SIMPLEQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames);
+
+       sc->sc_mmap_cur = -1;
+       sc->sc_mmap_count = 0;
 }
 
 usbd_status
@@ -2099,6 +2112,10 @@ uvideo_vs_decode_stream_header_isight(struct uvideo_so
 void
 uvideo_mmap_queue(struct uvideo_softc *sc, uint8_t *buf, int len)
 {
+       if (sc->sc_mmap_cur < 0 || sc->sc_mmap_count == 0 ||
+           sc->sc_mmap_buffer == NULL)
+               panic("%s: mmap buffers not allocated", __func__);
+
        /* find a buffer which is ready for queueing */
        while (sc->sc_mmap_cur < sc->sc_mmap_count) {
                if (sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.flags &
@@ -2114,6 +2131,9 @@ uvideo_mmap_queue(struct uvideo_softc *sc, uint8_t *bu
        bcopy(buf, sc->sc_mmap[sc->sc_mmap_cur].buf, len);
        sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.bytesused = len;
 
+       /* timestamp it */
+       getmicrotime(&sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.timestamp);
+
        /* queue it */
        SIMPLEQ_INSERT_TAIL(&sc->sc_mmap_q, &sc->sc_mmap[sc->sc_mmap_cur],
            q_frames);
@@ -2214,11 +2234,11 @@ uvideo_dump_desc_all(struct uvideo_softc *sc)
                                printf("bDescriptorSubtype=0x%02x",
                                    desc->bDescriptorSubtype);
                                /* XXX do correct length calculation */
-                               if (desc->bLength > 25) {
+                               if (desc->bLength >
+                                   sizeof(struct usb_video_frame_desc)) {
                                        printf(" (UDESCSUB_VS_FRAME_"
                                            "UNCOMPRESSED)\n");
-                                       uvideo_dump_desc_frame_uncompressed(
-                                           sc, desc);
+                                       uvideo_dump_desc_frame(sc, desc);
                                } else {
                                        printf(" (UDESCSUB_VC_PROCESSING_"
                                            "UNIT)\n");
@@ -2244,9 +2264,10 @@ uvideo_dump_desc_all(struct uvideo_softc *sc)
                                printf("bDescriptorSubtype=0x%02x",
                                    desc->bDescriptorSubtype);
                                printf(" (UDESCSUB_VS_FRAME_MJPEG)\n");
-                               if (desc->bLength > 26) {
+                               if (desc->bLength >
+                                   sizeof(struct usb_video_frame_desc)) {
                                        printf("|\n");
-                                       uvideo_dump_desc_frame_mjpeg(sc, desc);
+                                       uvideo_dump_desc_frame(sc, desc);
                                }
                                break;
                        case UDESCSUB_VS_COLORFORMAT:
@@ -2483,32 +2504,7 @@ uvideo_dump_desc_colorformat(struct uvideo_softc *sc,
            d->bTransferCharacteristics);
        printf("bMatrixCoefficients=0x%02x\n", d->bMatrixCoefficients);
 }
-
 void
-uvideo_dump_desc_frame_mjpeg(struct uvideo_softc *sc,
-    const usb_descriptor_t *desc)
-{
-       struct usb_video_frame_mjpeg_desc *d;
-
-       d = (struct usb_video_frame_mjpeg_desc *)(uint8_t *)desc;
-
-       printf("bLength=%d\n", d->bLength);
-       printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
-       printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype);
-       printf("bFrameIndex=0x%02x\n", d->bFrameIndex);
-       printf("bmCapabilities=0x%02x\n", d->bmCapabilities);
-       printf("wWidth=%d\n", UGETW(d->wWidth));
-       printf("wHeight=%d\n", UGETW(d->wHeight));
-       printf("dwMinBitRate=%d\n", UGETDW(d->dwMinBitRate));
-       printf("dwMaxBitRate=%d\n", UGETDW(d->dwMaxBitRate));
-       printf("dwMaxVideoFrameBufferSize=%d\n",
-           UGETDW(d->dwMaxVideoFrameBufferSize));
-       printf("dwDefaultFrameInterval=%d\n",
-           UGETDW(d->dwDefaultFrameInterval));
-       printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType);
-}
-
-void
 uvideo_dump_desc_format_mjpeg(struct uvideo_softc *sc,
     const usb_descriptor_t *desc)
 {
@@ -2530,12 +2526,13 @@ uvideo_dump_desc_format_mjpeg(struct uvideo_softc *sc,
 }
 
 void
-uvideo_dump_desc_frame_uncompressed(struct uvideo_softc *sc,
-    const usb_descriptor_t *desc)
+uvideo_dump_desc_frame(struct uvideo_softc *sc, const usb_descriptor_t *desc)
 {
-       struct usb_video_frame_uncompressed_desc *d;
+       struct usb_video_frame_desc *d;
+       uint8_t *p;
+       int length, i;
 
-       d = (struct usb_video_frame_uncompressed_desc *)(uint8_t *)desc;
+       d = (struct usb_video_frame_desc *)(uint8_t *)desc;
 
        printf("bLength=%d\n", d->bLength);
        printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
@@ -2551,6 +2548,35 @@ uvideo_dump_desc_frame_uncompressed(struct uvideo_soft
        printf("dwDefaultFrameInterval=%d\n",
            UGETDW(d->dwDefaultFrameInterval));
        printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType);
+
+       p = (uint8_t *)d;
+       p += sizeof(struct usb_video_frame_desc);
+
+       if (!d->bFrameIntervalType) {
+               /* continuous */
+               if (d->bLength < 38) {
+                       printf("invalid frame desc length for continuous\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);
+               }
+       } else {
+               /* discrete */
+               length = d->bLength - sizeof(struct usb_video_frame_desc);
+               for (i = 0; i < d->bFrameIntervalType; i++) {
+                       if (length <= 0) {
+                               printf("more frame intervals, but desc 
ended\n");
+                               break;
+                       }
+                       printf("dwFrameInterval = %d\n", UGETDW(p));
+                       p += sizeof(uDWord);
+                       length -= sizeof(uDWord);
+               }
+       }
 }
 
 void
@@ -2767,7 +2793,7 @@ int
 uvideo_enum_fsizes(void *v, struct v4l2_frmsizeenum *fsizes)
 {
        struct uvideo_softc *sc = v;
-       int i, idx, found = 0;
+       int idx, found = 0;
 
        for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
                if (sc->sc_fmtgrp[idx].pixelformat == fsizes->pixel_format) {
@@ -2778,16 +2804,14 @@ uvideo_enum_fsizes(void *v, struct v4l2_frmsizeenum *f
        if (found == 0)
                return (EINVAL);
 
-       i = fsizes->index + 1;
-       if (i > sc->sc_fmtgrp[idx].frame_num)
-               /* no more frames left */
+       if (fsizes->index >= sc->sc_fmtgrp[idx].frame_num)
                return (EINVAL);
 
        fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE;
        fsizes->un.discrete.width =
-           UGETW(sc->sc_fmtgrp[idx].frame[i]->wWidth);
+           UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wWidth);
        fsizes->un.discrete.height =
-           UGETW(sc->sc_fmtgrp[idx].frame[i]->wHeight);
+           UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wHeight);
 
        return (0);
 }
@@ -2796,20 +2820,61 @@ int
 uvideo_enum_fivals(void *v, struct v4l2_frmivalenum *fivals)
 {
        struct uvideo_softc *sc = v;
-       int idx, found = 0;
+       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) {
-                       found = 1;
+                       fmtgrp = &sc->sc_fmtgrp[idx];
                        break;
                }
        }
-       if (found == 0)
+       if (fmtgrp == NULL)
                return (EINVAL);
 
-       /* TODO */
+       for (idx = 0; idx < fmtgrp->frame_num; idx++) {
+               if (UGETW(fmtgrp->frame[idx]->wWidth) == fivals->width &&
+                   UGETW(fmtgrp->frame[idx]->wHeight) == fivals->height) {
+                       frame = fmtgrp->frame[idx];
+                       break;
+               }
+       }
+       if (frame == NULL)
+               return (EINVAL);
 
-       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->un.stepwise.min.numerator = UGETDW(p);
+               fivals->un.stepwise.min.denominator = 10000000;
+               p += sizeof(uDWord);
+               fivals->un.stepwise.max.numerator = UGETDW(p);
+               fivals->un.stepwise.max.denominator = 10000000;
+               p += sizeof(uDWord);
+               fivals->un.stepwise.step.numerator = UGETDW(p);
+               fivals->un.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) {
+                       printf("%s: frame desc too short?\n", __func__);
+                       return (EINVAL);
+               }
+               fivals->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+               fivals->un.discrete.numerator = UGETDW(p);
+               fivals->un.discrete.denominator = 10000000;
+       }
+
+       return (0);
 }
 
 int
@@ -2817,7 +2882,7 @@ uvideo_s_fmt(void *v, struct v4l2_format *fmt)
 {
        struct uvideo_softc *sc = v;
        struct uvideo_format_group *fmtgrp_save;
-       struct usb_video_frame_mjpeg_desc *frame_save;
+       struct usb_video_frame_desc *frame_save;
        struct uvideo_res r;
        int found, i;
        usbd_status error;
@@ -2854,10 +2919,11 @@ uvideo_s_fmt(void *v, struct v4l2_format *fmt)
        /* save a copy of current fromat group in case of negotiation fails */
        fmtgrp_save = sc->sc_fmtgrp_cur;
        frame_save = sc->sc_fmtgrp_cur->frame_cur;
-       /* set new format group */
+
+       /* set new format group and frame */
        sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[i];
        sc->sc_fmtgrp[i].frame_cur = sc->sc_fmtgrp[i].frame[r.fidx];
-       sc->sc_fmtgrp[i].format_dfidx = r.fidx;
+
        /* do device negotiation with commit */
        error = uvideo_vs_negotiation(sc, 1);
        if (error != USBD_NORMAL_COMPLETION) {
@@ -2900,6 +2966,53 @@ uvideo_g_fmt(void *v, struct v4l2_format *fmt)
 }
 
 int
+uvideo_s_parm(void *v, struct v4l2_streamparm *parm)
+{
+       struct uvideo_softc *sc = v;
+       usbd_status error;
+
+       if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               if (parm->parm.capture.timeperframe.numerator == 0 ||
+                   parm->parm.capture.timeperframe.denominator == 0)
+                       return (EINVAL);
+
+               /* only whole number frame rates for now */
+               sc->sc_frame_rate =
+                   parm->parm.capture.timeperframe.numerator /
+                   parm->parm.capture.timeperframe.denominator;
+       } else
+               return (EINVAL);
+
+       /* renegotiate if necessary */
+       if (sc->sc_negotiated_flag) {
+               error = uvideo_vs_negotiation(sc, 1);
+               if (error != USBD_NORMAL_COMPLETION)
+                       return (error);
+       }
+
+       return (0);
+}
+
+int
+uvideo_g_parm(void *v, struct v4l2_streamparm *parm)
+{
+       struct uvideo_softc *sc = v;
+       int ns;
+
+       ns = UGETDW(sc->sc_desc_probe.dwFrameInterval);
+       if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+               parm->parm.capture.capturemode = 0;
+               parm->parm.capture.timeperframe.numerator =
+                   (ns == 0) ? 0 : 10000000 / ns;
+               parm->parm.capture.timeperframe.denominator = 1;
+       } else
+               return (EINVAL);
+
+       return (0);
+}
+
+int
 uvideo_enum_input(void *v, struct v4l2_input *input)
 {
        if (input->index != 0)
@@ -2930,6 +3043,9 @@ uvideo_reqbufs(void *v, struct v4l2_requestbuffers *rb
 
        DPRINTF(1, "%s: %s: count=%d\n", DEVNAME(sc), __func__, rb->count);
 
+       if (sc->sc_mmap_count > 0 || sc->sc_mmap_buffer != NULL)
+               panic("%s: mmap buffers already allocated", __func__);
+
        /* limit the buffers */
        if (rb->count > UVIDEO_MAX_BUFFERS)
                sc->sc_mmap_count = UVIDEO_MAX_BUFFERS;
@@ -2970,6 +3086,9 @@ uvideo_reqbufs(void *v, struct v4l2_requestbuffers *rb
 
        /* tell how many buffers we have really allocated */
        rb->count = sc->sc_mmap_count;
+
+       /* start with the first buffer */
+       sc->sc_mmap_cur = 0;
 
        return (0);
 }
Index: usb/uvideo.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.h,v
retrieving revision 1.51
diff -u -p usb/uvideo.h
--- usb/uvideo.h        27 Nov 2010 00:38:29 -0000      1.51
+++ usb/uvideo.h        20 Mar 2011 21:10:57 -0000
@@ -343,8 +343,8 @@ struct usb_video_format_mjpeg_desc {
        uByte   bCopyProtect;
 } __packed;
 
-/* Table 3-2: Motion-JPEG Video Frame Descriptor */
-struct usb_video_frame_mjpeg_desc {
+/* Table 3-2: Video Frame Descriptor (same for mjpeg and uncompressed)*/
+struct usb_video_frame_desc {
        uByte   bLength;
        uByte   bDescriptorType;
        uByte   bDescriptorSubtype;
@@ -357,7 +357,7 @@ struct usb_video_frame_mjpeg_desc {
        uDWord  dwMaxVideoFrameBufferSize;
        uDWord  dwDefaultFrameInterval;
        uByte   bFrameIntervalType;
-       /* TODO add continous/discrete frame intervals (Table 3-3/3-4) */
+       /* uDWord ivals[]; frame intervals, length varies */
 } __packed;
 
 /*
@@ -379,23 +379,6 @@ struct usb_video_format_uncompressed_desc {
        uByte   bCopyProtect;
 } __packed;
 
-/* Table 3-2: Uncompressed Video Frame Descriptor */
-struct usb_video_frame_uncompressed_desc {
-       uByte   bLength;
-       uByte   bDescriptorType;
-       uByte   bDescriptorSubtype;
-       uByte   bFrameIndex;
-       uByte   bmCapabilities;
-       uWord   wWidth;
-       uWord   wHeight;
-       uDWord  dwMinBitRate;
-       uDWord  dwMaxBitRate;
-       uDWord  dwMaxVideoFrameBufferSize;
-       uDWord  dwDefaultFrameInterval;
-       uByte   bFrameIntervalType;
-       /* TODO add continous/discrete frame intervals (Table 3-3/3-4) */
-} __packed;
-
 /*
  * Driver specific private definitions.
  */
@@ -481,8 +464,8 @@ struct uvideo_format_group {
        struct uvideo_format_desc               *format;
        /* frame descriptors for mjpeg and uncompressed are identical */
 #define UVIDEO_MAX_FRAME                        32
-       struct usb_video_frame_mjpeg_desc       *frame_cur;
-       struct usb_video_frame_mjpeg_desc       *frame[UVIDEO_MAX_FRAME];
+       struct usb_video_frame_desc             *frame_cur;
+       struct usb_video_frame_desc             *frame[UVIDEO_MAX_FRAME];
        int                                      frame_num;
 };
 
@@ -687,6 +670,7 @@ struct uvideo_softc {
        int                                      sc_dying;
        int                                      sc_max_fbuf_size;
        int                                      sc_negotiated_flag;
+       int                                      sc_frame_rate;
 
        struct uvideo_frame_buffer               sc_frame_buffer;
 
Index: video.c
===================================================================
RCS file: /cvs/src/sys/dev/video.c,v
retrieving revision 1.26
diff -u -p video.c
--- video.c     26 Dec 2010 15:41:00 -0000      1.26
+++ video.c     20 Mar 2011 21:10:57 -0000
@@ -231,6 +231,16 @@ videoioctl(dev_t dev, u_long cmd, caddr_t data, int fl
                        error = (sc->hw_if->g_fmt)(sc->hw_hdl,
                            (struct v4l2_format *)data);
                break;
+       case VIDIOC_S_PARM:
+               if (sc->hw_if->s_parm)
+                       error = (sc->hw_if->s_parm)(sc->hw_hdl,
+                           (struct v4l2_streamparm *)data);
+               break;
+       case VIDIOC_G_PARM:
+               if (sc->hw_if->g_parm)
+                       error = (sc->hw_if->g_parm)(sc->hw_hdl,
+                           (struct v4l2_streamparm *)data);
+               break;
        case VIDIOC_ENUMINPUT:
                if (sc->hw_if->enum_input)
                        error = (sc->hw_if->enum_input)(sc->hw_hdl,
Index: video_if.h
===================================================================
RCS file: /cvs/src/sys/dev/video_if.h,v
retrieving revision 1.16
diff -u -p video_if.h
--- video_if.h  24 Aug 2008 11:05:02 -0000      1.16
+++ video_if.h  20 Mar 2011 21:10:57 -0000
@@ -40,6 +40,8 @@ struct video_hw_if {
        int     (*enum_fivals)(void *, struct v4l2_frmivalenum *);
        int     (*s_fmt)(void *, struct v4l2_format *);
        int     (*g_fmt)(void *, struct v4l2_format *);
+       int     (*s_parm)(void *, struct v4l2_streamparm *);
+       int     (*g_parm)(void *, struct v4l2_streamparm *);
        int     (*enum_input)(void *, struct v4l2_input *);
        int     (*s_input)(void *, int);
        int     (*reqbufs)(void *, struct v4l2_requestbuffers *);

Reply via email to