基于V4L2模块的驱动
时间:2009-04-23 来源:tacoe
V4L2(video for linux) 可以支持多种设备,它可以有以下几种接口:
1. 视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头.V4L2的最初设计就是应用于这种功能的.下面也是着重讲解这种应用.
2. 视频输出接口(video output interface):可以驱动计算机的外围视频图像设备--像可以输出电视信号格式的设备.
3. 直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU.
4. 视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号.
5. 收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流.
V4L2驱动的主要功能是使程序有发现设备的能力和操作设备.它主要是用过一系列的回调函数来实现这些功能.像设置高频头的频率,帧频,视频压缩格式和图像像参数等等.
V4L2提供了三种不同的API来传输外围设备和用户空间的数据.下面就vivi(drivers/media/video/vivi.c)来讲解一个V4L2驱动的编写.注意它是一个虚拟的设备驱动,没有与实际的硬件打交道.
一.下面首先来分析这个模块中的两个重要数据结构.
struct vivi_dev {
struct list_head vivi_devlist;内核双向链表,在内核数据结构里有描述
struct semaphore lock; 信号量,防止竞态访问
int users; 用户数量计数
/* various device info */
unsigned int resources;
struct video_device video_dev; 这个成员是这个结构的核心,用面向对象的话来说就是基类
struct vivi_dmaqueue vidq; DMA队列
/* Several counters */
int h,m,s,us,jiffies; 定时器定义
char timestr[13]; 其它一些资源变量.
};
像这样变义的结构在Linux C 中很普遍,这也是利用C来实现面向对象编程的强大方法.建立这个结构对象之后,所有的操作都是基于这个结构.或者这个结构派生出的来的其它结构.
struct vivi_fh {
struct vivi_dev *dev;
/* video capture */
struct vivi_fmt *fmt;
unsigned int width,height;
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
};
这个结构即是vivi_dev结构的更深层次封装,基于那个结构加入了更多的描述信息,如视频制式,视频画面大小,视频缓冲队列等等.在open的时候,会把这个结构赋给file结构中的private_data域.在释放设备时注销.其它的像ioctl,mmap,read.write等等都会用到这个结构,其实整个模块的编写的cdev差不多.只是视频设备的基类是video_device,而字符设备的基类是cdev而已.
二.数据传输方式:
在设备与应用程序之间有三种数据传输方式:
1.read与write这种方式,它像其它设备驱动一样,但是这种方式很慢,对于数据视频流不能满足其要求.
2.直接的内存访问,可以能过其映射方式来传输(IO数据流,交换指向缓冲区指针的方法),
这是视频设备通常用的方法.采用mmap()的方法,即有内核空间里开辟内存,再在程序里把这部分的内存映射到程序空间.如果有设备内存,即直接映射到设备的内核,这种性能更高.
3.异步IO口访问,但是这种方法在V4L2模块中还没有实现.
vivi中的mmap是利用第二种方法来实现的,这也是视频设备常用的方法:
static int
vivi_mmap(struct file *file, struct vm_area_struct * vma)
{
struct vivi_fh *fh = file->private_data;
int ret;
dprintk (1,"mmap called, vma=0x%08lx\n",(unsigned long)vma);
ret=videobuf_mmap_mapper(&fh->vb_vidq, vma);
dprintk (1,"vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
ret);
return ret;
}
videobuf_mmap_mapper(&fh->vb_vidq, vma);
这个核心函数把设备的I/O内存或者设备内存映射到系统为它开辟的虚拟内存.
三.操控设备的实现: ioctl
static int vivi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, vivi_do_ioctl);
}
一般不用自己来实现ioctl这个接口函数,而是由V4L2模块中的一个子模块这实现这些调用.vivi_do_ioctl 这个函数里调用一些命令来设备V4L2模块中的一些结构参数来改变或者获取设备的参数.
具体详细的操作可以参考源码(把.gz改成.c).
文档附件:vivi.gz
相关阅读 更多 +