腹黑溺宠 老婆不准躲:多种开发工具开发的9054驱动源程序比较(上)转

来源:百度文库 编辑:中财网 时间:2024/04/29 03:01:37

多种开发工具开发的9054驱动源程序比较(上)转

(2008-09-30 22:02:01) 转载标签:

pci9054

driver

杂谈

分类: 驱动开发,嵌入式开发

9054驱动程序设计
提纲:
1. 驱动程序设计概述。
a) 驱动程序设计概述。
b) 9054的编程基础。
2. 使用DRIVERSTUDIO设计9054驱动程序。
3. 使用DDK设计9054驱动程序。
4. 使用DRIVERWORKS设计。

在WINDOWS2000中,驱动程序分为三种:总线驱动程序(bus driver),功能驱动程序(function driver),以及过滤驱动程序(filter driver)。总线驱动,是指PCI总线的PDO驱动,由WINDOWS提供,不需要我们去设计。在工作过程中,总线驱动将扫描PCI插槽,并枚举它们,负责管理PCI总线的物理信号。我们所指的驱动程序设计,实质上是功能驱动程序(function driver)和过滤驱动程序(filter driver)。前者负责PCI设备的逻辑功能实现,后者是对驱动程序的某些功能进行扩展或复合,不在本篇中讨论。
9054驱动程序设计讨论的就是功能驱动程序,实际上,我们能得到的编程工具都是指向这个层次的(当然还可以编写过滤程序)。在这些工具中DDK是Microsoft提供的,是整个驱动程序开发的基础,DriverStudio就是建立在此基础之上的,因没用过DriverWork所以不知DW能否独立地编写驱动。有人说,DDK是驱程的汇编,而DS是驱程的C语音,那么DW,我理解就是驱程的VB了。比较过DDK和DS和DW,DW确实是最简单的,甚至它已经为PLX9054写了模板,还提供了实例,因为它是PLX的合作伙伴。但在DW里基本看不见DDK的影子。和DW比较,DS就复杂一些了,它的许多功能歧视是DDK例程的打包,它通过类的方法将DDK的功能包装在一起。要了解驱动程序的设计,最后还是要了解DDK的。
在学习驱动程序设计的初期时,我看的是《Windows2000 设备驱动程序设计指南》,它是一本DDK入门的好书。但在一开始,我竟不能读懂,可能是因为外国人的写书习惯和我们的不同,因为不懂,所以认为是本臭书。后来,我看了武安河的《WDM设备驱动程序开发》,这回看懂了,并跟着做了几个程序,于是对武佩服得五体投地,好书!但DS仍是将驱动包了起来,而且书中仍有许多的疑点,于是回头重看了《Windows2000 设备驱动程序设计指南》,此时,发现这本书写得极好,它将我的疑问基本都解决了,无怪乎,在网上许多网友极力推荐,好书和臭书的截然相反的评价,正是由于自己的认识水平所决定的。其后,又看了一本,也可称为WDM程序设计奠基之作的《Programming the Microsoft Windows Driver Model》,虽还没看完,但有了前两本的基础,已基本可以进行编程了。在《WDM设备驱动程序开发》中提供了PLX9054的样例,在《Programming the Microsoft Windows Driver Model》提供了S5933的样例。前者是用DS写的,而后者使用DDK。接下来,我将分析PLX9054地DS的驱动程序,并将S5933的DDK驱动,改成PLX9054,最后,附上DW的PLX9054驱动。

PLX9054作为PCI控制芯片,它对驱动程序设计影响的有以下几方面:
1. 双DMA通道,并支持S/C DMA方式,猝发方式。
2. 有三个地址空间:LocalBus地址空间,EEPROM地址空间,IO寄存器空间。
3. 通过对IO寄存器操作完成各项任务。
4. 可以对LocalBus进行寻址。
(陆续有来)

以下是武安河的9054驱动和DS提供的PCIWDM所进行的比较。
一.的比较
DS:
#ifndef _FUNCTION__
#define _FUNCTION__

#define DRIVER_FUNCTION_ADD_DEVICE
#define DRIVER_FUNCTION_PNP
#define DRIVER_FUNCTION_POWER
#define DRIVER_FUNCTION_SYSTEM_CONTROL
#define DRIVER_FUNCTION_UNLOAD
#define DRIVER_FUNCTION_CLOSE
#define DRIVER_FUNCTION_CREATE
#define DRIVER_FUNCTION_DEVICE_CONTROL

