阅读:11408回复:49
多种开发工具开发的9054驱动源程序比较
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进行寻址。 (陆续有来) |
|
沙发#
发布于:2004-11-08 14:28
好,请继续!
|
|
板凳#
发布于:2004-11-08 16:06
以下是武安河的9054驱动和DS提供的PCIWDM所进行的比较。
一.<function.h>的比较 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。 |
|
地板#
发布于:2004-11-08 16:11
二.在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是用来计数的。 |
|
地下室#
发布于:2004-11-08 16:27
三.在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 |
|
5楼#
发布于:2004-11-08 16:44
四.在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的处理例程。 |
|
6楼#
发布于:2004-11-08 23:23
好东西
顶 |
|
|
7楼#
发布于:2004-11-09 01:36
五。《武》的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. |
|
8楼#
发布于:2004-11-09 13:36
那你认为哪种方法好点?windriver如何?
|
|
9楼#
发布于:2004-11-09 17:26
我还没有认真看过windriver,但从它的例程看,好像是很简单的。
|
|
10楼#
发布于:2004-11-09 17:27
用DDK开发的9054驱动
和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。我将分两个例子来构建PLX9054的驱动,第一个,是对《Windows2000 设备驱动程序设计指南》里的分段DMA例子的扩充,它的结构比较简单,对理解DDK的运作很有帮助;第二个,我将对《Programming the Microsoft Windows Driver Model》里的S5933进行改造,因为这个例子里,涉及的概念较多,但可与前面《武》用DS开发的驱动媲美。以下,我将一面分析程序,一面将程序中涉及的重要概念进行解释。 例一: // //为了编程方便,作者提供了一个CUString类。 // Unicode.h // // Copyright (C) 2000 by Jerry Lozano // // #pragma once class CUString { public: CUString() {Init(); } // constructor relies on internal Init function CUString(const char* pAnsiString); CUString(PCWSTR pWideString); ~CUString(); // destructor gives back buffer allocation void Init(); // performs "real" initialization void Free(); // performs real destruct CUString(const CUString& orig); // copy constructure (required) CUString operator=(const CUString& rop); // assignment operator overload (required) BOOLEAN operator==(const CUString& rop) const; // comparison operator overload CUString operator+(const CUString& rop) const; // concatenation operator CUString& operator+=(const CUString& rop); // and a convenient concat operator PWSTR() const; // cast operator into wchar_t operator UNICODE_STRING&(); // cast into UNICODE_STRING operator ULONG() const; // cast operator into ULONG CUString(ULONG value); // converter: ULONG->CUString WCHAR& operator[](int idx); // buffer access operator USHORT Length() {return uStr.Length/2;} protected: UNICODE_STRING uStr; // W2K kernel structure for Unicode string enum ALLOC_TYPE {Empty, FromCode, FromPaged, FromNonPaged}; ALLOC_TYPE aType; // where buffer is allocated }; //以下是Unicode.cpp #ifdef WIN32DDK_TEST #include "DDKTestEnv.h" #else extern "C" { #include <NTDDK.h> } #endif #define max(a,b) ((a>b)?a:b) #include "Unicode.h" void CUString::Init() { uStr.Length = 0; uStr.MaximumLength = 0; uStr.Buffer = NULL; aType = Empty; } CUString::CUString(const char* pAnsiString) { ANSI_STRING str; RtlInitAnsiString(&str, pAnsiString); uStr.MaximumLength = (USHORT) max(32, RtlAnsiStringToUnicodeSize(&str) ); uStr.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, uStr.MaximumLength, 1633); aType = FromPaged; RtlAnsiStringToUnicodeString(&uStr, &str, FALSE); } CUString::CUString(PCWSTR pWideString) { RtlInitUnicodeString(&uStr, pWideString); aType = FromCode; } CUString::~CUString() { Free(); } void CUString::Free() { if (aType == FromPaged || aType == FromNonPaged) ExFreePool(uStr.Buffer); uStr.Buffer = NULL; uStr.Length = 0; uStr.MaximumLength = 0; } CUString::CUString(const CUString& orig) { // copy constructor (required) uStr.Length = 0; uStr.MaximumLength = orig.uStr.MaximumLength; uStr.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, uStr.MaximumLength, 1633); aType = FromPaged; RtlCopyUnicodeString(&uStr, (PUNICODE_STRING)&orig.uStr); uStr.Buffer[uStr.Length/2] = UNICODE_NULL; } CUString CUString::operator=(const CUString& rop) { // assignment operator overload (required) if (&rop != this) { // lop == rop ??? why was I called if (rop.uStr.Length >= uStr.Length || // does it fit? (aType != FromPaged && aType != FromNonPaged) ) { // it doesn't fit - free up existing buffer if (aType == FromPaged || aType == FromNonPaged) ExFreePool(uStr.Buffer); uStr.Length = 0; uStr.MaximumLength = rop.uStr.MaximumLength; // and allocate fresh space uStr.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, uStr.MaximumLength, 1633); aType = FromPaged; } RtlCopyUnicodeString(&uStr, (PUNICODE_STRING)&rop.uStr); uStr.Buffer[uStr.Length/2] = UNICODE_NULL; } return *this; } BOOLEAN CUString::operator ==(const CUString& rop) const { return RtlEqualUnicodeString(&this->uStr, &rop.uStr, FALSE); // case matters } CUString::operator PWSTR() const { return uStr.Buffer; } CUString::operator UNICODE_STRING &() { return uStr; } CUString CUString::operator+(const CUString& rop) const { CUString retVal; retVal.uStr.Length = this->uStr.Length + rop.uStr.Length; retVal.uStr.MaximumLength = max(32, retVal.uStr.Length+2); retVal.uStr.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, retVal.uStr.MaximumLength, 1633); RtlCopyUnicodeString(&retVal.uStr, (PUNICODE_STRING)&this->uStr); RtlAppendUnicodeStringToString(&retVal.uStr, (PUNICODE_STRING)&rop.uStr); retVal.uStr.Buffer[retVal.uStr.Length/2] = UNICODE_NULL; return retVal; } CUString& CUString::operator+=(const CUString& rop) { *this = *this + rop; return *this; } CUString::operator ULONG() const { ULONG retVal; RtlUnicodeStringToInteger((PUNICODE_STRING)&uStr, 0, &retVal); return retVal; } CUString::CUString(ULONG value) { // Converts from a ULONG into a CUString uStr.Length = 0; uStr.MaximumLength = 32; uStr.Buffer = (PWSTR) ExAllocatePoolWithTag(PagedPool, uStr.MaximumLength, 1633); aType = FromPaged; RtlIntegerToUnicodeString(value, 0, &uStr); } WCHAR& CUString::operator[](int idx) { // accesses an individual WCHAR in CUString buffer if (idx >= 0 && idx < uStr.MaximumLength/2) return uStr.Buffer[idx]; else return uStr.Buffer[0]; // got to return something } //有了CUString整个UNICODE_STRING处理起来就非常方便。 //我喜欢抄程序,真的用手抄,可以感觉一个好的程序的细节之美。 //以下是两个字符串的解释: // typedef struct _STRING { // USHORT Length; // USHORT MaximumLength; // PCHAR Buffer; // } ANSI_STRING *PANSI_STRING; //这是用于ANSI的字符串。 // typedef struct _UNICODE_STRING { // USHORT Length; // USHORT MaximumLength; // PWSTR Buffer; //Pointer to a buffer used to contain a string of wide characters // } UNICODE_STRING *PUNICODE_STRING; //这是驱动用的字符串。使用RtlInitUnicodeString()进行初始化。还可以用以下例程: //RtlAnsiStringToUnicodeSize, RtlAnsiStringToUnicodeString, RtlFreeUnicodeString, //RtlInitUnicodeString, RtlUnicodeStringToAnsiSize, RtlUnicodeStringToAnsiString |
|
11楼#
发布于:2004-11-10 23:03
接下来,我想通过比较两个DDK驱动,然后,得出自己的驱动。这两个驱动分别是前两经典的例子,为行文方便,我用《WDM》表示取自《Programming the Microsoft Windows Driver Model》的例子(Chap7PKTDMA),用《2000》表示取自《Windows2000 设备驱动程序设计指南》的例子(Chap12 DMASlave)。
一. DEVICE_EXTENSION的比较 《2000》: enum DRIVER_STATE {Stopped, Started, Removed}; typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDevice; PDEVICE_OBJECT pLowerDevice; ULONG DeviceNumber; CUString ustrDeviceName; // internal name CUString ustrSymLinkName; // external name PUCHAR portBase; // I/O register address ULONG portLength; KIRQL IRQL; // Irq for parallel port ULONG Vector; KAFFINITY Affinity; PKINTERRUPT pIntObj; // the interrupt object BOOLEAN bInterruptExpected; // TRUE iff this driver is expecting interrupt DRIVER_STATE state; // current state of driver PDMA_ADAPTER pDmaAdapter; ULONG mapRegisterCount; ULONG dmaChannel; // This is the "handle" assigned to the map registers // when the AdapterControl routine is called back PVOID mapRegisterBase; ULONG bytesRequested; ULONG bytesRemaining; ULONG transferSize; PUCHAR transferVA; // This flag is TRUE if writing, FALSE if reading BOOLEAN bWriting; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; //////////////////////////////////////////////////////////////////// //《WDM》: typedef struct tagDEVICE_EXTENSION { PDEVICE_OBJECT DeviceObject; // device object this extension belongs to,同 PDEVICE_OBJECT LowerDeviceObject; // next lower driver in same stack,同 PDEVICE_OBJECT Pdo; // the PDO IO_REMOVE_LOCK RemoveLock; // removal control locking structure,使用了IO_REMOVE_LOCK. UNICODE_STRING devname; //内部名称 PGENERIC_EXTENSION pgx; // device extension for GENERIC.SYS //在《WDM》的构建过程中,大量相同的工作被放在了GENERIC.SYS。 DEVQUEUE dqReadWrite; // queue for reads and writes //这是用于IRP串行化处理的一个类,在GENERIC.SYS里定义 LONG handles; // # open handles PKINTERRUPT InterruptObject; // address of interrupt object,同 PUCHAR portbase; // I/O port base address,同 ULONG nports; // number of assigned ports,同 PADAPTER_OBJECT AdapterObject; // DMA adapter object,同 ULONG nMapRegisters; // maximum # mapping registers ULONG xfer; // # bytes to transfer in this stage,同前bytesRequested; ULONG numxfer; // # bytes transferred so far,同前transferSize; ULONG nbytes; // # bytes remaining to transfer,同前bytesRemaining; ULONG nMapRegistersAllocated; // # map registers allocated for this transfer PVOID vaddr; // virtual addr for this stage of transfer,同前transferVA; PVOID regbase; // handle for base of mapping register set,同前mapRegisterBase; ULONG intcsr; // accumulated interrupt flags BOOLEAN mappedport; // true if we mapped port addr in StartDevice BOOLEAN busy; // true if device busy with a request,同前state. } DEVICE_EXTENSION, *PDEVICE_EXTENSION; ///////////////////////////////////////////////// //我的DEVICE_EXTENSION,参考了前两个定义,并以《WDM》为蓝本。 enum DRIVER_STATE{ Stopped, Started, Removed}; typedef struct tagDEVICE_EXTENSION { PDEVICE_OBJECT DeviceObject; // device object this extension belongs to,同 PDEVICE_OBJECT LowerDeviceObject; // next lower driver in same stack,同 PDEVICE_OBJECT Pdo; // the PDO,《WDM》 // IO_REMOVE_LOCK RemoveLock; // removal control locking structure,使用了IO_REMOVE_LOCK. //因为想简单些,所以没用,其实不用也是可以的。 ULONG DeviceNumber; //来自《2000》,用于DeviceObject计数 UNICODE_STRING devname; //内部名称 UNICODE_STRING SymLinkName; //根据《2000》加的。用于外部程序引用。 // PGENERIC_EXTENSION pgx; // device extension for GENERIC.SYS //在《WDM》的构建过程中,大量相同的工作被放在了GENERIC.SYS。 //因为本驱动,不需要GENERIC.SYS,其中的许多工作将在程序自身中完成。 // DEVQUEUE dqReadWrite; // queue for reads and writes //这是用于IRP串行化处理的一个类,在GENERIC.SYS里定义 //因为简化程序的控制,本驱动程序不用IRP串行处理。 // LONG handles; // # open handles PKINTERRUPT InterruptObject; // address of interrupt object,同 PUCHAR portbase; // I/O port base address,同 ULONG nports; // number of assigned ports,同 PADAPTER_OBJECT AdapterObject; // DMA adapter object,同 ULONG nMapRegisters; // maximum # mapping registers ULONG xfer; // # bytes to transfer in this stage,同前bytesRequested; ULONG numxfer; // # bytes transferred so far,同前transferSize; ULONG nbytes; // # bytes remaining to transfer,同前bytesRemaining; ULONG nMapRegistersAllocated; // # map registers allocated for this transfer PVOID vaddr; // virtual addr for this stage of transfer,同前transferVA; PVOID regbase; // handle for base of mapping register set,同前mapRegisterBase; ULONG intcsr; // accumulated interrupt flags BOOLEAN mappedport; // true if we mapped port addr in StartDevice DRIVER_STATE state; // current state of driver.来自《2000》 } DEVICE_EXTENSION, *PDEVICE_EXTENSION; /////////////////////////////////////////////////////////////////////////////////////// |
|
12楼#
发布于:2004-11-10 23:47
二. DriverEntery的比较
《2000》: //++ // Function: DriverEntry // // Description: // Initializes the driver. // // Arguments: // pDriverObject - Passed from I/O Manager // pRegistryPath - UNICODE_STRING pointer to // registry info (service key) // for this driver //本例子中没有对注册表进行操作,此pRegistryPath对应的是INF在注册表中设置的驱动注册目录 // Return value: // NTSTATUS signaling success or failure //-- extern "C" NTSTATUS DriverEntry ( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) { ULONG ulDeviceNumber = 0; NTSTATUS status = STATUS_SUCCESS; // Announce other driver entry points pDriverObject->DriverUnload = DriverUnload; // Announce the PNP AddDevice entry point pDriverObject->DriverExtension->AddDevice = AddDevice; // Announce the PNP Major Function entry point pDriverObject->MajorFunction[IRP_MJ_PNP] = DispPnp; // This includes Dispatch routines for Create, Write & Read pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchReadWrite; pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadWrite; pDriverObject->DriverStartIo = StartIo; // Notice that no device objects are created by DriverEntry. // Instead, we await the PnP call to AddDevice return status; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //《WDM》 extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { // DriverEntry KdPrint((DRIVERNAME " - Entering DriverEntry: DriverObject %8.8lXn", DriverObject)); // Insist that OS support at least the WDM level of the DDK we use if (!IoIsWdmVersionAvailable(1, 0)) { KdPrint((DRIVERNAME " - Expected version of WDM (%d.%2.2d) not availablen", 1, 0)); return STATUS_UNSUCCESSFUL; } // See if we're running under Win98 or NT: win98 = IsWin98(); #if DBG if (win98) KdPrint((DRIVERNAME " - Running under Windows 98n")); else KdPrint((DRIVERNAME " - Running under NTn")); #endif // Save the name of the service key servkey.Buffer = (PWSTR) ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(WCHAR)); if (!servkey.Buffer) { KdPrint((DRIVERNAME " - Unable to allocate %d bytes for copy of service key namen", RegistryPath->Length + sizeof(WCHAR))); return STATUS_INSUFFICIENT_RESOURCES; } servkey.MaximumLength = RegistryPath->Length + sizeof(WCHAR); RtlCopyUnicodeString(&servkey, RegistryPath); // Initialize function pointers DriverObject->DriverUnload = DriverUnload; DriverObject->DriverExtension->AddDevice = AddDevice; DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; DriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchReadWrite; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchCleanup; DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; return STATUS_SUCCESS; } // DriverEntry //比较这两个DriverEntry,基本是相同的。以下就以《2000》为蓝本来构建我的DriverEntery,因其简洁 extern "C" NTSTATUS DriverEntry ( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) { ULONG ulDeviceNumber = 0; NTSTATUS status = STATUS_SUCCESS; // Announce other driver entry points pDriverObject->DriverUnload = DriverUnload; // Announce the PNP AddDevice entry point pDriverObject->DriverExtension->AddDevice = AddDevice; // Announce the PNP Major Function entry point pDriverObject->MajorFunction[IRP_MJ_PNP] = DispPnp; // This includes Dispatch routines for Create, Write & Read pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchReadWrite; pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadWrite; pDriverObject->DriverStartIo = StartIo; // Notice that no device objects are created by DriverEntry. // Instead, we await the PnP call to AddDevice return status; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 三. AddDevice的实现 由于《WDM》的AddDevice用的是GENERIC.SYS来初始化,与《2000》有很多不同,较繁琐,而我的驱动是以《2000》为蓝本来写的,此处就不贴《WDM》的。由于我们的DEVICE_EXTENSION改了许多,所以要重新改写此AddDevice。以下就是改写后的部分: //++ // Function: AddDevice // // Description: // Called by the PNP Manager when a new device is // detected on a bus. The responsibilities include // creating an FDO, device name, and symbolic link. // // Arguments: // pDriverObject - Passed from PNP Manager // pdo - pointer to Physcial Device Object // passed from PNP Manager // // Return value: // NTSTATUS signaling success or failure //-- NTSTATUS AddDevice ( IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pdo ) { NTSTATUS status; PDEVICE_OBJECT pfdo; PDEVICE_EXTENSION pDevExt; static int ulDeviceNumber = 0; // Form the internal Device Name CUString devName("\Device\9054DMA"); // for "9054 DMA" dev devName += CUString(ulDeviceNumber); // Now create the device status = IoCreateDevice( pDriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pfdo ); if (!NT_SUCCESS(status)) return status; // Choose to use DIRECT_IO (typical for DMA) pfdo->Flags |= DO_DIRECT_IO; // Initialize the Device Extension //根据DEVICE_EXTENSION完成初始化 pDevExt = (PDEVICE_EXTENSION)pfdo->DeviceExtension; pDevExt->DeviceObject = pfdo; // back pointer pDevExt->DeviceNumber = ulDeviceNumber; pDevExt->DevName = devName; pDevExt->Pdo = pdo; //来自例程的输入参数 pDevExt->state = Stopped; // Pile this new fdo on top of the existing lower stack pDevExt->LowerDeviceObject = // downward pointer IoAttachDeviceToDeviceStack( pfdo, pdo); // This is where the upper pointer would be initialized. // Notice how the cast of the lower device's extension // must be known in order to find the offset pUpperDevice. // PLOWER_DEVEXT pLowerDevExt = (PLOWER_DEVEXT) // pDevExt->pLowerDevice->DeviceExtension; // pLowerDevExt->pUpperDevice = pfdo; // Form the symbolic link name CUString symLinkName("\??\DMAS"); symLinkName += CUString(ulDeviceNumber+1); // 1 based pDevExt->SymLinkName = symLinkName; // Now create the link name status = IoCreateSymbolicLink( &(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName ); if (!NT_SUCCESS(status)) { // if it fails now, must delete Device object IoDeleteDevice( pfdo ); return status; } // We need a DpcForIsr registration IoInitializeDpcRequest( pfdo, DpcForIsr ); // Clear the Device Initializing bit since the FDO was created // outside of DriverEntry. pfdo->Flags &= ~DO_DEVICE_INITIALIZING; // Made it ulDeviceNumber++; return STATUS_SUCCESS; } |
|
13楼#
发布于:2004-11-10 23:48
四. DispPnp的实现 当总线驱动器扫描到硬件时,就会发出一个IRP_MJ_PNP的IRP,它是实现PNP类型的驱动初始化硬件资源的地方。 我采用了《2000》的代码,因其清楚明了。 NTSTATUS DispPnp( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ) { // obtain current IRP stack location PIO_STACK_LOCATION pIrpStack; pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); switch (pIrpStack->MinorFunction) { case IRP_MN_START_DEVICE: return HandleStartDevice(pDO, pIrp ); case IRP_MN_STOP_DEVICE: return HandleStopDevice( pDO, pIrp ); case IRP_MN_REMOVE_DEVICE: return HandleRemoveDevice( pDO, pIrp ); default: // if not supported here, just pass it down return PassDownPnP(pDO, pIrp); } // all paths from the switch statement will "return" // the results of the handler invoked } //这是不处理的IRP,将它传到下一层。 NTSTATUS PassDownPnP( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ) { IoSkipCurrentIrpStackLocation( pIrp ); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension; return IoCallDriver(pDevExt->pLowerDevice, pIrp); } //上面的程序,我基本没有改动。 //接下来是要改动的部分 //这里可以跟《武》例子中的NTSTATUS PCI9054Device::OnStartDevice(Kirp I)进行比较了。 NTSTATUS HandleStartDevice( IN PDEVICE_OBJECT pDO,IN PIRP pIrp ) { // The stack location contains the Parameter info PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDO->DeviceExtension; PCM_RESOURCE_LIST pResourceList; PCM_FULL_RESOURCE_DESCRIPTOR pFullDescriptor; PCM_PARTIAL_RESOURCE_LIST pPartialList; PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialDescriptor; int i; NTSTATUS status; ULONG vector; KIRQL irql; KINTERRUPT_MODE mode; KAFFINITY affinity; BOOLEAN irqshare; BOOLEAN gotinterrupt = FALSE; PHYSICAL_ADDRESS portbase; BOOLEAN gotport = FALSE; pResourceList = pIrpStack->Parameters.StartDevice.AllocatedResourcesTranslated; pFullDescriptor = pResourceList->List; pPartialList = &pFullDescriptor->PartialResourceList; for (i=0; i<(int)pPartialList->Count; i++) { pPartialDescriptor = &pPartialList->PartialDescriptors; switch (pPartialDescriptor->Type) { case CmResourceTypeInterrupt: irql = (KIRQL)pPartialDescriptor->u.Interrupt.Level; vector = pPartialDescriptor->u.Interrupt.Vector; affinity = pPartialDescriptor->u.Interrupt.Affinity; mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED)? Latched : LevelSensitive; Irqshare = resource->ShareDisposition == CmResourceShareShared; Gotinterrupt = TRUE; break; case CmResourceTypePort: portbase = pPartialDescriptor->u.Port.Start; pDevExt->nports = pPartialDescriptor->u.Port.Length; gotport = TRUE; break; case default: // We don't do memory usage break; } } // Make sure we got our interrupt (and port) resources // Fail this IRP if we didn't. // Most likely cause for no interrupt: // Failure to request an interrupt resource for the // printer port from the Device Manager. // Be SURE to use Control Panel... // Administrative Tools... // Computer Management... // Device Manager... // Then select Ports...Printer Port (LPT1) // From the Port Settings tab, // select "Use any interrupt assigned to the port" if (!(gotport && gotinterrupt)) return STATUS_DEVICE_CONFIGURATION_ERROR; INTERFACE_TYPE bustype; //获得总线类型 ULONG junk; status = IoGetDeviceProperty( pdx->Pdo, DeviePropertyLegacyBusType, sizeof(bustype), &bustype, &junk); if(!NT_SUCCESS(status)) return status; pDevExt ->portbase = (PUCHAR)portbase.QuadPart; //构建一个DMA ADAPTER对象 DEVICE_DESCRIPTION dd; RtlZeroMemory(&dd, sizeof(dd)); dd.Version = DEVICE_DESCRIPTION_VERSION; dd.Master = TRUE; dd.ScatterGather = FALSE; dd.DemondMode = TRUE; dd.AutoInitialize = FALSE; dd.Dma32BitAddress = TRUE; dd.IgnoreCount = FALSE; dd.DmaChannel = 0; dd.InterfaceType = bustype; //《武》中是 PCIBus; dd.DmaWidth = Width32Bits; // PCI default width dd.DmaSpeed = Compatible; dd.MaximumLength = 0x1000; //仔细比较,发现与《武》的DEVICE_DESCRIPTION的初始化完全一致。 //以下是完整的DEVICE_DESCRIPTOR的定义 //typedef struct _DEVICE_DESCRIPTION { // ULONG Version; // BOOLEAN Master; // BOOLEAN ScatterGather; // BOOLEAN DemandMode; // BOOLEAN AutoInitialize; // BOOLEAN Dma32BitAddresses; // BOOLEAN IgnoreCount; // BOOLEAN Reserved1; // BOOLEAN Dma64BitAddresses; // ULONG BusNumber; // ULONG DmaChannel; // INTERFACE_TYPE InterfaceType; // DMA_WIDTH DmaWidth; // DMA_SPEED DmaSpeed; // ULONG MaximumLength; // ULONG DmaPort; // } DEVICE_DESCRIPTION, *PDEVICE_DESCRIPTION; //现在是将全部都设定了初值。 pdx->AdapterObject = IoGetDmaAdapter(pDevExt ->Pdo, &dd, & pDevExt ->nMapRegisters); //最后一个参数是指最大的可以映射的寄存器组个数。 if (!pDevExt ->AdapterObject) { // can't create adapter object KdPrint((DRIVERNAME " - Unable to create DMA adapter object\n")); pDevExt ->portbase = NULL; return STATUS_UNSUCCESSFUL; } // can't create adapter object // Create & connect to an Interrupt object status = IoConnectInterrupt( &pDevExt->InterruptObject, // the Interrupt object Isr, // our ISR pDevExt, // Service Context NULL, // no spin lock vector, // vector irql, // DIRQL irql, // DIRQL mode, // Latched or LevelSensitive irqshare, // Shared? affinity, // processors in an MP set FALSE ); // save FP registers? if (!NT_SUCCESS(status)) { return status; } //允许PCI中断和DMA通道0中断 WRITE_PORT_ULONG((PULONG)( pDevExt->portbase + INTCSR), 0x40100); //其中INTCSR是有9054 DATASHEET中得到的是0x68。 pDevExt->state = Started; return PassDownPnP(pDO, pIrp); } NTSTATUS HandleStopDevice( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDO->DeviceExtension; //禁止PCI中断和DMA通道0中断 WRITE_PORT_ULONG((PULONG)( pDevExt->portbase + INTCSR), 0); // Delete our Interrupt object if (pDevExt->InterruptObject) IoDisconnectInterrupt( pDevExt-> InterruptObject); pDevExt-> InterruptObject = NULL; // Delete the DMA Adapter object pDevExt->AdapterObject->DmaOperations-> FreeAdapterChannel( pDevExt->AdapterObject ); pDevExt-> AdapterObject = NULL; pDevExt->state = Stopped; return PassDownPnP(pDO, pIrp); } 以下是 DMA 传输的步骤: (1)在IRP_MJ_START_DEVICE的处理过程中,使用IoGetDmaAdapter,结合DEVICE_DESCRIPTION,构建AdapterObject。 (2) 在StartIo中,通过AllocateAdapterChannel启动回调例程AdapterControl(这个名字可不同)。 (3) 在AdapterControl中,使用MapTransfer,映射连续内存,并开始进行第一次传输 (4) 当DMA传输完,将产生一个中断,由ISR处理程序处理。 (5) ISR服务子程序,将实际处理任务交给DPC服务例程。 (6) DPC检测是否完成传输任务,如果没有,则再调用MapTransfer,映射连续内存,然后再启动下一次传输。如果完成,则结束此IRP. 其过程和前面《DS》处理DMA的流程很相似。 |
|
14楼#
发布于:2004-11-12 16:57
当用户程序发出读、写请求时,驱动程序将收到IRP_MJ_READ,或IRP_MJ_WRITE的IRP,然后,启动StartIo实现DMA传输。但DMA传输结束,9054会发出中断请求,驱动程序会启动ISR服务程序,为将IRQL的级别降低,ISR会将实际的工作交给DPC来完成。以下是9054的DMA所需代码。包括:StartIo, Isr, Dpc,及此例程中用到的子函数。
一. StartIo VOID StartIo(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { // StartIo PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); PMDL mdl = Irp->MdlAddress; pdx->numxfer = 0; pdx->xfer = pdx->nbytes = MmGetMdlByteCount(mdl); pdx->vaddr = MmGetMdlVirtualAddress(mdl); ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr, pdx->nbytes); if (nregs > pdx->nMapRegisters) { // staged transfer needed nregs = pdx->nMapRegisters; pdx->xfer = nregs * PAGE_SIZE - MmGetMdlByteOffset(mdl); } // staged transfer needed //注意,在这里完成了数据包的第一次分割。 //nregs中保存着整个DMA要传输的数据块所需的页面数。 //而nMapRegisters是在IoGetDmaAdapter中返回的系统可以映射的最大页面数量。 //当nregs > pdx->nMapRegisters,就必须减少nregs。 pdx->nMapRegistersAllocated = nregs; // save for deallocation later status = (*pdx->AdapterObject->DmaOperations->AllocateAdapterChannel) (pdx->AdapterObject, fdo, nregs, (PDRIVER_CONTROL) AdapterControl, pdx); //此处设置了AdapterControl, //将在AdapterControl里完成实际的DMA硬件控制。 if (!NT_SUCCESS(status)) { pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); IoStartNextPacket(fdo, FALSE); } } // StartIo /////////////////////////////////////////////////////////////////////////////// #pragma LOCKEDCODE IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo, PIRP junk, PVOID regbase, PDEVICE_EXTENSION pdx) { // AdapterControl PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite); PMDL mdl = Irp->MdlAddress; BOOLEAN isread = IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ; pdx->regbase = regbase; KeFlushIoBuffers(mdl, isread, TRUE); PHYSICAL_ADDRESS address = (*pdx->AdapterObject->DmaOperations->MapTransfer) (pdx->AdapterObject, mdl, regbase, pdx->vaddr, &pdx->xfer, !isread); //注意:只有在MapTransfer中映射的区域才是实际一次DMA传输的区域。 //这里的pdx->xfer是IN OUT类型,输出时是实际的传输量,可能与输入时的值不一样 //这里完成了数据的第二次截断。 //实际的硬件寄存器操作在下面进行。 StartTransfer(pdx, address, isread); return DeallocateObjectKeepRegisters; } // AdapterControl ////////////////////////////////////////////////////////////////////////////////////////// #pragma LOCKEDCODE VOID StartTransfer(PDEVICE_EXTENSION pdx, PHYSICAL_ADDRESS address, BOOLEAN isread) { // StartTransfer // Setup read or write transfer registers. Note that the 9054 calls a transfer // from memory to the device a "read" //Channel0 interrupt to the PCI Bus interrupt,Done Interrupt Enable,FIFO WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMAMODE0), 0x20C00); //DMA Channel0 PCI Address WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMAPADR0), address); //DMA Channel0 Local Address,自己设计的FIFO地址 WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMALADR0), 0x8); //DMA Channel0 Transfer Size(Bytes) WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMASIZ0), pdx->xfer); if (isread) { // read from device //from the Local Bus to the PCI Bus WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMADPR0), 0x8); } // read from device else { // write to device //from the PCI Bus to the Local Bus WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMADPR0), 0x0); } // write to device // start the transfer pdx->state = Started; //Channel0 Enable,Start WRITE_PORT_ULONG((PULONG) (pdx->portbase + DMACSR0), 0x3); } // StartTransfer 以下是《武》的StartDma VOID PCI9054Device::StartDMA(ULONG PAddress,ULONG NBytes) { //下面几条语句设置DMA通道0寄存器,启动块传输方式,从FIFO读数据 //Channel0 interrupt to the PCI Bus interrupt,Done Interrupt Enable,FIFO m_IoPortRange0.outd(DMAMODE0,0x20C00); //DMA Channel0 PCI Address m_IoPortRange0.outd(DMAPADR0,PAddress); //DMA Channel0 Local Address,自己设计的FIFO地址 m_IoPortRange0.outd(DMALADR0,0x8); //DMA Channel0 Transfer Size(Bytes) m_IoPortRange0.outd(DMASIZ0,NBytes); //from the Local Bus to the PCI Bus m_IoPortRange0.outd(DMADPR0,0x8); //Channel0 Enable,Start m_IoPortRange0.outb(DMACSR0,0x3); } 对DMA操作的步骤: 1. 设置DMA MODE 以适应BLOCK方式 2. 写入PCI Bus的起始地址 ,用于初始化DMAPADR 3. 写入LOCAL Bus 的起始地址,用于初始化DMALADR 4. 写入DMA传输数据块的大小,用于初始化DMASIZE 5. 确定传输方向,DMADPR[3],1是L到P,0是P到L。 6. 启动DMA,DMACSR的【1:0】,[0]是Channel0 Enable,【1】是Channel0 Start. 注意MapTransfer返回了映射后的地址,9054将使用它作为PCI Bus的起始地址。 |
|
15楼#
发布于:2004-11-13 10:33
学习中
|
|
|
16楼#
发布于:2004-11-16 08:44
好帖!学习楼主认真学习的态度和乐于助人的精神!
总结地很好! |
|
17楼#
发布于:2004-11-16 11:08
感谢dengyih,从你那里得到了很多启发,希望能继续。
绝对支持!! :o :o :o :o |
|
|
18楼#
发布于:2004-11-16 19:20
虽然我没学会,但是还要顶
|
|
|
19楼#
发布于:2004-11-16 20:49
首先感谢各位给我得鼓励,每当打开电脑的时候,第一件事总是看看帖子有没有回应,每当看到有同仁的鼓励,就觉得辛苦没有白费。谢谢!
以下就接着前一部分将本程序的后部分给补上。 二. ISR中断服务程序,当驱动绑定的中断产生,就进入ISR. 因为ISR的执行是在IRQL_DISPATCH上的,所以,具体的工作交给DPC,以降低IRQL的级别。 BOOLEAN Isr ( IN PKINTERRUPT pIntObj, IN PVOID pServiceContext ) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pServiceContext; PDEVICE_OBJECT pDevObj = pDevExt->pDevice; PIRP pIrp = pDevObj->CurrentIrp; // Check HW to see if interrupt for this driver // If not, return FALSE immediately //return FALSE; ULONG intcsr = READ_PORT_ULONG((PULONG) (pDevExt ->portbase + INTCSR)); If(( intcsr & 0x200000) == 0) { return FALSE; //不是,返回FALSE } // its our interrupt, deal with it // Dismiss the interrupt in HW now //先禁止中断 WRITE_PORT_ULONG((PULONG) (pDevExt ->portbase + DMAMODE0), 0x20800)); //再清除中断 WRITE_PORT_ULONG((PULONG) (pDevExt ->portbase + DMACSR0), 0x10)); // Do the rest of the work down // at DISPATCH_LEVEL IRQL IoRequestDpc( pDevObj, pIrp, (PVOID)pDevExt ); return TRUE; } 三. DPC,用于完成第二部分的传送。 本程序将一个大的数据块,分成了几次进行DMA传输,当其中一部分完成后,就进入第二部分。这个工作由DPC来完成。 其实,DPC不用怎么改,几乎是一个样的。要改的是DEVICE_EXTENSION的部分成员的名字。我在这里没有改,觉得《2000》的原代码的名字取得更好。在实际得程序中,一定要改过来。 VOID DpcForIsr(IN PKDPC pDpc, IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext ) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pContext; ULONG mapRegsNeeded; PMDL pMdl = pIrp->MdlAddress; // Flush the Apapter buffer to system RAM or device. pDevExt->pDmaAdapter->DmaOperations-> FlushAdapterBuffers( pDevExt->pDmaAdapter, pMdl, pDevExt->mapRegisterBase, pDevExt->transferVA, pDevExt->transferSize, pDevExt->bWriting ); // If the device is reporting errors, fail the IRP if (DEVICE_FAIL( pDevExt )) { // An error occurred, the DMA channel is now free pDevExt->pDmaAdapter->DmaOperations-> FreeAdapterChannel( pDevExt->pDmaAdapter ); pIrp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; pIrp->IoStatus.Information = pDevExt->bytesRequested - pDevExt->bytesRemaining; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); IoStartNextPacket( pDevObj, FALSE); } // Device had no errors, see if another partial needed pDevExt->bytesRemaining -= pDevExt->transferSize; if (pDevExt->bytesRemaining > 0) { // Another partial transfer needed // Update the transferVA and try to finish it pDevExt->transferVA += pDevExt->transferSize; pDevExt->transferSize = pDevExt->bytesRemaining; mapRegsNeeded = ADDRESS_AND_SIZE_TO_SPAN_PAGES( pDevExt->transferVA, pDevExt->transferSize ); // If it still doesn't fit in one swipe, // cut back the expectation if (mapRegsNeeded > pDevExt->mapRegisterCount) { mapRegsNeeded = pDevExt->mapRegisterCount; pDevExt->transferSize = mapRegsNeeded * PAGE_SIZE - BYTE_OFFSET( pDevExt->transferVA ); } // Now set up the mapping registers for another pDevExt->pDmaAdapter->DmaOperations-> MapTransfer( pDevExt->pDmaAdapter, pMdl, pDevExt->mapRegisterBase, pDevExt->transferVA, &pDevExt->transferSize, pDevExt->bWriting ); // And start the device StartTransfer( pDevExt ); } else { // Entire transfer has now completed - // Free the DMA channel for another device pDevExt->pDmaAdapter->DmaOperations-> FreeAdapterChannel( pDevExt->pDmaAdapter ); // And complete the IRP in glory pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = pDevExt->bytesRequested; // Choose a priority boost appropriate for device IoCompleteRequest( pIrp, IO_DISK_INCREMENT ); IoStartNextPacket( pDevObj, FALSE ); } } |
|
上一页
下一页