diff --git a/include/openthread/network_time.h b/include/openthread/network_time.h index 477e5385d..4ef3adef5 100644 --- a/include/openthread/network_time.h +++ b/include/openthread/network_time.h @@ -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); + /** * @} * diff --git a/src/core/api/network_time_api.cpp b/src/core/api/network_time_api.cpp index 67c053521..7d1fc9b04 100644 --- a/src/core/api/network_time_api.cpp +++ b/src/core/api/network_time_api.cpp @@ -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(aInstance); + + return instance.GetThreadNetif().GetTimeSync().SetTimeSyncCallback(aCallback, aCallbackContext); +} + #endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index fb8601457..577527f07 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -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) { diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 57ca980cd..80f903de5 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -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_ diff --git a/src/core/thread/time_sync_service.cpp b/src/core/thread/time_sync_service.cpp index 62faf43f6..0ad7cb877 100644 --- a/src/core/thread/time_sync_service.cpp +++ b/src/core/thread/time_sync_service.cpp @@ -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(static_cast(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().HandleStateChanged(aFlags); +} + +void TimeSync::HandleTimeout(Timer &aTimer) +{ + aTimer.GetOwner().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 diff --git a/src/core/thread/time_sync_service.hpp b/src/core/thread/time_sync_service.hpp index 6d8138297..f6b6da912 100644 --- a/src/core/thread/time_sync_service.hpp +++ b/src/core/thread/time_sync_service.hpp @@ -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. }; /** diff --git a/src/ncp/changed_props_set.cpp b/src/ncp/changed_props_set.cpp index 4191af14e..e43902f57 100644 --- a/src/ncp/changed_props_set.cpp +++ b/src/ncp/changed_props_set.cpp @@ -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 diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 03643d90f..60a997270 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -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 diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index c0c1b8246..318a7bc83 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -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); diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 815b6c0ee..68da34163 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -2618,6 +2618,17 @@ template <> otError NcpBase::HandlePropertyGet( exit: return error; } + +void NcpBase::HandleTimeSyncUpdate(void *aContext) +{ + static_cast(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)