将IRP分解成多个IRP

IRP可以分解成多个小IRP,例如,需要对某个设备读写大量的数据,但是设备所支
持的一次物理读写只有很小的字节数,这样对设备的读写操作就可以分解成多个IRP,每
个IRP所读写的字节数都在设备所允许的范围内。

#pragma PAGEDCODE 
NTSTATUS MyIoCompletion(
IN PDEVICE_OBJECT  DeviceObject,
IN PIRP  pIrp,
IN PVOID  Context
)
{
	KdPrint(("DriverB:Enter B HelloDDKReadCompletion\n"));

	PMYDRIVER_RW_CONTEXT rwContext = (PMYDRIVER_RW_CONTEXT)Context;
	NTSTATUS ntStatus = pIrp->IoStatus.Status;

	ULONG stageLength;

	if (rwContext && NT_SUCCESS(ntStatus))
	{
		//已经传送了多少字节
		rwContext->Numxfer += pIrp->IoStatus.Information;

		if (rwContext->Length)
		{
			//设定下一阶段读取字节数
			if (rwContext->Length > MAX_PACKAGE_SIZE)
			{
				stageLength = MAX_PACKAGE_SIZE;
			}
			else
			{
				stageLength = rwContext->Length;
			}
			//重新利用MDL
			MmPrepareMdlForReuse(rwContext->NewMdl);

			IoBuildPartialMdl(pIrp->MdlAddress,
				rwContext->NewMdl,
				(PVOID)rwContext->VirtualAddress,
				stageLength);

			rwContext->VirtualAddress += stageLength;
			rwContext->Length -= stageLength;

			IoCopyCurrentIrpStackLocationToNext(pIrp);
			PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(pIrp);

			nextStack->Parameters.Read.Length = stageLength;

			IoSetCompletionRoutine(pIrp,
				MyIoCompletion,
				rwContext,
				TRUE,
				TRUE,
				TRUE);

			IoCallDriver(rwContext->DeviceExtension->AttachedDeciveObjectPointer,
				pIrp);

			return STATUS_MORE_PROCESSING_REQUIRED;
		}
		else
		{
			//最后一次传输
			pIrp->IoStatus.Information = rwContext->Numxfer;
		}
	}

	KdPrint(("DriverB:Leave B HelloDDKReadCompletion\n"));
	return STATUS_MORE_PROCESSING_REQUIRED;
}
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
	DbgPrint("enter read proc\n");
	
	NTSTATUS status = STATUS_SUCCESS;
	//获取扩展设备的指针
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	//获得当前I/0堆栈
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

	ULONG totalLength,stageLength;

	//三环buffer的虚拟地址
	PVOID virtualAddress;

	//判断MDL是否为空
	if (!pIrp->MdlAddress)
	{
		status = STATUS_UNSUCCESSFUL;
		pIrp->IoStatus.Status = status;//getlasterror()得到的就是这个值
		pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
		IoCompleteRequest(pIrp, IO_NO_INCREMENT);
		return status;
	}

	virtualAddress = MmGetMdlVirtualAddress(pIrp->MdlAddress);
	totalLength = MmGetMdlByteCount(pIrp->MdlAddress);   // stack->Parameters.Read.Length 这个也可以得到length

	if (totalLength>MAX_PACKAGE_SIZE)
	{
		stageLength = MAX_PACKAGE_SIZE;
	}
	else
	{
		stageLength = totalLength;
	}

	//映射该内存  这里为什么又要再映射一次呢?
	PMDL mdl = IoAllocateMdl(virtualAddress,	//映射的
		totalLength,
		FALSE,
		FALSE,
		NULL
		);
	if (!mdl)
	{
		//失败就返回资源不足
		status = STATUS_INSUFFICIENT_RESOURCES;
		pIrp->IoStatus.Status = status;//getlasterror()得到的就是这个值
		pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
		IoCompleteRequest(pIrp, IO_NO_INCREMENT);
		return status;
	}

	//将IRP的MDL做重新映射
	IoBuildPartialMdl(  pIrp->MdlAddress,
						mdl,
						virtualAddress,
						stageLength
						);

	//分配非分页内存
	PMYDRIVER_RW_CONTEXT rwContext = (PMYDRIVER_RW_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(MYDRIVER_RW_CONTEXT), 'dale');
	
	rwContext->NewMdl = mdl;
	rwContext->PreviousMdl = pIrp->MdlAddress;
	rwContext->Length = totalLength - stageLength; //还剩下多少字节没有读取
	rwContext->Numxfer = 0;  //已经读取了多少字节
	rwContext->VirtualAddress = (ULONG)virtualAddress+stageLength; //下一阶段开始读取的地址
	rwContext->DeviceExtension = pDevExt;

	//复制当前的IO堆栈到下一层设备
	IoCopyCurrentIrpStackLocationToNext(pIrp);

	//得到下一层I/O堆栈
	PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(pIrp);

	//根据底层驱动的实现,底层驱动有可能读取这个数值,也有可能读取mdl的length
	nextStack->Parameters.Read.Length = stageLength;
	pIrp->MdlAddress = mdl;

	//设置完成例程
	IoSetCompletionRoutine(pIrp,
						  (PIO_COMPLETION_ROUTINE)MyIoCompletion,
						  rwContext,
						  TRUE,
						  TRUE,
						  TRUE
											
		);

	//调用底层驱动
	IoCallDriver(pDevExt->AttachedDeciveObjectPointer,pIrp);

	//设置MDL
	pIrp->MdlAddress = rwContext->PreviousMdl;

	//释放Mdl
	IoFreeMdl(rwContext->NewMdl);

	status = STATUS_SUCCESS;
	pIrp->IoStatus.Status = status;//getlasterror()得到的就是这个值
	pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return status;
}

被附加驱动A的Read Proc

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
								 IN PIRP pIrp) 
{
	KdPrint(("DriverA:Enter A HelloDDKRead\n"));
	NTSTATUS status = STATUS_SUCCESS;

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

	ULONG totalLength;
	PVOID virtualAddress;
	if (!pIrp->MdlAddress)
	{
		status = STATUS_UNSUCCESSFUL;
		totalLength = 0;
		goto HelloDDKRead_EXIT;
	}
	
	virtualAddress = MmGetMdlVirtualAddress(pIrp->MdlAddress);
	totalLength = MmGetMdlByteCount(pIrp->MdlAddress);

	RtlFillMemory(virtualAddress,totalLength,0xFF);

	KdPrint(("DriverA:virtualAddress:%x\n",virtualAddress));
	KdPrint(("DriverA:totalLength:%d\n",totalLength));

HelloDDKRead_EXIT:
	// 完成IRP
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = totalLength;	//bytes xfered
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	KdPrint(("DriverA:Leave A HelloDDKRead\n"));
	return status;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值