#endif
文件到此结束
武:
#ifndef __function_h__
#define __function_h__

#define DRIVER_FUNCTION_CREATE
#define DRIVER_FUNCTION_CLOSE
#define DRIVER_FUNCTION_READ
#define DRIVER_FUNCTION_STARTIO
#define DRIVER_FUNCTION_WRITE

#define DRIVER_FUNCTION_ADD_DEVICE
#define DRIVER_FUNCTION_PNP
#define DRIVER_FUNCTION_POWER
#define DRIVER_FUNCTION_SYSTEM_CONTROL
#define DRIVER_FUNCTION_UNLOAD

#define DRIVER_FUNCTION_CLEANUP

#endif

function.h是程序员将要重载的虚函数的说明。DRIVER_FUNCTION_XXXX对应IRP_MJ_XXXX。比如:DRIVER_FUNCTION_READ 对应着IRP_MJ_READ。
此处,武比DS多了:
#define DRIVER_FUNCTION_STARTIO //对应StartIO
#define DRIVER_FUNCTION_WRITE //对应Write
#define DRIVER_FUNCTION_CLEANUP //对应CleanUp
少了
#define DRIVER_FUNCTION_DEVICE_CONTROL //对应DeviceControl
其中,StartIO,Write,CleanUp,DeviceControl对应的是Kdevice的纯虚成员virtual member function。

二.在KDriver这一级别上DS与《武》的一样。
《武》:
#ifndef __PCI9054_h__
#define __PCI9054_h__

#define EOL "\n"

class PCI9054 : public KDriver
{
SAFE_DESTRUCTORS
public:
virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath);
virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo);


int m_Unit;

};

#endif // __PCI9054_h__


《DS》:
class PciwdmDriver : public KDriver
{
SAFE_DESTRUCTORS
public:
virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath);
virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo);

ULONG m_instance;
};

其中,m_instance和m_Unit是用来计数的。

三.在KDevice级的共性
《DS》:
class PciwdmDevice : public KPnpDevice
{
public:
PciwdmDevice(PDEVICE_OBJECT PDO, ULONG instance);

DEVMEMBER_DISPATCHERS // Macro to automatically declare the dispatcher member // functions of a class derived from KDevice.


virtual NTSTATUS DefaultPnp(KIrp I);
virtual NTSTATUS DefaultPower(KIrp I);
virtual NTSTATUS OnStartDevice(KIrp I);
virtual NTSTATUS OnStopDevice(KIrp I);

MEMBER_ISR (PciwdmDevice, TheIsr)
MEMBER_DPC (PciwdmDevice, TheDpc)

#ifdef __COMMENT_ONLY
// The following member functions are actually defined by the
// a DEVMEMBER_XXX macro (such as DEVMEMBER_DISPATCHERS).
// The macro __COMMENT_ONLY never gets defined. These comment-only
// definitions simply allow easy navigation to the functions within
// the Developer Studio using the class browser.

BOOLEAN TheIsr(void); // COMMENT_ONLY
VOID TheDpc(PVOID Arg1, PVOID Arg2); // COMMENT_ONLY
virtual NTSTATUS Create(KIrp I); // COMMENT_ONLY
virtual NTSTATUS Close(KIrp I); // COMMENT_ONLY
virtual NTSTATUS DeviceControl(KIrp I); // COMMENT_ONLY
virtual NTSTATUS SystemControl(KIrp I); // COMMENT_ONLY
#endif

// The device has one interrupt and one memory range (Base address register)

KPnpLowerDevice m_Pdo; // The Physical Device Object
KInterrupt m_Interrupt; // The device's interrupt
KDeferredCall m_Dpc; // DPC for interrupt
KMemoryRange m_MemRange; // Memory mapped region on device
KPciConfiguration m_Pci; // Config space interface object
};

此处有一个中断,一个DPC,KmemoryRange和一个KPciConfiguration,在《武》中没有个KPciConfiguration,而是多了 KioRange和KdmaAdapter。其中KPciConfiguration是用于获得PCI设置的类,KMemoryRange是PCI局部总线(LOCAL BUS)在PCI总线上的映射,KioRange是用于9054上寄存器的操作,KdmaAdapter是用于DMA操作的Adpter对象。
接下来,看《武》的PCI9054Device.h:
#ifndef __PCI9054Device_h__
#define __PCI9054Device_h__


