1.前言
本文对学习V4L2框架缓存管理做一个笔记。
2.v4l2中关于缓存管理的结构体
在v4l2中,使用vb2_queue结构体作为缓存管理的结构体
struct vb2_queue {
enum v4l2_buf_type type;
unsigned int io_modes;
unsigned fileio_read_once:1;
unsigned fileio_write_immediately:1;
unsigned allow_zero_bytesused:1;
struct mutex *lock;
struct v4l2_fh *owner;
const struct vb2_ops *ops;
const struct vb2_mem_ops *mem_ops;
void *drv_priv;
unsigned int buf_struct_size;
u32 timestamp_flags;
gfp_t gfp_flags;
u32 min_buffers_needed;
/* private: internal use only */
struct mutex mmap_lock;
enum v4l2_memory memory;
struct vb2_buffer *bufs[VIDEO_MAX_FRAME];
unsigned int num_buffers;
struct list_head queued_list;
unsigned int queued_count;
atomic_t owned_by_drv_count;
struct list_head done_list;
spinlock_t done_lock;
wait_queue_head_t done_wq;
void *alloc_ctx[VIDEO_MAX_PLANES];
unsigned int plane_sizes[VIDEO_MAX_PLANES];
unsigned int streaming:1;
unsigned int start_streaming_called:1;
unsigned int error:1;
unsigned int waiting_for_buffers:1;
struct vb2_fileio_data *fileio;
struct vb2_threadio_data *threadio;
...........................
};
这个结构体中重要的成员为vb2_ops,vb2_mem_ops,在xilinx平台中video_device时初始化这两个参数
2.1 vb2_ops
vb2_ops用来对buffer的入队出队进行管理,等后续分析
struct vb2_ops {
int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[]);
void (*wait_prepare)(struct vb2_queue *q);
void (*wait_finish)(struct vb2_queue *q);
int (*buf_init)(struct vb2_buffer *vb);
int (*buf_prepare)(struct vb2_buffer *vb);
void (*buf_finish)(struct vb2_buffer *vb);
void (*buf_cleanup)(struct vb2_buffer *vb);
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
void (*stop_streaming)(struct vb2_queue *q);
void (*buf_queue)(struct vb2_buffer *vb);
};
2.2 vb2_mem_ops
vb2_mem_ops用来申请,获取,释放缓存等函数
struct vb2_mem_ops {
void *(*alloc)(struct device *dev, unsigned long attrs,
unsigned long size,
enum dma_data_direction dma_dir,
gfp_t gfp_flags);
void (*put)(void *buf_priv);
struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
void *(*get_userptr)(struct device *dev, unsigned long vaddr,
unsigned long size,
enum dma_data_direction dma_dir);
void (*put_userptr)(void *buf_priv);
void (*prepare)(void *buf_priv);
void (*finish)(void *buf_priv);
void *(*attach_dmabuf)(struct device *dev,
struct dma_buf *dbuf,
unsigned long size,
enum dma_data_direction dma_dir);
void (*detach_dmabuf)(void *buf_priv);
int (*map_dmabuf)(void *buf_priv);
void (*unmap_dmabuf)(void *buf_priv);
void *(*vaddr)(void *buf_priv);
void *(*cookie)(void *buf_priv);
unsigned int (*num_users)(void *buf_priv);
int (*mmap)(void *buf_priv, struct vm_area_struct *vma);
};
在v4l2中实现了三种mem_ops,用来对buffer进行操作,分别为
/* drivers/media/v4l2-core/videobuf2-vmalloc.c
分配的缓冲区,虚拟地址连续,物理地址不一定连续
*/
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
#ifdef CONFIG_HAS_DMA
.get_dmabuf = vb2_vmalloc_get_dmabuf,
#endif
.map_dmabuf = vb2_vmalloc_map_dmabuf,
.unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
.attach_dmabuf = vb2_vmalloc_attach_dmabuf,
.detach_dmabuf = vb2_vmalloc_detach_dmabuf,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
/* drivers/media/v4l2-core/videobuf2-dma-sg.c
对于支持Scatter/Gather的DMA,可使用vb2_dma_sg_memops
*/
const struct vb2_mem_ops vb2_dma_sg_memops = {
.alloc = vb2_dma_sg_alloc,
.put = vb2_dma_sg_put,
.get_userptr = vb2_dma_sg_get_userptr,
.put_userptr = vb2_dma_sg_put_userptr,
.prepare = vb2_dma_sg_prepare,
.finish = vb2_dma_sg_finish,
.vaddr = vb2_dma_sg_vaddr,
.mmap = vb2_dma_sg_mmap,
.num_users = vb2_dma_sg_num_users,
.get_dmabuf = vb2_dma_sg_get_dmabuf,
.map_dmabuf = vb2_dma_sg_map_dmabuf,
.unmap_dmabuf = vb2_dma_sg_unmap_dmabuf,
.attach_dmabuf = vb2_dma_sg_attach_dmabuf,
.detach_dmabuf = vb2_dma_sg_detach_dmabuf,
.cookie = vb2_dma_sg_cookie,
};
/* drivers/media/v4l2-core/videobuf2-dma-contig.c */
/*分配的缓冲区,物理地址连续*/
const struct vb2_mem_ops vb2_dma_contig_memops = {
.alloc = vb2_dc_alloc,
.put = vb2_dc_put,
.get_dmabuf = vb2_dc_get_dmabuf,
.cookie = vb2_dc_cookie,
.vaddr = vb2_dc_vaddr,
.mmap = vb2_dc_mmap,
.get_userptr = vb2_dc_get_userptr,
.put_userptr = vb2_dc_put_userptr,
.prepare = vb2_dc_prepare,
.finish = vb2_dc_finish,
.map_dmabuf = vb2_dc_map_dmabuf,
.unmap_dmabuf = vb2_dc_unmap_dmabuf,
.attach_dmabuf = vb2_dc_attach_dmabuf,
.detach_dmabuf = vb2_dc_detach_dmabuf,
.num_users = vb2_dc_num_users,
};
3. 申请帧缓冲区
在获取图像数据前需要内核帧缓存区,应用层编程为:
struct v4l2_requestbuffers
{
__u32 count; // 缓冲区内缓冲帧的数目
enum v4l2_buf_type type; // 缓冲帧数据格式
enum v4l2_memorymemory; // 区别是内存映射还是用户指针方式
__u32 reserved[2];
};
struct v4l2_requestbuffers req;
req.count = nr_bufs; //缓存数量
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;//内存映射方式
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
{
printf("ERR(%s):VIDIOC_REQBUFS failed\n", __func__);
return -1;
}
通过ioctl函数向底层传入VIDIOC_REQBUFS命令来申请帧缓冲区,这里内核中Xilinx平台为例子,执行过程为
4.详细过程
在创建video_device时初始化了v4l2 ioctl函数集,vb2_ops,vb2_mem_ops,在xilinx-dma.c
//xilinx-dma.c
static const struct v4l2_file_operations xvip_dma_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
};
//xilinx-dma.c
static struct vb2_ops xvip_dma_queue_qops = {
.queue_setup = xvip_dma_queue_setup,
.buf_prepare = xvip_dma_buffer_prepare,
.buf_queue = xvip_dma_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.start_streaming = xvip_dma_start_streaming,
.stop_streaming = xvip_dma_stop_streaming,
};
//videobuf2-dma-contig.c
const struct vb2_mem_ops vb2_dma_contig_memops = {
.alloc = vb2_dc_alloc,
.put = vb2_dc_put,
.get_dmabuf = vb2_dc_get_dmabuf,
.cookie = vb2_dc_cookie,
.vaddr = vb2_dc_vaddr,
.mmap = vb2_dc_mmap,
.get_userptr = vb2_dc_get_userptr,
.put_userptr = vb2_dc_put_userptr,
.prepare = vb2_dc_prepare,
.finish = vb2_dc_finish,
.map_dmabuf = vb2_dc_map_dmabuf,
.unmap_dmabuf = vb2_dc_unmap_dmabuf,
.attach_dmabuf = vb2_dc_attach_dmabuf,
.detach_dmabuf = vb2_dc_detach_dmabuf,
.num_users = vb2_dc_num_users,
};
dma->video.fops = &xvip_dma_fops;
dma->queue.ops = &xvip_dma_queue_qops;
dma->queue.mem_ops = &vb2_dma_contig_memops;
在应用层掉用ioctl(fd, VIDIOC_REQBUFS, &req)
时,会调用xvip_dma_fops->.unlocked_ioctl->video_ioctl2->video_usercopy->__video_do_ioctl->v4l_reqbufs->.vidioc_reqbuf-> vb2_ioctl_reqbufs
static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct v4l2_requestbuffers *p = arg;
int ret = check_fmt(file, p->type);
if (ret)
return ret;
CLEAR_AFTER_FIELD(p, memory);
return ops->vidioc_reqbufs(file, fh, p);
}
这里的 ops->vidioc_reqbufs对应xilinx驱动的 vb2_ioctl_reqbufs
nt vb2_ioctl_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct video_device *vdev = video_devdata(file);
/*
1.__verify_memory_type判断v4l2_requestbuffers的memory
是否为VB2_MEMORY_MMAP ,VB2_MEMORY_USERPTR,VB2_MEMORY_DMABUF
应用层传入的是V4L2_MEMORY_MMAP,可以对应上
2.判断v4l2_requestbuffers->type是否与vdev->queue->type是否相等
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xvip_dma_init(xdev, dma, type, index);
dma->queue.type = type;
dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
所以v4l2_requestbuffers->type与vdev->queue->type相等
3.应用层使用的是memory类型为V4L2_MEMORY_MMAP,还会判断
if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
!q->mem_ops->put || !q->mem_ops->mmap),即 q->mem_ops的alloc,
put,mmap函数是否存在
4.vb2_fileio_is_active(q)
判断q->fileio值是否为空
*/
int res = __verify_memory_type(vdev->queue, p->memory, p->type);
if (res)
return res;
//如果判断到vdev->queue->owner以及vdev->queue->owner != file->private_data;
//成立的话return,在最好一句会赋值
if (vb2_queue_is_busy(vdev, file))
return -EBUSY;
res = __reqbufs(vdev->queue, p);
/* If count == 0, then the owner has released all buffers and he
is no longer owner of the queue. Otherwise we have a new owner. */
if (res == 0)
//将 file->private_data 赋值给vdev->queue->owner
vdev->queue->owner = p->count ? file->private_data : NULL;
return res;
}
分析__reqbufs
static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int num_buffers, allocated_buffers, num_planes = 0;
int ret;
/*
如果为streaming状态,返回
默认为flase
*/
if (q->streaming) {
dprintk(1, "streaming active\n");
return -EBUSY;
}
/*
if 满足条件的条件
1.申请的buf个数为0
2.有之前申请没有释放的缓冲区
2.memory的类型发生变化
*/
if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
/*
* We already have buffers allocated, so first check if they
* are not in use and can be freed.
*/
mutex_lock(&q->mmap_lock);
/*
对于之前是mmap的方式,需要判断之前申请的buffer是不是在使用中
如果buffer在使用中,则return
*/
if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
mutex_unlock(&q->mmap_lock);
dprintk(1, "memory in use, cannot free\n");
return -EBUSY;
}
/*
* Call queue_cancel to clean up any buffers in the PREPARED or
* QUEUED state which is possible if buffers were prepared or
* queued without ever calling STREAMON.
*/
/*
释放所有的buffer.
将q->num_buffers赋值为0
*/
__vb2_queue_cancel(q);
ret = __vb2_queue_free(q, q->num_buffers);
mutex_unlock(&q->mmap_lock);
if (ret)
return ret;
/*
* In case of REQBUFS(0) return immediately without calling
* driver's queue_setup() callback and allocating resources.
*/
/*
申请buffer为0,不做任何操作
*/
if (req->count == 0)
return 0;
}
/*
* Make sure the requested values and current defaults are sane.
*
*/
/*
1.取申请的buffer个数,其中VIDEO_MAX_FRAME=32
q->min_buffers_needed=0.一般个数为req->count
*/
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
//memory类型赋值
q->memory = req->memory;
/*
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
//调用q->ops->queue_setup即xvip_dma_queue_qops->queue_setup->xvip_dma_queue_setup
ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
q->plane_sizes, q->alloc_ctx);
if (ret)
return ret;
-------
分析xvip_dma_queue_setup
static int
xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct xvip_dma *dma = vb2_get_drv_priv(vq);
/* Make sure the image size is large enough. */
/*确保申请的图像大小小于平台所能支持的大小
最大为1920*1080*16
*/
if (fmt && fmt->fmt.pix.sizeimage < dma->format.sizeimage)
return -EINVAL;
/*
nplanes与颜色空间有关系,暂时分析
*/
*nplanes = 1;
/*设置sizes[0],与alloc_ctxs[0]
* 在dma初始化的时候已经初始化alloc_ctxs
*/
sizes[0] = fmt ? fmt->fmt.pix.sizeimage : dma->format.sizeimage;
alloc_ctxs[0] = dma->alloc_ctx;
return 0;
}
回到__reqbufs,分析重要函数__vb2_queue_alloc
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
unsigned int num_buffers, unsigned int num_planes)
{
unsigned int buffer;
struct vb2_buffer *vb;
int ret;
//从我们的应用层例子,这里的num_buffers为5
for (buffer = 0; buffer < num_buffers; ++buffer) {
/* Allocate videobuf buffer structures */
//每一个buffer申请一个vb2_buffer结构体
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
if (!vb) {
dprintk(1, "memory alloc for buffer struct failed\n");
break;
}
/* Length stores number of planes for multiplanar buffers */
//判断是否为多平面
if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
vb->v4l2_buf.length = num_planes;
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
//buffer编号
vb->v4l2_buf.index = q->num_buffers + buffer;
vb->v4l2_buf.type = q->type;
vb->v4l2_buf.memory = memory;
/* Allocate video buffer memory for the MMAP type */
if (memory == V4L2_MEMORY_MMAP) {
ret = __vb2_buf_mem_alloc(vb);
if (ret) {
dprintk(1, "failed allocating memory for "
"buffer %d\n", buffer);
kfree(vb);
break;
}
```
跟进重要函数__vb2_buf_mem_alloc,如果memory类型为V4L2_MEMORY_MMAP,则调用__vb2_buf_mem_alloc
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
enum dma_data_direction dma_dir =
V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
void *mem_priv;
int plane;
/*
* Allocate memory for all planes in this buffer
* NOTE: mmapped areas should be page aligned
*/
//针对于多平面进行操作
for (plane = 0; plane < vb->num_planes; ++plane) {
//字节对齐,q->plane_sizes[plane]在queue_set中被初始化
unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
//调用vb->q->mem_ops->alloc.即vb2_dma_contig_memops->alloc->vb2_dc_alloc
mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
size, dma_dir, q->gfp_flags);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
//设置私有变量,以及对应的平面大小
vb->planes[plane].mem_priv = mem_priv;
vb->v4l2_planes[plane].length = q->plane_sizes[plane];
}
return 0;
free:
/* Free already allocated memory if one of the allocations failed */
for (; plane > 0; --plane) {
call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);
vb->planes[plane - 1].mem_priv = NULL;
}
return -ENOMEM;
}
分析vb2_dma_contig_memops->alloc->vb2_dc_alloc
,其分配的缓冲区虚拟地址和物理地址都连续
[drivers\media\v4l2-core\videobuf2-dma-contig.c]
struct vb2_dc_buf {
struct device *dev;
void *vaddr; // 内存虚拟地址
unsigned long size; // 内存大小
dma_addr_t dma_addr; // 内存物理地址
enum dma_data_direction dma_dir; // DMA传输方向
struct sg_table *dma_sgt; // SG DMA相关
/* MMAP相关变量 */
struct vb2_vmarea_handler handler;
atomic_t refcount;
struct sg_table *sgt_base;
struct vm_area_struct *vma; // USERPTR相关变量
struct dma_buf_attachment *db_attach; // DMABUF相关变量
};
static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
enum dma_data_direction dma_dir, gfp_t gfp_flags)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct device *dev = conf->dev;
struct vb2_dc_buf *buf;
//分配一个struct vb2_dc_buf结构体
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
//然后分配存储图像数据的缓冲区,此缓冲区的物理地址和虚拟地址都连续
buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
GFP_KERNEL | gfp_flags);
if (!buf->vaddr) {
dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
/* Prevent the device from being released while the buffer is used */
buf->dev = get_device(dev);
buf->size = size;//保存图像缓冲区大小
buf->dma_dir = dma_dir;//设置DMA传输方向
//设置vb2_dc_buf->vb2_vmarea_handler结构体
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_dc_put;
buf->handler.arg = buf;
//增加引用计数
atomic_inc(&buf->refcount);
//返回分配的buffer大小
return buf;
}
回到__vb2_queue_alloc继续分析
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
unsigned int num_buffers, unsigned int num_planes)
{
unsigned int buffer;
struct vb2_buffer *vb;
int ret;
//从我们的应用层例子,这里的num_buffers为5
for (buffer = 0; buffer < num_buffers; ++buffer) {
/* Allocate videobuf buffer structures */
//每一个buffer申请一个vb2_buffer结构体
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
if (!vb) {
dprintk(1, "memory alloc for buffer struct failed\n");
break;
}
/* Length stores number of planes for multiplanar buffers */
//判断是否为多平面
if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
vb->v4l2_buf.length = num_planes;
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
//buffer编号
vb->v4l2_buf.index = q->num_buffers + buffer;
vb->v4l2_buf.type = q->type;
vb->v4l2_buf.memory = memory;
/* Allocate video buffer memory for the MMAP type */
if (memory == V4L2_MEMORY_MMAP) {
ret = __vb2_buf_mem_alloc(vb);
if (ret) {
dprintk(1, "failed allocating memory for "
"buffer %d\n", buffer);
kfree(vb);
break;
}
/*
* Call the driver-provided buffer initialization
* callback, if given. An error in initialization
* results in queue setup failure.
*/
//用驱动提供的buf_init函数进行初始化,xilinx没有提供
ret = call_vb_qop(vb, buf_init, vb);
if (ret) {
dprintk(1, "buffer %d %p initialization"
" failed\n", buffer, vb);
__vb2_buf_mem_free(vb);
kfree(vb);
break;
}
}
//保存缓冲区的地址
q->bufs[q->num_buffers + buffer] = vb;
}
//设置所有缓冲区的每个plane的长度
__setup_lengths(q, buffer);
------------------------------------------------------------------
static void __setup_lengths(struct vb2_queue *q, unsigned int n)
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
for (plane = 0; plane < vb->num_planes; ++plane)
vb->v4l2_planes[plane].length = q->plane_sizes[plane];
}
}
---------------------------------------------------------------------
//MMAP方式需要设置每一个plane的偏移值
if (memory == V4L2_MEMORY_MMAP)
__setup_offsets(q, buffer);
dprintk(1, "allocated %d buffers, %d plane(s) each\n",
buffer, num_planes);
return buffer;
}
分析__setup_offset
static void __setup_offsets(struct vb2_queue *q, unsigned int n)
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
unsigned long off;
//如果q->num_buffers存在
if (q->num_buffers) {
struct v4l2_plane *p;
//获取上一个编号的vb2_buffer
vb = q->bufs[q->num_buffers - 1];
/*获取上一个编号vb2_buffer保存的v4l2_plane,
*从前面的赋值来看vb->num_planes=0
*/
p = &vb->v4l2_planes[vb->num_planes - 1];
//更新off,off的值为p->m.mem_offset + p->length
off = PAGE_ALIGN(p->m.mem_offset + p->length);
} else {
//如果为第一个vb2_buffer,不需要处理,offset为0
off = 0;
}
for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
//对多平面来说,假设平面有4个,那么
//vb->v4l2_planes[1].m.mem_offset =
//PAGE_ALIGN(vb->v4l2_planes[plane].length) + vb->v4l2_planes[plane].m.mem_offset
for (plane = 0; plane < vb->num_planes; ++plane) {
//将前面得到的off值赋值给vb->v4l2_planes[plane].m.mem_offset
vb->v4l2_planes[plane].m.mem_offset = off;
dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
buffer, plane, off);
//则off = PAGE_ALIGN(vb->v4l2_planes[plane].length) + vb->v4l2_planes[plane].m.mem_offset
off += vb->v4l2_planes[plane].length;
off = PAGE_ALIGN(off);
}
}
}
再次回到__reqbufs
static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int num_buffers, allocated_buffers, num_planes = 0;
int ret;
/*
如果为streaming状态,返回
默认为flase
*/
if (q->streaming) {
dprintk(1, "streaming active\n");
return -EBUSY;
}
/*
if 满足条件的条件
1.申请的buf个数为0
2.有之前申请没有释放的缓冲区
2.memory的类型发生变化
*/
if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
/*
* We already have buffers allocated, so first check if they
* are not in use and can be freed.
*/
mutex_lock(&q->mmap_lock);
/*
对于之前是mmap的方式,需要判断之前申请的buffer是不是在使用中
如果buffer在使用中,则return
*/
if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
mutex_unlock(&q->mmap_lock);
dprintk(1, "memory in use, cannot free\n");
return -EBUSY;
}
/*
* Call queue_cancel to clean up any buffers in the PREPARED or
* QUEUED state which is possible if buffers were prepared or
* queued without ever calling STREAMON.
*/
/*
释放所有的buffer.
将q->num_buffers赋值为0
*/
__vb2_queue_cancel(q);
ret = __vb2_queue_free(q, q->num_buffers);
mutex_unlock(&q->mmap_lock);
if (ret)
return ret;
/*
* In case of REQBUFS(0) return immediately without calling
* driver's queue_setup() callback and allocating resources.
*/
/*
申请buffer为0,不做任何操作
*/
if (req->count == 0)
return 0;
}
/*
* Make sure the requested values and current defaults are sane.
*
*/
/*
1.取申请的buffer个数,其中VIDEO_MAX_FRAME=32
q->min_buffers_needed=0.一般个数为req->count
*/
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
//memory类型赋值
q->memory = req->memory;
/*
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
//调用q->ops->queue_setup即xvip_dma_queue_qops->queue_setup->xvip_dma_queue_setup
ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
q->plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
//获取申请到的buffer个数
allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
if (allocated_buffers == 0) {
dprintk(1, "memory allocation failed\n");
return -ENOMEM;
}
/*
* There is no point in continuing if we can't allocate the minimum
* number of buffers needed by this vb2_queue.
*/
if (allocated_buffers < q->min_buffers_needed)
ret = -ENOMEM;
/*
* Check if driver can handle the allocated number of buffers.
*/
//如果分配的缓冲区小于需要分配的数量,需要再次调用驱动提供的queue_setup函数
//从上面的分析,暂时不知道这种情况怎么样会出现
if (!ret && allocated_buffers < num_buffers) {
num_buffers = allocated_buffers;
ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
&num_planes, q->plane_sizes, q->alloc_ctx);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
/*
* Either the driver has accepted a smaller number of buffers,
* or .queue_setup() returned an error
*/
}
mutex_lock(&q->mmap_lock);
//将申请到的buffer个数allocated_buffers赋值给q->num_buffers
q->num_buffers = allocated_buffers;
if (ret < 0) {
/*
* Note: __vb2_queue_free() will subtract 'allocated_buffers'
* from q->num_buffers.
*/
__vb2_queue_free(q, allocated_buffers);
mutex_unlock(&q->mmap_lock);
return ret;
}
mutex_unlock(&q->mmap_lock);
/*
* Return the number of successfully allocated buffers
* to the userspace.
*/
//将分配的缓冲区个数返回给应用层
req->count = allocated_buffers;
q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type);
return 0;
}
以上
参考资料:
Linux V4L2驱动框架分析之(三):v4l2设备的缓存管理_m0_46525308的博客-CSDN博客
Linux V4L2子系统-videobuf2框架分析(三)_业余程序员plus的博客-CSDN博客
从应用调用vivi驱动分析v4l2 – 申请缓存(VIDIOC_REQBUFS)_dianlong_lee的博客-CSDN博客