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}, };