Chinaunix首页 | 论坛 | 博客
  • 博客访问: 495776
  • 博文数量: 135
  • 博客积分: 3010
  • 博客等级: 中校
  • 技术积分: 905
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-24 19:31
文章分类

全部博文(135)

文章存档

2010年(135)

我的朋友

分类: 嵌入式

2010-06-04 23:23:47

原文网址:
这篇文章是LWN写V4L2接口的设备驱动系列文章的第二篇。没看过介绍篇的,也许可以从那篇开始看。这一期文章将关注Video for Linux驱动的总体结构和设备注册过程。
开始之前,有必要提一点,那就是对于搞视频驱动的人来说,有两份资料是非常有价值的。
  • The.(V4L2 API说明)这份文档涵盖了用户空间视角下的API,但在很大程度上,V4L2驱动直接实现的就是那些API。所以大部分结构体是相同的,而且V4L2调用的语义也表述很很明了。打印一份出来(可以考虑去掉自由文本协议的文本内容,以保护树木[前面是作者原文,节省纸张就是保护树木嘛 ]),放在容易够到的地方。
  • 内核代码中的vivi驱动,即drivers/media/video/vivi.c.这是一个虚拟驱动。它可以用来测试,却不使用任何实际硬件。这样,它就成一个教人如何写V4L2驱动的非常好的实例。
首先,每个V4L2驱动都要包含一个必须的头文件:
1
  #include

大部分所需的信息都在这里。作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体。
一个视频驱动很可能要有处理PCI总线,或USB总线的部分。这里我们不会花什么时间还接触这些东西。通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它。然后还有一个V4L2的子系统接口。这个子系统是围绕video_device这个结构体建立的,它代表的是一个V4L2设备。讲解进入这个结构体的一切,将会是这个系列中几篇文章的主题。这里我们先有一个概览。
video_device结构体的name字段是这一类设备的名字,它会出现在内核日志和sysfs中出现。这个名字通常与驱动的名字相同。
所表示的设备有两个字段来描述。第一个字段(type)似乎是从V4L1的API中遗留下来的,它可以下列四个值之一:
  • VFL_TYPE_GRABBER 表明是一个图像采集设备–包括摄像头、调谐器,诸如此类。
  • VFL_TYPE_VBI 代表的设备是从视频消隐的时间段取得信息的设备。
  • VFL_TYPE_RADIO 代表无线电设备。
  • VFL_TYPE_VTX 代表视传设备。
如果你的设备支持上面提到的不只一种功能,那就要为每个功能注册一个V4L2设备。然而在V4L2中,注册的每个设备都可以用作它实际支持的各种模式(就是说,你要为一个物理设备创建不多个设备节点,但你却可以调用任意一个设备节点,来实现这个物理设备支持的任意功能)。实质上的问题是,在V4L2中,你实际上只需一个设备,注册多个V4l2设备只是为了与V4l1兼容。
第二个字段是type2,它以掩码的形式对设备的功能提供了更详尽的描述。它可以包含以下值:
  • 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己经配置好,就可以下面的函数注册了:
1
int video_register_device(struct video_device *vfd, int type, int nr);

这里vfd是设备的结构体(video_device),type的值与它的type字段值相同,nr也是一样,想要的子设备号(为-1则注册时自动分配)。返回值当为0,若返加的是负的
出错码,则表明出错了,和通常一样,我们要知道,设备一旦注册,它的函数可能就会立即调用,所以不到一切准备就绪,不要调用video_register_device();
设备的注销方法为:
1
void video_unregister_device(struct video_device *vfd);

请继续关注本系列的下篇文章,我们将会看看这些函数的具体实现。
open() 和 release()每个V4L2设备都需要open()函数,其原型也与常规的相同。
int (*open)(struct inode *inode, struct file *filp);

open()函数要做的第一件事是通过给定的inode找到内部设备,这是通过找到inode中存存储的子设备号来完成的。这里还可以实现一定数量的初始化,如果有关闭电源选项的话,这个时间恰好可以用来开启硬件电源。
V4L2规范还定义了一些相关的惯例。其一是:根据其设计,文件描述符可以在给定的任何时间重复打开。这样设定的目的是当一个应用在显示(或是产生)视频信号时,另一个应用可以改变控制值。所以,虽然某些操作是独占性质的(特别是数据读、写等),但是设备总体本身是要支持描述符复用的。
另一个值得一提的惯例是:open()函数,总体上讲,不可以改变硬件中现行的操作参数。有些时候可能会有这样的情况:通过命令行程序,根据一组参数(分辨率,视频格式等)来改变摄像头配置,然后运行一个完全不同的程序来,比如说,从摄像头获取上帧图像。如果摄像头在设置在中途改变了,这种模式就不好用。所以除非应用明确表示要改变设置(这种情况当然不包括在open函数中)V4L2驱动要尽量保持设定不变。
release()函数做一些必要清理工作。因为文件描述符可以重复打开,所以release函数中减小引用计数,并在彻底退出之前做检查。如果关闭的文件描述符是用来传输数据的,release函数很可能要关掉DMA,并做一些其他的清理工作。
本系列的下一篇文章我们将进入查询设备功能和设定系统模式的冗长过程之中,请续断关注。
阅读(1541) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~