blob: 0fdb8eb56b64300fdba11be0459a711cbecf20c8 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* 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 <openthread/cli.h>
#include <openthread/dataset.h>
#include <openthread/dataset_ftd.h>
#include <openthread/joiner.h>
#include <openthread/link.h>
#include <openthread/netdata.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/thread_ftd.h>
#include <platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h>
#include <platform/OpenThread/OpenThreadUtils.h>
#include <platform/ThreadStackManager.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <platform/internal/DeviceNetworkInfo.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>
extern "C" void otSysProcessDrivers(otInstance * aInstance);
namespace chip {
namespace DeviceLayer {
namespace Internal {
// 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)
{
ChipDeviceEvent 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 CHIP 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 ChipDeviceEvent * event)
{
if (event->Type == DeviceEventType::kThreadStateChange)
{
Impl()->LockThreadStack();
#if CHIP_DETAIL_LOGGING
LogOpenThreadStateChange(mOTInst, event->ThreadStateChange.OpenThread.Flags);
#endif // CHIP_DETAIL_LOGGING
Impl()->UnlockThreadStack();
}
}
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>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadEnabled(bool val)
{
otError otErr = OT_ERROR_NONE;
Impl()->LockThreadStack();
bool isEnabled = (otThreadGetDeviceRole(mOTInst) != OT_DEVICE_ROLE_DISABLED);
bool isIp6Enabled = otIp6IsEnabled(mOTInst);
if (val && !isIp6Enabled)
{
otErr = otIp6SetEnabled(mOTInst, val);
VerifyOrExit(otErr == OT_ERROR_NONE, );
}
if (val != isEnabled)
{
otErr = otThreadSetEnabled(mOTInst, val);
VerifyOrExit(otErr == OT_ERROR_NONE, );
}
if (!val && isIp6Enabled)
{
otErr = otIp6SetEnabled(mOTInst, val);
VerifyOrExit(otErr == OT_ERROR_NONE, );
}
exit:
Impl()->UnlockThreadStack();
return MapOpenThreadError(otErr);
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadProvision(const DeviceNetworkInfo & netInfo)
{
otError otErr = OT_ERROR_FAILED;
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;
}
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();
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>
ConnectivityManager::ThreadDeviceType GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetThreadDeviceType(void)
{
otLinkModeConfig linkMode;
ConnectivityManager::ThreadDeviceType deviceType;
Impl()->LockThreadStack();
linkMode = otThreadGetLinkMode(mOTInst);
if (linkMode.mDeviceType)
{
if (otThreadIsRouterEligible(mOTInst))
{
deviceType = ConnectivityManager::kThreadDeviceType_Router;
}
else
{
deviceType = ConnectivityManager::kThreadDeviceType_FullEndDevice;
}
}
else
{
if (linkMode.mRxOnWhenIdle)
{
deviceType = ConnectivityManager::kThreadDeviceType_MinimalEndDevice;
}
else
{
deviceType = ConnectivityManager::kThreadDeviceType_SleepyEndDevice;
}
}
Impl()->UnlockThreadStack();
return deviceType;
}
template <class ImplClass>
CHIP_ERROR
GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType)
{
CHIP_ERROR err = CHIP_NO_ERROR;
otLinkModeConfig linkMode;
VerifyOrExit(deviceType == ConnectivityManager::kThreadDeviceType_Router ||
deviceType == ConnectivityManager::kThreadDeviceType_FullEndDevice ||
deviceType == ConnectivityManager::kThreadDeviceType_MinimalEndDevice ||
deviceType == ConnectivityManager::kThreadDeviceType_SleepyEndDevice,
err = CHIP_ERROR_INVALID_ARGUMENT);
#if CHIP_PROGRESS_LOGGING
{
const char * deviceTypeStr;
switch (deviceType)
{
case ConnectivityManager::kThreadDeviceType_Router:
deviceTypeStr = "ROUTER";
break;
case ConnectivityManager::kThreadDeviceType_FullEndDevice:
deviceTypeStr = "FULL END DEVICE";
break;
case ConnectivityManager::kThreadDeviceType_MinimalEndDevice:
deviceTypeStr = "MINIMAL END DEVICE";
break;
case ConnectivityManager::kThreadDeviceType_SleepyEndDevice:
deviceTypeStr = "SLEEPY END DEVICE";
break;
default:
deviceTypeStr = "(unknown)";
break;
}
ChipLogProgress(DeviceLayer, "Setting OpenThread device type to %s", deviceTypeStr);
}
#endif // CHIP_PROGRESS_LOGGING
Impl()->LockThreadStack();
linkMode = otThreadGetLinkMode(mOTInst);
switch (deviceType)
{
case ConnectivityManager::kThreadDeviceType_Router:
case ConnectivityManager::kThreadDeviceType_FullEndDevice:
linkMode.mDeviceType = true;
linkMode.mRxOnWhenIdle = true;
otThreadSetRouterEligible(mOTInst, deviceType == ConnectivityManager::kThreadDeviceType_Router);
break;
case ConnectivityManager::kThreadDeviceType_MinimalEndDevice:
linkMode.mDeviceType = false;
linkMode.mRxOnWhenIdle = true;
break;
case ConnectivityManager::kThreadDeviceType_SleepyEndDevice:
linkMode.mDeviceType = false;
linkMode.mRxOnWhenIdle = false;
break;
default:
break;
}
otThreadSetLinkMode(mOTInst, linkMode);
Impl()->UnlockThreadStack();
exit:
return err;
}
template <class ImplClass>
void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetThreadPollingConfig(
ConnectivityManager::ThreadPollingConfig & pollingConfig)
{
pollingConfig = mPollingConfig;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadPollingConfig(
const ConnectivityManager::ThreadPollingConfig & pollingConfig)
{
mPollingConfig = pollingConfig;
return Impl()->AdjustPollingInterval();
}
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>
void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnMessageLayerActivityChanged(bool messageLayerIsActive)
{
Impl()->AdjustPollingInterval();
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadStatsCounters(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
otError otErr;
const otMacCounters * macCounters;
const otIpCounters * ipCounters;
otOperationalDataset activeDataset;
otDeviceRole role;
Impl()->LockThreadStack();
role = otThreadGetDeviceRole(mOTInst);
ChipLogProgress(DeviceLayer, "Thread Role: %d\n", role);
if (otDatasetIsCommissioned(mOTInst))
{
otErr = otDatasetGetActive(mOTInst, &activeDataset);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
if (activeDataset.mComponents.mIsChannelPresent)
{
ChipLogProgress(DeviceLayer, "Thread Channel: %d\n", activeDataset.mChannel);
}
}
macCounters = otLinkGetCounters(mOTInst);
ChipLogProgress(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",
macCounters->mRxTotal, macCounters->mRxUnicast, macCounters->mRxBroadcast, macCounters->mRxData,
macCounters->mRxDataPoll, macCounters->mRxBeacon, macCounters->mRxBeaconRequest, macCounters->mRxOther,
macCounters->mRxAddressFiltered, macCounters->mRxDestAddrFiltered);
ChipLogProgress(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",
macCounters->mTxTotal, macCounters->mTxUnicast, macCounters->mTxBroadcast, macCounters->mTxData,
macCounters->mTxDataPoll, macCounters->mTxBeacon, macCounters->mTxBeaconRequest, macCounters->mTxOther,
macCounters->mTxRetry, macCounters->mTxErrCca);
ChipLogProgress(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",
macCounters->mRxErrSec, macCounters->mRxErrNoFrame, macCounters->mRxErrUnknownNeighbor,
macCounters->mRxErrInvalidSrcAddr, macCounters->mRxErrFcs, macCounters->mRxErrOther);
ipCounters = otThreadGetIp6Counters(mOTInst);
ChipLogProgress(DeviceLayer,
"IP Counters:\n"
"IP Tx Success: %d\n"
"IP Rx Success: %d\n"
"IP Tx Fail: %d\n"
"IP Rx Fail: %d\n",
ipCounters->mTxSuccess, ipCounters->mRxSuccess, ipCounters->mTxFailure, ipCounters->mRxFailure);
Impl()->UnlockThreadStack();
exit:
return err;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyMinimal(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
otError otErr;
const otExtAddress * extAddress;
uint16_t rloc16;
uint16_t routerId;
uint16_t leaderRouterId;
uint32_t partitionId;
int8_t parentAverageRssi;
int8_t parentLastRssi;
int8_t instantRssi;
Impl()->LockThreadStack();
rloc16 = otThreadGetRloc16(mOTInst);
// Router ID is the top 6 bits of the RLOC
routerId = (rloc16 >> 10) & 0x3f;
leaderRouterId = otThreadGetLeaderRouterId(mOTInst);
otErr = otThreadGetParentAverageRssi(mOTInst, &parentAverageRssi);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
otErr = otThreadGetParentLastRssi(mOTInst, &parentLastRssi);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
partitionId = otThreadGetPartitionId(mOTInst);
extAddress = otLinkGetExtendedAddress(mOTInst);
instantRssi = otPlatRadioGetRssi(mOTInst);
ChipLogProgress(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",
rloc16, routerId, leaderRouterId, parentAverageRssi, parentLastRssi, partitionId, extAddress->m8[0],
extAddress->m8[1], extAddress->m8[2], extAddress->m8[3], extAddress->m8[4], extAddress->m8[5],
extAddress->m8[6], extAddress->m8[7], instantRssi);
Impl()->UnlockThreadStack();
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "GetAndLogThreadTopologyMinimul failed: %d", err);
}
return err;
}
#define TELEM_NEIGHBOR_TABLE_SIZE (64)
#define TELEM_PRINT_BUFFER_SIZE (64)
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyFull(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
otError otErr;
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];
uint16_t rloc16;
uint16_t routerId;
uint16_t leaderRouterId;
uint8_t leaderWeight;
uint8_t leaderLocalWeight;
uint32_t partitionId;
int8_t instantRssi;
uint8_t networkDataVersion;
uint8_t stableNetworkDataVersion;
uint16_t neighborTableSize = 0;
uint16_t childTableSize = 0;
Impl()->LockThreadStack();
rloc16 = otThreadGetRloc16(mOTInst);
// Router ID is the top 6 bits of the RLOC
routerId = (rloc16 >> 10) & 0x3f;
leaderRouterId = otThreadGetLeaderRouterId(mOTInst);
otErr = otThreadGetLeaderRloc(mOTInst, leaderAddr);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
leaderWeight = otThreadGetLeaderWeight(mOTInst);
leaderLocalWeight = otThreadGetLocalLeaderWeight(mOTInst);
otErr = otNetDataGet(mOTInst, false, networkData, &networkDataLen);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
networkDataVersion = otNetDataGetVersion(mOTInst);
otErr = otNetDataGet(mOTInst, true, stableNetworkData, &stableNetworkDataLen);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
stableNetworkDataVersion = otNetDataGetStableVersion(mOTInst);
extAddress = otLinkGetExtendedAddress(mOTInst);
partitionId = otThreadGetPartitionId(mOTInst);
instantRssi = otPlatRadioGetRssi(mOTInst);
iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
iterCopy = OT_NEIGHBOR_INFO_ITERATOR_INIT;
neighborTableSize = 0;
childTableSize = 0;
while (otThreadGetNextNeighborInfo(mOTInst, &iter, &neighborInfo[iter]) == OT_ERROR_NONE)
{
neighborTableSize++;
if (neighborInfo[iterCopy].mIsChild)
{
childTableSize++;
}
iterCopy = iter;
}
ChipLogProgress(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"
"Stable 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",
rloc16, routerId, leaderRouterId, leaderAddr->mFields.m8[0], leaderAddr->mFields.m8[1],
leaderAddr->mFields.m8[2], leaderAddr->mFields.m8[3], leaderAddr->mFields.m8[4], leaderAddr->mFields.m8[5],
leaderAddr->mFields.m8[6], leaderAddr->mFields.m8[7], leaderAddr->mFields.m8[8], leaderAddr->mFields.m8[9],
leaderAddr->mFields.m8[10], leaderAddr->mFields.m8[11], leaderAddr->mFields.m8[12], leaderAddr->mFields.m8[13],
leaderAddr->mFields.m8[14], leaderAddr->mFields.m8[15], leaderWeight, leaderLocalWeight, networkDataLen,
networkDataVersion, stableNetworkDataVersion, extAddress->m8[0], extAddress->m8[1], extAddress->m8[2],
extAddress->m8[3], extAddress->m8[4], extAddress->m8[5], extAddress->m8[6], extAddress->m8[7], partitionId,
instantRssi, neighborTableSize, childTableSize);
// Handle each neighbor event seperatly.
for (uint32_t i = 0; i < neighborTableSize; i++)
{
otNeighborInfo * neighbor = &neighborInfo[i];
if (neighbor->mIsChild)
{
otChildInfo * child = NULL;
otErr = otThreadGetChildInfoById(mOTInst, neighbor->mRloc16, child);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
snprintf(printBuf, TELEM_PRINT_BUFFER_SIZE, ", Timeout: %10" PRIu32 " NetworkDataVersion: %3" PRIu8, child->mTimeout,
child->mNetworkDataVersion);
}
else
{
printBuf[0] = 0;
}
ChipLogProgress(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, neighbor->mExtAddress.m8[0], neighbor->mExtAddress.m8[1], neighbor->mExtAddress.m8[2],
neighbor->mExtAddress.m8[3], neighbor->mExtAddress.m8[4], neighbor->mExtAddress.m8[5],
neighbor->mExtAddress.m8[6], neighbor->mExtAddress.m8[7], neighbor->mRloc16, neighbor->mAge,
neighbor->mLinkQualityIn, neighbor->mAverageRssi, neighbor->mLastRssi, neighbor->mLinkFrameCounter,
neighbor->mMleFrameCounter, neighbor->mRxOnWhenIdle ? 'Y' : 'n', neighbor->mSecureDataRequest ? 'Y' : 'n',
#if OPENTHREA_API_VERSION
neighbor->mFullThreadDevice ? 'Y' : 'n',
#else
'n',
#endif
neighbor->mFullNetworkData ? 'Y' : 'n', neighbor->mIsChild ? 'Y' : 'n', printBuf);
}
Impl()->UnlockThreadStack();
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "GetAndLogThreadTopologyFull failed: %s", err);
}
return err;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetPrimary802154MACAddress(uint8_t * buf)
{
const otExtAddress * extendedAddr = otLinkGetExtendedAddress(mOTInst);
memcpy(buf, extendedAddr, sizeof(otExtAddress));
return CHIP_NO_ERROR;
};
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::DoInit(otInstance * otInst)
{
CHIP_ERROR err = CHIP_NO_ERROR;
otError otErr = OT_ERROR_NONE;
// Arrange for OpenThread errors to be translated to text.
RegisterOpenThreadErrorFormatter();
mOTInst = NULL;
mPollingConfig.Clear();
// 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));
}
#ifndef __ZEPHYR__
otCliUartInit(otInst);
#endif
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));
// Enable use of secure data requests.
{
otLinkModeConfig linkMode = otThreadGetLinkMode(otInst);
linkMode.mSecureDataRequests = true;
otErr = otThreadSetLinkMode(otInst, linkMode);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
}
// Enable automatic assignment of Thread advertised addresses.
#if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
otIp6SetSlaacEnabled(otInst, true);
#endif
// If the Thread stack has been provisioned, but is not currently enabled, enable it now.
if (otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED && otDatasetIsCommissioned(otInst))
{
// Enable the Thread IPv6 interface.
otErr = otIp6SetEnabled(otInst, true);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
otErr = otThreadSetEnabled(otInst, true);
VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
ChipLogProgress(DeviceLayer, "OpenThread ifconfig up and thread start");
}
exit:
ChipLogProgress(DeviceLayer, "OpenThread started: %s", otThreadErrorToString(otErr));
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);
}
template <class ImplClass>
bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadInterfaceUpNoLock(void)
{
return otIp6IsEnabled(mOTInst);
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::AdjustPollingInterval(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint32_t newPollingIntervalMS = mPollingConfig.InactivePollingIntervalMS;
if (newPollingIntervalMS != 0)
{
Impl()->LockThreadStack();
uint32_t curPollingIntervalMS = otLinkGetPollPeriod(mOTInst);
if (newPollingIntervalMS != curPollingIntervalMS)
{
otError otErr = otLinkSetPollPeriod(mOTInst, newPollingIntervalMS);
err = MapOpenThreadError(otErr);
}
Impl()->UnlockThreadStack();
if (newPollingIntervalMS != curPollingIntervalMS)
{
ChipLogProgress(DeviceLayer, "OpenThread polling interval set to %" PRId32 "ms", newPollingIntervalMS);
}
}
return err;
}
template <class ImplClass>
void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ErasePersistentInfo(void)
{
ChipLogProgress(DeviceLayer, "Erasing Thread persistent info...");
Impl()->LockThreadStack();
otThreadSetEnabled(mOTInst, false);
otInstanceErasePersistentInfo(mOTInst);
Impl()->UnlockThreadStack();
}
template <class ImplClass>
void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnJoinerComplete(otError aError, void * aContext)
{
static_cast<GenericThreadStackManagerImpl_OpenThread *>(aContext)->OnJoinerComplete(aError);
}
template <class ImplClass>
void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnJoinerComplete(otError aError)
{
ChipLogProgress(DeviceLayer, "Join Thread network: %s", otThreadErrorToString(aError));
if (aError == OT_ERROR_NONE)
{
otError error = otThreadSetEnabled(mOTInst, true);
ChipLogProgress(DeviceLayer, "Start Thread network: %s", otThreadErrorToString(error));
}
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_JoinerStart(void)
{
CHIP_ERROR error = CHIP_NO_ERROR;
Impl()->LockThreadStack();
VerifyOrExit(!otDatasetIsCommissioned(mOTInst) && otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED,
error = MapOpenThreadError(OT_ERROR_INVALID_STATE));
VerifyOrExit(otJoinerGetState(mOTInst) == OT_JOINER_STATE_IDLE, error = MapOpenThreadError(OT_ERROR_BUSY));
if (!otIp6IsEnabled(mOTInst))
{
SuccessOrExit(error = MapOpenThreadError(otIp6SetEnabled(mOTInst, true)));
}
{
otJoinerDiscerner discerner;
uint32_t discriminator;
SuccessOrExit(error = ConfigurationMgr().GetSetupDiscriminator(discriminator));
discerner.mLength = 12;
discerner.mValue = discriminator;
ChipLogProgress(DeviceLayer, "Joiner Discerner: %u", discriminator);
otJoinerSetDiscerner(mOTInst, &discerner);
}
{
otJoinerPskd pskd;
uint32_t pincode;
SuccessOrExit(error = ConfigurationMgr().GetSetupPinCode(pincode));
snprintf(pskd.m8, sizeof(pskd.m8) - 1, "%09" PRIu32, pincode);
ChipLogProgress(DeviceLayer, "Joiner PSKd: %s", pskd.m8);
error = MapOpenThreadError(otJoinerStart(mOTInst, pskd.m8, NULL, NULL, NULL, NULL, NULL,
&GenericThreadStackManagerImpl_OpenThread::OnJoinerComplete, this));
}
exit:
Impl()->UnlockThreadStack();
ChipLogProgress(DeviceLayer, "Joiner start: %s", chip::ErrorStr(error));
return error;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip
#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP