VxBus是风河公司新的设备驱动程序架构,是VxWorks新增的特性,它是在VxWorks6.2及以后版本被增加到VxWorks中的。本文结合基于PCI2040数据采集卡驱动的开发过程,分析了VxBus架构下驱动的设计实现。

VxBus简介

VxBus是指在VxWorks中用于支持设备驱动的特有的架构,这种架构包含对minimal BSP的支持。它包括以下功能:

  • 允许设备驱动匹配对应设备;
  • 提供驱动程序访问硬件的机制;
  • 软件其他部分访问设备功能;
  • 在VxWorks系统中,实现设备驱动的模块化。

VxBus在总线控制器驱动程序服务的支持下,能在总线上发现设备,并执行一些初始化工作,使驱动与硬件设备之间正常的通讯。

VxWorks VxBus Demonstration

图1是VxBus 在整个系统中的位置示意图。从图1中可以看到,VxBus起到了辅助总线的作用,提供了对总线控制驱动的支持。

在VxWorks6.2版本发布前,设备驱动并不能被集成到VxWorks工程配置当中,为了添加或移出设备驱动,需要有丰富的BSP和驱动开发相关的知识。作为VxWorks系统组件的一部分,VxBus消除了上面遇到的一些难题,各种驱动和支持组件的添加与删除完全可以在Workbench工程中进行,不需要BSP和驱动相关的知识,也不会在添加或者删除驱动时增加管理VxWorks工程的额外工作。

vxBus下对设备管理做了更为详细的划分,简单说来,硬件称为device,软件叫做driver。如果一个device出现在硬件列表中,启动时需要到driver的队列中去找相应的driver,如果找到,二者结合成一个instance,否则在vxBusShow里可以看到一个orphan。使用vxBusShow可以比较清晰的看到driver列表和device列表以及orphan列表。

