32、即插即用

简介: 即插即用  1、即插即用(IRP_MJ_PNP)功能能够通过操作系统协调自动分配设备上的资源,如中断号,I/O地址,DMA资源,设备物理内存等。  WDM框架程序是分层驱动,WDM处于分层的高端,而总线驱动程序处于分层的低端。

即插即用 

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

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

 wps_clip_image-2403

图示 P35 2 

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

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

图示 MSDNEnd-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

代码
 
   
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

代码
 
   
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的子功能代码的即插即用IRPWDM一般将这个IRP转发到底层PDO来完成。PDO根据设备的类型枚举该设备的所有资源。PDO可以认为是一个NT驱动。

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

主函数
 
   
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 }

 

代码
 
   
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中。

wps_clip_image-2400

图示设备资源 P370

代码
 
   
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式驱动程序的一个特例。

目录
相关文章
|
5月前
|
安全 Linux API
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
在Linux的宏大世界中,各种各样的硬件设备如星辰般繁多。从常见的USB设备到复杂的网络接口卡,从嵌入式设备到强大的服务器,Linux需要在这些差异极大的硬件上运行。这就引出了一个问题:Linux是如何统一这些不同硬件的设备模型的呢?本文将探讨Linux是如何针对不同的硬件统一设备模型的,这一统一的设备模型对于应用程序开发人员来说又有何意义。让我们一探究竟🕵️‍♂️。
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
|
监控 安全 网络协议
设备组态网络应用与通信系统
设备组态网络应用与通信系统
|
存储 监控 Serverless
VNF1048F芯片深入介绍(上)
VNF1048F芯片深入介绍(上)
|
机器学习/深度学习 自动驾驶 安全
YOffleNet | YOLO V4 基于嵌入式设备的轻量化改进设计
YOffleNet | YOLO V4 基于嵌入式设备的轻量化改进设计
298 0
西门子S7-1200硬件的组态,设备配置、网络组态
今天我们来学习西门子S7-1200硬件的组态。在S7-1200中当用户新建一个项目时,应当先进行硬件组态,硬件组态是编写项目程序的基础。在STEP7 Basic中,硬件组态遵循所见即所得的原则,PLC和HMI设备都能在相同的环境以相同的方式插入列项目中。
西门子S7-1200硬件的组态,设备配置、网络组态
西门子S7-200 SMART硬件设备组态
西门子S7-200 SMART提供各种类型I/O点数丰富的CPU模块、扩展模块和信号板,对于不同的应用需求能够提供更加经济、灵活的解决方案。S7-200 SMART CPU本体集成了一个以太网端口和一个RS485端口,必要时还可以通过信号板扩展一个RS485端口或者一个RS232端口。其中,编程设备只能通过以太网接口连接到CPU。
西门子S7-200 SMART硬件设备组态
|
物联网 开发工具 开发者
如何适配ESL应用平台到新的硬件板
本文介绍 如何适配ESL应用平台到新的硬件板
456 0
如何适配ESL应用平台到新的硬件板
|
传感器 SoC 芯片
内幕消息:嵌入式软件挤出最低功耗模式
低功耗运行仍然是各行业应用的关键驱动因素。随着睡眠模式的增加,电源管理突然从单纯的硬件问题转移到软件开发人员必须考虑的事情上。功耗模式的最简单应用是当系统空闲时,将其置于休眠状态。然而,今天的MCU提供多种低功耗模式,进一步使低功耗设计复杂化。
1155 0