嵌入式Linux下的USB设备驱动技术
Linux以其稳定、高效、易订制、硬件支持广泛、源代码开放等特性,已在嵌入式领域迅速崛起,被国际上许多小型的跨国企业用作嵌入式产品的系统平台。
USB是UniversalSerialBus(通用串行总线)的简写,是1995年由Microsoft、Compaq、IBM等公司联合制订的一种新的PC串行通讯合同。它是一种快速、灵活的总线插口。与其它通讯插口相比较,USB插口的最大特征是便于使用,这也是USB的主要设计目标。USB的成功得益于在USB标准中除定义了通讯的数学层和家电层标准外。还定义了一套相对完整的软件合同堆栈。这促使多数USB设备都很容易在各类平台上工作。作为一种高速总线插口,USB适用于多种设备(如数码单反、MP3播放器、高速数据采集设备等)。另外,USB插口还支持热拔插,并且所有的配置过程都由系统手动完成,无须用户干预。
1Linux下的USB设备驱动
在Linux内核的不断升级过程中,驱动程序的结构相对稳定。因为USB设备也是外围设备的一种,因而,它的驱动程序结构与普通设备的驱动程序相同。Linux系统的设备分为字符设备(CharDevice)和块设备(BlockDevice)。字符设备支持面向块字符的I/O操作,它不通过系统的快速缓存,而只支持次序存取。块设备则支持面向块的I/O操作,所有块设备的I/O操作都通过在内核地址空间的I/O缓冲区进行,可以支持几乎任意宽度和任意位置上的I/O恳求。块设备与字符设备还有一点不同,就是块设备必须才能随机存取(RandomAccess),字符设备则没有这个要求。典型的字符设备包括键盘、键盘、串行口等,而块设备主要包括硬碟软驱设备、CD-Rom等。因为USB设备主要都是通过快速串行通信来读写数据,因而通常都可作为字符设备来进行处理。
2Linux下的USBcore
2.1Linux中USBcore与USB的结构关系
Linux操作系统中有一个称作“USBcore”的子系统,可提供支持USB设备驱动程序的API和USB主机控制器的驱动程序。同时提供有许多数据结构、宏定义和功能函数来对硬件或设备进行支持。在Linux下编撰USB设备的驱动程序时,从严格意义上讲linux usb键盘驱动,就是使用这种USBcore的子系统所定义的数据结构、宏和函数来编撰数据的处理功能。在Linux下,core、hostcontroller和driver两者之间的关系如图1所示。
2.2USBcore的初始化
USBcore从USB子系统的初始化开始。USB子系统的初始化则在文件drivers/u***/core/u***.c里。其代码如下:
subsys_initcall(u***_init);
module_exit(u***_exit);
代码中的subsys_initcall是一个宏,相当于module_init,只不过由于这部份代码是核心,开发者一般把它看作一个子系统,而不仅仅是一个模块。由于USBcore模块代表的不是某一个设备,而是所有USB设备赖以生存的模块。因而,在Linux中,像这样把一个类别的设备驱动归结为一个子系统(例如PCI子系统、scsi子系统等)。基本上,drivers/目录下边第一层的每位目录都可算作一个子系统,由于它们代表了一类设备。通常地,u***_init是真正的初始化函数,而u***_exit()则是整个USB子系统结束时的清除函数:
函数u***_init主要完成初始化和注册设备。
2.3USB里的设备模型
Linux里一个很重要的概念是设备模型。对于驱动来说,设备的概念就是总线和与其相连的各类设备。在内核里,总线、设备、驱动也就是bus、device、driver是设备模型很重要的三个概念,它们都有自己专属的结构。在include/linux/devide.h里的定义为:
structbus_type{……};
structdevice{……);
structdevice_driver{……};
每次出现一个设备都要向总线注册,每次出现一个驱动,也要向总线注册。系统初始化时,应扫描联接许多设备,并为每一个设备完善一个structdevice的变量。每一次都应有一个驱动程序,并要打算一个structdevice_driver结构的变量。还要把这种变量加入相应的数组(如把device插入devices数组,driver插入drivers数组)。这样,通过总线才能找到每一个设备和每一个驱动。但是,如果计算机里只有设备却没有对应的驱动,这么设备将难以工作。反过来,如果只有驱动却没有设备,驱动也起不了任何作用。对于USB设备,它可以在计算机启动之后再插入或则拔出计算机。因为device可以在任何时刻出现,而driver也可以在任何时刻被加载,所以linux操作系统好吗,每每一个structdevice诞生时,它才会去BUS的drivers数组中找寻自己的另一半。假如找到了匹配的设备,就调用device_bind_driver,并绑定好。
Linux设备模型中的总线落实在USB子系统里就是u***_bus_type,它在u***_init函数中可用retval=bus_register(&u***_bus_type)句子注册,而在driver.c文件里的定义如下:
该函数的实参对应的就是总线两条数组里的设备和驱动。当总线上有新设备和驱动时,这个函数都会被调用。
3USB驱动程序的描述符
一个设备可以有多个插口,一个插口可代表一个功能,因而,每位插口都对应着一个驱动。诸如一个USB设备有两种功能,一个按键,里面还带一个麦克风,这就是两个插口,就须要两个驱动程序,一个是鼠标驱动程序,一个是音频流驱动程序。
一个驱动程序是否支持一个设备,要通过读取设备的描述符来判定。这么,哪些是USB的描述符呢?USB的描述符是一个带有预定义格式的数据结构,上面保存有USB设备的各类属性和相关信息,可以通过向设备恳求获得它们的描述符内容来深刻了解和感知一个USB设备。主要有四种USB描述符linux压缩命令,分别为:插口描述符、端点描述符、设备描述符和配置描述符。
合同规定:一个USB设备必须支持这四大描述符,还有些描述符不是必须包含的,有些特殊设备拿来描述设备的不同特点,但这四大描述符是一个都不能少的。USB设备里有一个eeprom,可拿来储存设备本身信息,设备的描述符就储存在这儿。
上述四个描述符分别置于了include/linux/u***.肉文件中的structu***_host_interface、structu***_host_endpoint、structu***_device、struetu***_host_config里,而描述符结构体本身定义在include/linux/u***/ch9.h里.并分别用structu***_interface_descriptor、structu***_host_endpoint、structu***_device_descriptor和structu***_config_descriptor来表示。描述符结构体的定义应完全依照USB合同对描述符的规定来定义。
4USB插口驱动
4.1插口结构
平常编撰的USB驱动一般指的是写USB插口的驱动,一个插口对应一个插口驱动程序,须要以一个structu***_driver结构的对象为中心,并以设备的插口提供的功能为基础,来进行USB驱动程序的编撰。structu***_driver结构体通常定义在include/linux/u***.h文件里。具体如下:
structu***_driver{
constchar*name;
int(*probe)(structu***_interface*intf,const
structu***_device_jd*id);
void(*disconnect)(structu***_interface*intf);
int(*ioctl)(structu***_interface*intf,unsigned
intcode,void*buf);
int(*suspend)(structu***_interface*intf,
pm_message_tmessage);
int(*resume)(structu***_interface*intf);
void(*pre_reset)(structu***_interface*intf);
void(*post_reset)(structu***_interface*intf);
conststructu***_device_id*id_table;
structu***_dynidsdynids;
structu***drv_wrapdrvwrap;
unsignedintno_dynamic_id:1;
unsignedintsupports_autosuspend:1;
};
Name为驱动程序的名子,对应于/sys/bus/u***/drivers/下边的子目录名称。它只是彼此区别的一个代号,这儿的名子在所有的USB驱动中必须是惟一的。probe拿来瞧瞧这个USB驱动是否乐意接受某个插口的函数。Disconnect函数将在插口丧失联系或使用rmmod卸载驱动将它和插口强行分开时被调用。Ioctl函数则用在驱动通过u***fs和用户空间进行交流时使用。Suspend、esume分别在设备被挂起和唤起时使用。pre_reset、post_reset分别在设备即将复位(reset)和早已复位后使用。id_table的变量可拿来判定是否支持某个设备插口。Dynids是支持动态id的。实际上,虽然驱动早已加载了,也可以添加新的id给它。drvwrap是给USBcore分辨设备驱动和插口驱动用的。no_dynamic_id可以拿来严禁动态id。supports_autosuspend可对autosuspend提供支持,假如设置为0,则不再容许绑定到这个驱动的插口autosuspend。
插口驱动
当insmod或modprobe驱动的时侯,经过一个坎坷的过程,都会调用相应USB驱动里的xxx_init函数linux usb键盘驱动,因而去调用u***_register(),以将相应的USB驱动递交给设备模型,添加到USB总线的驱动数组里。当rmmod驱动时,同样,在经过一个坎坷的过程以后,再调用相应驱动里的xxx_cleanup函数,从而调用u***_deregister()将相应的USB驱动从USB总线的驱动数组里删掉。
5结束语
本文介绍了Linux下USBcore的工作原理,同时介绍了驱动USB必须了解的四个描述符。据悉,还介绍了Linux下u***插口驱动的工作原理。本文介绍的方式能适应于Linux下各类不同的USB设备驱动程序的开发。