申请内存
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;
}
同样会调用到__video_do_ioctl
//drivers\media\video\v4l2-ioctl.c
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
... ...
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *p = arg;
if (!ops->vidioc_reqbufs)
break;
//根据上层传来的V4L2_BUF_TYPE_VIDEO_CAPTURE判断一下,底层的驱动
//是否实现了vidioc_g_fmt_vid_cap或vidioc_g_fmt_vid_cap_mplane回调函数
ret = check_fmt(ops, p->type);
if (ret)
break;
if (p->type < V4L2_BUF_TYPE_PRIVATE)
CLEAR_AFTER_FIELD(p, memory);
//下面就是申请buffer的函数了
ret = ops->vidioc_reqbufs(file, fh, p);
dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
p->count,
prt_names(p->type, v4l2_type_names),
prt_names(p->memory, v4l2_memory_names));
break;
}
... ...
}
下面看一下vidioc_reqbufs的定义
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct vivi_dev *dev = video_drvdata(file);
return vb2_reqbufs(&dev->vb_vidq, p);
}
//下面vb2_reqbufs的函数有点长,中间有省略部分
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int num_buffers, num_planes;
unsigned long plane_sizes[VIDEO_MAX_PLANES];
int ret = 0;
//此时q->fileio应该为0,条件不成立跳过
if (q->fileio) {
dprintk(1, "reqbufs: file io in progress\n");
return -EBUSY;
}
//目前这个只支持V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR
//上层传入的时V4L2_MEMORY_MMAP, 所以跳过这个if
if (req->memory != V4L2_MEMORY_MMAP
&& req->memory != V4L2_MEMORY_USERPTR) {
dprintk(1, "reqbufs: unsupported memory type\n");
return -EINVAL;
}
//判断vb2_queue->type是否是请求的req->type
//vivi驱动初始化时设置为了V4L2_BUF_TYPE_VIDEO_CAPTURE
//所以下面条件不成立跳过
if (req->type != q->type) {
dprintk(1, "reqbufs: requested type is incorrect\n");
return -EINVAL;
}
//当前vb2_queue数据流的状态,这里还没申请所以这个状态应是0
if (q->streaming) {
dprintk(1, "reqbufs: streaming active\n");
return -EBUSY;
}
/*
* Make sure all the required memory ops for given memory type
* are available.
*/
//判断buffer申请类型V4L2_MEMORY_MMAP时,所需的回调函数是否已经初始化
//q->mem_ops->alloc, q->mem_ops->put和q->mem_ops->mmap
//这些回调函数时在vivi驱动初始化时赋值的q->mem_ops = &vb2_vmalloc_memops;
if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
return -EINVAL;
}
if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
return -EINVAL;
}
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.
*/
if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
dprintk(1, "reqbufs: memory in use, cannot free\n");
return -EBUSY;
}
__vb2_queue_free(q);
/*
* In case of REQBUFS(0) return immediately without calling
* driver's queue_setup() callback and allocating resources.
*/
if (req->count == 0)
return 0;
}
/*
* Make sure the requested values and current defaults are sane.
*/
//取应用申请的buffer数量和VIDEO_MAX_FRAME中较小的值
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
memset(plane_sizes, 0, sizeof(plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
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.
*/
//下面调用vb2_queue.ops.queue_setup函数,在vivi驱动初始化时赋值
//q->ops = &vivi_video_qops;queue_setup主要根据分辨率和申请的
//buffer数来计算num_planes的数量,plane_szies,并重新调整buffer数量
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
//下面函数会根据num_buffers, num_planes, plane_sizes,先初始化vb2_buffer结构
//每个buffer对应一个vb2_buffer描述结构,然后调用vb2_queue.mem_ops中的
//alloc函数为具体的摄像头数据申请存放空间
ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
plane_sizes);
if (ret == 0) {
dprintk(1, "Memory allocation failed\n");
return -ENOMEM;
}
/*
* Check if driver can handle the allocated number of buffers.
*/
if (ret < num_buffers) {
unsigned int orig_num_buffers;
orig_num_buffers = num_buffers = ret;
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
plane_sizes, q->alloc_ctx);
if (ret)
goto free_mem;
if (orig_num_buffers < num_buffers) {
ret = -ENOMEM;
goto free_mem;
}
/*
* Ok, driver accepted smaller number of buffers.
*/
ret = num_buffers;
}
/*
* Return the number of successfully allocated buffers
* to the userspace.
*/
req->count = ret;
return 0;
free_mem:
__vb2_queue_free(q);
return ret;
}
EXPORT_SYMBOL_GPL(vb2_reqbufs);
下面接着分析queue_setup和__vb2_queue_alloc函数。在这之前先贴一下vb2_queue.ops和vb2_queue.mem_ops的初始化时的函数
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
static struct vb2_ops vivi_video_qops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
.buf_finish = buffer_finish,
.buf_cleanup = buffer_cleanup,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
.wait_prepare = vivi_unlock,
.wait_finish = vivi_lock,
};
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
看一下queue_setup具体干了什么事
static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
unsigned int *nplanes, unsigned long sizes[],
void *alloc_ctxs[])
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
//下面dev->width和height就是上一篇设置图片格式时设置下来的。
size = dev->width * dev->height * 2;
//如果buffer count为0,则默认为32个buffer count
if (0 == *nbuffers)
*nbuffers = 32;
//计算大小,如果超过了 vid_limit * 1024 * 1024,则较少buffer的数量
while (size * *nbuffers > vid_limit * 1024 * 1024)
(*nbuffers)--;
//vivi默认只有一个plane
*nplanes = 1;
//把size赋值给plane_sizes
sizes[0] = size;
/*
* videobuf2-vmalloc allocator is context-less so no need to set
* alloc_ctxs array.
*/
dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
*nbuffers, size);
return 0;
}
__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 long plane_sizes[])
{
unsigned int buffer;
struct vb2_buffer *vb;
int ret;
for (buffer = 0; buffer < num_buffers; ++buffer) {
/* Allocate videobuf buffer structures */
//根据q->buf_struct_size去分配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;
//每个vb2_buffer的一些状态赋值
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
vb->v4l2_buf.index = 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, plane_sizes);
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.
*/
//vivi驱动定义的buf_init为空,什么也没做
ret = call_qop(q, buf_init, vb);
if (ret) {
dprintk(1, "Buffer %d %p initialization"
" failed\n", buffer, vb);
__vb2_buf_mem_free(vb);
kfree(vb);
break;
}
}
//把vb2_buffer赋值给vb2_queue->bufs数组,用vb2_queue去管理vb2_buffer
q->bufs[buffer] = vb;
}
q->num_buffers = buffer;
__setup_offsets(q);
dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
q->num_buffers, num_planes);
return buffer;
}
__vb2_buf_mem_alloc
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
unsigned long *plane_sizes)
{
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
int plane;
/* Allocate memory for all planes in this buffer */
//根据plane的数量去调用vb2_queue.mem_ops的alloc函数去分配空间
for (plane = 0; plane < vb->num_planes; ++plane) {
mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
plane_sizes[plane]);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
//分配的空间保存在vb2_buffer->planes数组的mem_priv指针里
vb->planes[plane].mem_priv = mem_priv;
vb->v4l2_planes[plane].length = plane_sizes[plane];
}
return 0;
free:
/* Free already allocated memory if one of the allocations failed */
for (; plane > 0; --plane)
call_memop(q, plane, put, vb->planes[plane - 1].mem_priv);
return -ENOMEM;
}
vb2_queue.mem_ops.alloc具体函数就是前面的vb2_vmalloc_alloc,看一下他具体的定义
static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
{
struct vb2_vmalloc_buf *buf;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return NULL;
buf->size = size;
// allocate zeroed virtually contiguous memory for userspace
//The resulting memory area is zeroed so it can be mapped to userspace
//without leaking data
buf->vaddr = vmalloc_user(buf->size);
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_vmalloc_put;
buf->handler.arg = buf;
if (!buf->vaddr) {
printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size);
kfree(buf);
return NULL;
}
atomic_inc(&buf->refcount);
printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n",
buf->size, buf->vaddr);
return buf;
}
最后看一下__setup_offsets(q);mmap时需要用到里面的offset, 之前漏看了。
/**
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
* every buffer on the queue
*/
static void __setup_offsets(struct vb2_queue *q)
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
unsigned long off = 0;
for (buffer = 0; buffer < q->num_buffers; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
//对应单个plane,其实mem_offset=0
for (plane = 0; plane < vb->num_planes; ++plane) {
vb->v4l2_planes[plane].m.mem_offset = off;
dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
buffer, plane, off);
off += vb->v4l2_planes[plane].length;
off = PAGE_ALIGN(off);
}
}
}
总结:申请buffer时,根据上层申请的buffer数量,驱动层创建对应数目的vb2_buffer、num_planes、并设置对应的state, 所属哪个vb2_queue。还有设置vb2_buffer对应的v4l2_buf.index、v4l2_buf.type、v4l2_buf.memory申请内存的方式(mmap, userptr)。
用vb2_queue->bufs[]存放对应的vb2_buffer。申请的内存空间放到vb2_buffer->planes[plane].mem_priv.vaddr;vb2_buffer结构里面的v4l2_buffer主要用来描述当前vb2_buffer申请的空间的index, type, timestamp, memory类型等等。