基于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)
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
2.看见满目的驱动开发模板
3.选择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 deviceNTSTATUS 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; resnoType) { 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 deviceVOID 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 DIRQLvoid 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 DIRQLUCHAR 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 DODEBUGPRINTstatic 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 problembool 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( outlenCmdOutputCount += ResultLen; } USHORT FailCode = PHDIO_OK; USHORT ByteNo=0; USHORT OutByteNo=0; // Loop through all commands while( ByteNoCancel) { 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%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; vno60) { FailCode = PHDIO_DELAY_TOO_LONG; goto fail; } for( UCHAR vno=0; vnoGotInterrupt) { 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 locationfail: 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_LEVELVOID 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 handledBOOLEAN 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 IRPVOID 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 routinestatic 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 outVOID 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 DIRQLstatic 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_XXXNTSTATUS 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);}/
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~