启动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