和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。
接下来,我想通过比较两个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的起始地址。《转自网易博友》
和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。的更多相关文章
- 驱动开发利器Microsoft Windows Driver Kit 7.1.0下载
在Windows 2000 与Windows XP 系统采用是WINDDK来开发WINDOWS驱动程序,我手头也有WINDDK,可是从Windows Vista开始之后,一般采用Microsoft W ...
- 【如何快速的开发一个完整的iOS直播app】(原理篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...
- 【如何快速的开发一个完整的iOS直播app】(播放篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看上篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,集成ijkpl ...
- 【如何快速的开发一个完整的iOS直播app】(美颜篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...
- 【如何快速的开发一个完整的iOS直播app】(采集篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,首先需要采集主 ...
- 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)
来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...
- 【如何快速的开发一个完整的iOS直播app】(推流篇)
前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,肯定需要流媒体服务器,本篇主要讲解直播中流媒体服务器搭建,并且讲解了如 ...
- [置顶] 《Windows编程零基础》__2 一个完整的程序
Windows开发的常识 1)窗口 Windows中最基本的概念也许就是窗口了,每一个前台程序都至少有一个窗口,一个窗口也是你可以看到的部分,比如,QQ有如下的登录窗口 基本上你在Windows中可见 ...
- 动态下载 Yahoo 网络数据存入 Microsoft SQL Server 再 Matlab 调用的一个完整例子
% 编程环境: Matlab 2014a, win7 32bit, Microsoft SQL Server 2008r2 %% % 清屏 clc; clear all; close all; %% ...
随机推荐
- objective-C学习笔记(八) 集合类型 Collection Types
OBJC的集合类型: 1.数组 Array 2.Set 3.键值对 Dictionary 数组:OC中的数组被定义为class,引用类型.索引从0开始,访问越界会抛出运行时异常. NSArray的元素 ...
- 关于LZO和LZOP
LZO 是一个适合实时解压.压缩的压缩库 LZOP 基于LZO库的压缩解压工具 PS:有了压缩解压库LZO,还不能直接操作文件压缩解压,需要LZOP 下载的话直接google吧~~~
- 关于asp.net 的一些好资料地址 , 防止丢失!
学习数据结构的好网站 : http://student.zjzk.cn/course_ware/data_structure/web/practice/practice1.htm http://www ...
- PHP创建定义数组
$array = array(); $array["key"] = "values"; ?> 在PHP中声明数组的方式主要有两种:1.用arr ...
- xar安装使用方法
xar是一种扩展的归档格式(eXtensible ARchive format),是一种开源的文件格式.xar文件在Mac OS X 10.5里是用于软件安装程序. ---------------- ...
- [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...
- GraphLab:新的面向机器学习的并行框架
大规模图数据计算引起了许多知名公司的关注,微软提出了用于图数据匹配的Horton - Querying Large Distributed Graphs(Link:http://research.mi ...
- java处理图片时找到不sun.awt.X11GraphicsEnvironment问题
-Djava.awt.headless=true 解决. export DISPLAY=:0或者xhost + localhost 来解决 1. 什么是Headless mode? Headle ...
- 转:CI配置SMARTY
1.到相应站点下载Smarty的源码包:2.将源码包里面的libs文件夹copy到CI的项目目录下面的libraries文件夹下,并重命名为Smarty:3.在目录 application/libra ...
- perl5 第二章 简单变量
第二章 简单变量 by flamephoenix 一.整型 二.浮点数 三.字符串 基本上,简单变量就是一个数据单元,这个单元可以是数字或字符串.一.整型 1.整型 PERL最常用的简单变量,由 ...