驱动程序最重要的部分就是驱动程序源代码文件,源文件描述了设备如何和VxBus、VxWorks OS交互。但是,VxWorks 设备驱动程序还需要另外一些文件,这些附加文件能够帮助你将自己编写的驱动集成到VxWorks编译环境中去,这也是发布驱动程序最重要的一步。本节主要讨论如何在源码树中找到相关的驱动程序文件和其他附加文件。最后还说明驱动程序的各个部分是如何安装在VxWorks OS中的。
在开发驱动程序之前,了解驱动程序文件在VxWorks源码树中的位置是非常重要的,驱动程序文件主要分布在源码树中的3个不同位置。
根据驱动程序的类型,installDir/vxworks-6.x/target/src/hwif目录下的驱动被组织成不同的子目录,例如,定时器的驱动程序在目录installDir/vxworks-6.x/target/src/hwif/timer
第三方驱动程序的组织方式允许驱动程序开发厂商和开发者创建第三方驱动程序,不需要担心不同厂商的文件之间的命名空间冲突。每一个想提供VxWorks驱动程序的厂商必须在3rdparty目录创建自己的子目录,比如说,Acme公司计划为vxworks开发第三方设备驱动程序,那么就必须在3rdparty目录创建自己的目录installDir/vxworks-6.x/target/3rdparty/acme,在这个目录下,不同类型的驱动程序又组织成不同目录,跟hwif目录一样。
风河公司提供的VxBus的驱动程序例子位于目录:installDir/vxworks-6.x/target/3rdparty/windriver/wrsample,这些文件可以被当做模板来帮助你开发第三方驱动程序,具体信息请参考wrsample目录下的README文件。
尽管一个驱动程序可以包括很多文件,比如多个源文件和多个头文件,但是一个标准的VxWorks驱动程序有一个最小的文件集,对于大多数vxworks驱动程序最少要求6个文件:
一般情况下,CDF文件,dc文件,dr文件都被认为是驱动程序的配置文件,下面详细介绍这些文件。
驱动程序源文件包含了驱动程序功能的实现逻辑,它们被放在目录installDir/vxworks-6.x/target/src/hwif, 第三方的被放在目录installDir/vxworks-6.x/target/3rdparty。很多VxWorks设备驱动程序只包含一个源文件,一个驱动程序可以包含一个或者几个可选的头文件;但是驱动程序可以包含多个源文件,但是此时必须在Makefile里面提供各个模块的依赖规则。下面以文件vxbCn3xxxTimer.c来说明VxWorks驱动程序的结构。
设备驱动程序的第一部分是一个描述VxBus初始化阶段要调用的例程的结构:
/* data structures used by the driver to register itself
* with Vxworks
*/
/* drvBusFuncs provides a set of entry points into the
* driver that are called during various phases of the
* boot process. Drivers can choose to implement 1 or
* more of these entry point, according to the needs of
* the driver during its initialization phases.
*/
LOCAL struct drvBusFuncs cn3xxxTimerDrvFuncs =
{
cn3xxxTimerInstInit, /* devInstanceInit */
cn3xxxTimerInstInit2, /* devInstanceInit2 */
cn3xxxTimerInstConnect /* devConnect */
};
接着就是描述驱动程序所支持的驱动方法的数据结构(每一种类别的驱动程序都必须实现该类的驱动方法):
/* cn3xxxTimerDrv_methods provides the list of driver
* methods that this driver supports. For each driver
* class supported by Wind River, one or more methods
* are expected to be defined for the driver. For
* timer driver class, the 'vxbTimerFuncGet' method
* is required to be supported.
*/
LOCAL struct vxbDeviceMethod cn3xxxTimerDrv_methods[] =
{
DEVMETHOD(vxbTimerFuncGet, cn3xxxTimerFuncGet),
{0,NULL}
};
跟着就是描述该驱动程序需要的注册信息的结构:
/* The cnxxxTimerDrvRegistration structure provides a
* description of the driver to VxWorks, so that VxWorks
* can connect this driver to appropriate hardware during
* the boot process.
*/
LOCAL struct vxbDevRegInfo cn3xxxTimerDrvRegistration =
{
NULL, /* reserved for VxBus use */
VXB_DEVID_DEVICE, /* devID */
VXB_BUSID_PLB, /* busID = PLB */
VXB_VER_4_0_0, /* vxbVersion */
"cn3xxxTimerDev", /* drvName */
&cn3xxxTimerDrvFuncs, /* pDrvBusFuncs */
NULL /* pMethods */
NULL /* devProbe */
};
在注册信息后面,驱动程序必须提供一个例程来向VxBus注册,表明该驱动程序的存在:
/* The vxbCn3xxxTimerDrvRegister function contains the
* first instructions of the device driver that are
* ever executed within a VxWorks system. This function
* registers the driver with VxBus by providing pointers
* to the data structures listed previously. Once this
* step is complete, VxWorks is able to associate this
* driver with appropriate hardware within the system
* to form an instance.
*/
void vxbCn3xxxTimerDrvRegister (void)
{
vxbDevRegister (&cn3xxxTimerDrvRegistration);
}
由于驱动程序注册方法被当做是驱动程序的第一个入口点,VxWorks必须被配置成:当该驱动程序向VxBus注册时,VxWorks知道调用该入口点。为了做到这点,VxWorks使用了之前提到了那几个驱动配置文件:CDF文件,dc文件,dr文件。
注意:VxBus和VxWorks要求驱动程序的注册方法必须是全局的。大多数驱动程序并不需要其他的全局符号,因此都可以声明成LOCAL。
该文件的全称是:component description File,组件描述文件。根据VxBus标准开发的VxWorks设备驱动程序都被编译成一个单独的模块,可以使用VxWorks配置工具非常轻松地将驱动程序配置进BSP中。但是,你必须为你的设备驱动程序创建一个VxWorks组件。
一个组件是一个基本的功能单元,它可以单独配置进入VxWorks内核镜像中。为了能够单独添加和删除设备驱动程序到VxWorks中,驱动程序必须能够被VxWorks配置工具识别成individual 组件。为了让驱动程序能够在Workbench或者vxprj中是可以配置的,你必须创建CDF文件,CDF文件提供VxWorks配置工具所需要的信息。针对风河公司发布的设备驱动程序,其对应的CDF文件位于以下目录:
installDir/vxworks-6.x/target/config/comps/vxWorks
在风河提供的驱动程序中,一个CDF文件可能包含着描述多个设备驱动程序的信息,对于第三方驱动,其CDF文件路径是在驱动程序目录下。
注意:内核配置工具并不自动搜索installDir/vxworks-6.x/target/3rdparty/ directories下的文件,为了让内核配置工具能够读取第三方驱动的CDF文件,必须手工将CDF文件拷贝到以下目录:installDir/vxworks-6.x/target/config/comps/vxWorks
为新驱动编写CDF文件之前,首先拷贝一个标准的CDF文件到你的驱动程序目录,然后根据你的驱动程序来修改CDF文件。CDF文件主要放在以下目录:
installDir/vxworks-6.x/target/config/comps/vxWorks
以下以一个PCI总线控制器的CDF文件为例,这个文件的路径是:installDir/vxworks-6.x/target/config/comps/vxWorks/40m85xxPci.cdf:
/* 40m85xxPci.cdf - Component configuration file */
Component DRV_PCIBUS_M85XX {
NAME M85xx PCI bus
SYNOPSIS M85xx PCI bus controller Driver
MODULES m85xxPci.o
SOURCE $(WIND_BASE)/target/src/hwif/busCtlr
_CHILDREN FOLDER_DRIVERS
_INIT_ORDER hardWareInterFaceBusInit
INIT_RTN m85xxPciRegister();
PROTOTYPE void m85xxPciRegister (void);
REQUIRES DRV_RESOURCE_M85XXCCSR \
INCLUDE_PARAM_SYS \
INCLUDE_PCI_BUS \
INCLUDE_PLB_BUS \
INCLUDE_VXBUS
INIT_AFTER INCLUDE_PCI_BUS
}
Component DRV_PCIBUS_M85XX {
CDF文件使用上述语句来定义一个组件ID。VxWorks中的每个组件必须用Component关键字来描述,驱动程序的ID一般以DRV_开始,并在ID中包含该驱动程序的描述性信息,每一类的驱动程序对组件ID都有着相同的命名习惯,比如DRV_PCIBUS_M85XX意味着它是一个PCI 总线控制器的组件。设备驱动程序组件标准的命名习惯应该是:DRV_CLASS_NAME,组件名字必须是唯一的,因此DRV_CLASS_NAME中的DRV和CLASS都有可能相同,这就要求NAME必须唯一才行。如果为第三方驱动写CDF文件,可以考虑把VENDOR和驱动程序名作为NAME的一部分。总之,保证DRV_CLASS_NAME唯一就可以。
注意:以前老的驱动程序一般以INCLUDE_开头,不过新驱动都更改了这一用法,使用DRV_开头。
NAME M85xx PCI bus
这是名字域,可读性较强,会在内核配置工具的名字栏显示该域定义的信息
SYNOPSIS M85xx PCI bus controller Driver
摘要域,主要是对该组件的简短介绍
MODULES m85xxPci.o
模块域,它列举了编译驱动程序生成的目标文件的名字。当一个驱动程序被包含在工程中时,VxWorks配置服务会分析模块域定义的目标文件中的内容,以决定是否会包含其他组件来把该驱动程序编译进VxWorks镜像。MODULES和REQUIES域一起,提供了把驱动程序编译进VxWorks内核所有需要的信息。
_CHILDREN FOLDER_DRIVERS
这个域用来帮助Workbench对相似的组件的显示进行管理,如下图:
_INIT_ORDER hardWareInterFaceBusInit
这个初始化顺序域描述的是:在VxWorks启动的过程中,应该在什么时候初始化本驱动程序,所有的总线驱动程序都必须在hardWareInterFaceBusInit中初始化,PCI总线也不例外。
INIT_RTN m85xxPciRegister();
定义设备驱动程序的初始化方法,因此,我们必须在这个域提供驱动程序注册方法。这个只是一个初级或者说最小的初始化,后续的初始化会等到VxWorks找到相关的硬件后就会将驱动程序和硬件绑定,形成了一个实例。
PROTOTYPE void m85xxPciRegister (void);
驱动注册方法的原型定义
REQUIRES …
该域描述了驱动程序能够在VxWorks中正常工作所必须有的组件。这个域是必须存在的,因为不是所有设备驱动程序的依赖性都能够通过检查驱动中的unresolved externals来决定的。为了支持该驱动程序,它和MODULES域一起决定哪些组件还需要被包含进来。
INIT_AFTER INCLUDE_PCI_BUS
这个INIT_AFTER和INIT_BEFORE被用于指明INIT_ORDER的依赖性,INIT_AFTER说明必须先初始化PCI总线,才能够初始化本驱动。INIT_BEFORE是在这之前必须要初始化本驱动。
HDR_FILES $(WIND_BASE)/target/src/hwif/h/end/fei8255xVxbEnd.h
上面的这行代码并没有在例子中出现,HDR_FILES主要提供驱动注册函数的原型
CFG_PARAMS参数配置
在初始化期间,有时候设备驱动程序需要一些配置信息,如果这些信息是和设备而不是实例相关的,则可以再编译阶段通过传递参数来实现。CFG_PARAMS和Parameter关键字将帮你实现这些任务。CFG_PARAMS用来指明组件要用的参数,而Parameter用来定义参数。如:
Component DRV_NET_SAMPLE {
NAME network device supporting jumbo frames
...
CFG_PARAMS SAMPLE_JUMBO_MTU_VALUE
}
Parameter SAMPLE_JUMBO_MTU_VALUE {
NAME Jumbo frame MTU size
SYNOPSIS max num of bytes in a jumbo MTU
TYPE int
DEFAULT 9000
}
每个参数都必须有NAME,SYNOPSIS,TYPE,DEFAULT四个域。TYPE描述了参数的数据类型,包括任何C类型,BOOL,string(NULL-terminated);DEFAULT定义的值是参数的默认值。
对于某些BSP,VxWorks支持两种不同的方式来构建VxWorks镜像,一是使用Workbench或者vxprj命令行工具,二是直接在BSP目录中调用make命令。所有的BSP都支持第一种方法,尽管大多数都支持make方式,但是并不是所有的VxWorks开发环境和SMP都支持。
当从makefile文件编译BSP时,CDF里面的信息并不用来配置BSP,程序员反而可以在BSP源文件config.h里面直接通过INCLUDE和EXCLUDE添加、删除某些组件,例如,如果你把Cn3xxx定时器驱动程序包含进你的VxWorks镜像中,可以在BSP中定义:#define DRV_TIMER_CN3XXX,为了简单起见,这里假设DRV_TIMER_CN3XXX不依赖于其他组件。修改完config.h文件后,你可以在BSP目录中直接调用make工具来编译BSP,编译BSP后,刚刚添加的定时器驱动程序已经可以被包含进VxWorks镜像了。
为了支持驱动程序在BSP中直接编译,你必须创建两个附加文件,前面提过的DC和DR文件,这两个文件的作用就是把驱动程序和BSP命令行工具关联起来。dc文件的名字必须和驱动程序源文件的名字一样(后缀不一样嘛),以CN3XXX定时器驱动程序为例,vxbCn3xxxTimer.dc文件如下:
IMPORT void vxbCn3xxxTimerDrvRegister();
DC文件主要是提供驱动程序注册方法的原型声明,或者以#ifdef../#endif来包含住:
#ifdef DRV_TIMER_CN3XXX
IMPORT void vxbCn3xxxTimerDrvRegister();
#endif
DR文件就更加简洁了,描述了如何调用驱动程序注册方法,vxbCn3xxxTimer.dr文件如下:
#ifdef DRV_TIMER_CN3XXX
vxbCn3xxxTimerDrvRegister();
#endif
注册方法必须使用#ifdef/#endif结构来包含,作用是当系统不包含此组件时,就不需要注册。在#ifdef后面使用的宏必须和CDF中使用的宏一致。对于风河公司提供的设备驱动程序,DC和DR文件的位置在以下目录:
installDir/vxworks-6.x/target/config/comps/src/hwif
对于第三方驱动,DR和DC文件在驱动程序的目录下。为了使这些文件起作用,它们必须被合并到VxWorks镜像的初始化文件中,当新的驱动被增加到VxWorks源码树时,可以使用以下命令来重新创建初始化文件:
cd installDir/vxworks-6.x/target/config/comps/src/hwif
make vxbUsrCmdLine.c
当执行MAKE命令时,它会搜索所有的驱动程序配置文件,并一起合并到vxbUsrCmdLine.c文件中,该文件的目录在:
installDir/vxworks-6.x/target/config/all/vxbUsrCmdLine.c
注意:vxbUsrCmdLine.c文件在以下几种情况下是不会被更新的:vxbUsrCmdLine.c的修改时间比dc、dr文件和installDir/vxworks-6.x/target/config/comps/src/hwif目录中的文件的修改时间要更新,在这种情况下,把以前的vxbUsrCmdLine.c文件重命名,并重新执行MAKE命令即可。
虽然MAKE环境并不要求有README文件,但是每个设备驱动程序都应该提供README文件给最终用户。第三方厂商可能会在README文件里面描述驱动版本信息、驱动程序的文件列表、已知的缺陷等信息。README文件包括三个数据段和三个行分隔符:
README: VxWorks/VxBus driver for device device
注意:驱动程序版本信息包括两个部分(例如2.3),不能使用3部分的版本信息格式,README文件的一个例子目录在:
installDir/vxworks-6.x/target/3rdparty/windriver/wrsample
为了在VxWorks环境中正确编译设备驱动程序,你必须提供合适的Makefiles。有两种makefile来帮助你解决问题,第一种是厂商的makefile,目录位于:installDir/vxworks-6.x/target/3rdparty/vendor/Makefile,第二种是驱动程序的makefile,目录位于:installDir/vxworks-6.x/target/3rdparty/vendor/driver/Makefile。这些makefiles里面的内容有点复杂,因此可以在以下目录拷贝一个makefile模板,然后修改即可:
installDir/vxworks-6.x/target/3rdparty/windriver
installDir/vxworks-6.x/target/3rdparty/windriver/wrsample
(1)厂商makeflie
厂商的makefile是被同一个厂商目录下的所有驱动程序共享的,它使用通配符来确定哪些驱动程序被安装到了厂商目录下,并为每一个设备驱动程序调用make命令。从目录installDir/vxworks-6.x/target/3rdparty/windriver拷贝makefile模板,并添加到对应厂商目录下installDir/vxworks-6.x/target/3rdparty/vendor,你可以根据makefile模板做相应的修改。
(2)驱动程序makefile
在以下目录创建驱动程序makefile
installDir/vxworks-6.x/target/3rdparty/vendor/driver
这个makefile文件用于编译驱动程序子目录中的源文件,这个makefile也可以从以下目录拷贝并加以修改:installDir/vxworks-6.x/target/3rdparty/windriver/wrsample。但是,跟厂商makefile不一样的是,驱动程序makefile不使用通配符来寻找驱动程序源文件,相反,它包含一个要生产的目标文件列表,并说明如何从源文件中编译生成这些目标文件。wrsample中的makefile的comment能够帮助你根据驱动程序来修改makefile,不过通常情况下,主要是修改LIB_BASE_NAME(公司名字),和OBJ_COMMON(要生产的目标文件列表)。另外如果你只想为某一CPU平台编译驱动程序,可以使用该CPU平台的定义来代替OBJ_COMMON,如POWERPC平台(OBJ_PPC32)。