日期:2014-05-16 浏览次数:20760 次
这篇文章是LWN写V4L2接口的设备驱动系列文章的第二篇。没看过介绍篇的,也许可以从那篇开始看。这一期文章将关注Video for Linux驱动的总体结构和设备注册过程。
开始之前,有必要提一点,那就是对于搞视频驱动的人来说,有两份资料是非常有价值的。
?TheV4L2 API Specification . (V4L2 API说明)这份文档涵盖了用户空间视角下的API,但在很大程度上,V4L2驱动直接实现的就是那些API。所以大部分结构体是相同的,而且V4L2调用的语义也表述很很明了。打印一份出来(可以考虑去掉自由文本协议的文本内容,以保护树木[前面是作者原文,节省纸张就是保护树木嘛 ]),放在容易够到的地方。
?内核代码中的vivi驱动,即drivers/media/video/vivi.c.这是一个虚拟驱动。它可以用来测试,却不使用任何实际硬件。这样,它就成一个教人如何写V4L2驱动的非常好的实例。
首先,每个V4L2驱动都要包含一个必须的头文件
?VFL_TYPE_GRABBER 表明是一个图像采集设备–包括摄像头、调谐器,诸如此类。
?VFL_TYPE_VBI 代表的设备是从视频消隐的时间段取得信息的设备。?VFL_TYPE_RADIO 代表无线电设备。
?VFL_TYPE_VTX 代表视传设备。
如果你的设备支持上面提到的不只一种功能,那就要为每个功能注册一个V4L2设备。然而在V4L2中,注册的每个设备都可以用作它实际支持的各种模式(就是说,你要为一个物理设备创建不多个设备节点,但你却可以调用任意一个设备节点,来实现这个物理设备支持的任意功能)。实质上的问题是,在V4L2中,你实 际上只需一个设备,注册多个V4l2设备只是为了与V4l1兼容。
第二个字段是type2,它以掩码的形式对设备的功能提供了更详尽的描述。它可以包含以下值:
/* These defines are V4L1 specific and should not be used with the V4L2 API!
They will be removed from this header in the future. */
?VID_TYPE_CAPTURE 它可以捕获视频数据
?VID_TYPE_TUNER 它可以接收不同的频率
?VID_TYPE_TELETEXT 它可以抓取字幕
?VID_TYPE_OVERLAY 它可以将视频数据直接覆盖到显示设备的帧缓冲区
?VID_TYPE_CHROMAKEY 一种特殊的覆盖能力,覆盖的仅是帧缓冲区中像素值为某特定值的区域
?VID_TYPE_CLIPPING 它可以剪辑覆盖数据
?VID_TYPE_FRAMERAM 它使用帧缓冲区中的存储器
?VID_TYPE_SCALES 它可以缩放视频数据
?VID_TYPE_MONOCHROME 这个是一个纯灰度设备
?VID_TYPE_SUBCAPTURE 它可以捕获图像的子区域
?VID_TYPE_MPEG_DECODER 它支持mpeg码流解码
?VID_TYPE_MPEG_ENCODER 它支持编码mpeg码流
?VID_TYPE_MJPEG_DECODER 它支持mjpeg解码
?VID_TYPE_MJPEG_ENCODER 它支持mjpeg编码
V4L2驱动还要初始化的一个字段是minor,它是你想要的子设备号。通常这个值都设为-1,这样会让video4linux子系统在注册时自动分配一个子设备号。
在 video_device结构体中,还有三组不同的函数指针集。第一组只包含一个函数,那就是 release(),如果驱动没有release()函数,内核就会抱怨(笔者发现一个件有趣的事,就是这个抱怨涉及到冒犯一篇LWN文章的作者)。 release()函数很重要:由于多种原因,对video_device的引用可以在最后一个应用关闭文件描述符后很长一段时间依然保持。它们甚至可以 在设备己经注销后依然保持。因此,在release()函数调用前,释放这个结构体是不安全的。所以这个函数通常要包含一个简单的kfree()调用。
video_device 的 file_operations结构体包含都是常规的函数指针。视频设备通常都包括open()和release()函数。注意:这里所说的 release函数并非上面所讲到的同名的release()函数,这个release() 函数只要设备关闭就要调用。通常都还要有read()和write()函数,这取决于设备的功能是输入还是输出。然而我们要注意的是,对于视频流设备而 言,传输数据还有别的方法。多数处理视频流数据的设备还需要实现poll()和mmap();而且每个V4L2设备都要有ioctl()函数,但是也可以
使用V4L2子系统的video_ioctl2();
第三组函数存在于video_device结构体本身里面,它们是V4L2 API的核心。这组函数有几十个,处理不同的设备配置操作、流输入输出和其他操作。
最 后,从一开始就要知道的一个字段就是debug.可以把它设成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是两个 都设,这是个掩码),可以生成很多的调试信息,它们可以帮助一个迷糊的程序员找到毛病,知道为什么驱动和应用谁也不知道对方在说什么。
视频设备注册
一旦video_device己经配置好,就可以下面的函数注册了:
int video_register_device(struct video_device *vfd, int type, int nr);
这里vfd是设备的结构体(video_device),type的值与它的type字段值相同,nr也是一样,想要的子设备号(为-1则注册时自动分配)。返回值当为0,若返加的是负的出错码,则表明出错了,和通常一样,我们要知道,设备一旦注册,它的函数可能就会立即调用,所以不到一切准备就绪,不要调用video_register_device();
设备的注销方法为:
void video_unregister_device(struct video_device *vfd);
请继续关注本系列的下篇文章,我们将会看看这些函数的具体实现。
open() 和 release()每个V4L2设备都需要open()函数,其原型也与常规的相同。
int (*open)(struct inode *inode, struct file *filp);
open()函数要做的第一件事是通过给定的inode找到内部设备,这是通过找到inode中存存储的子设备号来完成的。这里还可以实现一定数量的初始化,如果有关闭电源选项的话,这个时间