rangzh
驱动小牛
驱动小牛
  • 注册日期2005-04-24
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望150点
  • 贡献值0点
  • 好评度115点
  • 原创分0分
  • 专家分0分
阅读:2127回复:2

请看下这段代码

楼主#
更多 发布于:2007-11-09 22:59
其实就是向USBD提交IRP的代码,不过增加了超时取消机制。取消的代码参考了微软网站上的代码,主要有几点考虑:
1. 取消是一个过程,所以在开始取消和取消完成时进行判断,从而得到完成函数与取消过程执行时序;
2. 由于完成函数中要访问事件,所以在提交代码中确保资源释放前完成函数已经被调用了。但由于后面的问题不明确,不知道,完成函数是否会被两次调用,从而有可能出现等到第一次事件后就释放了资源,而第二次调用在设置时间时必定蓝屏。

主要的疑问,在代码中注释出来了:
1. 如果在取消的过程中, 完成函数被调用了, 那么完成函数是否还会再被调用一次?也就是说, 1那里的事件应不应该设置?
2. 如果取消完成时,  完成函数都未被调用。那随后完成函数是否会被再次调用?2那里的事件应不应该设置?
3. 如果IoCallDriver的返回值不是STATUS_PENDING,那么该IRP应该是同步完成的吧.这种情况下,完成函数是否会被调用?

其实1,2都可归结为对IoCancelIrp的调用是否会导致对完成函数的调用?这又细分为1,2两种情况,即如果完成函数已经执行,IoCancelIrp是否会导致完成函数再次调用;如果完成函数根本未执行, IoCancelIrp是否会导致完成函数的调用?下面的代码是基于否定的回答来写的。但调用IoSetIoCompletionRoutine时有一个参数invokeOnCancel,推测其意义则应该被调用?不解中。。。

问题3则是关于IoCallDriver...

