Windows驱动_USB驱动之六

时间:2021-06-07 17:43:10

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