LINUX&UVC驱动申请视频帧缓冲区VIDIOC_REQBUFS
2024-04-02
125
0
在启动UVC相机前,需要先准备好数据的存储缓冲区,使用VIDIOC_REQBUFS控制吗。
// 请求内存缓冲区
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 4;//申请拥4个缓冲帧的缓冲区
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
...
}
该控制码在LINUX&UVC中对应的操作函数是vidioc_reqbufs
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
...
.vidioc_reqbufs = uvc_ioctl_reqbufs,
...
}
uvc_ioctl_reqbufs调用队列的申请缓冲区函数uvc_request_buffers,然后进入uvc_request_buffers
static int uvc_ioctl_reqbufs(struct file *file, void *fh,struct v4l2_requestbuffers *rb)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
ret = uvc_acquire_privileges(handle);
if (ret < 0)
return ret;
mutex_lock(&stream->mutex);
ret = uvc_request_buffers(&stream->queue, rb);
mutex_unlock(&stream->mutex);
if (ret < 0)
return ret;
if (ret == 0)
uvc_dismiss_privileges(handle);
return 0;
}
加锁操作:
int uvc_request_buffers(struct uvc_video_queue *queue,
struct v4l2_requestbuffers *rb)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_reqbufs(&queue->queue, rb);
mutex_unlock(&queue->mutex);
return ret ? ret : rb->count;
}
进入vb2模块的vb2_reqbufs函数,其中依旧第一个参数是队列指针,第二个是上层应用的请求数据参数。
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
int ret = vb2_verify_memory_type(q, req->memory, req->type);
fill_buf_caps(q, &req->capabilities);
return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count);
}
vb2_reqbufs中是vb2的一个导出函数,做一些必要的检查,然后进入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);
vb2_core_reqbufs中先取一个合适的num_buffers,然后使用call_qop调用UVC驱动提供给队列的回调函数queue_setup,最使用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;
}
其实主要就是内存模块的对齐 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);
}
}
最终的分配内存函数
/**
* __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;
}