摘要:驱动程序是PCI设备与主机通信的关键。本文介绍了Windows2000下WDM驱动程序的基本原理,根据WDM的多种数据传输机制,给出了使用DDK实现各种机制的TMS320DM642驱动程序的方法。最后以实验数据说明各种传输机制实现TMS320DM642驱动程序的特点。
关键字:WDM,数据传输机制,驱动程序,DDK,TMS320DM642,PCI
1. 引言
PCI(Peripheral Component Interconnect)总线具有的高数据传输率、支持即插即用、低传输延时等特点使其成为与主机接口的首选标准[1]。开发的PCI设备要在Windows下正常工作,必须有满足要求的高效率的驱动程序,用户模式的应用程序才能通过驱动程序提供的接口来控制通信过程。
驱动程序是系统的一个瓶颈,好的驱动程序才能充分发挥硬件的良好特性[2]。TI(Texas Instruments)公司在其最高性能的TMS320C64x系列基础上专门为多媒体应用而设计的TMS320DM642支持PCI接口,为了传输更高分辨率的图像,必须充分利用其特点开发高效的驱动程序。
2. WDM 驱动模型分析
WDM(Windows Driver Model)设备驱动程序是微软公司为当前主流操作系统的驱动程序设计的一种架构,它规定了标准化的驱动模块。其主要目标是实现能够跨平台使用、更安全、更灵活、编制更简单的Windows设备驱动程序。
WDM驱动程序是分层的,不同层上的驱动程序有着不同的优先级。WDM引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device 0bject)两个类来描述硬件,一个PDO对应一个真实硬件。一个硬件只允许有一个PDO,却可以拥有多个FDO,在驱动程序中直接操作的不是硬件而是相应的PDO和FDO。在WDM模型中,每个硬件设备至少有两个驱动程序,其中一个就是所编写的功能驱动程序。另一个称之为总线驱动程序,它由系统提供,负责管理计算机与硬件连接。在对硬件访问的过程中,设备驱动程序只需与下层的总线驱动程序打交道,具体控制硬件的琐碎工作交给总线驱动程序去完成。在有的驱动程序中还有位于设备驱动程序和总线驱动程序之间的过滤器驱动程序,用于修改和监视IRP (I/O Request Pack)。在PCI设备的WDM驱动程序中,一般是编写功能驱动程序,PCI总线驱动程序由操作系统实现。
驱动程序的主要工作如下:初始化;创建和删除设备;即插即用处理;分发例程处理;处理Win32打开和关闭文件句柄的请求;处理Win32输入输出请求;访问硬件;调用其它驱动程序;取消I/O请求;超时I/O请求;处理电源管理[3]。
驱动程序开发需要专门的开发工具,开发WDM驱动程序的主要工具有微软提供的软件包(DDK, Driver Development Kit)和NuMega公司的DriverStudio。采用DDK开发驱动程序在深入了解操作系统的内核工作方式的基础上,则可实现代码简洁、结构清晰、高效的设备驱动程序。基于以上因素,本题选用了DDK和Visual C++的实现方式。
3. WDM数据传输机制原理
要实现高效的设备驱动,必须要根据具体硬件设备选择适当的数据传输机制。数据传输机制的选择是由设备的速度,平均数据量的大小等决定,一个设备可以选择多于一个的数据传输机制[4]。在CPU和存储器之间移动数据,使用以下三种机制:
3.1 过程控制I/O
Win32用户程序通过驱动程序发出一个I/O指令直接去读或写PCI设备数据。如果传输大量的数据,将会占用设备的CPU资源。
3.2 DMA(Direct Memory Access)直接存储器访问
直接存储器访问利用PCI设备的DMA控制器。DMA控制器是一个辅助处理器,它被用来在PCI设备和主机之间传送数据。为了开始一个I/O操作,驱动程序必须根据应用程序设置DMA控制器的开始缓冲区地址和传输个数等,DMA控制器即可完成数据传输,只需占用很少的设备CPU资源。当DMA控制器完成传输,就会产生中断,等待处理。但大多数的PCI设备在DMA操作时不能跨页访问主机内存,这制约了PCI设备的DMA的使用。
高速设备利用DMA传输大量数据,比过程控制I/O减少了数据传输的中断,占用较少的设备CPU资源。
3.3 共享缓冲区
驱动程序开辟一块公用缓冲区,将此缓冲区的起始地址映射到PCI设备和应用层,主机应用程序和PCI设备都可以根据起始地址访问。高速设备在传输大量数据时,根据需要可以在完成多个DMA后向主机申请中断。比直接存储器访问减少了数据传输的中断,占用更少的设备CPU资源。
4. 三种数据传输机制的实现
WDM驱动程序通过AddDevice例程创建设备,该例程首先选择并形成一个内部设备名,然后调用IoCreateDevice创建设备对象和设备扩展,接着注册一个或多个设备接口,以使应用程序能知道设备的存在,并给出设备名和创建符号连接,最后把新设备对象放到堆栈上,初始化设备对象的Flag成员,建立设备对象。大多数最低层和所有中间层驱动程序在DeviceObject->Flags中设置一个位,方法是使用DO_BUFFERED_IO或者DO_DIRECT_IO与他们创建的每个设备对象中的Flags做或操作。AddDevice例程部分关键语句如下所示:
NTSTATUS Dm642AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo)
{ …
status = IoCreateDevice( DriverObject, sizeof(DM642_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); //创造 fdo
//初始化设备
PDM642_DEVICE_EXTENSION dx = (PDM642_DEVICE_EXTENSION)fdo->DeviceExtension;
dx->fdo = fdo;
dx->pdo = pdo;
KeInitializeEvent( &dx->StoppingEvent, NotificationEvent, FALSE);
…
POWER_STATE NewState; //初始化电源状态
NewState.DeviceState = dx->PowerState;
PoSetPowerState( fdo, DevicePowerState, NewState);
//注册使能设备接口
status = IoRegisterDeviceInterface( pdo, &DM642_GUID, NULL, &dx->ifSymLinkName);
//设置 FDO 标志,其中DO_ BUFFERED _IO为过程控制I/O传输机制设置
//DO_DIRECT_IO为直接存储器访问和共享缓冲区传输机制设置
fdo->Flags |= DO_DIRECT_IO;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
…
return STATUS_SUCCESS;
}
4.1 过程控制I/O机制实现
在AddDevice例程中设置fdo->Flags |=DO_BUFFERED_IO。Win32用户程序发出ReadFile等设备操作请求时,I/O管理程序为各个请求创建功能代码如:IRP_MJ_READ,并提供相应的驱动程序例程Dm642Read,通过RtlCopyMemory函数实现数据传输。部分例程语句如下:
NTSTATUS Dm642Read (IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{ PDM642_DEVICE_EXTENSION dx = (PDM642_DEVICE_EXTENSION)fdo->DeviceExtension;
if( dx->IODisabled)return CompleteIrp( Irp, STATUS_DEVICE_NOT_CONNECTED, 0);
if (!LockDevice(dx))return CompleteIrp( Irp, STATUS_DELETE_PENDING, 0);
NTSTATUS status = PowerUpDevice(fdo);
if( !NT_SUCCESS(status))return CompleteIrp(Irp, status, 0);
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
LONG BytesTxd = 0;
LONGLONG FilePointer = IrpStack->Parameters.Read.ByteOffset.QuadPart;
ULONG ReadLen = IrpStack->Parameters.Read.Length;
KeAcquireSpinLock(&BufferLock,&irql);
if( status==STATUS_SUCCESS)
{ UCHAR mytest[N];
memset(mytest,0,sizeof(mytest));
RtlCopyMemory(mytest,dx->MemBase[0]+FilePointer,ReadLen);
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,dx->MemBase[0]+FilePointer,ReadLen);
}
KeReleaseSpinLock(&BufferLock,irql);
CompleteIrp(Irp,status,ReadLen);
UnlockDevice(dx);
return status;
}
由于此数据机制传输的过程都是在主机控制之下完成,所以耗费了主机CPU时间。同时TMS320DM642也因为不定时的响应主机任务而占用了本身资源。
4.2 DMA直接存储器访问机制实现
在AddDevice例程中设置fdo->Flags |=DO_DIRECT_IO。Win32用户程序发出ReadFile等设备操作请求时,I/O管理程序为各个请求创建功能代码如:IRP_MJ_READ。Dm642Read例程依次启动Dm642StartIo例程、AdapterControl例程、StartTransfer例程。启动数据传输时,I/O管理程序调用Dm642StartIo例程,在Dm642StartIo例程中计算映射寄存器个数、拆分传输请求,AdapterControl例程调用系统提供的适配器对象支持例程映射逻辑地址以执行DMA传输。在StartTransfer例程中根据TMS320DM642内部PCI模块要求,控制其EDMA(Enhanced Direct Memory Access)寄存器,启动数据传输。StartTransfer例程关键语句如下:
VOID StartTransfer (PDM642_DEVICE_EXTENSION dx, PHYSICAL_ADDRESS address, BOOLEAN isread)
{ …
if (isread)
{ …
//配置TMS320DM642寄存器启动EDMA传输数据
RtlCopyMemory((dx->MemBase[0]+0x00010),&udspma,sizeof(ULONG)); //DSPMA寄存器
RtlCopyMemory((dx->MemBase[0]+0x00014),&address.LowPart,sizeof(ULONG));//PCIMA寄存器
RtlCopyMemory((dx->MemBase[0]+0x00018),&upcimc,sizeof(ULONG));//PCIMC寄存器
}
…
}
采用DMA直接存储器访问机制,主机只需要在每个EDMA传输完成后配置下一个传输,这样比过程控制I/O机制节约了主机CPU时间。但主机必须接收EDMA传输完成的中断后,才能确定上一个传输完成;且由于采用此机制传输时TMS320DM642的EDMA不能跨页访问主机内存,使得每次传输不能超过4K的限制,这也制约了传输的速度。
4.3 共享缓冲区机制实现
驱动程序从处理IRP_MN_START_DEVICE请求的DispatchPnP例程中调用AllocateCommonBuffer以得到这个缓冲区,并且映射它的虚拟地址范围到系统物理内存。
为了经济地使用内存,输入缓冲区长度值要么小于等于PAGE_SIZE,要么应该是PAGE_SIZE 的倍数。如果AllocateCommonBuffer 返回NULL指针,驱动程序应该释放任何它已经请求的系统资源,并且返回STATUS_INSUFFICIENT_RESOURCE以响应IRP_MN_START_DEVICE请求。否则,AllocateCommonBuffer在系统虚拟地址空间中分配请求数量的内存,并返回指向那个缓冲区的两种不同类型的指针:其中vaCommonBuffer是为驱动程序访问此缓冲区返回的虚拟地址,paCommonBuffer是为PCI设备访问此缓冲区返回的逻辑地址。AdapterControl例程调用系统提供的适配器对象支持例程映射逻辑地址以支持共享缓冲区传输机制。StartDevice例程、AdapterControl例程关键语句如下:
NTSTATUS StartDevice ( IN PWDM2_DEVICE_EXTENSION dx, IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
{…
dx->vaCommonBuffer = (*dx->AdapterObject->DmaOperations->AllocateCommonBuffer)
(dx->AdapterObject,0x1000000, &dx->paCommonBuffer, FALSE);
…
}
IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo, PIRP junk, PVOID regbase, PWDM2_DEVICE_EXTENSION dx)
{ …
PHYSICAL_ADDRESS address = (*dx->AdapterObject->DmaOperations->MapTransfer)
(dx->AdapterObject, mdl, regbase, dx->paCommonBuffer, &dx->xfer, !isread);
addresstest = address.LowPart;
//将映射的逻辑地址写入TMS320DM642,供TMS320DM642启动EDMA使用
RtlCopyMemory(dx->MemBase[0]+0xB000,&addresstest,sizeof(ULONG));
…
}
TMS320DM642在BIOS基础上使用驱动程序映射的逻辑地址配置EDMA寄存器完成传输,直接将数据写入主机内存,主机应用程序通过驱动程序返回的虚拟地址访问该存储区。这样即可实现主机与TMS320DM642数据传输。由于TMS320DM642的EDMA在此机制下使用映射的逻辑地址等同于在其芯片内部传输数据,每次传输都可以使用最大传输量64K。同时TMS320DM642可以在一次EDMA完成后继续启动下一次传输,即在传输完成大块数据后,向主机申请一次中断,节省了主机CPU资源。
5 三种数据传输机制结果比较
通过对PCI设备TMS320DM642读、写大量数据,结果说明:使用过程控制I/O传输机制传输速率达20MB/s,但占用TMS320DM642的CPU大量资源;使用直接存储器访问输机制传输速率达30MB/s;而使用共享缓冲区传输机制传输速率可达45MB/s。
6 结束语
PCI设备为外设与外设、外设与主机之间实现高速数据传输提供了一个高效的途径,且 WDM驱动模型已经成为当今驱动程序开发的主流。本文在Windows环境下分别实现了基于不同WDM数据传输机制的PCI设备TMS320DM642的驱动程序,给出了各种传输机制的实现速率。实验证明灵活运用各种传输机制,尤其是共享缓冲区机制,可充分利用硬件资源达到高速传输的目的。
参考文献
[1]. 司玉美, 邹荣士, 郭立红. PCI串口通信卡WDM驱动程序设计与实现[J]. 微计算机信息,2005,21(11-2):1
[2]. 刘勇, 杨卫兵, 陈曜等.智能网卡驱动程序的性能评价 [J].计算机工程, 2005, 31(14):1.
[3]. 贾涛, 王铁岭. PCI数据采集卡的WDM 驱动程序开发[J].国外电子测量技术,206,25(8):2
[4]. 尹勇, 李宇.PCI总线设备开发宝典 [M].北京: 北京航空航天大学出版社, 2005:99.
作者简介
臧淼:女,1977年生,硕士,北方工业大学信息学院通信工程系讲师,主要研究方向为数字系统设计和多媒体通信。
联系方式:
联系人:臧淼
通信方式:北京石景山晋元庄路5号北方工业大学信息学院 100041
联系电话:010-88803049
手机:13693390959
电子信箱:zangmiao999@sina.com