class PCI9054Device : public KPnpDevice
{
// Constructors
public:
SAFE_DESTRUCTORS;
PCI9054Device(PDEVICE_OBJECT Pdo, ULONG Unit);
~PCI9054Device();

// Member Functions
public:
DEVMEMBER_DISPATCHERS
DEVMEMBER_DMAREADY(PCI9054Device, OnDmaReady) //用于DMA传输
DEVMEMBER_CANCELIRP(PCI9054Device, CancelQueuedIrp) //用于IRP队列操作
// The ISR (interrupt service routine)
MEMBER_ISR(PCI9054Device, Isr_Irq); //ISR例程
// The DPC (deferred procedure call) for the ISR
MEMBER_DPC(PCI9054Device, DpcFor_Irq); //DPC例程
virtual NTSTATUS OnStartDevice(KIrp I);
virtual NTSTATUS OnStopDevice(KIrp I);
virtual NTSTATUS OnRemoveDevice(KIrp I);
VOID Invalidate(void);
virtual NTSTATUS DefaultPnp(KIrp I);
virtual NTSTATUS DefaultPower(KIrp I);
void SerialRead(KIrp I);
void SerialWrite(KIrp I);
VOID StartDMA(ULONG PAddress,ULONG NBytes);

#ifdef _COMMENT_ONLY

VOID OnDmaReady(KDmaTransfer* pXfer, KIrp I); // COMMENT_ONLY
BOOLEAN Isr_Irq(void); // COMMENT_ONLY
VOID DpcFor_Irq(PVOID Arg1, PVOID Arg2); // COMMENT_ONLY
VOID CancelQueuedIrp(KIrp I); // COMMENT_ONLY
virtual NTSTATUS CleanUp(KIrp I); // COMMENT_ONLY
virtual NTSTATUS Create(KIrp I); // COMMENT_ONLY
virtual NTSTATUS Close(KIrp I); // COMMENT_ONLY
virtual NTSTATUS SystemControl(KIrp I); // COMMENT_ONLY
virtual NTSTATUS Read(KIrp I); // COMMENT_ONLY
virtual VOID StartIo(KIrp I); // COMMENT_ONLY
virtual NTSTATUS Write(KIrp I); // COMMENT_ONLY
#endif

// Member Data
protected:
// Unit number for this device (0-9)
ULONG m_Unit;
KPnpLowerDevice m_Lower;
// The following members correspond to hardware resources in the
// device.
KMemoryRange m_MemoryRange0;
KIoRange m_IoPortRange0;
KIoRange m_IoPortRange1;
KDmaAdapter m_Dma;
KDmaTransfer* m_CurrentTransfer;
KCommonDmaBuffer m_Buffer;
KInterrupt m_Irq;
KDeferredCall m_DpcFor_Irq;
};

#endif

四.在DeviceObject级别上,由于《DS》是一个通用的模板,而《武》的是一个专用驱动,所以有很多区别,但也仍有相似的地方,以下就列举其中相似的地方:
1.构造函数
《DS》:
PciwdmDevice::PciwdmDevice(PDEVICE_OBJECT PDO, ULONG instance) :
KPnpDevice(
PDO,
KUnitizedName(L"Pciwdm", instance),
FILE_DEVICE_UNKNOWN,
KUnitizedName(L"Pciwdm", instance),
0,
DO_DIRECT_IO
),

m_Pci(NULL)
{
// Initialize the connection to the Physical Device Object
// This enables us to pass IRPs down to the PDO for default
// processing.

if ( NT_SUCCESS(m_ConstructorStatus) )
m_ConstructorStatus = m_Pdo.Initialize(this, PDO);

if ( ! NT_SUCCESS(m_ConstructorStatus) )
return;

// Initialize the KPciConfiguration object. Although the exapmle
// includes this object, it is not strictly necessary. You can use
// to read and write the configuration space, but in a PnP environment,
// this is rarely required.

m_Pci.Initialize(m_Pdo.DeviceObject());

// Inform the base class of our lower edge object

SetLowerDevice(&m_Pdo);

// Set the standard policy for PnP handling

SetPnpPolicy();
}

《武》:
PCI9054Device::PCI9054Device(PDEVICE_OBJECT Pdo, ULONG Unit) :
KPnpDevice(Pdo, &PCI9054Device_Guid)
{

// Check constructor status
if ( ! NT_SUCCESS(m_ConstructorStatus) )
{
return;
}

// Remember our unit number
m_Unit = Unit;

// Initialize the lower device
m_Lower.Initialize(this, Pdo);

// Inform the base class of the lower edge device object
SetLowerDevice(&m_Lower);

// Initialize the PnP Policy settings to the "standard" policy
SetPnpPolicy();

}

