即插即用 

1、即插即用(IRP_MJ_PNP)功能能够通过操作系统协调自动分配设备上的资源,如中断号,I/O地址,DMA资源,设备物理内存等。 

WDM框架程序是分层驱动,WDM处于分层的高端,而总线驱动程序处于分层的低端。大部分即插即用IRP都会被WDM驱动发送到底层的驱动程序处理。

 32、即插即用_#include

图示 P35 2 

IRP_MJ_PNP类似于Win32中的消息,而IRP_MJ_PNP的派遣函数类似于Win32编程中的窗口函数。

所有的即插即用的事件,都会引发即插即用管理器向WDM驱动程序发送一个IRP_MJ_PNP。IRP_MJ_PNP是这个IRP的主功能代码,不同的即插即用事件会有不同的子功能代码。

图示 MSDN中End-User I/O Requests and File Objects

​http://msdn.microsoft.com/en-us/library/ff544248%28VS.85%29.aspx#​

Handling IRPs

​http://msdn.microsoft.com/en-us/library/ff546847%28VS.85%29.aspx​

具体的子功能号讲解见MSDN

​http://msdn.microsoft.com/en-us/library/ff558807%28VS.85%29.aspx​



32、即插即用_即插即用_0232、即插即用_#pragma_03代码


1 /************************************************************************

2 * 函数名称:HelloWDMAddDevice

3 * 功能描述:添加新设备

4 * 参数列表:

5 DriverObject:从I/O管理器中传进来的驱动对象

6 PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象

7 * 返回 值:返回添加新设备状态

8 *************************************************************************/

9  #pragma PAGEDCODE

10 NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,

11 IN PDEVICE_OBJECT PhysicalDeviceObject)

12 {

13 PAGED_CODE();

14 KdPrint(("Enter HelloWDMAddDevice\n"));

15

16 NTSTATUS status;

17 PDEVICE_OBJECT fdo;

18 status = IoCreateDevice(

19 DriverObject,

20 sizeof(DEVICE_EXTENSION),

21 NULL,//没有指定设备名

22   FILE_DEVICE_UNKNOWN,

23 0,

24 FALSE,

25 &fdo);

26 if( !NT_SUCCESS(status))

27 return status;

28 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;

29 pdx->fdo = fdo;

30 pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);

31

32 //创建设备接口

33   status = IoRegisterDeviceInterface(PhysicalDeviceObject, &MY_WDM_DEVICE, NULL, &pdx->interfaceName);

34 if( !NT_SUCCESS(status))

35 {

36 IoDeleteDevice(fdo);

37 return status;

38 }

39 KdPrint(("%wZ\n",&pdx->interfaceName));

40 IoSetDeviceInterfaceState(&pdx->interfaceName, TRUE);

41

42 if( !NT_SUCCESS(status))

43 {

44 if( !NT_SUCCESS(status))

45 {

46 return status;

47 }

48 }

49

50 fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;

51 fdo->Flags &= ~DO_DEVICE_INITIALIZING;

52

53 KdPrint(("Leave HelloWDMAddDevice\n"));

54 return STATUS_SUCCESS;

55 }

56

57  /************************************************************************

58 * 函数名称:HelloWDMPnp

59 * 功能描述:对即插即用IRP进行处理

60 * 参数列表:

61 fdo:功能设备对象

62 Irp:从IO请求包

63 * 返回 值:返回状态

64 *************************************************************************/

65 #pragma PAGEDCODE

66 NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,

67 IN PIRP Irp)

68 {

69 PAGED_CODE();

70

71 KdPrint(("Enter HelloWDMPnp\n"));

72 NTSTATUS status = STATUS_SUCCESS;

73 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

74 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

75 static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) =