如果需要增加一个设备,首先要修改的是hcfDeviceList,这是hwconf.c中的一个数组,这个数组中列有本系统中所有需要初始化的硬件设备。例如:

  HCF_DEVICE hcfDeviceList[] = {
  #ifdef DRV_SIO_ns16550
  { "ns16550", 0, VXB_BUSID_PLB, 0, ns1655x1Num, ns1655x1Resources },
  { "ns16550", 1, VXB_BUSID_PLB, 0, ns1655x2Num, ns1655x2Resources },
  #endif

第二个ns16550是新加的。在参数中指明了它的名字“ns16550”,初始化的时候根据这个名字去搜索驱动。它的unit是1,区别与前面的0,初始化结束以后,会在设备列表中看到这个unit number。ns1655x2Resources是前面定义的一组资源,跟ns1655x1Resources类似,只是regBase和irq按照实际情况做区分。ns1655x2Num是ns1655x2Resources的长度。

对于单核CPU,配置到这里就结束了。如果是多核,还需要修改一下sysDeviceFilter,这个函数决定一个设备在哪个核上初始化。如果还有hypervisor,还需要修改wrhvConfig.xml和vxworksX.xml,将特定的终端放开到指定的核。

硬件介绍

TI公司推出的PCI2040是一款用于实现PCI局部总线与DSP之间无缝链接的专用芯片。在VxWorks实时操作系统环境下实现主机与DSP的通讯,系统利用PCI2040实现TMS320VC5410与主机的通讯。TMS320VC5410的MCBSP0与TLC2548 连接,实现8路12位A/D数据的采集。TMS320VC5410将采集到的数据通过PCI2040传输到主机上,数据在主机上得到进一步的处理。硬件连接框图如图2所示。

VxWorks VxBus hardware connection

驱动开发

设备驱动初始化

设备的初始化,包含在BSP的初始化过程中,主要分三个阶段,如图3所示。

VxWorks VxBus device driver

内核预初始化阶段

在VxWorks内核预初始化早期,BSP的sysHwInit( )函数被执行[4],在这个函数中,设备驱动初始化工作第一步被执行。sysHwInit( )函数执行一些早期的初始化,调用hardWareInterFaceInit( )函数,执行初始化硬件内存分配机制,限制为设备驱动分配内存,这个函数接着调用hardWareInterFaceBusInit( ),在hardWareInterFaceBusInit( )函数中完成所有设备驱动和模块的注册工作。PCI2040的注册函数是vxbPci2040Register()。vxbPci2040Register()通过数据结构,向系统注册一些设备初始化函数。其中涉及到三个数据结构:

  LOCAL struct drvBusFuncs PciFuncs =
  {
  Pci2040InstInit,    /* devInstanceInit */
  Pci2040InstInit2,    /* devInstanceInit2 */
  Pci204InstConnect    /* devConnect */
  }

在这个结构中,包含了初始化阶段要调用的函数。下面的初始化过程会用到这些函数。

  LOCAL struct vxbDeviceMethod Pci2040Methods[] =
  {
  DEVMETHOD(ReadHPID,    PCI2040ReadHPID),
  DEVMETHOD(WriteHPID,    PCI2040WriteHPID),
  DEVMETHOD(ReadHPIA,    PCI2040ReadHPIA),
  DEVMETHOD(WriteHPIA,    PCI2040WriteHPIA),
  DEVMETHOD(ReadHPIC,    PCI2040ReadHPIC),
  DEVMETHOD(WriteHPIC,    PCI2040WriteHPIC),
  DEVMETHOD(ReadCSR,    PCI2040ReadCSR),
  DEVMETHOD(WriteCSR,    PCI2040WriteCSR),
  { 0, 0 }
  }

这个结构提供了应用软件操作硬件的一些函数及方法。

  LOCAL struct vxbPciRegister Pci2040DevPciRegistration =
  {
  {
  NULL,                        /* pNext */
  VXB_DEVID_DEVICE,        /* devID */
  VXB_BUSID_PCI,            /* busID = PCI */
  VXB_VER_4_0_0,                /* vxbVersion */
  LNPCI_NAME,                    /* drvName */
  &Pci2040Funcs,            /* 总线驱动函数*/
  Pci2040Methods,            /* 设备方法结构 */
  Pci2040Probe,                    /* 设备探测函数 */
  Pci2040ParamDefaults        /* 参数*/
  },
  NELEMENTS(PciPci204DevIDList),
  PciPci204DevIDList                /*设备资源列表*/
  };

最后这个结构在vxbPci2040Registe()中被使用。这个结构包括几个驱动的初始化入口,其中Pci2040Probe()是PCI2040采集卡的硬件探测函数,该函数在VxBus初始化过程中检测采集卡的数量,当检测到采集卡时,将采集卡与驱动结合,形成设备的一个实例,以便应用程序使用。Pci204InstanceInit( )函数在VxBus初始化的第一阶段被调用, Pci204InstanceInit( )函数只是简单地确保设备的中断被禁止。

当所有驱动在VxWorks注册之后,hardWareInterFaceBusInit( )和hardWareInterFaceInit( ) 函数返回,sysHwInit( ) 完成非VxBus 驱动的初始化并返回。sysHwInit( ) 函数返回后,VxWorks内核被初始化。

应用程序初始化驱动部分

在devInstanceInit2( )函数最后,创建用户的运行任务,并完成设备驱动的初始化。在这个阶段,Pci2040InstanceConnect( )函数被调用,完成最后的初始化工作,在这个函数中,主要是建立中断与中断服务程序的连接。

驱动程序的配置

采用VxBus驱动的一个主要优点是:设备的驱动程序可以被看成VxWorks 系统的一个组件,通过集成的Workbench开发环境来配置设备驱动。PCI2040数据采集卡需要有以下文件:

  • 一个驱动源文件PCI2040.c,执行驱动运行逻辑,包括PCI2040驱动的实现代码。
  • 一个组件描述文件PCI2040.cdf,允许集成驱动到VxWorks开发工具Workbench当中。
  • 一个PCI2040.dc文件,提供驱动注册函数原型。
  • 一个PCI2040.dr文件,提供一个调用注册函数的C语言代码段。
  • 一个readme文件 ,提供版本信息。
  • 一个makefile 文件,提供建立驱动的编译规则。
应用程序与驱动的通信

为了使设备和驱动能够在VxWorks系统中使用,让应用程序、中间件、VxWorks内核模块访问设备,执行一些操作,最基本的方法是在VxWorks中采用VxBus方法来实现硬件设备的访问。VxBus方法是在驱动中公开一个入口,使VxBus中API函数可以调用这些入口函数。在PCI2040初始化阶段,Pci2040Methods结构中注册的函数就是在驱动中公开的函数,用于对PCI2040的操作。

例如,通过PCI2040 完成对DSP数据寄存器的访问

struct vxbDriverControl ctrl;
vxbDevMethodRun(DEVMETHOD_CALL(ReadHPID),&ctrl);

vxbDevMethodRun( )函数够被用于调用一个指定的驱动方法,这个函数反复查找所有的实例,并检查每一个,看是否有指定公开申明的方法,如果实例有指定的方法,vxbDevMethodRun( )调用方法函数。

为了避免重复遍历在系统上的所有实例,可以用 vxbDevMethodGet( )函数找出驱动函数相对应的驱动方法 ,然后通过下面代码完成函数调用。

    STATUS (*methodFunc)(VXB_DEVICE_ID devID, void * pArg);
    methodFunc = bDevMethodGet(devID,DEVMETHOD_CALL(ReadHPID));
    if(methodFunc != NULL )
    (*methodFunc)(devID, pArg);

在PCI2040的数据采集卡中,通常是DSP在采集完数据后,通过中断通知主机,去读取数据。下面是中断服务相关代码。

  void PCI2040Isr()
  { ……
  temp=*(PCI2040. instID.pRegister+0x4); //读中断寄存器
  if((tempr&0x1)!=0)               //检查是否是该实例中断
  { *(PCI2040. instID.pRegister +0x4)=0x1;
  temp= *(PCI2040. instIDpDspHpicRegister);
  *(PCI2040. instIDpDspHpicRegister)=temp|0x0808;
  //通知DSP,清除HINT中断
  semGive(semForPci2040Int);
  }
  }

采用基于VxBus架构来开发PCI2040数据采集卡的驱动,通过扩展文件实现驱动的配置。与简单的非VxBus驱动相比,显然增加了工作量,然而对于基于多个BSP设备的复杂的驱动,VxBus驱动是优于非VxBus驱动的。通过实际运用证明,所开发采集卡的驱动能够稳定运行,并且能很方便地将该驱动移植到其他的系统。