深入理解linux内核v4l2框架之videobuf
Videobuf
下面来介绍以下videobuffer相关的一些东西。
V4L2核心api提供了一套标准的方法来处理视频缓冲,这些方法允许驱动实现read(),mmap(), overlay()等操作。同样也有方法支持DMA的scatter/gather操作,并且支持vmallocbuffer(这个大多用在USB驱动上)。
videobuf层功能是一种在v4l2驱动和用户空间当中的依附层,这话看起来有点绕,说白了就是提供一种功能框架,用来分配和管理视频缓冲区,它相对独立,却又被v4l2驱动使用。它有一组功能函数集用来实现许多标准的POSIX系统调用,包括read(),poll()和mmap()等等,还有一组功能函数集用来实现流式(streaming)IO的v4l2_ioctl调用,包括缓冲区的分配,入队和出队以及数据流控制等操作。使用videobuf需要驱动程序作者遵从一些强制的设计规则,但带来的好处是代码量的减少和v4l2框架API的一致。
缓冲类型
并不是所有的视频设备都使用相同的缓冲类型。实际上,有三种通用的类型:
–被分散在物理和内核虚拟地址空间的缓冲,几乎所有的用户空间缓冲都是这种类型,
如果可能的话分配内核空间的缓冲也很有意义,但是不幸的是,这个通常需要那些支持离散聚合DMA操作的硬件设备。
–物理上离散的但是虚拟地址是连续的,换句话说,就是用vmalloc分配的内核缓冲。这些缓冲很难用于DMA操作。
– 物理上连续的缓冲。
videobuf可以很好地处理这三种类型的缓冲,但是在此之前,驱动程序作者必须选择一种类型,并且以此类型为基础设计驱动。
数据结构,回调函数和初始化
根据选择的类型,包含不同的头文件,这些头文件在include/media/下面
<media/videobuf-dma-sg.h>
<media/videobuf-vmalloc.h>
<media/videobuf-dma-contig.h>
v4l2驱动需要包含一个videobuf_queue的实例用来管理缓冲队列,同时还要一个链表来维护这个队列,另外还要一个中断安全的spin_lock来保护队列的操作。
下一步就是要填充一个回调函数集来处理实际的缓冲区队列,这个函数集用videobuf_queue_ops来描述:
struct videobuf_queue_ops {
int *(buf_setup)(struct videobuf_queue*q, uint *count, uint *size);
int *(buf_prepare)(structvideobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field);
void *(buf_queue)(structvideobuf_queue*q,struct videobuf_buffer *vb);
void *(buf_release)(...);
}
buf_setup在IO处理请求之前被调用。目的是告诉videobuf关于IO的信息。 count参数提供一个缓冲区个数的参考,驱动必须检查它的合理性,一个经验是大于等于2,小于等于32个。Size参数指定了每一帧数据的大小。
buf_prepare每一个缓冲(videobuf_buffer结构描述的)将被传递给该回调函数,用来配置缓冲的height,width和fileds。如果field参数被设置为 VIDEOBUF_NEEDS_INIT,那么驱动将把vb传递给videobuf_iolock()这个函数。除此之外,该回调函数通常也将为vb分配内存,最后把vb的状态置为VIDEOBUF_PREPARED。
buf_queue当一个vb需要被放入IO请求队列时,调用该回调。它将把这个buffer放到可用的buffer链表当中去,然后把状态置为VIDEOBUF_QUEUED。
buf_release当一个buffer不再使用的时候,调用该回调函数。驱动必须保证 buffer上没有活跃的IO请求,之后就可以将这个buffer传递给合适的 free函数,根据申请的buffer类型调用对
应的释放函数:
linux内核视频教程全套
scatter/gather类型的调用
videobuf_dma_unmap(structvideobuf_queue, videobuf_dmabuf)
videobuf_dma_free(videobuf_dmabuf)
vmalloc类型的调用
videobuf_vmalloc_free(videobuf_buffer)
contiguous类型的调用
videobuf_dma_contig_free(videobuf_queue,videobuf_buffer)
有一种方法可以保证buffer上没有IO请求,调用函数
videobuf_waiton(videobuf_buffer,non_blocking, intr)
文件操作(v4l2_file_operations)
到了这儿,很多工作也就做完了,剩下的事情就是将对videobuf的调用传递给具体的驱动实现了。首先就是打开操作,这个操作要先对videobuf_queue进行初始化,初始化取决于申请的buffer是什么类型,有如下三种初始化函数可供调用:
void videobuf_queue_sg_init(structvideobuf_queue *q,
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)
void videobuf_queue_vmalloc_init(structvideobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int mszie,
void *priv,
struct mutex *ext_lock);
voidvideobuf_queue_dma_contig_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int mszie,
void *priv,
struct mutex *ext_lock);
以上三种初始化函数,有相同的参数,这些参数的从他们的名称就可以看出来锁代表的意义是什么。
这里着重说下v4l2_buf_type类型,
V4L2_BUF_TYPE_VIDEO_CAPTURE 指定buf的类型为capture,用于视频捕获设备
V4L2_BUF_TYPE_VIDEO_OUTPUT 指定buf的类型output,用于视频输出设备
V4L2_BUF_TYPE_VIDEO_OVERLAY 指定buf的类型为overlay,用于overlay设备
V4L2_BUF_TYPE_VBI_CAPTURE 用于vbi捕获设备
V4L2_BUF_TYPE_VBI_OUTPUT 用于vbi输出设备
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 用于切片vbi捕获设备
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT 用于切片vbi输出设备
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY 用于视频输出overlay设备