v4l2框架-开启视频流(stream on)

应用层开启视频流编程例子

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->vb2_ioctl_streamon

int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	struct video_device *vdev = video_devdata(file);

	if (vb2_queue_is_busy(vdev, file))
		return -EBUSY;
	return vb2_streamon(vdev->queue, i);
}

vidioc_streamon
->vb2_ioctl_streamon
->vb2_streamon

int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
	/*如果q->fileio存在,退出*/
	if (vb2_fileio_is_active(q)) {
		dprintk(1, "file io in progress\n");
		return -EBUSY;
	}
	return vb2_internal_streamon(q, type);
}

分析vb2_internal_streamon

static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
	int ret;

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

	/*如果已经streaming退出*/
	if (q->streaming) {
		dprintk(3, "already streaming\n");
		return 0;
	}

   /*没有分配buffer,退出*/
	if (!q->num_buffers) {
		dprintk(1, "no buffers have been allocated\n");
		return -EINVAL;
	}

	/*分配的buffer个数小于q->min_buffers_needed,退出*/
	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.
	 */
	 /*如果需要取出的buffer个数大于等于最小q->min_buffers_needed,退出*/
	if (q->queued_count >= q->min_buffers_needed) {
		ret = vb2_start_streaming(q);
		if (ret) {
			__vb2_queue_cancel(q);
			return ret;
		}
	}

	q->streaming = 1;

	dprintk(3, "successful\n");
	return 0;
}

vidioc_streamon
->vb2_ioctl_streamon
->vb2_streamon
->vb2_internal_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.
	 */

	/*从q->queued_list中逐项取出所有的vb2_queue,并且对每一个vb2_buffer调用__enqueue_in_driver*/
	list_for_each_entry(vb, &q->queued_list, queued_entry)
		__enqueue_in_driver(vb);

	/* Tell the driver to start streaming */
	q->start_streaming_called = 1;
	//调用成功,返回0
	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().
	 */
	 //读出q->owned_by_drv_count的值,如果为正值,成立,说明之前进行过__enqueue_in_driver
	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];
			如果vb->state == VB2_BUF_STATE_ACTIVE,也是在__enqueue_in_driver中调用
			if (vb->state == VB2_BUF_STATE_ACTIVE)
				vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);//把vb->done_entry挂载到q->done_list链表上
		}
		/* Must be zero now */
		WARN_ON(atomic_read(&q->owned_by_drv_count));//减1操作
	}
	/*
	 * 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;
}

最重要的函数__enqueue_in_driver用来设置vb2_buffer的状态为active,执行dma传输的准备操作

static void __enqueue_in_driver(struct vb2_buffer *vb)
{
	struct vb2_queue *q = vb->vb2_queue;
	unsigned int plane;

	//将vb2_buffer的状态置为VB2_BUF_STATE_ACTIVE
	vb->state = VB2_BUF_STATE_ACTIVE;
	//增加q->owned_by_drv_count的值
	atomic_inc(&q->owned_by_drv_count);

	/* sync buffers */
	//同步buffer,这里只有一个plane
	for (plane = 0; plane < vb->num_planes; ++plane)
		call_void_memop(vb, prepare, vb->planes[plane].mem_priv);

	call_void_vb_qop(vb, buf_queue, vb);
}

call_void_memop(vb, prepare, vb->planes[plane].mem_priv)调用的是

vb2_mem_ops vb2_dma_contig_memops

->vb2_dc_prepare

static void vb2_dc_prepare(void *buf_priv)
{
	struct vb2_dc_buf *buf = buf_priv;
	struct sg_table *sgt = buf->dma_sgt;

	/* DMABUF exporter will flush the cache for us */
	if (!sgt || buf->db_attach)
		return;

	/*如果CPU操作了DMA buffer的数据,然后你又想把控制权交给设备上的DMA 控制器,
	*让DMA控制器访问DMA buffer,这时候,在真正让HW(指DMA控制器)去访问DMA buffer之前,你需要调用:
	*以便device(也就是设备上的DMA控制器)可以看到cpu更新后的数据
	*/
	dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
}

call_void_vb_qop(vb, buf_queue, vb)调用的是vb2_ops xvip_dma_queue_qops->xvip_dma_buffer_queue

