www.pudn.com > Windows 2000设备驱动程序设计指南 .zip > Driver.cpp


// 
// Driver.c - Chapter 12 - DMA Slave Packet Driver 
// 
// Copyright (C) 2000 by Jerry Lozano 
// 
 
#include "Driver.h" 
 
// Forward declarations 
// 
NTSTATUS AddDevice ( 
			IN PDRIVER_OBJECT pDriverObject, 
			IN PDEVICE_OBJECT pdo	); 
 
NTSTATUS DispPnp(	IN PDEVICE_OBJECT pDO, 
					IN PIRP pIrp ); 
 
NTSTATUS PassDownPnP( IN PDEVICE_OBJECT pDO, 
					IN PIRP pIrp ); 
 
NTSTATUS HandleStartDevice(	IN PDEVICE_OBJECT pDO, 
							IN PIRP pIrp ); 
NTSTATUS HandleStopDevice(	IN PDEVICE_OBJECT pDO, 
							IN PIRP pIrp ); 
 
NTSTATUS HandleRemoveDevice(IN PDEVICE_OBJECT pDO, 
							IN PIRP pIrp ); 
 
static VOID DriverUnload ( 
		IN PDRIVER_OBJECT	pDriverObject	); 
 
static NTSTATUS DispatchCreate ( 
		IN PDEVICE_OBJECT	pDevObj, 
		IN PIRP				pIrp			); 
 
static NTSTATUS DispatchClose ( 
		IN PDEVICE_OBJECT	pDevObj, 
		IN PIRP				pIrp			); 
 
static NTSTATUS DispatchReadWrite( 
	IN PDEVICE_OBJECT pDO, 
	IN PIRP pIrp 
	); 
 
 
BOOLEAN Isr ( 
			IN PKINTERRUPT pIntObj, 
			IN PVOID pServiceContext		); 
 
VOID StartIo( 
	IN PDEVICE_OBJECT pDevObj, 
	IN PIRP pIrp 
	); 
 
NTSTATUS GetDmaInfo( IN INTERFACE_TYPE busType, 
				 IN PDEVICE_OBJECT pDevObj ); 
 
VOID DpcForIsr( 
	IN PKDPC pDpc, 
	IN PDEVICE_OBJECT pDevObj, 
	IN PIRP pIrp, 
	IN PVOID pContext 
	); 
 
IO_ALLOCATION_ACTION AdapterControl( 
					IN PDEVICE_OBJECT pDevObj, 
					IN PIRP pIrp, 
					IN PVOID MapRegisterBase, 
					IN PVOID pContext ); 
 
VOID StartTransfer( IN PDEVICE_EXTENSION pDevExt ); 
 
//++ 
// 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 
// 
// 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; 
} 
 
//++ 
// Function:	GetDmaInfo 
// 
// Description: 
//		Initializes the driver's DMA Adapter 
// 
// Arguments: 
//		busType - Isa, Internal, etc. 
//		busNumber - raw bus number 
// 
// Return value: 
//		NTSTATUS signaling success or failure 
//-- 
NTSTATUS GetDmaInfo( IN INTERFACE_TYPE busType, 
					 IN PDEVICE_OBJECT pDevObj ) { 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDevObj->DeviceExtension; 
 
	DEVICE_DESCRIPTION dd; 
	 
	// Zero out the entire structure 
	RtlZeroMemory( &dd, sizeof(dd) ); 
 
	dd.Version = DEVICE_DESCRIPTION_VERSION1; 
	dd.Master = FALSE;	// this is a slave device 
	dd.ScatterGather = FALSE; 
	dd.DemandMode = FALSE; 
	dd.AutoInitialize = FALSE; 
	dd.Dma32BitAddresses = FALSE; 
 
	dd.InterfaceType = busType;	// as passed in 
 
	dd.DmaChannel = pDevExt->dmaChannel; 
	dd.MaximumLength = MAX_DMA_LENGTH; 
	dd.DmaWidth = Width16Bits; 
	dd.DmaSpeed = Compatible; 
 
	// Compute the maximum number of mapping regs 
	// this device could possibly need.  Since the 
	// transfer may not be paged aligned, add one 
	// to allow the max xfer size to span a page. 
	pDevExt->mapRegisterCount = 
		(MAX_DMA_LENGTH / PAGE_SIZE) + 1; 
 
	pDevExt->pDmaAdapter = 
		IoGetDmaAdapter( pDevObj, 
					 &dd, 
				      &pDevExt->mapRegisterCount); 
 
	// If the Adapter object can't be assigned, fail 
	if (pDevExt->pDmaAdapter == NULL) 
		return STATUS_INSUFFICIENT_RESOURCES; 
	else 
		return STATUS_SUCCESS; 
} 
 
 
//++ 
// 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\\DMASLAVE"); // for "Slave 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 
	pDevExt = (PDEVICE_EXTENSION)pfdo->DeviceExtension; 
	pDevExt->pDevice = pfdo;	// back pointer 
	pDevExt->DeviceNumber = ulDeviceNumber; 
	pDevExt->ustrDeviceName = devName; 
	pDevExt->pIntObj = NULL; 
	pDevExt->bInterruptExpected = FALSE; 
	pDevExt->state = Stopped; 
 
	// Pile this new fdo on top of the existing lower stack 
	pDevExt->pLowerDevice =		// 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->ustrSymLinkName = 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; 
} 
 
 
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 
} 
 
NTSTATUS PassDownPnP( IN PDEVICE_OBJECT pDO, 
					IN PIRP pIrp ) { 
	IoSkipCurrentIrpStackLocation( pIrp ); 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDO->DeviceExtension; 
	return IoCallDriver(pDevExt->pLowerDevice, pIrp); 
} 
 
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; 
 
	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: 
			pDevExt->IRQL = (KIRQL) 
				pPartialDescriptor->u.Interrupt.Level; 
			pDevExt->Vector =  
				pPartialDescriptor->u.Interrupt.Vector; 
			pDevExt->Affinity =  
				pPartialDescriptor->u.Interrupt.Affinity; 
			break; 
 
		case CmResourceTypeDma: 
			pDevExt->dmaChannel =  
				pPartialDescriptor->u.Dma.Channel; 
 
			// Now use the DMA resources to grab an Adapter object 
			GetDmaInfo( Isa, pDO ); 
			break; 
 
		case CmResourceTypePort: 
			pDevExt->portBase = (PUCHAR) 
				pPartialDescriptor->u.Port.Start.LowPart; 
			pDevExt->portLength = 
				pPartialDescriptor->u.Port.Length; 
			break; 
 
		case CmResourceTypeMemory: 
			// 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 (pDevExt->IRQL == 0 || 
		pDevExt->portBase == 0) 
		return STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT; 
 
	// Create & connect to an Interrupt object 
	status = 
		IoConnectInterrupt( 
			&pDevExt->pIntObj,	// the Interrupt object 
			Isr,			// our ISR 
			pDevExt,		// Service Context 
			NULL,			// no spin lock 
			pDevExt->Vector,			// vector 
			pDevExt->IRQL,		// DIRQL 
			pDevExt->IRQL,		// DIRQL 
			Latched,	// Latched or LevelSensitive 
			TRUE,			// Shared? 
			pDevExt->Affinity,		// processors in an MP set 
			FALSE );		// save FP registers? 
	if (!NT_SUCCESS(status)) { 
		return status; 
	} 
	 
	pDevExt->state = Started; 
 
	return PassDownPnP(pDO, pIrp); 
 
} 
 
NTSTATUS HandleStopDevice(	IN PDEVICE_OBJECT pDO, 
							IN PIRP pIrp ) { 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDO->DeviceExtension; 
 
	// Delete our Interrupt object 
	if (pDevExt->pIntObj) 
		IoDisconnectInterrupt( pDevExt->pIntObj ); 
	pDevExt->pIntObj = NULL; 
 
	// Delete the DMA Adapter object 
	pDevExt->pDmaAdapter->DmaOperations-> 
		FreeAdapterChannel( pDevExt->pDmaAdapter ); 
	pDevExt->pDmaAdapter = NULL; 
 
	pDevExt->state = Stopped; 
 
	return PassDownPnP(pDO, pIrp); 
} 
 
NTSTATUS HandleRemoveDevice(IN PDEVICE_OBJECT pDO, 
							IN PIRP pIrp ) { 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDO->DeviceExtension; 
 
	if (pDevExt->state == Started) { 
		// Woah!  we still have an interrupt object out there! 
		// Delete our Interrupt object 
		if (pDevExt->pIntObj) 
			IoDisconnectInterrupt( pDevExt->pIntObj ); 
	} 
 
		// This will yield the symbolic link name 
	UNICODE_STRING pLinkName = 
		pDevExt->ustrSymLinkName; 
	// ... which can now be deleted 
	IoDeleteSymbolicLink(&pLinkName); 
	 
	// Delete the device 
	IoDeleteDevice( pDO ); 
	 
	pDevExt->state = Removed; 
	return PassDownPnP( pDO, pIrp ); 
} 
 
//++ 
// Function:	DriverUnload 
// 
// Description: 
//		Stops & Deletes devices controlled by this driver. 
//		Stops interrupt processing (if any) 
//		Releases kernel resources consumed by driver 
// 
// Arguments: 
//		pDriverObject - Passed from I/O Manager 
// 
// Return value: 
//		None 
//-- 
 
VOID DriverUnload ( 
		IN PDRIVER_OBJECT	pDriverObject	) { 
 
} 
 
//++ 
// Function:	DispatchCreate 
// 
// Description: 
//		Handles call from Win32 CreateFile request 
//		For this driver, does nothing 
// 
// Arguments: 
//		pDevObj - Passed from I/O Manager 
//		pIrp - Passed from I/O Manager 
// 
// Return value: 
//		NTSTATUS - success or failure code 
//-- 
 
NTSTATUS DispatchCreate ( 
		IN PDEVICE_OBJECT	pDevObj, 
		IN PIRP				pIrp			) { 
 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDevObj->DeviceExtension; 
	NTSTATUS status = STATUS_SUCCESS; 
	if (pDevExt->state != Started) 
		status = STATUS_DEVICE_REMOVED; 
	 
	pIrp->IoStatus.Status = status; 
	pIrp->IoStatus.Information = 0;	// no bytes xfered 
	IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
	return status; 
} 
 
//++ 
// Function:	DispatchClose 
// 
// Description: 
//		Handles call from Win32 CreateHandle request 
//		For this driver, does nothing 
// 
// Arguments: 
//		pDevObj - Passed from I/O Manager 
//		pIrp - Passed from I/O Manager 
// 
// Return value: 
//		NTSTATUS - success or failure code 
//-- 
 
NTSTATUS DispatchClose ( 
		IN PDEVICE_OBJECT	pDevObj, 
		IN PIRP				pIrp			) { 
	 
	pIrp->IoStatus.Status = STATUS_SUCCESS; 
	pIrp->IoStatus.Information = 0; 
	IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
	return STATUS_SUCCESS; 
} 
 
//++ 
// Function:	DispatchCancel 
// 
// Description: 
//		Handles canceled IRP 
// 
// Arguments: 
//		pDevObj - Passed from I/O Manager 
//		pIrp - Passed from I/O Manager 
// 
// Return value: 
//		NTSTATUS - success or failuer code 
//-- 
 
VOID DispatchCancel ( 
		IN PDEVICE_OBJECT	pDevObj, 
		IN PIRP				pIrp			) { 
 
	// Just complete the IRP 
	pIrp->IoStatus.Status = STATUS_CANCELLED; 
	pIrp->IoStatus.Information = 0;	// bytes xfered 
	IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
	IoStartNextPacket( pDevObj, TRUE ); 
} 
 
 
//++ 
// Function: 
//		DispatchReadWrite 
// 
// Description: 
//		This function dispatches ReadFile 
//		and WriteFile requests from Win32 
// 
// Arguments: 
//		Pointer to Device object 
//		Pointer to IRP for this request 
// 
// Return Value: 
//		This function returns STATUS_XXX 
//-- 
static NTSTATUS DispatchReadWrite( 
	IN PDEVICE_OBJECT pDO, 
	IN PIRP pIrp 
	) 
{ 
	PIO_STACK_LOCATION pIrpStack =  
				IoGetCurrentIrpStackLocation( pIrp ); 
	// 
	// Check for zero-length transfers 
	// 
	if( pIrpStack->Parameters.Read.Length == 0 ) { 
		pIrp->IoStatus.Status = STATUS_SUCCESS; 
		pIrp->IoStatus.Information = 0; 
		IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
		return STATUS_SUCCESS; 
	} 
	 
	// 
	// Start device operation 
	// 
	IoMarkIrpPending( pIrp ); 
	IoStartPacket( pDO, pIrp, 0, NULL ); 
	return STATUS_PENDING; 
} 
 
BOOLEAN Isr ( 
			IN PKINTERRUPT pIntObj, 
			IN PVOID pServiceContext		) { 
 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pServiceContext; 
	PDEVICE_OBJECT pDevObj = pDevExt->pDevice; 
	PIRP pIrp = pDevObj->CurrentIrp; 
 
	// Check HW to see if interrupt for this driver 
	// If not, return FALSE immediately 
		//return FALSE; 
 
	// its our interrupt, deal with it 
	// Dismiss the interrupt in HW now 
 
	// Were we expecting an interrupt? 
	if (!pDevExt->bInterruptExpected) 
		return TRUE;	// nope 
	pDevExt->bInterruptExpected = FALSE; 
 
	// Do the rest of the work down 
	// at DISPATCH_LEVEL IRQL 
	IoRequestDpc(  
		pDevObj,  
		pIrp, 
		(PVOID)pDevExt ); 
 
	return TRUE; 
} 
 
//++ 
// Function: 
//		StartIo 
// 
// Description: 
//		This function is responsible initiating the 
//		actual data transfer. The ultimate result 
//		should be an interrupt from the device. 
// 
// Arguments: 
//		Pointer to the Device object 
//		Pointer to the IRP for this request 
// 
// Return Value: 
//		(None) 
//-- 
VOID StartIo( IN PDEVICE_OBJECT pDevObj, 
			IN PIRP pIrp ) { 
	PIO_STACK_LOCATION pStack = 
		IoGetCurrentIrpStackLocation( pIrp ); 
 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pDevObj->DeviceExtension; 
 
	// The IRP holds the MDL structure, already set up by 
	//   the I/O Manager because DO_DIRECT_IO flag is set 
	PMDL pMdl = pIrp->MdlAddress; 
 
	ULONG mapRegsNeeded; 
	NTSTATUS status; 
 
	pDevExt->bWriting = FALSE;	// assume read operation 
 
	switch ( pStack->MajorFunction ) { 
	case IRP_MJ_WRITE: 
		pDevExt->bWriting = TRUE;	// bad assumption 
	case IRP_MJ_READ: 
		pDevExt->bytesRequested =  
			MmGetMdlByteCount( pMdl ); 
		pDevExt->transferVA = (PUCHAR) 
			MmGetMdlVirtualAddress( pMdl ); 
		pDevExt->bytesRemaining = 
		pDevExt->transferSize =  
			pDevExt->bytesRequested; 
 
		mapRegsNeeded = 
			ADDRESS_AND_SIZE_TO_SPAN_PAGES( 
				pDevExt->transferVA, 
				pDevExt->transferSize ); 
 
		if (mapRegsNeeded > pDevExt->mapRegisterCount) { 
			mapRegsNeeded = pDevExt->mapRegisterCount; 
			pDevExt->transferSize = 
				mapRegsNeeded * PAGE_SIZE - 
				MmGetMdlByteOffset( pMdl ); 
		} 
 
		status = pDevExt->pDmaAdapter->DmaOperations-> 
				AllocateAdapterChannel( 
					pDevExt->pDmaAdapter, 
					pDevObj, 
					mapRegsNeeded, 
					AdapterControl, 
					pDevExt ); 
 
		if (!NT_SUCCESS( status )) { 
			// fail the IRP & don't continue with it 
			pIrp->IoStatus.Status = status; 
			// Show no bytes transferred 
			pIrp->IoStatus.Information = 0; 
			IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
			IoStartNextPacket( pDevObj, FALSE); 
		} 
		break;	// nice job - AdapterControl takes it 
				//			from here on 
 
	default: 
		// Shouldn't be here - ditch this strange IRP 
		pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; 
		pIrp->IoStatus.Information = 0; 
		IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
		IoStartNextPacket( pDevObj, FALSE ); 
	} 
} 
 