NTSTATUS UsbSyncIrpComplete(
    IN    PDEVICE_OBJECT                        device,
    IN    PIRP                                irp,
    IN    PVOID                                context)
{
    PIF_IO_CONTEXT                            hwContext;

    completionRoutineCalled = TRUE;

    hwContext = (PIF_IO_CONTEXT) context;
    if( InterlockedExchange((PVOID)&hwContext->lock, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED )
    {
        return STATUS_MORE_PROCESSING_REQUIRED;
    }
    KeSetEvent(&hwContext->done, 0, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
#if 0
    if (InterlockedExchange(&hwContext->lock, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED)
    {
        return STATUS_MORE_PROCESSING_REQUIRED;
    }
    return STATUS_CONTINUE_COMPLETION;

    PSYNC_IRP_CONTEXT                        irpContext;

    irpContext = (PSYNC_IRP_CONTEXT)context;
    NdisAcquireSpinLock( &irpContext->lock );
    KeSetEvent( &irpContext->irpDone, IO_NO_INCREMENT, FALSE );
    NdisReleaseSpinLock( &irpContext->lock );

    return STATUS_MORE_PROCESSING_REQUIRED;
#endif
}


NTSTATUS UsbSyncSubmitIrpToUSBD(
    IN    PADAPTER_CONTEXT                    adapter,    
    IN    ULONG                                controlCode,
    IN    PVOID                                arg1,
    IN    PVOID                                arg2)
{
    NTSTATUS                                status, waitStatus;
    IO_STATUS_BLOCK                            statusBlock;    
    PIF_IO_CONTEXT                            context;
    PIRP                                    irp;
    PIO_STACK_LOCATION                        lowerIrpStack;    
    IRPLOCK                                    lock;
    LARGE_INTEGER                            waitTime;
    BOOLEAN                                    cancelled;

    
    status = STATUS_UNSUCCESSFUL;
    waitTime.QuadPart = -CFG_HW_USB_TIMEOUT;
    do
    {        
        // ---------------------------------------------------------------------
        // Build internal IOCTL IRP
        // ---------------------------------------------------------------------
        context = AllocateIOContext(adapter);
        if( NULL == context )
        {
            DBGERROR( "  Failed to allocate irp for USB syncronous operation!\n" );
            status = STATUS_INSUFFICIENT_RESOURCES;            
            break;
        }
        KeClearEvent(&context->done);
        context->lock = IRPLOCK_CANCELABLE;
        irp = context->irp;


        lowerIrpStack = IoGetNextIrpStackLocation( irp );        
        ASSERT( lowerIrpStack != NULL );

        lowerIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        lowerIrpStack->Parameters.DeviceIoControl.IoControlCode = controlCode;
        lowerIrpStack->Parameters.Others.Argument1 = arg1;        
        lowerIrpStack->Parameters.Others.Argument2 = arg2;

        if( AcquireAdapter(adapter) )
        {
            IoSetCompletionRoutine(
                irp,
                UsbSyncIrpComplete,
                context,
                TRUE,
                TRUE,
                TRUE );

            // -----------------------------------------------------------------
            // Submit the IRP and wait if necessary
            // -----------------------------------------------------------------
            status = IoCallDriver( adapter->hc.nextDevice, irp );

            if( STATUS_PENDING == status )
            {                
                status = KeWaitForSingleObject( &context->done, Executive, KernelMode, FALSE, &waitTime );
                if( STATUS_TIMEOUT == status )
                {
                    DBGERROR( "  Usb operation timeout!\n" );
                    // 开始取消时,IRP还未被完成函数处理, 则开始取消
                    if( InterlockedExchange((PVOID)&context->lock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE )
                    {
                        cancelled = IoCancelIrp(irp);
                        // 1. 如果取消过程中完成函数已经被调用了,那么IoCancelIrp是否会导致完成函数再次被调用。也就是说, 完成函数是被调用2次呢, 还是1次?
                        if( InterlockedExchange((PVOID)&context->lock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED )
                        {                            
                            DBGTRACE("Irp completed during cancelling\n");
                            KeSetEvent( &context->done, 0, FALSE );
                        }
                        // 2. 否则成功取消了, 完成函数应该没机会被调用?
                        else
                        {
                            DBGTRACE("Irp cancelled before completion\n");
                            KeSetEvent( &context->done, 0, FALSE );
                        }
                    }
                    // 开始取消前, IRP已经完成了, 则完成函数已经设置完成事件
                    else
                    {
                        DBGTRACE("Irp completed before cancel\n");
                    }

                    status = irp->IoStatus.Status;
                    //KeWaitForSingleObject( &irpContext.irpDone, Executive, KernelMode, FALSE, NULL );
                    while( waitStatus = KeWaitForSingleObject( &context->done, Executive, KernelMode, FALSE, &waitTime ) )
                    {
                        DBGTRACE("IRP Status: %08X\n", status);
                        NdisMSleep(2000);
                    }                
                }
                else
                {
                    DBGTRACE( "  IRP no timeout[%X]\n", status );

                    while( waitStatus = KeWaitForSingleObject( &context->done, Executive, KernelMode, FALSE, &waitTime ) )
                    {
                        NdisMSleep(2000);
                    }                    
                }            

            }
            // 3. 如果IRP未pending,则该IRP应该是同步完成的,完成函数是否被调用?
            else if( NT_SUCCESS(status) )
            {
                status = STATUS_SUCCESS;
                DBGTRACE( "  Control IRP success[%X]\n", status );

                while( waitStatus = KeWaitForSingleObject( &context->done, Executive, KernelMode, FALSE, &waitTime ) )
                {
                    NdisMSleep(2000);
                }                    
            }
            else
            {
                status = irp->IoStatus.Status;
                DBGTRACE( "  Control IRP error[%X]\n", status );

                while( waitStatus = KeWaitForSingleObject( &context->done, Executive, KernelMode, FALSE, &waitTime ) )
                {
                    NdisMSleep(2000);
                }                    
            }
            ReleaseAdapter( adapter );
        }
        //IoFreeIrp( irp );
        FreeIOContext(adapter, context);
    } while( FALSE );        
    return status;
 }
zhoujiamurong
驱动小牛
驱动小牛
  • 注册日期2006-03-20
  • 最后登录2009-05-06
  • 粉丝4
  • 关注0
  • 积分1081分
  • 威望360点
  • 贡献值0点
  • 好评度215点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2008-05-09 10:53
普及知识,谢谢
rangzh
驱动小牛
驱动小牛
  • 注册日期2005-04-24
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望150点
  • 贡献值0点
  • 好评度115点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-11-13 12:03
明白了, 如果在触发取消的那个时间点完成函数已经触发,则取消失败,不会再次调用完成函数;否则取消成功,完成函数也会被调用。

完成函数只会被调用一次
游客

返回顶部