| /* |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * @brief |
| * This file implements the functions for sending/receiving Spinel commands to the miniport. |
| */ |
| |
| #include "precomp.h" |
| #include "command.tmh" |
| |
| typedef struct _SPINEL_CMD_HANDLER_ENTRY |
| { |
| LIST_ENTRY Link; |
| volatile LONG RefCount; |
| SPINEL_CMD_HANDLER *Handler; |
| PVOID Context; |
| spinel_tid_t TransactionId; |
| } SPINEL_CMD_HANDLER_ENTRY; |
| |
| void AddEntryRef(SPINEL_CMD_HANDLER_ENTRY *pEntry) { InterlockedIncrement(&pEntry->RefCount); } |
| void ReleaseEntryRef(SPINEL_CMD_HANDLER_ENTRY *pEntry) |
| { |
| if (InterlockedDecrement(&pEntry->RefCount) == 0) |
| { |
| FILTER_FREE_MEM(pEntry); |
| } |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NDIS_STATUS |
| otLwfCmdInitialize( |
| _In_ PMS_FILTER pFilter |
| ) |
| { |
| NDIS_STATUS Status = NDIS_STATUS_SUCCESS; |
| NTSTATUS NtStatus = STATUS_SUCCESS; |
| uint32_t MajorVersion = 0; |
| uint32_t MinorVersion = 0; |
| uint32_t InterfaceType = 0; |
| |
| NET_BUFFER_LIST_POOL_PARAMETERS PoolParams = |
| { |
| { NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1 }, |
| NDIS_PROTOCOL_ID_DEFAULT, |
| TRUE, |
| 0, |
| 'lbNC', // CNbl |
| 0 |
| }; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| do |
| { |
| pFilter->cmdTIDsInUse = 0; |
| pFilter->cmdNextTID = 1; |
| pFilter->cmdResetReason = OT_PLAT_RESET_REASON_POWER_ON; |
| |
| NdisAllocateSpinLock(&pFilter->cmdLock); |
| InitializeListHead(&pFilter->cmdHandlers); |
| |
| KeInitializeEvent( |
| &pFilter->cmdResetCompleteEvent, |
| SynchronizationEvent, // auto-clearing event |
| FALSE // event initially non-signalled |
| ); |
| |
| // Enable rundown protection |
| ExReInitializeRundownProtection(&pFilter->cmdRundown); |
| |
| // Create the NDIS pool for creating the SendNetBufferList |
| pFilter->cmdNblPool = NdisAllocateNetBufferListPool(pFilter->FilterHandle, &PoolParams); |
| if (pFilter->cmdNblPool == NULL) |
| { |
| Status = NDIS_STATUS_RESOURCES; |
| LogWarning(DRIVER_DEFAULT, "Failed to create NetBufferList pool for Spinel commands"); |
| break; |
| } |
| |
| // Query the interface type to make sure it is a Thread device |
| #ifdef COMMAND_INIT_RETRY |
| pFilter->cmdInitTryCount = 0; |
| while (pFilter->cmdInitTryCount < 10) |
| { |
| NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_PROTOCOL_VERSION, "ii", &MajorVersion, &MinorVersion); |
| if (!NT_SUCCESS(NtStatus)) |
| { |
| pFilter->cmdInitTryCount++; |
| NdisMSleep(100); |
| continue; |
| } |
| break; |
| } |
| if (pFilter->cmdInitTryCount >= 10) |
| { |
| #else |
| NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_PROTOCOL_VERSION, "ii", &MajorVersion, &MinorVersion); |
| if (!NT_SUCCESS(NtStatus)) |
| { |
| #endif |
| Status = NDIS_STATUS_NOT_SUPPORTED; |
| LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_PROTOCOL_VERSION, %!STATUS!", NtStatus); |
| break; |
| } |
| if (MajorVersion != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR || |
| MinorVersion < 3) // TODO - Remove this minor version check with the next major version update |
| { |
| Status = NDIS_STATUS_NOT_SUPPORTED; |
| LogError(DRIVER_DEFAULT, "Protocol Version Mismatch! OsVer: %d.%d DeviceVer: %d.%d", |
| SPINEL_PROTOCOL_VERSION_THREAD_MAJOR, SPINEL_PROTOCOL_VERSION_THREAD_MINOR, |
| MajorVersion, MinorVersion); |
| break; |
| } |
| |
| NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_INTERFACE_TYPE, SPINEL_DATATYPE_UINT_PACKED_S, &InterfaceType); |
| if (!NT_SUCCESS(NtStatus)) |
| { |
| Status = NDIS_STATUS_NOT_SUPPORTED; |
| LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_INTERFACE_TYPE, %!STATUS!", NtStatus); |
| break; |
| } |
| if (InterfaceType != SPINEL_PROTOCOL_TYPE_THREAD) |
| { |
| Status = NDIS_STATUS_NOT_SUPPORTED; |
| LogError(DRIVER_DEFAULT, "SPINEL_PROP_INTERFACE_TYPE is invalid, %d", InterfaceType); |
| break; |
| } |
| |
| NtStatus = otLwfCmdResetDevice(pFilter, FALSE); |
| if (!NT_SUCCESS(NtStatus)) |
| { |
| Status = NDIS_STATUS_FAILURE; |
| break; |
| } |
| |
| } while (FALSE); |
| |
| LogFuncExitNDIS(DRIVER_DEFAULT, Status); |
| |
| // Clean up on failure |
| if (Status != NDIS_STATUS_SUCCESS) |
| { |
| otLwfCmdUninitialize(pFilter); |
| } |
| |
| return Status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| void |
| otLwfCmdUninitialize( |
| _In_ PMS_FILTER pFilter |
| ) |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| // Release and wait for run down. This will block waiting for any pending sends to complete |
| ExWaitForRundownProtectionRelease(&pFilter->cmdRundown); |
| |
| // Use the NBL Pool variable as a flag for initialization |
| if (pFilter->cmdNblPool) |
| { |
| // Clean up any pending handlers |
| PLIST_ENTRY Link = pFilter->cmdHandlers.Flink; |
| while (Link != &pFilter->cmdHandlers) |
| { |
| SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link); |
| Link = Link->Flink; |
| |
| if (pEntry->Handler) |
| { |
| pEntry->Handler(pFilter, pEntry->Context, 0, 0, NULL, 0); |
| } |
| |
| ReleaseEntryRef(pEntry); |
| } |
| InitializeListHead(&pFilter->cmdHandlers); |
| |
| // Free NBL Pool |
| NdisFreeNetBufferPool(pFilter->cmdNblPool); |
| pFilter->cmdNblPool = NULL; |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| // |
| // Receive Spinel Encoded Command |
| // |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| void |
| otLwfCmdProcess( |
| _In_ PMS_FILTER pFilter, |
| _In_ BOOLEAN DispatchLevel, |
| _In_ UINT command, |
| _In_reads_bytes_(cmd_data_len) const uint8_t* cmd_data_ptr, |
| _In_ spinel_size_t cmd_data_len |
| ) |
| { |
| uint8_t Header; |
| spinel_prop_key_t key; |
| uint8_t* value_data_ptr = NULL; |
| spinel_size_t value_data_len = 0; |
| |
| // Make sure it's an expected command |
| if (command < SPINEL_CMD_PROP_VALUE_IS || command > SPINEL_CMD_PROP_VALUE_REMOVED) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Recieved unhandled command, %u", command); |
| return; |
| } |
| |
| // Decode the key and data |
| if (spinel_datatype_unpack(cmd_data_ptr, cmd_data_len, "CiiD", &Header, NULL, &key, &value_data_ptr, &value_data_len) == -1) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Failed to unpack command key & data"); |
| return; |
| } |
| |
| // Get the transaction ID |
| if (SPINEL_HEADER_GET_TID(Header) == 0) |
| { |
| // Handle out of band last status locally |
| if (command == SPINEL_CMD_PROP_VALUE_IS && key == SPINEL_PROP_LAST_STATUS) |
| { |
| // Check if this is a reset |
| spinel_status_t status = SPINEL_STATUS_OK; |
| spinel_datatype_unpack(value_data_ptr, value_data_len, "i", &status); |
| |
| if ((status >= SPINEL_STATUS_RESET__BEGIN) && (status <= SPINEL_STATUS_RESET__END)) |
| { |
| LogInfo(DRIVER_DEFAULT, "Interface %!GUID! was reset (status %d).", &pFilter->InterfaceGuid, status); |
| pFilter->cmdResetReason = status - SPINEL_STATUS_RESET__BEGIN; |
| KeSetEvent(&pFilter->cmdResetCompleteEvent, IO_NO_INCREMENT, FALSE); |
| |
| // TODO - Should this be passed on to Thread or Tunnel logic? |
| } |
| } |
| else if (ExAcquireRundownProtection(&pFilter->ExternalRefs)) |
| { |
| // If this is a 'Value Is' command, process it for notification of state changes. |
| if (command == SPINEL_CMD_PROP_VALUE_IS) |
| { |
| if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE) |
| { |
| otLwfThreadValueIs(pFilter, DispatchLevel, key, value_data_ptr, value_data_len); |
| } |
| else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE) |
| { |
| otLwfTunValueIs(pFilter, DispatchLevel, key, value_data_ptr, value_data_len); |
| } |
| } |
| else if (command == SPINEL_CMD_PROP_VALUE_INSERTED) |
| { |
| if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE) |
| { |
| otLwfThreadValueInserted(pFilter, DispatchLevel, key, value_data_ptr, value_data_len); |
| } |
| else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE) |
| { |
| otLwfTunValueInserted(pFilter, DispatchLevel, key, value_data_ptr, value_data_len); |
| } |
| } |
| |
| ExReleaseRundownProtection(&pFilter->ExternalRefs); |
| } |
| } |
| // If there was a transaction ID, then look for the corresponding command handler |
| else |
| { |
| PLIST_ENTRY Link; |
| SPINEL_CMD_HANDLER_ENTRY* Handler = NULL; |
| |
| FILTER_ACQUIRE_LOCK(&pFilter->cmdLock, DispatchLevel); |
| |
| // Search for matching handlers for this command |
| Link = pFilter->cmdHandlers.Flink; |
| while (Link != &pFilter->cmdHandlers) |
| { |
| SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link); |
| Link = Link->Flink; |
| |
| if (SPINEL_HEADER_GET_TID(Header) == pEntry->TransactionId) |
| { |
| // Remove from the main list |
| RemoveEntryList(&pEntry->Link); |
| |
| // Cache the handler |
| Handler = pEntry; |
| |
| // Remove the transaction ID from the 'in use' bit field |
| pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId); |
| |
| break; |
| } |
| } |
| |
| FILTER_RELEASE_LOCK(&pFilter->cmdLock, DispatchLevel); |
| |
| // TODO - Set event |
| |
| // Process the handler we found, outside the lock |
| if (Handler) |
| { |
| // Call the handler function |
| Handler->Handler(pFilter, Handler->Context, command, key, value_data_ptr, value_data_len); |
| |
| // Free the entry |
| ReleaseEntryRef(Handler); |
| } |
| } |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| void |
| otLwfCmdRecveive( |
| _In_ PMS_FILTER pFilter, |
| _In_ BOOLEAN DispatchLevel, |
| _In_reads_bytes_(BufferLength) const PUCHAR Buffer, |
| _In_ ULONG BufferLength |
| ) |
| { |
| uint8_t Header; |
| UINT Command; |
| |
| // Unpack the header from the buffer |
| if (spinel_datatype_unpack(Buffer, BufferLength, "Ci", &Header, &Command) <= 0) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Failed to unpack header and command"); |
| return; |
| } |
| |
| // Validate the header |
| if ((Header & SPINEL_HEADER_FLAG) != SPINEL_HEADER_FLAG) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Recieved unrecognized frame, header=0x%x", Header); |
| return; |
| } |
| |
| // We only support IID zero for now |
| if (SPINEL_HEADER_GET_IID(Header) != 0) |
| { |
| LogVerbose(DRIVER_DEFAULT, "Recieved unsupported IID, %u", SPINEL_HEADER_GET_IID(Header)); |
| return; |
| } |
| |
| // Process the received command |
| otLwfCmdProcess(pFilter, DispatchLevel, Command, Buffer, BufferLength); |
| } |
| |
| // |
| // Send Async Spinel Encoded Command |
| // |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| spinel_tid_t |
| otLwfCmdGetNextTID( |
| _In_ PMS_FILTER pFilter |
| ) |
| { |
| spinel_tid_t TID = 0; |
| while (TID == 0) |
| { |
| NdisAcquireSpinLock(&pFilter->cmdLock); |
| |
| if (((1 << pFilter->cmdNextTID) & pFilter->cmdTIDsInUse) == 0) |
| { |
| TID = pFilter->cmdNextTID; |
| pFilter->cmdNextTID = SPINEL_GET_NEXT_TID(pFilter->cmdNextTID); |
| pFilter->cmdTIDsInUse |= (1 << TID); |
| } |
| |
| NdisReleaseSpinLock(&pFilter->cmdLock); |
| |
| if (TID == 0) |
| { |
| // TODO - Wait for event |
| } |
| } |
| return TID; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| void |
| otLwfCmdAddHandler( |
| _In_ PMS_FILTER pFilter, |
| _In_ SPINEL_CMD_HANDLER_ENTRY *pEntry |
| ) |
| { |
| // Get the next transaction ID. This call will block if there are |
| // none currently available. |
| pEntry->TransactionId = otLwfCmdGetNextTID(pFilter); |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "tid=%u", (ULONG)pEntry->TransactionId); |
| |
| NdisAcquireSpinLock(&pFilter->cmdLock); |
| |
| // Add to the handlers list |
| AddEntryRef(pEntry); |
| InsertTailList(&pFilter->cmdHandlers, &pEntry->Link); |
| |
| NdisReleaseSpinLock(&pFilter->cmdLock); |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdEncodeAndSendAsync( |
| _In_ PMS_FILTER pFilter, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_ spinel_tid_t tid, |
| _In_ ULONG MaxDataLength, |
| _In_opt_ const char *pack_format, |
| _In_opt_ va_list args |
| ) |
| { |
| NTSTATUS status = STATUS_SUCCESS; |
| PNET_BUFFER_LIST NetBufferList = NULL; |
| PNET_BUFFER NetBuffer = NULL; |
| ULONG NetBufferLength = 0; |
| PUCHAR DataBuffer = NULL; |
| spinel_ssize_t PackedLength; |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "Cmd=%u Key=%u tid=%u", (ULONG)Command, (ULONG)Key, (ULONG)tid); |
| |
| NetBufferList = |
| NdisAllocateNetBufferAndNetBufferList( |
| pFilter->cmdNblPool, // PoolHandle |
| 0, // ContextSize |
| 0, // ContextBackFill |
| NULL, // MdlChain |
| 0, // DataOffset |
| 0 // DataLength |
| ); |
| if (NetBufferList == NULL) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| LogWarning(DRIVER_DEFAULT, "Failed to create command NetBufferList"); |
| goto exit; |
| } |
| |
| // Initialize NetBuffer fields |
| NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); |
| NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL; |
| NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0; |
| NET_BUFFER_DATA_LENGTH(NetBuffer) = 0; |
| NET_BUFFER_DATA_OFFSET(NetBuffer) = 0; |
| NET_BUFFER_FIRST_MDL(NetBuffer) = NULL; |
| |
| // Calculate length of NetBuffer |
| NetBufferLength = 16 + MaxDataLength; |
| if (NetBufferLength < 64) NetBufferLength = 64; |
| |
| // Allocate the NetBuffer for NetBufferList |
| if (NdisRetreatNetBufferDataStart(NetBuffer, NetBufferLength, 0, NULL) != NDIS_STATUS_SUCCESS) |
| { |
| NetBuffer = NULL; |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| LogError(DRIVER_DEFAULT, "Failed to allocate NB for command NetBufferList, %u bytes", NetBufferLength); |
| goto exit; |
| } |
| |
| // Get the pointer to the data buffer |
| DataBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, NetBufferLength, NULL, 1, 0); |
| NT_ASSERT(DataBuffer); |
| |
| // Save the true NetBuffer length in the protocol reserved |
| NetBuffer->ProtocolReserved[0] = (PVOID)NetBufferLength; |
| NetBuffer->DataLength = 0; |
| |
| // Save the transaction ID in the protocol reserved |
| NetBuffer->ProtocolReserved[1] = (PVOID)tid; |
| |
| // Pack the header, command and key |
| PackedLength = |
| spinel_datatype_pack( |
| DataBuffer, |
| NetBufferLength, |
| "Cii", |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | tid, |
| Command, |
| Key); |
| if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| goto exit; |
| } |
| |
| NetBuffer->DataLength += (ULONG)PackedLength; |
| |
| // Pack the data (if any) |
| if (pack_format) |
| { |
| PackedLength = |
| spinel_datatype_vpack( |
| DataBuffer + NetBuffer->DataLength, |
| NetBufferLength - NetBuffer->DataLength, |
| pack_format, |
| args); |
| if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| goto exit; |
| } |
| |
| NetBuffer->DataLength += (ULONG)PackedLength; |
| } |
| |
| // Grab a ref for rundown protection |
| if (!ExAcquireRundownProtection(&pFilter->cmdRundown)) |
| { |
| status = STATUS_DEVICE_NOT_READY; |
| LogWarning(DRIVER_DEFAULT, "Failed to acquire rundown protection"); |
| goto exit; |
| } |
| |
| // Send the NBL down |
| NdisFSendNetBufferLists( |
| pFilter->FilterHandle, |
| NetBufferList, |
| NDIS_DEFAULT_PORT_NUMBER, |
| 0); |
| |
| // Clear local variable because we don't own the NBL any more |
| NetBufferList = NULL; |
| |
| exit: |
| |
| if (NetBufferList) |
| { |
| if (NetBuffer) |
| { |
| NetBuffer->DataLength = (ULONG)(ULONG_PTR)NetBuffer->ProtocolReserved[0]; |
| NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL); |
| } |
| NdisFreeNetBufferList(NetBufferList); |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdResetDevice( |
| _In_ PMS_FILTER pFilter, |
| _In_ BOOLEAN fAsync |
| ) |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| KeResetEvent(&pFilter->cmdResetCompleteEvent); |
| |
| NTSTATUS status = otLwfCmdEncodeAndSendAsync(pFilter, SPINEL_CMD_RESET, 0, 0, 0, NULL, NULL); |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "Failed to send SPINEL_CMD_RESET, %!STATUS!", status); |
| } |
| else if (!fAsync) |
| { |
| // Create the relative (negative) time to wait for 5 seconds |
| LARGE_INTEGER Timeout; |
| Timeout.QuadPart = -5000 * 10000; |
| |
| status = KeWaitForSingleObject(&pFilter->cmdResetCompleteEvent, Executive, KernelMode, FALSE, &Timeout); |
| if (status != STATUS_SUCCESS) |
| { |
| LogError(DRIVER_DEFAULT, "Failed waiting for reset complete, %!STATUS!", status); |
| status = STATUS_DEVICE_BUSY; |
| } |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, status); |
| |
| return status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdSendAsyncV( |
| _In_ PMS_FILTER pFilter, |
| _In_opt_ SPINEL_CMD_HANDLER *Handler, |
| _In_opt_ PVOID HandlerContext, |
| _Out_opt_ spinel_tid_t *pTid, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_ ULONG MaxDataLength, |
| _In_opt_ const char *pack_format, |
| _In_opt_ va_list args |
| ) |
| { |
| NTSTATUS status = STATUS_SUCCESS; |
| SPINEL_CMD_HANDLER_ENTRY *pEntry = NULL; |
| |
| if (pTid) *pTid = 0; |
| |
| // Create the handler entry and add it to the list |
| if (Handler) |
| { |
| pEntry = FILTER_ALLOC_MEM(pFilter->FilterHandle, sizeof(SPINEL_CMD_HANDLER_ENTRY)); |
| if (pEntry == NULL) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| LogWarning(DRIVER_DEFAULT, "Failed to allocate handler entry"); |
| goto exit; |
| } |
| |
| pEntry->RefCount = 1; |
| pEntry->Handler = Handler; |
| pEntry->Context = HandlerContext; |
| |
| otLwfCmdAddHandler(pFilter, pEntry); |
| |
| if (pTid) *pTid = pEntry->TransactionId; |
| } |
| |
| status = otLwfCmdEncodeAndSendAsync(pFilter, Command, Key, pEntry ? pEntry->TransactionId : 0, MaxDataLength, pack_format, args); |
| |
| // Remove the handler entry from the list |
| if (!NT_SUCCESS(status) && pEntry) |
| { |
| NdisAcquireSpinLock(&pFilter->cmdLock); |
| |
| // Remove from the main list |
| RemoveEntryList(&pEntry->Link); |
| |
| // Remove the transaction ID from the 'in use' bit field |
| pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId); |
| |
| NdisReleaseSpinLock(&pFilter->cmdLock); |
| |
| // TODO - Set event |
| |
| ReleaseEntryRef(pEntry); |
| } |
| |
| exit: |
| |
| if (pEntry) ReleaseEntryRef(pEntry); |
| |
| return status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdSendAsync( |
| _In_ PMS_FILTER pFilter, |
| _In_opt_ SPINEL_CMD_HANDLER *Handler, |
| _In_opt_ PVOID HandlerContext, |
| _Out_opt_ spinel_tid_t *pTid, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_ ULONG MaxDataLength, |
| _In_opt_ const char *pack_format, |
| ... |
| ) |
| { |
| va_list args; |
| va_start(args, pack_format); |
| NTSTATUS status = |
| otLwfCmdSendAsyncV(pFilter, Handler, HandlerContext, pTid, Command, Key, MaxDataLength, pack_format, args); |
| va_end(args); |
| return status; |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| BOOLEAN |
| otLwfCmdCancel( |
| _In_ PMS_FILTER pFilter, |
| _In_ BOOLEAN DispatchLevel, |
| _In_ spinel_tid_t tid |
| ) |
| { |
| PLIST_ENTRY Link; |
| SPINEL_CMD_HANDLER_ENTRY* Handler = NULL; |
| BOOLEAN Found = FALSE; |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "tid=%u", (ULONG)tid); |
| |
| FILTER_ACQUIRE_LOCK(&pFilter->cmdLock, DispatchLevel); |
| |
| // Search for matching handlers for this transaction ID |
| Link = pFilter->cmdHandlers.Flink; |
| while (Link != &pFilter->cmdHandlers) |
| { |
| SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link); |
| Link = Link->Flink; |
| |
| if (tid == pEntry->TransactionId) |
| { |
| // Remove from the main list |
| RemoveEntryList(&pEntry->Link); |
| |
| // Save handler to cancel outside lock |
| Handler = pEntry; |
| Found = TRUE; |
| |
| // Remove the transaction ID from the 'in use' bit field |
| pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId); |
| |
| break; |
| } |
| } |
| |
| FILTER_RELEASE_LOCK(&pFilter->cmdLock, DispatchLevel); |
| |
| if (Handler) |
| { |
| // Call the handler function |
| Handler->Handler(pFilter, Handler->Context, 0, 0, NULL, 0); |
| |
| // Free the entry |
| ReleaseEntryRef(Handler); |
| } |
| |
| LogFuncExitMsg(DRIVER_DEFAULT, "Found=%u", Found); |
| |
| return Found; |
| } |
| |
| // |
| // Send Packet/Frame |
| // |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| NTSTATUS |
| otLwfCmdSendIp6PacketAsync( |
| _In_ PMS_FILTER pFilter, |
| _In_ BOOLEAN DispatchLevel, |
| _In_ PNET_BUFFER IpNetBuffer, |
| _In_ BOOLEAN Secured |
| ) |
| { |
| NTSTATUS status = STATUS_SUCCESS; |
| PNET_BUFFER_LIST NetBufferList = NULL; |
| PNET_BUFFER NetBuffer = NULL; |
| ULONG NetBufferLength = 0; |
| PUCHAR DataBuffer = NULL; |
| PUCHAR IpDataBuffer = NULL; |
| spinel_ssize_t PackedLength; |
| IPV6_HEADER* v6Header; |
| |
| NetBufferList = |
| NdisAllocateNetBufferAndNetBufferList( |
| pFilter->cmdNblPool, // PoolHandle |
| 0, // ContextSize |
| 0, // ContextBackFill |
| NULL, // MdlChain |
| 0, // DataOffset |
| 0 // DataLength |
| ); |
| if (NetBufferList == NULL) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| LogWarning(DRIVER_DEFAULT, "Failed to create command NetBufferList"); |
| goto exit; |
| } |
| |
| // Initialize NetBuffer fields |
| NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); |
| NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL; |
| NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0; |
| NET_BUFFER_DATA_LENGTH(NetBuffer) = 0; |
| NET_BUFFER_DATA_OFFSET(NetBuffer) = 0; |
| NET_BUFFER_FIRST_MDL(NetBuffer) = NULL; |
| |
| // Calculate length of NetBuffer |
| NetBufferLength = 20 + IpNetBuffer->DataLength; |
| if (NetBufferLength < 64) NetBufferLength = 64; |
| |
| // Allocate the NetBuffer for NetBufferList |
| if (NdisRetreatNetBufferDataStart(NetBuffer, NetBufferLength, 0, NULL) != NDIS_STATUS_SUCCESS) |
| { |
| NetBuffer = NULL; |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| LogError(DRIVER_DEFAULT, "Failed to allocate NB for command NetBufferList, %u bytes", NetBufferLength); |
| goto exit; |
| } |
| |
| // Get the pointer to the data buffer for the header data |
| DataBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, NetBufferLength, NULL, 1, 0); |
| NT_ASSERT(DataBuffer); |
| |
| // Save the true NetBuffer length in the protocol reserved |
| NetBuffer->ProtocolReserved[0] = (PVOID)NetBufferLength; |
| NetBuffer->DataLength = 0; |
| |
| // Pack the header, command and key |
| PackedLength = |
| spinel_datatype_pack( |
| DataBuffer, |
| NetBufferLength, |
| "Cii", |
| (spinel_tid_t)(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0), |
| (UINT)SPINEL_CMD_PROP_VALUE_SET, |
| (Secured ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE)); |
| if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength) |
| { |
| status = STATUS_INSUFFICIENT_RESOURCES; |
| goto exit; |
| } |
| |
| NT_ASSERT(PackedLength >= 3); |
| NetBuffer->DataLength += (ULONG)PackedLength; |
| |
| // Copy over the data length |
| DataBuffer[NetBuffer->DataLength+1] = (((USHORT)IpNetBuffer->DataLength) >> 8) & 0xff; |
| DataBuffer[NetBuffer->DataLength] = (((USHORT)IpNetBuffer->DataLength) >> 0) & 0xff; |
| NetBuffer->DataLength += 2; |
| |
| v6Header = (IPV6_HEADER*)(DataBuffer + NetBuffer->DataLength); |
| |
| // Copy the IP packet data |
| IpDataBuffer = (PUCHAR)NdisGetDataBuffer(IpNetBuffer, IpNetBuffer->DataLength, v6Header, 1, 0); |
| if (IpDataBuffer != (PUCHAR)v6Header) |
| { |
| RtlCopyMemory(v6Header, IpDataBuffer, IpNetBuffer->DataLength); |
| } |
| |
| NetBuffer->DataLength += IpNetBuffer->DataLength; |
| |
| // Grab a ref for rundown protection |
| if (!ExAcquireRundownProtection(&pFilter->cmdRundown)) |
| { |
| status = STATUS_DEVICE_NOT_READY; |
| LogWarning(DRIVER_DEFAULT, "Failed to acquire rundown protection"); |
| goto exit; |
| } |
| |
| LogVerbose(DRIVER_DATA_PATH, "Filter: %p, IP6_SEND: %p : %!IPV6ADDR! => %!IPV6ADDR! (%u bytes)", |
| pFilter, NetBufferList, &v6Header->SourceAddress, &v6Header->DestinationAddress, |
| NET_BUFFER_DATA_LENGTH(IpNetBuffer)); |
| |
| // Send the NBL down |
| NdisFSendNetBufferLists( |
| pFilter->FilterHandle, |
| NetBufferList, |
| NDIS_DEFAULT_PORT_NUMBER, |
| DispatchLevel ? NDIS_SEND_FLAGS_DISPATCH_LEVEL : 0); |
| |
| // Clear local variable because we don't own the NBL any more |
| NetBufferList = NULL; |
| |
| exit: |
| |
| if (NetBufferList) |
| { |
| if (NetBuffer) |
| { |
| NetBuffer->DataLength = (ULONG)(ULONG_PTR)NetBuffer->ProtocolReserved[0]; |
| NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL); |
| } |
| NdisFreeNetBufferList(NetBufferList); |
| } |
| |
| return status; |
| } |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| VOID |
| otLwfCmdSendMacFrameComplete( |
| _In_ PMS_FILTER pFilter, |
| _In_ PVOID Context, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_reads_bytes_(DataLength) const uint8_t* Data, |
| _In_ spinel_size_t DataLength |
| ) |
| { |
| UNREFERENCED_PARAMETER(Context); |
| |
| pFilter->otLastTransmitError = OT_ERROR_ABORT; |
| |
| if (Data && Command == SPINEL_CMD_PROP_VALUE_IS) |
| { |
| if (Key == SPINEL_PROP_LAST_STATUS) |
| { |
| spinel_status_t spinel_status = SPINEL_STATUS_OK; |
| spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status); |
| if (packed_len > 0) |
| { |
| if (spinel_status == SPINEL_STATUS_OK) |
| { |
| pFilter->otLastTransmitError = OT_ERROR_NONE; |
| (void)spinel_datatype_unpack( |
| Data + packed_len, |
| DataLength - (spinel_size_t)packed_len, |
| "b", |
| &pFilter->otLastTransmitFramePending); |
| } |
| else |
| { |
| pFilter->otLastTransmitError = SpinelStatusToThreadError(spinel_status); |
| } |
| } |
| } |
| } |
| |
| // Set the completion event |
| KeSetEvent(&pFilter->SendNetBufferListComplete, IO_NO_INCREMENT, FALSE); |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| VOID |
| otLwfCmdSendMacFrameAsync( |
| _In_ PMS_FILTER pFilter, |
| _In_ otRadioFrame* Packet |
| ) |
| { |
| // Reset the completion event |
| KeResetEvent(&pFilter->SendNetBufferListComplete); |
| pFilter->SendPending = TRUE; |
| |
| NTSTATUS status = |
| otLwfCmdSendAsync( |
| pFilter, |
| otLwfCmdSendMacFrameComplete, |
| NULL, |
| NULL, |
| SPINEL_CMD_PROP_VALUE_SET, |
| SPINEL_PROP_STREAM_RAW, |
| Packet->mLength + 20, |
| SPINEL_DATATYPE_DATA_WLEN_S |
| SPINEL_DATATYPE_UINT8_S |
| SPINEL_DATATYPE_INT8_S, |
| Packet->mPsdu, |
| (uint32_t)Packet->mLength, |
| Packet->mChannel, |
| Packet->mPower |
| ); |
| if (!NT_SUCCESS(status)) |
| { |
| LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_STREAM_RAW failed, %!STATUS!", status); |
| pFilter->otLastTransmitError = OT_ERROR_ABORT; |
| KeSetEvent(&pFilter->SendNetBufferListComplete, IO_NO_INCREMENT, FALSE); |
| } |
| } |
| |
| // |
| // Send Synchronous Spinel Encoded Command |
| // |
| |
| typedef struct _SPINEL_GET_PROP_CONTEXT |
| { |
| KEVENT CompletionEvent; |
| spinel_prop_key_t Key; |
| PVOID *DataBuffer; |
| const char* Format; |
| va_list Args; |
| NTSTATUS Status; |
| } SPINEL_GET_PROP_CONTEXT; |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| VOID |
| otLwfGetPropHandler( |
| _In_ PMS_FILTER pFilter, |
| _In_ PVOID Context, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_reads_bytes_(DataLength) const uint8_t* Data, |
| _In_ spinel_size_t DataLength |
| ) |
| { |
| SPINEL_GET_PROP_CONTEXT* CmdContext = (SPINEL_GET_PROP_CONTEXT*)Context; |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key); |
| |
| UNREFERENCED_PARAMETER(pFilter); |
| |
| if (Data == NULL) |
| { |
| CmdContext->Status = STATUS_CANCELLED; |
| } |
| else if (Command != SPINEL_CMD_PROP_VALUE_IS) |
| { |
| CmdContext->Status = STATUS_INVALID_PARAMETER; |
| } |
| else if (Key == SPINEL_PROP_LAST_STATUS) |
| { |
| spinel_status_t spinel_status = SPINEL_STATUS_OK; |
| spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status); |
| if (packed_len < 0 || (ULONG)packed_len > DataLength) |
| { |
| CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES; |
| } |
| else |
| { |
| otError errorCode = SpinelStatusToThreadError(spinel_status); |
| LogVerbose(DRIVER_DEFAULT, "Get key=%u failed with %!otError!", CmdContext->Key, errorCode); |
| CmdContext->Status = ThreadErrorToNtstatus(errorCode); |
| } |
| } |
| else if (Key == CmdContext->Key) |
| { |
| if (CmdContext->DataBuffer) |
| { |
| *CmdContext->DataBuffer = FILTER_ALLOC_MEM(pFilter->FilterHandle, DataLength); |
| if (*CmdContext->DataBuffer == NULL) |
| { |
| CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES; |
| DataLength = 0; |
| } |
| else |
| { |
| memcpy(*CmdContext->DataBuffer, Data, DataLength); |
| Data = (uint8_t*)*CmdContext->DataBuffer; |
| } |
| } |
| |
| spinel_ssize_t packed_len = spinel_datatype_vunpack(Data, DataLength, CmdContext->Format, CmdContext->Args); |
| if (packed_len < 0 || (ULONG)packed_len > DataLength) |
| { |
| CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES; |
| } |
| else |
| { |
| CmdContext->Status = STATUS_SUCCESS; |
| } |
| } |
| else |
| { |
| CmdContext->Status = STATUS_INVALID_PARAMETER; |
| } |
| |
| // Set the completion event |
| KeSetEvent(&CmdContext->CompletionEvent, 0, FALSE); |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdGetProp( |
| _In_ PMS_FILTER pFilter, |
| _Out_opt_ PVOID *DataBuffer, |
| _In_ spinel_prop_key_t Key, |
| _In_ const char *pack_format, |
| ... |
| ) |
| { |
| NTSTATUS status; |
| LARGE_INTEGER WaitTimeout; |
| spinel_tid_t tid; |
| |
| // Create the context structure |
| SPINEL_GET_PROP_CONTEXT Context; |
| KeInitializeEvent(&Context.CompletionEvent, SynchronizationEvent, FALSE); |
| Context.Key = Key; |
| Context.DataBuffer = DataBuffer; |
| Context.Format = pack_format; |
| Context.Status = STATUS_SUCCESS; |
| va_start(Context.Args, pack_format); |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key); |
| |
| // Send the request transaction |
| status = |
| otLwfCmdSendAsyncV( |
| pFilter, |
| otLwfGetPropHandler, |
| &Context, |
| &tid, |
| SPINEL_CMD_PROP_VALUE_GET, |
| Key, |
| 0, |
| NULL, |
| NULL); |
| if (NT_SUCCESS(status)) |
| { |
| // Set a 1 second wait timeout |
| WaitTimeout.QuadPart = -1000 * 10000; |
| |
| // Wait for the response |
| if (KeWaitForSingleObject( |
| &Context.CompletionEvent, |
| Executive, |
| KernelMode, |
| FALSE, |
| &WaitTimeout) != STATUS_SUCCESS) |
| { |
| if (!otLwfCmdCancel(pFilter, FALSE, tid)) |
| { |
| KeWaitForSingleObject( |
| &Context.CompletionEvent, |
| Executive, |
| KernelMode, |
| FALSE, |
| NULL); |
| } |
| } |
| } |
| else |
| { |
| Context.Status = status; |
| } |
| |
| va_end(Context.Args); |
| |
| LogFuncExitNT(DRIVER_DEFAULT, Context.Status); |
| |
| return Context.Status; |
| } |
| |
| typedef struct _SPINEL_SET_PROP_CONTEXT |
| { |
| KEVENT CompletionEvent; |
| UINT ExpectedResultCommand; |
| spinel_prop_key_t Key; |
| NTSTATUS Status; |
| } SPINEL_SET_PROP_CONTEXT; |
| |
| _IRQL_requires_max_(DISPATCH_LEVEL) |
| VOID |
| otLwfSetPropHandler( |
| _In_ PMS_FILTER pFilter, |
| _In_ PVOID Context, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_reads_bytes_(DataLength) const uint8_t* Data, |
| _In_ spinel_size_t DataLength |
| ) |
| { |
| SPINEL_SET_PROP_CONTEXT* CmdContext = (SPINEL_SET_PROP_CONTEXT*)Context; |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key); |
| |
| UNREFERENCED_PARAMETER(pFilter); |
| |
| if (Data == NULL) |
| { |
| CmdContext->Status = STATUS_CANCELLED; |
| } |
| else if (Command == SPINEL_CMD_PROP_VALUE_IS && Key == SPINEL_PROP_LAST_STATUS) |
| { |
| spinel_status_t spinel_status = SPINEL_STATUS_OK; |
| spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status); |
| if (packed_len < 0 || (ULONG)packed_len > DataLength) |
| { |
| CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES; |
| } |
| else |
| { |
| otError errorCode = SpinelStatusToThreadError(spinel_status); |
| LogVerbose(DRIVER_DEFAULT, "Set key=%u failed with %!otError!", CmdContext->Key, errorCode); |
| CmdContext->Status = ThreadErrorToNtstatus(errorCode); |
| } |
| } |
| else if (Command != CmdContext->ExpectedResultCommand) |
| { |
| NT_ASSERT(FALSE); |
| CmdContext->Status = STATUS_INVALID_PARAMETER; |
| } |
| else if (Key == CmdContext->Key) |
| { |
| CmdContext->Status = STATUS_SUCCESS; |
| } |
| else |
| { |
| NT_ASSERT(FALSE); |
| CmdContext->Status = STATUS_INVALID_PARAMETER; |
| } |
| |
| // Set the completion event |
| KeSetEvent(&CmdContext->CompletionEvent, 0, FALSE); |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdSetPropV( |
| _In_ PMS_FILTER pFilter, |
| _In_ UINT Command, |
| _In_ spinel_prop_key_t Key, |
| _In_opt_ const char *pack_format, |
| _In_opt_ va_list args |
| ) |
| { |
| NTSTATUS status; |
| LARGE_INTEGER WaitTimeout; |
| spinel_tid_t tid; |
| |
| // Create the context structure |
| SPINEL_SET_PROP_CONTEXT Context; |
| KeInitializeEvent(&Context.CompletionEvent, SynchronizationEvent, FALSE); |
| Context.Key = Key; |
| Context.Status = STATUS_SUCCESS; |
| |
| LogFuncEntryMsg(DRIVER_DEFAULT, "Cmd=%u Key=%u", Command, (ULONG)Key); |
| |
| if (Command == SPINEL_CMD_PROP_VALUE_SET) |
| { |
| Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_IS; |
| } |
| else if (Command == SPINEL_CMD_PROP_VALUE_INSERT) |
| { |
| Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_INSERTED; |
| } |
| else if (Command == SPINEL_CMD_PROP_VALUE_REMOVE) |
| { |
| Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_REMOVED; |
| } |
| else |
| { |
| ASSERT(FALSE); |
| } |
| |
| // Send the request transaction |
| status = |
| otLwfCmdSendAsyncV( |
| pFilter, |
| otLwfSetPropHandler, |
| &Context, |
| &tid, |
| Command, |
| Key, |
| 8, |
| pack_format, |
| args); |
| if (NT_SUCCESS(status)) |
| { |
| // Set a 1 second wait timeout |
| WaitTimeout.QuadPart = -1000 * 10000; |
| |
| // Wait for the response |
| if (KeWaitForSingleObject( |
| &Context.CompletionEvent, |
| Executive, |
| KernelMode, |
| FALSE, |
| &WaitTimeout) != STATUS_SUCCESS) |
| { |
| if (!otLwfCmdCancel(pFilter, FALSE, tid)) |
| { |
| KeWaitForSingleObject( |
| &Context.CompletionEvent, |
| Executive, |
| KernelMode, |
| FALSE, |
| NULL); |
| } |
| } |
| } |
| else |
| { |
| Context.Status = status; |
| } |
| |
| LogFuncExitNT(DRIVER_DEFAULT, Context.Status); |
| |
| return Context.Status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdSetProp( |
| _In_ PMS_FILTER pFilter, |
| _In_ spinel_prop_key_t Key, |
| _In_opt_ const char *pack_format, |
| ... |
| ) |
| { |
| va_list args; |
| va_start(args, pack_format); |
| NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_SET, Key, pack_format, args); |
| va_end(args); |
| return status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdInsertProp( |
| _In_ PMS_FILTER pFilter, |
| _In_ spinel_prop_key_t Key, |
| _In_opt_ const char *pack_format, |
| ... |
| ) |
| { |
| va_list args; |
| va_start(args, pack_format); |
| NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_INSERT, Key, pack_format, args); |
| va_end(args); |
| return status; |
| } |
| |
| _IRQL_requires_max_(PASSIVE_LEVEL) |
| NTSTATUS |
| otLwfCmdRemoveProp( |
| _In_ PMS_FILTER pFilter, |
| _In_ spinel_prop_key_t Key, |
| _In_opt_ const char *pack_format, |
| ... |
| ) |
| { |
| va_list args; |
| va_start(args, pack_format); |
| NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_REMOVE, Key, pack_format, args); |
| va_end(args); |
| return status; |
| } |
| |
| // |
| // General Spinel Helpers |
| // |
| |
| otError |
| SpinelStatusToThreadError( |
| spinel_status_t error |
| ) |
| { |
| otError ret; |
| |
| switch (error) |
| { |
| case SPINEL_STATUS_OK: |
| ret = OT_ERROR_NONE; |
| break; |
| |
| case SPINEL_STATUS_FAILURE: |
| ret = OT_ERROR_FAILED; |
| break; |
| |
| case SPINEL_STATUS_DROPPED: |
| ret = OT_ERROR_DROP; |
| break; |
| |
| case SPINEL_STATUS_NOMEM: |
| ret = OT_ERROR_NO_BUFS; |
| break; |
| |
| case SPINEL_STATUS_BUSY: |
| ret = OT_ERROR_BUSY; |
| break; |
| |
| case SPINEL_STATUS_PARSE_ERROR: |
| ret = OT_ERROR_PARSE; |
| break; |
| |
| case SPINEL_STATUS_INVALID_ARGUMENT: |
| ret = OT_ERROR_INVALID_ARGS; |
| break; |
| |
| case SPINEL_STATUS_UNIMPLEMENTED: |
| ret = OT_ERROR_NOT_IMPLEMENTED; |
| break; |
| |
| case SPINEL_STATUS_INVALID_STATE: |
| ret = OT_ERROR_INVALID_STATE; |
| break; |
| |
| case SPINEL_STATUS_NO_ACK: |
| ret = OT_ERROR_NO_ACK; |
| break; |
| |
| case SPINEL_STATUS_CCA_FAILURE: |
| ret = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| break; |
| |
| case SPINEL_STATUS_ALREADY: |
| ret = OT_ERROR_ALREADY; |
| break; |
| |
| case SPINEL_STATUS_ITEM_NOT_FOUND: |
| ret = OT_ERROR_NOT_FOUND; |
| break; |
| |
| default: |
| if (error >= SPINEL_STATUS_STACK_NATIVE__BEGIN && error <= SPINEL_STATUS_STACK_NATIVE__END) |
| { |
| ret = (otError)(error - SPINEL_STATUS_STACK_NATIVE__BEGIN); |
| } |
| else |
| { |
| ret = OT_ERROR_FAILED; |
| } |
| break; |
| } |
| |
| return ret; |
| } |
| |
| BOOLEAN |
| try_spinel_datatype_unpack( |
| const uint8_t *data_in, |
| spinel_size_t data_len, |
| const char *pack_format, |
| ... |
| ) |
| { |
| va_list args; |
| va_start(args, pack_format); |
| spinel_ssize_t packed_len = spinel_datatype_vunpack(data_in, data_len, pack_format, args); |
| va_end(args); |
| |
| return !(packed_len < 0 || (spinel_size_t)packed_len > data_len); |
| } |