76 {

77 HandleStartDevice, // IRP_MN_START_DEVICE

78 DefaultPnpHandler, // IRP_MN_QUERY_REMOVE_DEVICE

79 HandleRemoveDevice, // IRP_MN_REMOVE_DEVICE

80 DefaultPnpHandler, // IRP_MN_CANCEL_REMOVE_DEVICE

81 DefaultPnpHandler, // IRP_MN_STOP_DEVICE

82 DefaultPnpHandler, // IRP_MN_QUERY_STOP_DEVICE

83 DefaultPnpHandler, // IRP_MN_CANCEL_STOP_DEVICE

84 DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_RELATIONS

85 DefaultPnpHandler, // IRP_MN_QUERY_INTERFACE

86 DefaultPnpHandler, // IRP_MN_QUERY_CAPABILITIES

87 DefaultPnpHandler, // IRP_MN_QUERY_RESOURCES

88 DefaultPnpHandler, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS

89 DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_TEXT

90 DefaultPnpHandler, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS

91 DefaultPnpHandler, //

92 DefaultPnpHandler, // IRP_MN_READ_CONFIG

93 DefaultPnpHandler, // IRP_MN_WRITE_CONFIG

94 DefaultPnpHandler, // IRP_MN_EJECT

95 DefaultPnpHandler, // IRP_MN_SET_LOCK

96 DefaultPnpHandler, // IRP_MN_QUERY_ID

97 DefaultPnpHandler, // IRP_MN_QUERY_PNP_DEVICE_STATE

98 DefaultPnpHandler, // IRP_MN_QUERY_BUS_INFORMATION

99 DefaultPnpHandler, // IRP_MN_DEVICE_USAGE_NOTIFICATION

100 DefaultPnpHandler, // IRP_MN_SURPRISE_REMOVAL

101 };

102

103 ULONG fcn = stack->MinorFunction;

104 if (fcn >= arraysize(fcntab))

105 { // 未知的子功能代码

106 status = DefaultPnpHandler(pdx, Irp); // some function we don't know about

107 return status;

108 }

109

110 #if DBG

111 static char* fcnname[] =

112 {

113 "IRP_MN_START_DEVICE",

114 "IRP_MN_QUERY_REMOVE_DEVICE",

115 "IRP_MN_REMOVE_DEVICE",

116 "IRP_MN_CANCEL_REMOVE_DEVICE",

117 "IRP_MN_STOP_DEVICE",

118 "IRP_MN_QUERY_STOP_DEVICE",

119 "IRP_MN_CANCEL_STOP_DEVICE",

120 "IRP_MN_QUERY_DEVICE_RELATIONS",

121 "IRP_MN_QUERY_INTERFACE",

122 "IRP_MN_QUERY_CAPABILITIES",

123 "IRP_MN_QUERY_RESOURCES",

124 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",

125 "IRP_MN_QUERY_DEVICE_TEXT",

126 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",

127 "",

128 "IRP_MN_READ_CONFIG",

129 "IRP_MN_WRITE_CONFIG",

130 "IRP_MN_EJECT",

131 "IRP_MN_SET_LOCK",

132 "IRP_MN_QUERY_ID",

133 "IRP_MN_QUERY_PNP_DEVICE_STATE",

134 "IRP_MN_QUERY_BUS_INFORMATION",

135 "IRP_MN_DEVICE_USAGE_NOTIFICATION",

136 "IRP_MN_SURPRISE_REMOVAL",

137 };

138

139 KdPrint(("PNP Request (%s)\n", fcnname[fcn]));

140 #endif // DBG

141

142 status = (*fcntab[fcn])(pdx, Irp);

143 KdPrint(("Leave HelloWDMPnp\n"));

144 return status;

145 }

146

147 /************************************************************************

148 * 函数名称:HelloWDMDispatchRoutine

149 * 功能描述:对缺省IRP进行处理

150 * 参数列表:

151 fdo:功能设备对象

152 Irp:从IO请求包

153 * 返回 值:返回状态

154 *************************************************************************/

155 #pragma PAGEDCODE

156 NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,

157 IN PIRP Irp)

158 {

159 PAGED_CODE();

160 KdPrint(("Enter HelloWDMDispatchRoutine\n"));

161 Irp->IoStatus.Status = STATUS_SUCCESS;

162 Irp->IoStatus.Information = 0; // no bytes xfered

163 IoCompleteRequest( Irp, IO_NO_INCREMENT );

164 KdPrint(("Leave HelloWDMDispatchRoutine\n"));

165 return STATUS_SUCCESS;

166 }

代码示例 P354

2、通过设备接口寻找设备

在WDM中,一般是通过设备接口来定位一个驱动程序的。不同NT驱动通过设备名或者符号链接来定位。

设备接口就一组全局标识,一个128位组成的数字。

