把功能复杂的驱动分解成多个简单的驱动。多个分层驱动程序形成一个设备堆栈,IRP请求首先发送到设备堆栈的顶层,然后以次穿越每层的设备堆栈,最终完成IRP的请求。
1、相关概念
分层驱动是指两个或两个以上的驱动程序,他们分别创建设备对象,并且形成一个由高到低的设备对象栈。IRP请求一般送到设备栈最顶层的设备对象。顶层设备对象可以处理该IRP请求,也可以向下转发。IRP请求结束时,按原路返回。
1)_DEVICE_OBJECT结构体,需要注意3个参数:
DriverObject 设备对象对应的驱动对象
NextDevice 记录下一个设备对象的指针
AttachedDevice 记录当前设备对象挂载的设备对象。
WDM驱动程序就属于分层驱动程序。最简单的WDM驱动程序分为两层:一层是PDO(物理设备对象),一层是FDO(功能设备对象),FDO挂载在PDO之上。PDO实现即插即用的功能,FDO完成逻辑功能,而将一些硬件相关请求发往PDL。
挂载指的是将高一层的设备对象挂载在低一层的设备对象上,从而形成一个设备栈。
IoAttachDeviceToDeviceStack 挂载
IoDetachDevice 卸载
2)I/O 堆栈
用一个叫做IO_STACK_LOCATION的数据结构来保存;它和设备堆栈紧密结合。在IRP中,有一个指向IO_STACK_LOCATION数组的指针。调用IoAllocateIrp创建Irp时,有一个StackSize,就是IO_STACK_LOCATION数组的大小。
图示 P322
3)向下转发IRP
3种方法处理IRP:直接处理;调用StartIo,向下转发。
一个设备堆栈对应一个IO_STACK_LOCATION堆栈元素。IRP内部有一个指针指向当前正使用的IO_STACK_LOCATION。IoGetCurrentIrpStackLocation 获得。
每次调用IoCallDriver时,内核函数会将IRP的当前指针向下移一个单位。而IoSkipCurrentIrpStackLocation 用来将当前I/O堆栈往回(上)移一个单位。
When sending an IRP to the next-lower driver, your driver can call IoSkipCurrentIrpStackLocation if you do not intend to provide an IoCompletion routine (the address of which is stored in the driver's IO_STACK_LOCATION structure). If you call IoSkipCurrentIrpStackLocation before calling IoCallDriver, the next-lower driver receives the same IO_STACK_LOCATION that your driver received.
If you intend to provide an IoCompletion routine for the IRP, your driver should call IoCopyCurrentIrpStackLocationToNext instead of IoSkipCurrentIrpStackLocation.
1 /* ***********************************************************************
2 * 函数名称:DriverEntry
3 * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
4 * 参数列表:
5 pDriverObject:从I/O管理器中传进来的驱动对象
6 pRegistryPath:驱动程序在注册表的中的路径
7 * 返回 值:返回初始化驱动状态
8 ************************************************************************ */
9 #pragma INITCODE
10 extern " C " NTSTATUS DriverEntry (
11 IN PDRIVER_OBJECT pDriverObject,
12 IN PUNICODE_STRING pRegistryPath )
13 {
14 NTSTATUS ntStatus;
15 KdPrint(( " DriverB:Enter B DriverEntry\n " ));
16
17 // 注册其他驱动调用函数入口
18 pDriverObject -> DriverUnload = HelloDDKUnload;
19 pDriverObject -> MajorFunction[IRP_MJ_CREATE] = HelloDDKCreate;
20 pDriverObject -> MajorFunction[IRP_MJ_CLOSE] = HelloDDKClose;
21 pDriverObject -> MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
22 pDriverObject -> MajorFunction[IRP_MJ_READ] = HelloDDKRead;
23
24 UNICODE_STRING DeviceName;
25 RtlInitUnicodeString( & DeviceName, L " \\Device\\MyDDKDeviceA " );
26
27 PDEVICE_OBJECT DeviceObject = NULL;
28 PFILE_OBJECT FileObject = NULL;
29 // 寻找DriverA创建的设备对象
30 ntStatus = IoGetDeviceObjectPointer( & DeviceName,FILE_ALL_ACCESS, & FileObject, & DeviceObject);
31
32 if ( ! NT_SUCCESS(ntStatus))
33 {
34 KdPrint(( " DriverB:IoGetDeviceObjectPointer() 0x%x\n " , ntStatus ));
35 return ntStatus;
36 }
37
38 // 创建自己的驱动设备对象
39 ntStatus = CreateDevice(pDriverObject);
40
41 if ( ! NT_SUCCESS( ntStatus ) )
42 {
43 ObDereferenceObject( FileObject );
44 DbgPrint( " IoCreateDevice() 0x%x!\n " , ntStatus );
45 return ntStatus;
46 }
47
48 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) pDriverObject -> DeviceObject -> DeviceExtension;
49
50 PDEVICE_OBJECT FilterDeviceObject = pdx -> pDevice;
51
52 // 将自己的设备对象挂载在DriverA的设备对象上
53 PDEVICE_OBJECT TargetDevice = IoAttachDeviceToDeviceStack( FilterDeviceObject,
54 DeviceObject );
55 // 将底层设备对象记录下来
56 pdx -> TargetDevice = TargetDevice;
57
58 if ( ! TargetDevice )
59 {
60 ObDereferenceObject( FileObject );
61 IoDeleteDevice( FilterDeviceObject );
62 DbgPrint( " IoAttachDeviceToDeviceStack() 0x%x!\n " , ntStatus );
63 return STATUS_INSUFFICIENT_RESOURCES;
64 }
65
66 FilterDeviceObject -> DeviceType = TargetDevice -> DeviceType;
67 FilterDeviceObject -> Characteristics = TargetDevice -> Characteristics;
68 FilterDeviceObject -> Flags &= ~ DO_DEVICE_INITIALIZING;
69 FilterDeviceObject -> Flags |= ( TargetDevice -> Flags & ( DO_DIRECT_IO |
70 DO_BUFFERED_IO ) );
71 ObDereferenceObject( FileObject );
72
73 KdPrint(( " DriverB:B attached A successfully!\n " ));
74
75 KdPrint(( " DriverB:Leave B DriverEntry\n " ));
76 return ntStatus;
77 }
78 /* ***********************************************************************
79 * 函数名称:HelloDDKUnload
80 * 功能描述:负责驱动程序的卸载操作
81 * 参数列表:
82 pDriverObject:驱动对象
83 * 返回 值:返回状态
84 ************************************************************************ */
85 #pragma PAGEDCODE
86 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
87 {
88 PDEVICE_OBJECT pNextObj;
89 KdPrint(( " DriverB:Enter B DriverUnload\n " ));
90 pNextObj = pDriverObject -> DeviceObject;
91
92 while (pNextObj != NULL)
93 {
94 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
95 pNextObj -> DeviceExtension;
96 pNextObj = pNextObj -> NextDevice;
97 // 从设备栈中弹出
98 IoDetachDevice( pDevExt -> TargetDevice);
99 // 删除该设备对象
100 IoDeleteDevice( pDevExt -> pDevice );
101 }
102 KdPrint(( " DriverB:Enter B DriverUnload\n " ));
103 }
104
105 /* ***********************************************************************
106 * 函数名称:HelloDDKDispatchRoutine
107 * 功能描述:对读IRP进行处理
108 * 参数列表:
109 pDevObj:功能设备对象
110 pIrp:从IO请求包
111 * 返回 值:返回状态
112 ************************************************************************ */
113 #pragma PAGEDCODE
114 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
115 IN PIRP pIrp)
116 {
117 KdPrint(( " DriverB:Enter B HelloDDKDispatchRoutine\n " ));
118 NTSTATUS ntStatus = STATUS_SUCCESS;
119 // 完成IRP
120 pIrp -> IoStatus.Status = ntStatus;
121 pIrp -> IoStatus.Information = 0 ; // bytes xfered
122 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
123 KdPrint(( " DriverB:Leave B HelloDDKDispatchRoutine\n " ));
124 return ntStatus;
125 }
126
127 #pragma PAGEDCODE
128 NTSTATUS HelloDDKCreate(IN PDEVICE_OBJECT pDevObj,
129 IN PIRP pIrp)
130 {
131 KdPrint(( " DriverB:Enter B HelloDDKCreate\n " ));
132 NTSTATUS ntStatus = STATUS_SUCCESS;
133 //
134 // // 完成IRP
135 // pIrp->IoStatus.Status = ntStatus;
136 // pIrp->IoStatus.Information = 0; // bytes xfered
137 // IoCompleteRequest( pIrp, IO_NO_INCREMENT );
138
139 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
140
141 IoSkipCurrentIrpStackLocation (pIrp);
142
143 ntStatus = IoCallDriver(pdx -> TargetDevice, pIrp);
144
145 KdPrint(( " DriverB:Leave B HelloDDKCreate\n " ));
146
147 return ntStatus;
148 }
149
150 #pragma PAGEDCODE
151 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
152 IN PIRP pIrp)
153 {
154 KdPrint(( " DriverB:Enter B HelloDDKCreate\n " ));
155 NTSTATUS ntStatus = STATUS_SUCCESS;
156 // 将自己完成IRP,改成由底层驱动负责
157
158 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
159
160 // 调用底层驱动
161 IoSkipCurrentIrpStackLocation (pIrp);
162
163 ntStatus = IoCallDriver(pdx -> TargetDevice, pIrp);
164
165 KdPrint(( " DriverB:Leave B HelloDDKCreate\n " ));
166
167 return ntStatus;
168 }
169
170 #pragma PAGEDCODE
171 NTSTATUS HelloDDKClose(IN PDEVICE_OBJECT pDevObj,
172 IN PIRP pIrp)
173 {
174 KdPrint(( " DriverB:Enter B HelloDDKClose\n " ));
175 NTSTATUS ntStatus = STATUS_SUCCESS;
176
177 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
178
179 IoSkipCurrentIrpStackLocation (pIrp);
180
181 ntStatus = IoCallDriver(pdx -> TargetDevice, pIrp);
182
183 KdPrint(( " DriverB:Leave B HelloDDKClose\n " ));
184
185 return ntStatus;
186 }
挂载设备对象代码示例 P324
转发IRP代码示例 P326 如上代码示例Read
2、完成例程
在将IRP发送给低层驱动或者其他驱动之前,可以对IRP设置一个完成例程。一旦底层驱动将IRP完成后,IRP完成例程将被触发,可以通过这个原理来获得通知。
IoSetCompletionRoutine ,如果参数CompletionRoutine 为NULL,则意味着完成例程取消。IRP被IoCompleteRequest 完成时,会一层层出栈,如果遇到完成例程,则调用完成例程。
当调用IoCallDriver后,当前驱动就失去了对IRP的控制;如果此时设置IRP的属性,会引起系统崩溃。完成例程返回两种状态,STATUS_SUCCESS,STATUS_MORE_PROCESSING_REQUIRED。如果返回是 STATUS_MORE_PROCESSING_REQUIRED,则本层设备堆栈重新获得IRP的控制权,并且设备栈不会向上弹出,也就是向上“回卷” 设备栈停止,此时可以再次向底层发送IRP。
1)传播Pending位
IRP的堆栈向上回卷时,底层I/O堆栈的Control域的SL_PENDING_RETURNED位必须传播到上一层。如果本层没有设备完成例程,传播是自动的;如果设备了完成例程,则需要程序员自己实现。
注意:只能在完成例程中设置。
2)返回STATUS_SUCCESS
如果是STATUS_SUCCESS,则继续回卷。
1 NTSTATUS
2 MyIoCompletion(
3 IN PDEVICE_OBJECT DeviceObject,
4 IN PIRP Irp,
5 IN PVOID Context
6 )
7 {
8 // 进入此函数标志底层驱动设备将IRP完成
9 KdPrint(( " Enter MyIoCompletion\n " ));
10 if (Irp -> PendingReturned)
11 {
12 // 传播pending位
13 IoMarkIrpPending( Irp );
14 }
15 return STATUS_SUCCESS; // 同STATUS_CONTINUE_COMPLETION
16 }
17
18 #pragma PAGEDCODE
19 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
20 IN PIRP pIrp)
21 {
22 KdPrint(( " DriverB:Enter B HelloDDKRead\n " ));
23 NTSTATUS ntStatus = STATUS_SUCCESS;
24 // 将自己完成IRP,改成由底层驱动负责
25
26 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
27
28 // 将当前IRP堆栈拷贝底层堆栈
29 IoCopyCurrentIrpStackLocationToNext(pIrp);
30
31 // 设置完成例程
32 IoSetCompletionRoutine(pIrp,MyIoCompletion,NULL,TRUE,TRUE,TRUE);
33
34 // 调用底层驱动
35 ntStatus = IoCallDriver(pdx -> TargetDevice, pIrp);
36
37 // 当IoCallDriver后,并且完成例程返回的是STATUS_SUCCESS
38 // IRP就不在属于派遣函数了,就不能对IRP进行操作了
39 if (ntStatus == STATUS_PENDING)
40 {
41 KdPrint(( " STATUS_PENDING\n " ));
42 }
43 ntStatus = STATUS_PENDING;
44
45 KdPrint(( " DriverB:Leave B HelloDDKRead\n " ));
46
47 return ntStatus;
48 }
示例代码 P333
3)STATUS_MORE_PROCESSING_REQUIRED
如果是STATUS_MORE_PROCESSING_REQUIRED,则本层堆栈重新获得控制,并且该IRP从完成状态变成了未完成状态,需要再次完成,即执行IoCompleteRequest 。
重新获得的IRP可以再次传下底层,也可以标志完成。
1 NTSTATUS
2 MyIoCompletion(
3 IN PDEVICE_OBJECT DeviceObject,
4 IN PIRP Irp,
5 IN PVOID Context
6 )
7 {
8
9 if (Irp -> PendingReturned == TRUE)
10 {
11 // 设置事件
12 KeSetEvent((PKEVENT)Context,IO_NO_INCREMENT,FALSE);
13 }
14
15 return STATUS_MORE_PROCESSING_REQUIRED;
16 }
17
18 #pragma PAGEDCODE
19 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
20 IN PIRP pIrp)
21 {
22 KdPrint(( " DriverB:Enter B HelloDDKRead\n " ));
23 NTSTATUS ntStatus = STATUS_SUCCESS;
24 // 将自己完成IRP,改成由底层驱动负责
25
26 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
27
28 // 将本层的IRP堆栈拷贝到底层堆栈
29 IoCopyCurrentIrpStackLocationToNext(pIrp);
30
31 KEVENT event ;
32 // 初始化事件
33 KeInitializeEvent( & event , NotificationEvent, FALSE);
34
35 // 设置完成例程
36 IoSetCompletionRoutine(pIrp,MyIoCompletion, & event ,TRUE,TRUE,TRUE);
37
38 // 调用底层驱动
39 ntStatus = IoCallDriver(pdx -> TargetDevice, pIrp);
40
41 if (ntStatus == STATUS_PENDING)
42 {
43 KdPrint(( " IoCallDriver return STATUS_PENDING,Waiting ...\n " ));
44 KeWaitForSingleObject( & event ,Executive,KernelMode ,FALSE,NULL);
45 ntStatus = pIrp -> IoStatus.Status;
46 }
47
48 // 虽然在底层驱动已经将IRP完成了,但是由于完成例程返回的是
49 // STATUS_MORE_PROCESSING_REQUIRED,因此需要再次调用IoCompleteRequest!
50 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
51
52 KdPrint(( " DriverB:Leave B HelloDDKRead\n " ));
53
54 return ntStatus;
55 }
示例代码 P335
3)将IRP分成多个IRP
通过一个例子来说明。
图示分层驱动模型 P336
1 #pragma PAGEDCODE
2 NTSTATUS
3 HelloDDKReadCompletion(
4 IN PDEVICE_OBJECT DeviceObject,
5 IN PIRP Irp,
6 IN PVOID Context
7 )
8 {
9 KdPrint(( " DriverB:Enter B HelloDDKReadCompletion\n " ));
10
11 PMYDRIVER_RW_CONTEXT rwContext = (PMYDRIVER_RW_CONTEXT) Context;
12 NTSTATUS ntStatus = Irp -> IoStatus.Status;
13
14 ULONG stageLength;
15
16 if (rwContext && NT_SUCCESS(ntStatus))
17 {
18 // 已经传送了多少字节
19 rwContext -> Numxfer += Irp -> IoStatus.Information;
20
21 if (rwContext -> Length)
22 {
23 // 设定下一阶段读取字节数
24 if (rwContext -> Length > MAX_PACKAGE_SIZE)
25 {
26 stageLength = MAX_PACKAGE_SIZE;
27 }
28 else
29 {
30 stageLength = rwContext -> Length;
31 }
32 // 重新利用MDL
33 MmPrepareMdlForReuse(rwContext -> NewMdl);
34
35 IoBuildPartialMdl(Irp -> MdlAddress,
36 rwContext -> NewMdl,
37 (PVOID) rwContext -> VirtualAddress,
38 stageLength);
39
40 rwContext -> VirtualAddress += stageLength;
41 rwContext -> Length -= stageLength;
42
43 IoCopyCurrentIrpStackLocationToNext(Irp);
44 PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
45
46 nextStack -> Parameters.Read.Length = stageLength;
47
48 IoSetCompletionRoutine(Irp,
49 HelloDDKReadCompletion,
50 rwContext,
51 TRUE,
52 TRUE,
53 TRUE);
54
55 IoCallDriver(rwContext -> DeviceExtension -> TargetDevice,
56 Irp);
57
58 return STATUS_MORE_PROCESSING_REQUIRED;
59 }
60 else
61 {
62 // 最后一次传输
63 Irp -> IoStatus.Information = rwContext -> Numxfer;
64 }
65 }
66
67 KdPrint(( " DriverB:Leave B HelloDDKReadCompletion\n " ));
68 return STATUS_MORE_PROCESSING_REQUIRED;
69 }
70
底层驱动示例代码 P337 见DriverA示例部分
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 KdPrint(( " DriverB:Enter B HelloDDKRead\n " ));
6 NTSTATUS status = STATUS_SUCCESS;
7
8 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
9 pDevObj -> DeviceExtension;
10
11 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
12
13 ULONG totalLength;
14 ULONG stageLength;
15 PMDL mdl;
16 PVOID virtualAddress;
17 PMYDRIVER_RW_CONTEXT rwContext = NULL;
18 PIO_STACK_LOCATION nextStack;
19
20 if ( ! pIrp -> MdlAddress)
21 {
22 status = STATUS_UNSUCCESSFUL;
23 totalLength = 0 ;
24 goto HelloDDKRead_EXIT;
25 }
26
27 // 获取MDL的虚拟地址
28 virtualAddress = MmGetMdlVirtualAddress(pIrp -> MdlAddress);
29 // 获取MDL的长度
30 totalLength = MmGetMdlByteCount(pIrp -> MdlAddress);
31
32 KdPrint(( " DriverB:(pIrp->MdlAddress)MmGetMdlVirtualAddress:%08X\n " ,MmGetMdlVirtualAddress(pIrp -> MdlAddress)));
33 KdPrint(( " DriverB:(pIrp->MdlAddress)MmGetMdlByteCount:%d\n " ,MmGetMdlByteCount(pIrp -> MdlAddress)));
34
35 // 将总的传输,分成几个阶段,这里设定每次阶段的长度
36 if (totalLength > MAX_PACKAGE_SIZE)
37 {
38 stageLength = MAX_PACKAGE_SIZE;
39 } else
40 {
41 stageLength = totalLength;
42 }
43
44 // 创建新的MDL
45 mdl = IoAllocateMdl((PVOID) virtualAddress,
46 totalLength,
47 FALSE,
48 FALSE,
49 NULL);
50
51 KdPrint(( " DriverB:(new mdl)MmGetMdlVirtualAddress:%08X\n " ,MmGetMdlVirtualAddress(mdl)));
52 KdPrint(( " DriverB:(new mdl)MmGetMdlByteCount:%d\n " ,MmGetMdlByteCount(mdl)));
53
54 if (mdl == NULL)
55 {
56 KdPrint(( " DriverB:Failed to alloc mem for mdl\n " ));
57 status = STATUS_INSUFFICIENT_RESOURCES;
58 goto HelloDDKRead_EXIT;
59 }
60
61 // 将IRP的MDL做重新映射
62 IoBuildPartialMdl(pIrp -> MdlAddress,
63 mdl,
64 (PVOID) virtualAddress,
65 stageLength);
66 KdPrint(( " DriverB:(new mdl)MmGetMdlVirtualAddress:%08X\n " ,MmGetMdlVirtualAddress(mdl)));
67 KdPrint(( " DriverB:(new mdl)MmGetMdlByteCount:%d\n " ,MmGetMdlByteCount(mdl)));
68
69 rwContext = (PMYDRIVER_RW_CONTEXT)
70 ExAllocatePool(NonPagedPool, sizeof (MYDRIVER_RW_CONTEXT));
71
72 rwContext -> NewMdl = mdl;
73 rwContext -> PreviousMdl = pIrp -> MdlAddress;
74 rwContext -> Length = totalLength - stageLength; // 还剩下多少没读取
75 rwContext -> Numxfer = 0 ; // 读了多少字节
76 rwContext -> VirtualAddress = ((ULONG_PTR)virtualAddress + stageLength); // 下一阶段开始读取的地址
77 rwContext -> DeviceExtension = pDevExt;
78
79 // 拷贝到底层堆栈
80 IoCopyCurrentIrpStackLocationToNext(pIrp);
81
82 nextStack = IoGetNextIrpStackLocation(pIrp);
83 // 根据底层驱动的实现,底层驱动有可能读取这个数值,也有可能读取mdl的length。
84 nextStack -> Parameters.Read.Length = stageLength;
85
86 pIrp -> MdlAddress = mdl;
87
88 // 设定完成例程
89 IoSetCompletionRoutine(pIrp,
90 (PIO_COMPLETION_ROUTINE)HelloDDKReadCompletion,
91 rwContext,
92 TRUE,
93 TRUE,
94 TRUE);
95
96 IoCallDriver(pDevExt -> TargetDevice,pIrp);
97
98 pIrp -> MdlAddress = rwContext -> PreviousMdl;
99 IoFreeMdl(rwContext -> NewMdl);
100
101 HelloDDKRead_EXIT:
102 // 完成IRP
103 pIrp -> IoStatus.Status = status;
104 pIrp -> IoStatus.Information = totalLength; // bytes xfered
105 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
106 KdPrint(( " DriverB:Leave B HelloDDKRead\n " ));
107 return status;
108 }
中间层驱动,读派遣函数示例代码 P339
图示完成例程与派遣例程 P338
完全例程 示例代码 P342 如上代码示例中