2.处理的例程(CREAT, CLOSE)
《DS》:
///////////////////////////////////////////////////////////////////
// Create handler
//
NTSTATUS PciwdmDevice::Create(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Pdo.PnpCall(this, I);
}

///////////////////////////////////////////////////////////////////
// Close handler
//
NTSTATUS PciwdmDevice::Close(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Pdo.PnpCall(this, I);
}

《武》:
NTSTATUS PCI9054Device::Create(KIrp I)
{
NTSTATUS status;

status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);

return status;
}

NTSTATUS PCI9054Device::Close(KIrp I)
{
NTSTATUS status;

status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);

return status;
}

3.处理例程(DefaultPnp、DefaultPower)
《DS》:
///////////////////////////////////////////////////////////////////
// DefaultPnp
//
NTSTATUS PciwdmDevice::DefaultPnp(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Pdo.PnpCall(this, I);
}

///////////////////////////////////////////////////////////////////
// DefaultPower
//
NTSTATUS PciwdmDevice::DefaultPower(KIrp I)
{
I.IndicatePowerIrpProcessed();
I.CopyParametersDown();
return m_Pdo.PnpPowerCall(this, I);
}

《武》:
NTSTATUS PCI9054Device::DefaultPnp(KIrp I)
{
I.ForceReuseOfCurrentStackLocationInCalldown();
return m_Lower.PnpCall(this, I);
}

NTSTATUS PCI9054Device::DefaultPower(KIrp I)
{
I.IndicatePowerIrpProcessed();
I.CopyParametersDown();
return m_Lower.PnpPowerCall(this, I);
}
DefaultPnp、DefaultPower是不处理的IRP中MinorFunction的处理例程。

五。《武》的DMA分析
由于《武》的源代码较多,在网上也容易下载,就不在此粘贴了。只是补充一些自己在读源程序时,的一些心得。
《武》的程序执行脉络:
(1)插入PCI卡,总线驱动扫描到新硬件,硬件安装向导提示安装,导入INF文件。由I/O管理器,打开sys文件,执行DriverEntry例程,然后执行AddDevice例程。
(2)重启系统。系统在启动时,发出IRP_MJ_START。驱动捕获此IRP,并执行OnStartDevice()例程,完成系统初始化。包括系统的资源列表,KDmaAdapter m_Dma,KcommonDmaBuffer m_Buffer,中断和DPC。
(3)当执行用户程序的“读”或“写”时,用户程序向驱动发出IRP_MJ_READ或IRP_MJ_WRITE。于是执行Read()(或Write())例程。由于使用了IRP串行技术,在例程最后调用QueueIrp()。就将执行IRP的操作交给了StartIo例程来完成。
(4) 在StartIo中,调用SerialRead()例程(或SerialWrite()例程)。
(5) 在SerialRead()例程中,创建DMA传输控制类KdmaTransfer *m_CurrentTransfer的实例。
(6) m_CurrentTransfer->Initiate()例程,调用回调函数OnDmaReady。OnDmaReady检测DMA有无结束,结束则使用Terminate(),如果没有结束,则调用StartDMA。
(7) StartDMA通过写9054寄存器,完成DMA操作。在完成一次后,会产生一个中断。
(8) ISR服务例程会捕获此中断要求。在清除了中断标志后,调用DPC服务例程。
(9) DPC例程中,调用m_CurrentTransfer->Continue()例程。则程序回到(6)步。

或许用《DS》的原文更能说明DMA的执行过程:
The sequence of events for a DMA transfer goes like this:
(1) Driver constructs an instance of KDmaTransfer. This could be a static instance in the global scope, an instance allocated from the heap, or an instance that is a component of a device object.
(2) When the operation is requested, driver calls Initiate(). There are two forms. If the default constructor (no arguments) was used to create the object, the driver must call the extended form of Initiate().
(3) Initiate() allocates the adapter, sets up the initial segment of the transfer, and invokes the driver's callback.
(4) The callback informs the device that the current segment is ready, and the transfer of the current segment begins.
(5) The driver detects (usually by interrupt) that the current segment has completed, and calls member function Continue().
(6) Continue() completes the transfer of the current segment and sets up the next segment (if necessary), and again invokes the driver's callback.
(7) The callback checks if there are bytes left to transfer. If so, the process continues as in the third step. Otherwise, calls member function Terminate(), and deletes the instance of KDmaTransfer.