Windows驱动_USB驱动之六

                 今天运动了一下,感觉好很多,虽然有点累,今天,我们谈一下程序员的业余时间,如何分配,众所周知,程序员是必须跟电脑打交道的职业,所以我们的大量的时间会在电脑上度过,再因为,电脑已经称为人们生活中的很重要的一部分,所以大量的业余时间,休息时间都会放在电脑上,程序员也是如此,这样算起来,其实,我们除了睡觉,基本都是和电脑一起度过的。面对这样高强度的工作,我们应该学会休息,运动是再好不过的了,业余时间,最好不要对着电脑,忘记说了,手机现在也跟电脑抢我们的时间,我们不是对着大屏幕,就是对着小屏幕,我认为,我们应该在业余的时间,不要对着屏幕,最好是跟同学,同事一起聚聚,一起打打球啊,当然,我们还是需要在业余时间进行学习的,所以,还是需要保持一定量的学习,后面,我一个星期,准备只更新3篇博客,不像现在这样每天更新,其余的时间,都用来休息。

 

                 昨天我们看了Windows驱动中的USB设备的最后的配置,今天我们来看下USB设备数据传输,包含读,些,IOCTL等。我们知道,当一个应用程序,或者称为上层好了,应该有一个摄像头的应用,它会调用微软的AVSTREAM给出的API,然后由AVSTREAM,结合KS.sys,对USB设备驱动进行调用,怎么跟USB设备驱动进行交换了,它必须找到USB设备驱动的设备对象,因为我们的管道,是FW已经确定的,总线驱动已经PNP管理都知道,所以,框架可以通过管道跟设备驱动进行交互,我们设备驱动的最上层,一般都有个文件对象,作为我们对于外面的接口,这个文件对象,上层进行操作时,系统分配给上层的,文件对象一般都是有文件名,USB管道的索引号一般都在管道的文件对象名中指定,我们再来回忆下UsbSamp_EvtDeviceFileCreate这个函数,首先我们从文件对象中,得到文件名,从文件名中解析出管道到索引号,然后调用       

    

pipe = WdfUsbInterfaceGetConfiguredPipe(
                DeviceContext->UsbInterface,
                (UCHAR)uval, //PipeIndex,
                NULL
                );


得到管道,保存在文件对象的上下文中。

我们首先从USB设备驱动的IOCTL开始,函数为UsbSamp_EvtIoDeviceControl

 

VOID
UsbSamp_EvtIoDeviceControl(
    _In_ WDFQUEUE   Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t     OutputBufferLength,
    _In_ size_t     InputBufferLength,
    _In_ ULONG      IoControlCode
    )