不用IoCreateSymbolicLink ,而用IoRegisterDeviceInterface为设备创建设备链接。




见上面的示例代码中

HelloWDMAddDevice


 

示例代码 P358

新的符号链接包括四部分组成:何种总线,类设备名称,该设备类型的第几个设备,指定设备的UUID。

A driver registers an interface instance once and then calls IoSetDeviceinterfaceState to enable and disable the interface.

1)应用程序寻找接口

应用程序寻找接口,是通过设备接口和设备号决定的。

一般通过SetupDiXX系统函数来获得设备接口。

http://msdn.microsoft.com/en-us/library/ff551015%28VS.85%29.aspx



32、即插即用_即插即用_0232、即插即用_#pragma_03代码


1 #include <windows.h>

2 #include <stdio.h>

3 #include <winioctl.h>

4 #include "function.h"

5

6 HANDLE GetDeviceViaInterface( GUID* pGuid, DWORD instance)

7 {

8 // Get handle to relevant device information set

9 HDEVINFO info = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);

10 if(info==INVALID_HANDLE_VALUE)

11 {

12 printf("No HDEVINFO available for this GUID\n");

13 return NULL;

14 }

15

16 // Get interface data for the requested instance

17 SP_INTERFACE_DEVICE_DATA ifdata;

18 ifdata.cbSize = sizeof(ifdata);

19 if(!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, instance, &ifdata))

20 {

21 printf("No SP_INTERFACE_DEVICE_DATA available for this GUID instance\n");

22 SetupDiDestroyDeviceInfoList(info);

23 return NULL;

24 }

25

26 // Get size of symbolic link name

27 DWORD ReqLen;

28 SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &ReqLen, NULL);

29 PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]);

30 if( ifDetail==NULL)

31 {

32 SetupDiDestroyDeviceInfoList(info);

33 return NULL;

34 }

35

36 // Get symbolic link name

37 ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);

38 if( !SetupDiGetDeviceInterfaceDetail(info, &ifdata, ifDetail, ReqLen, NULL, NULL))

39 {

40 SetupDiDestroyDeviceInfoList(info);

41 delete ifDetail;

42 return NULL;

43 }

44

45 printf("Symbolic link is %s\n",ifDetail->DevicePath);

46 // Open file

47 HANDLE rv = CreateFile( ifDetail->DevicePath,

48 GENERIC_READ | GENERIC_WRITE,

49 FILE_SHARE_READ | FILE_SHARE_WRITE,

50 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

51 if( rv==INVALID_HANDLE_VALUE) rv = NULL;

52

53 delete ifDetail;

54 SetupDiDestroyDeviceInfoList(info);

55 return rv;

56 }

57

58 DWORD ReadPort(HANDLE hDevice,DWORD port)

59 {

60 DWORD dwOutput;

61 DWORD dwRead;

62 DeviceIoControl(hDevice, IOCTL_READ_PORT_ULONG, &port, 4, &dwOutput, 4, &dwRead, NULL);

63 return dwOutput;

64 }

65 VOID WritePort(HANDLE hDevice,DWORD port,DWORD value)

66 {

67 PVOID buffer[2];

68 buffer[0] = (PVOID)port;

69 buffer[1] = (PVOID)value;

70 DWORD dwWrite;

71 DeviceIoControl(hDevice, IOCTL_WRITE_PORT_ULONG, &port, 8, NULL, 0, &dwWrite, NULL);

72 }

73 VOID TestDriver(HANDLE hDevice)

74 {

75 DWORD dwOutput;

76 DeviceIoControl(hDevice, IOCTL_TEST, NULL, 0, NULL, 0, &dwOutput, NULL);

77 }

78

79


 

示例代码 P359

INI文件注意:VendorID and ProductID。

2)启动设备

启动设备时,设备管理器向WDM驱动发一个IRP_MN_START_DEVICE的子功能代码的即插即用IRP。WDM一般将这个IRP转发到底层PDO来完成。PDO根据设备的类型枚举该设备的所有资源。PDO可以认为是一个NT驱动。

在设备管理器看到的是分配资源中的中断号,而WDM驱动中使用是翻译资源里的中断号。



32、即插即用_即插即用_0232、即插即用_#pragma_03主函数


1 #include <windows.h>

2 #include <stdio.h>

3 #include <winioctl.h>

4

