基于Windows8与Visual Studio11开发第一个Wdm驱动程序

WDM是英文Windows Driver Model(WDM)的缩写,中文意思是“视窗驱动程序模块”,多使用在一些声卡的驱动程序上。

 

Windows驱动程序模型

  WDM是WINDOWS2000认证的驱动程序,WIN2000由NT发展而来,所以对于设备的支持功能有限,同时为了最大限度的保障稳定性,所以推崇WDM驱动,但同时WDM驱动也就是功能最少的。因为设备厂商开发进度以及微软的态度,可能会禁止某些功能,所以像8738和PCI128D这类声卡在WIN2000中都是不能驱动四声道的。

  Windows驱动程序模型(Windows Driver Model,WDM)是Windows98和Windows2000使用的新的驱动程序设计规范。使用WDM使得硬件驱动程序更加稳定,让操作系统对硬件更加有效地控制硬件。除了定义一个驱动程序与操作系统连接的标准接口以外,WDM也指明了驱动程序应该采用的更加模块化的设计。

WDM特性

  动态构造WDM驱动程序堆栈是实现即插即用设备支持的关键

  模块化的WDM体系结构中灵活统一的接口,使操作系统可以动态地配置不同的驱动程序模块来支持特定的设备。一个典型的驱动程序堆栈由通用设备、协议及特定协议和特定总线的微型驱动程序联接的总线类驱动程序构成。动态构造WDM驱动程序堆栈是实现即插即用设备支持的关键。

  WDM 流媒体应用程序

  对于流媒体应用程序,WDM在核心态提供了快速反应的接口来处理I/O流。WDM的流接口是通过标准的WDM类接口提供出的。

  WDM支持USB、IEEE 1394、ACPI等全新的硬件标准

  WDM支持USB、IEEE 1394、ACPI等全新的硬件标准。而且以往在两个平台上同时运行时需要编写两个截然不同的驱动程序,现在只需要编写一个WDM驱动程序就可以了。

驱动程序体系  在Windows2000中包括WDM不但扩展了支持设备的数量,也给微软的主要网络操作系统带来了即插即用能力。尽管Windows2000并不能支持市面上所有的硬件,它的硬件兼容性列表(Hardware Compatibility list,HCL)已经远远大于WindowsNT4.0的Windows2000的驱动程序体系包括类驱动程序,总线驱动程序,迷你驱动程序和过滤器驱动程序。

类驱动

  类驱动是设计来控制一个硬件类别的常用、通用和基本的功能和能力的驱动程序。硬件类别有很多,例如键盘,鼠标,显示器,控制卡,视频设备,CDROM,软驱,硬盘等等。

总线驱动程序

  总线驱动程序是用来连接设备和计算机的控制器的驱动程序。
  总线类型有周边元件扩展接口(Peripheral Component Interconnect, PCI),通用串行总线(Universal Serial Bus,USB),IEEE(Instituteof Electricaland Electronics Engineers,国际电子电气工程师协会)1394,小型计算机系统接口(Small Computer Systems Interface,SCSI),PC卡(PCMCIA)工业标准体系(Industrial Standard Architecture,ISA),扩展工业标准体系(Extended Industrial Standard Architecture,EISA),增强型集成设备电子接口(Enhanced Integrated Device Electronics,EIDE),扩展能力端口(Extended Capabilities Port,ECP)和COM端口等等。

迷你驱动程序

  Windows2000包括控制这些控制器连接的驱动程序。总线设备的驱动程序既有标准类驱动程序,也有迷你驱动程序和过滤器驱动程序的厂商特定实现。
  迷你驱动程序与类驱动和总线驱动相接口,提供操作系统对硬件的控制和存取,它定义了硬件和类驱动间的接口,并提供对类驱动不提供的特定或专有的功能和能力的控制机制。

过滤器驱动程序

  过滤器驱动程序用于对设备能力限制或授予更广泛的存取,通过安装在类驱动程序的上层或下层实现。过滤器经常用来防止给设备发送不支持的命令或者用来将命令由类驱动程序的语法翻译为硬件设备要求的语法。
  Windows Driver Model(WDM)架构有利于提升音效处理的效能:原来需要在影像、音效设备上的程序代码,现在可以直接包含在操作系统中,因此能更快地与硬件同步更新。

Windows2000支持三种类驱动程序

数据流类驱动程序

  支持数据流的高带宽传输,得到更快的数据处理速度。这个类驱动程序经常与音频端口类驱动程序结合使用,以支持实时的视频和音频。该类驱动也负责多任务时序,直接内存存取(DMA),内存优化,即插即用和I/O缓冲区管理。

输入设备类驱动程序

  管理多种总线(如USB)间的数据与指令语法翻译。大多数时候,本类驱动控制由用户交互接口传来的数据,如键盘,鼠标和游戏杆等。

音频端口类驱动程序

  支持多媒体的实时流式回放。它依靠数据流类驱动程序,直接控制硬件,该驱动程序主要使用COM1(I/O地址03F8-03FF)COM2(I/O地址02F8-02FF)</P

Win32 Driver Model Win32驱动程序模型

  WDM(Win32 Driver Model),即Win32驱动程序模型,是Microsoft力推的全新驱动程序模式,旨在通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低所必须开发的驱动程序的数量和复杂性。

微型驱动程序结构

  除了通用的平台服务和扩展外,WDM还实现了一个模块化的、分层次类型的微型驱动程序结构。类型驱动程序实现了支持通用总线、协议或设备类所需的功能性接口。类型驱动程序的一般特性是为逻辑设备的命令设置、协议和代码重用所需的总线接口实现标准化提供必要的条件。

  基本上,WDM规范依靠一个标准化的类驱动程序来控制一类硬件的最常用和基本的功能。Windows2000为每一类硬件包括一个本地类驱动程序,然后,对应于某个特定厂商或者硬件型号的迷你驱动程序加入在类驱动中未包括的特殊或定制功能。厂商还可以更进一步地加入过滤器驱动程序来提供在类驱动的上层或底层进行的微调特性,以此来提供驱动程序本身和由硬件设备执行的任务最大的效率。在绝大多数情况下,这种设计将得到各方面均兼容于Windows2000的驱动程序,提供更好的性能,消耗更少的系统资源,并且对驱动程序大小的缩减经常达到90%。注意此处大小指的只是厂商/型号特定驱动程序,而不包括本地类驱动程序。

 

Win32 Driver Model Win32驱动程序模型  WDM(Win32 Driver Model),即Win32驱动程序模型,是Microsoft力推的全新驱动程序模式,旨在通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低所必须开发的驱动程序的数量和复杂性。

微型驱动程序结构

  除了通用的平台服务和扩展外,WDM还实现了一个模块化的、分层次类型的微型驱动程序结构。类型驱动程序实现了支持通用总线、协议或设备类所需的功能性接口。类型驱动程序的一般特性是为逻辑设备的命令设置、协议和代码重用所需的总线接口实现标准化提供必要的条件。

  基本上,WDM规范依靠一个标准化的类驱动程序来控制一类硬件的最常用和基本的功能。Windows2000为每一类硬件包括一个本地类驱动程序,然后,对应于某个特定厂商或者硬件型号的迷你驱动程序加入在类驱动中未包括的特殊或定制功能。厂商还可以更进一步地加入过滤器驱动程序来提供在类驱动的上层或底层进行的微调特性,以此来提供驱动程序本身和由硬件设备执行的任务最大的效率。在绝大多数情况下,这种设计将得到各方面均兼容于Windows2000的驱动程序,提供更好的性能,消耗更少的系统资源,并且对驱动程序大小的缩减经常达到90%。注意此处大小指的只是厂商/型号特定驱动程序,而不包括本地类驱动程序。

 

下面我们来亲自实践基于Windows8与Visual Studio11的开发

Visual studio11与Windows8带来格外不同的新体验

 

1.启动Vs11

基于Windows8与Visual Studio11开发第一个Wdm驱动程序_第1张图片

2.看见满目的驱动开发模板

基于Windows8与Visual Studio11开发第一个Wdm驱动程序_第2张图片

3.选择Wdm驱动

基于Windows8与Visual Studio11开发第一个Wdm驱动程序_第3张图片

4.创建成功以后

看见驱动开发模式的两种

基于Windows8与Visual Studio11开发第一个Wdm驱动程序_第4张图片

 

5.编译Wdm驱动,并查看inf文件

;
; MyDriverMVP.inf
;

[Version]
Signature="$WINDOWS NT$"
Class=
ClassGuid=
Provider=
DriverVer=
CatalogFile=

[DestinationDirs]
DefaultDestDir = 12


[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]


[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[Standard.NT$ARCH$]


[Strings]
ManufacturerName=""
ClassName=""
DiskName="MyDriverMVP Source Disk"

插入下列代码,执行WdmIo查询

#include "wdmIo.h"
#include "Ioctl.h"

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

NTSTATUS StoreCmds( PUCHAR* pCmds, ULONG* pCmdsLen, ULONG len, PVOID Buffer);
NTSTATUS RetrieveResources(IN PWDMIO_DEVICE_EXTENSION dx,
						   IN PCM_RESOURCE_LIST AllocatedResourcesTranslated);
void WriteByte( IN PWDMIO_DEVICE_EXTENSION dx, IN ULONG offset, IN UCHAR byte);
UCHAR ReadByte( IN PWDMIO_DEVICE_EXTENSION dx, IN ULONG offset);
BOOLEAN InterruptHandler(IN PKINTERRUPT Interrupt, IN PWDMIO_DEVICE_EXTENSION dx);

BOOLEAN RunCmdsSynch( IN PDEVICE_OBJECT fdo);
BOOLEAN RunCmds( IN PDEVICE_OBJECT fdo, IN bool CanTrace);
bool ProcessCmds(	IN PWDMIO_DEVICE_EXTENSION dx,
					IN PUCHAR Buffer, IN ULONG len,
					OUT PUCHAR OutBuffer, IN ULONG outlen,
					IN bool CanTrace
				  );

BOOLEAN RunWriteCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx);
BOOLEAN RunReadCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx);
BOOLEAN RunStartReadCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx);

/////////////////////////////////////////////////////////////////////////////
//	StartDevice:	Start the device

NTSTATUS StartDevice( IN PWDMIO_DEVICE_EXTENSION dx, IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
{
	if( dx->GotResources)
		return STATUS_SUCCESS;

	NTSTATUS status = RetrieveResources(dx,AllocatedResourcesTranslated);
	if( !NT_SUCCESS(status))
		return status;

	// Map memory
	if( dx->PortNeedsMapping)
	{
		dx->PortBase = (PUCHAR)MmMapIoSpace( dx->PortStartAddress, dx->PortLength, MmNonCached);
		if( !dx->PortBase)
			return STATUS_NO_MEMORY;
	}
	else
		dx->PortBase = (PUCHAR)dx->PortStartAddress.LowPart;

	// Reconnect to interrupt if necessary
	if( dx->ConnectedToInterrupt)
	{
		if( !dx->GotInterrupt)
			status = STATUS_INSUFFICIENT_RESOURCES;
		else
			status = IoConnectInterrupt( &dx->InterruptObject, (PKSERVICE_ROUTINE)InterruptHandler,
							dx, NULL, dx->Vector, dx->Irql, dx->Irql, dx->Mode, FALSE, dx->Affinity, FALSE);
		if( !NT_SUCCESS(status))
		{
			dx->ConnectedToInterrupt = false;
			return status;
		}
	}

	// Device is now started
	dx->GotResources = true;
	
	return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////
//	RetrieveResources:	Get resources from given list.
//						Must at least have a port or memory given.
//						Save any given interrupt as well.

NTSTATUS RetrieveResources( IN PWDMIO_DEVICE_EXTENSION dx, IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
{
	if( AllocatedResourcesTranslated==NULL ||
		AllocatedResourcesTranslated->Count==0)
	{
		DebugPrintMsg("RetrieveResources: No allocated translated resources");
		return STATUS_DEVICE_CONFIGURATION_ERROR;
	}

	// Get to actual resources
	PCM_PARTIAL_RESOURCE_LIST list = &AllocatedResourcesTranslated->List[0].PartialResourceList;
	PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list->PartialDescriptors;
	ULONG NumResources = list->Count;

	DebugPrint("RetrieveResources: %d resource lists %d resources", AllocatedResourcesTranslated->Count, NumResources);

	bool GotError = false;

	// Clear dx
	dx->GotInterrupt = false;
	dx->GotPortOrMemory = false;

	// Go through each allocated resource
	for( ULONG resno=0; resno<NumResources; resno++,resource++)
	{
		switch( resource->Type)
		{
		case CmResourceTypePort:
			if( dx->GotPortOrMemory) { GotError = true; break; }
			dx->GotPortOrMemory = true;
			dx->PortStartAddress = resource->u.Port.Start;
			dx->PortLength = resource->u.Port.Length;
			dx->PortNeedsMapping = (resource->Flags & CM_RESOURCE_PORT_IO)==0;
			dx->PortInIOSpace = !dx->PortNeedsMapping;
			DebugPrint("RetrieveResources: Port %L Length %d NeedsMapping %d",
							dx->PortStartAddress,
							dx->PortLength, dx->PortNeedsMapping);
			break;

		case CmResourceTypeInterrupt:
			dx->GotInterrupt = true;
			dx->Irql = (KIRQL)resource->u.Interrupt.Level;
			dx->Vector = resource->u.Interrupt.Vector;
			dx->Affinity = resource->u.Interrupt.Affinity;
			dx->Mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED)
						? Latched : LevelSensitive;
			DebugPrint("RetrieveResources: Interrupt vector %x IRQL %d Affinity %d Mode %d",
							dx->Vector, dx->Irql, dx->Affinity, dx->Mode);
			break;

		case CmResourceTypeMemory:
			if( dx->GotPortOrMemory) { GotError = true; break; }
			dx->GotPortOrMemory = true;
			dx->PortStartAddress = resource->u.Memory.Start;
			dx->PortLength = resource->u.Memory.Length;
			dx->PortInIOSpace = false;
			dx->PortNeedsMapping = true;
			DebugPrint("RetrieveResources: Memory %L Length %d",
							dx->PortStartAddress, dx->PortLength);
			break;

		case CmResourceTypeDma:
		case CmResourceTypeDeviceSpecific:
		case CmResourceTypeBusNumber:
		default:
			DebugPrint("RetrieveResources: Unrecognised resource type %d", resource->Type);
			GotError = true;
			break;
		}
	}

	// Check we've got the resources we need
	if( GotError || !dx->GotPortOrMemory /*|| !GotInterrupt*/)
		return STATUS_DEVICE_CONFIGURATION_ERROR;

	return STATUS_SUCCESS;
}
	
/////////////////////////////////////////////////////////////////////////////
//	StopDevice:	Stop device

VOID StopDevice( IN PWDMIO_DEVICE_EXTENSION dx)
{
	DebugPrintMsg("StopDevice");
	if( !dx->GotResources)
		return;
	dx->GotResources = false;

	// Unmap memory
	if (dx->PortNeedsMapping)
		MmUnmapIoSpace( (PVOID)dx->PortBase, dx->PortLength);

	// Disconnect from interrupt
	if( dx->ConnectedToInterrupt)
	{
		IoDisconnectInterrupt( dx->InterruptObject);
		//dx->ConnectedToInterrupt = false;	Don't do this.  So we know to reconnect
	}
}

/////////////////////////////////////////////////////////////////////////////
//	WriteByte:	Output a byte
//				Silently ignores request if register out of range
//	Don't call DebugPrint as may be called at DIRQL

void WriteByte( IN PWDMIO_DEVICE_EXTENSION dx, IN ULONG offset, IN UCHAR byte)
{
	if( offset>=dx->PortLength) return;
	PUCHAR Port = dx->PortBase+offset;
	if( dx->PortInIOSpace)
		WRITE_PORT_UCHAR( Port, byte);
	else
		WRITE_REGISTER_UCHAR( Port, byte);
}

/////////////////////////////////////////////////////////////////////////////
//	ReadByte:	Input a byte
//				Silently ignores request if register out of range
//	Don't call DebugPrint as may be called at DIRQL

UCHAR ReadByte( IN PWDMIO_DEVICE_EXTENSION dx, IN ULONG offset)
{
	if( offset>=dx->PortLength) return 0;
	PUCHAR Port = dx->PortBase+offset;
	UCHAR b;
	if( dx->PortInIOSpace)
		b = READ_PORT_UCHAR(Port);
	else
		b = READ_REGISTER_UCHAR(Port);
	return b;
}

/////////////////////////////////////////////////////////////////////////////
//	WdmIoStartIo:	Process an IRP from the head of the device IRP queue.
//				1	Only IOCTL, Read and Write IRPs get here.
//				2	The IRP is either completed here, or completed once the
//					interrupt driven read or writes completes, times out
//					or is cancelled.
//				3	Note that IRP may be cancelled at any time during this
//					processing, so we check IRP's Cancel flag when appropriate.
//				4	The Irp parameter is equal to fdo->CurrentIrp until it
//					is completed and IoStartNextPacket called.

VOID WdmIoStartIo( IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
	PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
	PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
	PUCHAR Buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;

	// Zero the output count
	dx->CmdOutputCount = 0;
	dx->ConnectIntQueued = false;

	DebugPrint( "WdmIoStartIo: %x %I",Irp,Irp);

	// Stop the 1 second timer if necessary
	if( dx->StopTimer)
	{
		IoStopTimer(fdo);
		dx->StopTimer = false;
	}

	NTSTATUS status = STATUS_SUCCESS;

	// Switch on the IRP major function code
	switch( IrpStack->MajorFunction)
	{
	/////////////////////////////////////////////////////////////////////////
	case IRP_MJ_DEVICE_CONTROL:
	{
		ULONG ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
		ULONG InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
		ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
		switch( ControlCode)
		{
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case IOCTL_PHDIO_RUN_CMDS:
			DebugPrint( "WdmIoStartIo: Run Cmds %s", dx->ConnectedToInterrupt?"(synchronised)":"");
			// If necessary make a temp buffer for the output data
			dx->RunCmdsOutBuffer = NULL;
			if( OutputLength>0)
			{
				dx->RunCmdsOutBuffer = (PUCHAR)ExAllocatePool(NonPagedPool,OutputLength);
				if( dx->RunCmdsOutBuffer==NULL)
				{
					status = STATUS_UNSUCCESSFUL;
					break;
				}
			}
			// Run the commands, synchronized with interrupt if necessary
			if( dx->ConnectedToInterrupt)
			{
				if( !KeSynchronizeExecution( dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)RunCmdsSynch, (PVOID)fdo))
					status = STATUS_UNSUCCESSFUL;
			}
			else
			{
				if( !RunCmds(fdo,true))
					status = STATUS_UNSUCCESSFUL;

				// Return straightaway if ConnectIntWQI queued
				if( dx->ConnectIntQueued) return;
			}
			// Copy temp output buffer back into shared IOCTL buffer
			if( dx->RunCmdsOutBuffer!=NULL)
			{
				RtlCopyMemory( Buffer, dx->RunCmdsOutBuffer, OutputLength);
				ExFreePool(dx->RunCmdsOutBuffer);
				dx->RunCmdsOutBuffer = NULL;
			}
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case IOCTL_PHDIO_CMDS_FOR_READ:
			DebugPrintMsg( "WdmIoStartIo: Store cmds for read");
			status = StoreCmds( &dx->ReadCmds, &dx->ReadCmdsLen, InputLength, Buffer);
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case IOCTL_PHDIO_CMDS_FOR_READ_START:
			DebugPrintMsg( "WdmIoStartIo: Store cmds for read start");
			status = StoreCmds( &dx->StartReadCmds, &dx->StartReadCmdsLen, InputLength, Buffer);
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case IOCTL_PHDIO_CMDS_FOR_WRITE:
			DebugPrintMsg( "WdmIoStartIo: Store cmds for write");
			status = StoreCmds( &dx->WriteCmds, &dx->WriteCmdsLen, InputLength, Buffer);
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case IOCTL_PHDIO_GET_RW_RESULTS:
#if DBG
			if( dx->TxCmdOutputCount>sizeof(dx->TxResult))
			{
				DebugPrint( "WdmIoStartIo: Get RW Results: dx->TxCmdOutputCount too big at %d",dx->CmdOutputCount);
				dx->CmdOutputCount = sizeof(dx->TxResult);
			}
#endif
			// Copy cmd output first
			dx->CmdOutputCount = dx->TxCmdOutputCount;
			if( dx->CmdOutputCount>OutputLength)
				 dx->CmdOutputCount = OutputLength;
			RtlCopyMemory( Buffer, dx->TxResult, dx->CmdOutputCount);
			// Then add on last interrupt reg value
			if( dx->CmdOutputCount+1<=OutputLength)
				Buffer[dx->CmdOutputCount++] = dx->TxLastIntReg;

			DebugPrint( "WdmIoStartIo: Get RW Results: %d bytes",dx->CmdOutputCount);
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		default:
			status = STATUS_NOT_SUPPORTED;
		}
		break;
	}

	/////////////////////////////////////////////////////////////////////////
	case IRP_MJ_WRITE:
		if( dx->WriteCmds==NULL || !dx->ConnectedToInterrupt)
		{
			status = STATUS_INVALID_DEVICE_REQUEST;
			break;
		}

		// Store transfer details
		dx->TxTotal = IrpStack->Parameters.Write.Length;
		dx->TxLeft = dx->TxTotal;
		dx->TxBuffer = (PUCHAR)Buffer;
		dx->TxStatus = STATUS_SUCCESS;
		dx->TxIsWrite = true;
		RtlZeroMemory( dx->TxResult, sizeof(dx->TxResult));
		DebugPrint( "WdmIoStartIo: Write %d bytes: %*s",dx->TxTotal,dx->TxTotal,dx->TxBuffer);

		// Start timeout timer
		dx->Timeout = dx->SetTimeout+1;
		IoStartTimer(fdo);

		// Send first value
		if( KeSynchronizeExecution( dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)RunWriteCmdsSynch, (PVOID)dx))
		{
			status = STATUS_UNSUCCESSFUL;
			break;
		}
		return;

	/////////////////////////////////////////////////////////////////////////
	case IRP_MJ_READ:
		if( dx->ReadCmds==NULL || !dx->ConnectedToInterrupt)
		{
			status = STATUS_INVALID_DEVICE_REQUEST;
			break;
		}

		// Store transfer details
		dx->TxTotal = IrpStack->Parameters.Read.Length;
		dx->TxLeft = dx->TxTotal;
		dx->TxBuffer = (PUCHAR)Buffer;
		dx->TxStatus = STATUS_SUCCESS;
		dx->TxIsWrite = false;
		RtlZeroMemory( dx->TxResult, sizeof(dx->TxResult));
		DebugPrint( "WdmIoStartIo: Read %d bytes: %*s",dx->TxTotal,dx->TxTotal,dx->TxBuffer);

		// Start timeout timer
		dx->Timeout = dx->SetTimeout;
		if( dx->Timeout<=0) dx->Timeout = 10;
		IoStartTimer(fdo);

		// Run StartReadCmds if available
		if( dx->StartReadCmds!=NULL)
		{
			DebugPrintMsg( "WdmIoStartIo: Read: Running start read commands");
			if( KeSynchronizeExecution( dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)RunStartReadCmdsSynch, (PVOID)dx))
			{
				status = STATUS_UNSUCCESSFUL;
				break;
			}
		}
		return;
	/////////////////////////////////////////////////////////////////////////
	default:
		status = STATUS_NOT_SUPPORTED;
		break;
	}

	/////////////////////////////////////////////////////////////////////////
	// Complete this IRP

	if( Irp->Cancel) status = STATUS_CANCELLED;

	// Remove cancel routine
	KIRQL OldIrql;
	IoAcquireCancelSpinLock( &OldIrql);
	IoSetCancelRoutine( Irp, NULL);
	IoReleaseCancelSpinLock(OldIrql);

	// Unlock device, complete IRP and start next
	UnlockDevice(dx);
	DebugPrint( "WdmIoStartIo: CmdOutputCount %d", dx->CmdOutputCount);
	CompleteIrp(Irp, status, dx->CmdOutputCount);
	IoStartNextPacket( fdo, TRUE);
}

/////////////////////////////////////////////////////////////////////////////
//	StoreCmds:	Copy commands from IOCTL input buffer to new buffer (in dx)

NTSTATUS StoreCmds( PUCHAR* pCmds, ULONG* pCmdsLen, ULONG len, PVOID Buffer)
{
	// Save commands for later processing
	if( len==0) return STATUS_INVALID_PARAMETER;
	FreeIfAllocated(*pCmds);
	*pCmds = (PUCHAR)ExAllocatePool( NonPagedPool, len);
	if( *pCmds==NULL)
		return STATUS_NO_MEMORY;
	RtlCopyMemory( *pCmds, Buffer, len);
	*pCmdsLen = len;
	return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////
//	RunCmds:		Run commands for IOCTL_WDMIO_RUN_CMDS
//	RunCmdsSynch:	RunCmds called as a Critical Section routine at DIRQL
//
//	Only do trace output if not run as a Critical Section routine.
//
//	Runs at DISPATCH_LEVEL or DIRQL
//	Return	TRUE if commands ran (successfully or not)

BOOLEAN RunCmdsSynch( IN PDEVICE_OBJECT fdo)
{
	return RunCmds( fdo, false);
}

BOOLEAN RunCmds( IN PDEVICE_OBJECT fdo, IN bool CanTrace)
{
	PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
	PIRP Irp = fdo->CurrentIrp;
	PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
	ULONG InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
	ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
	PUCHAR Buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;

	ProcessCmds( dx, Buffer, InputLength, dx->RunCmdsOutBuffer, OutputLength, CanTrace);

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//	RunWriteCmdsSynch:	Run stored write commands
//						ProcessCmds output sent to dx->TxResult
//	Runs at DIRQL
//	Return TRUE if bytes all transferred (or in error)

BOOLEAN RunWriteCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->TxLeft==0) return TRUE;

	dx->CmdOutputCount = 0;
	BOOLEAN rv = ProcessCmds( dx, dx->WriteCmds, dx->WriteCmdsLen, dx->TxResult, sizeof(dx->TxResult), false);
	dx->TxCmdOutputCount = dx->CmdOutputCount;
	if( !rv)
	{
		dx->TxStatus = STATUS_UNSUCCESSFUL;
		return TRUE;
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//	RunReadCmdsSynch:	Run stored read commands
//						ProcessCmds output sent to dx->TxResult
//	Runs at DIRQL
//	Return TRUE if bytes all transferred (or in error)

BOOLEAN RunReadCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->TxLeft==0) return TRUE;

	dx->CmdOutputCount = 0;
	BOOLEAN rv = ProcessCmds( dx, dx->ReadCmds, dx->ReadCmdsLen, dx->TxResult, sizeof(dx->TxResult), false);
	dx->TxCmdOutputCount = dx->CmdOutputCount;
	if( !rv)
	{
		dx->TxStatus = STATUS_UNSUCCESSFUL;
		return TRUE;
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//	RunStartReadCmdsSynch:	Run stored start read commands
//							ProcessCmds output sent to dx->TxResult
//	Runs at DIRQL
//	Return TRUE if bytes all transferred (or in error)

BOOLEAN RunStartReadCmdsSynch( IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->TxLeft==0) return TRUE;

	dx->CmdOutputCount = 0;
	BOOLEAN rv = ProcessCmds( dx, dx->StartReadCmds, dx->StartReadCmdsLen, dx->TxResult, sizeof(dx->TxResult), false);
	dx->TxCmdOutputCount = dx->CmdOutputCount;
	if( !rv)
	{
		dx->TxStatus = STATUS_UNSUCCESSFUL;
		return TRUE;
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//	Command names for DebugPrint

#if DODEBUGPRINT
static char* PHD_IO_CMDS[] =
{
	"PHDIO_OR",
	"PHDIO_AND",
	"PHDIO_XOR",

	"PHDIO_WRITE",
	"PHDIO_READ",

	"PHDIO_DELAY",

	"PHDIO_WRITES",
	"PHDIO_READS",

	"PHDIO_IRQ_CONNECT",

	"PHDIO_TIMEOUT",
	"PHDIO_WRITE_NEXT",
	"PHDIO_READ_NEXT",
};
static int NUM_PHD_IO_CMDS = sizeof(PHD_IO_CMDS)/sizeof(char*);
#endif

/////////////////////////////////////////////////////////////////////////////
//	Useful macros for ProcessCmds
//	GetUChar:			Get next UCHAR in newly declared variable (if available)
//	GetUCharNoDeclare:	Get next UCHAR in existing variable (if available)
//	SetUChar:			Store UCHAR in output buffer (if there's room)

#define GetUChar(var) if( ByteNo>=len) { FailCode=PHDIO_NO_CMD_PARAMS; goto fail; } UCHAR var = *Buffer++; ByteNo++
#define GetUCharNoDeclare(var) if( ByteNo>=len) { FailCode=PHDIO_NO_CMD_PARAMS; goto fail; }; var = *Buffer++; ByteNo++
#define SetUChar(b) if( OutByteNo>=outlen) { FailCode=PHDIO_NO_OUTPUT_ROOM; goto fail; } *OutBuffer++ = (b); OutByteNo++; dx->CmdOutputCount++

/////////////////////////////////////////////////////////////////////////////
//	ProcessCmds:	Process commands in given buffer.
//					If output buffer given, first word has result code and
//					second word has index into input buffer of problem cmd.
//					Actual output values are stored after this.
//
//	Currently can only process UCHAR size commands.
//	Only produce DebugPrint output if CanTrace is true
//
//	return false if there's a problem

bool ProcessCmds(	IN PWDMIO_DEVICE_EXTENSION dx,
					IN PUCHAR Buffer, IN ULONG len,
					OUT PUCHAR OutBuffer, IN ULONG outlen,
					IN bool CanTrace
				  )
{
	if( CanTrace) { DebugPrint( "ProcessCmds. input:%d output:%d", len, outlen); }

	PIRP Irp = dx->fdo->CurrentIrp;

	// Zero first 2 words of output buffer (if available)
	PUSHORT Result = (PUSHORT)OutBuffer;
	const int ResultLen = 2*sizeof(USHORT);
	if( outlen<ResultLen)
	{
		Result = NULL;
		outlen = 0;
	}
	else
	{
		OutBuffer += ResultLen;
		outlen -= ResultLen;
		*Result = PHDIO_OK;
		*(Result+1) = 0;
		dx->CmdOutputCount += ResultLen;
	}
	USHORT FailCode = PHDIO_OK;

	USHORT ByteNo=0;
	USHORT OutByteNo=0;
	// Loop through all commands
	while( ByteNo<len)
	{
		// See if we've been cancelled
		if( Irp->Cancel)
		{
			FailCode = PHDIO_CANCELLED;
			goto fail;
		}
		// Get next command
		UCHAR Cmd = *Buffer++;
		ByteNo++;
		UCHAR Size = Cmd&0xC0;
		if( Size!=0)
		{
			FailCode = PHDIO_BYTE_CMDS_ONLY;
			goto fail;	// Replace with following once uwords and ulongs supported
		}
/*
		bool isUCHAR = true;
		bool isUWORD = false;
		bool isULONG = false;
		if( Size==0x40) { isUCHAR = false; isUWORD = true; }
		if( Size==0x80) { isUCHAR = false; isULONG = true; }
		Cmd &= 0x3F;
*/
#if DODEBUGPRINT
		if( CanTrace)
			if( Cmd<NUM_PHD_IO_CMDS)
				DebugPrint( "Cmd: %s", PHD_IO_CMDS[Cmd]);
#endif
		/////////////////////////////////////////////////////////////////////
		switch( Cmd)
		{
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_OR:
		{
			GetUChar(reg);
			GetUChar(orvalue);
			UCHAR bvalue = ReadByte( dx, reg);
			UCHAR oredvalue = bvalue|orvalue;
			if( CanTrace) { DebugPrint( "Or %d %2x.  %2x->%2x", reg, orvalue, bvalue, oredvalue); }
			WriteByte( dx, reg, oredvalue);
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_AND:
		{
			GetUChar(reg);
			GetUChar(andvalue);
			UCHAR bvalue = ReadByte( dx, reg);
			UCHAR andedvalue = bvalue&andvalue;
			if( CanTrace) { DebugPrint( "And %d %2x.  %2x->%2x", reg, andvalue, bvalue, andedvalue); }
			WriteByte( dx, reg, andedvalue);
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_XOR:
		{
			GetUChar(reg);
			GetUChar(xorvalue);
			UCHAR bvalue = ReadByte( dx, reg);
			UCHAR xoredvalue = bvalue^xorvalue;
			if( CanTrace) { DebugPrint( "Xor %d %2x.  %2x->%2x", reg, xorvalue, bvalue, xoredvalue); }
			WriteByte( dx, reg, xoredvalue);
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_WRITE:
		{
			GetUChar(reg);
			GetUChar(bvalue);
			if( CanTrace) { DebugPrint( "Write %d %2x", reg, bvalue); }
			WriteByte( dx, reg, bvalue);
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_READ:
		{
			GetUChar(reg);
			UCHAR bvalue = ReadByte( dx, reg);
			SetUChar(bvalue);
			if( CanTrace) { DebugPrint( "Read %d %2x", reg, bvalue); }
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_DELAY:
		{
			GetUChar(delay);
			if( CanTrace) { DebugPrint( "Delay %dus", delay); }
			if( delay>60) { FailCode = PHDIO_DELAY_TOO_LONG; goto fail; }
			KeStallExecutionProcessor(delay);
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_WRITES:
		{
			GetUChar(reg);
			GetUChar(count);
			GetUChar(delay);
			if( CanTrace) { DebugPrint( "Write %d values to %d, delay %dus", count, reg, delay); }
			if( delay>60) { FailCode = PHDIO_DELAY_TOO_LONG; goto fail; }
			for( UCHAR vno=0; vno<count; vno++)
			{
				GetUChar(bvalue);
				if( CanTrace) { DebugPrint( "Writing %d %2x", reg, bvalue); }
				WriteByte( dx, reg, bvalue);
				KeStallExecutionProcessor(delay);
			}
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_READS:
		{
			GetUChar(reg);
			GetUChar(count);
			GetUChar(delay);
			if( CanTrace) { DebugPrint( "Read %d values from %d, delay %dus", count, reg, delay); }
			if( delay>60) { FailCode = PHDIO_DELAY_TOO_LONG; goto fail; }
			for( UCHAR vno=0; vno<count; vno++)
			{
				UCHAR bvalue = ReadByte( dx, reg);
				KeStallExecutionProcessor(delay);
				SetUChar(bvalue);
				if( CanTrace) { DebugPrint( "Read %d %2x", reg, bvalue); }
			}
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_IRQ_CONNECT:
		{
			if( !dx->GotInterrupt) { FailCode = PHDIO_NO_INTERRUPT; goto fail; }
			GetUCharNoDeclare(dx->InterruptReg);
			if( dx->InterruptReg>=dx->PortLength) { FailCode = PHDIO_NOT_IN_RANGE; goto fail; }
			GetUCharNoDeclare(dx->InterruptRegMask);
			GetUCharNoDeclare(dx->InterruptRegValue);
			if( (dx->InterruptRegValue&dx->InterruptRegMask) != dx->InterruptRegValue)
				{ FailCode = PHDIO_BAD_INTERRUPT_VALUE; goto fail; }

			if( CanTrace) { DebugPrint( "Connect.  Reg %d  Mask %2x Value %2x",
				dx->InterruptReg, dx->InterruptRegMask, dx->InterruptRegValue); }

			dx->ConnectIntQueued = true;
			ExQueueWorkItem( &dx->ConnectIntWQI, DelayedWorkQueue);

			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_TIMEOUT:
			GetUCharNoDeclare(dx->SetTimeout);
			break;
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_WRITE_NEXT:
		{
			if( dx->Timeout==-1) { FailCode = PHDIO_CANNOT_RW_NEXT; goto fail; }
			if( dx->TxLeft==0) { FailCode = PHDIO_NO_DATA_LEFT_TO_TRANSFER; goto fail; }
			GetUChar(reg);
			WriteByte( dx, reg, *dx->TxBuffer++);
			dx->TxLeft--;
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		case PHDIO_READ_NEXT:
		{
			if( dx->Timeout==-1) { FailCode = PHDIO_CANNOT_RW_NEXT; goto fail; }
			if( dx->TxLeft==0) { FailCode = PHDIO_NO_DATA_LEFT_TO_TRANSFER; goto fail; }
			GetUChar(reg);
			*dx->TxBuffer++ = ReadByte( dx, reg);
			dx->TxLeft--;
			break;
		}
		// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / //
		default:
			FailCode = PHDIO_UNRECOGNISED_CMD;
			goto fail;
		}
	}
	return true;

	/////////////////////////////////////////////////////////////////////////
	// Store failure code and location
fail:
	if( CanTrace) { DebugPrint( "ProcessCmds. FailCode %d at input:%d output:%d", FailCode, ByteNo-1, OutByteNo); }
	if( Result!=NULL)
	{
		*Result++ = FailCode;
		*Result = ByteNo-1;
	}
	return false;
}

/////////////////////////////////////////////////////////////////////////////
//	IrqConnectRoutine:	Work queue item to connect to an interrupt at PASSIVE_LEVEL

VOID IrqConnectRoutine( IN PVOID Context)
{
	PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)Context;

	DebugPrint( "IrqConnectRoutine");
	dx->ConnectIntQueued = false;

	// Get the current IRP that we are working on
	PIRP Irp = dx->fdo->CurrentIrp;
	if( Irp==NULL) return;

	// Cancel if necessary
	NTSTATUS status;
	if( Irp->Cancel)
		status = STATUS_CANCELLED;
	else
	{
		// Try to connect to interrupt
		status = IoConnectInterrupt( &dx->InterruptObject, (PKSERVICE_ROUTINE)InterruptHandler,
							dx, NULL, dx->Vector, dx->Irql, dx->Irql, dx->Mode, FALSE, dx->Affinity, FALSE);
		if( NT_SUCCESS(status))
			dx->ConnectedToInterrupt = true;
		else
		{
			// Store FailCode in output 
			if( dx->RunCmdsOutBuffer!=NULL)
				*dx->RunCmdsOutBuffer = PHDIO_CANNOT_CONNECT_TO_INTERRUPT;
		}
	}

	// Copy commands output to RUN_CMDS buffer
	if( dx->RunCmdsOutBuffer!=NULL)
	{
		PUCHAR Buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
		PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
		ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;

		RtlCopyMemory( Buffer, dx->RunCmdsOutBuffer, OutputLength);
		ExFreePool(dx->RunCmdsOutBuffer);
		dx->RunCmdsOutBuffer = NULL;
	}

	// Remove cancel routine
	KIRQL OldIrql;
	IoAcquireCancelSpinLock( &OldIrql);
	IoSetCancelRoutine( Irp, NULL);
	IoReleaseCancelSpinLock(OldIrql);

	// Unlock device, complete IRP and start next
	UnlockDevice(dx);
	DebugPrint( "IrqConnectRoutine: CmdOutputCount %d", dx->CmdOutputCount);
	CompleteIrp(Irp, status, dx->CmdOutputCount);
	KeRaiseIrql( DISPATCH_LEVEL, &OldIrql);
	IoStartNextPacket( dx->fdo, TRUE);
	KeLowerIrql(OldIrql);
}

/////////////////////////////////////////////////////////////////////////////
//	InterruptHandler:	Handle interrupts (during StartIo processing of R/W)
//					1	Always read the relevant status regsiter.
//					2	Only proceed if it has the right value,
//						ie to signal that our device caused the interrupt.
//					3	If IRP being cancelled then just call DPC to complete IRP.
//					4	Normally run write or read cmds to do whatever is necessary
//						to output or input next byte.
//					5	If all buffer txd (or error) then call DPC to complete IRP
//	Do not call DebugPrint here
//	Return TRUE if interrupt handled

BOOLEAN InterruptHandler(IN PKINTERRUPT Interrupt, IN PWDMIO_DEVICE_EXTENSION dx)
{
	// See if interrupt is ours
	dx->TxLastIntReg = ReadByte( dx, dx->InterruptReg);
	if( (dx->TxLastIntReg&dx->InterruptRegMask) != dx->InterruptRegValue)
		return FALSE;

	// If no transfer in progress then no further processing required
	if( dx->Timeout==-1) return TRUE;

	// See if current IRP being cancelled
	PDEVICE_OBJECT fdo = dx->fdo;
	PIRP Irp = fdo->CurrentIrp;
	if( Irp==NULL) return TRUE;
	BOOLEAN TxComplete = Irp->Cancel;
	if( !TxComplete)
	{
		// Run relevant set of commands
		if( dx->TxIsWrite)
			TxComplete = RunWriteCmdsSynch(dx);
		else
			TxComplete = RunReadCmdsSynch(dx);
	}
	// If all done, in error or being cancelled then call DPC to complete IRP
	if( TxComplete)
	{
		dx->Timeout = -1;
		IoRequestDpc( fdo, Irp, dx);
	}
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//	WdmIoDpcForIsr:	Complete current IRP

VOID WdmIoDpcForIsr(IN PKDPC Dpc, IN PDEVICE_OBJECT fdo, 
					IN PIRP Irp, IN PWDMIO_DEVICE_EXTENSION dx)
{
	dx->Timeout = -1;
	ULONG BytesTxd = dx->TxTotal-dx->TxLeft;
	if( Irp->Cancel) dx->TxStatus = STATUS_CANCELLED;

	DebugPrint("WdmIoDpcForIsr: Status %x Info %d", dx->TxStatus, BytesTxd);

	// Remove cancel routine
	KIRQL OldIrql;
	IoAcquireCancelSpinLock( &OldIrql);
	IoSetCancelRoutine( Irp, NULL);
	IoReleaseCancelSpinLock(OldIrql);

	// Unlock device and complete IRP
	UnlockDevice(dx);
	CompleteIrp(Irp, dx->TxStatus, BytesTxd);
	IoStartNextPacket( fdo, TRUE);

	// Stop timer calls
	dx->StopTimer = true;
}

/////////////////////////////////////////////////////////////////////////////
//	Timeout1sSynch:	Timeout check 
//	Return TRUE if operation has timed out
//	Called as a Critical Section routine

static BOOLEAN Timeout1sSynch( IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->Timeout==-1 || --dx->Timeout>0)
		return FALSE;
	dx->Timeout = -1;
	dx->TxStatus = STATUS_NO_MEDIA_IN_DEVICE;	// Win32: ERROR_NOT_READY
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//	Timeout1s:	One second timer call
//				Call Timeout1sSynch and then DPC if time out

VOID Timeout1s( IN PDEVICE_OBJECT fdo, IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->Timeout==-1) return;

	DebugPrint("Timeout1s: Timeout is %d",dx->Timeout);
	PIRP Irp = fdo->CurrentIrp;
#if DBG
	if( Irp==NULL) return;
#endif
	if( Irp->Cancel || KeSynchronizeExecution( dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)Timeout1sSynch, dx))
		WdmIoDpcForIsr( NULL, fdo, fdo->CurrentIrp, dx);
}

/////////////////////////////////////////////////////////////////////////////
//	WdmIoCancelIrp:
//
//	Description:
//		Cancel this IRP.
//			Called to cancel a Irp.
//			Called when CancelIo called or process finished without closing handle.
//			IRP must have set this as its cancel routine.
//
//		1	If IRP currently being processed in StartIo or interrupt handler
//			then just quit without completing IRP.  The IRP Cancel flag will 
//			be detected in due course and the IRP completed (as cancelled) then.
//		2	If IRP still in StartIo queue then remove it and complete it as cancelled.

VOID WdmIoCancelIrp( IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
	PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
	DebugPrint("WdmIoCancelIrp: Cancelling %x %I", Irp, Irp);
	if( Irp==fdo->CurrentIrp)
	{
		DebugPrintMsg("WdmIoCancelIrp: IRP running in StartIo");
		// IRP is being processed by WdmIoStartIo.
		// Irp->Cancel flag already set.
		// WdmIoStartIo or timeout will detect Cancel flag and cancel IRP in due course
		IoReleaseCancelSpinLock(Irp->CancelIrql);
	}
	else
	{
		DebugPrintMsg("WdmIoCancelIrp: IRP in StartIo queue");
		// IRP is still in StartIo device queue.
		// Just dequeue and cancel it.  No need to start next IRP.
		BOOLEAN dequeued = KeRemoveEntryDeviceQueue(
								&fdo->DeviceQueue,
								&Irp->Tail.Overlay.DeviceQueueEntry);

		IoReleaseCancelSpinLock(Irp->CancelIrql);

		if( dequeued)
		{
			UnlockDevice(dx);
			CompleteIrp( Irp, STATUS_CANCELLED);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
//	CancelCurrentIrpSynch:	If a transfer is in progress, mark it for cancelling
//	Return TRUE if operation can been cancelled
//	Runs at DIRQL

static BOOLEAN CancelCurrentIrpSynch( IN PWDMIO_DEVICE_EXTENSION dx)
{
	if( dx->Timeout==-1)
		return FALSE;
	dx->Timeout = -1;
	dx->TxStatus = STATUS_CANCELLED;
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//	WdmIoCleanup:
//
//	Description:
//		Handle IRP_MJ_CLEANUP requests
//		Cancel queued IRPs which match given FileObject
//
//		WdmIo cancels *all* queued IRPs and the current Irp
//
//	Arguments:
//		Pointer to our FDO
//		Pointer to the IRP
//			IrpStack->FileObject has handle to file
//
//	Return Value:
//		This function returns STATUS_XXX

NTSTATUS WdmIoDispatchCleanup( IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
	PWDMIO_DEVICE_EXTENSION dx = (PWDMIO_DEVICE_EXTENSION)fdo->DeviceExtension;
	DebugPrintMsg("WdmIoDispatchCleanup");
	KIRQL OldIrql;
	IoAcquireCancelSpinLock(&OldIrql);

	// Cancel all IRPs in the I/O Manager maintained queue in device object
	PKDEVICE_QUEUE_ENTRY QueueEntry;
	while( (QueueEntry=KeRemoveDeviceQueue(&fdo->DeviceQueue)) != NULL)
	{
		PIRP CancelIrp = CONTAINING_RECORD( QueueEntry, IRP, Tail.Overlay.DeviceQueueEntry);
		CancelIrp->Cancel = TRUE;
		CancelIrp->CancelIrql = OldIrql;
		CancelIrp->CancelRoutine = NULL;

		IoReleaseCancelSpinLock(OldIrql);
		DebugPrint("WdmIoDispatchCleanup: Cancelling %x %I",CancelIrp,CancelIrp);
		UnlockDevice(dx);
		CompleteIrp( CancelIrp, STATUS_CANCELLED);
		IoAcquireCancelSpinLock(&OldIrql);
	}
	IoReleaseCancelSpinLock(OldIrql);

	// Forcibly cancel any in-progress IRP
	if( dx->Timeout!=-1)
	{
		if( KeSynchronizeExecution( dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)CancelCurrentIrpSynch, dx))
		{
			if( fdo->CurrentIrp!=NULL)
			{
				DebugPrint("WdmIoDispatchCleanup: Cancelled in-progress IRP %x %I",fdo->CurrentIrp,fdo->CurrentIrp);
				WdmIoDpcForIsr( NULL, fdo, fdo->CurrentIrp, dx);
			}
		}
	}

	return CompleteIrp( Irp, STATUS_SUCCESS);
}

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


 

 


 

 

赶紧下载VS11体验吧

http://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200098144

你可能感兴趣的:(windows,null,buffer,extension,output,delay)