/*++

Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework request object.

    OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.
Return Value:

    VOID

--*/
{
    WDFDEVICE          device;
    PVOID              ioBuffer;
    size_t             bufLength;
    NTSTATUS           status;
    PDEVICE_CONTEXT    pDevContext;
    PFILE_CONTEXT      pFileContext;
    ULONG              length = 0;

    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

    UsbSamp_DbgPrint(3, ("Entered UsbSamp_DispatchDevCtrl\n"));

    PAGED_CODE();

    //
    // initialize variables
    //
    device = WdfIoQueueGetDevice(Queue);
    pDevContext = GetDeviceContext(device);

    switch(IoControlCode) {

    case IOCTL_USBSAMP_RESET_PIPE:

        pFileContext = GetFileContext(WdfRequestGetFileObject(Request));

        if (pFileContext->Pipe == NULL) {
            status = STATUS_INVALID_PARAMETER;
        }
        else {
            status = ResetPipe(pFileContext->Pipe);
        }

        break;

    case IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR:


        if (pDevContext->UsbConfigurationDescriptor) {

            length = pDevContext->UsbConfigurationDescriptor->wTotalLength;

            status = WdfRequestRetrieveOutputBuffer(Request, length, &ioBuffer, &bufLength);
            if (!NT_SUCCESS(status)){
                UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputBuffer failed\n"));
                break;
            }

            RtlCopyMemory(ioBuffer,
                          pDevContext->UsbConfigurationDescriptor,
                          length);

            status = STATUS_SUCCESS;
        }
        else {
            status = STATUS_INVALID_DEVICE_STATE;
        }

        break;

    case IOCTL_USBSAMP_RESET_DEVICE:

        status = ResetDevice(device);
        break;

    default :
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    WdfRequestCompleteWithInformation(Request, status, length);

    UsbSamp_DbgPrint(3, ("Exit UsbSamp_DispatchDevCtrl\n"));

    return;
}

 

IOCTL_USBSAMP_RESET_PIPE:进行管道的重启,这个很简单,通过文件对象上下文中保留的管道,直接调用   

WdfUsbTargetPipeResetSynchronously(Pipe,
                            WDF_NO_HANDLE, // WDFREQUEST
                             NULL  // PWDF_REQUEST_SEND_OPTIONS
 );

进行管道的重启。

NTSTATUS  WdfUsbTargetPipeResetSynchronously(
    IN WDFUSBPIPE  
Pipe
,
    IN OPTIONAL WDFREQUEST  
Request
,
    IN OPTIONAL PWDF_REQUEST_SEND_OPTIONS  
RequestOptions
    );

IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR,请求控制描述符,因为之前我们已经得到了,直接分配内存输出给请求的空间。

IOCTL_USBSAMP_RESET_DEVICE:重启设备的请求,首先调用StopAllPipes停止所有的管道。

VOID
StopAllPipes(
    _In_ PDEVICE_CONTEXT DeviceContext
    )
{
    UCHAR count,i;

    count = DeviceContext->NumberConfiguredPipes;
    for (i = 0; i < count; i++) {
        WDFUSBPIPE pipe;
        pipe = WdfUsbInterfaceGetConfiguredPipe(DeviceContext->UsbInterface,
                                                i, //PipeIndex,
                                                NULL
                                                );
        WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pipe),
                        WdfIoTargetCancelSentIo);
    }
}

这个函数很简单,里面调用的函数,之前有说过。再往下看,调用下面:

//
    // It may not be necessary to check whether device is connected before
    // resetting the port.
    //
    status = WdfUsbTargetDeviceIsConnectedSynchronous(pDeviceContext->WdfUsbTargetDevice);

    if (NT_SUCCESS(status)) {
        status = WdfUsbTargetDeviceResetPortSynchronously(pDeviceContext->WdfUsbTargetDevice);
    }

 

我们直接看下MSDN中的函数就知道了。
NTSTATUS  WdfUsbTargetDeviceIsConnectedSynchronous(
    IN WDFUSBDEVICE  
UsbDevice
    );

NTSTATUS  WdfUsbTargetDeviceResetPortSynchronously(
    IN WDFUSBDEVICE  
UsbDevice
    );

然后,调用StartAllPipes打开所有的PIPE:

StartAllPipes(
    _In_ PDEVICE_CONTEXT DeviceContext
    )
{
    NTSTATUS status;
    UCHAR count,i;

    count = DeviceContext->NumberConfiguredPipes;
    for (i = 0; i < count; i++) {
        WDFUSBPIPE pipe;
        pipe = WdfUsbInterfaceGetConfiguredPipe(DeviceContext->UsbInterface,
                                                i, //PipeIndex,
                                                NULL
                                                );
        status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pipe));
        if (!NT_SUCCESS(status)) {
            UsbSamp_DbgPrint(1, ("StartAllPipes - failed pipe #%d\n", i));
        }
    }
}


这个函数,跟StopAllPipes基本一直,我们就不详述。

下面看下,读请求:

VOID
UsbSamp_EvtIoRead(
    _In_ WDFQUEUE         Queue,
    _In_ WDFREQUEST       Request,
    _In_ size_t           Length
    )
