- 首先需要说明目前在比较新的内核中已经采用了
vb2_queue与vb2_buffer来替代旧版本内核中经常使用到的
videobuf_queue与videobuf_buffer。
两者主要用于对用户层申请VIDIOC_REQBUF时的使用。
从用户层Request的Memory的类型区分,典型的两种是:
V4L2_MEMORY_USERPTR以及V4L2_MEMORY_MMAP,前者的内存主动权位于用户层,即驱动中的视频输出内存地址由用户层来提供,后者MMAP操作的内存缓存类型一般需要由驱动自己来实现内存的分配。
在旧版本内核中,假设需要实现两种Video设备操作接口,分别对应两种Memory_type, 则一般需要提供的ops接口如下:
V4L2_MEMORY_USERPTR:
static const struct v4l2_file_operations xxx_video0_fops= {
.owner = THIS_MODULE,
.open = xxx,
.release = xxx,
.poll = xxx,
.unlocked_ioctl = xxx,
};
V4L2_MEMORY_MMAP:
static const struct v4l2_file_operations xxxx_video1_fops = {
.owner = THIS_MODULE,
.open = xxx,
.release = xxx,
.poll = xxx,
.unlocked_ioctl = xxx,
.mmap = xxx,
};
两者的区别就是多了一个mmap操作的接口。
此外,对于旧版内核而言mmap的操作可以由内核提供的mmap方式来实现,一般的处理方式如下:
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv,
struct mutex *ext_lock)
{
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
priv, &qops, ext_lock);
}
这里通过内核提供的一个qops来完成对每一个videobuf_buffer的alloc_vb操作,该过程主要是申请一个videobuf_buffer但并不会直接申请存储视频图像数据的内存块,所以一般在Request_buffer的过程就是主要完成alloc_vb的操作,重点是记录下申请的每个videobuf_buffer所在的index值,该过程对V4L2_MEMORY_USERPTR 和V4L2_MEMORY_MMAP 是一致的。
static struct videobuf_qtype_ops qops = {
.magic = MAGIC_QTYPE_OPS,
.alloc_vb = __videobuf_alloc_vb,
.iolock = __videobuf_iolock,
.mmap_mapper = __videobuf_mmap_mapper,
.vaddr = __videobuf_to_vaddr,
};
而对V4L2_MEMORY_MMAP的缓存区域而言,他需要内核完成buffer真正存储数据的内存块的申请,一般就是通过用户层的mmap来完成的,而且是在这个mmap的过程中采取完成buffer区域的alloc操作,在低版本内核中通过__videobuf_mmap_mapper来实现:
mem->size = PAGE_ALIGN(buf->bsize);
mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
&mem->dma_handle, GFP_KERNEL);
在完成实际物理存储区域的申请后,才会再去执行一次mmap
- 对比旧版本中mmap的处理机制,在新版内核中需要注意的时,可不再使用上述的模式,对于V4L2_MEMORY_MMAP类型的vb2_buffer来而言直接在Request_buffer的过程中就需要完成对实际内存区域块的申请vb2_reqbufs->__vb2_buf_mem_alloc(),而这个申请的接口在新的内核中通过实现vb2_mem_ops接口来完成,主要用于处理V4L2_MEMORY_MMAP :
struct vb2_mem_ops {
void *(*alloc)(void *alloc_ctx, unsigned long size);
void (*put)(void *buf_priv);
void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write);
void (*put_userptr)(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);
};
该接口需要在驱动中进行实现,alloc一般完成实际物理内存区域块的申请,而mmap是用于对用户空间的mmap操作的接口响应。当然在新版本的内核中依旧还是支持上述1小节所描述的处理过程的,依旧可以采用这种方式来实现video相关的驱动。同理对于方式二这种处理方式也可以在旧内核中来实现,其根本是对vb2_queue的 videobuf_qtype_op的实现。
仔细阅读源码后,可以发现本质上1和2小节描述的处理过程的根本区别在于旧版本架构中mmap过程完成实际buffer物理内存的申请以及内存映射的操作,新版本是将alloc过程放在Requestbuffer的过程中,而将mmap映射放在实际的mmap操作中实现,。
总的来说,对于新旧版本之所以这样处理的原因是,原因在于现在更多的采用V4L2_MEMORY_USERPTR类型的缓存数申请(主要是现在全新的Android系统中video申请缓存主要是来自GPU、共享内存/dev/ION等等区域块,一般再由内存申请的话会增加驱动的复杂度),故根本不需要再去实现mmap的操作,故一般不需要再实现vb2_mem_ops接口。而如果还是采用旧版的处理方式那么就需要videobuf_queue_dma_contig_init来提供一个videobuf_qtype_ops,因为alloc_vb是旧版中无论是V4L2_MEMORY_MMAP还是V4L2_MEMORY_USERPTR 均要实现videobuf_qtype_ops 。
对比而言对于V4L2_MEMORY_USERPTR来说新版本可以减少一定的编码量,无需vb2_mem_ops。
当然,架构的实现方式可以由多种多样,本质的目的是在同样的V4L2框架的基础下让Video设备可以正常工作即可。