//++ 
// Function: 
//		AdapterControl 
// 
// Description: 
//		This function sets up the first 
//		part of a split transfer and 
//		starts the DMA device. 
// 
// Arguments: 
//		Device object 
//		Current Irp 
//		Map register base handle 
//		Pointer to Device Extension 
// 
// Return Value: 
//		KeepObject 
//-- 
IO_ALLOCATION_ACTION AdapterControl( 
					IN PDEVICE_OBJECT pDevObj, 
					IN PIRP pIrp, 
					IN PVOID MapRegisterBase, 
					IN PVOID pContext ) { 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
								pContext; 
 
	// Save the handle to the mapping register set 
	pDevExt->mapRegisterBase = MapRegisterBase; 
 
	// Flush the CPU cache(s),  
	//	if necessary on this platform... 
	KeFlushIoBuffers( pIrp->MdlAddress, 
				   !pDevExt->bWriting, 	// inverted 
				   TRUE );			// yes DMA 
 
	pDevExt->pDmaAdapter->DmaOperations-> 
		MapTransfer( pDevExt->pDmaAdapter, 
				   pIrp->MdlAddress, 
				   MapRegisterBase,	 
				   pDevExt->transferVA, 
				   &pDevExt->transferSize, 
				   pDevExt->bWriting ); 
 
	// Start the device 
	StartTransfer( pDevExt ); 
 
	return KeepObject; 
} 
//++ 
// Function: 
//		DpcForIsr 
// 
// Description: 
//		This function performs the low-IRQL 
//		post-processing of I/O requests 
// 
// Arguments: 
//		Pointer to a DPC object 
//		Pointer to the Device object 
//		Pointer to the IRP for this request 
//		Pointer to the Device Extension 
// 
// Return Value: 
//		(None) 
//-- 
VOID DpcForIsr(IN PKDPC pDpc, 
			IN PDEVICE_OBJECT pDevObj, 
			IN PIRP pIrp, 
			IN PVOID pContext ) { 
 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) 
		pContext; 
	ULONG mapRegsNeeded; 
	PMDL pMdl = pIrp->MdlAddress; 
 
	// Flush the Apapter buffer to system RAM or device. 
	pDevExt->pDmaAdapter->DmaOperations-> 
		FlushAdapterBuffers( pDevExt->pDmaAdapter, 
						 pMdl, 
						 pDevExt->mapRegisterBase,	 
						 pDevExt->transferVA, 
						 pDevExt->transferSize, 
						 pDevExt->bWriting ); 
 
	// If the device is reporting errors, fail the IRP 
	if (DEVICE_FAIL( pDevExt )) { 
		// An error occurred, the DMA channel is now free 
		pDevExt->pDmaAdapter->DmaOperations-> 
			FreeAdapterChannel( pDevExt->pDmaAdapter ); 
		pIrp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; 
		pIrp->IoStatus.Information = 
			pDevExt->bytesRequested -  
			pDevExt->bytesRemaining; 
		IoCompleteRequest( pIrp, IO_NO_INCREMENT ); 
		IoStartNextPacket( pDevObj, FALSE); 
	} 
 
	// Device had no errors, see if another partial needed 
	pDevExt->bytesRemaining -= pDevExt->transferSize; 
	if (pDevExt->bytesRemaining > 0) { 
		// Another partial transfer needed 
		// Update the transferVA and try to finish it 
		pDevExt->transferVA += pDevExt->transferSize; 
		pDevExt->transferSize = pDevExt->bytesRemaining; 
		mapRegsNeeded = 
			ADDRESS_AND_SIZE_TO_SPAN_PAGES( 
				pDevExt->transferVA, 
				pDevExt->transferSize ); 
		// If it still doesn't fit in one swipe, 
		//	cut back the expectation 
		if (mapRegsNeeded > pDevExt->mapRegisterCount) { 
			mapRegsNeeded = pDevExt->mapRegisterCount; 
			pDevExt->transferSize = 
				mapRegsNeeded * PAGE_SIZE - 
				BYTE_OFFSET( pDevExt->transferVA ); 
		} 
 
		// Now set up the mapping registers for another 
		pDevExt->pDmaAdapter->DmaOperations-> 
			MapTransfer( pDevExt->pDmaAdapter, 
					   pMdl, 
					   pDevExt->mapRegisterBase, 
					   pDevExt->transferVA, 
					   &pDevExt->transferSize, 
					   pDevExt->bWriting ); 
 
		// And start the device 
		StartTransfer( pDevExt ); 
	} else { 
		// Entire transfer has now completed - 
		// Free the DMA channel for another device 
		pDevExt->pDmaAdapter->DmaOperations-> 
			FreeAdapterChannel( pDevExt->pDmaAdapter ); 
		// And complete the IRP in glory 
		pIrp->IoStatus.Status = STATUS_SUCCESS; 
		pIrp->IoStatus.Information =  
			pDevExt->bytesRequested; 
 
		// Choose a priority boost appropriate for device 
		IoCompleteRequest( pIrp, IO_DISK_INCREMENT ); 
		IoStartNextPacket( pDevObj, FALSE ); 
	} 
} 
 
VOID StartTransfer( IN PDEVICE_EXTENSION pDevExt ) { 
	// This place holder routine would hold the code 
	// necessary to manipulate the slave DMA device 
	// so that it starts the transfer of data. 
}