Linux环境下的USB摄像头驱动开发
摘要:嵌入式环境下的USB摄像头驱动需要充分利用USB总线与内核中的USB核心密切配合并且保证数据流的高速与稳定。本文从Linux内核的USB核心模块出发,遵循Video4Linux接口标准,采用urb策略与内存映射的方式以提高数据读取速度,设计开发了基于Linux环境下的USB摄像头驱动,并在ARM9实验平台上对该驱动程序进行了测试与分析。
关键词:嵌入式Linux;USB摄像头驱动;urb;内存映射机制
引言:嵌入式图像处理的应用范围随着嵌入式技术的不断发展而不断扩大,摄像头作为数字图像采集的基础,驱动程序的开发显得尤为重要。作为操作系统内核与硬件设备之间的接口,驱动程序是否优良,直接影响着硬件设备能否正常、高效运转。带有U S B ( 通用串行总线) 接口的摄像头因其具有支持热插拔、接口通用性好、实时性强、较高的传输速率、低成本、易于扩展等优点而备受青睐。但是嵌入式系统中支持U S B 摄像头的驱动程序很少,因此,研究和开发嵌入式环境下的U S B 摄像头驱动程序具有应用价值和现实意义。
USB摄像头驱动程序构架
设备驱动程序是计算机软、硬件之间存在的悖论的产物:软件和硬件不应该互相渗透到对方的领域。因此,设备驱动程序是介于操作系统与硬件之间,将硬件设备的具体工作细节对操作系统内核完全屏蔽的接口。用户的操作通过标准化的调用执行,经设备驱动程序映射到实际的硬件设备上。
由于在不同的环境下通常需要不同的方式来操作硬件,所以在编写访问硬件的设备驱动程序时,不能给用户强加任何特定的策略( 策略,既是如何使用这些功能) 。驱动程序应该处理如何使硬件工作的问题,而将怎样使用硬件的问题留给上层应用程序,即驱动程序的作用在于提供机制(机制,既是需要提供什么功能)。
1.1USB子系统分析
一个U S B 子系统是由几个点对点的连接方式构建而成的树状结构,在该系统中U S B 是担当设备和主控制器之间通信通道的角色,对它所发送或者接受的数据没有任何特殊的内容和结构上的要求。L i n u x 内核支持两种主要类型的U S B 驱动程序:宿主系统上的驱动程序和设备上的驱动程序。宿主系统上的U S B 驱动程序控制插入其中的U S B 设备而设备上的驱动程序则控制该设备如何与主机通信。
L i n u x 内核提供了U S B 核心模块,该模块将不同类型的U S B 主控制器完全屏蔽,而为上层的U S B 设备驱动程序提供了用于访问和控制U S B 设备的接口( 如图1 ) 。因此U S B 核心模块将U S B 主控制器和U S B设备驱动程序隔开,也同时将U S B 驱动程序的编写与具体的硬件隔开。
1.2 驱动模块构架
每个硬件设备在操作系统内核中都对应一个驱动模块。U S B 摄像头设备在内核中表现为一个字符设备,并且属于视频类的硬件设备。L i n u x内核为视频类设备提供了内核接口V i d e o 4 L i n u x ,该内核接口使得L i n u x环境下的上层应用程序可以像访问普通文件一样对其进行读写操作。因此该驱动模块提供的机制应该符合Video4L inu x接口:
1 . O p en :提供给驱动程序以初始化的能力,从而为以后的操作完成初始化做准备;
2 . R e l ea s e :释放O p e n 分配的内存空间;
3 . R e a d :将摄像头获取的图像数据拷贝到应用程序空间;
4 . M m a p :内存映射使得进程之间通过映射同一个普通文件实现内存共享;
5 . I o c t l :提供一种执行设备特定命令的方法。
关键技术研究
U S B 驱动程序与U S B 核心、用户应用层结合紧密,需要开发者对L i nu x的内核有很好的掌握,理解驱动程序与U S B 设备的关联方式、系统内核与U S B 设备的通信方式、内存的映射机制以及由于U S B 热插拔而引出的驱动探测、断开机制的实现对U S B 设备驱动开发有很好的指导作用。
2.1驱动程序与USB设备的关联
L i n u x 操作系统内核提供了U S B 核心模块用于处理U S B 设备的复杂操作。在U S B 核心中将U S B 设备划分为配置、接口和端点三个层次接口(如图2)。
1. 端点:USB通信最基本的形式是通过端点完成。U S B 端点只能往一个方向传送数据,从主机到设备( 输出端点) 或者从设备到主机( 输入端点) 。端点可以看做是一个单向的管道。
2 . 接口:U S B 接口只处理一种U S B 逻辑连接。一些U S B 设备具有多个接口,一个U S B 接口代表了一个基本功能,而每个U S B 驱动程序控制一个接口。
3 . 配置:一个U S B 设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。
综上所述,端点是U S B 通信的最小单位,多个端点绑定成为一个接口,驱动程序是和接口绑定在一起的,一个接口对应一个驱动,在一个U S B 设备中可以有多个接口,既是可以有多个驱动与之绑定,多个接口对应于一个配置,用于调整U S B 设备的状态。
2.2系统内核与USB设备通信
在L i n u x 系统中,内核是通u r b ( U S B 请求块) 完成与U S B 设备的通信功能的。U r b 被用来以一种异步的方式往/ 从特定的U S B 设备上的特定U S B 端点发送/ 接收数据。所以,u r b是在L i n u x 内核与U S B 设备之间的信息承载体,U S B 设备驱动程序通过该结构向U S B 设备发送操作信息或者从U S B 设备处接收硬件状态信息反馈。在L inu x USB设备驱动中,可以为单个端点分配多个u r b ,也可以根据实际需求让多个不同端点重用单个u r b 。图3为一个ur b数据模块的生命周期。
1 . 创建u r b :为了遵循U S B 核心对u r b 使用的计数机制,urb 结构体不能在驱动程序中或者另一个结构体中静态地创建。
2 .初始化u r b :由于u r b 结构是最终制定给特定的U S B端点,通过端点进行数据通行的,因此需要根据端点的不同类型对u r b 结构进行初始化。在表1中列出了USB端点的四中不同类型的数据传送方式。
对于U S B 摄像头,因其为实时数据收集设备,注重于保持一个恒定的数据流,可以容忍数据丢失的情况,所以该类设备选用等时模式进行数据传输:
urb ->p ipe = usb_rc v i soc p ipe(gspca_dev->dev, ep ->desc.bEndpo int Address);
3 . 提交u r b :U S B 驱动程序在成功创建u r b 之后,就可以提交到U S B 核心以发送到USB设备:
i n t u s b _ s u b m i t _ u r b ( s t r u c t u r b * ur b, int mem_ ags);
4 . 销毁u r b :当u r b 结束之后,驱动程序将销毁该u r b ,此该urb 结构将不会被驱动程序访问:
vo id usb_f ree_ur b(str uct ur b* ur b);
至此,通过u r b 数据请求模块完成了操作系统内核与U S B 设备( U S B摄像头) 的数据通信。当u r b 结束的时候,驱动程序将调用u r b 的结束处理例程结束U S B 核心对u r b 的处理,将对它的控制权返还给设备驱动程序。
当U S B 设备接入宿主机并且U S B核心判定该U S B 设备驱动应该处理时,探测函数将被调用。在探测函数中,U S B 摄像头驱动程序完成初始化可用于控制U S B 设备的局部数据结构体,将所需要的与设备有关的信息保存到局部结构体中。探测函数还应该锁定摄像头的端点地址以及缓冲区大小,为操作系统与摄像头进行通信做好准备。
在摄像头驱动的开发中,使用以下代码完成探测函数的主要工作:
i f ( ! d e v - > b u l k _ i n _ e n d p o i n t A d d r
& & ( e n d p o i n t - > b E n d p o i n t A d d r e s s &
USB_DI R_I N) &&
( (e n d p o i n t - > b m A tt r i b u te s & U S B _
ENDPOI N T_X FERT YPE_M A SK)
= = U S B _ E N D P O I N T _ X F E R _
BULK)) {
b u f f e r _ s i z e = e n d p o i n t ->wMa x PacketSi ze;
dev->bul k_in_si ze = bu er_si ze;
d e v - > b u l k _ i n _ e n d p o i n t A d d r =
endpo int->bEndpo int Address;
d e v - > b u l k _ i n _ b u f f e r =
kmal loc( bu er_si ze, GFP_KER NEL);
i f (!dev->bul k_in_bu er) {
e r r ( \" C o u l d n o t a l l o c a t e b u l k _ i n _
bu er \");
g o t o
er ror ;
}
}
当把USB 摄像头从宿主机上拔下时候,驱动程序不再控制该设备,此时断开函数将被调用,该函数完成后期清理工作以防止U S B 核心对驱动程序进行不适当的数据访问。在断开函数中需要防止o p e n 函数与断开函数之间可能出现的并发竞争状态,使用锁机制可以很好的避免这一点。
s t a t i c v o i d s k e l _ d i s c o n n e c t ( s t r u c t
usb_inter face *inter face){
str uct usb_skel *dev ;
int minor = inter face->minor ;
lock_ker nel();
dev = usb_get_intfdata(inter face);
usb_set_intfdata(inter face, NULL);
u s b _ d e r e g i s t e r _ d e v ( i n t e r f a c e ,
&skel_class);
unlock_ker nel();
kref_put(&dev->kref , skel_delete);
}
2.3 内存映射
U S B 摄像头驱动使用V i d e o 4 L i n u x接口获取图像信息的方法有两种:直接读取图像数据和使用内存映射方法获取图像信息。本文采用后一种方式获取图像信息。使用内存映射,使得进程之间通过映射同一个内存数据空间,实现内存的共享,让进程可以像访问普通文件一样对硬件设备获取的数据进行访问[ 5 ]。该方法很好地提高了数据的访问速度,并且由于不使用r e a d 系统调用,大大降低了系统开销。
通过以下主要代码实现内存的分配:
…
addr = (unsigned long ) f rame->data;
w hi le (si ze > 0) {
p a g e = v m a l l o c
((vo id *) addr);
…
}
…
v ma->v m_ops = &gspca_v m_ops;
v ma->v m_pr ivate_data = f rame;
gspca_v m_open(v ma);
…
在代码中使用v m a l l o c 内核函数分配内存,该函数分配的虚拟地址空间在物理上可能不是连续的,但是内核却认为它们在地址上是连续的。该函数是L i nu x 内存分配机制的基础,在如何使用硬件上没有区别。
测试与性能分析
嵌入式测试平台如图4 ,主要由A R M 9 芯片、工作频率为4 0 0 M H z ,6 4 M 的S D R A M ,3 . 5 英寸L C D 屏幕以及U S B 接口构成,能够满足U S B 摄像头驱动测试的硬件要求。
驱动程序在宿主机接上U S B 摄像头后,能够顺利完成初始化以及探测功能,运行用户应用软件后能够顺利获取图像,长时间工作性能稳定。在不同的时间间隔下测试摄像头的帧数情况,获取数据见表2。
由表中数据可以看出,平均帧数在1 1 左右,能够满足视频设备采集图像的基本需求。
结语
本文介绍了驱动程序与U S B 设备之间的关联,详细说明了系统内核与U S B 设备之间的通信方式、U S B 摄像头的探测以及断开函数的设计技术以及内存映射机制,开发出了符合Vi d e o 4 L i n u x 内核接口标准的设备驱动程序,通过测试数据可以看出该驱动程序能够满足嵌入式L i nu x 环境下的视频图像采集要求。本文介绍的驱动程序的开发技术对于嵌入式L i nu x 环境下的U S B 设备驱动开发有一定的借鉴意义。
Linux环境下USB摄像头驱动开发
计算机与信息工程系
电子信息工程10-1
赵丹
因篇幅问题不能全部显示,请点此查看更多更全内容