/*++

Routine Description:

    Called by the framework when it receives Read requests.

Arguments:

    Queue - Default queue handle
    Request - Handle to the read/write request
    Lenght - Length of the data buffer associated with the request.
                 The default property of the queue is to not dispatch
                 zero lenght read & write requests to the driver and
                 complete is with status success. So we will never get
                 a zero length request.

Return Value:


--*/
{
    PFILE_CONTEXT           fileContext = NULL;
    WDFUSBPIPE              pipe;
    WDF_USB_PIPE_INFORMATION   pipeInfo;

    PAGED_CODE();

    //
    // Get the pipe associate with this request.
    //
    fileContext = GetFileContext(WdfRequestGetFileObject(Request));
    pipe = fileContext->Pipe;
    if (pipe == NULL) {
        UsbSamp_DbgPrint(1, ("pipe handle is NULL\n"));
        WdfRequestCompleteWithInformation(Request, STATUS_INVALID_PARAMETER, 0);
        return;
    }
    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(pipe, &pipeInfo);

    if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) ||
       (WdfUsbPipeTypeInterrupt == pipeInfo.PipeType)) {

        ReadWriteBulkEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeRead);
        return;

    } 
    else if (WdfUsbPipeTypeIsochronous == pipeInfo.PipeType){

#if !defined(BUFFERED_READ_WRITE) // if doing DIRECT_IO
        ReadWriteIsochEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeRead);
        return;
#endif

    }

    UsbSamp_DbgPrint(1, ("ISO transfer is not supported for buffered I/O transfer\n"));
    WdfRequestCompleteWithInformation(Request, STATUS_INVALID_DEVICE_REQUEST, 0);

    return;
}

这里,有分辨是中断或者BULK读请求还是同步读请求,Bulk和中断读写请求,调用ReadWriteBulkEndPoints,同步读写请求ReadWriteIsochEndPoints,这里我们说明一下,一般我们进行同步读写请求时,一般是通过DIRECT_IO的形式。因为读写请求都是调用这两个函数,所以写请求,呆会就不分析了,我们先看ReadWriteBulkEndPoints

VOID
ReadWriteBulkEndPoints(
    _In_ WDFQUEUE         Queue,
    _In_ WDFREQUEST       Request,
    _In_ ULONG            Length,
    _In_ WDF_REQUEST_TYPE RequestType
    )
/*++

Routine Description:

    This callback is invoked when the framework received  WdfRequestTypeRead or
    RP_MJ_WRITE request. This read/write is performed in stages of
    maximum transfer size. Once a stage of transfer is complete, then the
    request is circulated again, until the requested length of transfer is
    performed.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.

    Request - Handle to a framework request object. This one represents
              the WdfRequestTypeRead/WdfRequestTypeWrite IRP received by the framework.

    Length - Length of the input/output buffer.

Return Value:

   VOID

--*/
{
    size_t                totalLength = Length;
    size_t                stageLength = 0;
    NTSTATUS              status;
    PVOID                 virtualAddress = 0;
    PREQUEST_CONTEXT      rwContext = NULL;
    PFILE_CONTEXT         fileContext = NULL;
    WDFUSBPIPE            pipe;
    WDF_USB_PIPE_INFORMATION pipeInfo;
    WDFMEMORY             reqMemory;
    WDFMEMORY_OFFSET      offset;
    WDF_OBJECT_ATTRIBUTES objectAttribs;
    PDEVICE_CONTEXT       deviceContext;
    PPIPE_CONTEXT         pipeContext;

    ULONG                 maxTransferSize;
 
    UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - begins\n"));

    //
    // First validate input parameters.
    //
    deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));

    if (totalLength > deviceContext->MaximumTransferSize) {
        UsbSamp_DbgPrint(1, ("Transfer length > circular buffer\n"));
        status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    if ((RequestType != WdfRequestTypeRead) &&
        (RequestType != WdfRequestTypeWrite)) {
        UsbSamp_DbgPrint(1, ("RequestType has to be either Read or Write\n"));
        status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    //
    // Get the pipe associate with this request.
    //
    fileContext = GetFileContext(WdfRequestGetFileObject(Request));
    pipe = fileContext->Pipe;
    pipeContext = GetPipeContext(pipe);
    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(pipe, &pipeInfo);

    rwContext = GetRequestContext(Request);

    if (RequestType == WdfRequestTypeRead) {
        status = WdfRequestRetrieveOutputBuffer(Request, Length, &virtualAddress, &totalLength);
        rwContext->Read = TRUE;

    } 
    else { //Write

        status = WdfRequestRetrieveInputBuffer(Request, Length, &virtualAddress, &totalLength);
        rwContext->Read = FALSE;
    }

    if (!NT_SUCCESS(status)){
        UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputBuffer failed\n"));
        goto Exit;
    }

    //
    // The transfer request is for totalLength.
    // We can perform a max of maxTransfersize in each stage.
    //
    maxTransferSize = GetMaxTransferSize(pipe, deviceContext);

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

    WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs);
    objectAttribs.ParentObject = Request;
    status = WdfMemoryCreatePreallocated(&objectAttribs,
                                         virtualAddress,
                                         totalLength,
                                         &reqMemory);
    if (!NT_SUCCESS(status)){
        UsbSamp_DbgPrint(1, ("WdfMemoryCreatePreallocated failed\n"));
        goto Exit;
    }

    offset.BufferOffset = 0;
    offset.BufferLength = stageLength;

    //
    // The framework format call validates to make sure that you are reading or
    // writing to the right pipe type, sets the appropriate transfer flags,
    // creates an URB and initializes the request.
    //
    if (RequestType == WdfRequestTypeRead) {

        UsbSamp_DbgPrint(3, ("Read operation\n"));
        status = WdfUsbTargetPipeFormatRequestForRead(pipe,
                                                      Request,
                                                      reqMemory,
                                                      &offset);
    } 
    else {

        UsbSamp_DbgPrint(3, ("Write operation\n"));
        status = WdfUsbTargetPipeFormatRequestForWrite(pipe,
                                                       Request,
                                                       reqMemory,
                                                       &offset);
    }

    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeFormatRequest failed 0x%x\n", status));
        goto Exit;
    }

