From e69019f962c71c3cad8ee5855f18b00bb483a700 Mon Sep 17 00:00:00 2001 From: Shu Chen Date: Wed, 27 Jun 2018 02:46:32 +0800 Subject: [PATCH] [time-sync] network-wide time synchronization service (#2618) OpenThread network-wide time synchronization service is an experimental feature, not part of the standard Thread protocol. Feature Overview: * All the nodes in the same Thread partition sync to the same Thread network-wide time; * Microsecond level time synchronization precision; * Flexible time accuracy and time sync period configuration; * APIs for application layer use case, support both CLI and NCP version. The feature is wrapped by OPENTHREAD_CONFIG_ENABLE_TIME_SYNC, there's no change to current Thread 1.1 implementation if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC is not enabled. If OPENTHREAD_CONFIG_ENABLE_TIME_SYNC is enabled, the node could: * Attach to a time sync enabled network, otherwise * Attach to a standard Thread 1.1 network, otherwise * Form a new time sync enabled network In addition, if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED is also enable, the node could only: * Attach to a time sync enabled network, otherwise * Form a new time sync enabled network Note: Currently, the feature is only supported on nRF52840 and posix platforms. And the network time is only synced among Routers and REEDs, the SEDs will be supported later as an optional feature. --- examples/platforms/nrf52840/alarm.c | 18 +- .../openthread-core-nrf52840-config.h | 15 +- examples/platforms/nrf52840/platform-config.h | 12 +- examples/platforms/nrf52840/radio.c | 56 +++- examples/platforms/posix/alarm.c | 29 +- examples/platforms/posix/radio.c | 38 +++ include/openthread/Makefile.am | 1 + include/openthread/network_time.h | 138 ++++++++ include/openthread/platform/Makefile.am | 1 + include/openthread/platform/radio.h | 33 +- include/openthread/platform/time.h | 81 +++++ include/openthread/types.h | 4 + src/cli/README.md | 26 ++ src/cli/cli.cpp | 60 ++++ src/cli/cli.hpp | 3 + src/core/Makefile.am | 3 + src/core/api/link_raw_api.cpp | 9 + src/core/api/network_time_api.cpp | 92 ++++++ src/core/common/message.cpp | 3 + src/core/common/message.hpp | 59 ++++ src/core/mac/mac.cpp | 82 ++++- src/core/mac/mac.hpp | 6 + src/core/mac/mac_frame.cpp | 226 ++++++++++--- src/core/mac/mac_frame.hpp | 301 +++++++++++++++++- src/core/openthread-core-default-config.h | 50 +++ src/core/thread/mesh_forwarder.cpp | 71 ++++- src/core/thread/mle.cpp | 75 +++++ src/core/thread/mle.hpp | 42 +++ src/core/thread/mle_router.cpp | 128 +++++++- src/core/thread/mle_router_ftd.hpp | 14 + src/core/thread/mle_router_mtd.hpp | 3 + src/core/thread/mle_tlvs.hpp | 153 ++++++++- src/core/thread/thread_netif.cpp | 4 +- src/core/thread/thread_netif.hpp | 14 + src/core/thread/time_sync_service.cpp | 154 +++++++++ src/core/thread/time_sync_service.hpp | 165 ++++++++++ src/core/thread/topology.hpp | 37 ++- src/ncp/ncp_base.cpp | 16 + src/ncp/ncp_base.hpp | 10 + src/ncp/ncp_base_ftd.cpp | 38 +++ src/ncp/ncp_base_mtd.cpp | 20 ++ src/ncp/spinel.c | 16 + src/ncp/spinel.h | 32 ++ tests/unit/test_mac_frame.cpp | 42 ++- 44 files changed, 2283 insertions(+), 97 deletions(-) create mode 100644 include/openthread/network_time.h create mode 100644 include/openthread/platform/time.h create mode 100644 src/core/api/network_time_api.cpp create mode 100644 src/core/thread/time_sync_service.cpp create mode 100644 src/core/thread/time_sync_service.hpp diff --git a/examples/platforms/nrf52840/alarm.c b/examples/platforms/nrf52840/alarm.c index 0abf9d2d7..8f9c26bf3 100644 --- a/examples/platforms/nrf52840/alarm.c +++ b/examples/platforms/nrf52840/alarm.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "platform.h" @@ -67,6 +68,8 @@ #define US_PER_OVERFLOW (512UL * US_PER_S) ///< Time that has passed between overflow events. On full RTC speed, it occurs every 512 s. #define MS_PER_S 1000UL + +#define XTAL_ACCURACY 40 // The crystal used on nRF52840PDK has ±20ppm accuracy. // clang-format on typedef enum { kMsTimer, kUsTimer, k802154Timer, kNumTimers } AlarmIndex; @@ -88,7 +91,8 @@ typedef struct static volatile uint32_t sOverflowCounter; ///< Counter of RTC overflowCounter, incremented by 2 on each OVERFLOW event. static volatile uint8_t sMutex; ///< Mutex for write access to @ref sOverflowCounter. static volatile uint64_t sTimeOffset = 0; ///< Time overflowCounter to keep track of current time (in millisecond). -static AlarmData sTimerData[kNumTimers]; ///< Data of the timers. +static AlarmData sTimerData[kNumTimers]; ///< Data of the timers. + static const AlarmChannelData sChannelData[kNumTimers] = // { // [kMsTimer] = @@ -563,3 +567,15 @@ void RTC_IRQ_HANDLER(void) } } } + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +uint64_t otPlatTimeGet(void) +{ + return nrf5AlarmGetCurrentTime(); +} + +uint16_t otPlatTimeGetXtalAccuracy(void) +{ + return XTAL_ACCURACY; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC diff --git a/examples/platforms/nrf52840/openthread-core-nrf52840-config.h b/examples/platforms/nrf52840/openthread-core-nrf52840-config.h index dc504842d..023fc3451 100644 --- a/examples/platforms/nrf52840/openthread-core-nrf52840-config.h +++ b/examples/platforms/nrf52840/openthread-core-nrf52840-config.h @@ -158,15 +158,26 @@ */ #define OPENTHREAD_CONFIG_HEAP_SIZE_NO_DTLS 2048 +/** + * @def OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + * + * Define as 1 to enable the time synchronization service feature. + * + * @note If it's enabled, plaforms must support interrupt context and concurrent access AES. + * + */ +#define OPENTHREAD_CONFIG_ENABLE_TIME_SYNC 0 + /** * @def NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT * * Define as 1 to enable AES usage in interrupt context and AES-256, by introducing a software AES under platform layer. * - * @note This feature must be enabled to support AES-256 used by Commissioner and Joiner. + * @note This feature must be enabled to support AES-256 used by Commissioner and Joiner, and AES usage in interrupt context + * used by time synchronization service. * */ -#if OPENTHREAD_ENABLE_COMMISSIONER || OPENTHREAD_ENABLE_JOINER +#if OPENTHREAD_ENABLE_COMMISSIONER || OPENTHREAD_ENABLE_JOINER || OPENTHREAD_CONFIG_ENABLE_TIME_SYNC #define NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT 1 #else #define NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT 0 diff --git a/examples/platforms/nrf52840/platform-config.h b/examples/platforms/nrf52840/platform-config.h index 7d91b637c..ae8e4bab0 100644 --- a/examples/platforms/nrf52840/platform-config.h +++ b/examples/platforms/nrf52840/platform-config.h @@ -495,15 +495,17 @@ #endif /** - * @def NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT + * @def NRF_802154_TX_STARTED_NOTIFY_ENABLED * - * Define as 1 to enable AES usage in interrupt context. + * If notification of started transmission should be enabled in the driver. * - * @note A software AES is enabled to support AES usage in interrupt context. + * @note This feature is enabled by default if OpenThread time synchronization service is enabled. * */ -#ifndef NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT -#define NRF_MBEDTLS_AES_ALT_INTERRUPT_CONTEXT 0 +#ifndef NRF_802154_TX_STARTED_NOTIFY_ENABLED +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +#define NRF_802154_TX_STARTED_NOTIFY_ENABLED 1 +#endif #endif #endif // PLATFORM_CONFIG_H_ diff --git a/examples/platforms/nrf52840/radio.c b/examples/platforms/nrf52840/radio.c index 0781318ec..99b42c654 100644 --- a/examples/platforms/nrf52840/radio.c +++ b/examples/platforms/nrf52840/radio.c @@ -43,9 +43,11 @@ #include #include +#include #include #include #include +#include #include "platform-nrf5.h" #include "platform.h" @@ -78,6 +80,12 @@ static otRadioFrame sReceivedFrames[NRF_802154_RX_BUFFERS]; static otRadioFrame sTransmitFrame; static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE + 1]; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +static otRadioIeInfo sTransmitIeInfo; +static otRadioIeInfo sReceivedIeInfos[NRF_802154_RX_BUFFERS]; +static otInstance * sInstance = NULL; +#endif + static otRadioFrame sAckFrame; static int8_t sDefaultTxPower; @@ -103,6 +111,9 @@ static void dataInit(void) sDisabled = true; sTransmitFrame.mPsdu = sTransmitPsdu + 1; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + sTransmitFrame.mIeInfo = &sTransmitIeInfo; +#endif sReceiveError = OT_ERROR_NONE; @@ -322,7 +333,9 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) { - (void)aInstance; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + sInstance = aInstance; +#endif otError result = OT_ERROR_NONE; @@ -661,7 +674,11 @@ void nrf5RadioProcess(otInstance *aInstance) } } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +void nrf_802154_received_timestamp_raw(uint8_t *p_data, int8_t power, uint8_t lqi, uint32_t time) +#else void nrf_802154_received_raw(uint8_t *p_data, int8_t power, uint8_t lqi) +#endif { otRadioFrame *receivedFrame = NULL; @@ -670,14 +687,17 @@ void nrf_802154_received_raw(uint8_t *p_data, int8_t power, uint8_t lqi) if (sReceivedFrames[i].mPsdu == NULL) { receivedFrame = &sReceivedFrames[i]; + + memset(receivedFrame, 0, sizeof(*receivedFrame)); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + receivedFrame->mIeInfo = &sReceivedIeInfos[i]; +#endif break; } } assert(receivedFrame != NULL); - memset(receivedFrame, 0, sizeof(*receivedFrame)); - receivedFrame->mPsdu = &p_data[1]; receivedFrame->mLength = p_data[0]; receivedFrame->mInfo.mRxInfo.mRssi = power; @@ -688,6 +708,12 @@ void nrf_802154_received_raw(uint8_t *p_data, int8_t power, uint8_t lqi) receivedFrame->mInfo.mRxInfo.mMsec = timestamp / US_PER_MS; receivedFrame->mInfo.mRxInfo.mUsec = timestamp - receivedFrame->mInfo.mRxInfo.mMsec * US_PER_MS; #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // Get the timestamp when the SFD was received. + uint32_t offset = + (int32_t)otPlatAlarmMicroGetNow() - (int32_t)nrf_802154_first_symbol_timestamp_get(time, p_data[0]); + receivedFrame->mIeInfo->mTimestamp = otPlatTimeGet() - offset; +#endif PlatformEventSignalPending(); } @@ -775,3 +801,27 @@ int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) (void)aInstance; return NRF52840_RECEIVE_SENSITIVITY; } + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +void nrf_802154_tx_started(const uint8_t *aFrame) +{ + assert(aFrame == sTransmitPsdu); + + if (sTransmitFrame.mIeInfo->mTimeIeOffset != 0) + { + uint8_t *timeIe = sTransmitFrame.mPsdu + sTransmitFrame.mIeInfo->mTimeIeOffset; + uint64_t time = otPlatTimeGet() + sTransmitFrame.mIeInfo->mNetworkTimeOffset; + + *timeIe = sTransmitFrame.mIeInfo->mTimeSyncSeq; + + *(++timeIe) = (uint8_t)(time & 0xff); + for (uint8_t i = 1; i < sizeof(uint64_t); i++) + { + time = time >> 8; + *(++timeIe) = (uint8_t)(time & 0xff); + } + + otPlatRadioFrameUpdated(sInstance, &sTransmitFrame); + } +} +#endif diff --git a/examples/platforms/posix/alarm.c b/examples/platforms/posix/alarm.c index 88149a02f..663c4301c 100644 --- a/examples/platforms/posix/alarm.c +++ b/examples/platforms/posix/alarm.c @@ -60,15 +60,19 @@ void platformAlarmInit(uint32_t aSpeedUpFactor) gettimeofday(&sStart, NULL); } -uint32_t otPlatAlarmMilliGetNow(void) +uint64_t platformGetNow(void) { struct timeval tv; gettimeofday(&tv, NULL); timersub(&tv, &sStart, &tv); - return (uint32_t)(((uint64_t)tv.tv_sec * sSpeedUpFactor * MS_PER_S) + - ((uint64_t)tv.tv_usec * sSpeedUpFactor / US_PER_MS)); + return (uint64_t)tv.tv_sec * sSpeedUpFactor * US_PER_S + (uint64_t)tv.tv_usec * sSpeedUpFactor; +} + +uint32_t otPlatAlarmMilliGetNow(void) +{ + return (uint32_t)(platformGetNow() / US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) @@ -86,12 +90,7 @@ void otPlatAlarmMilliStop(otInstance *aInstance) uint32_t otPlatAlarmMicroGetNow(void) { - struct timeval tv; - - gettimeofday(&tv, NULL); - timersub(&tv, &sStart, &tv); - - return (uint32_t)(tv.tv_sec * US_PER_S + tv.tv_usec) * sSpeedUpFactor; + return (uint32_t)platformGetNow(); } void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) @@ -196,4 +195,16 @@ void platformAlarmProcess(otInstance *aInstance) #endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +uint64_t otPlatTimeGet(void) +{ + return platformGetNow(); +} + +uint16_t otPlatTimeGetXtalAccuracy(void) +{ + return 0; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + #endif // OPENTHREAD_POSIX_VIRTUAL_TIME == 0 diff --git a/examples/platforms/posix/radio.c b/examples/platforms/posix/radio.c index b50a54058..e3ac948e2 100644 --- a/examples/platforms/posix/radio.c +++ b/examples/platforms/posix/radio.c @@ -30,10 +30,12 @@ #if OPENTHREAD_POSIX_VIRTUAL_TIME == 0 +#include #include #include #include #include +#include #include "utils/code_utils.h" @@ -109,6 +111,11 @@ static otRadioFrame sReceiveFrame; static otRadioFrame sTransmitFrame; static otRadioFrame sAckFrame; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +static otRadioIeInfo sTransmitIeInfo; +static otRadioIeInfo sReceivedIeInfo; +#endif + static uint8_t sExtendedAddress[OT_EXT_ADDRESS_SIZE]; static uint16_t sShortAddress; static uint16_t sPanid; @@ -423,6 +430,14 @@ void platformRadioInit(void) sReceiveFrame.mPsdu = sReceiveMessage.mPsdu; sTransmitFrame.mPsdu = sTransmitMessage.mPsdu; sAckFrame.mPsdu = sAckMessage.mPsdu; + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + sTransmitFrame.mIeInfo = &sTransmitIeInfo; + sReceiveFrame.mIeInfo = &sReceivedIeInfo; +#else + sTransmitFrame.mIeInfo = NULL; + sReceiveFrame.mIeInfo = NULL; +#endif } void platformRadioDeinit(void) @@ -560,6 +575,10 @@ void radioReceive(otInstance *aInstance) sReceiveFrame.mInfo.mRxInfo.mUsec = 0; // Don't support microsecond timer for now. #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + sReceiveFrame.mIeInfo->mTimestamp = otPlatTimeGet(); +#endif + sReceiveFrame.mLength = (uint8_t)(rval - 1); if (sAckWait && sTransmitFrame.mChannel == sReceiveMessage.mChannel && isFrameTypeAck(sReceiveFrame.mPsdu) && @@ -579,6 +598,25 @@ void radioReceive(otInstance *aInstance) void radioSendMessage(otInstance *aInstance) { +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (sTransmitFrame.mIeInfo->mTimeIeOffset != 0) + { + uint8_t *timeIe = sTransmitFrame.mPsdu + sTransmitFrame.mIeInfo->mTimeIeOffset; + uint64_t time = otPlatTimeGet() + sTransmitFrame.mIeInfo->mNetworkTimeOffset; + + *timeIe = sTransmitFrame.mIeInfo->mTimeSyncSeq; + + *(++timeIe) = (uint8_t)(time & 0xff); + for (uint8_t i = 0; i < sizeof(uint64_t); i++) + { + time = time >> 8; + *(++timeIe) = (uint8_t)(time & 0xff); + } + + otPlatRadioFrameUpdated(aInstance, &sTransmitFrame); + } +#endif + sTransmitMessage.mChannel = sTransmitFrame.mChannel; otPlatRadioTxStarted(aInstance, &sTransmitFrame); diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am index cb461fe9f..c07de5a10 100644 --- a/include/openthread/Makefile.am +++ b/include/openthread/Makefile.am @@ -72,6 +72,7 @@ openthread_headers = \ message.h \ ncp.h \ netdata.h \ + network_time.h \ openthread.h \ server.h \ tasklet.h \ diff --git a/include/openthread/network_time.h b/include/openthread/network_time.h new file mode 100644 index 000000000..4401c049b --- /dev/null +++ b/include/openthread/network_time.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018, 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 defines the OpenThread Network Time Synchronization Service API. + */ + +#ifndef OPENTHREAD_NETWORK_TIME_H_ +#define OPENTHREAD_NETWORK_TIME_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-network-time + * + * @brief + * This module includes functions that control network time synchronization service. + * + * @{ + * + */ + +/** + * This enum represents OpenThread time synchronization status. + * + */ +typedef enum otNetworkTimeStatus { + OT_NETWORK_TIME_UNSYNCHRONIZED = -1, ///< The device hasn't attached to a network. + OT_NETWORK_TIME_RESYNC_NEEDED = 0, ///< The device hasn’t received time sync for more than two periods time. + OT_NETWORK_TIME_SYNCHRONIZED = 1, ///< The device network time is synchronized. +} otNetworkTimeStatus; + +/** + * zero is considered as invalid time synchronization sequence. + * + */ +#define OT_TIME_SYNC_INVALID_SEQ 0 + +/** + * Get the Thread network time. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[inout] aNetworkTime The Thread network time in microseconds. + * + * @returns The time synchronization status. + * + */ +otNetworkTimeStatus otNetworkTimeGet(otInstance *aInstance, uint64_t &aNetworkTime); + +/** + * Set the time synchronization period. + * + * This function can only be called while Thread protocols are disabled. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTimeSyncPeriod The time synchronization period, in seconds. + * + * @retval OT_ERROR_NONE Successfully set the time sync period. + * @retval OT_ERROR_INVALID_STATE Thread protocols are enabled. + * + */ +otError otNetworkTimeSetSyncPeriod(otInstance *aInstance, uint16_t aTimeSyncPeriod); + +/** + * Get the time synchronization period. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @returns The time synchronization period. + * + */ +uint16_t otNetworkTimeGetSyncPeriod(otInstance *aInstance); + +/** + * Set the time synchronization XTAL accuracy threshold for Router-Capable device. + * + * This function can only be called while Thread protocols are disabled. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aXTALThreshold The XTAL accuracy threshold for Router, in PPM. + * + * @retval OT_ERROR_NONE Successfully set the time sync period. + * @retval OT_ERROR_INVALID_STATE Thread protocols are enabled. + * + */ +otError otNetworkTimeSetXtalThreshold(otInstance *aInstance, uint16_t aXTALThreshold); + +/** + * Get the time synchronization XTAL accuracy threshold for Router. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @returns The XTAL accuracy threshold for Router, in PPM. + * + */ +uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_NETWORK_TIME_H_ diff --git a/include/openthread/platform/Makefile.am b/include/openthread/platform/Makefile.am index 7db0fd6a5..179ad3b0c 100644 --- a/include/openthread/platform/Makefile.am +++ b/include/openthread/platform/Makefile.am @@ -38,6 +38,7 @@ ot_platform_headers = \ logging.h \ radio.h \ random.h \ + time.h \ uart.h \ spi-slave.h \ settings.h \ diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 884f82253..f5d754310 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -92,15 +92,27 @@ typedef enum otRadioCaps { OT_RADIO_CAPS_CSMA_BACKOFF = 8, ///< Radio supports CSMA backoff for frame transmission (but no retry). } otRadioCaps; +/** + * This structure represents the IEEE 802.15.4 Header IE (Information Element) related information of a radio frame. + */ +typedef struct otRadioIeInfo +{ + uint8_t mTimeIeOffset; ///< The Time IE offset from the start of PSDU. + uint8_t mTimeSyncSeq; ///< The Time sync sequence. + uint64_t mTimestamp; ///< The time in microseconds when the SFD was received. + int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time. +} otRadioIeInfo; + /** * This structure represents an IEEE 802.15.4 radio frame. */ typedef struct otRadioFrame { - uint8_t *mPsdu; ///< The PSDU. - uint8_t mLength; ///< Length of the PSDU. - uint8_t mChannel; ///< Channel used to transmit/receive the frame. - bool mDidTx : 1; ///< Set to true if this frame sent from the radio. Ignored by radio driver. + uint8_t * mPsdu; ///< The PSDU. + uint8_t mLength; ///< Length of the PSDU. + uint8_t mChannel; ///< Channel used to transmit/receive the frame. + bool mDidTx : 1; ///< Set to true if this frame sent from the radio. Ignored by radio driver. + otRadioIeInfo *mIeInfo; ///< The pointer to the Header IE(s) related information. /** * The union of transmit and receive information for a radio frame. @@ -564,6 +576,19 @@ extern void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanM */ int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance); +/** + * The radio driver calls this method to notify OpenThread to process transmit security for the frame, + * this happens when the frame includes Header IE(s) that were updated before transmission. + * + * @note This function will be called from interrupt context, it should only read/write data passed in + * via @p aFrame, but should not read/write any state within OpenThread. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aFrame The radio frame which neeeds to process transmit security. + * + */ +extern void otPlatRadioFrameUpdated(otInstance *aInstance, otRadioFrame *aFrame); + /** * @} * diff --git a/include/openthread/platform/time.h b/include/openthread/platform/time.h new file mode 100644 index 000000000..84f2023a6 --- /dev/null +++ b/include/openthread/platform/time.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, 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 includes the platform abstraction for the time service. + */ + +#ifndef TIME_H_ +#define TIME_H_ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-time + * + * @brief + * This module includes the platform abstraction for the time service. + * + * @{ + * + */ + +/** + * Get the current time (64bits width). + * + * @returns The current time in microseconds. + * + */ +uint64_t otPlatTimeGet(void); + +/** + * Get the device's XTAL accuracy. + * + * @returns The device's XTAL accuracy, in ppm. + * + */ +uint16_t otPlatTimeGetXtalAccuracy(void); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TIME_H_ diff --git a/include/openthread/types.h b/include/openthread/types.h index 0f09d5b5f..f1cc4dfe4 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -1160,6 +1160,10 @@ typedef struct otThreadLinkInfo int8_t mRss; ///< Received Signal Strength in dBm. uint8_t mLqi; ///< Link Quality Indicator for a received message. bool mLinkSecurity; ///< Indicates whether or not link security is enabled. + + // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_ENABLE_TIME_SYNC`) is enabled. + uint8_t mTimeSyncSeq; ///< The time sync sequence. + int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds. } otThreadLinkInfo; #ifdef OTDLL diff --git a/src/cli/README.md b/src/cli/README.md index 14dab0391..4d86d18d7 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -47,6 +47,7 @@ OpenThread test scripts use the CLI to execute test cases. * [networkdiagnostic](#networkdiagnostic-get-addr-type-) * [networkidtimeout](#networkidtimeout) * [networkname](#networkname) +* [networktime](#networktime) * [panid](#panid) * [parent](#parent) * [parentpriority](#parentpriority) @@ -605,6 +606,31 @@ Set network name. Done ``` +### networktime + +Get the Thread network time and the time sync parameters. + +```bash +> networktime +Network Time: 21084154us (synchronized) +Time Sync Period: 100s +XTAL Threshold: 300ppm +Done +``` + +### networktime \ \ + +Set time sync parameters + +* timesyncperiod: The time synchronization period, in seconds. +* xtalthreshold: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM. + +```bash +> networktime 100 300 +Done +``` + + ### dataset panid \ Set panid. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index acd499478..99c960bb8 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -45,6 +45,9 @@ #include #include #include +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +#include +#endif #include #if OPENTHREAD_FTD @@ -177,6 +180,9 @@ const struct Command Interpreter::sCommands[] = { {"networkidtimeout", &Interpreter::ProcessNetworkIdTimeout}, #endif {"networkname", &Interpreter::ProcessNetworkName}, +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + {"networktime", &Interpreter::ProcessNetworkTime}, +#endif {"panid", &Interpreter::ProcessPanId}, {"parent", &Interpreter::ProcessParent}, #if OPENTHREAD_FTD @@ -1579,6 +1585,60 @@ exit: AppendResult(error); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +void Interpreter::ProcessNetworkTime(int argc, char *argv[]) +{ + otError error = OT_ERROR_NONE; + long value; + + if (argc == 0) + { + uint64_t time; + otNetworkTimeStatus networkTimeStatus; + + networkTimeStatus = otNetworkTimeGet(mInstance, time); + + mServer->OutputFormat("Network Time: %dus", time); + + switch (networkTimeStatus) + { + case OT_NETWORK_TIME_UNSYNCHRONIZED: + mServer->OutputFormat(" (unsynchronized)\r\n"); + break; + + case OT_NETWORK_TIME_RESYNC_NEEDED: + mServer->OutputFormat(" (resync needed)\r\n"); + break; + + case OT_NETWORK_TIME_SYNCHRONIZED: + mServer->OutputFormat(" (synchronized)\r\n"); + break; + + default: + break; + } + + mServer->OutputFormat("Time Sync Period: %ds\r\n", otNetworkTimeGetSyncPeriod(mInstance)); + mServer->OutputFormat("XTAL Threshold: %dppm\r\n", otNetworkTimeGetXtalThreshold(mInstance)); + } + else if (argc == 2) + { + SuccessOrExit(error = ParseLong(argv[0], value)); + SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, static_cast(value))); + + SuccessOrExit(error = ParseLong(argv[1], value)); + SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, static_cast(value))); + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + +exit: + AppendResult(error); +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + void Interpreter::ProcessPanId(int argc, char *argv[]) { otError error = OT_ERROR_NONE; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 12275970d..49f66318a 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -266,6 +266,9 @@ private: void ProcessNetworkIdTimeout(int argc, char *argv[]); #endif void ProcessNetworkName(int argc, char *argv[]); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + void ProcessNetworkTime(int argc, char *argv[]); +#endif void ProcessPanId(int argc, char *argv[]); void ProcessParent(int argc, char *argv[]); #if OPENTHREAD_FTD diff --git a/src/core/Makefile.am b/src/core/Makefile.am index a1a50828d..38cfebd82 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -114,6 +114,7 @@ SOURCES_COMMON = \ api/link_raw_api.cpp \ api/message_api.cpp \ api/netdata_api.cpp \ + api/network_time_api.cpp \ api/server_api.cpp \ api/tasklet_api.cpp \ api/thread_api.cpp \ @@ -195,6 +196,7 @@ SOURCES_COMMON = \ thread/src_match_controller.cpp \ thread/thread_netif.cpp \ thread/tmf_proxy.cpp \ + thread/time_sync_service.cpp \ thread/topology.cpp \ utils/channel_manager.cpp \ utils/channel_monitor.cpp \ @@ -326,6 +328,7 @@ HEADERS_COMMON = \ thread/thread_tlvs.hpp \ thread/thread_uri_paths.hpp \ thread/tmf_proxy.hpp \ + thread/time_sync_service.hpp \ thread/topology.hpp \ utils/channel_manager.hpp \ utils/channel_monitor.hpp \ diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index 6bc580306..9fc024a89 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -729,6 +729,15 @@ uint16_t otLinkGetShortAddress(otInstance *aInstance) return static_cast(aInstance)->GetLinkRaw().GetShortAddress(); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +void otPlatRadioFrameUpdated(otInstance *aInstance, otRadioFrame *aFrame) +{ + // Note: For now this functionality is not supported in Radio Only mode. + (void)aInstance; + (void)aFrame; +} +#endif + #endif // OPENTHREAD_RADIO #endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API diff --git a/src/core/api/network_time_api.cpp b/src/core/api/network_time_api.cpp new file mode 100644 index 000000000..67c053521 --- /dev/null +++ b/src/core/api/network_time_api.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018, 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 + * This file implements the OpenThread Network Time Synchronization Service API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + +#include "openthread/network_time.h" + +#include "common/instance.hpp" + +using namespace ot; + +otNetworkTimeStatus otNetworkTimeGet(otInstance *aInstance, uint64_t &aNetworkTime) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetThreadNetif().GetTimeSync().GetTime(aNetworkTime); +} + +otError otNetworkTimeSetSyncPeriod(otInstance *aInstance, uint16_t aTimeSyncPeriod) +{ + otError error = OT_ERROR_NONE; + Instance &instance = *static_cast(aInstance); + + VerifyOrExit(instance.GetThreadNetif().GetMle().GetRole() == OT_DEVICE_ROLE_DISABLED, + error = OT_ERROR_INVALID_STATE); + + instance.GetThreadNetif().GetTimeSync().SetTimeSyncPeriod(aTimeSyncPeriod); + +exit: + return error; +} + +uint16_t otNetworkTimeGetSyncPeriod(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetThreadNetif().GetTimeSync().GetTimeSyncPeriod(); +} + +otError otNetworkTimeSetXtalThreshold(otInstance *aInstance, uint16_t aXtalThreshold) +{ + otError error = OT_ERROR_NONE; + Instance &instance = *static_cast(aInstance); + + VerifyOrExit(instance.GetThreadNetif().GetMle().GetRole() == OT_DEVICE_ROLE_DISABLED, + error = OT_ERROR_INVALID_STATE); + + instance.GetThreadNetif().GetTimeSync().SetXtalThreshold(aXtalThreshold); + +exit: + return error; +} + +uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetThreadNetif().GetTimeSync().GetXtalThreshold(); +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 574e7eb74..fb35a6f67 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -668,6 +668,9 @@ Message *Message::Clone(uint16_t aLength) const messageCopy->SetSubType(GetSubType()); messageCopy->SetPriority(GetPriority()); messageCopy->SetLinkSecurityEnabled(IsLinkSecurityEnabled()); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + messageCopy->SetTimeSync(IsTimeSync()); +#endif exit: diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index d7b4895c8..bf70bf590 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -114,6 +114,11 @@ struct MessageInfo uint8_t mPriority : 2; ///< Identifies the message priority level (lower value is higher priority). bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue. bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful. +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + bool mTimeSync : 1; ///< Indicates whether the message is also used for time sync purpose. + uint8_t mTimeSyncSeq; ///< The time sync sequence. + int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds. +#endif }; /** @@ -710,6 +715,60 @@ public: return (!mBuffer.mHead.mInfo.mInPriorityQ) ? mBuffer.mHead.mInfo.mQueue.mMessage : NULL; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method indicates whether or not the message is also used for time sync purpose. + * + * @retval TRUE If the message is also used for time sync purpose. + * @retval FALSE If the message is not used for time sync purpose. + * + */ + bool IsTimeSync(void) const { return mBuffer.mHead.mInfo.mTimeSync; } + + /** + * This method sets whether or not the message is also used for time sync purpose. + * + * @param[in] aEnabled TRUE if the message is also used for time sync purpose, FALSE otherwise. + * + */ + void SetTimeSync(bool aEnabled) { mBuffer.mHead.mInfo.mTimeSync = aEnabled; } + + /** + * This method sets the offset to network time. + * + * @param[in] aNetworkTimeOffset The offset to network time. + * + */ + void SetNetworkTimeOffset(int64_t aNetworkTimeOffset) + { + mBuffer.mHead.mInfo.mNetworkTimeOffset = aNetworkTimeOffset; + } + + /** + * This method gets the offset to network time. + * + * @returns The offset to network time. + * + */ + int64_t GetNetworkTimeOffset(void) const { return mBuffer.mHead.mInfo.mNetworkTimeOffset; } + + /** + * This method sets the time sync sequence. + * + * @param[in] aTimeSyncSeq The time sync sequence. + * + */ + void SetTimeSyncSeq(uint8_t aTimeSyncSeq) { mBuffer.mHead.mInfo.mTimeSyncSeq = aTimeSyncSeq; } + + /** + * This method gets the time sync sequence. + * + * @returns The time sync sequence. + * + */ + uint8_t GetTimeSyncSeq(void) const { return mBuffer.mHead.mInfo.mTimeSyncSeq; } +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + private: /** * This method returns a pointer to the message pool to which this message belongs diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 61fa0d5de..4e97a64d8 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -1104,7 +1104,11 @@ void Mac::BeginTransmit(void) { otError error = OT_ERROR_NONE; bool applyTransmitSecurity = true; + bool processTransmitAesCcm = true; Frame & sendFrame(*GetOperationFrame()); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + uint8_t timeIeOffset = 0; +#endif VerifyOrExit(mEnabled, error = OT_ERROR_ABORT); @@ -1161,10 +1165,24 @@ void Mac::BeginTransmit(void) break; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + timeIeOffset = GetTimeIeOffset(sendFrame); + sendFrame.SetTimeIeOffset(timeIeOffset); + + if (timeIeOffset != 0) + { + // Transmit security will be processed after time IE content is updated. + processTransmitAesCcm = false; + sendFrame.SetTimeSyncSeq(GetNetif().GetTimeSync().GetTimeSyncSeq()); + sendFrame.SetNetworkTimeOffset(GetNetif().GetTimeSync().GetNetworkTimeOffset()); + } + +#endif + if (applyTransmitSecurity) { // Security Processing - ProcessTransmitSecurity(sendFrame, true); + ProcessTransmitSecurity(sendFrame, processTransmitAesCcm); } } @@ -1923,6 +1941,15 @@ void Mac::HandleReceivedFrame(Frame *aFrame, otError aError) netif.GetMeshForwarder().GetDataPollManager().CheckFramePending(*aFrame); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + + if (aFrame->GetVersion() == Frame::kFcfFrameVersion2015) + { + ProcessTimeIe(*aFrame); + } + +#endif + if (neighbor != NULL) { #if OPENTHREAD_ENABLE_MAC_FILTER @@ -2287,5 +2314,58 @@ void Mac::LogFrameTxFailure(const Frame &, otError) const #endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +void Mac::ProcessTimeIe(Frame &aFrame) +{ + TimeIe *timeIe = reinterpret_cast(aFrame.GetTimeIe()); + + VerifyOrExit(timeIe != NULL); + + aFrame.SetNetworkTimeOffset(static_cast(timeIe->GetTime()) - static_cast(aFrame.GetTimestamp())); + aFrame.SetTimeSyncSeq(timeIe->GetSequence()); + +exit: + return; +} + +uint8_t Mac::GetTimeIeOffset(Frame &aFrame) +{ + uint8_t offset = 0; + uint8_t *base = aFrame.GetPsdu(); + uint8_t *cur = NULL; + + cur = aFrame.GetTimeIe(); + VerifyOrExit(cur != NULL); + + cur += sizeof(VendorIeHeader); + offset = cur - base; + +exit: + return offset; +} + +/** + * This function will be called from interrupt context, it should only read/write data passed in + * via @p aFrame, but should not read/write any state within OpenThread. + */ +extern "C" void otPlatRadioFrameUpdated(otInstance *aInstance, otRadioFrame *aFrame) +{ + Instance * instance = static_cast(aInstance); + Frame frame = *static_cast(aFrame); + const ExtAddress *extAddress = NULL; + + VerifyOrExit(instance->IsInitialized()); + + if (frame.GetSecurityEnabled() == true) + { + extAddress = &instance->GetThreadNetif().GetMac().GetExtAddress(); + instance->GetThreadNetif().GetMac().ProcessTransmitAesCcm(frame, extAddress); + } + +exit: + return; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 05e5701c3..54a3a132d 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include +#include #include "common/locator.hpp" #include "common/tasklet.hpp" @@ -986,6 +987,11 @@ private: void LogFrameTxFailure(const Frame &aFrame, otError aError) const; void LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + void ProcessTimeIe(Frame &aFrame); + uint8_t GetTimeIeOffset(Frame &aFrame); +#endif + static const char *OperationToString(Operation aOperation); Operation mOperation; diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 622dd0559..f945e529b 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -111,27 +111,23 @@ otError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) assert(false); } - // Source PAN + Address + // Source PAN + if (IsSrcPanIdPresent(aFcf)) + { + length += sizeof(PanId); + } + + // Source Address switch (aFcf & kFcfSrcAddrMask) { case kFcfSrcAddrNone: break; case kFcfSrcAddrShort: - if ((aFcf & kFcfPanidCompression) == 0) - { - length += sizeof(PanId); - } - length += sizeof(ShortAddress); break; case kFcfSrcAddrExt: - if ((aFcf & kFcfPanidCompression) == 0) - { - length += sizeof(PanId); - } - length += sizeof(ExtAddress); break; @@ -363,6 +359,30 @@ exit: return index; } +bool Frame::IsSrcPanIdPresent(uint16_t aFcf) const +{ + bool srcPanIdPresent = false; + + if ((aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone && (aFcf & kFcfPanidCompression) == 0) + { +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // Handle a special case in IEEE 802.15.4-2015, when Pan ID Compression is 0, but Src Pan ID is not present: + // Dest Address: Extended + // Source Address: Extended + // Dest Pan ID: Present + // Src Pan ID: Not Present + // Pan ID Compression: 0 + if ((aFcf & kFcfFrameVersionMask) != kFcfFrameVersion2015 || (aFcf & kFcfDstAddrMask) != kFcfDstAddrExt || + (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrExt) +#endif + { + srcPanIdPresent = true; + } + } + + return srcPanIdPresent; +} + otError Frame::GetSrcPanId(PanId &aPanId) const { otError error = OT_ERROR_NONE; @@ -408,7 +428,7 @@ uint8_t Frame::FindSrcAddrIndex(void) const } // Source PAN - if ((fcf & kFcfPanidCompression) == 0) + if (IsSrcPanIdPresent(fcf)) { index += sizeof(PanId); } @@ -515,24 +535,20 @@ uint8_t Frame::FindSecurityHeaderIndex(void) const break; } - // Source PAN + Address + // Source PAN + if (IsSrcPanIdPresent(fcf)) + { + index += sizeof(PanId); + } + + // Source Address switch (fcf & kFcfSrcAddrMask) { case kFcfSrcAddrShort: - if ((fcf & kFcfPanidCompression) == 0) - { - index += sizeof(PanId); - } - index += sizeof(ShortAddress); break; case kFcfSrcAddrExt: - if ((fcf & kFcfPanidCompression) == 0) - { - index += sizeof(PanId); - } - index += sizeof(ExtAddress); break; } @@ -784,7 +800,7 @@ otError Frame::SetPayloadLength(uint8_t aLength) return OT_ERROR_NONE; } -uint8_t Frame::FindPayloadIndex(void) const +uint8_t Frame::SkipSecurityHeaderIndex(void) const { uint8_t index = 0; uint16_t fcf; @@ -816,27 +832,23 @@ uint8_t Frame::FindPayloadIndex(void) const ExitNow(index = kInvalidIndex); } - // Source PAN + Address + // Source PAN + if (IsSrcPanIdPresent(fcf)) + { + index += sizeof(PanId); + } + + // Source Address switch (fcf & kFcfSrcAddrMask) { case kFcfSrcAddrNone: break; case kFcfSrcAddrShort: - if ((fcf & kFcfPanidCompression) == 0) - { - index += sizeof(PanId); - } - index += sizeof(ShortAddress); break; case kFcfSrcAddrExt: - if ((fcf & kFcfPanidCompression) == 0) - { - index += sizeof(PanId); - } - index += sizeof(ExtAddress); break; @@ -873,6 +885,49 @@ uint8_t Frame::FindPayloadIndex(void) const } } +exit: + return index; +} + +uint8_t Frame::FindPayloadIndex(void) const +{ + uint16_t fcf = GetFrameControlField(); + uint8_t index = SkipSecurityHeaderIndex(); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + uint8_t *cur = NULL; + uint8_t *footer = GetFooter(); +#endif + + VerifyOrExit(index != kInvalidIndex); + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + cur = GetPsdu() + index; + + if (IsIePresent()) + { + while (cur + sizeof(HeaderIe) <= footer) + { + HeaderIe *ie = reinterpret_cast(cur); + uint8_t len = static_cast(ie->GetLength()); + + cur += sizeof(HeaderIe); + index += sizeof(HeaderIe); + + VerifyOrExit(cur + len <= footer, index = kInvalidIndex); + + cur += len; + index += len; + + if (ie->GetId() == kHeaderIeTermination2) + { + break; + } + } + + // Assume no Payload IE in current implementation + } +#endif + // Command ID if ((fcf & kFcfFrameTypeMask) == kFcfFrameMacCmd) { @@ -894,10 +949,10 @@ exit: return payload; } -const uint8_t *Frame::GetPayload(void) const +uint8_t *Frame::GetPayload(void) const { - uint8_t index = FindPayloadIndex(); - const uint8_t *payload = GetPsdu() + index; + uint8_t index = FindPayloadIndex(); + uint8_t *payload = GetPsdu() + index; VerifyOrExit(index != kInvalidIndex, payload = NULL); @@ -910,11 +965,104 @@ uint8_t *Frame::GetFooter(void) return GetPsdu() + GetPsduLength() - GetFooterLength(); } -const uint8_t *Frame::GetFooter(void) const +uint8_t *Frame::GetFooter(void) const { return GetPsdu() + GetPsduLength() - GetFooterLength(); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +uint8_t Frame::FindHeaderIeIndex(void) const +{ + uint8_t index; + + VerifyOrExit(IsIePresent(), index = kInvalidIndex); + + index = SkipSecurityHeaderIndex(); + +exit: + return index; +} + +otError Frame::AppendHeaderIe(HeaderIe *aIeList, uint8_t aIeCount) +{ + otError error = OT_ERROR_NONE; + uint8_t index = FindHeaderIeIndex(); + uint8_t *cur; + uint8_t *base; + + VerifyOrExit(index != kInvalidIndex, error = OT_ERROR_FAILED); + cur = GetPsdu() + index; + base = cur; + + for (uint8_t i = 0; i < aIeCount; i++) + { + memcpy(cur, &aIeList[i], sizeof(HeaderIe)); + cur += sizeof(HeaderIe); + cur += aIeList[i].GetLength(); + } + + SetPsduLength(GetPsduLength() + cur - base); + +exit: + return error; +} + +uint8_t *Frame::GetHeaderIe(uint8_t aIeId) const +{ + uint8_t index = FindHeaderIeIndex(); + uint8_t *cur = NULL; + uint8_t *payload = GetPayload(); + + VerifyOrExit(index != kInvalidIndex); + + cur = GetPsdu() + index; + + while (cur + sizeof(HeaderIe) <= payload) + { + HeaderIe *ie = reinterpret_cast(cur); + uint8_t len = static_cast(ie->GetLength()); + + if (ie->GetId() == aIeId) + { + break; + } + + cur += sizeof(HeaderIe); + + VerifyOrExit(cur + len <= payload, cur = NULL); + + cur += len; + } + + if (cur == payload) + { + cur = NULL; + } + +exit: + return cur; +} + +uint8_t *Frame::GetTimeIe(void) const +{ + TimeIe * timeIe = NULL; + uint8_t *cur = NULL; + uint8_t oui[kVendorOuiSize] = {kVendorOuiNest & 0xff, (kVendorOuiNest >> 8) & 0xff, (kVendorOuiNest >> 16) & 0xff}; + + cur = GetHeaderIe(kHeaderIeVendor); + VerifyOrExit(cur != NULL); + + cur += sizeof(HeaderIe); + + timeIe = reinterpret_cast(cur); + VerifyOrExit(memcmp(oui, timeIe->GetVendorOui(), kVendorOuiSize) == 0, cur = NULL); + VerifyOrExit(timeIe->GetSubType() == kVendorIeTime, cur = NULL); + +exit: + return cur; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + Frame::InfoString Frame::ToInfoString(void) const { InfoString string; diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index d6e30fc2a..44a45a1e2 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -377,6 +377,76 @@ private: Type mType; ///< The address type (Short, Extended, or none). }; +/** + * This class implements IEEE 802.15.4 IE (Information Element) generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class HeaderIe +{ +public: + enum + { + kIdOffset = 7, + kIdMask = 0xff << kIdOffset, + kLengthOffset = 0, + kLengthMask = 0x7f << kLengthOffset, + }; + + /** + * This method initializes the Header IE. + * + */ + void Init(void) { mHeaderIe = 0; } + + /** + * This method returns the IE Element Id. + * + * @returns the IE Element Id. + * + */ + uint16_t GetId(void) const { return (ot::Encoding::LittleEndian::HostSwap16(mHeaderIe) & kIdMask) >> kIdOffset; } + + /** + * This method sets the IE Element Id. + * + * @param[in] aID The IE Element Id. + * + */ + void SetId(uint16_t aId) + { + mHeaderIe = ot::Encoding::LittleEndian::HostSwap16( + (ot::Encoding::LittleEndian::HostSwap16(mHeaderIe) & ~kIdMask) | ((aId << kIdOffset) & kIdMask)); + } + + /** + * This method returns the IE content length. + * + * @returns the IE content length. + * + */ + uint16_t GetLength(void) const + { + return (ot::Encoding::LittleEndian::HostSwap16(mHeaderIe) & kLengthMask) >> kLengthOffset; + } + + /** + * This method sets the IE content length. + * + * @param[in] aLength The IE content length. + * + */ + void SetLength(uint16_t aLength) + { + mHeaderIe = + ot::Encoding::LittleEndian::HostSwap16((ot::Encoding::LittleEndian::HostSwap16(mHeaderIe) & ~kLengthMask) | + ((aLength << kLengthOffset) & kLengthMask)); + } + +private: + uint16_t mHeaderIe; +} OT_TOOL_PACKED_END; + /** * This class implements IEEE 802.15.4 MAC frame generation and parsing. * @@ -403,12 +473,14 @@ public: kFcfFramePending = 1 << 4, kFcfAckRequest = 1 << 5, kFcfPanidCompression = 1 << 6, + kFcfIePresent = 1 << 9, kFcfDstAddrNone = 0 << 10, kFcfDstAddrShort = 2 << 10, kFcfDstAddrExt = 3 << 10, kFcfDstAddrMask = 3 << 10, kFcfFrameVersion2006 = 1 << 12, - kFcfFrameVersionMask = 3 << 13, + kFcfFrameVersion2015 = 2 << 12, + kFcfFrameVersionMask = 3 << 12, kFcfSrcAddrNone = 0 << 14, kFcfSrcAddrShort = 2 << 14, kFcfSrcAddrExt = 3 << 14, @@ -453,6 +525,12 @@ public: kMacCmdCoordinatorRealignment = 8, kMacCmdGtsRequest = 9, + kHeaderIeVendor = 0x00, + kHeaderIeTermination2 = 0x7f, + kVendorOuiNest = 0x18b430, + kVendorOuiSize = 3, + kVendorIeTime = 0x01, + kInfoStringSize = 110, ///< Max chars needed for the info string representation (@sa ToInfoString()). }; @@ -491,6 +569,14 @@ public: */ uint8_t GetType(void) const { return GetPsdu()[0] & kFcfFrameTypeMask; } + /** + * This method returns the IEEE 802.15.4 Frame Version. + * + * @returns The IEEE 802.15.4 Frame Version. + * + */ + uint16_t GetVersion(void) const { return GetFrameControlField() & kFcfFrameVersionMask; }; + /** * This method indicates whether or not security is enabled. * @@ -534,6 +620,15 @@ public: */ void SetAckRequest(bool aAckRequest); + /** + * This method indicates whether or not IEs present. + * + * @retval TRUE If IEs present. + * @retval FALSE If no IE present. + * + */ + bool IsIePresent(void) const { return (GetFrameControlField() & kFcfIePresent) != 0; }; + /** * This method returns the Sequence Number value. * @@ -610,6 +705,14 @@ public: */ otError SetDstAddr(const Address &aAddress); + /** + * This method indicates whether or not the Src PanId is present. + * + * @returns TRUE if the Src PanId is present, FALSE otherwise. + * + */ + bool IsSrcPanIdPresent(uint16_t aFcf) const; + /** * This method gets the Source PAN Identifier. * @@ -980,7 +1083,7 @@ public: * @returns A pointer to the PSDU. * */ - const uint8_t *GetPsdu(void) const { return mPsdu; } + uint8_t *GetPsdu(void) const { return mPsdu; } /** * This method returns a pointer to the MAC Header. @@ -1012,7 +1115,7 @@ public: * @returns A pointer to the MAC Payload. * */ - const uint8_t *GetPayload(void) const; + uint8_t *GetPayload(void) const; /** * This method returns a pointer to the MAC Footer. @@ -1028,7 +1131,88 @@ public: * @returns A pointer to the MAC Footer. * */ - const uint8_t *GetFooter(void) const; + uint8_t *GetFooter(void) const; + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method sets the Time IE offset. + * + * @param[in] aOffset The Time IE offset, 0 means no Time IE. + * + */ + void SetTimeIeOffset(uint8_t aOffset) { mIeInfo->mTimeIeOffset = aOffset; } + + /** + * This method sets the offset to network time. + * + * @param[in] aNetworkTimeOffset The offset to network time. + * + */ + void SetNetworkTimeOffset(int64_t aNetworkTimeOffset) { mIeInfo->mNetworkTimeOffset = aNetworkTimeOffset; } + + /** + * This method gets the offset to network time. + * + * @returns The offset to network time. + * + */ + int64_t GetNetworkTimeOffset(void) const { return mIeInfo->mNetworkTimeOffset; } + + /** + * This method sets the time sync sequence. + * + * @param[in] aTimeSyncSeq The time sync sequence. + * + */ + void SetTimeSyncSeq(uint8_t aTimeSyncSeq) { mIeInfo->mTimeSyncSeq = aTimeSyncSeq; } + + /** + * This method gets the time sync sequence. + * + * @returns The time sync sequence. + * + */ + uint8_t GetTimeSyncSeq(void) const { return mIeInfo->mTimeSyncSeq; } + + /** + * This method returns the timestamp when the SFD was received. + * + * @returns The timestamp when the SFD was received, in microseconds. + * + */ + uint64_t GetTimestamp(void) const { return mIeInfo->mTimestamp; } + + /** + * This method appends Header IEs to MAC header. + * + * @param[in] aIeList The pointer to the Header IEs array. + * @param[in] aIeCount The number of Header IEs in the array. + * + * @retval OT_ERROR_NONE Successfully appended the Header IEs. + * @retval OT_ERROR_FAILED If IE Present bit is not set. + * + */ + otError AppendHeaderIe(HeaderIe *aIeList, uint8_t aIeCount); + + /** + * This method returns a pointer to the Header IE. + * + * @param[in] aIeId The Element Id of the Header IE. + * + * @returns A pointer to the Header IE, NULL if not found. + * + */ + uint8_t *GetHeaderIe(uint8_t aIeId) const; + + /** + * This method returns a pointer to the vendor specific Time IE. + * + * @returns A pointer to the Time IE, NULL if not found. + * + */ + uint8_t *GetTimeIe(void) const; + +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC /** * This method returns information about the frame object as an `InfoString` object. @@ -1051,7 +1235,11 @@ private: uint8_t FindSrcPanIdIndex(void) const; uint8_t FindSrcAddrIndex(void) const; uint8_t FindSecurityHeaderIndex(void) const; + uint8_t SkipSecurityHeaderIndex(void) const; uint8_t FindPayloadIndex(void) const; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + uint8_t FindHeaderIeIndex(void) const; +#endif static uint8_t GetKeySourceLength(uint8_t aKeyIdMode); }; @@ -1271,6 +1459,111 @@ private: uint8_t mExtendedPanId[kExtPanIdSize]; } OT_TOOL_PACKED_END; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +/** + * This class implements vendor specific Header IE generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class VendorIeHeader +{ +public: + /** + * This method returns the Vendor OUI. + * + * @returns the Vendor OUI. + * + */ + const uint8_t *GetVendorOui(void) const { return mVendorOui; } + + /** + * This method sets the Vendor OUI. + * + * @param[in] aVendorOui A pointer to the Vendor OUI. + * + */ + void SetVendorOui(uint8_t *aVendorOui) { memcpy(mVendorOui, aVendorOui, Frame::kVendorOuiSize); } + + /** + * This method returns the Vendor IE sub-type. + * + * @returns the Vendor IE sub-type. + * + */ + uint8_t GetSubType(void) const { return mSubType; } + + /** + * This method sets the Vendor IE sub-type. + * + * @param[in] the Vendor IE sub-type. + * + */ + void SetSubType(uint8_t aSubType) { mSubType = aSubType; } + +private: + uint8_t mVendorOui[Frame::kVendorOuiSize]; + uint8_t mSubType; +} OT_TOOL_PACKED_END; + +/** + * This class implements Time Header IE generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class TimeIe : public VendorIeHeader +{ +public: + /** + * This method initializes the time IE. + * + */ + void Init(void) + { + uint8_t oui[3] = {Frame::kVendorOuiNest & 0xff, (Frame::kVendorOuiNest >> 8) & 0xff, + (Frame::kVendorOuiNest >> 16) & 0xff}; + + SetVendorOui(oui); + SetSubType(Frame::kVendorIeTime); + } + + /** + * This method returns the time sync sequence. + * + * @returns the time sync sequence. + * + */ + uint8_t GetSequence(void) const { return mSequence; } + + /** + * This method sets the tine sync sequence. + * + * @param[in] aSequence The time sync sequence. + * + */ + void SetSequence(uint8_t aSequence) { mSequence = aSequence; } + + /** + * This method returns the network time. + * + * @returns the network time, in microseconds. + * + */ + uint64_t GetTime(void) const { return ot::Encoding::LittleEndian::HostSwap64(mTime); } + + /** + * This method sets the network time. + * + * @param[in] aTime The network time. + * + */ + void SetTime(uint64_t aTime) { mTime = ot::Encoding::LittleEndian::HostSwap64(aTime); } + +private: + uint8_t mSequence; + uint64_t mTime; +} OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** * @} * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 07a893d99..2730612c6 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -1632,4 +1632,54 @@ #define OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE 256 #endif +/** + * @def OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + * + * Define as 1 to enable the time synchronization service feature. + * + * @note If it's enabled, plaforms must support interrupt context and concurrent access AES. + * + */ +#ifndef OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +#define OPENTHREAD_CONFIG_ENABLE_TIME_SYNC 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED + * + * Define as 1 to require time synchronization when attaching to a network. If the device is router capable + * and cannot find a neighboring router supporting time synchronization, the device will form a new partition. + * If the device is not router capable, the device will remain an orphan. + * + * Applicable only if time synchronization service feature is enabled (i.e., OPENTHREAD_CONFIG_ENABLE_TIME_SYNC is set) + * + */ +#ifndef OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED +#define OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_TIME_SYNC_PERIOD + * + * Specifies the default period of time synchronization, in seconds. + * + * Applicable only if time synchronization service feature is enabled (i.e., OPENTHREAD_CONFIG_ENABLE_TIME_SYNC is set) + * + */ +#ifndef OPENTHREAD_CONFIG_TIME_SYNC_PERIOD +#define OPENTHREAD_CONFIG_TIME_SYNC_PERIOD 30 +#endif + +/** + * @def OPENTHREAD_CONFIG_TIME_SYNC_XTAL_THRESHOLD + * + * Specifies the default XTAL threshold for a device to become Router in time synchronization enabled network, in PPM. + * + * Applicable only if time synchronization service feature is enabled (i.e., OPENTHREAD_CONFIG_ENABLE_TIME_SYNC is set) + * + */ +#ifndef OPENTHREAD_CONFIG_TIME_SYNC_XTAL_THRESHOLD +#define OPENTHREAD_CONFIG_TIME_SYNC_XTAL_THRESHOLD 300 +#endif + #endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_ diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 2e54972bf..572795e32 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -622,6 +622,9 @@ otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) uint16_t dstpan; uint8_t secCtl = Mac::Frame::kSecNone; otError error = OT_ERROR_NONE; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + Mac::HeaderIe ieList[2]; +#endif if (mAddMeshHeader) { @@ -635,7 +638,20 @@ otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) } // initialize MAC header - fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006; + fcf = Mac::Frame::kFcfFrameData; + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + + if (aMessage.IsTimeSync()) + { + fcf |= Mac::Frame::kFcfFrameVersion2015 | Mac::Frame::kFcfIePresent; + } + else +#endif + { + fcf |= Mac::Frame::kFcfFrameVersion2006; + } + fcf |= (mMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt; fcf |= (mMacSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt; @@ -687,7 +703,21 @@ otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) if (dstpan == netif.GetMac().GetPanId()) { - fcf |= Mac::Frame::kFcfPanidCompression; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // Handle a special case in IEEE 802.15.4-2015, when Pan ID Compression is 0, but Src Pan ID is not present: + // Dest Address: Extended + // Src Address: Extended + // Dest Pan ID: Present + // Src Pan ID: Not Present + // Pan ID Compression: 0 + + if ((fcf & Mac::Frame::kFcfFrameVersionMask) != Mac::Frame::kFcfFrameVersion2015 || + (fcf & Mac::Frame::kFcfDstAddrMask) != Mac::Frame::kFcfDstAddrExt || + (fcf & Mac::Frame::kFcfSrcAddrMask) != Mac::Frame::kFcfSrcAddrExt) +#endif + { + fcf |= Mac::Frame::kFcfPanidCompression; + } } aFrame.InitMacHeader(fcf, secCtl); @@ -696,6 +726,28 @@ otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) aFrame.SetDstAddr(mMacDest); aFrame.SetSrcAddr(mMacSource); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + + if (aMessage.IsTimeSync()) + { + Mac::TimeIe *ie; + uint8_t * cur = NULL; + + ieList[0].Init(); + ieList[0].SetId(Mac::Frame::kHeaderIeVendor); + ieList[0].SetLength(sizeof(Mac::TimeIe)); + ieList[1].Init(); + ieList[1].SetId(Mac::Frame::kHeaderIeTermination2); + ieList[1].SetLength(0); + aFrame.AppendHeaderIe(ieList, 2); + + cur = aFrame.GetHeaderIe(Mac::Frame::kHeaderIeVendor); + ie = reinterpret_cast(cur + sizeof(Mac::HeaderIe)); + ie->Init(); + } + +#endif + payload = aFrame.GetPayload(); headerLength = 0; @@ -833,6 +885,9 @@ otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame) if (mMessageNextOffset < aMessage.GetLength()) { aFrame.SetFramePending(true); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + aMessage.SetTimeSync(false); +#endif } exit: @@ -1110,6 +1165,10 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame) linkInfo.mRss = aFrame.GetRssi(); linkInfo.mLqi = aFrame.GetLqi(); linkInfo.mLinkSecurity = aFrame.GetSecurityEnabled(); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + linkInfo.mNetworkTimeOffset = aFrame.GetNetworkTimeOffset(); + linkInfo.mTimeSyncSeq = aFrame.GetTimeSyncSeq(); +#endif payload = aFrame.GetPayload(); payloadLength = aFrame.GetPayloadLength(); @@ -1204,6 +1263,10 @@ void MeshForwarder::HandleFragment(uint8_t * aFrame, message->SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity); message->SetPanId(aLinkInfo.mPanId); message->AddRss(aLinkInfo.mRss); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + message->SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq); + message->SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset); +#endif headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, fragmentHeader.GetDatagramSize()); VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE); @@ -1372,6 +1435,10 @@ void MeshForwarder::HandleLowpanHC(uint8_t * aFrame, message->SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity); message->SetPanId(aLinkInfo.mPanId); message->AddRss(aLinkInfo.mRss); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + message->SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq); + message->SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset); +#endif headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, 0); VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index b8ba5ade4..f0790756b 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -36,6 +36,7 @@ #include "mle.hpp" #include +#include #include "common/code_utils.hpp" #include "common/debug.hpp" @@ -55,6 +56,7 @@ #include "thread/key_manager.hpp" #include "thread/mle_router.hpp" #include "thread/thread_netif.hpp" +#include "thread/time_sync_service.hpp" using ot::Encoding::BigEndian::HostSwap16; @@ -1349,6 +1351,38 @@ exit: return error; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +otError Mle::AppendTimeRequest(Message &aMessage) +{ + TimeRequestTlv tlv; + + tlv.Init(); + + return aMessage.Append(&tlv, sizeof(tlv)); +} + +otError Mle::AppendTimeParameter(Message &aMessage) +{ + TimeParameterTlv tlv; + + tlv.Init(); + tlv.SetTimeSyncPeriod(GetNetif().GetTimeSync().GetTimeSyncPeriod()); + tlv.SetXtalThreshold(GetNetif().GetTimeSync().GetXtalThreshold()); + + return aMessage.Append(&tlv, sizeof(tlv)); +} + +otError Mle::AppendXtalAccuracy(Message &aMessage) +{ + XtalAccuracyTlv tlv; + + tlv.Init(); + tlv.SetXtalAccuracy(otPlatTimeGetXtalAccuracy()); + + return aMessage.Append(&tlv, sizeof(tlv)); +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + otError Mle::AppendActiveTimestamp(Message &aMessage) { ThreadNetif & netif = GetNetif(); @@ -1845,6 +1879,9 @@ otError Mle::SendParentRequest(ParentRequestType aType) SuccessOrExit(error = AppendChallenge(*message, mParentRequest.mChallenge, sizeof(mParentRequest.mChallenge))); SuccessOrExit(error = AppendScanMask(*message, scanMask)); SuccessOrExit(error = AppendVersion(*message)); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + SuccessOrExit(error = AppendTimeRequest(*message)); +#endif memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xff02); @@ -2604,6 +2641,12 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn case Header::kCommandAnnounce: HandleAnnounce(aMessage, aMessageInfo); break; + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + case Header::kCommandTimeSync: + mle.HandleTimeSync(aMessage, aMessageInfo); + break; +#endif } exit: @@ -2947,6 +2990,9 @@ otError Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInf MleFrameCounterTlv mleFrameCounter; ChallengeTlv challenge; Mac::ExtAddress extAddress; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + TimeParameterTlv timeParameter; +#endif // Source Address SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); @@ -3050,6 +3096,27 @@ otError Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInf mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + + // Time Parameter + if (Tlv::GetTlv(aMessage, Tlv::kTimeParameter, sizeof(timeParameter), timeParameter) == OT_ERROR_NONE) + { + VerifyOrExit(timeParameter.IsValid()); + + GetNetif().GetTimeSync().SetTimeSyncPeriod(timeParameter.GetTimeSyncPeriod()); + GetNetif().GetTimeSync().SetXtalThreshold(timeParameter.GetXtalThreshold()); + } + +#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED + else + { + // If the time sync feature is required, don't choose the parent which doesn't support it. + ExitNow(); + } + +#endif // OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // Challenge SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); @@ -3158,6 +3225,14 @@ otError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::MessageIn netif.GetPendingDataset().ClearNetwork(); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // Sync to Thread network time + if (aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ) + { + GetNetif().GetTimeSync().HandleTimeSyncMessage(aMessage); + } +#endif + // Parent Attach Success mAttachTimer.Stop(); SetStateDetached(); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index dadc71ea7..a79a3375a 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -301,6 +301,13 @@ public: kCommandAnnounce = 15, ///< Announce kCommandDiscoveryRequest = 16, ///< Discovery Request kCommandDiscoveryResponse = 17, ///< Discovery Response + + /** + * Applicable/Required only when time synchronization service + * (`OPENTHREAD_CONFIG_ENABLE_TIME_SYNC`) is enabled. + * + */ + kCommandTimeSync = 99, ///< Time Synchronization }; /** @@ -1248,6 +1255,41 @@ protected: */ otError AppendAddressRegistration(Message &aMessage); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method appends a Time Request TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval OT_ERROR_NONE Successfully appended the Time Request TLV. + * @retval OT_ERROR_NO_BUFS Insufficient buffers available to append the Time Request TLV. + * + */ + otError AppendTimeRequest(Message &aMessage); + + /** + * This method appends a Time Parameter TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval OT_ERROR_NONE Successfully appended the Time Parameter TLV. + * @retval OT_ERROR_NO_BUFS Insufficient buffers available to append the Time Parameter TLV. + * + */ + otError AppendTimeParameter(Message &aMessage); + + /** + * This method appends a XTAL Accuracy TLV to a message. + * + * @param[in] aMessage A reference to the message. + * + * @retval OT_ERROR_NONE Successfully appended the XTAL Accuracy TLV. + * @retval OT_ERROR_NO_BUFS Insufficient buffers available to append the XTAl Accuracy TLV. + * + */ + otError AppendXtalAccuracy(Message &aMessage); +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** * This method appends a Active Timestamp TLV to a message. * diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 664280646..12d1207d1 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -50,6 +50,7 @@ #include "thread/thread_netif.hpp" #include "thread/thread_tlvs.hpp" #include "thread/thread_uri_paths.hpp" +#include "thread/time_sync_service.hpp" using ot::Encoding::BigEndian::HostSwap16; @@ -506,6 +507,10 @@ otError MleRouter::SendLinkRequest(Neighbor *aNeighbor) break; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + SuccessOrExit(error = AppendTimeRequest(*message)); +#endif + if (aNeighbor == NULL) { Random::FillBuffer(mChallenge, sizeof(mChallenge)); @@ -561,6 +566,9 @@ otError MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::Message SourceAddressTlv sourceAddress; TlvRequestTlv tlvRequest; uint16_t rloc16; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + TimeRequestTlv timeRequest; +#endif LogMleMessage("Receive Link Request", aMessageInfo.GetPeerAddr()); @@ -646,6 +654,17 @@ otError MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::Message tlvRequest.SetLength(0); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (Tlv::GetTlv(aMessage, Tlv::kTimeRequest, sizeof(timeRequest), timeRequest) == OT_ERROR_NONE) + { + neighbor->SetTimeSyncEnabled(true); + } + else + { + neighbor->SetTimeSyncEnabled(false); + } +#endif + SuccessOrExit(error = SendLinkAccept(aMessageInfo, neighbor, tlvRequest, challenge)); exit: @@ -717,6 +736,13 @@ otError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, aNeighbor->SetState(Neighbor::kStateLinkRequest); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (aNeighbor->IsTimeSyncEnabled()) + { + message->SetTimeSync(true); + } +#endif + if (aMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(error = AddDelayedResponse(*message, aMessageInfo.GetPeerAddr(), @@ -900,6 +926,9 @@ otError MleRouter::HandleLinkAccept(const Message & aMessage, SendDataRequest(aMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + GetNetif().GetTimeSync().HandleTimeSyncMessage(aMessage); +#endif break; case OT_DEVICE_ROLE_CHILD: @@ -1167,7 +1196,12 @@ otError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::Messa ExitNow(); } - if (ComparePartitions(IsSingleton(route), leaderData, IsSingleton(), mLeaderData) > 0) + if (ComparePartitions(IsSingleton(route), leaderData, IsSingleton(), mLeaderData) > 0 +#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED + // if time sync is required, it will only migrate to a better network which also enables time sync. + && aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ +#endif + ) { BecomeChild(kAttachBetter); } @@ -1189,6 +1223,10 @@ otError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::Messa VerifyOrExit(IsActiveRouter(sourceAddress.GetRloc16()) && route.IsValid()); routerId = GetRouterId(sourceAddress.GetRloc16()); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + GetNetif().GetTimeSync().HandleTimeSyncMessage(aMessage); +#endif + if (IsFullThreadDevice() && ((mRouterTable.GetActiveRouterCount() == 0) || (static_cast(route.GetRouterIdSequence() - mRouterTable.GetRouterIdSequence()) > 0))) @@ -1507,6 +1545,9 @@ otError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::Messa ChallengeTlv challenge; Router * leader; Child * child; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + TimeRequestTlv timeRequest; +#endif LogMleMessage("Receive Parent Request", aMessageInfo.GetPeerAddr()); @@ -1577,6 +1618,16 @@ otError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::Messa child->ResetLinkFailures(); child->SetState(Neighbor::kStateParentRequest); child->SetDataRequestPending(false); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (Tlv::GetTlv(aMessage, Tlv::kTimeRequest, sizeof(timeRequest), timeRequest) == OT_ERROR_NONE) + { + child->SetTimeSyncEnabled(true); + } + else + { + child->SetTimeSyncEnabled(false); + } +#endif } if (!child->IsStateValidOrRestoring()) @@ -1794,6 +1845,13 @@ void MleRouter::HandleStateUpdateTimer(void) SynchronizeChildNetworkData(); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (mRole == OT_DEVICE_ROLE_LEADER || mRole == OT_DEVICE_ROLE_ROUTER) + { + GetNetif().GetTimeSync().ProcessTimeSync(); + } +#endif + exit: return; } @@ -1814,6 +1872,12 @@ otError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &aChalle SuccessOrExit(error = AppendLinkFrameCounter(*message)); SuccessOrExit(error = AppendMleFrameCounter(*message)); SuccessOrExit(error = AppendResponse(*message, aChallenge.GetChallenge(), aChallenge.GetLength())); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (aChild->IsTimeSyncEnabled()) + { + SuccessOrExit(error = AppendTimeParameter(*message)); + } +#endif aChild->GenerateChallenge(); @@ -2740,6 +2804,13 @@ otError MleRouter::SendChildIdResponse(Child &aChild) netif.GetMeshForwarder().GetSourceMatchController().SetSrcMatchAsShort(aChild, false); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + if (aChild.IsTimeSyncEnabled()) + { + message->SetTimeSync(true); + } +#endif + memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); destination.SetIid(aChild.GetExtAddress()); @@ -3695,6 +3766,10 @@ otError MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) statusTlv.SetStatus(aStatus); SuccessOrExit(error = message->Append(&statusTlv, sizeof(statusTlv))); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + SuccessOrExit(error = AppendXtalAccuracy(*message)); +#endif + SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr())); messageInfo.SetSockAddr(GetMeshLocal16()); messageInfo.SetPeerPort(kCoapUdpPort); @@ -3889,6 +3964,9 @@ void MleRouter::HandleAddressSolicit(Coap::Header &aHeader, Message &aMessage, c ThreadRloc16Tlv rlocTlv; ThreadStatusTlv statusTlv; Router * router = NULL; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + XtalAccuracyTlv xtalAccuracyTlv; +#endif VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_PARSE); @@ -3901,6 +3979,15 @@ void MleRouter::HandleAddressSolicit(Coap::Header &aHeader, Message &aMessage, c SuccessOrExit(error = ThreadTlv::GetTlv(aMessage, ThreadTlv::kStatus, sizeof(statusTlv), statusTlv)); VerifyOrExit(statusTlv.IsValid(), error = OT_ERROR_PARSE); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + // In a time sync enabled network, all routers' xtal accuracy must be less than the threshold. + if (Tlv::GetTlv(aMessage, Tlv::kXtalAccuracy, sizeof(xtalAccuracyTlv), xtalAccuracyTlv) != OT_ERROR_NONE || + xtalAccuracyTlv.GetXtalAccuracy() > GetNetif().GetTimeSync().GetXtalThreshold()) + { + ExitNow(router = NULL); + } +#endif + // see if allocation already exists router = mRouterTable.GetRouter(macAddr64Tlv.GetMacAddr()); @@ -4554,6 +4641,45 @@ bool MleRouter::IsSleepyChildSubscribed(const Ip6::Address &aAddress, Child &aCh aChild.HasIp6Address(GetInstance(), aAddress); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +otError MleRouter::HandleTimeSync(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + LogMleMessage("Receive Time Sync", aMessageInfo.GetPeerAddr()); + + GetNetif().GetTimeSync().HandleTimeSyncMessage(aMessage); + + return OT_ERROR_NONE; +} + +otError MleRouter::SendTimeSync(void) +{ + otError error = OT_ERROR_NONE; + Ip6::Address destination; + Message * message = NULL; + + VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); + SuccessOrExit(error = AppendHeader(*message, Header::kCommandTimeSync)); + + message->SetTimeSync(true); + + memset(&destination, 0, sizeof(destination)); + destination.mFields.m16[0] = HostSwap16(0xff02); + destination.mFields.m16[7] = HostSwap16(0x0001); + SuccessOrExit(error = SendMessage(*message, destination)); + + LogMleMessage("Send Time Sync", destination); + +exit: + + if (error != OT_ERROR_NONE && message != NULL) + { + message->Free(); + } + + return error; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + } // namespace Mle } // namespace ot diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp index aedac5c22..d4dfcfd86 100644 --- a/src/core/thread/mle_router_ftd.hpp +++ b/src/core/thread/mle_router_ftd.hpp @@ -669,6 +669,17 @@ public: */ static uint8_t LinkQualityToCost(uint8_t aLinkQuality); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method generates an MLE Time Synchronization message. + * + * @retval OT_ERROR_NONE Successfully sent an MLE Time Synchronization message. + * @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Time Synchronization message. + * + */ + otError SendTimeSync(void); +#endif + private: enum { @@ -707,6 +718,9 @@ private: otError HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); otError HandleNetworkDataUpdateRouter(void); otError HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + otError HandleTimeSync(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); +#endif otError ProcessRouteTlv(const RouteTlv &aRoute); void StopAdvertiseTimer(void); diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp index 8a8df6dbd..42fd3404c 100644 --- a/src/core/thread/mle_router_mtd.hpp +++ b/src/core/thread/mle_router_mtd.hpp @@ -147,6 +147,9 @@ private: void HandlePartitionChange(void) {} void StopAdvertiseTimer(void) {} otError ProcessRouteTlv(const RouteTlv &) { return OT_ERROR_NONE; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + otError HandleTimeSync(const Message &, const Ip6::MessageInfo &) { return OT_ERROR_DROP; } +#endif ChildTable mChildTable; RouterTable mRouterTable; diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index fc8062209..dcee8f630 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -109,7 +109,17 @@ public: kActiveDataset = 24, ///< Active Operational Dataset TLV kPendingDataset = 25, ///< Pending Operational Dataset TLV kDiscovery = 26, ///< Thread Discovery TLV - kInvalid = 255, + + /** + * Applicable/Required only when time synchronization service + * (`OPENTHREAD_CONFIG_ENABLE_TIME_SYNC`) is enabled. + * + */ + kTimeRequest = 252, ///< Time Request TLV + kTimeParameter = 253, ///< Time Parameter TLV + kXtalAccuracy = 254, ///< XTAL Accuracy TLV + + kInvalid = 255, }; /** @@ -1595,6 +1605,147 @@ private: uint16_t mPanId; } OT_TOOL_PACKED_END; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +/** + * This class implements Time Request TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class TimeRequestTlv : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) + { + SetType(kTimeRequest); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } +} OT_TOOL_PACKED_END; + +/** + * This class implements Time Parameter TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class TimeParameterTlv : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) + { + SetType(kTimeParameter); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the time sync period. + * + * @returns The time sync period. + * + */ + uint16_t GetTimeSyncPeriod(void) const { return HostSwap16(mTimeSyncPeriod); } + + /** + * This method sets the time sync period. + * + * @param[in] aTimeSyncPeriod The time sync period. + * + */ + void SetTimeSyncPeriod(uint16_t aTimeSyncPeriod) { mTimeSyncPeriod = HostSwap16(aTimeSyncPeriod); } + + /** + * This method returns the XTAL accuracy threshold. + * + * @returns The XTAL accuracy threshold. + * + */ + uint16_t GetXtalThreshold(void) const { return HostSwap16(mXtalThreshold); } + + /** + * This method sets the XTAL accuracy threshold. + * + * @param[in] aXTALThreshold The XTAL accuracy threshold. + * + */ + void SetXtalThreshold(uint16_t aXtalThreshold) { mXtalThreshold = HostSwap16(aXtalThreshold); } + +private: + uint16_t mTimeSyncPeriod; + uint16_t mXtalThreshold; +} OT_TOOL_PACKED_END; + +/** + * This class implements XTAL Accuracy TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class XtalAccuracyTlv : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) + { + SetType(kXtalAccuracy); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the XTAL accuracy. + * + * @returns The XTAL accuracy. + * + */ + uint16_t GetXtalAccuracy(void) const { return HostSwap16(mXtalAccuracy); } + + /** + * This method sets the XTAL accuracy. + * + * @param[in] aXTALAccuracy The XTAL accuracy. + * + */ + void SetXtalAccuracy(uint16_t aXtalAccuracy) { mXtalAccuracy = HostSwap16(aXtalAccuracy); } + +private: + uint16_t mXtalAccuracy; +} OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** * This class implements Active Timestamp TLV generation and parsing. * diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 5230fb4dd..e6d224b1d 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -102,7 +102,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance) , mAnnounceBegin(aInstance) , mPanIdQuery(aInstance) , mEnergyScan(aInstance) - +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + , mTimeSync(aInstance) +#endif { mCoap.SetInterceptor(&ThreadNetif::TmfFilter, this); } diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 9e5422568..e1f868fc4 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -78,6 +78,7 @@ #include "thread/network_data_local.hpp" #include "thread/network_diagnostic.hpp" #include "thread/panid_query_server.hpp" +#include "thread/time_sync_service.hpp" #include "utils/child_supervision.hpp" #if OPENTHREAD_ENABLE_JAM_DETECTION @@ -412,6 +413,16 @@ public: */ PanIdQueryServer &GetPanIdQueryServer(void) { return mPanIdQuery; } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method returns a reference to the Time Synchronization Service object. + * + * @returns A reference to the Time Synchronization Service object. + * + */ + TimeSync &GetTimeSync(void) { return mTimeSync; } +#endif + /** * This method returns whether Thread Management Framework Addressing Rules are met. * @@ -483,6 +494,9 @@ private: AnnounceBeginServer mAnnounceBegin; PanIdQueryServer mPanIdQuery; EnergyScanServer mEnergyScan; +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + TimeSync mTimeSync; +#endif }; /** diff --git a/src/core/thread/time_sync_service.cpp b/src/core/thread/time_sync_service.cpp new file mode 100644 index 000000000..d9c46b3da --- /dev/null +++ b/src/core/thread/time_sync_service.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018, 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 + * This file implements OpenThread Time Synchronization Service. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + +#include "time_sync_service.hpp" + +#include +#include +#include + +#include "common/instance.hpp" +#include "common/logging.hpp" + +namespace ot { + +TimeSync::TimeSync(Instance &aInstance) + : InstanceLocator(aInstance) + , mTimeSyncRequired(false) + , mTimeSyncSeq(OT_TIME_SYNC_INVALID_SEQ) + , mTimeSyncPeriod(OPENTHREAD_CONFIG_TIME_SYNC_PERIOD) + , mXtalThreshold(OPENTHREAD_CONFIG_TIME_SYNC_XTAL_THRESHOLD) + , mLastTimeSyncSent(0) + , mLastTimeSyncReceived(0) + , mNetworkTimeOffset(0) +{ +} + +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 = otPlatTimeGet() + mNetworkTimeOffset; + + return networkTimeStatus; +} + +void TimeSync::HandleTimeSyncMessage(const Message &aMessage) +{ + VerifyOrExit(aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ); + + if (mTimeSyncSeq != OT_TIME_SYNC_INVALID_SEQ && (int8_t)(aMessage.GetTimeSyncSeq() - mTimeSyncSeq) < 0) + { + // receive older time sync sequence. + mTimeSyncRequired = true; + } + else if (GetInstance().GetThreadNetif().GetMle().GetRole() != OT_DEVICE_ROLE_LEADER) + { + // Update network time in following three cases: + // 1. during first attach; + // 2. already attached, receive newer time sync sequence; + // 3. during reattach or migration process. + if (mTimeSyncSeq == OT_TIME_SYNC_INVALID_SEQ || (int8_t)(aMessage.GetTimeSyncSeq() - mTimeSyncSeq) > 0 || + GetInstance().GetThreadNetif().GetMle().GetRole() == OT_DEVICE_ROLE_DETACHED) + { + // update network time and forward it. + mLastTimeSyncReceived = TimerMilli::GetNow(); + mTimeSyncSeq = aMessage.GetTimeSyncSeq(); + mNetworkTimeOffset = aMessage.GetNetworkTimeOffset(); + mTimeSyncRequired = true; + } + } + +exit: + return; +} + +void TimeSync::IncrementTimeSyncSeq(void) +{ + if (++mTimeSyncSeq == OT_TIME_SYNC_INVALID_SEQ) + { + ++mTimeSyncSeq; + } +} + +#if OPENTHREAD_FTD +void TimeSync::ProcessTimeSync(void) +{ + if (GetInstance().GetThreadNetif().GetMle().GetRole() == OT_DEVICE_ROLE_LEADER && + TimerMilli::GetNow() - mLastTimeSyncSent > TimerMilli::SecToMsec(mTimeSyncPeriod)) + { + IncrementTimeSyncSeq(); + mTimeSyncRequired = true; + } + + if (mTimeSyncRequired) + { + VerifyOrExit(GetInstance().GetThreadNetif().GetMle().SendTimeSync() == OT_ERROR_NONE); + + mLastTimeSyncSent = TimerMilli::GetNow(); + mTimeSyncRequired = false; + } + +exit: + return; +} +#endif // OPENTHREAD_FTD + +} // 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 new file mode 100644 index 000000000..71a8f4fc2 --- /dev/null +++ b/src/core/thread/time_sync_service.hpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018, 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 + * This file includes definitions for Thread network time synchronization service. + */ + +#ifndef TIME_SYNC_HPP_ +#define TIME_SYNC_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +#include +#include + +#include "common/locator.hpp" +#include "common/message.hpp" +#include "common/timer.hpp" + +namespace ot { + +/** + * This class implements OpenThread Time Synchronization Service. + * + */ +class TimeSync : public InstanceLocator +{ +public: + /** + * This constructor initializes the object. + * + */ + TimeSync(Instance &aInstance); + + /** + * Get the Thread network time. + * + * @param[inout] aNetworkTime The Thread network time in microseconds. + * + * @returns The time synchronization status. + * + */ + otNetworkTimeStatus GetTime(uint64_t &aNetworkTime) const; + + /** + * Handle the message which includes time synchronization information. + * + * @param[in] aMessage The message which includes time synchronization information. + * + */ + void HandleTimeSyncMessage(const Message &aMessage); + +#if OPENTHREAD_FTD + /** + * Send a time synchronization message when it is required. + * + * Note: A time synchronization message is required in following cases: + * 1. Leader send time sync message periodically; + * 2. Router(except Leader) received a time sync message with newer sequence; + * 3. Router received a time sync message with older sequence. + * + */ + void ProcessTimeSync(void); +#endif + + /** + * This method gets the time synchronization sequence. + * + * @returns The time synchronization sequence. + * + */ + uint8_t GetTimeSyncSeq(void) const { return mTimeSyncSeq; }; + + /** + * This method gets the time offset to the Thread network time. + * + * @returns The time offset to the Thread network time, in microseconds. + * + */ + int64_t GetNetworkTimeOffset(void) const { return mNetworkTimeOffset; }; + + /** + * Set the time synchronization period. + * + * @param[in] aTimeSyncPeriod The time synchronization period, in seconds. + * + */ + void SetTimeSyncPeriod(uint16_t aTimeSyncPeriod) { mTimeSyncPeriod = aTimeSyncPeriod; } + + /** + * Get the time synchronization period. + * + * @returns The time synchronization period, in seconds. + * + */ + uint16_t GetTimeSyncPeriod(void) const { return mTimeSyncPeriod; } + + /** + * Set the time synchronization XTAL accuracy threshold for Router. + * + * @param[in] aXtalThreshold The XTAL accuracy threshold for Router, in PPM. + * + */ + void SetXtalThreshold(uint16_t aXtalThreshold) { mXtalThreshold = aXtalThreshold; } + + /** + * Get the time synchronization XTAL accuracy threshold for Router. + * + * @returns The XTAL accuracy threshold for Router, in PPM. + * + */ + uint16_t GetXtalThreshold(void) const { return mXtalThreshold; } + +private: + /** + * Increase the time synchronization sequence. + * + */ + void IncrementTimeSyncSeq(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. + uint16_t mXtalThreshold; ///< The XTAL accuracy threshold for a device to become a Router, in PPM. + uint32_t mLastTimeSyncSent; ///< The time when the last time synchronization message was sent. + uint32_t mLastTimeSyncReceived; ///< The time when the last time synchronization message was received. + int64_t mNetworkTimeOffset; ///< The time offset to the Thread Network time +}; + +/** + * @} + */ + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + +#endif // TIME_SYNC_HPP_ diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 16c885891..d2b154c6c 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -336,6 +336,24 @@ public: */ uint8_t GetChallengeSize(void) const { return sizeof(mValidPending.mPending.mChallenge); } +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + /** + * This method indicates whether or not time sync feature is enabled. + * + * @returns TRUE if time sync feature is enabled, FALSE otherwise. + * + */ + bool IsTimeSyncEnabled(void) const { return mTimeSyncEnabled; } + + /** + * This method sets whether or not time sync feature is enabled. + * + * @param[in] aEnable TRUE if time sync feature is enabled, FALSE otherwise. + * + */ + void SetTimeSyncEnabled(bool aEnabled) { mTimeSyncEnabled = aEnabled; } +#endif + private: Mac::ExtAddress mMacAddr; ///< The IEEE 802.15.4 Extended Address uint32_t mLastHeard; ///< Time when last heard. @@ -352,13 +370,18 @@ private: } mPending; } mValidPending; - uint32_t mKeySequence; ///< Current key sequence - uint16_t mRloc16; ///< The RLOC16 - uint8_t mState : 3; ///< The link state - uint8_t mMode : 4; ///< The MLE device mode - bool mDataRequest : 1; ///< Indicates whether or not a Data Poll was received - uint8_t mLinkFailures; ///< Consecutive link failure count - LinkQualityInfo mLinkInfo; ///< Link quality info (contains average RSS, link margin and link quality) + uint32_t mKeySequence; ///< Current key sequence + uint16_t mRloc16; ///< The RLOC16 + uint8_t mState : 3; ///< The link state + uint8_t mMode : 4; ///< The MLE device mode + bool mDataRequest : 1; ///< Indicates whether or not a Data Poll was received +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + uint8_t mLinkFailures : 7; ///< Consecutive link failure count + bool mTimeSyncEnabled : 1; ///< Indicates whether or not time sync feature is enabled. +#else + uint8_t mLinkFailures; ///< Consecutive link failure count +#endif + LinkQualityInfo mLinkInfo; ///< Link quality info (contains average RSS, link margin and link quality) }; /** diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index b66f04f66..2dfd7912d 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -217,6 +217,9 @@ const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = { NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_RX_SUCCESS), NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_TX_FAILURE), NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_RX_FAILURE), +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_TIME), +#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_FTD @@ -253,6 +256,11 @@ const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = { NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_ENABLED), NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL), #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + NCP_GET_PROP_HANDLER_ENTRY(TIME_SYNC_PERIOD), + NCP_GET_PROP_HANDLER_ENTRY(TIME_SYNC_XTAL_THRESHOLD), +#endif + #endif // OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API @@ -361,6 +369,10 @@ const NcpBase::PropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] = { NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_ENABLED), NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL), #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + NCP_SET_PROP_HANDLER_ENTRY(TIME_SYNC_PERIOD), + NCP_SET_PROP_HANDLER_ENTRY(TIME_SYNC_XTAL_THRESHOLD), +#endif #endif // #if OPENTHREAD_FTD }; @@ -2026,6 +2038,10 @@ otError NcpBase::GetPropertyHandler_CAPS(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MANAGER)); #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_TIME_SYNC)); +#endif + #if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ERROR_RATE_TRACKING)); #endif diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index ac7bb51a1..0cb5d1af9 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -628,6 +628,10 @@ protected: NCP_GET_PROP_HANDLER(NEST_LEGACY_LAST_NODE_JOINED); #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + NCP_GET_PROP_HANDLER(THREAD_NETWORK_TIME); +#endif + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD // -------------------------------------------------------------------------- @@ -695,6 +699,12 @@ protected: NCP_GET_PROP_HANDLER(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL); NCP_SET_PROP_HANDLER(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL); #endif +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + NCP_GET_PROP_HANDLER(TIME_SYNC_PERIOD); + NCP_SET_PROP_HANDLER(TIME_SYNC_PERIOD); + NCP_GET_PROP_HANDLER(TIME_SYNC_XTAL_THRESHOLD); + NCP_SET_PROP_HANDLER(TIME_SYNC_XTAL_THRESHOLD); +#endif #endif // OPENTHREAD_FTD diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index 674dc2028..da6cc727f 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -998,6 +998,44 @@ exit: #endif // OPENTHREAD_ENABLE_CHANNEL_MANAGER +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +otError NcpBase::GetPropertyHandler_TIME_SYNC_PERIOD(void) +{ + return mEncoder.WriteUint16(otNetworkTimeGetSyncPeriod(mInstance)); +} + +otError NcpBase::SetPropertyHandler_TIME_SYNC_PERIOD(void) +{ + otError error = OT_ERROR_NONE; + uint16_t timeSyncPeriod; + + SuccessOrExit(error = mDecoder.ReadUint16(timeSyncPeriod)); + + SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, timeSyncPeriod)); + +exit: + return error; +} + +otError NcpBase::GetPropertyHandler_TIME_SYNC_XTAL_THRESHOLD(void) +{ + return mEncoder.WriteUint16(otNetworkTimeGetXtalThreshold(mInstance)); +} + +otError NcpBase::SetPropertyHandler_TIME_SYNC_XTAL_THRESHOLD(void) +{ + otError error = OT_ERROR_NONE; + uint16_t xtalThreshold; + + SuccessOrExit(error = mDecoder.ReadUint16(xtalThreshold)); + + SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, xtalThreshold)); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + } // namespace Ncp } // namespace ot diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 2d02e66f3..b869472ed 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -46,6 +46,9 @@ #include #endif #include +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +#include +#endif #include #include #include @@ -2521,6 +2524,23 @@ void NcpBase::StopLegacy(void) #endif // OPENTHREAD_ENABLE_LEGACY +#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC +otError NcpBase::GetPropertyHandler_THREAD_NETWORK_TIME(void) +{ + otError error = OT_ERROR_NONE; + otNetworkTimeStatus networkTimeStatus; + uint64_t time; + + networkTimeStatus = otNetworkTimeGet(mInstance, time); + + SuccessOrExit(error = mEncoder.WriteUint64(time)); + SuccessOrExit(error = mEncoder.WriteInt8((int8_t)networkTimeStatus)); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC + otError NcpBase::EncodeChannelMask(uint32_t aChannelMask) { otError error = OT_ERROR_NONE; diff --git a/src/ncp/spinel.c b/src/ncp/spinel.c index d81454622..ba1de7906 100644 --- a/src/ncp/spinel.c +++ b/src/ncp/spinel.c @@ -1641,6 +1641,18 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) ret = "PROP_CHANNEL_MANAGER_AUTO_SELECT_INTERVAL"; break; + case SPINEL_PROP_THREAD_NETWORK_TIME: + ret = "PROP_THREAD_NETWORK_TIME"; + break; + + case SPINEL_PROP_TIME_SYNC_PERIOD: + ret = "PROP_TIME_SYNC_PERIOD"; + break; + + case SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD: + ret = "PROP_TIME_SYNC_XTAL_THRESHOLD"; + break; + case SPINEL_PROP_UART_BITRATE: ret = "PROP_UART_BITRATE"; break; @@ -2226,6 +2238,10 @@ const char *spinel_capability_to_cstr(unsigned int capability) ret = "CAP_OPENTHREAD_LOG_METADATA"; break; + case SPINEL_CAP_TIME_SYNC: + ret = "CAP_TIME_SYNC"; + break; + case SPINEL_CAP_ERROR_RATE_TRACKING: ret = "CAP_ERROR_RATE_TRACKING"; break; diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h index 02089593b..8aa7bc16a 100644 --- a/src/ncp/spinel.h +++ b/src/ncp/spinel.h @@ -452,6 +452,7 @@ enum SPINEL_CAP_ERROR_RATE_TRACKING = (SPINEL_CAP_OPENTHREAD__BEGIN + 4), SPINEL_CAP_CHANNEL_MANAGER = (SPINEL_CAP_OPENTHREAD__BEGIN + 5), SPINEL_CAP_OPENTHREAD_LOG_METADATA = (SPINEL_CAP_OPENTHREAD__BEGIN + 6), + SPINEL_CAP_TIME_SYNC = (SPINEL_CAP_OPENTHREAD__BEGIN + 7), SPINEL_CAP_OPENTHREAD__END = 640, SPINEL_CAP_THREAD__BEGIN = 1024, @@ -1567,6 +1568,37 @@ typedef enum { */ SPINEL_PROP_CHANNEL_MANAGER_AUTO_SELECT_INTERVAL = SPINEL_PROP_OPENTHREAD__BEGIN + 6, + /// Thread network time. + /** Format: `Xc` - Read only + * + * Data per item is: + * + * `X`: The Thread network time, in microseconds. + * `c`: Time synchronization status. + * + */ + SPINEL_PROP_THREAD_NETWORK_TIME = SPINEL_PROP_OPENTHREAD__BEGIN + 7, + + /// Thread time synchronization period + /** Format: `S` - Read-Write + * + * Data per item is: + * + * `S`: Time synchronization period, in seconds. + * + */ + SPINEL_PROP_TIME_SYNC_PERIOD = SPINEL_PROP_OPENTHREAD__BEGIN + 8, + + /// Thread Time synchronization XTAL accuracy threshold for Router + /** Format: `S` - Read-Write + * + * Data per item is: + * + * `S`: The XTAL accuracy threshold for Router, in PPM. + * + */ + SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD = SPINEL_PROP_OPENTHREAD__BEGIN + 9, + SPINEL_PROP_OPENTHREAD__END = 0x2000, /// UART Bitrate diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index aec0af8bc..9daeec60a 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -46,26 +46,34 @@ void TestMacHeader(void) uint8_t secCtl; uint8_t headerLength; } tests[] = { - {Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrNone, 0, 3}, - {Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrShort, 0, 7}, - {Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrExt, 0, 13}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrNone, 0, 7}, - {Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrNone, 0, 13}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort, 0, 11}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt, 0, 17}, - {Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort, 0, 17}, - {Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt, 0, 23}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrNone, 0, 3}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrShort, 0, 7}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrExt, 0, 13}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrNone, 0, 7}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrNone, 0, 13}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort, 0, 11}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt, 0, 17}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort, 0, 17}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt, 0, 23}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfPanidCompression, 0, 9}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt | Mac::Frame::kFcfPanidCompression, 0, 15}, - {Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfPanidCompression, 0, 15}, - {Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt | Mac::Frame::kFcfPanidCompression, 0, 21}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | + Mac::Frame::kFcfPanidCompression, + 0, 9}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt | + Mac::Frame::kFcfPanidCompression, + 0, 15}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort | + Mac::Frame::kFcfPanidCompression, + 0, 15}, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt | + Mac::Frame::kFcfPanidCompression, + 0, 21}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfPanidCompression | - Mac::Frame::kFcfSecurityEnabled, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | + Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled, Mac::Frame::kSecMic32 | Mac::Frame::kKeyIdMode1, 15}, - {Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfPanidCompression | - Mac::Frame::kFcfSecurityEnabled, + {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | + Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled, Mac::Frame::kSecMic32 | Mac::Frame::kKeyIdMode2, 19}, };