[low-power] enhance mCslFrameRequestAheadUs calculation for RCP (#10488)

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 <[email protected]>
This commit is contained in:
Maciej Baczmański
2024-07-19 21:39:32 -07:00
committed by GitHub
parent 602167f94a
commit 4e3483c751
12 changed files with 219 additions and 15 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (429)
#define OPENTHREAD_API_VERSION (430)
/**
* @addtogroup api-instance
+19
View File
@@ -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 changed.
*
* @param[in] aInstance The OpenThread instance structure.
*
*/
extern void otPlatRadioBusLatencyChanged(otInstance *aInstance);
/**
* Enable/Disable source address match feature.
*
+6
View File
@@ -229,6 +229,12 @@ public:
*/
void HandleEnergyScanDone(int8_t aMaxRssi);
/**
* This callback method handles "Bus Latency Changed" event from radio platform.
*
*/
void HandleBusLatencyChanged(void);
#if OPENTHREAD_CONFIG_DIAG_ENABLE
/**
* This callback method handles a "Receive Done" event from radio platform when diagnostics mode is enabled.
+7
View File
@@ -58,6 +58,13 @@ void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aA
void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) { Get<Mac::SubMac>().HandleEnergyScanDone(aMaxRssi); }
void Radio::Callbacks::HandleBusLatencyChanged(void)
{
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
Get<CslTxScheduler>().UpdateFrameRequestAhead();
#endif
}
#if OPENTHREAD_CONFIG_DIAG_ENABLE
void Radio::Callbacks::HandleDiagsReceiveDone(Mac::RxFrame *aFrame, Error aError)
{
+20
View File
@@ -116,6 +116,17 @@ exit:
return;
}
extern "C" void otPlatRadioBusLatencyChanged(otInstance *aInstance)
{
Instance &instance = AsCoreType(aInstance);
VerifyOrExit(instance.IsInitialized());
instance.Get<Radio::Callbacks>().HandleBusLatencyChanged();
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 otPlatRadioBusLatencyChanged(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)
{
+8 -3
View File
@@ -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)
+7 -1
View File
@@ -231,11 +231,17 @@ public:
*/
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;
@@ -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 \<buslatency\>
Set the bus latency in microseconds between the host and the radio chip.
```bash
> diag radiospinel buslatency 1000
Done
```
+48
View File
@@ -79,6 +79,7 @@ RadioSpinel::RadioSpinel(void)
, mPanId(0xffff)
, mChannel(0)
, mRxSensitivity(0)
, mBusLatency(0)
, mState(kStateDisabled)
, mIsPromiscuous(false)
, mRxOnWhenIdle(true)
@@ -1785,6 +1786,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<uint32_t>(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);
@@ -1912,6 +1948,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.mBusLatencyChanged != nullptr)
{
mCallbacks.mBusLatencyChanged(mInstance);
}
}
void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus)
{
OT_UNUSED_VARIABLE(aStatus);
+37
View File
@@ -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 changed.
*
* @param[in] aInstance The OpenThread instance structure.
*
*/
void (*mBusLatencyChanged)(otInstance *aInstance);
/**
* This callback notifies user of `RadioSpinel` that the transmission has started.
*
@@ -682,6 +690,18 @@ public:
*/
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.
*
@@ -859,6 +879,22 @@ public:
*/
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.
*
@@ -1233,6 +1269,7 @@ private:
otError mTxError;
static otExtAddress sIeeeEui64;
static otRadioCaps sRadioCaps;
uint32_t mBusLatency;
State mState;
bool mIsPromiscuous : 1; ///< Promiscuous mode.
+35 -10
View File
@@ -89,10 +89,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.mBusLatencyChanged = otPlatRadioBusLatencyChanged;
callbacks.mReceiveDone = otPlatRadioReceiveDone;
callbacks.mTransmitDone = otPlatRadioTxDone;
callbacks.mTxStarted = otPlatRadioTxStarted;
resetRadio = !mRadioUrl.HasParam("no-reset");
skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check");
@@ -135,6 +136,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
@@ -553,25 +561,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<size_t>(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)
@@ -905,6 +924,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)
{
+1
View File
@@ -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"