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