| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "pch.hpp" |
| #include "serial.tmh" |
| |
| PAGED |
| _No_competing_thread_ |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| SerialInitialize( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialInitialize attempts to find and open the first COM port available, with |
| the assumption that it should be for the Thread device. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| |
| Return Value: |
| |
| NTSTATUS - A failure here will indicate the serial COM port was not able |
| to be opened. |
| --*/ |
| { |
| NTSTATUS status = STATUS_UNSUCCESSFUL; |
| PWSTR SymbolicLinkList = NULL; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| WDF_OBJECT_ATTRIBUTES attr = { 0 }; |
| WDF_WORKITEM_CONFIG config = { 0 }; |
| |
| // |
| // Send Queue Variables |
| // |
| |
| InitializeListHead(&AdapterContext->SendQueue); |
| AdapterContext->SendQueueRunning = false; |
| |
| WDF_OBJECT_ATTRIBUTES_INIT(&attr); |
| attr.ParentObject = AdapterContext->Device; |
| status = WdfSpinLockCreate(&attr, &AdapterContext->SendLock); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfSpinLockCreate(lockSend) failed %!STATUS!", status); |
| break; |
| } |
| |
| WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); |
| attr.ParentObject = AdapterContext->Device; |
| WDF_WORKITEM_CONFIG_INIT(&config, SerialSendLoop); |
| |
| status = WdfWorkItemCreate(&config, &attr, &AdapterContext->SendWorkItem); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialSendLoop) failed %!STATUS!", status); |
| break; |
| } |
| GetWdfDeviceInfo(AdapterContext->SendWorkItem)->AdapterContext = AdapterContext; |
| |
| // |
| // Receive Variables |
| // |
| |
| WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); |
| attr.ParentObject = AdapterContext->Device; |
| WDF_WORKITEM_CONFIG_INIT(&config, SerialRecvLoop); |
| |
| status = WdfWorkItemCreate(&config, &attr, &AdapterContext->RecvWorkItem); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialRecvLoop) failed %!STATUS!", status); |
| break; |
| } |
| |
| GetWdfDeviceInfo(AdapterContext->RecvWorkItem)->AdapterContext = AdapterContext; |
| |
| // Query the system for device with SERIAL interface |
| status = |
| IoGetDeviceInterfaces( |
| &GUID_DEVINTERFACE_COMPORT, |
| NULL, |
| 0, |
| &SymbolicLinkList // List of symbolic names; separate by NULL, EOL with NULL+NULL. |
| ); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IoGetDeviceInterfaces failed %!STATUS!", status); |
| break; |
| } |
| |
| // Make sure there is a COM port found |
| NT_ASSERT(SymbolicLinkList); |
| if (*SymbolicLinkList == NULL) { |
| status = STATUS_DEVICE_NOT_CONNECTED; |
| LogError(DRIVER_DEFAULT, "No COM ports found!"); |
| break; |
| } |
| |
| #if DBG |
| for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Symbolic Name found: %ws", sym); |
| } |
| #endif |
| |
| // Try to open each serial port until we get that one works or we exhaust them all |
| for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1) |
| { |
| // Initialize the target |
| status = SerialInitializeTarget(AdapterContext, sym); |
| |
| // Break on success |
| if (NT_SUCCESS(status)) { |
| break; |
| } |
| } |
| |
| } while (false); |
| |
| // Clean up on failure |
| if (!NT_SUCCESS(status)) { |
| SerialUninitialize(AdapterContext); |
| } |
| |
| if (SymbolicLinkList) { |
| ExFreePool(SymbolicLinkList); |
| SymbolicLinkList = NULL; |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| PAGED |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| VOID |
| SerialUninitialize( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialUninitialize cleans up any cached Wdf IoTarget created from SerialInitialize. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| --*/ |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| // TODO - Clean up work item |
| |
| // TODO - Clean up send queue |
| |
| SerialUninitializeTarget(AdapterContext); |
| |
| if (AdapterContext->RecvWorkItem) { |
| WdfWorkItemFlush(AdapterContext->RecvWorkItem); |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| PAGED |
| _No_competing_thread_ |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| SerialInitializeTarget( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ PCWSTR TargetName |
| ) |
| { |
| NTSTATUS status = STATUS_UNSUCCESSFUL; |
| WDFIOTARGET tempTarget = WDF_NO_HANDLE; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| DECLARE_UNICODE_STRING_SIZE(PortName, 64); // Maximum name length of the device path to a serial port |
| WDF_IO_TARGET_OPEN_PARAMS openParams = { 0 }; |
| WDF_OBJECT_ATTRIBUTES attr = { 0 }; |
| |
| // Create the Wdf IoTarget |
| status = WdfIoTargetCreate(AdapterContext->Device, WDF_NO_OBJECT_ATTRIBUTES, &tempTarget); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfIoTargetCreate failed %!STATUS!", status); |
| break; |
| } |
| |
| // Try the COM port |
| LogInfo(DRIVER_DEFAULT, "Opening device: %ws", TargetName); |
| RtlInitUnicodeString(&PortName, TargetName); |
| WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( |
| &openParams, |
| &PortName, |
| GENERIC_READ | GENERIC_WRITE); |
| |
| // Open the port on the target |
| status = WdfIoTargetOpen(tempTarget, &openParams); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfIoTargetOpen(%wZ) failed %!STATUS!", &PortName, status); |
| break; |
| } |
| |
| AdapterContext->WdfIoTarget = tempTarget; |
| tempTarget = WDF_NO_HANDLE; |
| |
| WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); |
| attr.ParentObject = AdapterContext->Device; |
| |
| status = WdfRequestCreate(&attr, AdapterContext->WdfIoTarget, &AdapterContext->RecvReadRequest); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "WdfRequestCreate failed %!STATUS!", status); |
| break; |
| } |
| |
| // Try to configure the target |
| status = SerialConfigure(AdapterContext); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "SerialConfigure failed %!STATUS!", status); |
| break; |
| } |
| |
| } while (false); |
| |
| // Clean up on failure |
| if (!NT_SUCCESS(status)) { |
| SerialUninitializeTarget(AdapterContext); |
| } |
| |
| if (tempTarget) { |
| WdfIoTargetClose(tempTarget); |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| PAGED |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| VOID |
| SerialUninitializeTarget( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialUninitializeTarget cleans up any cached Wdf IoTarget created from SerialInitializeTarget. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| --*/ |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| if (AdapterContext->WdfIoTarget) { |
| // |
| // WdfIoTargetStop will cancel all the outstanding I/O and wait |
| // for them to complete before returning. WdfIoTargetStop with the |
| // action type WdfIoTargetCancelSentIo can be called at IRQL PASSIVE_LEVEL only. |
| // |
| WdfIoTargetStop(AdapterContext->WdfIoTarget, WdfIoTargetCancelSentIo); |
| WdfIoTargetClose(AdapterContext->WdfIoTarget); |
| AdapterContext->WdfIoTarget = NULL; |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| _Must_inspect_result_ |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| FORCEINLINE |
| SerialSendIoctl( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ ULONG IoctlCode, |
| _In_opt_ PWDF_REQUEST_SEND_OPTIONS RequestOptions = NULL, |
| _In_opt_ PWDF_MEMORY_DESCRIPTOR InputBuffer = WDF_NO_HANDLE, |
| _In_opt_ PWDF_MEMORY_DESCRIPTOR OutputBuffer = WDF_NO_HANDLE, |
| _Out_opt_ PULONG_PTR BytesReturned = NULL |
| ) |
| /*++ |
| Routine Description: |
| |
| Helper/Wrapper function for WdfIoTargetSendIoctlSynchronously. |
| |
| --*/ |
| { |
| return WdfIoTargetSendIoctlSynchronously( |
| AdapterContext->WdfIoTarget, |
| WDF_NO_HANDLE, |
| IoctlCode, |
| InputBuffer, |
| OutputBuffer, |
| RequestOptions, |
| BytesReturned); |
| } |
| |
| PAGED |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| SerialConfigure( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialInitialize attempts to find and open the first COM port available, with |
| the assumption that it should be for the Thread device. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| |
| Return Value: |
| |
| NTSTATUS - A failure here will indicate the serial COM port was not able |
| to be configured as desired. |
| --*/ |
| { |
| NTSTATUS status = STATUS_UNSUCCESSFUL; |
| WDF_MEMORY_DESCRIPTOR inputDesc; |
| WDF_REQUEST_SEND_OPTIONS wrso = { |
| sizeof(WDF_REQUEST_SEND_OPTIONS), |
| WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, |
| WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete |
| }; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| // Initial reset of the device |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_RESET_DEVICE, &wrso); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_RESET_DEVICE failed %!STATUS!", status); |
| break; |
| } |
| |
| // 8 bits, no parity, 1 stop bit |
| { |
| const SERIAL_LINE_CONTROL slc = { STOP_BIT_1, NO_PARITY, 8 }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&slc, sizeof(slc)); |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_LINE_CONTROL, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_LINE_CONTROL failed %!STATUS!", status ); |
| break; |
| } |
| } |
| |
| // Xon and Xoff characters |
| { |
| const SERIAL_CHARS sc = { 0, 0, 0, 0, 0x11, 0x13 }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sc, sizeof(sc)); |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_CHARS, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_CHARS failed %!STATUS!", status); |
| break; |
| } |
| } |
| |
| // Baud rate |
| { |
| const SERIAL_BAUD_RATE sbr = { 115200 }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sbr, sizeof(sbr)); |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_BAUD_RATE, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_BAUD_RATE failed %!STATUS!", status); |
| break; |
| } |
| } |
| |
| /*{ |
| // Only send if CTS is set, Set RTS before sending |
| const SERIAL_HANDFLOW shf = |
| { |
| SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL, |
| MAX_SPINEL_COMMAND_LENGTH, MAX_SPINEL_COMMAND_LENGTH |
| }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&shf, sizeof(shf)); |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_HANDFLOW, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_HANDFLOW failed %!STATUS!", status); |
| // break; Ignore for now |
| } |
| } |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_XON, &wrso); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_XON failed %!STATUS!", status); |
| break; |
| } |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_RTS, &wrso); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_RTS failed %!STATUS!", status); |
| break; |
| } |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_DTR, &wrso); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_DTR failed %!STATUS!", status); |
| break; |
| }*/ |
| |
| { |
| const SERIAL_TIMEOUTS sto = { |
| 1, 0, 0, // On read, only timeout if more than 1ms *between* bytes (wait forever for first byte) |
| 1, 10 // Write times out after (1ms * n-bytes) + (10ms) |
| }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sto, sizeof(sto)); |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_TIMEOUTS, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_TIMEOUTS failed %!STATUS!", status); |
| break; |
| } |
| } |
| |
| status = SerialFlushAndCheckStatus(AdapterContext); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "SerialFlushAndCheckStatus failed %!STATUS!", status); |
| break; |
| } |
| |
| } while (false); |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| PAGED |
| _IRQL_requires_(PASSIVE_LEVEL) |
| NTSTATUS |
| SerialCheckStatus( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ bool DataExpected |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialCheckStatus validates the current status of the serial COM port. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| |
| Return Value: |
| |
| NTSTATUS - A failure here will indicate the serial COM port is not in an |
| expected state. |
| --*/ |
| { |
| NTSTATUS status = STATUS_UNSUCCESSFUL; |
| WDF_MEMORY_DESCRIPTOR outputDesc; |
| WDF_REQUEST_SEND_OPTIONS wrso = { |
| sizeof(WDF_REQUEST_SEND_OPTIONS), |
| WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, |
| WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete |
| }; |
| ULONG_PTR bytesReturned = 0; |
| SERIAL_STATUS ss = { 0 }; |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDesc, (PVOID)&ss, sizeof(ss)); |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| // Check to ensure we are ready to send |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_GET_COMMSTATUS, &wrso, WDF_NO_HANDLE, &outputDesc, &bytesReturned); |
| |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_GET_COMMSTATUS failed %!STATUS!", status); |
| } |
| else if (bytesReturned >= sizeof(ss)) |
| { |
| if (ss.HoldReasons) |
| { |
| if (ss.HoldReasons != SERIAL_TX_WAITING_FOR_CTS) |
| { |
| LogError(DRIVER_DEFAULT, "HoldReasons is wrong (should only be CTS, but is %x)", ss.HoldReasons ); |
| status = STATUS_INVALID_DEVICE_STATE; |
| } |
| else if (!DataExpected) |
| { |
| LogError(DRIVER_DEFAULT, "Adapter already has data on init!?!?!"); |
| status = STATUS_INVALID_STATE_TRANSITION; |
| } |
| } |
| if (ss.Errors) |
| { |
| LogWarning(DRIVER_DEFAULT, "Unexpected Error %x", ss.Errors); |
| status = STATUS_UNSUCCESSFUL; |
| } |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| PAGED |
| _IRQL_requires_(PASSIVE_LEVEL) |
| NTSTATUS |
| SerialFlushAndCheckStatus( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialFlushAndCheckStatus flushed and validates the current status of the serial COM port. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| |
| Return Value: |
| |
| NTSTATUS - A failure here will indicate the serial COM port is not in an |
| expected state. |
| --*/ |
| { |
| NTSTATUS status = STATUS_UNSUCCESSFUL; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| WDF_MEMORY_DESCRIPTOR inputDesc; |
| WDF_REQUEST_SEND_OPTIONS wrso = { |
| sizeof(WDF_REQUEST_SEND_OPTIONS), |
| WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, |
| WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete |
| }; |
| const ULONG flags = SERIAL_PURGE_RXABORT | SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXABORT | SERIAL_PURGE_TXCLEAR; |
| |
| WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&flags, sizeof(flags)); |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_PURGE, &wrso, &inputDesc); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_PURGE failed %!STATUS!", status); |
| break; |
| } |
| |
| status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_CLEAR_STATS, &wrso); |
| |
| if (!NT_SUCCESS(status)) { |
| LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_CLEAR_STATS failed %!STATUS!", status); |
| break; |
| } |
| |
| status = SerialCheckStatus(AdapterContext, false); |
| for (int i = 0; !NT_SUCCESS(status) && i < 20; i++) |
| { |
| NdisMSleep(1); // just sleep enough to give up our quantum |
| status = SerialCheckStatus(AdapterContext, false); |
| } |
| |
| } while (false); |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| bool |
| SerialPushSend( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ PSERIAL_SEND_ITEM SendItem |
| ) |
| { |
| WdfSpinLockAcquire(AdapterContext->SendLock); |
| |
| // Start the work item up if it's not already running |
| if (!AdapterContext->SendQueueRunning) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Starting Send Work Item"); |
| AdapterContext->SendQueueRunning = true; |
| WdfWorkItemEnqueue(AdapterContext->SendWorkItem); |
| } |
| |
| // Insert the new item at the end of the list |
| InsertTailList(&AdapterContext->SendQueue, &SendItem->Link); |
| |
| WdfSpinLockRelease(AdapterContext->SendLock); |
| |
| return true; |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| PSERIAL_SEND_ITEM |
| SerialPopSend( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext |
| ) |
| { |
| PLIST_ENTRY current = NULL; |
| |
| // Grab the head of the list |
| // Careful, this might have gotten aborted, leaving the list empty |
| WdfSpinLockAcquire(AdapterContext->SendLock); |
| |
| if (!IsListEmpty(&AdapterContext->SendQueue)) |
| { |
| current = RemoveHeadList(&AdapterContext->SendQueue); |
| } |
| if (current == NULL) |
| { |
| // Do this under the lock, but the state is consumed outside the lock |
| AdapterContext->SendQueueRunning = false; |
| LogVerbose(DRIVER_DEFAULT, "Send Work Item Complete"); |
| } |
| |
| WdfSpinLockRelease(AdapterContext->SendLock); |
| |
| return current ? CONTAINING_RECORD(current, SERIAL_SEND_ITEM, Link) : NULL; |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| NTSTATUS |
| SerialSendData( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ PNET_BUFFER_LIST NetBufferList |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialSendData encodes and queues up the data to be sent over the serial COM port. |
| |
| Arguments: |
| |
| AdapterContext - handle to a OTTMP Adapter |
| |
| NetBufferLists - a single NET_BUFFER_LIST object, containing a signle NET_BUFFER for |
| Spinel tunnel commands. |
| |
| DispatchLevel - flag indicating if we are running at dispatch or not |
| |
| Return Value: |
| |
| NTSTATUS - A failure here will indicate we either failed to encode or queue the data. |
| --*/ |
| { |
| NTSTATUS status = STATUS_SUCCESS; |
| WDF_OBJECT_ATTRIBUTES attributes; |
| WDFMEMORY WdfMemBuffer = NULL; |
| PSERIAL_SEND_ITEM SendItem = NULL; |
| PUCHAR DecodedBuffer = NULL; |
| ULONG DecodedBufferLength = NetBufferList->FirstNetBuffer->DataLength; |
| ULONG EncodedBufferLength; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| do |
| { |
| // Get the decoded buffer from the NBL/NB. We required |
| // the use of contiguous buffers. |
| DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBufferList->FirstNetBuffer, DecodedBufferLength, NULL, 1, 0); |
| if (DecodedBuffer == NULL) { |
| status = STATUS_INVALID_PARAMETER; |
| break; |
| } |
| |
| LogVerbose(DRIVER_DEFAULT, "Sending %u decoded bytes", DecodedBufferLength); |
| DumpBuffer(DecodedBuffer, DecodedBufferLength); |
| |
| // Calculate the buffer size required |
| EncodedBufferLength = HdlcComputeEncodedLength(DecodedBuffer, DecodedBufferLength); |
| |
| // Allocate the memory |
| WDF_OBJECT_ATTRIBUTES_INIT(&attributes); |
| attributes.ParentObject = AdapterContext->Device; |
| #pragma warning(push) |
| #pragma warning(suppress: 28160) // Param 3 could be 0 |
| status = WdfMemoryCreate( |
| &attributes, |
| NonPagedPoolNx, |
| 0, |
| SERIAL_SEND_ITEM_SIZE + EncodedBufferLength, |
| &WdfMemBuffer, |
| (PVOID*)&SendItem |
| ); |
| #pragma warning(pop) |
| |
| if (!NT_SUCCESS(status)) { |
| LogWarning(DRIVER_DEFAULT, "WdfMemoryCreate (%u bytes) failed %!STATUS!", (SERIAL_SEND_ITEM_SIZE + EncodedBufferLength), status); |
| break; |
| } |
| |
| SendItem->NetBufferList = NetBufferList; |
| SendItem->WdfMemory = WdfMemBuffer; |
| SendItem->EncodedBufferLength = EncodedBufferLength; |
| |
| // Encode data |
| if (!HdlcEncodeBuffer(DecodedBuffer, DecodedBufferLength, SendItem->EncodedBuffer, EncodedBufferLength)) { |
| NT_ASSERT(FALSE); // Should never fail, unless we have a bug in the length computation |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| break; |
| } |
| |
| // Queue data to be sent out |
| if (!SerialPushSend(AdapterContext, SendItem)) { |
| status = STATUS_DEVICE_NOT_READY; |
| break; |
| } |
| |
| } while (false); |
| |
| if (!NT_SUCCESS(status)) { |
| if (WdfMemBuffer) { |
| WdfObjectDelete(WdfMemBuffer); |
| } |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| PAGED |
| _Function_class_(EVT_WDF_WORKITEM) |
| _IRQL_requires_same_ |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| VOID |
| SerialSendLoop( |
| _In_ WDFWORKITEM WorkItem |
| ) |
| /*++ |
| Routine Description: |
| |
| SerialSendLoop handles the actual sending of data over the serial COM port. |
| |
| Arguments: |
| |
| WorkItem - handle to a Wdf Device Info object for the Adapter context |
| |
| --*/ |
| { |
| POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext; |
| WDF_REQUEST_SEND_OPTIONS wrso = { |
| sizeof(WDF_REQUEST_SEND_OPTIONS), |
| WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, |
| WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete |
| }; |
| WDFMEMORY_OFFSET offset = { 0 }; |
| PSERIAL_SEND_ITEM SendItem = NULL; |
| WDF_MEMORY_DESCRIPTOR wmd; |
| NTSTATUS status; |
| |
| WDF_OBJECT_ATTRIBUTES Attributes; |
| WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); |
| Attributes.ParentObject = AdapterContext->Device; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| #pragma warning(push) |
| #pragma warning(suppress: 6387) // Param 2 is NULL |
| WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&wmd, NULL, &offset); |
| #pragma warning(pop) |
| |
| while (NULL != (SendItem = SerialPopSend(AdapterContext))) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Sending %u encoded bytes", SendItem->EncodedBufferLength); |
| DumpBuffer(SendItem->EncodedBuffer, SendItem->EncodedBufferLength); |
| |
| if (SendItem->EncodedBufferLength > 0) |
| { |
| offset.BufferLength = SendItem->EncodedBufferLength; |
| status = |
| WdfMemoryCreatePreallocated( |
| &Attributes, |
| SendItem->EncodedBuffer, |
| SendItem->EncodedBufferLength, |
| &wmd.u.HandleType.Memory); |
| |
| if (!NT_SUCCESS( status )) { |
| LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status); |
| } |
| else |
| { |
| // Send the buffer out |
| status = WdfIoTargetSendWriteSynchronously(AdapterContext->WdfIoTarget, NULL, &wmd, NULL, &wrso, NULL); |
| |
| if (!NT_SUCCESS( status )) { |
| LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status); |
| } |
| |
| WdfObjectDelete(wmd.u.HandleType.Memory); |
| } |
| } |
| else |
| { |
| status = STATUS_INVALID_PARAMETER; |
| } |
| |
| // Complete the NetBufferList |
| SendItem->NetBufferList->Status = status; |
| #ifdef OTTMP_LEGACY |
| NdisMSendNetBufferListsComplete(AdapterContext->Adapter, SendItem->NetBufferList, 0); |
| #else |
| NetBufferListsCompleteSend(SendItem->NetBufferList); |
| #endif |
| |
| // Hack to sleep 1 ms per 5 bytes sent |
| NdisMSleep(1000 * (1 + SendItem->EncodedBufferLength / 5)); |
| |
| // Cleanup |
| WdfObjectDelete(SendItem->WdfMemory); |
| }; |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| PAGED |
| _Function_class_(EVT_WDF_WORKITEM) |
| _IRQL_requires_same_ |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| VOID |
| SerialRecvLoop( |
| _In_ WDFWORKITEM WorkItem |
| ) |
| { |
| POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext; |
| WDFMEMORY mem = { 0 }; |
| NTSTATUS status; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| WDFREQUEST & request = AdapterContext->RecvReadRequest; |
| |
| WDF_OBJECT_ATTRIBUTES attributes; |
| WDF_OBJECT_ATTRIBUTES_INIT(&attributes); |
| attributes.ParentObject = AdapterContext->Device; |
| status = |
| WdfMemoryCreatePreallocated( |
| &attributes, |
| AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, |
| MAX_SPINEL_COMMAND_LENGTH, |
| &mem); |
| |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "WdfMemoryCreateFromLookaside failed %!STATUS!", status); |
| break; |
| } |
| |
| status = WdfIoTargetFormatRequestForRead(AdapterContext->WdfIoTarget, request, mem, NULL, NULL); |
| |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "WdfIoTargetFormatRequestForRead failed %!STATUS!", status); |
| break; |
| } |
| else |
| { |
| WdfRequestSetCompletionRoutine(request, SerialRecvComplete, AdapterContext); |
| if (WdfRequestSend(request, AdapterContext->WdfIoTarget, WDF_NO_SEND_OPTIONS)) |
| { |
| // Send succeeded, no cleanup |
| mem = NULL; |
| break; |
| } |
| |
| status = WdfRequestGetStatus(request); |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "WdfRequestSend failed %!STATUS!", status); |
| } |
| |
| WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 }; |
| WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); |
| |
| // refresh the request so it is ready to reuse |
| status = WdfRequestReuse(request, &reuseParams); |
| if (!NT_SUCCESS(status)) |
| { |
| NT_ASSERT(NT_SUCCESS(status)); |
| LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status); |
| } |
| } |
| |
| } while (false); |
| |
| if (mem) { |
| WdfObjectDelete(mem); |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| _When_(return==0,_At_(*pNetBufferList, __drv_allocatesMem(mem))) |
| _When_(return==0,_At_((*pNetBufferList)->FirstNetBuffer, __drv_allocatesMem(mem))) |
| NTSTATUS |
| SerialAllocateNetBufferList( |
| _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, |
| _In_ ULONG BufferLength, |
| _Out_ PNET_BUFFER_LIST *pNetBufferList |
| ) |
| { |
| NTSTATUS status = STATUS_SUCCESS; |
| PNET_BUFFER_LIST NetBufferList = NULL; |
| PNET_BUFFER NetBuffer = NULL; |
| |
| do |
| { |
| |
| #ifdef OTTMP_LEGACY |
| // Allocate the NetBufferList |
| NetBufferList = NdisAllocateNetBufferList(AdapterContext->pGlobals->hNblPool, 0, 0); |
| |
| if (NetBufferList == NULL) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| break; |
| } |
| |
| // Allocate the NetBuffer |
| NetBufferList->FirstNetBuffer = NdisAllocateNetBufferMdlAndData(AdapterContext->pGlobals->hNbPool); |
| |
| if (NetBufferList->FirstNetBuffer == NULL) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| break; |
| } |
| #else |
| // Grab a NetBufferList from the collection |
| status = NetBufferListCollectionRetrieveNbls(AdapterContext->ReceiveCollection, 1, &NetBufferList); |
| |
| if (!NT_SUCCESS(status)) |
| { |
| break; |
| } |
| #endif |
| |
| NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; |
| NetBuffer = NetBufferList->FirstNetBuffer; |
| |
| // If there is no buffer allocated yet, go ahead and allocate the max |
| if (NET_BUFFER_DATA_LENGTH(NetBuffer) == 0) |
| { |
| // Allocate the max buffer size |
| ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, MAX_SPINEL_COMMAND_LENGTH, 0, NULL); |
| if (ndisStatus != NDIS_STATUS_SUCCESS) |
| { |
| LogError(DRIVER_DEFAULT, "NdisRetreatNetBufferDataStart failed %!NDIS_STATUS!", ndisStatus); |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| break; |
| } |
| } |
| |
| // By this point, we should have a NetBuffer with a contiguous memory block of MAX_SPINEL_COMMAND_LENGTH bytes, |
| // though it's offset could be anywhere in the buffer, from when it was previously used. |
| |
| // Adjust buffer length to fit the requested length |
| if (NET_BUFFER_DATA_LENGTH(NetBuffer) > BufferLength) |
| { |
| NdisAdvanceNetBufferDataStart(NetBuffer, NET_BUFFER_DATA_LENGTH(NetBuffer) - BufferLength, FALSE, NULL); |
| } |
| else if (NET_BUFFER_DATA_LENGTH(NetBuffer) < BufferLength) |
| { |
| ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, BufferLength - NET_BUFFER_DATA_LENGTH(NetBuffer), 0, NULL); |
| NT_ASSERT(ndisStatus == NDIS_STATUS_SUCCESS); |
| if (ndisStatus != NDIS_STATUS_SUCCESS) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| break; |
| } |
| } |
| |
| // Set the output |
| *pNetBufferList = NetBufferList; |
| |
| } while (FALSE); |
| |
| if (!NT_SUCCESS(status)) |
| { |
| #ifdef OTTMP_LEGACY |
| if (NetBuffer) NdisFreeNetBuffer(NetBuffer); |
| if (NetBufferList) NdisFreeNetBufferList(NetBufferList); |
| #else |
| if (NetBufferList) NetBufferListsDiscardReceive(NetBufferList); |
| #endif |
| } |
| |
| return status; |
| } |
| |
| _Function_class_(EVT_WDF_REQUEST_COMPLETION_ROUTINE) |
| _IRQL_requires_same_ |
| VOID |
| SerialRecvComplete( |
| _In_ WDFREQUEST Request, |
| _In_ WDFIOTARGET Target, |
| _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, |
| _In_ WDFCONTEXT Context |
| ) |
| { |
| POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)Context; |
| NTSTATUS status; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| UNREFERENCED_PARAMETER(Target); // Except for an assert |
| NT_ASSERT((Target == AdapterContext->WdfIoTarget) || (WDF_NO_HANDLE == AdapterContext->WdfIoTarget)); |
| NT_ASSERT(Request == AdapterContext->RecvReadRequest); |
| |
| WDFMEMORY mem = Params->Parameters.Read.Buffer; |
| NT_ASSERT(mem); |
| |
| status = WdfRequestGetStatus(Request); |
| if (NT_SUCCESS(status)) |
| { |
| NT_ASSERT(Params->Type == WdfRequestTypeRead); |
| NT_ASSERT(Params->Parameters.Read.Offset == 0); |
| |
| size_t MemoryLength = 0; |
| NT_ASSERT(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength == (PUCHAR)WdfMemoryGetBuffer(mem, &MemoryLength)); |
| UNREFERENCED_PARAMETER(MemoryLength); |
| |
| LogVerbose(DRIVER_DEFAULT, "Received %u encoded bytes", (ULONG)Params->IoStatus.Information); |
| DumpBuffer(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, (ULONG)Params->IoStatus.Information); |
| |
| AdapterContext->RecvBufferLength += (ULONG)Params->IoStatus.Information; |
| |
| // Decode and receive |
| ULONG ReadOffset = 0; |
| while (AdapterContext->RecvBufferLength > ReadOffset) |
| { |
| // Parse, validate and compute the decoded buffer size requirements |
| ULONG UsedEncodedBufferLength = AdapterContext->RecvBufferLength - ReadOffset; |
| ULONG DecodedBufferLength = 0; |
| bool HasGoodBuffer = false; |
| bool HasCompleteBuffer = |
| HdlcDecodeBuffer( |
| AdapterContext->RecvBuffer + ReadOffset, |
| &UsedEncodedBufferLength, |
| &DecodedBufferLength, |
| NULL, |
| &HasGoodBuffer); |
| |
| // We should never have used more buffer than available |
| NT_ASSERT(UsedEncodedBufferLength <= AdapterContext->RecvBufferLength - ReadOffset); |
| |
| // Did we have a complete (start and end sequence chars) buffer? |
| if (!HasCompleteBuffer) |
| { |
| AdapterContext->RecvBufferLength -= ReadOffset; |
| |
| LogWarning(DRIVER_DEFAULT, "Buffering %u incomplete bytes", AdapterContext->RecvBufferLength); |
| NT_ASSERT(AdapterContext->RecvBufferLength < MAX_SPINEL_COMMAND_LENGTH); |
| |
| memmove_s(AdapterContext->RecvBuffer, sizeof(AdapterContext->RecvBuffer), |
| AdapterContext->RecvBuffer + ReadOffset, AdapterContext->RecvBufferLength); |
| break; |
| } |
| else |
| { |
| // Was the buffer too short or did it's FCS not match? |
| if (HasGoodBuffer) |
| { |
| NT_ASSERT(UsedEncodedBufferLength <= MAX_SPINEL_COMMAND_LENGTH); |
| |
| // Allocate the NetBufferList & NetBuffer to decode the data to |
| PNET_BUFFER_LIST NetBufferList = NULL; |
| status = SerialAllocateNetBufferList(AdapterContext, DecodedBufferLength, &NetBufferList); |
| |
| if (NT_SUCCESS(status)) |
| { |
| PNET_BUFFER NetBuffer = NetBufferList->FirstNetBuffer; |
| NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer)); |
| |
| // Get pointer to contiguous buffer |
| PUCHAR DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, DecodedBufferLength, NULL, 1, 0); |
| NT_ASSERT(DecodedBuffer); |
| if (DecodedBuffer) |
| { |
| HasCompleteBuffer = |
| HdlcDecodeBuffer( |
| AdapterContext->RecvBuffer + ReadOffset, |
| &UsedEncodedBufferLength, |
| &DecodedBufferLength, |
| DecodedBuffer, |
| &HasGoodBuffer); |
| |
| NT_ASSERT(HasCompleteBuffer); |
| NT_ASSERT(HasGoodBuffer); |
| NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer)); |
| |
| LogVerbose(DRIVER_DEFAULT, "Received %u decoded bytes", DecodedBufferLength); |
| DumpBuffer(DecodedBuffer, DecodedBufferLength); |
| } |
| else |
| { |
| status = STATUS_INVALID_PARAMETER; |
| } |
| |
| if (NT_SUCCESS(status)) |
| { |
| // Indicate up the new NBL we just created |
| #ifdef OTTMP_LEGACY |
| NdisMIndicateReceiveNetBufferLists( |
| AdapterContext->Adapter, |
| NetBufferList, |
| NDIS_DEFAULT_PORT_NUMBER, |
| 1, |
| 0 |
| ); |
| #else |
| NetBufferListsCompleteReceive( |
| NetBufferList, |
| NDIS_DEFAULT_PORT_NUMBER, |
| 0 |
| ); |
| #endif |
| } |
| else |
| { |
| #ifdef OTTMP_LEGACY |
| NdisFreeNetBuffer(NetBuffer); |
| NdisFreeNetBufferList(NetBufferList); |
| #else |
| NetBufferListsDiscardReceive(NetBufferList); |
| #endif |
| } |
| } |
| } |
| else |
| { |
| LogWarning(DRIVER_DEFAULT, "Dropping %u bad bytes", UsedEncodedBufferLength); |
| DumpBuffer(AdapterContext->RecvBuffer + ReadOffset, UsedEncodedBufferLength); |
| } |
| |
| // Skip over used data |
| ReadOffset += UsedEncodedBufferLength; |
| } |
| } |
| |
| // We read all the buffer, so reset the length |
| if (AdapterContext->RecvBufferLength == ReadOffset) |
| { |
| AdapterContext->RecvBufferLength = 0; |
| } |
| } |
| else |
| { |
| LogError(DRIVER_DEFAULT, "Read request failed %!STATUS!", status); |
| } |
| |
| WdfObjectDelete(mem); |
| |
| if (status != STATUS_DELETE_PENDING) |
| { |
| WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 }; |
| WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); |
| status = WdfRequestReuse(Request, &reuseParams); |
| if (!NT_SUCCESS(status)) |
| { |
| NT_ASSERT(NT_SUCCESS(status)); |
| LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status); |
| } |
| |
| LogVerbose(DRIVER_DEFAULT, "Starting recv worker"); |
| WdfWorkItemEnqueue(AdapterContext->RecvWorkItem); |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| VOID |
| DumpLine( |
| _In_reads_bytes_(aLength) PCUCHAR aBuf, |
| _In_ size_t aLength |
| ) |
| { |
| char buf[80] = {0}; |
| char *cur = buf; |
| |
| sprintf_s(cur, sizeof(buf) - (cur - buf), "|"); |
| cur += 1; |
| |
| for (size_t i = 0; i < 16; i++) |
| { |
| if (i < aLength) |
| { |
| sprintf_s(cur, sizeof(buf) - (cur - buf), " %02X", aBuf[i]); |
| cur += 3; |
| } |
| else |
| { |
| sprintf_s(cur, sizeof(buf) - (cur - buf), " .."); |
| cur += 3; |
| } |
| |
| if (!((i + 1) % 8)) |
| { |
| sprintf_s(cur, sizeof(buf) - (cur - buf), " |"); |
| cur += 2; |
| } |
| } |
| |
| sprintf_s(cur, sizeof(buf) - (cur - buf), " "); |
| cur += 1; |
| |
| for (size_t i = 0; i < 16; i++) |
| { |
| if (i < aLength && isprint(0x7f & aBuf[i])) |
| { |
| char c = 0x7f & aBuf[i]; |
| sprintf_s(cur, sizeof(buf) - (cur - buf), "%c", c); |
| cur += 1; |
| } |
| else |
| { |
| sprintf_s(cur, sizeof(buf) - (cur - buf), "."); |
| cur += 1; |
| } |
| } |
| |
| LogVerbose(DRIVER_DEFAULT, "%s", buf); |
| } |
| |
| VOID |
| DumpBuffer( |
| _In_reads_bytes_(aLength) PCUCHAR aBuf, |
| _In_ size_t aLength |
| ) |
| { |
| for (size_t i = 0; i < aLength; i += 16) |
| { |
| DumpLine(aBuf + i, (aLength - i) < 16 ? (aLength - i) : 16); |
| } |
| } |