| /* |
| * |
| * Copyright (c) 2019 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * Contains non-inline method definitions for the |
| * GenericThreadStackManagerImpl_OpenThread<> template. |
| */ |
| |
| #ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |
| #define GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |
| |
| #include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h> |
| #include <Weave/DeviceLayer/ThreadStackManager.h> |
| #include <Weave/DeviceLayer/OpenThread/GenericThreadStackManagerImpl_OpenThread.h> |
| #include <Weave/DeviceLayer/OpenThread/OpenThreadUtils.h> |
| #include <Weave/Profiles/network-provisioning/NetworkProvisioning.h> |
| #include <Weave/DeviceLayer/internal/DeviceNetworkInfo.h> |
| #include <Weave/Support/crypto/WeaveRNG.h> |
| #include <Weave/Support/TraitEventUtils.h> |
| #include <nest/trait/network/TelemetryNetworkWpanTrait.h> |
| |
| #include <openthread/thread.h> |
| #include <openthread/tasklet.h> |
| #include <openthread/link.h> |
| #include <openthread/dataset.h> |
| #include <openthread/dataset_ftd.h> |
| #include <openthread/thread_ftd.h> |
| |
| using namespace ::nl::Weave::Profiles::NetworkProvisioning; |
| using namespace Schema::Nest::Trait::Network; |
| |
| extern "C" void otSysProcessDrivers(otInstance *aInstance); |
| |
| namespace nl { |
| namespace Weave { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| // Assert some presumptions in this code |
| static_assert(DeviceNetworkInfo::kMaxThreadNetworkNameLength == OT_NETWORK_NAME_MAX_SIZE); |
| static_assert(DeviceNetworkInfo::kThreadExtendedPANIdLength == OT_EXT_PAN_ID_SIZE); |
| static_assert(DeviceNetworkInfo::kThreadMeshPrefixLength == OT_MESH_LOCAL_PREFIX_SIZE); |
| static_assert(DeviceNetworkInfo::kThreadNetworkKeyLength == OT_MASTER_KEY_SIZE); |
| static_assert(DeviceNetworkInfo::kThreadPSKcLength == OT_PSKC_MAX_SIZE); |
| |
| // Fully instantiate the generic implementation class in whatever compilation unit includes this file. |
| template class GenericThreadStackManagerImpl_OpenThread<ThreadStackManagerImpl>; |
| |
| /** |
| * Called by OpenThread to alert the ThreadStackManager of a change in the state of the Thread stack. |
| * |
| * By default, applications never need to call this method directly. However, applications that |
| * wish to receive OpenThread state change call-backs directly from OpenThread (e.g. by calling |
| * otSetStateChangedCallback() with their own callback function) can call this method to pass |
| * state change events to the ThreadStackManager. |
| */ |
| template<class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnOpenThreadStateChange(uint32_t flags, void * context) |
| { |
| WeaveDeviceEvent event; |
| event.Type = DeviceEventType::kThreadStateChange; |
| event.ThreadStateChange.RoleChanged = (flags & OT_CHANGED_THREAD_ROLE) != 0; |
| event.ThreadStateChange.AddressChanged = (flags & (OT_CHANGED_IP6_ADDRESS_ADDED|OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0; |
| event.ThreadStateChange.NetDataChanged = (flags & OT_CHANGED_THREAD_NETDATA) != 0; |
| event.ThreadStateChange.ChildNodesChanged = (flags & (OT_CHANGED_THREAD_CHILD_ADDED|OT_CHANGED_THREAD_CHILD_REMOVED)) != 0; |
| event.ThreadStateChange.OpenThread.Flags = flags; |
| PlatformMgr().PostEvent(&event); |
| } |
| |
| template<class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ProcessThreadActivity(void) |
| { |
| otTaskletsProcess(mOTInst); |
| otSysProcessDrivers(mOTInst); |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_HaveRouteToAddress(const IPAddress & destAddr) |
| { |
| bool res = false; |
| |
| // Lock OpenThread |
| Impl()->LockThreadStack(); |
| |
| // No routing of IPv4 over Thread. |
| VerifyOrExit(!destAddr.IsIPv4(), res = false); |
| |
| // If the device is attached to a Thread network... |
| if (IsThreadAttachedNoLock()) |
| { |
| // Link-local addresses are always presumed to be routable, provided the device is attached. |
| if (destAddr.IsIPv6LinkLocal()) |
| { |
| ExitNow(res = true); |
| } |
| |
| // Iterate over the routes known to the OpenThread stack looking for a route that covers the |
| // destination address. If found, consider the address routable. |
| // Ignore any routes advertised by this device. |
| // If the destination address is a ULA, ignore default routes. Border routers advertising |
| // default routes are not expected to be capable of routing Weave fabric ULAs unless they |
| // advertise those routes specifically. |
| { |
| otError otErr; |
| otNetworkDataIterator routeIter = OT_NETWORK_DATA_ITERATOR_INIT; |
| otExternalRouteConfig routeConfig; |
| const bool destIsULA = destAddr.IsIPv6ULA(); |
| |
| while ((otErr = otNetDataGetNextRoute(Impl()->OTInstance(), &routeIter, &routeConfig)) == OT_ERROR_NONE) |
| { |
| const IPPrefix prefix = ToIPPrefix(routeConfig.mPrefix); |
| char addrStr[64]; |
| prefix.IPAddr.ToString(addrStr, sizeof(addrStr)); |
| if (!routeConfig.mNextHopIsThisDevice && |
| (!destIsULA || routeConfig.mPrefix.mLength > 0) && |
| ToIPPrefix(routeConfig.mPrefix).MatchAddress(destAddr)) |
| { |
| ExitNow(res = true); |
| } |
| } |
| } |
| } |
| |
| exit: |
| |
| // Unlock OpenThread |
| Impl()->UnlockThreadStack(); |
| |
| return res; |
| } |
| |
| template<class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnPlatformEvent(const WeaveDeviceEvent * event) |
| { |
| if (event->Type == DeviceEventType::kThreadStateChange) |
| { |
| #if WEAVE_DETAIL_LOGGING |
| |
| Impl()->LockThreadStack(); |
| |
| LogOpenThreadStateChange(mOTInst, event->ThreadStateChange.OpenThread.Flags); |
| |
| Impl()->UnlockThreadStack(); |
| |
| #endif // WEAVE_DETAIL_LOGGING |
| } |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadEnabled(void) |
| { |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| curRole = otThreadGetDeviceRole(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return (curRole != OT_DEVICE_ROLE_DISABLED); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadEnabled(bool val) |
| { |
| otError otErr = OT_ERROR_NONE; |
| |
| Impl()->LockThreadStack(); |
| |
| bool isEnabled = (otThreadGetDeviceRole(mOTInst) != OT_DEVICE_ROLE_DISABLED); |
| if (val != isEnabled) |
| { |
| otErr = otThreadSetEnabled(mOTInst, val); |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| return MapOpenThreadError(otErr); |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadProvisioned(void) |
| { |
| bool provisioned; |
| |
| Impl()->LockThreadStack(); |
| provisioned = otDatasetIsCommissioned(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return provisioned; |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadAttached(void) |
| { |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| curRole = otThreadGetDeviceRole(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetThreadProvision(DeviceNetworkInfo & netInfo, bool includeCredentials) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otOperationalDataset activeDataset; |
| |
| netInfo.Reset(); |
| |
| Impl()->LockThreadStack(); |
| |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| err = MapOpenThreadError(otErr); |
| } |
| else |
| { |
| err = WEAVE_ERROR_INCORRECT_STATE; |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| SuccessOrExit(err); |
| |
| netInfo.NetworkType = kNetworkType_Thread; |
| netInfo.NetworkId = kThreadNetworkId; |
| netInfo.FieldPresent.NetworkId = true; |
| if (activeDataset.mComponents.mIsNetworkNamePresent) |
| { |
| strncpy(netInfo.ThreadNetworkName, (const char *)activeDataset.mNetworkName.m8, sizeof(netInfo.ThreadNetworkName)); |
| } |
| if (activeDataset.mComponents.mIsExtendedPanIdPresent) |
| { |
| memcpy(netInfo.ThreadExtendedPANId, activeDataset.mExtendedPanId.m8, sizeof(netInfo.ThreadExtendedPANId)); |
| netInfo.FieldPresent.ThreadExtendedPANId = true; |
| } |
| if (activeDataset.mComponents.mIsMeshLocalPrefixPresent) |
| { |
| memcpy(netInfo.ThreadMeshPrefix, activeDataset.mMeshLocalPrefix.m8, sizeof(netInfo.ThreadMeshPrefix)); |
| netInfo.FieldPresent.ThreadMeshPrefix = true; |
| } |
| if (includeCredentials) |
| { |
| if (activeDataset.mComponents.mIsMasterKeyPresent) |
| { |
| memcpy(netInfo.ThreadNetworkKey, activeDataset.mMasterKey.m8, sizeof(netInfo.ThreadNetworkKey)); |
| netInfo.FieldPresent.ThreadNetworkKey = true; |
| } |
| if (activeDataset.mComponents.mIsPSKcPresent) |
| { |
| memcpy(netInfo.ThreadPSKc, activeDataset.mPSKc.m8, sizeof(netInfo.ThreadPSKc)); |
| netInfo.FieldPresent.ThreadPSKc = true; |
| } |
| } |
| if (activeDataset.mComponents.mIsPanIdPresent) |
| { |
| netInfo.ThreadPANId = activeDataset.mPanId; |
| } |
| if (activeDataset.mComponents.mIsChannelPresent) |
| { |
| netInfo.ThreadChannel = activeDataset.mChannel; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadProvision(const DeviceNetworkInfo & netInfo) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otError otErr; |
| otOperationalDataset newDataset; |
| |
| // Form a Thread operational dataset from the given network parameters. |
| memset(&newDataset, 0, sizeof(newDataset)); |
| newDataset.mComponents.mIsActiveTimestampPresent = true; |
| newDataset.mComponents.mIsPendingTimestampPresent = true; |
| if (netInfo.ThreadNetworkName[0] != 0) |
| { |
| strncpy((char *)newDataset.mNetworkName.m8, netInfo.ThreadNetworkName, sizeof(newDataset.mNetworkName.m8)); |
| newDataset.mComponents.mIsNetworkNamePresent = true; |
| } |
| if (netInfo.FieldPresent.ThreadExtendedPANId) |
| { |
| memcpy(newDataset.mExtendedPanId.m8, netInfo.ThreadExtendedPANId, sizeof(newDataset.mExtendedPanId.m8)); |
| newDataset.mComponents.mIsExtendedPanIdPresent = true; |
| } |
| if (netInfo.FieldPresent.ThreadMeshPrefix) |
| { |
| memcpy(newDataset.mMeshLocalPrefix.m8, netInfo.ThreadMeshPrefix, sizeof(newDataset.mMeshLocalPrefix.m8)); |
| newDataset.mComponents.mIsMeshLocalPrefixPresent = true; |
| } |
| if (netInfo.FieldPresent.ThreadNetworkKey) |
| { |
| memcpy(newDataset.mMasterKey.m8, netInfo.ThreadNetworkKey, sizeof(newDataset.mMasterKey.m8)); |
| newDataset.mComponents.mIsMasterKeyPresent = true; |
| } |
| if (netInfo.FieldPresent.ThreadPSKc) |
| { |
| memcpy(newDataset.mPSKc.m8, netInfo.ThreadPSKc, sizeof(newDataset.mPSKc.m8)); |
| newDataset.mComponents.mIsPSKcPresent = true; |
| } |
| if (netInfo.ThreadPANId != kThreadPANId_NotSpecified) |
| { |
| newDataset.mPanId = netInfo.ThreadPANId; |
| newDataset.mComponents.mIsPanIdPresent = true; |
| } |
| if (netInfo.ThreadChannel != kThreadChannel_NotSpecified) |
| { |
| newDataset.mChannel = netInfo.ThreadChannel; |
| newDataset.mComponents.mIsChannelPresent = true; |
| } |
| |
| // Set the dataset as the active dataset for the node. |
| Impl()->LockThreadStack(); |
| otErr = otDatasetSetActive(mOTInst, &newDataset); |
| Impl()->UnlockThreadStack(); |
| |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ClearThreadProvision(void) |
| { |
| Impl()->LockThreadStack(); |
| otThreadSetEnabled(mOTInst, false); |
| otInstanceErasePersistentInfo(mOTInst); |
| Impl()->UnlockThreadStack(); |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_HaveMeshConnectivity(void) |
| { |
| bool res; |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| |
| // Get the current Thread role. |
| curRole = otThreadGetDeviceRole(mOTInst); |
| |
| // If Thread is disabled, or the node is detached, then the node has no mesh connectivity. |
| if (curRole == OT_DEVICE_ROLE_DISABLED || curRole == OT_DEVICE_ROLE_DETACHED) |
| { |
| res = false; |
| } |
| |
| // If the node is a child, that implies the existence of a parent node which provides connectivity |
| // to the mesh. |
| else if (curRole == OT_DEVICE_ROLE_CHILD) |
| { |
| res = true; |
| } |
| |
| // Otherwise, if the node is acting as a router, scan the Thread neighbor table looking for at least |
| // one other node that is also acting as router. |
| else |
| { |
| otNeighborInfoIterator neighborIter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighborInfo; |
| |
| res = false; |
| |
| while (otThreadGetNextNeighborInfo(mOTInst, &neighborIter, &neighborInfo) == OT_ERROR_NONE) |
| { |
| if (!neighborInfo.mIsChild) |
| { |
| res = true; |
| break; |
| } |
| } |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| return res; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadStatsCounters(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otError otErr; |
| nl::Weave::Profiles::DataManagement_Current::event_id_t eventId; |
| Schema::Nest::Trait::Network::TelemetryNetworkWpanTrait::NetworkWpanStatsEvent counterEvent = { 0 }; |
| const otMacCounters *macCounters; |
| const otIpCounters *ipCounters; |
| otOperationalDataset activeDataset; |
| otDeviceRole role; |
| |
| Impl()->LockThreadStack(); |
| |
| // Get Mac Counters |
| macCounters = otLinkGetCounters(mOTInst); |
| |
| // Rx Counters |
| counterEvent.phyRx = macCounters->mRxTotal; |
| counterEvent.macUnicastRx = macCounters->mRxUnicast; |
| counterEvent.macBroadcastRx = macCounters->mRxBroadcast; |
| counterEvent.macRxData = macCounters->mRxData; |
| counterEvent.macRxDataPoll = macCounters->mRxDataPoll; |
| counterEvent.macRxBeacon = macCounters->mRxBeacon; |
| counterEvent.macRxBeaconReq = macCounters->mRxBeaconRequest; |
| counterEvent.macRxOtherPkt = macCounters->mRxOther; |
| counterEvent.macRxFilterWhitelist = macCounters->mRxAddressFiltered; |
| counterEvent.macRxFilterDestAddr = macCounters->mRxDestAddrFiltered; |
| |
| // Tx Counters |
| counterEvent.phyTx = macCounters->mTxTotal; |
| counterEvent.macUnicastTx = macCounters->mTxUnicast; |
| counterEvent.macBroadcastTx = macCounters->mTxBroadcast; |
| counterEvent.macTxAckReq = macCounters->mTxAckRequested; |
| counterEvent.macTxNoAckReq = macCounters->mTxNoAckRequested; |
| counterEvent.macTxAcked = macCounters->mTxAcked; |
| counterEvent.macTxData = macCounters->mTxData; |
| counterEvent.macTxDataPoll = macCounters->mTxDataPoll; |
| counterEvent.macTxBeacon = macCounters->mTxBeacon; |
| counterEvent.macTxBeaconReq = macCounters->mTxBeaconRequest; |
| counterEvent.macTxOtherPkt = macCounters->mTxOther; |
| counterEvent.macTxRetry = macCounters->mTxRetry; |
| |
| // Tx Error Counters |
| counterEvent.macTxFailCca = macCounters->mTxErrCca; |
| |
| // Rx Error Counters |
| counterEvent.macRxFailDecrypt = macCounters->mRxErrSec; |
| counterEvent.macRxFailNoFrame = macCounters->mRxErrNoFrame; |
| counterEvent.macRxFailUnknownNeighbor = macCounters->mRxErrUnknownNeighbor; |
| counterEvent.macRxFailInvalidSrcAddr = macCounters->mRxErrInvalidSrcAddr; |
| counterEvent.macRxFailFcs = macCounters->mRxErrFcs; |
| counterEvent.macRxFailOther = macCounters->mRxErrOther; |
| |
| // Get Ip Counters |
| ipCounters = otThreadGetIp6Counters(mOTInst); |
| |
| // Ip Counters |
| counterEvent.ipTxSuccess = ipCounters->mTxSuccess; |
| counterEvent.ipRxSuccess = ipCounters->mRxSuccess; |
| counterEvent.ipTxFailure = ipCounters->mTxFailure; |
| counterEvent.ipRxFailure = ipCounters->mRxFailure; |
| |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| if (activeDataset.mComponents.mIsChannelPresent) |
| { |
| counterEvent.channel = activeDataset.mChannel; |
| } |
| } |
| |
| role = otThreadGetDeviceRole(mOTInst); |
| |
| switch (role) |
| { |
| case OT_DEVICE_ROLE_LEADER: |
| counterEvent.nodeType |= TelemetryNetworkWpanTrait::NODE_TYPE_LEADER; |
| // Intentional fall-through: if it's a leader, then it's also a router |
| case OT_DEVICE_ROLE_ROUTER: |
| counterEvent.nodeType |= TelemetryNetworkWpanTrait::NODE_TYPE_ROUTER; |
| break; |
| case OT_DEVICE_ROLE_CHILD: |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| default: |
| counterEvent.nodeType = 0; |
| break; |
| } |
| |
| counterEvent.threadType = TelemetryNetworkWpanTrait::THREAD_TYPE_OPENTHREAD; |
| |
| WeaveLogProgress(DeviceLayer, |
| "Rx Counters:\n" |
| "PHY Rx Total: %d\n" |
| "MAC Rx Unicast: %d\n" |
| "MAC Rx Broadcast: %d\n" |
| "MAC Rx Data: %d\n" |
| "MAC Rx Data Polls: %d\n" |
| "MAC Rx Beacons: %d\n" |
| "MAC Rx Beacon Reqs: %d\n" |
| "MAC Rx Other: %d\n" |
| "MAC Rx Filtered Whitelist: %d\n" |
| "MAC Rx Filtered DestAddr: %d\n", |
| counterEvent.phyRx, counterEvent.macUnicastRx, counterEvent.macBroadcastRx, counterEvent.macRxData, |
| counterEvent.macRxDataPoll, counterEvent.macRxBeacon, counterEvent.macRxBeaconReq, counterEvent.macRxOtherPkt, |
| counterEvent.macRxFilterWhitelist, counterEvent.macRxFilterDestAddr); |
| |
| WeaveLogProgress(DeviceLayer, |
| "Tx Counters:\n" |
| "PHY Tx Total: %d\n" |
| "MAC Tx Unicast: %d\n" |
| "MAC Tx Broadcast: %d\n" |
| "MAC Tx Data: %d\n" |
| "MAC Tx Data Polls: %d\n" |
| "MAC Tx Beacons: %d\n" |
| "MAC Tx Beacon Reqs: %d\n" |
| "MAC Tx Other: %d\n" |
| "MAC Tx Retry: %d\n" |
| "MAC Tx CCA Fail: %d\n", |
| counterEvent.phyTx, counterEvent.macUnicastTx, counterEvent.macBroadcastTx, counterEvent.macTxData, |
| counterEvent.macTxDataPoll, counterEvent.macTxBeacon, counterEvent.macTxBeaconReq, counterEvent.macTxOtherPkt, |
| counterEvent.macTxRetry, counterEvent.macTxFailCca); |
| |
| WeaveLogProgress(DeviceLayer, |
| "Failure Counters:\n" |
| "MAC Rx Decrypt Fail: %d\n" |
| "MAC Rx No Frame Fail: %d\n" |
| "MAC Rx Unknown Neighbor Fail: %d\n" |
| "MAC Rx Invalid Src Addr Fail: %d\n" |
| "MAC Rx FCS Fail: %d\n" |
| "MAC Rx Other Fail: %d\n", |
| counterEvent.macRxFailDecrypt, counterEvent.macRxFailNoFrame, counterEvent.macRxFailUnknownNeighbor, |
| counterEvent.macRxFailInvalidSrcAddr, counterEvent.macRxFailFcs, counterEvent.macRxFailOther); |
| |
| eventId = nl::LogEvent(&counterEvent); |
| WeaveLogProgress(DeviceLayer, "OpenThread Telemetry Stats Event Id: %u\n", eventId); |
| |
| Impl()->UnlockThreadStack(); |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyMinimal(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otError otErr; |
| nl::Weave::Profiles::DataManagement_Current::event_id_t eventId; |
| Schema::Nest::Trait::Network::TelemetryNetworkWpanTrait::NetworkWpanTopoMinimalEvent topologyEvent = { 0 }; |
| const otExtAddress *extAddress; |
| |
| Impl()->LockThreadStack(); |
| |
| topologyEvent.rloc16 = otThreadGetRloc16(mOTInst); |
| |
| // Router ID is the top 6 bits of the RLOC |
| topologyEvent.routerId = (topologyEvent.rloc16 >> 10) & 0x3f; |
| |
| topologyEvent.leaderRouterId = otThreadGetLeaderRouterId(mOTInst); |
| |
| otErr = otThreadGetParentAverageRssi(mOTInst, &topologyEvent.parentAverageRssi); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| otErr = otThreadGetParentLastRssi(mOTInst, &topologyEvent.parentLastRssi); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| topologyEvent.partitionId = otThreadGetPartitionId(mOTInst); |
| |
| extAddress = otLinkGetExtendedAddress(mOTInst); |
| |
| topologyEvent.extAddress.mBuf = (uint8_t *)extAddress; |
| topologyEvent.extAddress.mLen = sizeof(otExtAddress); |
| |
| topologyEvent.instantRssi = otPlatRadioGetRssi(mOTInst); |
| |
| WeaveLogProgress(DeviceLayer, |
| "Thread Topology:\n" |
| "RLOC16: %04X\n" |
| "Router ID: %u\n" |
| "Leader Router ID: %u\n" |
| "Parent Avg RSSI: %d\n" |
| "Parent Last RSSI: %d\n" |
| "Partition ID: %d\n" |
| "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "Instant RSSI: %d\n", |
| topologyEvent.rloc16, topologyEvent.routerId, topologyEvent.leaderRouterId, topologyEvent.parentAverageRssi, |
| topologyEvent.parentLastRssi, topologyEvent.partitionId, topologyEvent.extAddress.mBuf[0], topologyEvent.extAddress.mBuf[1], |
| topologyEvent.extAddress.mBuf[2], topologyEvent.extAddress.mBuf[3], topologyEvent.extAddress.mBuf[4], |
| topologyEvent.extAddress.mBuf[5], topologyEvent.extAddress.mBuf[6], topologyEvent.extAddress.mBuf[7], topologyEvent.instantRssi); |
| |
| eventId = nl::LogEvent(&topologyEvent); |
| WeaveLogProgress(DeviceLayer, "OpenThread Telemetry Stats Event Id: %u\n", eventId); |
| |
| Impl()->UnlockThreadStack(); |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(DeviceLayer, "GetAndLogThreadTopologyMinimul failed: %s", nl::ErrorStr(err)); |
| } |
| |
| return err; |
| } |
| |
| #define TELEM_NEIGHBOR_TABLE_SIZE (64) |
| #define TELEM_PRINT_BUFFER_SIZE (64) |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyFull(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otError otErr; |
| nl::Weave::Profiles::DataManagement_Current::EventOptions neighborTopoOpts(true); |
| nl::Weave::Profiles::DataManagement_Current::event_id_t eventId; |
| Schema::Nest::Trait::Network::TelemetryNetworkWpanTrait::NetworkWpanTopoFullEvent fullTopoEvent = { 0 }; |
| Schema::Nest::Trait::Network::TelemetryNetworkWpanTrait::TopoEntryEvent neighborTopoEvent = { 0 }; |
| otIp6Address * leaderAddr = NULL; |
| uint8_t * networkData = NULL; |
| uint8_t * stableNetworkData = NULL; |
| uint8_t networkDataLen = 0; |
| uint8_t stableNetworkDataLen = 0; |
| const otExtAddress * extAddress; |
| otNeighborInfo neighborInfo[TELEM_NEIGHBOR_TABLE_SIZE]; |
| otNeighborInfoIterator iter; |
| otNeighborInfoIterator iterCopy; |
| char printBuf[TELEM_PRINT_BUFFER_SIZE]; |
| |
| Impl()->LockThreadStack(); |
| |
| fullTopoEvent.rloc16 = otThreadGetRloc16(mOTInst); |
| |
| // Router ID is the top 6 bits of the RLOC |
| fullTopoEvent.routerId = (fullTopoEvent.rloc16 >> 10) & 0x3f; |
| |
| fullTopoEvent.leaderRouterId = otThreadGetLeaderRouterId(mOTInst); |
| |
| otErr = otThreadGetLeaderRloc(mOTInst, leaderAddr); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| fullTopoEvent.leaderAddress.mBuf = leaderAddr->mFields.m8; |
| fullTopoEvent.leaderAddress.mLen = OT_IP6_ADDRESS_SIZE; |
| |
| fullTopoEvent.leaderWeight = otThreadGetLeaderWeight(mOTInst); |
| |
| fullTopoEvent.leaderLocalWeight = otThreadGetLocalLeaderWeight(mOTInst); |
| |
| otErr = otNetDataGet(mOTInst, false, networkData, &networkDataLen); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| fullTopoEvent.networkData.mBuf = networkData; |
| fullTopoEvent.networkData.mLen = networkDataLen; |
| |
| fullTopoEvent.networkDataVersion = otNetDataGetVersion(mOTInst); |
| |
| otErr = otNetDataGet(mOTInst, true, stableNetworkData, &stableNetworkDataLen); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| fullTopoEvent.stableNetworkData.mBuf = stableNetworkData; |
| fullTopoEvent.stableNetworkData.mLen = stableNetworkDataLen; |
| |
| fullTopoEvent.stableNetworkDataVersion = otNetDataGetStableVersion(mOTInst); |
| |
| // Deprecated property |
| fullTopoEvent.preferredRouterId = -1; |
| |
| extAddress = otLinkGetExtendedAddress(mOTInst); |
| |
| fullTopoEvent.extAddress.mBuf = (uint8_t *)extAddress; |
| fullTopoEvent.extAddress.mLen = sizeof(otExtAddress); |
| |
| fullTopoEvent.partitionId = otThreadGetPartitionId(mOTInst); |
| |
| fullTopoEvent.instantRssi = otPlatRadioGetRssi(mOTInst); |
| |
| iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| iterCopy = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| fullTopoEvent.neighborTableSize = 0; |
| fullTopoEvent.childTableSize = 0; |
| |
| while (otThreadGetNextNeighborInfo(mOTInst, &iter, &neighborInfo[iter]) == OT_ERROR_NONE) |
| { |
| fullTopoEvent.neighborTableSize++; |
| if (neighborInfo[iterCopy].mIsChild) |
| { |
| fullTopoEvent.childTableSize++; |
| } |
| iterCopy = iter; |
| } |
| |
| WeaveLogProgress(DeviceLayer, |
| "Thread Topology:\n" |
| "RLOC16: %04X\n" |
| "Router ID: %u\n" |
| "Leader Router ID: %u\n" |
| "Leader Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "Leader Weight: %d\n" |
| "Local Leader Weight: %d\n" |
| "Network Data Len: %d\n" |
| "Network Data Version: %d\n" |
| "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "Partition ID: %X\n" |
| "Instant RSSI: %d\n" |
| "Neighbor Table Length: %d\n" |
| "Child Table Length: %d\n", |
| fullTopoEvent.rloc16, fullTopoEvent.routerId, fullTopoEvent.leaderRouterId, |
| fullTopoEvent.leaderAddress.mBuf[0], fullTopoEvent.leaderAddress.mBuf[1], fullTopoEvent.leaderAddress.mBuf[2], fullTopoEvent.leaderAddress.mBuf[3], |
| fullTopoEvent.leaderAddress.mBuf[4], fullTopoEvent.leaderAddress.mBuf[5], fullTopoEvent.leaderAddress.mBuf[6], fullTopoEvent.leaderAddress.mBuf[7], |
| fullTopoEvent.leaderAddress.mBuf[8], fullTopoEvent.leaderAddress.mBuf[9], fullTopoEvent.leaderAddress.mBuf[10], fullTopoEvent.leaderAddress.mBuf[11], |
| fullTopoEvent.leaderAddress.mBuf[12], fullTopoEvent.leaderAddress.mBuf[13], fullTopoEvent.leaderAddress.mBuf[14], fullTopoEvent.leaderAddress.mBuf[15], |
| fullTopoEvent.leaderWeight, fullTopoEvent.leaderLocalWeight, fullTopoEvent.networkData.mLen, fullTopoEvent.networkDataVersion, |
| fullTopoEvent.extAddress.mBuf[0], fullTopoEvent.extAddress.mBuf[1], fullTopoEvent.extAddress.mBuf[2], fullTopoEvent.extAddress.mBuf[3], |
| fullTopoEvent.extAddress.mBuf[4], fullTopoEvent.extAddress.mBuf[5], fullTopoEvent.extAddress.mBuf[6], fullTopoEvent.extAddress.mBuf[7], |
| fullTopoEvent.partitionId, fullTopoEvent.instantRssi, fullTopoEvent.neighborTableSize, fullTopoEvent.childTableSize); |
| |
| eventId = nl::LogEvent(&fullTopoEvent); |
| WeaveLogProgress(DeviceLayer, "OpenThread Full Topology Event Id: %u\n", eventId); |
| |
| // Populate the neighbor event options. |
| // This way the neighbor topology entries are linked to the actual topology full event. |
| neighborTopoOpts.relatedEventID = eventId; |
| neighborTopoOpts.relatedImportance = TelemetryNetworkWpanTrait::NetworkWpanTopoFullEvent::Schema.mImportance; |
| |
| // Handle each neighbor event seperatly. |
| for (uint32_t i = 0; i < fullTopoEvent.neighborTableSize; i++) |
| { |
| otNeighborInfo * neighbor = &neighborInfo[i]; |
| |
| neighborTopoEvent.extAddress.mBuf = neighbor->mExtAddress.m8; |
| neighborTopoEvent.extAddress.mLen = sizeof(uint64_t); |
| |
| neighborTopoEvent.rloc16 = neighbor->mRloc16; |
| neighborTopoEvent.linkQualityIn = neighbor->mLinkQualityIn; |
| neighborTopoEvent.averageRssi = neighbor->mAverageRssi; |
| neighborTopoEvent.age = neighbor->mAge; |
| neighborTopoEvent.rxOnWhenIdle = neighbor->mRxOnWhenIdle; |
| // TODO: not supported in old version of OpenThread used by Nordic SDK. |
| // neighborTopoEvent.fullFunction = neighbor->mFullThreadDevice; |
| neighborTopoEvent.fullFunction = false; |
| neighborTopoEvent.secureDataRequest = neighbor->mSecureDataRequest; |
| neighborTopoEvent.fullNetworkData = neighbor->mFullNetworkData; |
| neighborTopoEvent.lastRssi = neighbor->mLastRssi; |
| neighborTopoEvent.linkFrameCounter = neighbor->mLinkFrameCounter; |
| neighborTopoEvent.mleFrameCounter = neighbor->mMleFrameCounter; |
| neighborTopoEvent.isChild = neighbor->mIsChild; |
| |
| if (neighborTopoEvent.isChild) |
| { |
| otChildInfo * child = NULL; |
| otErr = otThreadGetChildInfoById(mOTInst, neighborTopoEvent.rloc16, child); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| neighborTopoEvent.timeout = child->mTimeout; |
| neighborTopoEvent.networkDataVersion = child->mNetworkDataVersion; |
| |
| neighborTopoEvent.SetTimeoutPresent(); |
| neighborTopoEvent.SetNetworkDataVersionPresent(); |
| |
| snprintf(printBuf, TELEM_PRINT_BUFFER_SIZE, ", Timeout: %10lu NetworkDataVersion: %3d", |
| neighborTopoEvent.timeout, neighborTopoEvent.networkDataVersion); |
| } |
| else |
| { |
| neighborTopoEvent.SetTimeoutNull(); |
| neighborTopoEvent.SetNetworkDataVersionNull(); |
| |
| printBuf[0] = 0; |
| } |
| |
| WeaveLogProgress(DeviceLayer, |
| "TopoEntry[%u]: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "RLOC: %04X\n" |
| "Age: %3d\n" |
| "LQI: %1d\n" |
| "AvgRSSI: %3d\n" |
| "LastRSSI: %3d\n" |
| "LinkFrameCounter: %10d\n" |
| "MleFrameCounter: %10d\n" |
| "RxOnWhenIdle: %c\n" |
| "SecureDataRequest: %c\n" |
| "FullFunction: %c\n" |
| "FullNetworkData: %c\n" |
| "IsChild: %c%s\n", |
| i, neighborTopoEvent.extAddress.mBuf[0], neighborTopoEvent.extAddress.mBuf[1], neighborTopoEvent.extAddress.mBuf[2], |
| neighborTopoEvent.extAddress.mBuf[3], neighborTopoEvent.extAddress.mBuf[4], neighborTopoEvent.extAddress.mBuf[5], |
| neighborTopoEvent.extAddress.mBuf[6], neighborTopoEvent.extAddress.mBuf[7], neighborTopoEvent.rloc16, neighborTopoEvent.age, |
| neighborTopoEvent.linkQualityIn, neighborTopoEvent.averageRssi, neighborTopoEvent.lastRssi, neighborTopoEvent.linkFrameCounter, |
| neighborTopoEvent.mleFrameCounter, neighborTopoEvent.rxOnWhenIdle ? 'Y' : 'n', neighborTopoEvent.secureDataRequest ? 'Y' : 'n', |
| neighborTopoEvent.fullFunction ? 'Y' : 'n', neighborTopoEvent.fullNetworkData ? 'Y' : 'n', |
| neighborTopoEvent.isChild ? 'Y' : 'n', printBuf); |
| |
| eventId = nl::LogEvent(&neighborTopoEvent, neighborTopoOpts); |
| WeaveLogProgress(DeviceLayer, "OpenThread Neighbor TopoEntry[%u] Event Id: %ld\n", i, eventId); |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(DeviceLayer, "GetAndLogThreadTopologyFull failed: %s", nl::ErrorStr(err)); |
| } |
| return err; |
| } |
| |
| template<class ImplClass> |
| WEAVE_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::DoInit(otInstance * otInst) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| otError otErr; |
| |
| // Arrange for OpenThread errors to be translated to text. |
| RegisterOpenThreadErrorFormatter(); |
| |
| mOTInst = NULL; |
| |
| // If an OpenThread instance hasn't been supplied, call otInstanceInitSingle() to |
| // create or acquire a singleton instance of OpenThread. |
| if (otInst == NULL) |
| { |
| otInst = otInstanceInitSingle(); |
| VerifyOrExit(otInst != NULL, err = MapOpenThreadError(OT_ERROR_FAILED)); |
| } |
| |
| mOTInst = otInst; |
| |
| // Arrange for OpenThread to call the OnOpenThreadStateChange method whenever a |
| // state change occurs. Note that we reference the OnOpenThreadStateChange method |
| // on the concrete implementation class so that that class can override the default |
| // method implementation if it chooses to. |
| otErr = otSetStateChangedCallback(otInst, ImplClass::OnOpenThreadStateChange, NULL); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| // TODO: generalize this |
| { |
| otLinkModeConfig linkMode; |
| |
| memset(&linkMode, 0, sizeof(linkMode)); |
| linkMode.mRxOnWhenIdle = true; |
| linkMode.mSecureDataRequests = true; |
| linkMode.mDeviceType = true; |
| linkMode.mNetworkData = true; |
| |
| otErr = otThreadSetLinkMode(otInst, linkMode); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| } |
| |
| // TODO: not supported in old version of OpenThread used by Nordic SDK. |
| // otIp6SetSlaacEnabled(otInst, false); |
| |
| otErr = otIp6SetEnabled(otInst, true); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| if (otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED && otDatasetIsCommissioned(otInst)) |
| { |
| otErr = otThreadSetEnabled(otInst, true); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| template<class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadAttachedNoLock(void) |
| { |
| otDeviceRole curRole = otThreadGetDeviceRole(mOTInst); |
| return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); |
| } |
| |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace Weave |
| } // namespace nl |
| |
| |
| #endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |