黄 峰 单家方 匡光力 (中国科学院等离子体物理研究所 10 室 230031 ) 摘要: 本文介绍 QNX6.20 系统下设备资源管理器的运行机理和体系系统。结合 PXI 总线设备的体系结构,我们采用三级分离的方式开发 PXI 多功能采集卡的驱动程序。文章具体分析了应用程序级和硬件驱动级驱动函数的开发,重点阐述了系统调用级驱动函数的开发。 关键词: QNX PXI 资源管理器 进程管理器 实时操作系统 The Development of the Driver of PXI Multi-function Data Acquisition Card in QNX HUANG Feng, SHAN Jia-Fang, KUANG Guang-ling ( Institute of Plasma Physics, Chinese Academy of Sciences, Hefei 230031, China)
Abstract: The article introduces the system architecture and running theory of the device resource manager in QNX. Combining the system architecture of the PXI bus device, we develop the driver of PXI multi-function data acquisition card in the method of the three-level driver. In the article, we concretely analyze the development of driver function at the application level and the device driver level; give emphasis to the development of driver function at the system quote level. Key words: QNX PXI Process Manager Resource Manager Real Time OS 1 引言 QNX 是一个分布式、多用户、多任务、可嵌入式的实时操作系统。被广泛应用到电信系统、航天仪器、工业自动化等方面。目前很多 PXI 总线的外围设备都不具备 QNX 下的驱动程序。要在 QNX 系统下应用就需要自己开发驱动程序。 QNX 系统下的设备驱动程序被称作为资源管理器。由于 QNX 微内核符合 POSIX 标准, QNX 下设备管理函数大部分是 POSIX 函数。为了使微内核更具有灵活性和减少应用系统对存储器的需求,以及适应不同级别不同规模系统的应用, QNX 允许用户编写特定的资源管理进程。资源管理器是为各种不同的设备提高统一的界面接口,它们管理的有真实的硬件(串口、并口等)也有虚拟的设备( /dev/null ,虚拟终端等)。在其他的操作系统中这些功能经常与内核协同工作。但是在 QNX 系统中资源管理器不需要任何内核的安排,实际上它就是一个普通的用户级服务程序。它接收其他程序发来的消息,并且可以选择是否需要与硬件进行通信,具有强大功能和灵活性的本地 IPC 服务使得资源管理器从操作系统中分离出来。 2 QNX 资源管理器原理 2.1 设备驱动原理 QNX 资源管理器与操作系统内核的分离使得其驱动原理与其他操作系统下的设备驱动程序有所不同。在 QNX 下,系统用一种非常灵活的机制——名字空间分配( pathname space mapping )将资源管理器与其客户端进行绑定。资源管理器对于所要管理的资源都赋予一个特定的路径名,并向进程管理器进行注册。进程管理器起着调度资源管理器以满足客户进程请求的作用。 2.2 QNX 资源管理器框架 从功能上来分, QNX 设备管理器通常由以下四层构成: (1) 功能函数层:提供统一的功能函数。 (2) 消息接受层:接受和分析客户进程发送的请求消息 (3) 消息调度层:调度消息队列的处理。 (4) 线程管理层:管理资源管理器内的线程。 具体地说,就是资源管理器先创建一个通道(由 dispatch_create () 函数实现),这样客户程序就可以连接到资源管理器并进行进一步的服务请求。然后资源管理器向进程管理器注册一个特定的名字空间( resmgr_attach ()函数实现)。进程管理器根据注册的名字空间为请求操作的客户进程指派相应的资源管理器。由指派的资源管理器响应请求操作。请求操作是以消息的形式与资源管理器进行通信。消息有两大类:建立连接消息和 I/O 消息。在资源管理器中有两个结构( resmgr_connect_funcs_t 类型和 resmgr_io_funcs_t 类型)分别登记了这两类消息对应的响应函数。在资源管理器初始化时,要为各种消息指定相应的响应函数。例如,为打开设备的消息指定函数: main (int argc, char **argv) { … resmgr_connect_funcs_t connect_funcs ; /*install all of the default function */ iofunc_funs_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs, RESMGRIO_NFUNCS, &io_funcs); /*take over the open function */ connect_funcs.open = io_open; … } int io_open (resmgr_context_t *ctp, io_open_t *msg , RESMGR_HANDLE_T *handle, void *extra) {return (iofunc_open_default (ctp, msg, handle, extra) ); } 3 PXI 总线系统体系结构 PXI (PCI eXtensions for Instrumentation ,面向仪器系统的 PCI 扩展 ) 是一种坚固的基于 PC 的测量和自动化平台。 PXI 的规格区分为硬件与软件两个部分。其中硬件部分是基于 CompactPCI 的规格,也就是 PICMG 2.0 。建构于 CompactPCI 的机构规格与 PCI 的电气规格之上,加上仪器上所需要的电气信号延伸,即是所谓 PXI 的规格。所以, PXI 的数据传输速率的峰值于 33MHz 与 32-bit 的总线上,可达 132MB/s 。于 66MHz , 64-bit 的总线上则可高达 528MB/s 。远高于 GPIB 与 VXI 接口的传输速率。
( 1 ) 10MHz 参考时脉 (10MHz Reference Clock) ( 2 ) 8-bit PXI 触发总线 (PXI Trigger Bus) ( 3 )星形触发 (Star Trigger) ( 4 ) 13-bit 局部总线 (Local Bus) 利用 PXI 上延伸信号中的触发总线可达到多卡同步的功能;利用星形触发可以给其他扩充槽发送信号;利用局部总线可将控制信号一级一级地传输,或者作为区域的通信管道。 4 PXI 驱动程序实现 由于 PXI 结合了 PCI 的电气总线特性并与 PCI 软件兼容。很多有关 PCI 总线操作的 API 函数同样可以应用到 PXI 总线上。本文以 PXI-2010 多功能数据采集卡驱动开发为例介绍在 QNX 如何开发 PXI 外围设备的驱动。 PXI-2010 是台湾凌华公司新研制出来的一块四通道同步 A/D 、两通道同步 D/A 高速采集卡。为了充分利用原有 Linux 下驱动程序原代码。 PXI 卡的驱动程序采用三级分离的结构。 整个驱动程序分为三级:( 1 )应用程序级,( 2 )系统调用级,( 3 )硬件驱动级。 4.1 应用程序级 根据各种卡具有的功能提供给用户的开发函数,它是面向用户的。主要实现多通道同步 A/D 和 D/A 、多卡同步以及自动修正等功能。由于 QNX 和 Linux 一样把设备看作是“设备文件”,两者的应用程序级驱动函数基本一致,都是利用系统调用函数( open 函数、 close 函数、 devctl 函数等)来实现采集卡具有的各种功能。例如,多卡同步函数: I16 D2K_SSI_SourceConn (U16 wCardNumber, U16 sigCode) { DAS_IOT_SSI iotSSI ; memset( &iotSSI, '/0', sizeof(DAS_IOT_SSI) ) ; iotSSI.wSigCode = sigCode ; iotSSI.wDir = 1 ; if (devctl (CurrentCard.hDevDriver, DAS_IOC_SSI_CTL, &iotSSI , sizeof(iotSSI) ,NULL) < 0 ) return (iotSSI.wErrorCode) ; return NoError ; } 从代码中可见,这一级别的驱动是在应用程序中实现的。用户调用 devctl 函数给资源管理器发送一个 devctl 消息,消息类型为 DAS_IOC_SSI_CTL ,资源管理器从 resmgr_io_funcs_t 类型表中找到 devctl 对应的响应函数 io_devct (),并传送给它消息类型和数据,调用相应的设备控制函数将采集卡连接到 PXI 触发总线上实现多卡同步功能。 4.2 系统调用级 系统调用级驱动也就是 QNX 资源管理器,是面向 “ 设备文件 ” 的标准系统函数。它是应用程序级驱动函数的开发基础,也是 QNX 内核和应用程序之间的接口。由于 QNX 与 Linux 的驱动原理有所不同,在开发 QNX 驱动时,这一级别的驱动是开发的主要部分。 4.2.1 PXI 资源管理器的体系结构 资源管理器的体系结构主要包括以下三部分: (1) 服务请求通道 (2) 名字空间(路径名) (3) 接收和处理消息队列 PXI 资源管理器响应请求流程如下(如图 2 ): ( 1 )应用级驱动向进程管理器请求对 /dev/pxi2010 操作。进程管理器根据路径名将请求对 /dev/pxi2010 操作的进程移交给 PXI 资源管理器处理。 ( 2 ) PXI 资源管理器根据应用级驱动发送的打开设备的消息调用 io_open ()函数打开采集卡。 (3) 打开采集卡后,应用级驱动就可以根据需要调用 devctl ()函数给系统调用级驱动(资源管理器)发送控制设备消息,资源管理器接收到消息后调用 io_devctl ()函数来控制设备。 ( 4 )完成采集后应用级驱动调用 close ()函数来发送关闭设备消息,接收到消息后资源管理器同样调用 io_close ()函数关闭设备,并释放有关资源。
4.2.2 关键数据结构 ( 1 )设备描述数据结构
由于系统调用级驱动面向的是符合 POSIX 标准的“设备文件”,所使用的函数也是标准的 POSIX 函数。在 QNX 资源管理器中为了描述设备,定义了三个重要的数据结构(如图 3 ) , 其中上下文信息结构和加载结构定义如下: typedef struct _iofunc_ ocb { typedef struct _iofunc_mount{ IOFUNC_ATTR_T *attr; uint32_t flags; int32_t ioflag uint32_t conf; off_t offset; dev_t dev; uint16_t sflag; int32_t blocksize; uint16_t flags; iofuncs_funcs_t *funcs; } iofunc_ocb_t; }iofunc_mount_t; 上下文信息结构主要是指打开一个设备、文件的当前位置之类的信息;属性结构指的是具体的某个设备,例如: /dev/ser1, /dev/ser2 等;加载结构指的是对于块设备文件系统而言的全局加载信息。 ( 2 )消息传送数据结构 资源管理器中的 io_open(ctp, msg, handle, extra) 函数和 io_devctl(ctp, msg, ocb) 函数中的参数是用于接收应用级驱动发送来的消息数据和返回结果。其中 ctp 主要含有用户的信息; msg 是一个消息结构,在 io_open 中是 io_open_t 类型结构,在 io_devctl 中是 io_devctl_t 类型结构;而 ocb 是在 open 时赋于的上下文信息结构; handle 是在 resmgr_attach() 时传进去的 handle ,通常指向 iofunc_attr_t ; extra 是为别类 open 操作准备的,通常 open 时,不会用到。 4.2.3 PXI 资源管理器的功能模块 ( 1 )初始化设备模块 在 QNX 系统下,完成对一个 PXI 设备的初始化,需要完成以下工作: 1 )检查 PXI 总线设备是否存在。根据厂商标识码和设备标识码,利用 pci_find_device() 函数查找设备。 2 )如果存在的话,读出配置头中的信息提供给驱动程序使用。 3 )分配设备基地址、中断号等资源。读出 I/O 基地址后利用 mmap ()函数进行存储映射资源,将创建的共享内存对象中的一段内存映射到 PXI 卡中,以便进入这个 PXI 卡中。侦测出设备中断号后,将中断处理程序通过调用 qnx_hint_attach() 函数绑定到设备上。 4 )将连接消息响应函数表和 I/O 消息响应函数初始化,再向进程管理器注册路径名 /dev/pxi2010 。 PXI 与 PCI 在软件上是兼容的,因此 PXI 所有软件操作都是以 PCI 为基础的。 QNX 下的资源管理器是一个用户级的普通应用程序,初始化设备的工作可以在程序启动时进行。 ( 2 )打开设备模块 在这个模块里主要实现申请对设备的控制权,初始化采集卡的寄存器,设置采集卡的默认参数(采集信号范围、触发方式和触发电平、装入自动修正量等)。 ( 3 )数据读写和控制信息模块 PXI 设备驱动程序可以通过 resmgr_io_funcs_t 结构中的函数 io_devctl (),向应用程序提供对硬件进行控制的接口。不同的操作通过消息类型来区别。不同的消息调用不同的硬件控制函数。例如上面所提到的多卡同步函数的消息类型就是 DAS_IOC_SSI_CTL ,其调用的硬件控制函数是 DAQ2K_Set_SSI(PPCI_DEVEXT pDevExt, DAS_IOT_SSI* pSbuf) 。模块框架如下: int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb) {
int nbytes, status, previous;
union {
data_t
data;
int
data32; } *rx_data;
if ((status = iofunc_devctl_default(ctp, msg, ocb)) != _RESMGR_DEFAULT) {return(status);}
status = nbytes = 0;
rx_data = _DEVCTL_DATA(msg->i);
switch (msg->i.dcmd) {
case DAS_IOC_GET_DESP : // 读取采集卡的有关信息 … /* 调用信息读取硬件控制函数 */
break;
case DAS_IOC_SSI_CTL: // 将采集卡连接到触发总线上
…
break;
case DAS_IOC_AIO_CONFIG : // 进行模拟量输入输出设置 ……
case DAS_IOC_SIMU_AI_PIO : // 进行多通道模拟量同步输入 … /* 调用同步输入硬件控制函数 */ default :
return DAS_ERR_NO_FUNCTION; };
memset(&msg->o, 0, sizeof(msg->o));
msg->o.ret_val = status;
msg->o.nbytes = nbytes;
return(_RESMGR_PTR(ctp, &msg->o, sizeof(msg->o) + nbytes)); }
( 4 )中断处理模块 PC 的中断资源比较有限,只有 0~15 的中断号,因此大部分外部设备都是以共享的形式申请中断号的。当中断发生的时候,中断处理程序首先负责对中断进行识别,然后再做进一步的处理。 PXI 资源管理器在初始化模块中侦测采集卡的中断号,并将中断处理程序进行绑定。 ( 5 )释放设备模块 释放设备模块主要负责释放对设备的控制权,释放占用的内存和中断等,所做的事情正好与打开设备模块相反。 4.3 硬件驱动级 硬件驱动级函数是实际操作物理硬件的函数,主要是面向厂商板卡开发人员。在各种操作系统下硬件驱动级函数相差无几。因此可以借助厂商提供的 Linux 或 Windows 下原代码进行少量的修改就行。 5. 小结 由于 QNX 是微内核,其设备驱动程序无需内核调试,适合普通开发人员开发。本文采用了三级分离驱动的方式开发 PXI 多功能数据采集卡的驱动程序,既可以充分发挥 PXI 采集卡的功能,又可以有效地利用厂商提供的 Linux 系统下的设备驱动函数的原代码,减少驱动程序开发的工作量。
参考文献 : 1 . QNX Neutrino Realtime Operating System System Architecture , QNX Soft System Ltd. 2002 2 . QNX Neutrino Realtime Operating System Programmer’s Guide, QNX Soft System Ltd. 2002 3 . QNX 实时操作系统使用手册 北京领先实时科技有限责任公司 2001 4 . QNX Neutrino 实时编程入门 北京领先实时科技有限责任公司 2001 5 . QNX 驱动程序编程入门 北京领先实时科技有限责任公司 2001 6 . PCI eXtensions for Instrumentation Specification Revision 2.0 PXI Systems Alliance 2000 7 . PXI-2010 4-CH.Simultaneous.High performance Multi-function Data Acquisition Card User’s Guide, ADLINK Technology Inc. 2002 8 . D2K-DASK Data Acquisition Software Development Kit User’s Guide, ADLINK Technology Inc. 2002 作者简介: 黄峰 1977 年生,中科院等离子体所在读博士研究生,研究方向: 3.7GHz 低杂波监控系统。 |