5 #include "function.h"

6

7 #include "../MyDriver/guid.h"

8

9 int main()

10 {

11 HANDLE hDevice = GetDeviceViaInterface((LPGUID)&MY_WDM_DEVICE,0);

12

13 if (hDevice == INVALID_HANDLE_VALUE)

14 {

15 printf("Failed to obtain file handle to device: "

16 "%s with Win32 error code: %d\n",

17 "MyWDMDevice", GetLastError() );

18 return 1;

19 }

20

21 CloseHandle(hDevice);

22 return 0;

23 }


 


32、即插即用_即插即用_0232、即插即用_#pragma_03代码


1 #pragma LOCKEDCODE

2 NTSTATUS OnRequestComplete(PDEVICE_OBJECT junk, PIRP Irp, PKEVENT pev)

3 { // OnRequestComplete

4 //在完成例程中设置等待事件

5 KeSetEvent(pev, 0, FALSE);

6 //标志本IRP还需要再次被完成

7 return STATUS_MORE_PROCESSING_REQUIRED;

8 }

9

10 ///////////////////////////////////////////////////////////////////////////////

11

12 #pragma PAGEDCODE

13 NTSTATUS ForwardAndWait(PDEVICE_EXTENSION pdx, PIRP Irp)

14 { // ForwardAndWait

15 PAGED_CODE();

16

17 KEVENT event;

18 //初始化事件

19 KeInitializeEvent(&event, NotificationEvent, FALSE);

20

21 //将本层堆栈拷贝到下一层堆栈

22 IoCopyCurrentIrpStackLocationToNext(Irp);

23 //设置完成例程

24 IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,

25 (PVOID) &event, TRUE, TRUE, TRUE);

26

27 //调用底层驱动,即PDO

28 IoCallDriver(pdx->NextStackDevice, Irp);

29 //等待PDO完成

30 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

31 return Irp->IoStatus.Status;

32 }


 

转发并等待示例 P366

枚举相关资源

PDO完成IRP_MN_START_DEVICE后,会将获得的设备资源存储在IRP的设备栈中,并且存储中stack中的AllocatedResourcesTranslated中。

32、即插即用_#include_10

图示设备资源 P370



32、即插即用_即插即用_0232、即插即用_#pragma_03代码


1 #pragma PAGEDCODE

2 VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list)

3 {

4 //枚举资源

5 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list->PartialDescriptors;

6 ULONG nres = list->Count;

7 ULONG i;

8

9 for (i = 0; i < nres; ++i, ++resource)

10 { // for each resource

11 ULONG type = resource->Type;

12

13 static char* name[] = {

14 "CmResourceTypeNull",

15 "CmResourceTypePort",

16 "CmResourceTypeInterrupt",

17 "CmResourceTypeMemory",

18 "CmResourceTypeDma",

19 "CmResourceTypeDeviceSpecific",

20 "CmResourceTypeBusNumber",

21 "CmResourceTypeDevicePrivate",

22 "CmResourceTypeAssignedResource",

23 "CmResourceTypeSubAllocateFrom",

24 };

25

26 KdPrint((" type %s", type < arraysize(name) ? name[type] : "unknown"));

27

28 switch (type)

29 { // select on resource type

30 case CmResourceTypePort:

31 case CmResourceTypeMemory:

32 KdPrint((" start %8X%8.8lX length %X\n",

33 resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,

34 resource->u.Port.Length));

35 break;

36

37 case CmResourceTypeInterrupt:

38 KdPrint((" level %X, vector %X, affinity %X\n",

39 resource->u.Interrupt.Level, resource->u.Interrupt.Vector,

40 resource->u.Interrupt.Affinity));

41 break;

42

43 case CmResourceTypeDma:

44 KdPrint((" channel %d, port %X\n",

45 resource->u.Dma.Channel, resource->u.Dma.Port));

46 } // select on resource type

47 } // for each resource

48 }

示例代码 P371

  一个设备一般需要加载两个WDM程序,一个是总线驱动程序(它提供PDO),另一个是功能驱动程序(它提供FDO),PDO和FDO这两个设备对象形成一个设备栈。非即插即用功能的IRP一般在FDO中处理,而即插即用功能相关的IRP会被转发到PDO中处理。

  WDM驱动程序是NT式驱动程序的一个特例。