vivi代码
v4l2测试代码
step 6 : 运行设备
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
printf("ERR(%s):VIDIOC_STREAMON failed\n", __func__);
return -1;
}
调用vidioc_streamon
vidioc_streamon
-> vb2_streamon
-> vb2_core_streamon
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vivi_dev *dev = video_drvdata(file);
return vb2_streamon(&dev->vb_vidq, i);
}
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
return vb2_core_streamon(q, type);
}
int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
{
int ret;
if (type != q->type) {
dprintk(1, "invalid stream type\n");
return -EINVAL;
}
/*
* 如果已经启动了,则这里不再继续执行
* start_streaming_called 不用考虑???
* 在qbuf的时候有2种情况
* 1. q->start_streaming_called = 1
* 2. q->start_streaming_called = 0, q->streaming = 1
* 情况1,说明其他应用已经正常抓图了,也就是底层驱动start_streaming执行没问题
* 情况2,说明底层驱动start_streaming执行失败了,这时候会继续执行stream_on
* 只有stream_on没有问题,qbuf才会返回正常值给应用
* 所以需要应用把关,如果返回值错了,就不要向下走了
* 所以这里也可以知道 如果q->streaming = 1
* 那么q->start_streaming_called = 1
* 这里就可以直接返回了
*/
if (q->streaming) {
dprintk(3, "already streaming\n");
return 0;
}
if (!q->num_buffers) {
dprintk(1, "no buffers have been allocated\n");
return -EINVAL;
}
if (q->num_buffers < q->min_buffers_needed) {
dprintk(1, "need at least %u allocated buffers\n",
q->min_buffers_needed);
return -EINVAL;
}
/*
* Tell driver to start streaming provided sufficient buffers
* are available.
*/
/*
* 这个if语句上一篇文章提了一句
* 就是缓冲区不够,无法启动
* 导致 q->streaming = 1
* start_streaming_called = 0
*
*/
if (q->queued_count >= q->min_buffers_needed) {
/*
* 这里牵扯到使用media
* 因为vivi没有使用,所以这里暂时跳过
* 后面再分析
*/
ret = v4l_vb2q_enable_media_source(q);
if (ret)
return ret;
ret = vb2_start_streaming(q);
if (ret)
return ret;
}
/*
* 执行了stream_on的标志
*/
q->streaming = 1;
return 0;
}
vidioc_streamon
-> vb2_streamon
-> vb2_core_streamon
-> vb2_start_streaming
static int vb2_start_streaming(struct vb2_queue *q)
{
struct vb2_buffer *vb;
int ret;
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
/*
* 之前分析过__enqueue_in_driver
* 当时的情况是在本次应用QBUF的时候,发现操作的video节点被其他的应用打开
* 且正在抓图
* 这种情况下__enqueue_in_driver会被调用
* 现在看来在stream_on的时候,还会被再次调用
* 主意这里不仅仅是当前应用的buffer,还有其他操作该video节点应用的buffer
* 底层驱动是不是要判断一下,防止被多次添加
* 另外q->owned_by_drv_count的值一直在增加
* 对于owned_by_drv_count最后是怎么判断?
* 下面失败的时候,会dec,所以不用担心
* 如果是ioctl(STREAM_ON)能走到这里,说明只有当前这一个应用在操作节点
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
/* Tell the driver to start streaming */
/*
* start_streaming_called
* 记录的是底层驱动start_streaming被调用
*/
q->start_streaming_called = 1;
ret = call_qop(q, start_streaming, q,
atomic_read(&q->owned_by_drv_count));
if (!ret)
return 0;
/*
* 调用失败置0
*/
q->start_streaming_called = 0;
dprintk(1, "driver refused to start streaming\n");
/*
* If you see this warning, then the driver isn't cleaning up properly
* after a failed start_streaming(). See the start_streaming()
* documentation in videobuf2-core.h for more information how buffers
* should be returned to vb2 in start_streaming().
*/
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
unsigned i;
/*
* Forcefully reclaim buffers if the driver did not
* correctly return them to vb2.
*/
for (i = 0; i < q->num_buffers; ++i) {
vb = q->bufs[i];
if (vb->state == VB2_BUF_STATE_ACTIVE)
/*
* 这里面会把owned_by_drv_count进行减操作
*/
vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
}
/* Must be zero now */
WARN_ON(atomic_read(&q->owned_by_drv_count));
}
/*
* If done_list is not empty, then start_streaming() didn't call
* vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED) but STATE_ERROR or
* STATE_DONE.
*/
WARN_ON(!list_empty(&q->done_list));
return ret;
}
vidioc_streamon
-> vb2_streamon
-> vb2_core_streamon
-> vb2_start_streaming
-> call_qop(start_streaming --- start_streaming
static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
dprintk(dev, 1, "%s\n", __func__);
return vivi_start_generating(dev);
}
static int vivi_start_generating(struct vivi_dev *dev)
{
struct vivi_dmaqueue *dma_q = &dev->vidq;
dprintk(dev, 1, "%s\n", __func__);
/* Resets frame counters */
dev->ms = 0;
dev->mv_count = 0;
dev->jiffies = jiffies;
dma_q->frame = 0;
dma_q->ini_jiffies = jiffies;
dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
if (IS_ERR(dma_q->kthread)) {
v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
return PTR_ERR(dma_q->kthread);
}
/* Wakes thread */
wake_up_interruptible(&dma_q->wq);
dprintk(dev, 1, "returning from %s\n", __func__);
return 0;
}
static void vivi_thread_tick(struct vivi_dev *dev)
{
struct vivi_dmaqueue *dma_q = &dev->vidq;
struct vivi_buffer *buf;
unsigned long flags = 0;
dprintk(dev, 1, "Thread tick\n");
spin_lock_irqsave(&dev->slock, flags);
if (list_empty(&dma_q->active)) {
dprintk(dev, 1, "No active queue to serve\n");
goto unlock;
}
/*
* 取出active上的每一个buffer
*/
buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
list_del(&buf->list);
buf->vb.timestamp = ktime_get_ns();
/* Fill buffer */
/*
* 这里相当于填充帧数据
*/
vivi_fillbuff(dev, buf);
dprintk(dev, 1, "filled buffer %p\n", buf);
/*
* 这里最重要
*/
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.index);
unlock:
spin_unlock_irqrestore(&dev->slock, flags);
}
vidioc_streamon
-> vb2_streamon
-> vb2_core_streamon
-> vb2_start_streaming
-> call_qop(start_streaming --- start_streaming
-> vb2_buffer_done
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned long flags;
unsigned int plane;
if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
return;
if (WARN_ON(state != VB2_BUF_STATE_DONE &&
state != VB2_BUF_STATE_ERROR &&
state != VB2_BUF_STATE_QUEUED &&
state != VB2_BUF_STATE_REQUEUEING))
state = VB2_BUF_STATE_ERROR;
#ifdef CONFIG_VIDEO_ADV_DEBUG
/*
* Although this is not a callback, it still does have to balance
* with the buf_queue op. So update this counter manually.
*/
vb->cnt_buf_done++;
#endif
dprintk(4, "done processing on buffer %d, state: %d\n",
vb->index, state);
/*
* 对于mmap来说
* need_cache_sync_on_finish这个是1
* 同样的need_cache_sync_on_prepare也是1,这个之前分析过
* 也就是说填充完buffer后,需要调用驱动的finish进行同步
* 这里对应vivi的buffer_finish,这里只是打印信息,所以忽略
*/
if ((state != VB2_BUF_STATE_QUEUED &&
state != VB2_BUF_STATE_REQUEUEING) &&
vb->need_cache_sync_on_finish) {
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, finish, vb->planes[plane].mem_priv);
}
spin_lock_irqsave(&q->done_lock, flags);
if (state == VB2_BUF_STATE_QUEUED ||
state == VB2_BUF_STATE_REQUEUEING) {
vb->state = VB2_BUF_STATE_QUEUED;
} else {
/*
* 把buffer挂载到done_list链表上
*/
/* Add the buffer to the done buffers list */
list_add_tail(&vb->done_entry, &q->done_list);
vb->state = state;
}
atomic_dec(&q->owned_by_drv_count);
spin_unlock_irqrestore(&q->done_lock, flags);
trace_vb2_buf_done(q, vb);
switch (state) {
case VB2_BUF_STATE_QUEUED:
return;
case VB2_BUF_STATE_REQUEUEING:
if (q->start_streaming_called)
__enqueue_in_driver(vb);
return;
default:
/* Inform any processes that may be waiting for buffers */
/*
* 唤醒等待队列中的进程
* 一定要记住这句代码,后面会用到
*/
wake_up(&q->done_wq);
break;
}
}