/* * @implemented */ VOID NTAPI IoFreeMdl(PMDL Mdl) { /* Tell Mm to reuse the MDL */ MmPrepareMdlForReuse(Mdl); /* Check if this was a pool allocation */ if (!(Mdl->MdlFlags & MDL_ALLOCATED_FIXED_SIZE)) { /* Free it from the pool */ ExFreePoolWithTag(Mdl, TAG_MDL); } else { /* Free it from the lookaside */ IopFreeMdlFromLookaside(Mdl, LookasideMdlList); } }
FORCEINLINE NTSTATUS FxRequestBuffer::GetOrAllocateMdlWorker( __in PFX_DRIVER_GLOBALS FxDriverGlobals, __deref_out PMDL* Mdl, __in BOOLEAN * ReuseMdl, __in LONG Length, __in PVOID Buffer, __inout size_t* SizeOfMdl, __in BOOLEAN UnlockWhenFreed, __deref_out_opt PMDL* MdlToFree ) { size_t sizeofCurrentMdl; sizeofCurrentMdl = MmSizeOfMdl(Buffer, Length); // // Caller of this function (GetOrAllocateMdl) verifies that pages // are already unlocked. Asserting here, in case we start using this // function elsewhere. // // This is why we don't unlock pages either in reuse or non-reuse case. // ASSERT(UnlockWhenFreed == FALSE); UNREFERENCED_PARAMETER(UnlockWhenFreed); //for fre build // // If ReuseMdl is TRUE then the Mdl to be reused is passed in. // if (*ReuseMdl && sizeofCurrentMdl <= *SizeOfMdl) { MmPrepareMdlForReuse(*MdlToFree); *Mdl = *MdlToFree; } else { *ReuseMdl = FALSE; // // Since *Mdl may have the original IRP Mdl // free *MdlToFree and not *Mdl // if (*MdlToFree != NULL) { FxMdlFree(FxDriverGlobals, *MdlToFree); *MdlToFree = NULL; if (SizeOfMdl != NULL) { *SizeOfMdl = 0; } } *Mdl = FxMdlAllocate(FxDriverGlobals, NULL, // owning FxObject Buffer, Length, FALSE, FALSE); if (*Mdl == NULL) { ASSERT(SizeOfMdl ? (*SizeOfMdl == 0) : TRUE); return STATUS_INSUFFICIENT_RESOURCES; } if (SizeOfMdl != NULL) { *SizeOfMdl = sizeofCurrentMdl; } } return STATUS_SUCCESS; }
/*++ 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 --*/ VOID ReadWriteCompletion(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; PCHAR operation; ULONG bytesReadWritten; UNREFERENCED_PARAMETER(Context); rwContext = GetRequestContext(Request); PSDrv_DbgPrint(3, ("ReadWriteCompletion - begins\n")); if (rwContext->Read) { operation = "Read"; } else { operation = "Write"; } pipe = (WDFUSBPIPE)Target; status = CompletionParams->IoStatus.Status; if (!NT_SUCCESS(status)) { 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 PSDrv_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)) { PSDrv_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl for Read failed! (Status = %x)\n", status)); goto End; } } else { status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)) { PSDrv_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl for Write failed! (Status = %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)) { PSDrv_DbgPrint(1, ("WdfUsbTargetPipeFormatRequestForUrb failed! (Status = %x)\n", status)); status = STATUS_INSUFFICIENT_RESOURCES; goto End; } WdfRequestSetCompletionRoutine(Request, ReadWriteCompletion, NULL); // Send the request asynchronously. if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { PSDrv_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. PSDrv_DbgPrint(3, ("ReadWriteCompletion - ends 1\n")); return; End: // Dump the request context, complete the request and return. PSDrv_DbgPrint(3, ("rwContext->UrbMemory = %p\n", rwContext->UrbMemory)); PSDrv_DbgPrint(3, ("rwContext->Mdl = %p\n", rwContext->Mdl)); PSDrv_DbgPrint(3, ("rwContext->Length = %d\n", rwContext->Length)); PSDrv_DbgPrint(3, ("rwContext->Numxfer = %d\n", rwContext->Numxfer)); PSDrv_DbgPrint(3, ("rwContext->VirtualAddress = %p\n", rwContext->VirtualAddress)); IoFreeMdl(rwContext->Mdl); PSDrv_DbgPrint(3, ("Bulk or Interrupt %s request has finished. (Status = %x)\n", operation, status)); WdfRequestComplete(Request, status); PSDrv_DbgPrint(3, ("ReadWriteCompletion - ends 2\n")); return; }
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; }
NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context) { PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context; PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; BOOLEAN packetDone = FALSE; BOOLEAN idleRequest = FALSE; /* * Put all the assertions and spew in here so we don't have to look at them. */ DBGLOGRETURNPACKET(pkt); DBGCHECKRETURNEDPKT(pkt); HISTORYLOGRETURNEDPACKET(pkt); if (fdoData->IdlePrioritySupported == TRUE) { idleRequest = ClasspIsIdleRequest(pkt->OriginalIrp); if (idleRequest) { InterlockedDecrement(&fdoData->ActiveIdleIoCount); ASSERT(fdoData->ActiveIdleIoCount >= 0); } else { InterlockedDecrement(&fdoData->ActiveIoCount); ASSERT(fdoData->ActiveIoCount >= 0); KeQuerySystemTime(&fdoData->LastIoTime); fdoData->IdleTicks = 0; } } // // If partial MDL was used, unmap the pages. When the packet is retried, the // MDL will be recreated. If the packet is done, the MDL will be ready to be reused. // if (pkt->UsePartialMdl) { MmPrepareMdlForReuse(pkt->PartialMdl); } if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS) { fdoData->LoggedTURFailureSinceLastIO = FALSE; /* * The port driver should not have allocated a sense buffer * if the SRB succeeded. */ ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)); /* * Add this packet's transferred length to the original IRP's. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if ((pkt->InLowMemRetry) || (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) { packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { /* * The packet failed. We may retry it if possible. */ BOOLEAN shouldRetry; /* * Make sure IRP status matches SRB error status (since we propagate it). */ if (NT_SUCCESS(Irp->IoStatus.Status)){ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } /* * The packet failed. * So when sending the packet down we either saw either an error or STATUS_PENDING, * and so we returned STATUS_PENDING for the original IRP. * So now we must mark the original irp pending to match that, _regardless_ of * whether we actually switch threads here by retrying. * (We also have to mark the irp pending if the lower driver marked the irp pending; * that is dealt with farther down). */ if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ IoMarkIrpPending(pkt->OriginalIrp); } /* * Interpret the SRB error (to a meaningful IRP status) * and determine if we should retry this packet. * This call looks at the returned SENSE info to figure out what to do. */ shouldRetry = InterpretTransferPacketError(pkt); /* * Sometimes the port driver can allocates a new 'sense' buffer * to report transfer errors, e.g. when the default sense buffer * is too small. If so, it is up to us to free it. * Now that we're done interpreting the sense info, free it if appropriate. * Then clear the sense buffer so it doesn't pollute future errors returned in this packet. */ if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt)); FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb); pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData; pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); } else { ASSERT(pkt->Srb.SenseInfoBuffer == &pkt->SrbErrorSenseData); ASSERT(pkt->Srb.SenseInfoBufferLength <= sizeof(SENSE_DATA)); } RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(SENSE_DATA)); /* * If the SRB queue is locked-up, release it. * Do this after calling the error handler. */ if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){ ClassReleaseQueue(pkt->Fdo); } if (NT_SUCCESS(Irp->IoStatus.Status)){ /* * The error was recovered above in the InterpretTransferPacketError() call. */ ASSERT(!shouldRetry); /* * In the case of a recovered error, * add the transfer length to the original Irp as we would in the success case. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if ((pkt->InLowMemRetry) || (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) { packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { if (shouldRetry && (pkt->NumRetries > 0)){ packetDone = RetryTransferPacket(pkt); } else if (shouldRetry && (pkt->RetryHistory != NULL)){ // don't limit retries if class driver has custom interpretation routines packetDone = RetryTransferPacket(pkt); } else { packetDone = TRUE; } } } /* * If the packet is completed, put it back in the free list. * If it is the last packet servicing the original request, complete the original irp. */ if (packetDone){ LONG numPacketsRemaining; PIRP deferredIrp; PDEVICE_OBJECT Fdo = pkt->Fdo; UCHAR uniqueAddr; /* * In case a remove is pending, bump the lock count so we don't get freed * right after we complete the original irp. */ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr); /* * The original IRP should get an error code * if any one of the packets failed. */ if (!NT_SUCCESS(Irp->IoStatus.Status)){ pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; /* * If the original I/O originated in user space (i.e. it is thread-queued), * and the error is user-correctable (e.g. media is missing, for removable media), * alert the user. * Since this is only one of possibly several packets completing for the original IRP, * we may do this more than once for a single request. That's ok; this allows * us to test each returned status with IoIsErrorUserInduced(). */ if (IoIsErrorUserInduced(Irp->IoStatus.Status) && pkt->CompleteOriginalIrpWhenLastPacketCompletes && pkt->OriginalIrp->Tail.Overlay.Thread){ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, Fdo); } } /* * We use a field in the original IRP to count * down the transfer pieces as they complete. */ numPacketsRemaining = InterlockedDecrement( (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]); if (numPacketsRemaining > 0){ /* * More transfer pieces remain for the original request. * Wait for them to complete before completing the original irp. */ } else { /* * All the transfer pieces are done. * Complete the original irp if appropriate. */ ASSERT(numPacketsRemaining == 0); if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ IO_PAGING_PRIORITY priority = (TEST_FLAG(pkt->OriginalIrp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(pkt->OriginalIrp) : IoPagingPriorityInvalid; KIRQL oldIrql; if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){ ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == IoGetCurrentIrpStackLocation(pkt->OriginalIrp)->Parameters.Read.Length); ClasspPerfIncrementSuccessfulIo(fdoExt); } ClassReleaseRemoveLock(Fdo, pkt->OriginalIrp); /* * We submitted all the downward irps, including this last one, on the thread * that the OriginalIrp came in on. So the OriginalIrp is completing on a * different thread iff this last downward irp is completing on a different thread. * If BlkCache is loaded, for example, it will often complete * requests out of the cache on the same thread, therefore not marking the downward * irp pending and not requiring us to do so here. If the downward request is completing * on the same thread, then by not marking the OriginalIrp pending we can save an APC * and get extra perf benefit out of BlkCache. * Note that if the packet ever cycled due to retry or LowMemRetry, * we set the pending bit in those codepaths. */ if (pkt->Irp->PendingReturned){ IoMarkIrpPending(pkt->OriginalIrp); } ClassCompleteRequest(Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT); // // Drop the count only after completing the request, to give // Mm some amount of time to issue its next critical request // if (priority == IoPagingPriorityHigh) { KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo) { fdoData->MaxInterleavedNormalIo = 0; } else { fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo; } fdoData->NumHighPriorityPagingIo--; if (fdoData->NumHighPriorityPagingIo == 0) { LARGE_INTEGER period; // // Exiting throttle mode // KeQuerySystemTime(&fdoData->ThrottleStopTime); period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart; fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart); } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); } if (idleRequest) { ClasspCompleteIdleRequest(fdoExt); } /* * We may have been called by one of the class drivers (e.g. cdrom) * via the legacy API ClassSplitRequest. * This is the only case for which the packet engine is called for an FDO * with a StartIo routine; in that case, we have to call IoStartNextPacket * now that the original irp has been completed. */ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) { if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (??)")); } else { KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable KeLowerIrql(oldIrql); } } } } /* * If the packet was synchronous, write the final result back to the issuer's status buffer * and signal his event. */ if (pkt->SyncEventPtr){ KeSetEvent(pkt->SyncEventPtr, 0, FALSE); pkt->SyncEventPtr = NULL; } /* * Free the completed packet. */ pkt->UsePartialMdl = FALSE; // pkt->OriginalIrp = NULL; pkt->InLowMemRetry = FALSE; EnqueueFreeTransferPacket(Fdo, pkt); /* * Now that we have freed some resources, * try again to send one of the previously deferred irps. */ deferredIrp = DequeueDeferredClientIrp(fdoData); if (deferredIrp){ ServiceTransferRequest(Fdo, deferredIrp); } ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr); } return STATUS_MORE_PROCESSING_REQUIRED; }
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); } }