从应用调用vivi驱动分析v4l2 -- 出队列(VIDIOC_DQBUF)

Linux v4l2架构学习总链接

vivi代码

v4l2测试代码

step 7 : 读取数据

2,出队列

struct v4l2_buffer buffer;

buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;

if (ioctl(fd, VIDIOC_DQBUF, &buffer) < 0)
{
    printf("ERR(%s):VIDIOC_DQBUF failed, dropped frame\n", __func__);
    return -1;
}

调用驱动代码 vidioc_dqbuf

static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
}

vidioc_dqbuf
    -> vb2_dqbuf

        -> vb2_core_dqbuf


int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
{
	int ret;

	if (vb2_fileio_is_active(q)) {
		dprintk(1, "file io in progress\n");
		return -EBUSY;
	}

	if (b->type != q->type) {
		dprintk(1, "invalid buffer type\n");
		return -EINVAL;
	}

	ret = vb2_core_dqbuf(q, NULL, b, nonblocking);

	/*
	 *  After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be
	 *  cleared.
	 */
	b->flags &= ~V4L2_BUF_FLAG_DONE;

	return ret;
}
int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
		   bool nonblocking)
{
	struct vb2_buffer *vb = NULL;
	int ret;

        /*
         * 看下面具体分析
         * ret = 0的时候,得到有效的buf的地址 vb
         */


	ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
	if (ret < 0)
		return ret;

	switch (vb->state) {

        /*
         * 正常来说,STATE_DONE
         */

	case VB2_BUF_STATE_DONE:
		dprintk(3, "returning done buffer\n");
		break;
	case VB2_BUF_STATE_ERROR:
		dprintk(3, "returning done buffer with errors\n");
		break;
	default:
		dprintk(1, "invalid buffer state\n");
		return -EINVAL;
	}

        /*
         * vb可以访问到缓冲区,驱动可以最后对缓冲区的数据进行修改
         * 不修改的话,后面将释放放给用户空间
         */

	call_void_vb_qop(vb, buf_finish, vb);

	if (pindex)
		*pindex = vb->index;

	/* Fill buffer information for the userspace */

        
        /*
         * __vb2_get_done_vb中分析过
         * 如果这个last_buffer_dequeued置位了,会不会忽略最后一帧
         * last_buffer_dequeued的条件是
         * 1.V4L2_BUF_FLAG_DONE,vb2_buffer_done中会置位
         * 2.V4L2_BUF_FLAG_DONE,驱动中可以置位
         * 以上2个条件成立才会将last_buffer_dequeued置1
         * 关键是last_buffer_dequeued在哪里置1呢?
         * 就是下面这个fill_user_buffer
         * 说明出来完标记了V4L2_BUF_FLAG_DONE和V4L2_BUF_FLAG_LAST的
         * 这最后一帧后,才将last_buffer_dequeued置1,所以最后一帧不会丢
         */

	if (pb)
		call_void_bufop(q, fill_user_buffer, vb, pb);

	/* Remove from videobuf queue */

        /*
         * 出队列,并将计数减1
         */
	list_del(&vb->queued_entry);
	q->queued_count--;

	trace_vb2_dqbuf(q, vb);

	/* go back to dequeued state */

        /*
         * 对于MMAP
         * 主要就是 vb->state = VB2_BUF_STATE_DEQUEUED;
         * 更改状态
         */

	__vb2_dqbuf(vb);

	dprintk(2, "dqbuf of buffer %d, with state %d\n",
			vb->index, vb->state);

	return 0;

}

vidioc_dqbuf
    -> vb2_dqbuf

        -> vb2_core_dqbuf

             -> __vb2_get_done_vb


