/*++ 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 --*/ VOID ReadWriteBulkEndPoints(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN ULONG Length, IN WDF_REQUEST_TYPE RequestType) { PMDL newMdl = NULL; PMDL 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; WDF_REQUEST_SEND_OPTIONS sendOptions; PSDrv_DbgPrint(3, ("ReadWriteBulkEndPoints - begins\n")); // First validate input parameters. deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); if (totalLength > deviceContext->MaximumTransferSize) { PSDrv_DbgPrint(1, ("Transfer length (%d) is bigger then MaximumTransferSize (%d)!\n", totalLength, deviceContext->MaximumTransferSize)); status = STATUS_INVALID_PARAMETER; goto Exit; } if ((RequestType != WdfRequestTypeRead) && (RequestType != WdfRequestTypeWrite)) { PSDrv_DbgPrint(1, ("RequestType has to be either Read or Write! (RequestType = %d)\n", RequestType)); 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)) { PSDrv_DbgPrint(1, ("Usbd pipe type is not bulk or interrupt! (PipeType = %d)\n", pipeInfo.PipeType)); status = STATUS_INVALID_DEVICE_REQUEST; goto Exit; } rwContext = GetRequestContext(Request); if(RequestType == WdfRequestTypeRead) { status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)) { PSDrv_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl failed! (Status = %x)\n", status)); goto Exit; } urbFlags |= USBD_TRANSFER_DIRECTION_IN; rwContext->Read = TRUE; PSDrv_DbgPrint(3, ("This is a read operation...\n")); } else { status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)) { PSDrv_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl failed! (Status = %x)\n", status)); goto Exit; } urbFlags |= USBD_TRANSFER_DIRECTION_OUT; rwContext->Read = FALSE; PSDrv_DbgPrint(3, ("This is a 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) { PSDrv_DbgPrint(1, ("IoAllocateMdl failed! (newMdl is NULL)\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)) { PSDrv_DbgPrint(1, ("WdfMemoryCreate for urbMemory failed! (Status = %x)\n", status)); 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)) { PSDrv_DbgPrint(1, ("WdfUsbTargetPipeFormatRequestForUrb failed! (Status = %x)\n", status)); 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; // Set the timeout if (fileContext->nTimeOut != 0) { WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT); WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, WDF_REL_TIMEOUT_IN_MS(fileContext->nTimeOut)); PSDrv_DbgPrint(3, ("Pipe timeout is set to: %d\n", fileContext->nTimeOut)); if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), &sendOptions)) { status = WdfRequestGetStatus(Request); ASSERT(!NT_SUCCESS(status)); } } else { 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); } } PSDrv_DbgPrint(3, ("ReadWriteBulkEndPoints - 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 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 --*/ { 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; ULONG maxTransferSize; PPIPE_CONTEXT pipeContext; 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); 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 maxTransfersize in each stage. // maxTransferSize = GetMaxTransferSize(pipe, deviceContext); if (totalLength > maxTransferSize) { stageLength = maxTransferSize; } 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 = WdfUsbTargetDeviceCreateUrb(deviceContext->WdfUsbTargetDevice, &objectAttribs, &urbMemory, &urb); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfUsbTargetDeviceCreateUrb failed %x\n", status)); goto Exit; } #if (NTDDI_VERSION >= NTDDI_WIN8) if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && pipeContext->StreamConfigured == TRUE) { // // For super speed bulk pipe with streams, we specify one of its associated // usbd pipe handles to format an URB for sending or receiving data. // The usbd pipe handle is returned by the HCD via sucessful open-streams request // usbdPipeHandle = GetStreamPipeHandleFromBulkPipe(pipe); } else { usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); } #else usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); #endif 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, UsbSamp_EvtReadWriteCompletion, deviceContext); // // 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); NT_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; }
/*++ 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; }
static VOID UsbChief_ReadEndPoint(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN ULONG totalLength) { PMDL newMdl=NULL, requestMdl = NULL; PURB urb = NULL; WDFMEMORY urbMemory; ULONG stageLength = 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; UsbChief_DbgPrint(DEBUG_RW, ("UsbChief_DispatchReadWrite - begins\n")); deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); fileContext = GetFileContext(WdfRequestGetFileObject(Request)); pipe = fileContext->Pipe; WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); rwContext = GetRequestContext(Request); status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); if(!NT_SUCCESS(status)){ UsbChief_DbgPrint(0, ("WdfRequestRetrieveOutputWdmMdl failed %x\n", status)); goto Exit; } virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(requestMdl); if (totalLength > MAX_TRANSFER_SIZE) stageLength = MAX_TRANSFER_SIZE; else stageLength = totalLength; newMdl = IoAllocateMdl((PVOID) virtualAddress, totalLength, FALSE, FALSE, NULL); if (!newMdl) { UsbChief_DbgPrint(0, ("Failed to alloc mem for mdl\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } 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)) { UsbChief_DbgPrint(0, ("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, USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, NULL); status = WdfUsbTargetPipeFormatRequestForUrb(pipe, Request, urbMemory, NULL ); if (!NT_SUCCESS(status)) { UsbChief_DbgPrint(0, ("Failed to format requset for urb\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } WdfRequestSetCompletionRoutine(Request, UsbChief_ReadCompletion, NULL); 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); } } }
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 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; }