V4L/DVB (9860): gspca - ov534: Frame transfer improvements.
authorJim Paris <jim@jtan.com>
Thu, 4 Dec 2008 07:52:40 +0000 (04:52 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 30 Dec 2008 11:38:55 +0000 (09:38 -0200)
The indirect registers at 0x1c/0x1d control frame settings.  If we
leave the values at 0x0a and 0x0b at their reset-time defaults, frame
data from the camera matches the UVC payload format.  This lets us
better reassemble the data into frames and know when data was lost.

This also lets us relax the bulk_size requirement from 600K to 2K,
which should help systems on with limited RAM (like the PS3).

Signed-off-by: Jim Paris <jim@jtan.com>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/ov534.c

index 79bb6241d27f7d17be73d9d7381892f15dbc7c9d..e02c8f70f8140d78bacbb2928191af8eba9c4216 100644 (file)
@@ -165,10 +165,6 @@ static const __u8 ov534_reg_initdata[][2] = {
        { 0xe2, 0x00 },
        { 0xe7, 0x3e },
 
-       { 0x1c, 0x0a },
-       { 0x1d, 0x22 },
-       { 0x1d, 0x06 },
-
        { 0x96, 0x00 },
 
        { 0x97, 0x20 },
@@ -327,18 +323,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
 
-       /*
-        * On some architectures we need contiguous memory for urb buffers, and
-        * in low memory situation 'sizeimage' can be too much.
-        * 16kiB chunks should be available even when we are low in memory.
-        * TODO: CHECK this description: is the problem arch-dependent or more
-        *       general?
-        */
-       cam->bulk_size = 16 * 1024;
+       cam->bulk_size = 2048;
        cam->bulk_nurbs = 2;
 
-       PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size);
-
        return 0;
 }
 
@@ -383,19 +370,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
 
 static int sd_start(struct gspca_dev *gspca_dev)
 {
-       struct gspca_frame *frame;
-
        /* start streaming data */
        ov534_set_led(gspca_dev->dev, 1);
        ov534_reg_write(gspca_dev->dev, 0xe0, 0x00);
 
-       frame = gspca_get_i_frame(gspca_dev);
-       if (frame == NULL) {
-               PDEBUG(D_ERR, "NULL frame!");
-               return -1;
-       }
-       gspca_frame_add(gspca_dev, FIRST_PACKET, frame, gspca_dev->usb_buf, 0);
-
        return 0;
 }
 
@@ -406,18 +384,79 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
        ov534_set_led(gspca_dev->dev, 0);
 }
 
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
 static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
                        __u8 *data, int len)
 {
-       int framesize = frame->v4l2_buf.length;
-
-       if (len == framesize) {
-               frame = gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                                       data, len);
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
-       } else
-               PDEBUG(D_PACK, "packet len = %d, framesize = %d", len,
-                      framesize);
+       static __u32 last_pts;
+       __u32 this_pts;
+       static int last_fid;
+       int this_fid;
+
+       /* Payloads are prefixed with a the UVC-style header.  We
+          consider a frame to start when the FID toggles, or the PTS
+          changes.  A frame ends when EOF is set, and we've received
+          the correct number of bytes. */
+
+       /* Verify UVC header.  Header length is always 12 */
+       if (data[0] != 12 || len < 12) {
+               PDEBUG(D_PACK, "bad header");
+               goto discard;
+       }
+
+       /* Check errors */
+       if (data[1] & UVC_STREAM_ERR) {
+               PDEBUG(D_PACK, "payload error");
+               goto discard;
+       }
+
+       /* Extract PTS and FID */
+       if (!(data[1] & UVC_STREAM_PTS)) {
+               PDEBUG(D_PACK, "PTS not present");
+               goto discard;
+       }
+       this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2];
+       this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+
+       /* If PTS or FID has changed, start a new frame. */
+       if (this_pts != last_pts || this_fid != last_fid) {
+               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+               last_pts = this_pts;
+               last_fid = this_fid;
+       }
+
+       /* Add the data from this payload */
+       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                               data + 12, len - 12);
+
+       /* If this packet is marked as EOF, end the frame */
+       if (data[1] & UVC_STREAM_EOF) {
+               last_pts = 0;
+
+               if ((frame->data_end - frame->data) !=
+                   (gspca_dev->width * gspca_dev->height * 2)) {
+                       PDEBUG(D_PACK, "short frame");
+                       goto discard;
+               }
+
+               gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0);
+       }
+
+       /* Done */
+       return;
+
+discard:
+       /* Discard data until a new frame starts. */
+       gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
 }
 
 /* sub-driver description */