mirror of
https://github.com/espressif/openthread.git
synced 2026-09-01 14:59:54 +00:00
[ncp] notify host asynchronously upon a significant thread network time change (#3238)
This commit is contained in:
committed by
Jonathan Hui
parent
a5f1ed263d
commit
4f99bcfaf3
@@ -63,6 +63,8 @@ typedef enum otNetworkTimeStatus
|
||||
OT_NETWORK_TIME_SYNCHRONIZED = 1, ///< The device network time is synchronized.
|
||||
} otNetworkTimeStatus;
|
||||
|
||||
typedef void (*otNetworkTimeSyncCallbackFn)(void *aCallbackContext);
|
||||
|
||||
/**
|
||||
* zero is considered as invalid time synchronization sequence.
|
||||
*
|
||||
@@ -128,6 +130,21 @@ otError otNetworkTimeSetXtalThreshold(otInstance *aInstance, uint16_t aXTALThres
|
||||
*/
|
||||
uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Set a callback to be called when a network time sync or status change occurs
|
||||
*
|
||||
* This callback shall be called only when the network time offset jumps by
|
||||
* OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US or when the status changes.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
* @param[in] aCallbackFn The callback function to be called
|
||||
* @param[in] aCallbackContext The context to be passed to the callback function upon invocation
|
||||
*
|
||||
*/
|
||||
void otNetworkTimeSyncSetCallback(otInstance * aInstance,
|
||||
otNetworkTimeSyncCallbackFn aCallbackFn,
|
||||
void * aCallbackContext);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -89,4 +89,12 @@ uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance)
|
||||
|
||||
return instance.GetThreadNetif().GetTimeSync().GetXtalThreshold();
|
||||
}
|
||||
|
||||
void otNetworkTimeSyncSetCallback(otInstance *aInstance, otNetworkTimeSyncCallbackFn aCallback, void *aCallbackContext)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
return instance.GetThreadNetif().GetTimeSync().SetTimeSyncCallback(aCallback, aCallbackContext);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
|
||||
@@ -587,6 +587,13 @@ template <> inline MeshCoP::PendingDataset &Instance::Get(void)
|
||||
return GetThreadNetif().GetPendingDataset();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
template <> inline TimeSync &Instance::Get(void)
|
||||
{
|
||||
return GetThreadNetif().GetTimeSync();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_ENABLE_APPLICATION_COAP
|
||||
template <> inline Coap::ApplicationCoap &Instance::Get(void)
|
||||
{
|
||||
|
||||
@@ -1886,4 +1886,15 @@
|
||||
#ifndef OPENTHREAD_CONFIG_IPV6_DEFAULT_MAX_DATAGRAM
|
||||
#define OPENTHREAD_CONFIG_IPV6_DEFAULT_MAX_DATAGRAM 1280
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US
|
||||
*
|
||||
* This setting sets the minimum amount of time (in microseconds) that the network time must jump due to
|
||||
* a time sync event for listeners to be notified of the new network time.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US
|
||||
#define OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US 10000
|
||||
#endif
|
||||
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
|
||||
#include "common/instance.hpp"
|
||||
#include "common/logging.hpp"
|
||||
#include "common/owner-locator.hpp"
|
||||
|
||||
#define ABS(value) (((value) >= 0) ? (value) : -(value))
|
||||
|
||||
namespace ot {
|
||||
|
||||
@@ -57,41 +60,28 @@ TimeSync::TimeSync(Instance &aInstance)
|
||||
#endif
|
||||
, mLastTimeSyncReceived(0)
|
||||
, mNetworkTimeOffset(0)
|
||||
, mTimeSyncCallback(NULL)
|
||||
, mTimeSyncCallbackContext(NULL)
|
||||
, mNotifierCallback(&TimeSync::HandleStateChanged, this)
|
||||
, mTimer(aInstance, HandleTimeout, this)
|
||||
, mCurrentStatus(OT_NETWORK_TIME_UNSYNCHRONIZED)
|
||||
{
|
||||
aInstance.GetNotifier().RegisterCallback(mNotifierCallback);
|
||||
|
||||
CheckAndHandleChanges(false);
|
||||
}
|
||||
|
||||
otNetworkTimeStatus TimeSync::GetTime(uint64_t &aNetworkTime) const
|
||||
{
|
||||
otNetworkTimeStatus networkTimeStatus = OT_NETWORK_TIME_SYNCHRONIZED;
|
||||
otDeviceRole role = GetInstance().GetThreadNetif().GetMle().GetRole();
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case OT_DEVICE_ROLE_DISABLED:
|
||||
case OT_DEVICE_ROLE_DETACHED:
|
||||
networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED;
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_CHILD:
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
if ((TimerMilli::GetNow() - mLastTimeSyncReceived) > 2 * TimerMilli::SecToMsec(mTimeSyncPeriod))
|
||||
{
|
||||
// The device hasn’t received time sync for more than two periods time.
|
||||
networkTimeStatus = OT_NETWORK_TIME_RESYNC_NEEDED;
|
||||
}
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
break;
|
||||
}
|
||||
|
||||
aNetworkTime = static_cast<uint64_t>(static_cast<int64_t>(otPlatTimeGet()) + mNetworkTimeOffset);
|
||||
|
||||
return networkTimeStatus;
|
||||
return mCurrentStatus;
|
||||
}
|
||||
|
||||
void TimeSync::HandleTimeSyncMessage(const Message &aMessage)
|
||||
{
|
||||
const int64_t origNetworkTimeOffset = mNetworkTimeOffset;
|
||||
|
||||
VerifyOrExit(aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ);
|
||||
|
||||
if (mTimeSyncSeq != OT_TIME_SYNC_INVALID_SEQ && (int8_t)(aMessage.GetTimeSyncSeq() - mTimeSyncSeq) < 0)
|
||||
@@ -113,6 +103,11 @@ void TimeSync::HandleTimeSyncMessage(const Message &aMessage)
|
||||
mTimeSyncSeq = aMessage.GetTimeSyncSeq();
|
||||
mNetworkTimeOffset = aMessage.GetNetworkTimeOffset();
|
||||
mTimeSyncRequired = true;
|
||||
|
||||
// Only notify listeners of an update for network time offset jumps of more than
|
||||
// OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US but notify listeners regardless if the status changes.
|
||||
CheckAndHandleChanges(ABS(mNetworkTimeOffset - origNetworkTimeOffset) >=
|
||||
OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +123,14 @@ void TimeSync::IncrementTimeSyncSeq(void)
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSync::NotifyTimeSyncCallback(void)
|
||||
{
|
||||
if (mTimeSyncCallback != NULL)
|
||||
{
|
||||
mTimeSyncCallback(mTimeSyncCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
void TimeSync::ProcessTimeSync(void)
|
||||
{
|
||||
@@ -151,6 +154,72 @@ exit:
|
||||
}
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
void TimeSync::HandleStateChanged(otChangedFlags aFlags)
|
||||
{
|
||||
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
|
||||
{
|
||||
CheckAndHandleChanges(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSync::HandleTimeout(void)
|
||||
{
|
||||
CheckAndHandleChanges(false);
|
||||
}
|
||||
|
||||
void TimeSync::HandleStateChanged(Notifier::Callback &aCallback, otChangedFlags aFlags)
|
||||
{
|
||||
aCallback.GetOwner<TimeSync>().HandleStateChanged(aFlags);
|
||||
}
|
||||
|
||||
void TimeSync::HandleTimeout(Timer &aTimer)
|
||||
{
|
||||
aTimer.GetOwner<TimeSync>().HandleTimeout();
|
||||
}
|
||||
|
||||
void TimeSync::CheckAndHandleChanges(bool aTimeUpdated)
|
||||
{
|
||||
otNetworkTimeStatus networkTimeStatus = OT_NETWORK_TIME_SYNCHRONIZED;
|
||||
const otDeviceRole role = GetInstance().GetThreadNetif().GetMle().GetRole();
|
||||
const uint32_t resyncNeededThresholdMs = 2 * TimerMilli::SecToMsec(mTimeSyncPeriod);
|
||||
const uint32_t timeSyncLastSyncMs = TimerMilli::GetNow() - mLastTimeSyncReceived;
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case OT_DEVICE_ROLE_DISABLED:
|
||||
case OT_DEVICE_ROLE_DETACHED:
|
||||
networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED;
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_CHILD:
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
if (timeSyncLastSyncMs > resyncNeededThresholdMs)
|
||||
{
|
||||
// The device hasn’t received time sync for more than two periods time.
|
||||
networkTimeStatus = OT_NETWORK_TIME_RESYNC_NEEDED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Schedule a check 1 millisecond after two periods of time
|
||||
assert(resyncNeededThresholdMs >= timeSyncLastSyncMs);
|
||||
mTimer.Start(resyncNeededThresholdMs - timeSyncLastSyncMs + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
break;
|
||||
}
|
||||
|
||||
if (networkTimeStatus != mCurrentStatus || aTimeUpdated)
|
||||
{
|
||||
mCurrentStatus = networkTimeStatus;
|
||||
|
||||
NotifyTimeSyncCallback();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "common/locator.hpp"
|
||||
#include "common/message.hpp"
|
||||
#include "common/notifier.hpp"
|
||||
#include "common/timer.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -137,13 +138,72 @@ public:
|
||||
*/
|
||||
uint16_t GetXtalThreshold(void) const { return mXtalThreshold; }
|
||||
|
||||
/**
|
||||
* Set the time sync callback to be notified of a network time update.
|
||||
*
|
||||
* @param[in] aCallback The callback to be called when time sync is handled.
|
||||
* @param[im] aCallbackContext The context to be passed to callback.
|
||||
*
|
||||
*/
|
||||
void SetTimeSyncCallback(otNetworkTimeSyncCallbackFn aCallback, void *aCallbackContext)
|
||||
{
|
||||
mTimeSyncCallback = aCallback;
|
||||
mTimeSyncCallbackContext = aCallbackContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be called when thread state changes.
|
||||
*
|
||||
* @param[in] aFlags Flags that denote the state change events.
|
||||
*
|
||||
*/
|
||||
void HandleStateChanged(otChangedFlags aFlags);
|
||||
|
||||
/**
|
||||
* Callback to be called when timer expires.
|
||||
*
|
||||
*/
|
||||
void HandleTimeout(void);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Callback to be called when thread state changes.
|
||||
*
|
||||
* @param[in] aCallback Callback context.
|
||||
* @param[in] aFlags Flags that denote the state change events.
|
||||
*
|
||||
*/
|
||||
static void HandleStateChanged(Notifier::Callback &aCallback, otChangedFlags aFlags);
|
||||
|
||||
/**
|
||||
* Callback to be called when timer expires.
|
||||
*
|
||||
* @param[in] aTimer The corresponding timer.
|
||||
*
|
||||
*/
|
||||
static void HandleTimeout(Timer &aTimer);
|
||||
|
||||
/**
|
||||
* Check and handle any status change, and notify observers if applicable.
|
||||
*
|
||||
* @param[in] aNotifyTimeUpdated True to denote that observers should be notified due to a time change, false
|
||||
* otherwise.
|
||||
*
|
||||
*/
|
||||
void CheckAndHandleChanges(bool aNotifyTimeUpdated);
|
||||
|
||||
/**
|
||||
* Increase the time synchronization sequence.
|
||||
*
|
||||
*/
|
||||
void IncrementTimeSyncSeq(void);
|
||||
|
||||
/**
|
||||
* Notify any listener of a network time sync update event.
|
||||
*
|
||||
*/
|
||||
void NotifyTimeSyncCallback(void);
|
||||
|
||||
bool mTimeSyncRequired; ///< Indicate whether or not a time synchronization message is required.
|
||||
uint8_t mTimeSyncSeq; ///< The time synchronization sequence.
|
||||
uint16_t mTimeSyncPeriod; ///< The time synchronization period.
|
||||
@@ -153,6 +213,12 @@ private:
|
||||
#endif
|
||||
uint32_t mLastTimeSyncReceived; ///< The time when the last time synchronization message was received.
|
||||
int64_t mNetworkTimeOffset; ///< The time offset to the Thread Network time
|
||||
otNetworkTimeSyncCallbackFn
|
||||
mTimeSyncCallback; ///< The callback to be called when time sync is handled or status updated.
|
||||
void * mTimeSyncCallbackContext; ///< The context to be passed to callback.
|
||||
Notifier::Callback mNotifierCallback; ///< Callback for thread state changes.
|
||||
TimerMilli mTimer; ///< Timer for checking if a resync is required.
|
||||
otNetworkTimeStatus mCurrentStatus; ///< Current network time status.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -89,6 +89,9 @@ const ChangedPropsSet::Entry ChangedPropsSet::mSupportedProps[] = {
|
||||
{SPINEL_PROP_LAST_STATUS, SPINEL_STATUS_JOIN_RSP_TIMEOUT, false}, // 32
|
||||
{SPINEL_PROP_LAST_STATUS, SPINEL_STATUS_JOIN_SUCCESS, false}, // 33
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
{SPINEL_PROP_THREAD_NETWORK_TIME, SPINEL_STATUS_OK, false}, // 34
|
||||
#endif
|
||||
};
|
||||
|
||||
uint8_t ChangedPropsSet::GetNumEntries(void) const
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <openthread/link.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/ncp.h>
|
||||
#include <openthread/network_time.h>
|
||||
#include <openthread/platform/misc.h>
|
||||
#include <openthread/platform/radio.h>
|
||||
|
||||
@@ -264,6 +265,9 @@ NcpBase::NcpBase(Instance *aInstance)
|
||||
otSetStateChangedCallback(mInstance, &NcpBase::HandleStateChanged, this);
|
||||
otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
|
||||
otIp6SetReceiveFilterEnabled(mInstance, true);
|
||||
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
otNetworkTimeSyncSetCallback(mInstance, &NcpBase::HandleTimeSyncUpdate, this);
|
||||
#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
#if OPENTHREAD_ENABLE_UDP_FORWARD
|
||||
otUdpForwardSetForwarder(mInstance, &NcpBase::HandleUdpForwardStream, this);
|
||||
#endif
|
||||
|
||||
@@ -263,6 +263,9 @@ protected:
|
||||
static void HandleStateChanged(otChangedFlags aFlags, void *aContext);
|
||||
void ProcessThreadChangedFlags(void);
|
||||
|
||||
static void HandleTimeSyncUpdate(void *aContext);
|
||||
void HandleTimeSyncUpdate(void);
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
static void HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo *aChildInfo);
|
||||
void HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo &aChildInfo);
|
||||
|
||||
@@ -2618,6 +2618,17 @@ template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_TIME>(
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void NcpBase::HandleTimeSyncUpdate(void *aContext)
|
||||
{
|
||||
static_cast<NcpBase *>(aContext)->HandleTimeSyncUpdate();
|
||||
}
|
||||
|
||||
void NcpBase::HandleTimeSyncUpdate(void)
|
||||
{
|
||||
mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_NETWORK_TIME);
|
||||
mUpdateChangedPropsTask.Post();
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
|
||||
|
||||
void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext)
|
||||
|
||||
Reference in New Issue
Block a user