static VOID UsbChief_ReadCompletion(IN WDFREQUEST Request, IN WDFIOTARGET Target, PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, IN WDFCONTEXT Context) { PMDL requestMdl; WDFUSBPIPE pipe; ULONG stageLength; NTSTATUS status; PREQUEST_CONTEXT rwContext; PURB urb; ULONG bytesRead; UNREFERENCED_PARAMETER(Context); rwContext = GetRequestContext(Request); pipe = (WDFUSBPIPE)Target; status = CompletionParams->IoStatus.Status; if (!NT_SUCCESS(status)){ UsbChief_QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe); goto End; } urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL); bytesRead = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; rwContext->Numxfer += bytesRead; if (rwContext->Length == 0) { WdfRequestSetInformation(Request, rwContext->Numxfer); goto End; } if (rwContext->Length > MAX_TRANSFER_SIZE) stageLength = MAX_TRANSFER_SIZE; else stageLength = rwContext->Length; UsbChief_DbgPrint(DEBUG_RW, ("Stage next Read transfer... %d bytes remaing\n", rwContext->Length)); MmPrepareMdlForReuse(rwContext->Mdl); status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)){ UsbChief_DbgPrint(0, ("WdfRequestRetrieveOutputWdmMdl for Read failed %x\n", status)); goto End; } IoBuildPartialMdl(requestMdl, rwContext->Mdl, (PVOID) rwContext->VirtualAddress, stageLength); urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength; rwContext->VirtualAddress += stageLength; rwContext->Length -= stageLength; status = WdfUsbTargetPipeFormatRequestForUrb(pipe, Request, rwContext->UrbMemory, NULL); if (!NT_SUCCESS(status)) { UsbChief_DbgPrint(0, ("Failed to format requset for urb\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto End; } WdfRequestSetCompletionRoutine(Request, UsbChief_ReadCompletion, NULL); if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { UsbChief_DbgPrint(0, ("WdfRequestSend for Read failed\n")); status = WdfRequestGetStatus(Request); goto End; } return; End: IoFreeMdl(rwContext->Mdl); UsbChief_DbgPrint(DEBUG_RW, ("Read request completed with status 0x%x\n", status)); WdfRequestComplete(Request, status); }
void AndroidUsbPipeFileObject::OnCommonReadWriteCompletion( WDFREQUEST request, PWDF_REQUEST_COMPLETION_PARAMS completion_params, AndroidUsbWdfRequestContext* context) { ASSERT_IRQL_LOW_OR_DISPATCH(); NTSTATUS status = completion_params->IoStatus.Status; if (!NT_SUCCESS(status)){ GoogleDbgPrint("\n========== Request completed with failure: %X", status); IoFreeMdl(context->mdl); // If this was IOCTL-originated write we must unlock and free // our transfer MDL. if (context->is_ioctl && !context->is_read) { MmUnlockPages(context->transfer_mdl); IoFreeMdl(context->transfer_mdl); } WdfRequestComplete(request, status); return; } // Get our URB buffer PURB urb = reinterpret_cast<PURB>(WdfMemoryGetBuffer(context->urb_mem, NULL)); ASSERT(NULL != urb); // Lets see how much has been transfered and update our counters accordingly ULONG bytes_transfered = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; // We expect writes to transfer entire packet ASSERT((bytes_transfered == context->transfer_size) || context->is_read); context->num_xfer += bytes_transfered; context->length -= bytes_transfered; // Is there anything left to transfer? Now, by the protocol we should // successfuly complete partial reads, instead of waiting on full set // of requested bytes being accumulated in the read buffer. if ((0 == context->length) || context->is_read) { status = STATUS_SUCCESS; // This was the last transfer if (context->is_ioctl && !context->is_read) { // For IOCTL-originated writes we have to return transfer size through // the IOCTL's output buffer. ULONG* ret_transfer = reinterpret_cast<ULONG*>(OutAddress(request, NULL)); ASSERT(NULL != ret_transfer); if (NULL != ret_transfer) *ret_transfer = context->num_xfer; WdfRequestSetInformation(request, sizeof(ULONG)); // We also must unlock / free transfer MDL MmUnlockPages(context->transfer_mdl); IoFreeMdl(context->transfer_mdl); } else { // For other requests we report transfer size through the request I/O // completion status. WdfRequestSetInformation(request, context->num_xfer); } IoFreeMdl(context->mdl); WdfRequestComplete(request, status); return; } // There are something left for the transfer. Prepare for it. // Required to free any mapping made on the partial MDL and // reset internal MDL state. MmPrepareMdlForReuse(context->mdl); // Update our virtual address context->virtual_address = reinterpret_cast<char*>(context->virtual_address) + bytes_transfered; // Calculate size of this transfer ULONG stage_len = (context->length > GetTransferGranularity()) ? GetTransferGranularity() : context->length; IoBuildPartialMdl(context->transfer_mdl, context->mdl, context->virtual_address, stage_len); // Reinitialize the urb and context urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stage_len; context->transfer_size = stage_len; // Format the request to send a URB to a USB pipe. status = WdfUsbTargetPipeFormatRequestForUrb(wdf_pipe(), request, context->urb_mem, NULL); ASSERT(NT_SUCCESS(status)); if (!NT_SUCCESS(status)) { if (context->is_ioctl && !context->is_read) { MmUnlockPages(context->transfer_mdl); IoFreeMdl(context->transfer_mdl); } IoFreeMdl(context->mdl); WdfRequestComplete(request, status); return; } // Reset the completion routine WdfRequestSetCompletionRoutine(request, CommonReadWriteCompletionEntry, this); // Send the request asynchronously. if (!WdfRequestSend(request, wdf_pipe_io_target(), WDF_NO_SEND_OPTIONS)) { if (context->is_ioctl && !context->is_read) { MmUnlockPages(context->transfer_mdl); IoFreeMdl(context->transfer_mdl); } status = WdfRequestGetStatus(request); IoFreeMdl(context->mdl); WdfRequestComplete(request, status); } }
NTSTATUS BthEchoRepeatReaderSubmit( __in PBTHECHOSAMPLE_DEVICE_CONTEXT_HEADER DevCtxHdr, __in PBTHECHO_REPEAT_READER RepeatReader ) /*++ Description: This routine submits the repeat reader. In case of failure it invoked contreader failed callback Arguments: DevCtxHdr - Device context header RepeatReader - Repeat reader to submit Return Value: NTSTATUS Status code. --*/ { NTSTATUS status, statusReuse; WDF_REQUEST_REUSE_PARAMS reuseParams; struct _BRB_L2CA_ACL_TRANSFER *brb = &RepeatReader->TransferBrb; DevCtxHdr->ProfileDrvInterface.BthReuseBrb((PBRB)brb, BRB_L2CA_ACL_TRANSFER); WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_UNSUCCESSFUL); statusReuse = WdfRequestReuse(RepeatReader->RequestPendingRead, &reuseParams); ASSERT(NT_SUCCESS(statusReuse)); UNREFERENCED_PARAMETER(statusReuse); // // Check if we are stopping, if yes set StopEvent and exit. // // After this point request is eligible for cancellation, so if this // flag gets set after we check it, request will be cancelled for stopping // the repeat reader and next time around we will stop when we are invoked // again upon completion of cancelled request. // if (RepeatReader->Stopping) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CONT_READER, "Continuos reader 0x%p stopping", RepeatReader); KeSetEvent(&RepeatReader->StopEvent, 0, FALSE); status = STATUS_SUCCESS; goto exit; } // // Format request for L2CA IN transfer // status = BthEchoConnectionObjectFormatRequestForL2CaTransfer( RepeatReader->Connection, RepeatReader->RequestPendingRead, &brb, RepeatReader->MemoryPendingRead, ACL_TRANSFER_DIRECTION_IN | ACL_SHORT_TRANSFER_OK ); if (!NT_SUCCESS(status)) { goto exit; } // // Set a CompletionRoutine callback function. // WdfRequestSetCompletionRoutine( RepeatReader->RequestPendingRead, BthEchoRepeatReaderPendingReadCompletion, RepeatReader ); // // Clear the stop event before sending the request // This is relevant only on start of the repeat reader // (i.e. the first submission) // this event eventually gets set only when repeat reader stops // and not on every resubmission. // KeClearEvent(&RepeatReader->StopEvent); if (FALSE == WdfRequestSend( RepeatReader->RequestPendingRead, DevCtxHdr->IoTarget, NULL )) { status = WdfRequestGetStatus(RepeatReader->RequestPendingRead); TraceEvents(TRACE_LEVEL_ERROR, DBG_CONT_READER, "Request send failed for request 0x%p, Brb 0x%p, Status code %!STATUS!\n", RepeatReader->RequestPendingRead, brb, status ); goto exit; } else { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CONT_READER, "Resubmited pending read with request 0x%p, Brb 0x%p", RepeatReader->RequestPendingRead, brb ); } exit: if (!NT_SUCCESS(status)) { // // Invoke the reader failed callback before setting the event // to ensure that the connection object is alive during this callback // RepeatReader->Connection->ContinuousReader.BthEchoConnectionObjectContReaderFailedCallback( RepeatReader->Connection->DevCtxHdr, RepeatReader->Connection ); // // If we failed to send pending read, set the event since // we will not get completion callback // KeSetEvent(&RepeatReader->StopEvent, 0, FALSE); } return status; }
NTSTATUS AndroidUsbPipeFileObject::CommonBulkReadWrite( WDFREQUEST request, PMDL transfer_mdl, ULONG length, bool is_read, ULONG time_out, bool is_ioctl) { ASSERT_IRQL_LOW_OR_DISPATCH(); ASSERT(IsPipeAttached()); if (!IsPipeAttached()) { WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE); return STATUS_INVALID_DEVICE_STATE; } // Quick access check. Might be redundant though... ASSERT((is_read && is_input_pipe()) || (!is_read && is_output_pipe())); if ((is_read && is_output_pipe()) || (!is_read && is_input_pipe())) { WdfRequestComplete(request, STATUS_ACCESS_DENIED); return STATUS_ACCESS_DENIED; } // Set URB flags ULONG urb_flags = USBD_SHORT_TRANSFER_OK | (is_read ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT); // Calculate transfer length for this stage. ULONG stage_len = (length > GetTransferGranularity()) ? GetTransferGranularity() : length; // Get virtual address that we're gonna use in the transfer. // We rely here on the fact that we're in the context of the calling thread. void* virtual_address = MmGetMdlVirtualAddress(transfer_mdl); // Allocate our private MDL for this address which we will use for the transfer PMDL new_mdl = IoAllocateMdl(virtual_address, length, FALSE, FALSE, NULL); ASSERT(NULL != new_mdl); if (NULL == new_mdl) { WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES); return STATUS_INSUFFICIENT_RESOURCES; } // Map the portion of user buffer that we're going to transfer at this stage // to our mdl. IoBuildPartialMdl(transfer_mdl, new_mdl, virtual_address, stage_len); // Allocate memory for URB and associate it with this request WDF_OBJECT_ATTRIBUTES mem_attrib; WDF_OBJECT_ATTRIBUTES_INIT(&mem_attrib); mem_attrib.ParentObject = request; WDFMEMORY urb_mem = NULL; PURB urb = NULL; NTSTATUS status = WdfMemoryCreate(&mem_attrib, NonPagedPool, GANDR_POOL_TAG_BULKRW_URB, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), &urb_mem, reinterpret_cast<PVOID*>(&urb)); ASSERT(NT_SUCCESS(status) && (NULL != urb)); if (!NT_SUCCESS(status)) { IoFreeMdl(new_mdl); WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES); return STATUS_INSUFFICIENT_RESOURCES; } // Get USB pipe handle for our pipe and initialize transfer request for it USBD_PIPE_HANDLE usbd_pipe_hndl = usbd_pipe(); ASSERT(NULL != usbd_pipe_hndl); if (NULL == usbd_pipe_hndl) { IoFreeMdl(new_mdl); WdfRequestComplete(request, STATUS_INTERNAL_ERROR); return STATUS_INTERNAL_ERROR; } // Initialize URB with request information UsbBuildInterruptOrBulkTransferRequest( urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), usbd_pipe_hndl, NULL, new_mdl, stage_len, urb_flags, NULL); // Build transfer request status = WdfUsbTargetPipeFormatRequestForUrb(wdf_pipe(), request, urb_mem, NULL); ASSERT(NT_SUCCESS(status)); if (!NT_SUCCESS(status)) { IoFreeMdl(new_mdl); WdfRequestComplete(request, status); return status; } // Initialize our request context. AndroidUsbWdfRequestContext* context = GetAndroidUsbWdfRequestContext(request); ASSERT(NULL != context); if (NULL == context) { IoFreeMdl(new_mdl); WdfRequestComplete(request, STATUS_INTERNAL_ERROR); return STATUS_INTERNAL_ERROR; } context->object_type = AndroidUsbWdfObjectTypeRequest; context->urb_mem = urb_mem; context->transfer_mdl = transfer_mdl; context->mdl = new_mdl; context->length = length; context->transfer_size = stage_len; context->num_xfer = 0; context->virtual_address = virtual_address; context->is_read = is_read; context->initial_time_out = time_out; context->is_ioctl = is_ioctl; // Set our completion routine WdfRequestSetCompletionRoutine(request, CommonReadWriteCompletionEntry, this); // Init send options (our timeout goes here) WDF_REQUEST_SEND_OPTIONS send_options; if (0 != time_out) { WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_TIMEOUT); WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&send_options, WDF_REL_TIMEOUT_IN_MS(time_out)); } // Timestamp first WdfRequestSend KeQuerySystemTime(&context->sent_at); // Send request asynchronously. if (WdfRequestSend(request, wdf_pipe_io_target(), (0 == time_out) ? WDF_NO_SEND_OPTIONS : &send_options)) { return STATUS_SUCCESS; } // Something went wrong here status = WdfRequestGetStatus(request); ASSERT(!NT_SUCCESS(status)); GoogleDbgPrint("\n!!!!! CommonBulkReadWrite: WdfRequestGetStatus (is_read = %u) failed: %08X", is_read, status); WdfRequestCompleteWithInformation(request, status, 0); return status; }
VOID ReadWriteCompletion( 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 --*/ { WDFUSBPIPE pipe; ULONG stageLength; NTSTATUS status; PREQUEST_CONTEXT rwContext; ULONG bytesReadWritten; WDFMEMORY_OFFSET offset; PCHAR operation; PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; UNREFERENCED_PARAMETER(Context); rwContext = GetRequestContext(Request); usbCompletionParams = CompletionParams->Parameters.Usb.Completion; rwContext = GetRequestContext(Request); if (rwContext->Read) { operation = "Read"; bytesReadWritten = usbCompletionParams->Parameters.PipeRead.Length; } else { operation = "Write"; bytesReadWritten = usbCompletionParams->Parameters.PipeWrite.Length; } 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. // TODO: preallocate per pipe workitem to avoid allocation failure. QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe); goto End; } 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)); if (rwContext->Length > MAX_TRANSFER_SIZE) { stageLength = MAX_TRANSFER_SIZE; } else { stageLength = rwContext->Length; } offset.BufferOffset = rwContext->Numxfer; offset.BufferLength = stageLength; rwContext->Length -= stageLength; if (rwContext->Read) { status = WdfUsbTargetPipeFormatRequestForRead( pipe, Request, usbCompletionParams->Parameters.PipeRead.Buffer, &offset); } else { status = WdfUsbTargetPipeFormatRequestForWrite( pipe, Request, usbCompletionParams->Parameters.PipeWrite.Buffer, &offset); } if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeFormat%sRequest failed 0x%x\n", operation, status)); goto End; } WdfRequestSetCompletionRoutine( Request, ReadWriteCompletion, NULL); // // 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); UsbSamp_DbgPrint(3, ("%s request completed with status 0x%x\n", operation, status)); WdfRequestComplete(Request, status); return; }
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 MAX_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; WDFMEMORY reqMemory; WDFMEMORY_OFFSET offset; WDF_OBJECT_ATTRIBUTES objectAttribs; PDEVICE_CONTEXT deviceContext; 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; 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; } // // If the totalLength exceeds MAX_TRANSFER_SIZE, we will break // that into multiple transfer of size no more than MAX_TRANSFER_SIZE // in each stage. // if (totalLength > MAX_TRANSFER_SIZE) { stageLength = MAX_TRANSFER_SIZE; } 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; } WdfRequestSetCompletionRoutine( Request, ReadWriteCompletion, NULL); // // set REQUEST_CONTEXT parameters. // rwContext->Length = totalLength - 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; }
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 WdfRequestTypeWrite request. This read/write is performed in stages of MAX_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 --*/ { PMDL newMdl=NULL, requestMdl = NULL; PURB urb = NULL; WDFMEMORY urbMemory; ULONG totalLength = Length; ULONG stageLength = 0; ULONG urbFlags = 0; NTSTATUS status; ULONG_PTR virtualAddress = 0; PREQUEST_CONTEXT rwContext = NULL; PFILE_CONTEXT fileContext = NULL; WDFUSBPIPE pipe; WDF_USB_PIPE_INFORMATION pipeInfo; WDF_OBJECT_ATTRIBUTES objectAttribs; USBD_PIPE_HANDLE usbdPipeHandle; PDEVICE_CONTEXT deviceContext; 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; WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); if((WdfUsbPipeTypeBulk != pipeInfo.PipeType) && (WdfUsbPipeTypeInterrupt != pipeInfo.PipeType)) { UsbSamp_DbgPrint(1, ("Usbd pipe type is not bulk or interrupt\n")); status = STATUS_INVALID_DEVICE_REQUEST; goto Exit; } rwContext = GetRequestContext(Request); if(RequestType == WdfRequestTypeRead) { status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)){ UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl failed %x\n", status)); goto Exit; } urbFlags |= USBD_TRANSFER_DIRECTION_IN; rwContext->Read = TRUE; UsbSamp_DbgPrint(3, ("Read operation\n")); } else { status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)){ UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl failed %x\n", status)); goto Exit; } urbFlags |= USBD_TRANSFER_DIRECTION_OUT; rwContext->Read = FALSE; UsbSamp_DbgPrint(3, ("Write operation\n")); } urbFlags |= USBD_SHORT_TRANSFER_OK; virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(requestMdl); // // the transfer request is for totalLength. // we can perform a max of MAX_TRANSFER_SIZE // in each stage. // if (totalLength > MAX_TRANSFER_SIZE) { stageLength = MAX_TRANSFER_SIZE; } else { stageLength = totalLength; } newMdl = IoAllocateMdl((PVOID) virtualAddress, totalLength, FALSE, FALSE, NULL); if (newMdl == NULL) { UsbSamp_DbgPrint(1, ("Failed to alloc mem for mdl\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } // // map the portion of user-buffer described by an mdl to another mdl // IoBuildPartialMdl(requestMdl, newMdl, (PVOID) virtualAddress, stageLength); WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs); objectAttribs.ParentObject = Request; status = WdfMemoryCreate(&objectAttribs, NonPagedPool, POOL_TAG, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), &urbMemory, (PVOID*) &urb); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("Failed to alloc mem for urb\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); UsbBuildInterruptOrBulkTransferRequest(urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), usbdPipeHandle, NULL, newMdl, stageLength, urbFlags, NULL); status = WdfUsbTargetPipeFormatRequestForUrb(pipe, Request, urbMemory, NULL ); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } WdfRequestSetCompletionRoutine(Request, ReadWriteCompletion, NULL); // // set REQUEST_CONTEXT parameters. // rwContext->UrbMemory = urbMemory; rwContext->Mdl = newMdl; rwContext->Length = totalLength - stageLength; rwContext->Numxfer = 0; rwContext->VirtualAddress = virtualAddress + stageLength; if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { status = WdfRequestGetStatus(Request); ASSERT(!NT_SUCCESS(status)); } Exit: if (!NT_SUCCESS(status)) { WdfRequestCompleteWithInformation(Request, status, 0); if (newMdl != NULL) { IoFreeMdl(newMdl); } } UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - ends\n")); return; }
VOID ReadWriteCompletion( 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; UNREFERENCED_PARAMETER(Context); rwContext = GetRequestContext(Request); 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)); if (rwContext->Length > MAX_TRANSFER_SIZE) { stageLength = MAX_TRANSFER_SIZE; } 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, ReadWriteCompletion, NULL); // // 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; }