[ncp] add support for controlling power state of NCP MCU (#2635)

This commit adds a configuration option to enable NCP power state
control `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL`.
When enabled, the platform is expected to provide two functions:
`otPlatSetMcuPowerState()` and `otPlatGetMcuPowerState()`. Host
can then control the power state using the newly added spinel
property `SPINEL_PROP_MCU_POWER_STATE`.
This commit is contained in:
Abtin Keshavarzian
2018-04-05 10:20:47 -07:00
committed by Jonathan Hui
parent ab55bece52
commit ecd28458ec
8 changed files with 312 additions and 27 deletions
+48 -3
View File
@@ -141,6 +141,7 @@ Currently defined values are:
* 10: `CAP_TRNG`: Support for true random number generation. See (#feature-trng).
* 11: `CAP_CMD_MULTI`: Support for `CMD_PROP_VALUE_MULTI_GET` ((#cmd-prop-value-multi-get)), `CMD_PROP_VALUE_MULTI_SET` ((#cmd-prop-value-multi-set), and `CMD_PROP_VALUES_ARE` ((#cmd-prop-values-are)).
* 12: `CAP_UNSOL_UPDATE_FILTER`: Support for `PROP_UNSOL_UPDATE_FILTER` ((#prop-unsol-update-filter)) and `PROP_UNSOL_UPDATE_LIST` ((#prop-unsol-update-list)).
* 13: `CAP_MCU_POWER_SAVE`: Support for controlling NCP's MCU power state (`PROP_MCU_POWER_STATE`).
* 16: `CAP_802_15_4_2003`
* 17: `CAP_802_15_4_2006`
* 18: `CAP_802_15_4_2011`
@@ -192,7 +193,7 @@ This value is encoded as an unsigned 8-bit integer.
The host **MUST** only use this property from NLI 0. Behavior when used
from other NLIs is undefined.
### PROP 7: PROP_POWER_STATE {#prop-power-state}
### PROP 7: PROP_POWER_STATE {#prop-power-state} (deprecated)
* Type: Read-Write
* Packed-Encoding: `C`
@@ -201,10 +202,12 @@ Octets: | 1
--------|------------------
Fields: | POWER_STATE
This property is **deprecated**. `MCU_POWER_STATE` provides similar
functionality.
Describes the current power state of the NCP. By writing to this
property you can manage the lower state of the NCP. Enumeration is
encoded as a single unsigned byte.
Defined values are:
* 0: `POWER_STATE_OFFLINE`: NCP is physically powered off.
@@ -258,7 +261,7 @@ Unlike most other properties, setting this property to true when the
value of the property is already true **MUST** fail with a last status
of `STATUS_ALREADY`.
### PROP 10: PROP_HOST_POWER_STATE {#prop-host-power-state}
### PROP 12: PROP_HOST_POWER_STATE {#prop-host-power-state}
* Type: Read-Write
* Packed-Encoding: `C`
@@ -327,6 +330,48 @@ it was entering a low-power state.
The host **MUST** only use this property from NLI 0. Behavior when used
from other NLIs is undefined.
### PROP 13: PROP_MCU_POWER_STATE {#prop-mcu-power-state}
* Type: Read-Write
* Packed-Encoding: `C`
* Required capability: CAP_MCU_POWER_SAVE
This property specifies the desired power state of NCP's micro-controller
(MCU) when the underlying platform's operating system enters idle mode (i.e.,
all active tasks/events are processed and the MCU can potentially enter a
energy-saving power state).
The power state primarily determines how the host should interact with the NCP
and whether the host needs an external trigger (a "poke") to NCP before it can
communicate with the NCP or not. After a reset, the MCU power state MUST be
`SPINEL_MCU_POWER_STATE_ON`.
Defined values are:
* 0: `SPINEL_MCU_POWER_STATE_ON`: NCP's MCU stays on and active all the time.
When the NCP's desired power state is set to this value, host can send
messages to NCP without requiring any "poke" or external triggers. MCU is
expected to stay on and active. Note that the `ON` power state only determines
the MCU's power mode and is not related to radio's state.
* 1: `SPINEL_MCU_POWER_STATE_LOW_POWER`: NCP's MCU can enter low-power
(energy-saving) state. When the NCP's desired power state is set to
`LOW_POWER`, host is expected to "poke" the NCP (e.g., an external trigger
like an interrupt) before it can communicate with the NCP (send a message
to the NCP). The "poke" mechanism is determined by the platform code (based
on NCP's interface to the host).
While power state is set to `LOW_POWER`, NCP can still (at any time) send
messages to host. Note that receiving a message from the NCP does NOT
indicate that the NCP's power state has changed, i.e., host is expected to
continue to "poke" NCP when it wants to talk to the NCP until the power
state is explicitly changed (by setting this property to `ON`).
Note that the `LOW_POWER` power state only determines the MCU's power mode
and is not related to radio's state.
* 2: `SPINEL_MCU_POWER_STATE_OFF`: NCP is fully powered off.
An NCP hardware reset (via a RESET pin) is required to bring the NCP back
to `SPINEL_MCU_POWER_STATE_ON`. RAM is not retained after reset.
### PROP 4104: PROP_UNSOL_UPDATE_FILTER {#prop-unsol-update-filter}
* Required only if `CAP_UNSOL_UPDATE_FILTER` is set.
+12
View File
@@ -89,3 +89,15 @@ void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState)
{
(void)aInstance;
return (aState == OT_PLAT_MCU_POWER_STATE_ON) ? OT_ERROR_NONE : OT_ERROR_FAILED;
}
otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance)
{
(void)aInstance;
return OT_PLAT_MCU_POWER_STATE_ON;
}
+86
View File
@@ -105,6 +105,92 @@ void otPlatAssertFail(const char *aFilename, int aLineNumber);
*/
void otPlatWakeHost(void);
/**
* Enumeration of micro-controller's power states.
*
* These values are used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL` is enabled.
*
* The power state specifies the desired power state of NCP's micro-controller (MCU) when the underlying platform's
* operating system enters idle mode (i.e., all active tasks/events are processed and the MCU can potentially enter a
* energy-saving power state).
*
* The power state primarily determines how the host should interact with the NCP and whether the host needs an
* external trigger (a "poke") to NCP before it can communicate with the NCP or not.
*
* After a reset, the MCU power state MUST be `OT_PLAT_POWER_STATE_ON`.
*
*/
typedef enum {
/**
* NCP's MCU stays on and active all the time.
*
* When the NCP's desired power state is set to `ON`, host can send messages to NCP without requiring any "poke" or
* external triggers.
*
* @note The `ON` power state only determines the MCU's power mode and is not related to radio's state.
*
*/
OT_PLAT_MCU_POWER_STATE_ON = 0,
/**
* NCP's MCU can enter low-power (energy-saving) state.
*
* When the NCP's desired power state is set to `LOW_POWER`, host is expected to "poke" the NCP (e.g., an external
* trigger like an interrupt) before it can communicate with the NCP (send a message to the NCP). The "poke"
* mechanism is determined by the platform code (based on NCP's interface to the host).
*
* While power state is set to `LOW_POWER`, NCP can still (at any time) send messages to host. Note that receiving
* a message from the NCP does NOT indicate that the NCP's power state has changed, i.e., host is expected to
* continue to "poke" when it wants to talk to the NCP until the power state is explicitly changed (by a successful
* call to `otPlatSetMcuPowerState()` changing the state to `ON`).
*
* @note The `LOW_POWER` power state only determines the MCU's power mode and is not related to radio's state
* (radio is managed by OpenThread core and device role, e.g., device being sleepy or not.
*
*/
OT_PLAT_MCU_POWER_STATE_LOW_POWER = 1,
/**
* NCP is fully off.
*
* An NCP hardware reset (via a RESET pin) is required to bring the NCP back to `SPINEL_MCU_POWER_STATE_ON`.
* RAM is not retained after reset.
*
*/
OT_PLAT_MCU_POWER_STATE_OFF = 2,
} otPlatMcuPowerState;
/**
* This function sets the desired MCU power state.
*
* This is only applicable and used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL`
* is enabled.
*
* @param[in] aInstance A pointer to OpenThread instance.
* @param[in] aState The new MCU power state.
*
* @retval OT_ERROR_NONE The power state updated successfully.
* @retval OT_ERROR_FAILED The given MCU power state is not supported by the platform.
*
*/
otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState);
/**
* This function gets the current desired MCU power state.
*
* This is only applicable and used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL`
* is enabled.
*
* After a reset, the power state MUST return `OT_PLAT_POWER_STATE_ON`. During operation, power state SHOULD only
* change through an explicit successful call to `otPlatSetMcuPowerState()`.
*
* @param[in] aInstance A pointer to OpenThread instance.
*
* @returns The current power state.
*
*/
otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance);
/**
* @}
*
+21
View File
@@ -1248,6 +1248,27 @@
#define OPENTHREAD_CONFIG_NCP_SPINEL_RESPONSE_QUEUE_SIZE 15
#endif
/**
* @def OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
*
* Define to 1 to enable support controlling of desired power state of NCP's micro-controller.
*
* The power state specifies the desired power state of NCP's micro-controller (MCU) when the underlying platform's
* operating system enters idle mode (i.e., all active tasks/events are processed and the MCU can potentially enter a
* energy-saving power state).
*
* The power state primarily determines how the host should interact with the NCP and whether the host needs an
* external trigger (a "poke") before it can communicate with the NCP or not.
*
* When enabled, the platform is expected to provide `otPlatSetMcuPowerState()` and `otPlatGetMcuPowerState()`
* functions (please see `openthread/platform/misc.h`). Host can then control the power state using
* `SPINEL_PROP_MCU_POWER_STATE`.
*
*/
#ifndef OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
#define OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL 0
#endif
/**
* @def OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
*
+98 -2
View File
@@ -76,6 +76,7 @@ const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
NCP_GET_PROP_HANDLER_ENTRY(PHY_RX_SENSITIVITY),
NCP_GET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_GET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(MCU_POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(PROTOCOL_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_PANID),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR),
@@ -261,6 +262,7 @@ const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
const NcpBase::PropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
{
NCP_SET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_SET_PROP_HANDLER_ENTRY(MCU_POWER_STATE),
NCP_SET_PROP_HANDLER_ENTRY(UNSOL_UPDATE_FILTER),
NCP_SET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_SET_PROP_HANDLER_ENTRY(PHY_CHAN),
@@ -1791,6 +1793,10 @@ otError NcpBase::GetPropertyHandler_CAPS(void)
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_COUNTERS));
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_UNSOL_UPDATE_FILTER));
#if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MCU_POWER_STATE));
#endif
#if OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_RAW));
#endif
@@ -1859,9 +1865,100 @@ otError NcpBase::GetPropertyHandler_INTERFACE_COUNT(void)
return mEncoder.WriteUint8(1); // Only one interface for now
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_MCU_POWER_STATE(void)
{
spinel_mcu_power_state_t state = SPINEL_MCU_POWER_STATE_ON;
switch (otPlatGetMcuPowerState(mInstance))
{
case OT_PLAT_MCU_POWER_STATE_ON:
state = SPINEL_MCU_POWER_STATE_ON;
break;
case OT_PLAT_MCU_POWER_STATE_LOW_POWER:
state = SPINEL_MCU_POWER_STATE_LOW_POWER;
break;
case OT_PLAT_MCU_POWER_STATE_OFF:
state = SPINEL_MCU_POWER_STATE_OFF;
break;
}
return mEncoder.WriteUint8(state);
}
otError NcpBase::SetPropertyHandler_MCU_POWER_STATE(void)
{
otError error = OT_ERROR_NONE;
otPlatMcuPowerState powerState;
uint8_t state;
SuccessOrExit(error = mDecoder.ReadUint8(state));
switch (state)
{
case SPINEL_MCU_POWER_STATE_ON:
powerState = OT_PLAT_MCU_POWER_STATE_ON;
break;
case SPINEL_MCU_POWER_STATE_LOW_POWER:
powerState = OT_PLAT_MCU_POWER_STATE_LOW_POWER;
break;
case SPINEL_MCU_POWER_STATE_OFF:
powerState = OT_PLAT_MCU_POWER_STATE_OFF;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
SuccessOrExit(error = otPlatSetMcuPowerState(mInstance, powerState));
#if OPENTHREAD_FTD || OPENTHREAD_MTD
// If the call `otPlatSetMcuPowerState()` was successful and the desire
// state is `OFF`, ensure to disable Thread (MLE) operation (and stop
// legacy) and also bring the IPv6 interface down.
if (powerState == OT_PLAT_MCU_POWER_STATE_OFF)
{
if (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)
{
otThreadSetEnabled(mInstance, false);
StopLegacy();
}
if (otIp6IsEnabled(mInstance))
{
otIp6SetEnabled(mInstance, false);
}
}
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
exit:
return error;
}
#else // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_MCU_POWER_STATE(void)
{
return mEncoder.WriteUint8(SPINEL_MCU_POWER_STATE_ON);
}
otError NcpBase::SetPropertyHandler_MCU_POWER_STATE(void)
{
return OT_ERROR_DISABLED_FEATURE;
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_POWER_STATE(void)
{
return mEncoder.WriteUint8(SPINEL_POWER_STATE_ONLINE); // Always online at the moment
return mEncoder.WriteUint8(SPINEL_POWER_STATE_ONLINE);
}
otError NcpBase::SetPropertyHandler_POWER_STATE(void)
@@ -2162,7 +2259,6 @@ otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
return error;
}
extern "C" void otNcpPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
{
char logString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE];
+11
View File
@@ -363,6 +363,8 @@ protected:
NCP_GET_PROP_HANDLER(INTERFACE_COUNT);
NCP_GET_PROP_HANDLER(POWER_STATE);
NCP_SET_PROP_HANDLER(POWER_STATE);
NCP_GET_PROP_HANDLER(MCU_POWER_STATE);
NCP_SET_PROP_HANDLER(MCU_POWER_STATE);
NCP_GET_PROP_HANDLER(HWADDR);
NCP_GET_PROP_HANDLER(LOCK);
NCP_GET_PROP_HANDLER(HOST_POWER_STATE);
@@ -691,6 +693,15 @@ protected:
otError SetPropertyHandler_STREAM_RAW(uint8_t aHeader);
#endif
#if OPENTHREAD_ENABLE_LEGACY
void StartLegacy(void);
void StopLegacy(void);
#else
void StartLegacy(void) { }
void StopLegacy(void) { }
#endif
protected:
static NcpBase *sNcpInstance;
static spinel_status_t ThreadErrorToSpinelStatus(otError aError);
+23 -21
View File
@@ -284,28 +284,12 @@ otError NcpBase::SetPropertyHandler_NET_STACK_UP(void)
if (enabled != false)
{
error = otThreadSetEnabled(mInstance, true);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStartLegacy != NULL))
{
mLegacyHandlers->mStartLegacy();
}
#endif // OPENTHREAD_ENABLE_LEGACY
StartLegacy();
}
else
{
error = otThreadSetEnabled(mInstance, false);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStopLegacy != NULL))
{
mLegacyHandlers->mStopLegacy();
}
#endif // OPENTHREAD_ENABLE_LEGACY
StopLegacy();
}
}
@@ -2520,9 +2504,6 @@ void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
mUpdateChangedPropsTask.Post();
}
#endif // OPENTHREAD_ENABLE_LEGACY
#if OPENTHREAD_ENABLE_LEGACY
otError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(void)
{
return mEncoder.WriteData(mLegacyUlaPrefix, sizeof(mLegacyUlaPrefix));
@@ -2560,6 +2541,27 @@ otError NcpBase::GetPropertyHandler_NEST_LEGACY_LAST_NODE_JOINED(void)
return mEncoder.WriteEui64(mLegacyLastJoinedNode);
}
void NcpBase::StartLegacy(void)
{
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStartLegacy != NULL))
{
mLegacyHandlers->mStartLegacy();
}
}
void NcpBase::StopLegacy(void)
{
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStopLegacy != NULL))
{
mLegacyHandlers->mStopLegacy();
}
}
#endif // OPENTHREAD_ENABLE_LEGACY
otError NcpBase::EncodeChannelMask(uint32_t aChannelMask)
+13 -1
View File
@@ -216,6 +216,16 @@ typedef enum
SPINEL_SCAN_STATE_DISCOVER = 3,
} spinel_scan_state_t;
typedef enum
{
SPINEL_MCU_POWER_STATE_ON = 0,
SPINEL_MCU_POWER_STATE_LOW_POWER = 1,
SPINEL_MCU_POWER_STATE_OFF = 2,
} spinel_mcu_power_state_t;
// The `spinel_power_state_t` enumeration and `POWER_STATE`
// property are deprecated. Please use `MCU_POWER_STATE`
// instead.
typedef enum
{
SPINEL_POWER_STATE_OFFLINE = 0,
@@ -397,6 +407,7 @@ enum
SPINEL_CAP_TRNG = 10,
SPINEL_CAP_CMD_MULTI = 11,
SPINEL_CAP_UNSOL_UPDATE_FILTER = 12,
SPINEL_CAP_MCU_POWER_STATE = 13,
SPINEL_CAP_802_15_4__BEGIN = 16,
SPINEL_CAP_802_15_4_2003 = (SPINEL_CAP_802_15_4__BEGIN + 0),
@@ -457,12 +468,13 @@ typedef enum
SPINEL_PROP_VENDOR_ID = 4, ///< [i]
SPINEL_PROP_CAPS = 5, ///< capability list [A(i)]
SPINEL_PROP_INTERFACE_COUNT = 6, ///< Interface count [C]
SPINEL_PROP_POWER_STATE = 7, ///< PowerState [C]
SPINEL_PROP_POWER_STATE = 7, ///< PowerState [C] (deprecated, use `MCU_POWER_STATE` instead).
SPINEL_PROP_HWADDR = 8, ///< PermEUI64 [E]
SPINEL_PROP_LOCK = 9, ///< PropLock [b]
SPINEL_PROP_HBO_MEM_MAX = 10, ///< Max offload mem [S]
SPINEL_PROP_HBO_BLOCK_MAX = 11, ///< Max offload block [S]
SPINEL_PROP_HOST_POWER_STATE = 12, ///< Host MCU power state [C]
SPINEL_PROP_MCU_POWER_STATE = 13, ///< NCP's MCU power state [c]
SPINEL_PROP_BASE_EXT__BEGIN = 0x1000,