从应用调用vivi驱动分析v4l2 -- 停止设备(VIDIOC_STREAMOFF)

Linux v4l2架构学习总链接

vivi代码

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);
	}
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值