在上次培训(2013年11月15日)中第一章第2小节有讲到应用层调用v4l2的例子,代码如下:
经过我研究,代码是没有问题的。之前认为调用VIDIOC_QUERYBUF之后没有对buffer的状态查询就开始VIDIOC_DQBUF有不妥。 其实是我理解不够。其实,此时VIDIOC_DQBUF是一个阻塞函数,直到有数据后才会返回。
v4l2等待数据有效的方法有两种:
在v4l2内部维护着两个buffer的队列,一个给驱动用(简称Queue A),一个给用户使用(简称Queue B)。可以先初略的理解队列中存储着每个buffer的指针。
驱动需要使用buffer来填充数据时,从Queue A中取出一个buffer,填充之。填满后会存入Queue B中。而驱动每次调用VIDIOC_DQBUF,会去Queue B中查询,若有buffer则出队次buffer,否则等待直到有buffer。用户使用完buffer后,调用VIDIOC_QBUF将buffer入队到Queue A中。
基本的过程就是这样。后面章节会具体讲v4l2缓冲区管理的细节。敬请期待。
用户调用VIDIOC_DQBUF时,v4l2会调用vb2_dqbuf函数,其实现如下:
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
{
struct vb2_buffer *vb = NULL;
int ret;
if (q->fileio) {
dprintk(1, "dqbuf: file io in progress\n");
return -EBUSY;
}
if (b->type != q->type) {
dprintk(1, "dqbuf: invalid buffer type\n");
return -EINVAL;
}
ret = __vb2_get_done_vb(q, &vb, nonblocking);
if (ret < 0) {
dprintk(1, "dqbuf: error getting next done buffer\n");
return ret;
}
ret = call_qop(q, buf_finish, vb);
if (ret) {
dprintk(1, "dqbuf: buffer finish failed\n");
return ret;
}
switch (vb->state) {
case VB2_BUF_STATE_DONE:
dprintk(3, "dqbuf: Returning done buffer\n");
break;
case VB2_BUF_STATE_ERROR:
dprintk(3, "dqbuf: Returning done buffer with errors\n");
break;
default:
dprintk(1, "dqbuf: Invalid buffer state\n");
return -EINVAL;
}
/* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
dprintk(1, "dqbuf of buffer %d, with state %d\n",
vb->v4l2_buf.index, vb->state);
vb->state = VB2_BUF_STATE_DEQUEUED;
return 0;
}
其中最重要的操作(高亮的那一行)调用__vb2_get_done_vb函数去获取一个已经填充好的buffer。
static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
int nonblocking) { unsigned long flags; int ret;
/*
* Wait for at least one buffer to become available on the done_list.
*/
ret = __vb2_wait_for_done_vb(q, nonblocking);
if (ret)
return ret;
/*
* Driver's lock has been held since we last verified that done_list
* is not empty, so no need for another list_empty(done_list) check.
*/
spin_lock_irqsave(&q->done_lock, flags);
*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
list_del(&(*vb)->done_entry);
spin_unlock_irqrestore(&q->done_lock, flags);
return 0;
}
从上面的代码中可以看出__vb2_get_done_vb函数调用__vb2_wait_for_done_vb获取一个buffer,并将其从队列中删除。__vb2_wait_for_done_vb代码如下:
static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
{
for (;;) {
int ret;
if (!q->streaming) {
dprintk(1, "Streaming off, will not wait for buffers\n");
return -EINVAL;
}
if (!list_empty(&q->done_list)) {
/*
* Found a buffer that we were waiting for.
*/
break;
}
if (nonblocking) {
dprintk(1, "Nonblocking and no buffers to dequeue, "
"will not wait\n");
return -EAGAIN;
}
/*
* We are streaming and blocking, wait for another buffer to
* become ready or for streamoff. Driver's lock is released to
* allow streamoff or qbuf to be called while waiting.
*/
call_qop(q, wait_prepare, q);
/*
* All locks have been released, it is safe to sleep now.
*/
dprintk(3, "Will sleep waiting for buffers\n");
ret = wait_event_interruptible(q->done_wq,
!list_empty(&q->done_list) || !q->streaming);
/*
* We need to reevaluate both conditions again after reacquiring
* the locks or return an error if one occurred.
*/
call_qop(q, wait_finish, q);
if (ret)
return ret;
}
return 0;
}
从上面的代码,调用wait_event_interruptible函数实现等待的。退出等待的条件是q->done_list不为空。
如此这样,VIDIOC_DQBUF就实现了阻塞操作。
从__vb2_wait_for_done_vb函数的实现中,亦可发现,当nonblocking变量为True时,__vb2_wait_for_done_vb不会等待队列非空,直接返回-EAGAIN.