#if (NTDDI_VERSION >= NTDDI_WIN8)
    //
    // If the request is for a super speed bulk pipe with streams,
    // configure its urb's PipeHandle with its associated stream's PipeHandle
    //
    if(WdfUsbPipeTypeBulk == pipeInfo.PipeType &&
        pipeContext->StreamConfigured == TRUE) {
        ConfigureStreamPipeHandleForRequest(Request, pipe);
    }
#endif

    WdfRequestSetCompletionRoutine(
                            Request,
                            UsbSamp_EvtReadWriteCompletion,
                            deviceContext);
    //
    // set REQUEST_CONTEXT parameters.
    //
    rwContext->Length  = (ULONG)totalLength - (ULONG)stageLength;
    rwContext->Numxfer = 0;

    //
    // Send the request asynchronously.
    //
    if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) {
        UsbSamp_DbgPrint(1, ("WdfRequestSend failed\n"));
        status = WdfRequestGetStatus(Request);
        goto Exit;
    }


Exit:
    if (!NT_SUCCESS(status)) {
        WdfRequestCompleteWithInformation(Request, status, 0);
    }

    UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - ends\n"));

    return;
}


别看这个函数的代码很长,其实逻辑很简单,主要调用WdfUsbTargetPipeFormatRequestForRead和WdfUsbTargetPipeFormatRequestForWrite,这两个函数都是异步的,所以下面有设置完成例程。看下这两个函数在MSDN中的说明,其实很简单就不说了。

NTSTATUS   WdfUsbTargetPipeFormatRequestForRead(
    IN WDFUSBPIPE  
Pipe
,
    IN WDFREQUEST  
Request
,
    IN OPTIONAL WDFMEMORY  
ReadMemory
,
    IN OPTIONAL PWDFMEMORY_OFFSET  
ReadOffset
    );

NTSTATUS  WdfUsbTargetPipeFormatRequestForWrite(
    IN WDFUSBPIPE  
Pipe
,
    IN WDFREQUEST  
Request
,
    IN OPTIONAL WDFMEMORY  
WriteMemory
,
    IN OPTIONAL PWDFMEMORY_OFFSET  
WriteOffset
    );

 

 

对于Windows 8,满足如下的条件,还要进行额外的配置

#if (NTDDI_VERSION >= NTDDI_WIN8)
    //
    // If the request is for a super speed bulk pipe with streams,
    // configure its urb's PipeHandle with its associated stream's PipeHandle
    //
    if(WdfUsbPipeTypeBulk == pipeInfo.PipeType &&
        pipeContext->StreamConfigured == TRUE) {
        ConfigureStreamPipeHandleForRequest(Request, pipe);
    }
#endif


看下ConfigureStreamPipeHandleForRequest这个函数:

VOID
ConfigureStreamPipeHandleForRequest(
    _In_ WDFREQUEST       Request,
    _In_ WDFUSBPIPE       Pipe
    )
/*++

Routine Description:

    The framework has formated request for super speed bulk pipe. 
    For stream transfer, use the associated stream's PipeHandle for transfer.

Arguments:
    
    Request - Read/Write Request.

    Pipe - Bullk Pipe

Return Value:

    NULL

--*/
{
    PIRP                irp;
    PIO_STACK_LOCATION  irpSp;
    PURB                urb;

    //
    // Get the IRP that is associated with the framework request object.
    //
    irp = WdfRequestWdmGetIrp(Request);

    //
    // Obtain the next-lower driver's I/O stack location in the IRP
    //
    irpSp = IoGetNextIrpStackLocation(irp);

    //
    // The framework uses pipe's Pipehandle for data transfer by default.
    // For stream transfer, we should use the associated stream's PipeHandle of 
    // the super speed bulk pipe for transfer. Replace the PipeHandle with 
    // its associated stream's PipeHandle .
    //
    urb = irpSp->Parameters.Others.Argument1;
    urb->UrbBulkOrInterruptTransfer.PipeHandle = GetStreamPipeHandleFromBulkPipe(Pipe);

}

这里,我们从Request得到了IRP,然后从IRP得到了下层的STACK,下面调用GetStreamPipeHandleFromBulkPipe,这个函数还不清楚,只知道在Win8中新加的关于Stream方面的。后续再进行详述。

USBD_PIPE_HANDLE
GetStreamPipeHandleFromBulkPipe(
    _In_ WDFUSBPIPE                 Pipe
    )
/*++

Routine Description:

    This routine gets a stream USBD_PIPE_HANDLE from a super speed bulk pipe

Arguments:

    Pipe - Bullk Pipe

Return Value:

    A stream's USBD_PIPE_HANDLE

--*/
{
    PPIPE_CONTEXT               pipeContext;

    PUSBSAMP_STREAM_INFO        pStreamInfo;
    USBD_PIPE_HANDLE            streamPipeHandle;
    ULONG                       index;

    pipeContext = GetPipeContext(Pipe);

    if (pipeContext->StreamConfigured == FALSE) 
    {
        streamPipeHandle = NULL;
        goto End;
    }

    pStreamInfo = &pipeContext->StreamInfo;

    if (pStreamInfo->NumberOfStreams == 0 ||
        pStreamInfo->StreamList == NULL)
    {
         streamPipeHandle = NULL;
         goto End;
    }

    //
    // Specify one associate stream's PipeHandle as the super speed bulk endpoint's PipeHandle
    //
    index = 1;

    streamPipeHandle = pStreamInfo->StreamList[index].PipeHandle;

End:
    return streamPipeHandle;

}


再看,我们设置了一个读写完成例程:

    WdfRequestSetCompletionRoutine(
                            Request,
                            UsbSamp_EvtReadWriteCompletion,
                            deviceContext);


我们看下这个完成例程UsbSamp_EvtReadWriteCompletion

VOID
UsbSamp_EvtReadWriteCompletion(
    _In_ WDFREQUEST                  Request,
    _In_ WDFIOTARGET                 Target,
    PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
    _In_ WDFCONTEXT                  Context
    )
