和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

}

#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

接下来,我想通过比较两个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;

///////////////////////////////////////////////////////////////////////////////////////

二. 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;

}

四. 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[i];

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的流程很相似。

当用户程序发出读、写请求时,驱动程序将收到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的起始地址。《转自网易博友》

用DDK开发的9054驱动 .的更多相关文章

  1. Beaglebone Black开发板安装驱动

    Beaglebone Black开发板安装驱动 Beaglebone Black开发板安装驱动,在使用Beaglebone Black开发板子做任何事情之前首先需要安装驱动.下面的内容就了展示在Win ...

  2. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  3. 【阿里云IoT+YF3300】9.快速开发modbus设备驱动

    Modbus是一种串行通信协议,是莫迪康公司为PLC(编程逻辑控制器)通信而设计的协议.Modbus目前已经成为工业领域通信协议的业界标准,大部分的仪器仪表都支持该通信协议.很早以前就开发过基于Mod ...

  4. STC8H开发(六): SPI驱动ADXL345三轴加速度检测模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  5. STC8H开发(五): SPI驱动nRF24L01无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  6. STC8H开发(七): I2C驱动MPU6050三轴加速度+三轴角速度检测模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  7. STC8H开发(十): SPI驱动Nokia5110 LCD(PCD8544)

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  8. STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  9. Android 开发之 ---- 底层驱动开发(一) 【转】

    转自:http://blog.csdn.net/jmq_0000/article/details/7372783 版权声明:本文为博主原创文章,未经博主允许不得转载. 驱动概述 说到 Android ...

随机推荐

  1. spring使用redis做缓存

    缓存 什么是缓存? 在高并发下,为了提高访问的性能,需要将数据库中 一些经常展现和不会频繁变更的数据,存放在存取速率更快的内存中.这样可以 降低数据的获取时间,带来更好的体验 减轻数据库的压力 缓存适 ...

  2. DOM中对象的获得

    DOM的所有对象会在页面打开时,由浏览器页面创建. 浏览器把dom定点对象Document对像的引用交给了window对象. 1.document对象的获得    var doc = window.d ...

  3. 精通libGDX游戏开发-RPG实战-欢迎来到RPG的世界

    欢迎来到RPG的世界 本章我会快速的使用tiled这样的瓷砖地图工具,来带领大家创造所设想的世界. 创建并编辑瓷砖地图 瓷砖地图(tile-based map)是广泛应用于各种游戏类型的地图格式,li ...

  4. SDP(4):ScalikeJDBC- JDBC-Engine:Updating

    在上一篇博文里我们把JDBC-Engine的读取操作部分分离出来进行了讨论,在这篇准备把更新Update部分功能介绍一下.当然,JDBC-Engine的功能是基于ScalikeJDBC的,所有的操作和 ...

  5. javascipt中的DOM对象

    1.HTML中DOM对象的概念 HTML Document Object Model(文档对象模型) HTML DOM定义了访问和操作HTML文档的标准方法 HTML DOM把HTML文档呈现为带有元 ...

  6. 【OS】NMON的简介和使用

    [OS]NMON的简介和使用 目前NMON已开源,以sourceforge为根据地,网址是http://nmon.sourceforge.net. 1. 目的 本文介绍操作系统监控工具Nmon的概念. ...

  7. Linux系统下LNMP一键搭建Linux、PHP、MySQL环境(适合新手搭建linux下的web生成环境)

    一. 首先要解释一下,什么是LNMP,LNMP起源于LAMP,LAMP是Linux+Apache+Mysql/MariaDB+Perl/PHP/Python的缩写,这里将Web服务端的Apache替换 ...

  8. srand()和rand()函数的使用

    rand()函数不接受参数,默认以1为种子(即起始值). 随机数生成器总是以相同的种子开始,所以形成的伪随机数列也相同,失去了随机意义.(但这样便于程序调试) srand()函数就是指明种子的大小:只 ...

  9. 配置 github 上的程序

    最近学习的node.vue的单页模式,看到github (地址:https://github.com/bailicangdu/node-elm)上面有大神做了一个几十页的系统,心想怎么弄到本地研究下 ...

  10. iOS UITableView的多选

    一些列表经常需要编辑多选的功能,而UITableview自带多选删除的功能,使用起来方便,不需要自己去做数据存储和选中状态转换,可以减少不少开发时间.下面就来介绍下UITableView多选的使用. ...