Add support for the Host Power State feature. (#1772)

* Add support for the Host Power State feature.

* Fix compiler ewrrors.

* Fix compiler errors round 2.

* Add newlines to end of file.

* Address review comments.

* Arrange to send the power state response again if the first attempt fails.

* Fix compiler issue.
This commit is contained in:
pvanhorn
2017-05-19 11:33:38 -07:00
committed by Jonathan Hui
parent 4f5b17ab0b
commit 7862f1107e
16 changed files with 316 additions and 25 deletions
+6
View File
@@ -32,6 +32,7 @@
#include <openthread/openthread.h>
#include <openthread/cli.h>
#include <openthread/platform/uart.h>
#include <openthread/platform/misc.h>
bool skipNextLine = false;
@@ -83,3 +84,8 @@ EXTERN_C ThreadError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
return error;
}
EXTERN_C void otPlatWakeHost(void)
{
}
+5
View File
@@ -43,3 +43,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
// TODO: Write me!
return kPlatResetReason_PowerOn;
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+5
View File
@@ -69,3 +69,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
return kPlatResetReason_Unknown;
}
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+5
View File
@@ -43,3 +43,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
(void)aInstance;
return kPlatResetReason_PowerOn;
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+5
View File
@@ -93,3 +93,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
return reason;
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+5
View File
@@ -99,3 +99,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
return reason;
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+5
View File
@@ -42,3 +42,8 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
(void)aInstance;
return kPlatResetReason_PowerOn;
}
void otPlatWakeHost(void)
{
// TODO: implement an operation to wake the host from sleep state.
}
+7
View File
@@ -99,6 +99,13 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance);
*/
void otPlatAssertFail(const char *aFilename, int aLineNumber);
/**
* This function performs a platform specific operation to wake the host MCU.
* This is used only for NCP configurations.
*
*/
void otPlatWakeHost(void);
/**
* @}
*
+132
View File
@@ -119,6 +119,7 @@ const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
{ SPINEL_PROP_POWER_STATE, &NcpBase::GetPropertyHandler_POWER_STATE },
{ SPINEL_PROP_HWADDR, &NcpBase::GetPropertyHandler_HWADDR },
{ SPINEL_PROP_LOCK, &NcpBase::GetPropertyHandler_LOCK },
{ SPINEL_PROP_HOST_POWER_STATE, &NcpBase::GetPropertyHandler_HOST_POWER_STATE },
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::GetPropertyHandler_PHY_ENABLED },
{ SPINEL_PROP_PHY_FREQ, &NcpBase::GetPropertyHandler_PHY_FREQ },
@@ -270,6 +271,7 @@ const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
{
{ SPINEL_PROP_POWER_STATE, &NcpBase::SetPropertyHandler_POWER_STATE },
{ SPINEL_PROP_HOST_POWER_STATE, &NcpBase::SetPropertyHandler_HOST_POWER_STATE },
#if OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::SetPropertyHandler_PHY_ENABLED },
@@ -566,6 +568,9 @@ NcpBase::NcpBase(otInstance *aInstance):
mUpdateChangedPropsTask(aInstance->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this),
mChangedFlags(NCP_PLAT_RESET_REASON),
mShouldSignalEndOfScan(false),
mHostPowerState(SPINEL_HOST_POWER_STATE_ONLINE),
mHostPowerStateInProgress(false),
mHostPowerStateHeader(0),
#if OPENTHREAD_ENABLE_JAM_DETECTION
mShouldSignalJamStateChange(false),
#endif
@@ -1240,6 +1245,19 @@ exit:
// MARK: Serial Traffic Glue
// ----------------------------------------------------------------------------
void NcpBase::HandleFrameTransmitDone(void *aContext, ThreadError aError)
{
NcpBase *obj = static_cast<NcpBase *>(aContext);
obj->HandleFrameTransmitDone(aError);
}
void NcpBase::HandleFrameTransmitDone(ThreadError aError)
{
(void) aError;
mHostPowerStateInProgress = false;
}
ThreadError NcpBase::OutboundFrameSend(void)
{
ThreadError errorCode;
@@ -1265,6 +1283,11 @@ void NcpBase::HandleReceive(const uint8_t *buf, uint16_t bufLength)
parsedLength = spinel_datatype_unpack(buf, bufLength, SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_DATA_S, &header, &command, &arg_ptr, &arg_len);
tid = SPINEL_HEADER_GET_TID(header);
// Receiving any message from the host has the side effect of transitioning the host power state to online.
mHostPowerState = SPINEL_HOST_POWER_STATE_ONLINE;
mHostPowerStateInProgress = false;
mTxFrameBuffer.SetFrameTransmitCallback(NULL, NULL);
if (parsedLength == bufLength)
{
errorCode = HandleCommand(header, command, arg_ptr, static_cast<uint16_t>(arg_len));
@@ -1367,12 +1390,39 @@ void NcpBase::HandleSpaceAvailableInTxBuffer(void)
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
if (mHostPowerStateHeader)
{
SuccessOrExit(
GetPropertyHandler_HOST_POWER_STATE(
mHostPowerStateHeader,
SPINEL_PROP_HOST_POWER_STATE
));
mHostPowerStateHeader = 0;
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
mTxFrameBuffer.SetFrameTransmitCallback(&NcpBase::HandleFrameTransmitDone, this);
mHostPowerStateInProgress = true;
}
}
UpdateChangedProps();
exit:
return;
}
bool NcpBase::ShouldWakeHost(void)
{
return (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE && !mHostPowerStateInProgress);
}
bool NcpBase::ShouldDeferHostSend(void)
{
return (mHostPowerState == SPINEL_HOST_POWER_STATE_DEEP_SLEEP && !mHostPowerStateInProgress);
}
void NcpBase::IncrementFrameErrorCounter(void)
{
mFramingErrorCounter++;
@@ -1952,6 +2002,17 @@ ThreadError NcpBase::GetPropertyHandler_LOCK(uint8_t header, spinel_prop_key_t k
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_HOST_POWER_STATE(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
mHostPowerState
);
}
ThreadError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
@@ -3657,6 +3718,77 @@ ThreadError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::SetPropertyHandler_HOST_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint8_t value;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
switch (value)
{
case SPINEL_HOST_POWER_STATE_OFFLINE:
case SPINEL_HOST_POWER_STATE_DEEP_SLEEP:
case SPINEL_HOST_POWER_STATE_LOW_POWER:
case SPINEL_HOST_POWER_STATE_ONLINE:
// Adopt the requested power state.
mHostPowerState = static_cast<spinel_host_power_state_t>(value);
break;
case SPINEL_HOST_POWER_STATE_RESERVED:
// Per the specification, treat this as synonymous with SPINEL_HOST_POWER_STATE_DEEP_SLEEP.
mHostPowerState = SPINEL_HOST_POWER_STATE_DEEP_SLEEP;
break;
default:
// Per the specification, treat unrecognized values as synonymous with SPINEL_HOST_POWER_STATE_LOW_POWER.
mHostPowerState = SPINEL_HOST_POWER_STATE_LOW_POWER;
break;
}
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
mHostPowerStateHeader = 0;
errorCode = HandleCommandPropertyGet(header, key);
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
if (errorCode == kThreadError_None)
{
mTxFrameBuffer.SetFrameTransmitCallback(&NcpBase::HandleFrameTransmitDone, this);
}
mHostPowerStateInProgress = true;
}
if (errorCode != kThreadError_None)
{
mHostPowerStateHeader = header;
}
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
+20
View File
@@ -140,6 +140,16 @@ protected:
*/
void HandleSpaceAvailableInTxBuffer(void);
/**
* Called by the subclass to learn when the host wake operation must be issued.
*/
bool ShouldWakeHost(void);
/**
* Called by the subclass to learn when the transfer to the host should be deferred.
*/
bool ShouldDeferHostSend(void);
private:
ThreadError OutboundFrameSend(void);
@@ -230,6 +240,10 @@ private:
static void HandleNetifStateChanged(uint32_t flags, void *context);
static void HandleFrameTransmitDone(void *aContext, ThreadError aError);
void HandleFrameTransmitDone(ThreadError aError);
private:
ThreadError OutboundFrameFeedPacked(const char *pack_format, ...);
@@ -336,6 +350,7 @@ private:
ThreadError GetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_HWADDR(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_LOCK(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_HOST_POWER_STATE(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_PHY_FREQ(uint8_t header, spinel_prop_key_t key);
ThreadError GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t header, spinel_prop_key_t key);
@@ -436,6 +451,8 @@ private:
ThreadError SetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len);
ThreadError SetPropertyHandler_HOST_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len);
ThreadError SetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len);
ThreadError SetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
@@ -660,6 +677,9 @@ private:
Tasklet mUpdateChangedPropsTask;
uint32_t mChangedFlags;
bool mShouldSignalEndOfScan;
spinel_host_power_state_t mHostPowerState;
bool mHostPowerStateInProgress;
uint8_t mHostPowerStateHeader;
#if OPENTHREAD_ENABLE_JAM_DETECTION
bool mShouldSignalJamStateChange;
+47 -4
View File
@@ -52,6 +52,7 @@ NcpFrameBuffer::NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLen) :
otMessageQueueInit(&mMessageQueue);
otMessageQueueInit(&mWriteFrameMessageQueue);
SetCallbacks(NULL, NULL, NULL);
SetFrameTransmitCallback(NULL, NULL);
Clear();
}
@@ -102,7 +103,17 @@ void NcpFrameBuffer::Clear(void)
{
if (mEmptyBufferCallback != NULL)
{
mEmptyBufferCallback(mCallbackContext, this);
mEmptyBufferCallback(mEmptyBufferCallbackContext, this);
}
}
if (mFrameTransmitMark == mReadFrameStart)
{
if (mFrameTransmitCallback != NULL)
{
mFrameTransmitCallback(mFrameTransmitContext, kThreadError_Abort);
mFrameTransmitCallback = NULL;
mFrameTransmitMark = NULL;
}
}
}
@@ -112,7 +123,29 @@ void NcpFrameBuffer::SetCallbacks(BufferCallback aEmptyBufferCallback, BufferCal
{
mEmptyBufferCallback = aEmptyBufferCallback;
mNonEmptyBufferCallback = aNonEmptyBufferCallback;
mCallbackContext = aContext;
mEmptyBufferCallbackContext = aContext;
}
ThreadError NcpFrameBuffer::SetFrameTransmitCallback(FrameTransmitCallback aFrameTransmitCallback, void *aContext)
{
ThreadError error = kThreadError_None;
VerifyOrExit(mFrameTransmitCallback == NULL || aFrameTransmitCallback == NULL, error = kThreadError_Busy);
mFrameTransmitCallback = aFrameTransmitCallback;
mFrameTransmitContext = aContext;
if (mFrameTransmitCallback != NULL)
{
mFrameTransmitMark = mWriteFrameStart;
}
else
{
mFrameTransmitMark = NULL;
}
exit:
return error;
}
// Returns the next buffer pointer addressing the wrap-around at the end of buffer.
@@ -329,7 +362,7 @@ ThreadError NcpFrameBuffer::InFrameEnd(void)
{
if (mNonEmptyBufferCallback != NULL)
{
mNonEmptyBufferCallback(mCallbackContext, this);
mNonEmptyBufferCallback(mEmptyBufferCallbackContext, this);
}
}
@@ -603,7 +636,17 @@ ThreadError NcpFrameBuffer::OutFrameRemove(void)
{
if (mEmptyBufferCallback != NULL)
{
mEmptyBufferCallback(mCallbackContext, this);
mEmptyBufferCallback(mEmptyBufferCallbackContext, this);
}
}
if (mFrameTransmitMark == mReadFrameStart)
{
if (mFrameTransmitCallback != NULL)
{
mFrameTransmitCallback(mFrameTransmitContext, kThreadError_None);
mFrameTransmitCallback = NULL;
mFrameTransmitMark = NULL;
}
}
+47 -21
View File
@@ -51,6 +51,15 @@ public:
*/
typedef void (*BufferCallback)(void *aContext, NcpFrameBuffer *aNcpFrameBuffer);
/**
* Defines a function pointer callback which is invoked to inform that a pre-tagged frame has transmitted.
*
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aError An error value representing the success or failure of the frame transmit attempt.
*
*/
typedef void (*FrameTransmitCallback)(void *aContext, ThreadError aError);
/**
* This constructor creates an NCP frame buffer.
*
@@ -234,6 +243,19 @@ public:
*/
uint16_t OutFrameGetLength(void);
/**
* This method provides a callback to NcpBuffer that it will use when the last successfully written
* frame is removed from the buffer.
*
* @param[in] aFrameTransmitCallback Callback invoked when NcpBuffer transmits the current last frame.
* @param[in] aContex A pointer to arbitrary context information.
*
* @retval kThreadError_None Successfully accepted the callback.
* @retval kThreadError_Busy The feature is already in use and busy.
*
*/
ThreadError SetFrameTransmitCallback(FrameTransmitCallback aFrameTransmitCallback, void *aContext);
private:
/*
@@ -324,34 +346,38 @@ private:
// Instance variables
uint8_t * const mBuffer; // Pointer to the buffer used to store the data.
uint8_t * const mBufferEnd; // Points to after the end of buffer.
const uint16_t mBufferLength; // Length of the the buffer.
uint8_t * const mBuffer; // Pointer to the buffer used to store the data.
uint8_t * const mBufferEnd; // Points to after the end of buffer.
const uint16_t mBufferLength; // Length of the the buffer.
BufferCallback mEmptyBufferCallback; // Callback to signal when buffer becomes empty.
BufferCallback mNonEmptyBufferCallback; // Callback to signal when buffer becomes non-empty.
void * mCallbackContext; // Context passed to callbacks.
BufferCallback mEmptyBufferCallback; // Callback to signal when buffer becomes empty.
BufferCallback mNonEmptyBufferCallback; // Callback to signal when buffer becomes non-empty.
void * mEmptyBufferCallbackContext;// Context passed to callbacks.
otMessageQueue mMessageQueue; // Main message queue.
FrameTransmitCallback mFrameTransmitCallback; // Callback to signal when a particular frame has been transmitted.
void * mFrameTransmitContext; // Context passed to mFrameTransmitCallback;
uint8_t * mFrameTransmitMark; // The end position of the desired frame in the frame buffer.
otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written.
uint8_t * mWriteFrameStart; // Pointer to start of current frame being written.
uint8_t * mWriteSegmentHead; // Pointer to start of current segment in the frame being written.
uint8_t * mWriteSegmentTail; // Pointer to end of current segment in the frame being written.
otMessageQueue mMessageQueue; // Main message queue.
ReadState mReadState; // Read state.
uint16_t mReadFrameLength; // Length of current frame being read.
otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written.
uint8_t * mWriteFrameStart; // Pointer to start of current frame being written.
uint8_t * mWriteSegmentHead; // Pointer to start of current segment in the frame being written.
uint8_t * mWriteSegmentTail; // Pointer to end of current segment in the frame being written.
uint8_t * mReadFrameStart; // Pointer to start of current frame being read.
uint8_t * mReadSegmentHead; // Pointer to start of current segment in the frame being read.
uint8_t * mReadSegmentTail; // Pointer to end of current segment in the frame being read.
uint8_t * mReadPointer; // Pointer to next byte to read (either in segment or in msg buffer).
ReadState mReadState; // Read state.
uint16_t mReadFrameLength; // Length of current frame being read.
otMessage * mReadMessage; // Current Message in the frame being read.
uint16_t mReadMessageOffset; // Offset within current message being read.
uint8_t * mReadFrameStart; // Pointer to start of current frame being read.
uint8_t * mReadSegmentHead; // Pointer to start of current segment in the frame being read.
uint8_t * mReadSegmentTail; // Pointer to end of current segment in the frame being read.
uint8_t * mReadPointer; // Pointer to next byte to read (either in segment or in msg buffer).
uint8_t mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read.
uint8_t * mReadMessageTail; // Pointer to end of current part in mMessageBuffer.
otMessage * mReadMessage; // Current Message in the frame being read.
uint16_t mReadMessageOffset; // Offset within current message being read.
uint8_t mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read.
uint8_t * mReadMessageTail; // Pointer to end of current part in mMessageBuffer.
};
} // namespace ot
+6
View File
@@ -40,6 +40,7 @@
#include <openthread/ncp.h>
#include <openthread/platform/spi-slave.h>
#include <openthread/platform/misc.h>
#include "openthread-core-config.h"
#include "openthread-instance.h"
@@ -239,6 +240,11 @@ ThreadError NcpSpi::PrepareNextSpiSendFrame(void)
VerifyOrExit(!mTxFrameBuffer.IsEmpty());
if (super_t::ShouldWakeHost())
{
otPlatWakeHost();
}
SuccessOrExit(errorCode = mTxFrameBuffer.OutFrameBegin());
frameLength = mTxFrameBuffer.OutFrameGetLength();
+7
View File
@@ -43,6 +43,7 @@
#include <openthread/ncp.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/uart.h>
#include <openthread/platform/misc.h>
#include "openthread-core-config.h"
#include "openthread-instance.h"
@@ -143,6 +144,12 @@ void NcpUart::EncodeAndSendToUart(void)
{
case kStartingFrame:
if (super_t::ShouldWakeHost())
{
otPlatWakeHost();
}
VerifyOrExit(super_t::ShouldDeferHostSend() == false);
SuccessOrExit(mFrameEncoder.Init(mUartBuffer));
mTxFrameBuffer.OutFrameBegin();
+4
View File
@@ -940,6 +940,10 @@ spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
ret = "PROP_HBO_BLOCK_MAX";
break;
case SPINEL_PROP_HOST_POWER_STATE:
ret = "PROP_HOST_POWER_STATE";
break;
case SPINEL_PROP_STREAM_DEBUG:
ret = "PROP_STREAM_DEBUG";
break;
+10
View File
@@ -190,6 +190,15 @@ typedef enum
SPINEL_POWER_STATE_ONLINE = 4,
} spinel_power_state_t;
typedef enum
{
SPINEL_HOST_POWER_STATE_OFFLINE = 0,
SPINEL_HOST_POWER_STATE_DEEP_SLEEP = 1,
SPINEL_HOST_POWER_STATE_RESERVED = 2,
SPINEL_HOST_POWER_STATE_LOW_POWER = 3,
SPINEL_HOST_POWER_STATE_ONLINE = 4,
} spinel_host_power_state_t;
enum
{
SPINEL_NET_FLAG_ON_MESH = (1 << 0),
@@ -399,6 +408,7 @@ typedef enum
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_BASE_EXT__BEGIN = 0x1000,