Linux v4l2架构学习总链接
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
{
printf("ERR(%s):VIDIOC_STREAMOFF failed\n", __func__);
return -1;
}
调用vidioc_streamoff
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vivi_dev *dev = video_drvdata(file);
return vb2_streamoff(&dev->vb_vidq, i);
}
int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
{
/*
* 之前的分析都没有涉及到fileio
* vivi这部分文章,后续可能会去专门分析
*/
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
return vb2_core_streamoff(q, type);
}
vidioc_streamoff
-> vb2_streamoff
-> vb2_core_sreamoff
-> __vb2_queue_cancel
int vb2_core_streamoff(struct vb2_queue *q, unsigned int type)
{
if (type != q->type) {
dprintk(1, "invalid stream type\n");
return -EINVAL;
}
/*
* Cancel will pause streaming and remove all buffers from the driver
* and videobuf, effectively returning control over them to userspace.
*
* Note that we do this even if q->streaming == 0: if you prepare or
* queue buffers, and then call streamoff without ever having called
* streamon, you would still expect those buffers to be returned to
* their normal dequeued state.
*/
__vb2_queue_cancel(q);
q->waiting_for_buffers = !q->is_output;
q->last_buffer_dequeued = false;
dprintk(3, "successful\n");
return 0;
}
static void __vb2_queue_cancel(struct vb2_queue *q)
{
unsigned int i;
/*
* Tell driver to stop all transactions and release all queued
* buffers.
*/
/*
* 之前分析过 start_streaming_called 置1
* 说明start_streaming被正常执行了
* 这里streamoff,所以调用stop_streaming
* vivi驱动的stop_streaming主要调用
* vivi_stop_generating 取消产生帧数据
* 这里不再跟进分析
*/
if (q->start_streaming_called)
call_void_qop(q, stop_streaming, q);
/*
* If you see this warning, then the driver isn't cleaning up properly
* in stop_streaming(). See the stop_streaming() documentation in
* videobuf2-core.h for more information how buffers should be returned
* to vb2 in stop_streaming().
*/
/* owned_by_drv_count这个变量
* __enqueue_in_driver 函数里面进行inc加操作
* vb2_buffer_done 函数里面会进行 dec减操作
* 理想情况下,这个值为0
* 不为0的话,说明还有buffer在被驱动操作,没有存入数据
* 也就是没有执行vb2_buffer_done
*/
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
for (i = 0; i < q->num_buffers; ++i)
if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) {
pr_warn("driver bug: stop_streaming operation is leaving buf %p in active state\n",
q->bufs[i]);
vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR);
}
/* Must be zero now */
WARN_ON(atomic_read(&q->owned_by_drv_count));
}
q->streaming = 0;
q->start_streaming_called = 0;
q->queued_count = 0;
q->error = 0;
/*
* Remove all buffers from videobuf's list...
*/
/*
* 将所有的buffer从队列上移除
*/
INIT_LIST_HEAD(&q->queued_list);
/*
* ...and done list; userspace will not receive any buffers it
* has not already dequeued before initiating cancel.
*/
INIT_LIST_HEAD(&q->done_list);
atomic_set(&q->owned_by_drv_count, 0);
/*
* 这里还会有谁在等待这个唤醒?
* 1.其他应用在使用poll
* 2.其他应用在dqbuf
*/
wake_up_all(&q->done_wq);
/*
* Reinitialize all buffers for next use.
* Make sure to call buf_finish for any queued buffers. Normally
* that's done in dqbuf, but that's not going to happen when we
* cancel the whole queue. Note: this code belongs here, not in
* __vb2_dqbuf() since in vb2_core_dqbuf() there is a critical
* call to __fill_user_buffer() after buf_finish(). That order can't
* be changed, so we can't move the buf_finish() to __vb2_dqbuf().
*/
for (i = 0; i < q->num_buffers; ++i) {
struct vb2_buffer *vb = q->bufs[i];
/*
* qbuf的时候会设置 buf为VB2_BUF_STATE_PREPARED
* 没有问题的话就会把buf放到队列上 状态改成
* VB2_BUF_STATE_QUEUED
* qbuf中应用如果不特殊指定,以下2个值都是1
* need_cache_sync_on_finish
* need_cache_sync_on_prepare
* 其中prepare会调用prepare
* finish调用finish,一般都是成对使用
* 这个也和应用传递的参数有关系
* 比如设置参数 V4L2_BUF_FLAG_NO_CACHE_CLEAN
* 表示不用清cache,那么need_cache_sync_on_prepare = 0
* 就不会调用prepare
* 比如设置参数 V4L2_BUF_FLAG_NO_CACHE_INVALIDATE
* 缓存无效,那么need_cache_sync_on_finish = 0
* 这里就不会调用finish
*/
if ((vb->state == VB2_BUF_STATE_PREPARED ||
vb->state == VB2_BUF_STATE_QUEUED) &&
vb->need_cache_sync_on_finish) {
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, finish,
vb->planes[plane].mem_priv);
}
/*
* 这里解释一下上面的注释
* 2个方面
* 1. buf_finish通常在dqbuf中完成,但是当取消这个队列的时候就不会发生。
* 不会发生猜测是无法正常使用dqbuf
* 2. 解释了为什么不能把buf_finsh放入__vb2_dqbuf
* 因为buf_finsh和__vb2_dqbuf中间有关键代码 __fill_user_buffer
* 这关键代码作用???
* 看了一下代码,__fill_user_buffer会根据vb->state的值,返回给用户空间不同的
* 信息,而__vb2_dqbuf会将vb->state变成固定的值
* 对于buf_finish基本也是和buf_prepare配对使用
* vivi的buf_finish只是打印信息,没有具体作用,不分析
*/
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
vb->state = VB2_BUF_STATE_PREPARED;
call_void_vb_qop(vb, buf_finish, vb);
}
__vb2_dqbuf(vb);
}
}