Windows下USB驱动同步URB转IRP请求函数代码-改进版
2021-08-30
262
1
URB的同步调用一般使用:Windows下USB驱动同步URB转IRP请求函数代码 http://www.usbzh.com/article/detail-547.html
但是,在某些特定的情况下,有时会因为下底设备并没有完成而挂死。
这里提供一种超时取消IRP的方法,同时考虑到了线和切换的情况。
这里的实现机制比较巧妙,在各个完成阶段使用lock来标识当前IRP的运行状况。
lock = 0;
timeoutContext = ALLOCPOOL(NonPagedPool, sizeof(USB_REQUEST_TIMEOUT_CONTEXT));
if (timeoutContext) {
timeoutContext->event = &event;
timeoutContext->lock = &lock;
IoSetCompletionRoutine( irp,
CallDriverSyncCompletion, // context
timeoutContext,
TRUE, TRUE, TRUE);
status = IoCallDriver(devObj, irp);
if (status == STATUS_PENDING) {
dueTime.QuadPart = -10000 * USB_REQUEST_TIMEOUT;
status = KeWaitForSingleObject(
&event,
Executive, // wait reason
KernelMode,
FALSE, // not alertable
&dueTime);
if (status == STATUS_TIMEOUT) {
DBGWARN(("CallDriverSync timed out!\n"));
if (InterlockedExchange(&lock, 1) == 0) {
//
// We got it to the IRP before it was completed. We can cancel
// the IRP without fear of losing it, as the completion routine
// won't let go of the IRP until we say so.
//
IoCancelIrp(irp);
//
// Release the completion routine. If it already got there,
// then we need to complete it ourselves. Otherwise we got
// through IoCancelIrp before the IRP completed entirely.
//
if (InterlockedExchange(&lock, 2) == 3) {
//
// Mark it pending because we switched threads.
//
IoMarkIrpPending(irp);
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
}
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
// Return an error code because STATUS_TIMEOUT is a successful
// code.
irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
}
}
FREEPOOL(timeoutContext);
status = irp->IoStatus.Status;
}
完成例程的代码如下:
NTSTATUS CallDriverSyncCompletion(IN PDEVICE_OBJECT devObjOrNULL, IN PIRP irp, IN PVOID Context)
{
PUSB_REQUEST_TIMEOUT_CONTEXT timeoutContext = Context;
PKEVENT event = timeoutContext->event;
PLONG lock = timeoutContext->lock;
ASSERT(irp->IoStatus.Status != STATUS_IO_TIMEOUT);
InterlockedExchange(lock, 3);
KeSetEvent(event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
可以看到,完成例程的上下文包含一个事件event和一个lock的标识。
初始态lock=0,然后向下调用IRP,等待5秒钟超时后,将lock置1并返回之前的状态,如果还为0,表示真的超时,然后调用IoCancelIrp取消IRP.
取消IRP会引起IoSetCompletionRoutine的调用,在其内部一般会调用IoCompleteRequest,不过在其调用我们的完成例程CallDriverSyncCompletion由于返回了STATUS_MORE_PROCESSING_REQUIRED,表示我们再次获取了IRP的所有权,所以这里我们需要自己调用IRP的完成。
完成例程调用后,会将lock设为3,这里我们再次挂起IRP,重新完成IRP.
HID人机交互QQ群:564808376
UAC音频QQ群:218581009
UVC相机QQ群:331552032
BOT&UASP大容量存储QQ群:258159197
STC-USB单片机QQ群:315457461
USB技术交流QQ群2:580684376
USB技术交流QQ群:952873936