static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
{
	/*
	 * All operations on vb_done_list are performed under done_lock
	 * spinlock protection. However, buffers may be removed from
	 * it and returned to userspace only while holding both driver's
	 * lock and the done_lock spinlock. Thus we can be sure that as
	 * long as we hold the driver's lock, the list will remain not
	 * empty if list_empty() check succeeds.
	 */

	for (;;) {
		int ret;


                /*
                 * waiting_in_dqbuf这个值什么时候置1?
                 * 见这个函数下面的代码.....
                 */


		if (q->waiting_in_dqbuf) {
			dprintk(1, "another dup()ped fd is waiting for a buffer\n");
			return -EBUSY;
		}


                /*
                 * 调用过STREAM_ON 这里streaming值为1
                 * 如何为0,说明执行力stream_off操作,就不会有数据继续产生
                 * 所以这里也不用继续执行了
                 */


		if (!q->streaming) {
			dprintk(1, "streaming off, will not wait for buffers\n");
			return -EINVAL;
		}

		if (q->error) {
			dprintk(1, "Queue in error state, will not wait for buffers\n");
			return -EIO;
		}

                
                /*
                 * 如果这个last_buffer_dequeued置位了
                 * 那么说明后面没有有效的帧数据了,所以直接返回就行
                 * 问题来了,这里会不会忽略最后一帧?
                 * 答案是不会,原因后面分析
                 */
                

		if (q->last_buffer_dequeued) {
			dprintk(3, "last buffer dequeued already, will not wait for buffers\n");
			return -EPIPE;
		}



                /*
                 * done_list上有buffer则跳出这个循环,继续往下走
                 * 对于使用了select的方式,这里应该就返回了
                 */


		if (!list_empty(&q->done_list)) {
			/*
			 * Found a buffer that we were waiting for.
			 */
			break;
		}


                /*
                 * 如果应用传递的是不堵塞
                 * 那么这里直接返回
                 */


		if (nonblocking) {
			dprintk(3, "nonblocking and no buffers to dequeue, will not wait\n");
			return -EAGAIN;
		}

                
                /*
                 * 走到这里说明应用打算使用阻塞方式
                 * 将waiting_in_dqbuf置1
                 */


		q->waiting_in_dqbuf = 1;
		/*
		 * 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.
		 */


                /*
                 * wait_prepare 对应vivi驱动的vivi_unlock
                 * 主要就是代码 mutex_unlock(&dev->mutex);
                 * 释放自旋锁
                 * 没有这个必要要使用锁???
                 */


		call_void_qop(q, wait_prepare, q);

		/*
		 * All locks have been released, it is safe to sleep now.
		 */
		dprintk(3, "will sleep waiting for buffers\n");


                /*
                 * wait_event_interruptible(wq, condition)
                 * 对于condition来说
                 * condition = 0 休眠
                 * condition = 1 唤醒
                 * 前提是wake_up_interruptible唤醒后,进一步才是condition
                 * 对于返回值 
                 * 1.condition = 1时调用这个函数将直接返回0
                 * 2.正常wakeup且condition=1 返回0
                 * 3.其他信号唤醒,返回负值
                 */


		ret = wait_event_interruptible(q->done_wq,
				!list_empty(&q->done_list) || !q->streaming ||
				q->error);

		/*
		 * We need to reevaluate both conditions again after reacquiring
		 * the locks or return an error if one occurred.
		 */


                /*
                 * wait_finish 对应vivi驱动的vivi_lock
                 * 主要就是代码 mutex_lock(&dev->mutex);
                 * 获取自旋锁
                 *
                 * 如果这样的话,就算stream_off,自旋锁还是没有释放 ???
                 */               


		call_void_qop(q, wait_finish, q);
    		q->waiting_in_dqbuf = 0;
		if (ret) {
			dprintk(1, "sleep was interrupted\n");
			return ret;
		}
	}
	return 0;
}


static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
			     void *pb, int nonblocking)
{
	unsigned long flags;
	int ret = 0;

	/*
	 * Wait for at least one buffer to become available on the done_list.
	 */


        /*
         * ret = 0 获取到了有效的buffer
         */


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


        /*
         * 得到一个有效的buffer,*vb是地址
         */


	*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
	/*
	 * Only remove the buffer from done_list if all planes can be
	 * handled. Some cases such as V4L2 file I/O and DVB have pb
	 * == NULL; skip the check then as there's nothing to verify.
	 */

        /*
         * verify_planes_array 这里对应的是多平台
         */


	if (pb)
		ret = call_bufop(q, verify_planes_array, *vb, pb);
	if (!ret)
                /*
                 * 将buffer从done_list上移除
                 */

		list_del(&(*vb)->done_entry);
	spin_unlock_irqrestore(&q->done_lock, flags);

	return ret;
}

应用程序放在后面写

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dianlong_lee

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

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

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

打赏作者

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

抵扣说明:

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

余额充值