mirror of
https://github.com/espressif/openthread.git
synced 2026-07-05 03:40:26 +00:00
8a8c8cde41
* Add otLinkRaw API * Separate Mac and otLinkRaw* * Make pretty * otLinkRawSetEnabled doen't change radio enable state. Assumed to always be enabled right now. * NCP usage of otLinkRaw * Make otLinkRaw compile time removal (on by default). Disable otLinkRaw on Windows kernel mode. Check for mLinkRawEnabled state in otLinkRaw APIs and return errors. * Make raw usage compile time removable. * Additional cleanup * Fix Linux build breaks * Merge otlwf changes for new Spinel command interface * CC2538 Add Support for ACK timeouts * Revert "CC2538 Add Support for ACK timeouts" This reverts commit 7dcc4caebc3dc50dc56401cf7e009f315b4c196c. * Updates for the energy scan interface
1309 lines
40 KiB
C++
1309 lines
40 KiB
C++
/*
|
|
* 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);
|
|
}
|
|
}
|