接下来,我想通过比较两个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的起始地址。《转自网易博友》