From e3f354b1dc574a39f9e1d47fac5d6dacad874b81 Mon Sep 17 00:00:00 2001 From: Maciej Baczmanski Date: Thu, 11 Jul 2024 16:44:38 +0200 Subject: [PATCH] [low-power] enhance `mCslFrameRequestAheadUs` calculation for RCP Implement `otPlatRadioGetBusLatency` API, add optional `bus-latency` argument to ot-daemon and `diag radiospinel buslatency`diagnostic commands. Add callback to notify that the bus latency has been updated and update frame request ahead from runtime, by recalculating the `mCslFrameRequestAheadUs` value. Changes allow setting a bus latency while starting a new session between host and RCP device. This way, one host can be connected to different devices and vice versa, ensuring that the latency will be added to `mCslFrameRequestAheadUs` calculations and CSL tx requests will not be sent too late. Signed-off-by: Maciej Baczmanski --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 19 +++++++++ src/core/radio/radio.hpp | 6 +++ src/core/radio/radio_callbacks.cpp | 7 ++++ src/core/radio/radio_platform.cpp | 20 +++++++++ src/core/thread/csl_tx_scheduler.cpp | 11 +++-- src/core/thread/csl_tx_scheduler.hpp | 8 +++- src/lib/spinel/README_RADIO_SPINEL_DIAG.md | 30 ++++++++++++++ src/lib/spinel/radio_spinel.cpp | 48 ++++++++++++++++++++++ src/lib/spinel/radio_spinel.hpp | 37 +++++++++++++++++ src/posix/platform/radio.cpp | 45 +++++++++++++++----- src/posix/platform/radio_url.cpp | 1 + 12 files changed, 219 insertions(+), 15 deletions(-) create mode 100644 src/lib/spinel/README_RADIO_SPINEL_DIAG.md diff --git a/include/openthread/instance.h b/include/openthread/instance.h index ef6e2d4f85b8..2444f50b82c5 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (428) +#define OPENTHREAD_API_VERSION (429) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index b1afdaa8352a..7995429a1525 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -757,6 +757,17 @@ uint64_t otPlatRadioGetNow(otInstance *aInstance); */ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance); +/** + * Get the bus latency in microseconds between the host and the radio chip. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The bus latency in microseconds between the host and the radio chip. + * Return 0 when the MAC and above layer and Radio layer resides on the same chip. + * + */ +uint32_t otPlatRadioGetBusLatency(otInstance *aInstance); + /** * @} * @@ -1003,6 +1014,14 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1 */ extern void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi); +/** + * The radio driver calls this method to notify OpenThread that the spinel bus latency has been updated. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +extern void otPlatRadioBusLatencyUpdated(otInstance *aInstance); + /** * Enable/Disable source address match feature. * diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index b76763083faf..5e108f721710 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -229,6 +229,12 @@ class Radio : public InstanceLocator, private NonCopyable */ void HandleEnergyScanDone(int8_t aMaxRssi); + /** + * This callback method handles "Bus Latency Updated" event from radio platform. + * + */ + void HandleBusLatencyUpdated(void); + #if OPENTHREAD_CONFIG_DIAG_ENABLE /** * This callback method handles a "Receive Done" event from radio platform when diagnostics mode is enabled. diff --git a/src/core/radio/radio_callbacks.cpp b/src/core/radio/radio_callbacks.cpp index 6a6131c19fdd..a834ba0203c4 100644 --- a/src/core/radio/radio_callbacks.cpp +++ b/src/core/radio/radio_callbacks.cpp @@ -58,6 +58,13 @@ void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aA void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) { Get().HandleEnergyScanDone(aMaxRssi); } +void Radio::Callbacks::HandleBusLatencyUpdated(void) +{ +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + Get().UpdateFrameRequestAhead(); +#endif +} + #if OPENTHREAD_CONFIG_DIAG_ENABLE void Radio::Callbacks::HandleDiagsReceiveDone(Mac::RxFrame *aFrame, Error aError) { diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 0f6001246f76..c304274e40db 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -116,6 +116,17 @@ extern "C" void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyS return; } +extern "C" void otPlatRadioBusLatencyUpdated(otInstance *aInstance) +{ + Instance &instance = AsCoreType(aInstance); + + VerifyOrExit(instance.IsInitialized()); + instance.Get().HandleBusLatencyUpdated(); + +exit: + return; +} + #if OPENTHREAD_CONFIG_DIAG_ENABLE extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { @@ -153,6 +164,8 @@ extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *, extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) {} +extern "C" void otPlatRadioBusLatencyUpdated(otInstance *) {} + #if OPENTHREAD_CONFIG_DIAG_ENABLE extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) {} @@ -250,6 +263,13 @@ OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return 0; } +OT_TOOL_WEAK uint32_t otPlatRadioGetBusLatency(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return 0; +} + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *aInstance) { diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 35a070c1ec42..0f28c43a0d6c 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -32,6 +32,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/time.hpp" #include "mac/mac.hpp" @@ -68,16 +69,20 @@ CslTxScheduler::CslTxScheduler(Instance &aInstance) , mFrameContext() , mCallbacks(aInstance) { - InitFrameRequestAhead(); + UpdateFrameRequestAhead(); } -void CslTxScheduler::InitFrameRequestAhead(void) +void CslTxScheduler::UpdateFrameRequestAhead(void) { uint32_t busSpeedHz = otPlatRadioGetBusSpeed(&GetInstance()); + uint32_t busLatency = otPlatRadioGetBusLatency(&GetInstance()); + // longest frame on bus is 127 bytes with some metadata, use 150 bytes for bus Tx time estimation uint32_t busTxTimeUs = ((busSpeedHz == 0) ? 0 : (150 * 8 * 1000000 + busSpeedHz - 1) / busSpeedHz); - mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs; + mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs + busLatency; + LogInfo("Bus TX Time: %lu usec, Latency: %lu usec. Calculated CSL Frame Request Ahead: %lu usec", + ToUlong(busTxTimeUs), ToUlong(busLatency), ToUlong(mCslFrameRequestAheadUs)); } void CslTxScheduler::Update(void) diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 7b3d12940911..5073706c7e85 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -231,11 +231,17 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable */ void Clear(void); + /** + * Updates the value of `mCslFrameRequestAheadUs`, based on bus speed, bus latency + * and `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US`. + * + */ + void UpdateFrameRequestAhead(void); + private: // Guard time in usec to add when checking delay while preparing the CSL frame for tx. static constexpr uint32_t kFramePreparationGuardInterval = 1500; - void InitFrameRequestAhead(void); void RescheduleCslTx(void); uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const; diff --git a/src/lib/spinel/README_RADIO_SPINEL_DIAG.md b/src/lib/spinel/README_RADIO_SPINEL_DIAG.md new file mode 100644 index 000000000000..f2d4d7e92e38 --- /dev/null +++ b/src/lib/spinel/README_RADIO_SPINEL_DIAG.md @@ -0,0 +1,30 @@ +# OpenThread Diagnostics - Radio Spinel diagnostic commands + +This module provides Spinel based radio transceiver diagnostic commands. + +`OPENTHREAD_CONFIG_DIAG_ENABLE` is required. + +## Command List + +- [buslatency](#buslatency) + +## Command Details + +### buslatency + +Get the bus latency in microseconds between the host and the radio chip. + +```bash +> diag radiospinel buslatency +0 +Done +``` + +#### buslatency \ + +Set the bus latency in microseconds between the host and the radio chip. + +```bash +> diag radiospinel buslatency 1000 +Done +``` diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index da71ecf387d2..272e24ac2719 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -79,6 +79,7 @@ RadioSpinel::RadioSpinel(void) , mPanId(0xffff) , mChannel(0) , mRxSensitivity(0) + , mBusLatency(0) , mState(kStateDisabled) , mIsPromiscuous(false) , mRxOnWhenIdle(true) @@ -1775,6 +1776,41 @@ void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, voi aContext = mOutputContext; } +otError RadioSpinel::RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS); + + aArgs++; + aArgsLength--; + + if (strcmp(aArgs[0], "buslatency") == 0) + { + if (aArgsLength == 1) + { + PlatDiagOutput("%lu\n", ToUlong(GetBusLatency())); + } + else if (aArgsLength == 2) + { + uint32_t busLatency; + char *endptr; + + busLatency = static_cast(strtoul(aArgs[1], &endptr, 0)); + VerifyOrExit(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); + + SetBusLatency(busLatency); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + } + +exit: + return error; +} + otError RadioSpinel::PlatDiagProcess(const char *aString) { return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); @@ -1904,6 +1940,18 @@ uint64_t RadioSpinel::GetNow(void) { return (mIsTimeSynced) ? (otPlatTimeGet() + uint32_t RadioSpinel::GetBusSpeed(void) const { return GetSpinelDriver().GetSpinelInterface()->GetBusSpeed(); } +uint32_t RadioSpinel::GetBusLatency(void) const { return mBusLatency; } + +void RadioSpinel::SetBusLatency(uint32_t aBusLatency) +{ + mBusLatency = aBusLatency; + + if (IsEnabled() && mCallbacks.mBusLatencyUpdated != nullptr) + { + mCallbacks.mBusLatencyUpdated(mInstance); + } +} + void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus) { OT_UNUSED_VARIABLE(aStatus); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 5abc8de439f6..ecec5bb8d288 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -87,6 +87,14 @@ struct RadioSpinelCallbacks */ void (*mEnergyScanDone)(otInstance *aInstance, int8_t aMaxRssi); + /** + * This callback notifies user of `RadioSpinel` that the bus latency has been updated. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ + void (*mBusLatencyUpdated)(otInstance *aInstance); + /** * This callback notifies user of `RadioSpinel` that the transmission has started. * @@ -680,6 +688,18 @@ class RadioSpinel : private Logger */ bool IsDiagEnabled(void) const { return mDiagMode; } + /** + * Processes RadioSpinel - specific diagnostics commands. + * + * @param[in] aArgsLength The number of arguments in @p aArgs. + * @param[in] aArgs The arguments of diagnostics command line. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_INVALID_ARGS Failed due to invalid arguments provided. + * + */ + otError RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength); + /** * Processes platform diagnostics commands. * @@ -857,6 +877,22 @@ class RadioSpinel : private Logger */ uint32_t GetBusSpeed(void) const; + /** + * Returns the bus latency between the host and the radio. + * + * @returns Bus latency in microseconds. + * + */ + uint32_t GetBusLatency(void) const; + + /** + * Sets the bus latency between the host and the radio. + * + * @param[in] aBusLatency Bus latency in microseconds. + * + */ + void SetBusLatency(uint32_t aBusLatency); + /** * Returns the co-processor sw version string. * @@ -1231,6 +1267,7 @@ class RadioSpinel : private Logger otError mTxError; static otExtAddress sIeeeEui64; static otRadioCaps sRadioCaps; + uint32_t mBusLatency; State mState; bool mIsPromiscuous : 1; ///< Promiscuous mode. diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index c82b07ebfd13..e52d9504b22f 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -84,10 +84,11 @@ void Radio::Init(const char *aUrl) callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone; callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone; #endif // OPENTHREAD_CONFIG_DIAG_ENABLE - callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; - callbacks.mReceiveDone = otPlatRadioReceiveDone; - callbacks.mTransmitDone = otPlatRadioTxDone; - callbacks.mTxStarted = otPlatRadioTxStarted; + callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; + callbacks.mBusLatencyUpdated = otPlatRadioBusLatencyUpdated; + callbacks.mReceiveDone = otPlatRadioReceiveDone; + callbacks.mTransmitDone = otPlatRadioTxDone; + callbacks.mTxStarted = otPlatRadioTxStarted; resetRadio = !mRadioUrl.HasParam("no-reset"); skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); @@ -130,6 +131,13 @@ void Radio::ProcessRadioUrl(const RadioUrl &aRadioUrl) SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode)); } + if (aRadioUrl.HasParam("bus-latency")) + { + uint32_t busLatency; + SuccessOrDie(aRadioUrl.ParseUint32("bus-latency", busLatency)); + mRadioSpinel.SetBusLatency(busLatency); + } + ProcessMaxPowerTable(aRadioUrl); #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE @@ -548,25 +556,36 @@ void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { - // deliver the platform specific diags commands to radio only ncp. OT_UNUSED_VARIABLE(aInstance); - char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; - char *cur = cmd; - char *end = cmd + sizeof(cmd); + otError error = OT_ERROR_NONE; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; + char *cur = cmd; + char *end = cmd + sizeof(cmd); #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE if (strcmp(aArgs[0], "rcpcaps") == 0) { - return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); + error = GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); + ExitNow(); } #endif + if (strcmp(aArgs[0], "radiospinel") == 0) + { + error = GetRadioSpinel().RadioSpinelDiagProcess(aArgs, aArgsLength); + ExitNow(); + } + for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++) { cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); } - return GetRadioSpinel().PlatDiagProcess(cmd); + // deliver the platform specific diags commands to radio only ncp. + error = GetRadioSpinel().PlatDiagProcess(cmd); + +exit: + return error; } void otPlatDiagModeSet(bool aMode) @@ -900,6 +919,12 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return GetRadioSpinel().GetBusSpeed(); } +uint32_t otPlatRadioGetBusLatency(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return GetRadioSpinel().GetBusLatency(); +} + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) { diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index db0c13461c10..57e1df0c25a6 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -121,6 +121,7 @@ const char *otSysGetRadioUrlHelpString(void) " fem-lnagain[=dbm] Set the Rx LNA gain in dBm of the external FEM.\n" " no-reset Do not send Spinel reset command to RCP on initialization.\n" " skip-rcp-compatibility-check Skip checking RCP API version and capabilities during initialization.\n" + " bus-latency[=usec] Communication latency in usec, default is 0.\n" #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE " iid Set the Spinel Interface ID for this process. Valid values are 0-3.\n" " iid-list List of IIDs a host can subscribe to receive spinel frames other than \n"