V4L/DVB (13908): cx18: Add initial working VIDIOC_G_ENC_INDEX ioctl() support
authorAndy Walls <awalls@radix.net>
Fri, 1 Jan 2010 01:09:51 +0000 (22:09 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 26 Feb 2010 18:10:30 +0000 (15:10 -0300)
VIDIOC_G_ENC_INDEX support see the light of day.
Some notes:
1. With default capture parameters, the CX23418 seems to transfer 192 index
entries (4.5 kB worth) at 10 second intervals.

2. Index streams don't seem to be supported for MPEG 2 TS streams

3. The index entries seem to claim every frame is a B-Frame.  Possible
firmware bug.

4. The cx18 driver does not try to capture an index stream when inserting
sliced VBI into the MPEg stream as the offsets would need fixup.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx18/cx18-driver.h
drivers/media/video/cx18/cx18-fileops.c
drivers/media/video/cx18/cx18-ioctl.c

index ff34262..930bab6 100644 (file)
@@ -288,6 +288,18 @@ struct cx18_options {
 #define CX18_SLICED_TYPE_WSS_625        (5)
 #define CX18_SLICED_TYPE_VPS            (7)
 
+/**
+ * list_entry_is_past_end - check if a previous loop cursor is off list end
+ * @pos:       the type * previously used as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Check if the entry's list_head is the head of the list, thus it's not a
+ * real entry but was the loop cursor that walked past the end
+ */
+#define list_entry_is_past_end(pos, head, member) \
+       (&pos->member == (head))
+
 struct cx18_buffer {
        struct list_head list;
        dma_addr_t dma_handle;
index b1ad03f..d522d84 100644 (file)
@@ -392,18 +392,6 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
        return len;
 }
 
-/**
- * list_entry_is_past_end - check if a previous loop cursor is off list end
- * @pos:       the type * previously used as a loop cursor.
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- *
- * Check if the entry's list_head is the head of the list, thus it's not a
- * real entry but was the loop cursor that walked past the end
- */
-#define list_entry_is_past_end(pos, head, member) \
-       (&pos->member == (head))
-
 static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
                struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
 {
index 3e4fc19..b81dd0e 100644 (file)
@@ -775,10 +775,143 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
        return 0;
 }
 
+static int _cx18_process_idx_data(struct cx18_buffer *buf,
+                                 struct v4l2_enc_idx *idx)
+{
+       int consumed, remaining;
+       struct v4l2_enc_idx_entry *e_idx;
+       struct cx18_enc_idx_entry *e_buf;
+
+       /* Frame type lookup: 1=I, 2=P, 4=B */
+       const int mapping[8] = {
+               -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
+               -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
+       };
+
+       /*
+        * Assumption here is that a buf holds an integral number of
+        * struct cx18_enc_idx_entry objects and is properly aligned.
+        * This is enforced by the module options on IDX buffer sizes.
+        */
+       remaining = buf->bytesused - buf->readpos;
+       consumed = 0;
+       e_idx = &idx->entry[idx->entries];
+       e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
+
+       while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
+              idx->entries < V4L2_ENC_IDX_ENTRIES) {
+
+               e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
+                               | le32_to_cpu(e_buf->offset_low);
+
+               e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
+                            | le32_to_cpu(e_buf->pts_low);
+
+               e_idx->length = le32_to_cpu(e_buf->length);
+
+               e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
+
+               e_idx->reserved[0] = 0;
+               e_idx->reserved[1] = 0;
+
+               idx->entries++;
+               e_idx = &idx->entry[idx->entries];
+               e_buf++;
+
+               remaining -= sizeof(struct cx18_enc_idx_entry);
+               consumed += sizeof(struct cx18_enc_idx_entry);
+       }
+
+       /* Swallow any partial entries at the end, if there are any */
+       if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
+               consumed += remaining;
+
+       buf->readpos += consumed;
+       return consumed;
+}
+
+static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
+                                struct v4l2_enc_idx *idx)
+{
+       if (s->type != CX18_ENC_STREAM_TYPE_IDX)
+               return -EINVAL;
+
+       if (mdl->curr_buf == NULL)
+               mdl->curr_buf = list_first_entry(&mdl->buf_list,
+                                                struct cx18_buffer, list);
+
+       if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+               /*
+                * For some reason we've exhausted the buffers, but the MDL
+                * object still said some data was unread.
+                * Fix that and bail out.
+                */
+               mdl->readpos = mdl->bytesused;
+               return 0;
+       }
+
+       list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+               /* Skip any empty buffers in the MDL */
+               if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+                       continue;
+
+               mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
+
+               /* exit when MDL drained or request satisfied */
+               if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
+                   mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+                   mdl->readpos >= mdl->bytesused)
+                       break;
+       }
+       return 0;
+}
+
 static int cx18_g_enc_index(struct file *file, void *fh,
                                struct v4l2_enc_idx *idx)
 {
-       return -EINVAL;
+       struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+       struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+       s32 tmp;
+       struct cx18_mdl *mdl;
+
+       if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
+               return -EINVAL;
+
+       /* Compute the best case number of entries we can buffer */
+       tmp = s->buffers -
+                         s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
+       if (tmp <= 0)
+               tmp = 1;
+       tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
+
+       /* Fill out the header of the return structure */
+       idx->entries = 0;
+       idx->entries_cap = tmp;
+       memset(idx->reserved, 0, sizeof(idx->reserved));
+
+       /* Pull IDX MDLs and buffers from q_full and populate the entries */
+       do {
+               mdl = cx18_dequeue(s, &s->q_full);
+               if (mdl == NULL) /* No more IDX data right now */
+                       break;
+
+               /* Extract the Index entry data from the MDL and buffers */
+               cx18_process_idx_data(s, mdl, idx);
+               if (mdl->readpos < mdl->bytesused) {
+                       /* We finished with data remaining, push the MDL back */
+                       cx18_push(s, mdl, &s->q_full);
+                       break;
+               }
+
+               /* We drained this MDL, schedule it to go to the firmware */
+               cx18_enqueue(s, mdl, &s->q_free);
+
+       } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
+
+       /* Tell the work handler to send free IDX MDLs to the firmware */
+       cx18_stream_load_fw_queue(s);
+       return 0;
 }
 
 static int cx18_encoder_cmd(struct file *file, void *fh,