Windows下USB驱动同步URB转IRP请求函数代码-改进版

0 0 2021-07-14 本文地址:http://www.usbzh.com/fun/detail-39.html

URB的同步调用一般使用:Windows下USB驱动同步URB转IRP请求函数代码 http://www.usbzh.com/fun/detail-31.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.

取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!