和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. DBCP和C3P0使用--未完善

    一.前言: DBCP和C3PO都可以作为数据连接池, 二. 导入jar包: 三.配置applicationContext.xml文件 配置dbcp <!-- 创建数据源 --> <b ...

  2. Spring常用注解简析

    1. Autowired 自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property.当然,getter看个人需求,如果私有属性需要对外提供的话,应当 ...

  3. javascript中的字符串对象和数组对象

    1.javascript的对象的概念 在javascript中,除了null和undefined以处,其他的数据类型都被定义成了对象 也可以用创建对象的方法定义变量,string,math,array ...

  4. Selenium UI自动化解决iframe定位问题

      更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6735116.html 一个阴雨霏霏 ...

  5. Typora最常用快捷键

    插入图片:直接拖动到指定位置即可或者ctrl+shift+i 插入链接:ctrl+k 对于本地图片,我们可以直接拖进来 双回车可以回到行首

  6. 计算机基础之Windows10操作系统安装U盘制作

    1.第一步,下载Windows10--ISO镜像(Windows7类似),下载站点: https://msdn.itellyou.cn/(百度搜索msdn即可),个人认为这是最干净的操作系统镜像站点, ...

  7. Windows Server 2016-安装AD域服务注意事项

    使用 Active Directory域服务 (AD DS) 服务器角色,可以创建用于用户和资源管理的可伸缩.安全及可管理的基础机构,并可以提供对启用目录的应用程序(如 Microsoft Excha ...

  8. python数据分析工具包(2)——Numpy(二)

    上一篇文章简单地介绍了numpy的一些基本数据类型,以及生成数组和矩阵的操作.下面我们来看一下矩阵的基本运算.在线性代数中,常见的矩阵运算包括,计算行列式.求逆矩阵.矩阵的秩等.下面我们来一一实现. ...

  9. Qt滑动条设计与实现

    没有找到Qt的滑动条控件,所以自己写了一个,能够实现亮度调节.音量调节等功能. 效果如下图: 主要设计思路: 有些调节功能如对比度是有负值的,所以需要能对滑动条的数值范围进行设置,不局限于0~100 ...

  10. ZooKeeper简介与集群部署

    ZooKeeper 是一个开源的分布式协调服务,由雅虎公司创建,是Google Chubby的开源实现,ZooKeeper的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的 ...