static void xvip_dma_buffer_queue(struct vb2_buffer *vb)
{
	struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
	struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb);
	struct dma_async_tx_descriptor *desc;
	//获取DMA的地址
	dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0);
	u32 flags;

	if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
		dma->xt.dir = DMA_DEV_TO_MEM;
		dma->xt.src_sgl = false;
		dma->xt.dst_sgl = true;
		dma->xt.dst_start = addr;
	} else {
		flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
		dma->xt.dir = DMA_MEM_TO_DEV;
		dma->xt.src_sgl = true;
		dma->xt.dst_sgl = false;
		dma->xt.src_start = addr;
	}

	dma->xt.frame_size = 1;
	dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp;
	dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size;
	dma->xt.numf = dma->format.height;

	//执行不连续的、交叉的DMA传输
	desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags);
	if (!desc) {
		dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n");
		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
		return;
	}
	desc->callback = xvip_dma_complete;
	desc->callback_param = buf;

	spin_lock_irq(&dma->queued_lock);
	//将dma->queued_bufs放入buf->queue中
	list_add_tail(&buf->queue, &dma->queued_bufs);
	spin_unlock_irq(&dma->queued_lock);

	//提交dma传输请求
	dmaengine_submit(desc);

	//如果为streaming状态,那么执行dma_async_issue_pending
	if (vb2_is_streaming(&dma->queue))
		//启动DMA传输,这里并不成立
		dma_async_issue_pending(dma->dma);
}

回到vb2_start_streaming,call_qop(q, start_streaming, q,atomic_read(&q->owned_by_drv_count))调用vb2_ops xvip_dma_queue_qop->xvip_dma_start_streaming,成功返回0,失败返回其他值

static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count)
{
	struct xvip_dma *dma = vb2_get_drv_priv(vq);
	struct xvip_dma_buffer *buf, *nbuf;
	struct xvip_pipeline *pipe;
	int ret;

	dma->sequence = 0;

	/*
	 * Start streaming on the pipeline. No link touching an entity in the
	 * pipeline can be activated or deactivated once streaming is started.
	 *
	 * Use the pipeline object embedded in the first DMA object that starts
	 * streaming.
	 */
	 //media先不管
	pipe = dma->video.entity.pipe
	     ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe;

	ret = media_entity_pipeline_start(&dma->video.entity, &pipe->pipe);
	if (ret < 0)
		goto error;

	/* Verify that the configured format matches the output of the
	 * connected subdev.
	 */
	 //判断sersor的格式是否支持
	ret = xvip_dma_verify_format(dma);
	if (ret < 0)
		goto error_stop;

	ret = xvip_pipeline_prepare(pipe, dma);
	if (ret < 0)
		goto error_stop;

	/* Start the DMA engine. This must be done before starting the blocks
	 * in the pipeline to avoid DMA synchronization issues.
	 */
	 //启动硬件开始传输
	dma_async_issue_pending(dma->dma);

	/* Start the pipeline. */
	//调用各个media模块的stream on,比如CSI,Sensor等
	xvip_pipeline_set_stream(pipe, true);

	return 0;

error_stop:
	media_entity_pipeline_stop(&dma->video.entity);

error:
	/* Give back all queued buffers to videobuf2. */
	spin_lock_irq(&dma->queued_lock);
	list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) {
		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED);
		list_del(&buf->queue);
	}
	spin_unlock_irq(&dma->queued_lock);

	return ret;
}

返回vb2_start_streaming失败之后会将q->start_streaming_called置0,所以vb2_start_stream的过程为(错误处理不做理解)

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.
	 */

	/*从q->queued_list中逐项取出所有的vb2_queue,并且对每一个vb2_buffer调用__enqueue_in_driver*/
	list_for_each_entry(vb, &q->queued_list, queued_entry)
		__enqueue_in_driver(vb);

	/* Tell the driver to start streaming */
	q->start_streaming_called = 1;
	//调用成功,返回0
	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().
	 */
	 //读出q->owned_by_drv_count的值,如果为正值,成立,说明之前进行过__enqueue_in_driver
	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];
			如果vb->state == VB2_BUF_STATE_ACTIVE,也是在__enqueue_in_driver中调用
			if (vb->state == VB2_BUF_STATE_ACTIVE)
				vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);//把vb->done_entry挂载到q->done_list链表上
		}
		/* Must be zero now */
		WARN_ON(atomic_read(&q->owned_by_drv_count));//减1操作
	}
	/*
	 * 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;
}

以上就是stream on的大致分析,同步buffers,启动DMA传输,执行完成之后将q->streaming = 1置为1

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值