天天看點

32、即插即用

即插即用 

1、即插即用(IRP_MJ_PNP)功能能夠通過作業系統協調自動配置設定裝置上的資源,如中斷号,I/O位址,DMA資源,裝置實體記憶體等。 

WDM架構程式是分層驅動,WDM處于分層的高端,而總線驅動程式處于分層的低端。大部分即插即用IRP都會被WDM驅動發送到底層的驅動程式處理。

32、即插即用

圖示 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

<a href="http://msdn.microsoft.com/en-us/library/ff544248%28VS.85%29.aspx#">http://msdn.microsoft.com/en-us/library/ff544248%28VS.85%29.aspx#</a>

Handling IRPs

<a href="http://msdn.microsoft.com/en-us/library/ff546847%28VS.85%29.aspx">http://msdn.microsoft.com/en-us/library/ff546847%28VS.85%29.aspx</a>

具體的子功能号講解見MSDN

<a href="http://msdn.microsoft.com/en-us/library/ff558807%28VS.85%29.aspx">http://msdn.microsoft.com/en-us/library/ff558807%28VS.85%29.aspx</a>

32、即插即用
32、即插即用

代碼

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 &amp;fdo);

26 if( !NT_SUCCESS(status))

27 return status;

28 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo-&gt;DeviceExtension;

29 pdx-&gt;fdo = fdo;

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

31

32 //建立裝置接口

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

34 if( !NT_SUCCESS(status))

35 {

36 IoDeleteDevice(fdo);

37 return status;

38 }

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

40 IoSetDeviceInterfaceState(&amp;pdx-&gt;interfaceName, TRUE);

41

42 if( !NT_SUCCESS(status))

43 {

44 if( !NT_SUCCESS(status))

45 {

46 return status;

47 }

48 }

49

50 fdo-&gt;Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;

51 fdo-&gt;Flags &amp;= ~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-&gt;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-&gt;MinorFunction;

104 if (fcn &gt;= 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-&gt;IoStatus.Status = STATUS_SUCCESS;

162 Irp-&gt;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、即插即用
32、即插即用

1 #include &lt;windows.h&gt;

2 #include &lt;stdio.h&gt;

3 #include &lt;winioctl.h&gt;

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 }

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, &amp;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, &amp;ifdata, NULL, 0, &amp;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-&gt;cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);

38 if( !SetupDiGetDeviceInterfaceDetail(info, &amp;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-&gt;DevicePath);

46 // Open file

47 HANDLE rv = CreateFile( ifDetail-&gt;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;

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, &amp;port, 4, &amp;dwOutput, 4, &amp;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, &amp;port, 8, NULL, 0, &amp;dwWrite, NULL);

72 }

73 VOID TestDriver(HANDLE hDevice)

74 {

75 DWORD dwOutput;

76 DeviceIoControl(hDevice, IOCTL_TEST, NULL, 0, NULL, 0, &amp;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、即插即用
32、即插即用

主函數

4

5 #include "function.h"

6

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

8

9 int main()

10 {

11 HANDLE hDevice = GetDeviceViaInterface((LPGUID)&amp;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、即插即用
32、即插即用

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(&amp;event, NotificationEvent, FALSE);

21 //将本層堆棧拷貝到下一層堆棧

22 IoCopyCurrentIrpStackLocationToNext(Irp);

23 //設定完成例程

24 IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,

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

26

27 //調用底層驅動,即PDO

28 IoCallDriver(pdx-&gt;NextStackDevice, Irp);

29 //等待PDO完成

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

31 return Irp-&gt;IoStatus.Status;

32 }

轉發并等待示例 P366

枚舉相關資源

PDO完成IRP_MN_START_DEVICE後,會将獲得的裝置資源存儲在IRP的裝置棧中,并且存儲中stack中的AllocatedResourcesTranslated中。

32、即插即用

圖示裝置資源 P370

32、即插即用
32、即插即用

1 #pragma PAGEDCODE

2 VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list)

3 {

4 //枚舉資源

5 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list-&gt;PartialDescriptors;

6 ULONG nres = list-&gt;Count;

7 ULONG i;

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

10 { // for each resource

11 ULONG type = resource-&gt;Type;

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 };

26 KdPrint((" type %s", type &lt; 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-&gt;u.Port.Start.HighPart, resource-&gt;u.Port.Start.LowPart,

34 resource-&gt;u.Port.Length));

35 break;

36

37 case CmResourceTypeInterrupt:

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

39 resource-&gt;u.Interrupt.Level, resource-&gt;u.Interrupt.Vector,

40 resource-&gt;u.Interrupt.Affinity));

41 break;

42

43 case CmResourceTypeDma:

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

45 resource-&gt;u.Dma.Channel, resource-&gt;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式驅動程式的一個特例。