/*++

Routine Description:

    This is the completion routine for reads/writes
    If the irp completes with success, we check if we
    need to recirculate this irp for another stage of
    transfer.

Arguments:

    Context - Driver supplied context
    Device - Device handle
    Request - Request handle
    Params - request completion params

Return Value:
    None

--*/
{
    PMDL                    requestMdl;
    WDFUSBPIPE              pipe;
    ULONG                   stageLength;
    NTSTATUS               status;
    PREQUEST_CONTEXT        rwContext;
    PURB                    urb;
    PCHAR                   operation;
    ULONG                   bytesReadWritten;
    ULONG                   maxTransferSize;
    PDEVICE_CONTEXT         deviceContext;

    rwContext = GetRequestContext(Request);
    deviceContext = Context;

    if (rwContext->Read) {
        operation = "Read";
    } 
    else {
        operation = "Write";
    }

    pipe = (WDFUSBPIPE) Target   ;
    status = CompletionParams->IoStatus.Status;

    if (!NT_SUCCESS(status)){
        //
        // Queue a workitem to reset the pipe because the completion could be
        // running at DISPATCH_LEVEL.
        //
        QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe);
        goto End;
    }

    urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL);
    bytesReadWritten = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
    rwContext->Numxfer += bytesReadWritten;

    //
    // If there is anything left to transfer.
    //
    if (rwContext->Length == 0) {
        //
        // this is the last transfer
        //
        WdfRequestSetInformation(Request, rwContext->Numxfer);
        goto End;
    }

    //
    // Start another transfer
    //
    UsbSamp_DbgPrint(3, ("Stage next %s transfer...\n", operation));

    //
    // The transfer request is for totalLength. 
    // We can perform a max of maxTransfersize in each stage.
    //
    maxTransferSize = GetMaxTransferSize(pipe, deviceContext);

    if (rwContext->Length > maxTransferSize) {
        stageLength = maxTransferSize;
    }
    else {
        stageLength = rwContext->Length;
    }

    //
    // Following call is required to free any mapping made on the partial MDL
    // and reset internal MDL state.
    //
    MmPrepareMdlForReuse(rwContext->Mdl);

    if (rwContext->Read) {
        status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl);
        if (!NT_SUCCESS(status)){
            UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl for Read failed %x\n", status));
            goto End;
        }
    } 
    else {
        status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl);
        if (!NT_SUCCESS(status)){
            UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl for Write failed %x\n", status));
            goto End;
        }
    }

    IoBuildPartialMdl(requestMdl,
                      rwContext->Mdl,
                      (PVOID) rwContext->VirtualAddress,
                      stageLength);

    //
    // reinitialize the urb
    //
    urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength;

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

    //
    // Format the request to send a URB to a USB pipe.
    //
    status = WdfUsbTargetPipeFormatRequestForUrb(pipe,
                                Request,
                                rwContext->UrbMemory,
                                NULL);
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n"));
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto End;
    }

    WdfRequestSetCompletionRoutine(Request, UsbSamp_EvtReadWriteCompletion, deviceContext);

    //
    // Send the request asynchronously.
    //
    if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) {
        UsbSamp_DbgPrint(1, ("WdfRequestSend for %s failed\n", operation));
        status = WdfRequestGetStatus(Request);
        goto End;
    }

    //
    // Else when the request completes, this completion routine will be
    // called again.
    //
    return;

End:
    //
    // We are here because the request failed or some other call failed.
    // Dump the request context, complete the request and return.
    //
    DbgPrintRWContext(rwContext);

    IoFreeMdl(rwContext->Mdl);

    UsbSamp_DbgPrint(3, ("%s request completed with status 0x%x\n",
                                                    operation, status));

    WdfRequestComplete(Request, status);

    return;
}


这个完成例程分为两部分,首先如果请求完成,返回SUCCESS,我们就调用QueuePassiveLevelCallback,创建一个WorkItem,进行PIPE的Reset,如果不成功,我们还需要继续发送这个请求,继续设置这个完成例程,直到成功。这里我们调用的是WdfUsbTargetPipeFormatRequestForUrb,跟之前的Read Write差不多,我们看下MSDN中的说明:

NTSTATUS   WdfUsbTargetPipeFormatRequestForUrb(
    IN WDFUSBPIPE  
Pipe
,
    IN WDFREQUEST  
Request
,
    IN WDFMEMORY  
UrbMemory
,
    IN OPTIONAL PWDFMEMORY_OFFSET  
UrbMemoryOffset
   );
至此,USB的中断传输和BULK传输都已经介绍完了,后续开始介绍同步传输,因为同步传输比较复杂,明天再来看一下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值