VIDIOC_REQBUFS向驱动申请帧缓冲代码详解
1. 概述
-
VIDIOC_REQBUFS请求在内核空间分配视频缓冲区 -
功能:
请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存)
V4L2是视频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBUFS控制命令申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数,把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。 -
参数说明:
参数类型为V4L2的申请缓冲区数据结构体类型struct v4l2_requestbuffers;v4l2_requestbuffers结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
一般不超过5个,CAP_BUF_NUM = 4 -
返回值说明:
执行成功时,函数返回值为0;V4L2驱动层分配好了视频缓冲区;
2. 应用层
struct v4l2_requestbuffers {
__u32 count;
__u32 type; /* enum v4l2_buf_type */
__u32 memory; /* enum v4l2_memory */
__u32 reserved[2];
};
struct v4l2_requestbuffers req;
/* 申请设备的缓存区 */
memset(&req, 0, sizeof(req));
req.count = CAP_BUF_NUM; //申请一个拥有四个缓冲帧的缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0)
{
if (EINVAL == errno)
{
printf(stderr, "%s does not support "
"memory mapping\n", dev->dev);
return TFAIL;
}
else
{
printf(stderr, "%s does not support "
"memory mapping, unknow error\n", dev->dev);
return TFAIL;
}
}
if (req.count < 2)
{
printf(stderr, "Insufficient buffer memory on %s\n",
dev->dev);
return TFAIL;
}
3. 内核驱动
3.1 vb2_ioctl_reqbufs函数
/* vb2 ioctl helpers */
int vb2_ioctl_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
{
// 获取dev
struct video_device *vdev = video_devdata(file);
// 检查队列memory类型是否一致,不一致则直接退出
int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type);
if (res)
return res;
if (vb2_queue_is_busy(vdev, file))
return -EBUSY;
res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count);
/*
* 注意
* 1. 当count=0的时候,释放所有buffer
*/
/* 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)
vdev->queue->owner = p->count ? file->private_data : NULL;
return res;
}
EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
3.2 vb2_core_reqbufs函数
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count)
{
unsigned int num_buffers, allocated_buffers, num_planes = 0;
// #define VB2_MAX_PLANES (8)
unsigned plane_sizes[VB2_MAX_PLANES] = { };
int ret;
// 正在取流中
if (q->streaming) {
dprintk(1, "streaming active\n");
return -EBUSY;
}
/*
* 注意
* 1。 这里进行count=0判断,memory类型判断,删除所有buffer,所以为什么应用层会进行0 buffer的申请操作。
*/
if (*count == 0 || q->num_buffers != 0 || q->memory != memory) {
/*
* We already have buffers allocated, so first check if they
* are not in use and can be freed.
*/
mutex_lock(&q->mmap_lock);
if (q->memory == VB2_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.
*/
__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.
*/
if (*count == 0)
return 0;
}
/*
* Make sure the requested values and current defaults are sane.
*/
// #define VB2_MAX_FRAME (32)
num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
// min_buffers_needed = 2一般为2
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
q->memory = memory;
// 从驱动层获取到可以支持多少buffers和planes per buffer
/*
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, plane_sizes, q->alloc_devs);
if (ret)
return ret;
// 最后分配buffer
/* Finally, allocate buffers and video memory */
allocated_buffers =__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
if (allocated_buffers == 0) {
dprintk(1, "memory allocation failed\n");
return -ENOMEM;
}
// 检查是否大于最小需求buffer
/*
* 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.
*/
if (!ret && allocated_buffers < num_buffers) {
num_buffers = allocated_buffers;
/*
* num_planes is set by the previous queue_setup(), but since it
* signals to queue_setup() whether it is called from create_bufs()
* vs reqbufs() we zero it here to signal that queue_setup() is
* called for the reqbufs() case.
*/
num_planes = 0;
ret = call_qop(q, queue_setup, q, &num_buffers,
&num_planes, plane_sizes, q->alloc_devs);
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);
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);
/*
* 注意
* 1.这里对应用层传递过来的count进行了,已经分配buffer数量的更新操作。
*/
/*
* Return the number of successfully allocated buffers
* to the userspace.
*/
*count = allocated_buffers;
q->waiting_for_buffers = !q->is_output;
return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
3.3 __vb2_queue_alloc函数
/**
* __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
* video buffer memory for all buffers/planes on the queue and initializes the
* queue
*
* Returns the number of buffers successfully allocated.
*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
unsigned int num_buffers, unsigned int num_planes,
const unsigned plane_sizes[VB2_MAX_PLANES])
{
unsigned int buffer, plane;
struct vb2_buffer *vb;
int ret;
/* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */
num_buffers = min_t(unsigned int, num_buffers,
VB2_MAX_FRAME - q->num_buffers);
// 分配 vidoe buffer了
for (buffer = 0; buffer < num_buffers; ++buffer)
{
/* Allocate videobuf buffer structures */
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
if (!vb) {
dprintk(1, "memory alloc for buffer struct failed\n");
break;
}
// 设置video buffer的数据成员
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
vb->index = q->num_buffers + buffer;
vb->type = q->type;
vb->memory = memory;
for (plane = 0; plane < num_planes; ++plane) {
vb->planes[plane].length = plane_sizes[plane];
vb->planes[plane].min_length = plane_sizes[plane];
}
/*
* 注意
* 1. 这里使用的idx索引
*/
q->bufs[vb->index] = vb;
/* Allocate video buffer memory for the MMAP type */
if (memory == VB2_MEMORY_MMAP)
{
ret = __vb2_buf_mem_alloc(vb);
if (ret) {
dprintk(1, "failed allocating memory for buffer %d\n",
buffer);
q->bufs[vb->index] = NULL;
kfree(vb);
break;
}
__setup_offsets(vb);
/*
* Call the driver-provided buffer initialization
* callback, if given. An error in initialization
* results in queue setup failure.
*/
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);
q->bufs[vb->index] = NULL;
kfree(vb);
break;
}
}
}
dprintk(1, "allocated %d buffers, %d plane(s) each\n",
buffer, num_planes);
return buffer;
}
3.4 __setup_offsets函数
// 其实主要就是内存模块的对齐 page
/**
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
* the buffer.
*/
static void __setup_offsets(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
unsigned long off = 0;
if (vb->index)
{
struct vb2_buffer *prev = q->bufs[vb->index - 1];
struct vb2_plane *p = &prev->planes[prev->num_planes - 1];
off = PAGE_ALIGN(p->m.offset + p->length);
}
for (plane = 0; plane < vb->num_planes; ++plane)
{
vb->planes[plane].m.offset = off;
dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
vb->index, plane, off);
off += vb->planes[plane].length;
off = PAGE_ALIGN(off);
}
}
3.5 __vb2_buf_mem_alloc函数
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
*/
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
int plane;
int ret = -ENOMEM;
/*
* Allocate memory for all planes in this buffer
* NOTE: mmapped areas should be page aligned
*/
for (plane = 0; plane < vb->num_planes; ++plane)
{
// page对齐
unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
// 内核mmap
mem_priv = call_ptr_memop(vb, alloc,
q->alloc_devs[plane] ? : q->dev,
q->dma_attrs, size, q->dma_dir, q->gfp_flags);
if (IS_ERR_OR_NULL(mem_priv)) {
if (mem_priv)
ret = PTR_ERR(mem_priv);
goto free;
}
/* Associate allocator private data with this plane */
vb->planes[plane].mem_priv = mem_priv;
}
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 ret;
}


被折叠的 条评论
为什么被折叠?



