把功能复杂的驱动分解成多个简单的驱动。多个分层驱动程序形成一个设备堆栈,IRP请求首先发送到设备堆栈的顶层,然后以次穿越每层的设备堆栈,最终完成IRP的请求。

1、相关概念

分层驱动是指两个或两个以上的驱动程序,他们分别创建设备对象,并且形成一个由高到低的设备对象栈。IRP请求一般送到设备栈最顶层的设备对象。顶层设备对象可以处理该IRP请求,也可以向下转发。IRP请求结束时,按原路返回。

1_DEVICE_OBJECT结构体,需要注意3个参数:

DriverObject 设备对象对应的驱动对象

NextDevice 记录下一个设备对象的指针

AttachedDevice 记录当前设备对象挂载的设备对象。

WDM驱动程序就属于分层驱动程序。最简单的WDM驱动程序分为两层:一层是PDO(物理设备对象),一层是FDO(功能设备对象),FDO挂载在PDO之上。PDO实现即插即用的功能,FDO完成逻辑功能,而将一些硬件相关请求发往PDL

挂载指的是将高一层的设备对象挂载在低一层的设备对象上,从而形成一个设备栈。

IoAttachDeviceToDeviceStack 挂载

IoDetachDevice 卸载

2I/O 堆栈

用一个叫做IO_STACK_LOCATION的数据结构来保存;它和设备堆栈紧密结合。在IRP中,有一个指向IO_STACK_LOCATION数组的指针。调用IoAllocateIrp创建Irp时,有一个StackSize,就是IO_STACK_LOCATION数组的大小。

31、分层驱动程序

图示 P322

3)向下转发IRP

3种方法处理IRP:直接处理;调用StartIo,向下转发。

一个设备堆栈对应一个IO_STACK_LOCATION堆栈元素。IRP内部有一个指针指向当前正使用的IO_STACK_LOCATIONIoGetCurrentIrpStackLocation 获得。

每次调用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,则意味着完成例程取消。IRPIoCompleteRequest 完成时,会一层层出栈,如果遇到完成例程,则调用完成例程。

当调用IoCallDriver后,当前驱动就失去了对IRP的控制;如果此时设置IRP的属性,会引起系统崩溃。完成例程返回两种状态,STATUS_SUCCESSSTATUS_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

3STATUS_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

通过一个例子来说明。

31、分层驱动程序

图示分层驱动模型 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

31、分层驱动程序

图示完成例程与派遣例程 P338

完全例程 示例代码 P342  如上代码示例中

分类:

技术点:

相关文章:

相关资源