[ncp-buffer] Frame Priority Support (#1948)

This commit adds support for setting two levels of priority (high or
low) to frames stored in `NcpFrameBuffer`. High-priority frames are
read before any queued low-priority frames. Within the same priority
level FIFO order is preserved.

The `NcpBase` is updated to send/queue the spinel response frames (with
non- zero `tid`) as higher-priority frames. This ensures faster response
time for spinel commands.

This commit also adds/updates test cases for unit testing the NCP
buffer and its new priority feature.
This commit is contained in:
Abtin Keshavarzian
2017-07-10 08:47:23 -07:00
committed by Jonathan Hui
parent 529b188609
commit 529c291f7a
10 changed files with 962 additions and 368 deletions
+57 -35
View File
@@ -732,9 +732,21 @@ NcpBase *NcpBase::GetNcpInstance(void)
// MARK: Outbound Frame methods
// ----------------------------------------------------------------------------
otError NcpBase::OutboundFrameBegin(void)
otError NcpBase::OutboundFrameBegin(uint8_t aHeader)
{
return mTxFrameBuffer.InFrameBegin();
NcpFrameBuffer::Priority priority;
// Non-zero tid indicates this is a response to a spinel command.
if (SPINEL_HEADER_GET_TID(aHeader) != 0)
{
priority = NcpFrameBuffer::kPriorityHigh;
}
else
{
priority = NcpFrameBuffer::kPriorityLow;
}
return mTxFrameBuffer.InFrameBegin(priority);
}
otError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
@@ -767,13 +779,14 @@ void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint1
{
otError error = OT_ERROR_NONE;
uint16_t length = otMessageGetLength(aMessage);
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(header));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_THREAD_TMF_PROXY_STREAM,
length
@@ -823,15 +836,16 @@ void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext)
void NcpBase::HandleDatagramFromStack(otMessage *aMessage)
{
otError error = OT_ERROR_NONE;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
bool isSecure = otMessageIsLinkSecurityEnabled(aMessage);
uint16_t length = otMessageGetLength(aMessage);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(header));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE,
length
@@ -859,7 +873,7 @@ exit:
if (error != OT_ERROR_NONE)
{
SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
SendLastStatus(header, SPINEL_STATUS_DROPPED);
mDroppedOutboundIpFrameCounter++;
}
else
@@ -887,13 +901,14 @@ void NcpBase::HandleRawFrame(const otRadioFrame *aFrame, void *aContext)
void NcpBase::HandleRawFrame(const otRadioFrame *aFrame)
{
uint16_t flags = 0;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
if (!mIsRawStreamEnabled)
{
goto exit;
}
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(OutboundFrameBegin(header));
if (aFrame->mDidTX)
{
@@ -904,7 +919,7 @@ void NcpBase::HandleRawFrame(const otRadioFrame *aFrame)
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_STREAM_RAW,
aFrame->mLength
@@ -1070,8 +1085,9 @@ void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aEr
void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError)
{
uint16_t flags = 0;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(OutboundFrameBegin(header));
if (aFrame->mDidTX)
{
@@ -1082,7 +1098,7 @@ void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError)
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_STREAM_RAW,
(aError == OT_ERROR_NONE) ? aFrame->mLength : 0
@@ -1467,9 +1483,11 @@ void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength)
}
void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag,
NcpFrameBuffer *aNcpBuffer)
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpBuffer)
{
OT_UNUSED_VARIABLE(aNcpBuffer);
OT_UNUSED_VARIABLE(aPriority);
static_cast<NcpBase *>(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag);
}
@@ -1750,7 +1768,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr
va_list args;
va_start(args, aPackFormat);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedVPacked(aPackFormat, args));
SuccessOrExit(error = OutboundFrameSend());
@@ -1765,7 +1783,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedData(aValuePtr, aValueLen));
SuccessOrExit(error = OutboundFrameSend());
@@ -1778,7 +1796,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedMessage(aMessage));
@@ -2069,7 +2087,7 @@ otError NcpBase::CommandHandler_PEEK(uint8_t aHeader, unsigned int aCommand, con
VerifyOrExit(mAllowPeekDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
}
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
(
@@ -2193,7 +2211,7 @@ otError NcpBase::GetPropertyHandler_CAPS(uint8_t aHeader, spinel_prop_key_t aKey
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2493,7 +2511,7 @@ otError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t aHeader, spinel_pr
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2770,7 +2788,7 @@ otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t aHeader, spinel_
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2798,7 +2816,7 @@ otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t aHeader,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2827,7 +2845,7 @@ otError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t aHeader,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2855,7 +2873,7 @@ otError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t aH
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -2969,7 +2987,7 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t aHeader, spinel_p
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -3036,7 +3054,7 @@ otError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t aHeader, spine
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS,
aKey));
@@ -3087,7 +3105,7 @@ otError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spin
uint8_t numEntries = 0;
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS,
aKey));
@@ -3134,7 +3152,7 @@ otError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -3342,7 +3360,7 @@ otError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_p
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -3415,7 +3433,7 @@ otError NcpBase::GetPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spin
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -3839,7 +3857,7 @@ otError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t aHeader, spinel_
otMessageGetBufferInfo(mInstance, &bufferInfo);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -3957,7 +3975,7 @@ otError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_k
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -4019,7 +4037,7 @@ otError NcpBase::GetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_k
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameBegin(aHeader));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
@@ -7415,14 +7433,16 @@ exit:
void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
{
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH);
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(OutboundFrameBegin(header));
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_DATA_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_NEST_LEGACY_ULA_PREFIX,
aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH
@@ -7436,14 +7456,16 @@ exit:
void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
mLegacyNodeDidJoin = true;
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(OutboundFrameBegin(header));
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_EUI64_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_NEST_LEGACY_JOINED_NODE,
aExtAddr->m8
+4 -2
View File
@@ -111,11 +111,13 @@ protected:
/**
* This method is called to start a new outbound frame.
*
* param[in] aHeader The spinel header byte
*
* @retval OT_ERROR_NONE Successfully started a new frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame.
*
*/
otError OutboundFrameBegin(void);
otError OutboundFrameBegin(uint8_t aHeader);
/**
* This method adds data to the current outbound frame being written.
@@ -192,7 +194,7 @@ private:
#endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
static void HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag,
NcpFrameBuffer *aNcpBuffer);
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpBuffer);
void HandleFrameRemovedFromNcpBuffer(NcpFrameBuffer::FrameTag aFrameTag);
static void HandleDatagramFromStack(otMessage *aMessage, void *aContext);
+246 -97
View File
@@ -35,6 +35,7 @@
#include "ncp_buffer.hpp"
#include "utils/wrap_string.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
namespace ot {
@@ -45,7 +46,11 @@ NcpFrameBuffer::NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLen) :
mBufferEnd(aBuffer + aBufferLen),
mBufferLength(aBufferLen)
{
otMessageQueueInit(&mMessageQueue);
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
otMessageQueueInit(&mMessageQueue[priority]);
}
otMessageQueueInit(&mWriteFrameMessageQueue);
SetFrameAddedCallback(NULL, NULL);
SetFrameRemovedCallback(NULL, NULL);
@@ -57,16 +62,19 @@ void NcpFrameBuffer::Clear(void)
otMessage *message;
// Write (InFrame) related variables
mWriteFrameStart = mBuffer;
mWriteFrameStart[kPriorityLow] = mBuffer;
mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
mWriteDirection = kUnknown;
mWriteSegmentHead = mBuffer;
mWriteSegmentTail = mBuffer;
mWriteFrameTag = kInvalidTag;
// Read (OutFrame) related variables
mReadState = kReadStateDone;
mReadState = kReadStateNotActive;
mReadFrameLength = kUnknownFrameLength;
mReadFrameStart = mBuffer;
mReadFrameStart[kPriorityLow] = mBuffer;
mReadFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
mReadSegmentHead = mBuffer;
mReadSegmentTail = mBuffer;
mReadPointer = mBuffer;
@@ -83,10 +91,13 @@ void NcpFrameBuffer::Clear(void)
otMessageFree(message);
}
while ((message = otMessageQueueGetHead(&mMessageQueue)) != NULL)
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
otMessageQueueDequeue(&mMessageQueue, message);
otMessageFree(message);
while ((message = otMessageQueueGetHead(&mMessageQueue[priority])) != NULL)
{
otMessageQueueDequeue(&mMessageQueue[priority], message);
otMessageFree(message);
}
}
}
@@ -102,84 +113,129 @@ void NcpFrameBuffer::SetFrameRemovedCallback(BufferCallback aFrameRemovedCallbac
mFrameRemovedContext = aFrameRemovedContext;
}
// Increments the buffer pointer by one byte while handling the the wrap-around at the end of buffer.
uint8_t *NcpFrameBuffer::Next(uint8_t *aBufPtr) const
// Returns an updated buffer pointer by moving forward/backward (based on `aDirection`) from `aBufPtr` by a given
// offset. The resulting buffer pointer is ensured to stay within the `mBuffer` boundaries.
uint8_t *NcpFrameBuffer::GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const
{
aBufPtr++;
return (aBufPtr == mBufferEnd) ? mBuffer : aBufPtr;
}
uint8_t *ptr = aBufPtr;
// Returns an advanced (moved forward) version of the given buffer pointer by the given offset.
uint8_t *NcpFrameBuffer::Advance(uint8_t *aBufPtr, uint16_t aOffset) const
{
aBufPtr += aOffset;
while (aBufPtr >= mBufferEnd)
switch (aDirection)
{
aBufPtr -= mBufferLength;
case kForward:
ptr += aOffset;
while (ptr >= mBufferEnd)
{
ptr -= mBufferLength;
}
break;
case kBackward:
ptr -= aOffset;
while (ptr < mBuffer)
{
ptr += mBufferLength;
}
break;
case kUnknown:
assert(false);
break;
}
return aBufPtr;
return ptr;
}
// Gets the distance between two buffer pointers (adjusts for the wrap-around).
uint16_t NcpFrameBuffer::GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr) const
// Gets the distance between two buffer pointers (adjusts for the wrap-around) given a direction (forward or backward).
uint16_t NcpFrameBuffer::GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr, Direction aDirection) const
{
size_t distance;
size_t distance = 0;
if (aEndPtr >= aStartPtr)
switch (aDirection)
{
distance = static_cast<size_t>(aEndPtr - aStartPtr);
}
else
{
distance = static_cast<size_t>(mBufferEnd - aStartPtr);
distance += static_cast<size_t>(aEndPtr - mBuffer);
case kForward:
if (aEndPtr >= aStartPtr)
{
distance = static_cast<size_t>(aEndPtr - aStartPtr);
}
else
{
distance = static_cast<size_t>(mBufferEnd - aStartPtr);
distance += static_cast<size_t>(aEndPtr - mBuffer);
}
break;
case kBackward:
if (aEndPtr <= aStartPtr)
{
distance = static_cast<size_t>(aStartPtr - aEndPtr);
}
else
{
distance = static_cast<size_t>(mBufferEnd - aEndPtr);
distance += static_cast<size_t>(aStartPtr - mBuffer);
}
break;
case kUnknown:
assert(false);
break;
}
return static_cast<uint16_t>(distance);
}
// Writes a uint16 value at the given buffer pointer (big-endian style).
void NcpFrameBuffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue)
void NcpFrameBuffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection)
{
*aBufPtr = (aValue >> 8);
*Next(aBufPtr) = (aValue & 0xff);
*GetUpdatedBufPtr(aBufPtr, 1, aDirection) = (aValue & 0xff);
}
// Reads a uint16 value at the given buffer pointer (big-endian style).
uint16_t NcpFrameBuffer::ReadUint16At(uint8_t *aBufPtr)
uint16_t NcpFrameBuffer::ReadUint16At(uint8_t *aBufPtr, Direction aDirection)
{
uint16_t value;
value = static_cast<uint16_t>((*aBufPtr) << 8);
value += *Next(aBufPtr);
value += *GetUpdatedBufPtr(aBufPtr, 1, aDirection);
return value;
}
// Writes a bytes at the write tail, discards the frame if buffer gets full.
// Writes a byte at the write tail, discards the frame if buffer gets full.
otError NcpFrameBuffer::InFrameFeedByte(uint8_t aByte)
{
otError error = OT_ERROR_NONE;
uint8_t *newTail = Next(mWriteSegmentTail);
uint8_t *newTail;
VerifyOrExit(newTail != mReadFrameStart, error = OT_ERROR_NO_BUFS);
assert(mWriteDirection != kUnknown);
*mWriteSegmentTail = aByte;
mWriteSegmentTail = newTail;
newTail = GetUpdatedBufPtr(mWriteSegmentTail, 1, mWriteDirection);
exit:
if (error != OT_ERROR_NONE)
// Ensure the `newTail` has not reached the `mWriteFrameStart` for other direction (other priority level).
if (newTail != mWriteFrameStart[(mWriteDirection == kForward) ? kBackward : kForward])
{
*mWriteSegmentTail = aByte;
mWriteSegmentTail = newTail;
}
else
{
error = OT_ERROR_NO_BUFS;
InFrameDiscard();
}
return error;
}
// This method begins a new segment (if one is not already open)
// This method begins a new segment (if one is not already open).
otError NcpFrameBuffer::InFrameBeginSegment(void)
{
otError error = OT_ERROR_NONE;
@@ -188,8 +244,8 @@ otError NcpFrameBuffer::InFrameBeginSegment(void)
// Verify that segment is not yet started (i.e., head and tail are the same).
VerifyOrExit(mWriteSegmentHead == mWriteSegmentTail);
// If this is the start of a new frame (i.e., frame start is same as segment head)
if (mWriteFrameStart == mWriteSegmentHead)
// Check if this is the start of a new frame (i.e., frame start is same as segment head).
if (mWriteFrameStart[mWriteDirection] == mWriteSegmentHead)
{
headerFlags |= kSegmentHeaderNewFrameFlag;
}
@@ -200,8 +256,8 @@ otError NcpFrameBuffer::InFrameBeginSegment(void)
SuccessOrExit(error = InFrameFeedByte(0));
}
// Write the flags at the segment head
WriteUint16At(mWriteSegmentHead, headerFlags);
// Write the flags at the segment head.
WriteUint16At(mWriteSegmentHead, headerFlags, mWriteDirection);
exit:
return error;
@@ -213,7 +269,7 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags)
uint16_t segmentLength;
uint16_t header;
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail);
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
if (segmentLength >= kSegmentHeaderSize)
{
@@ -221,10 +277,10 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags)
segmentLength -= kSegmentHeaderSize;
// Update the length and the flags in segment header (at segment head pointer).
header = ReadUint16At(mWriteSegmentHead);
header = ReadUint16At(mWriteSegmentHead, mWriteDirection);
header |= (segmentLength & kSegmentHeaderLengthMask);
header |= aHeaderFlags;
WriteUint16At(mWriteSegmentHead, header);
WriteUint16At(mWriteSegmentHead, header, mWriteDirection);
// Move the segment head to current tail (to be ready for a possible next segment).
mWriteSegmentHead = mWriteSegmentTail;
@@ -236,13 +292,15 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags)
}
}
// This method discards the current frame.
// This method discards the current frame being written.
void NcpFrameBuffer::InFrameDiscard(void)
{
otMessage *message;
VerifyOrExit(mWriteDirection != kUnknown);
// Move the write segment head and tail pointers back to frame start.
mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart;
mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
// Free any messages associated with current frame.
while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL)
@@ -250,13 +308,38 @@ void NcpFrameBuffer::InFrameDiscard(void)
otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
otMessageFree(message);
}
mWriteDirection = kUnknown;
exit:
UpdateReadWriteStartPointers();
}
otError NcpFrameBuffer::InFrameBegin(void)
// Returns `true` if in middle of writing a frame with given priority.
bool NcpFrameBuffer::InFrameIsWriting(Priority aPriority) const
{
// Discard any previous frame.
return (mWriteDirection == static_cast<Direction>(aPriority));
}
otError NcpFrameBuffer::InFrameBegin(Priority aPriority)
{
// Discard any previous unfinished frame.
InFrameDiscard();
switch (aPriority)
{
case kPriorityHigh:
mWriteDirection = kBackward;
break;
case kPriorityLow:
mWriteDirection = kForward;
break;
}
// Set up the segment head and tail
mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
return OT_ERROR_NONE;
}
@@ -264,6 +347,8 @@ otError NcpFrameBuffer::InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDa
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
// Begin a new segment (if we are not in middle of segment already).
SuccessOrExit(error = InFrameBeginSegment());
@@ -281,6 +366,9 @@ otError NcpFrameBuffer::InFrameFeedMessage(otMessage *aMessage)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aMessage != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
// Begin a new segment (if we are not in middle of segment already).
SuccessOrExit(error = InFrameBeginSegment());
@@ -297,29 +385,35 @@ exit:
otError NcpFrameBuffer::InFrameEnd(void)
{
otMessage *message;
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
// End/Close the current segment (if any).
InFrameEndSegment(kSegmentHeaderNoFlag);
// Save and use the frame start pointer as the tag associated with the frame.
mWriteFrameTag = mWriteFrameStart;
mWriteFrameTag = mWriteFrameStart[mWriteDirection];
// Update the frame start pointer to current segment head to be ready for next frame.
mWriteFrameStart = mWriteSegmentHead;
mWriteFrameStart[mWriteDirection] = mWriteSegmentHead;
// Move all the messages from the frame queue to the main queue.
while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL)
{
otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
otMessageQueueEnqueue(&mMessageQueue, message);
otMessageQueueEnqueue(&mMessageQueue[mWriteDirection], message);
}
if (mFrameAddedCallback != NULL)
{
mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, this);
mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, static_cast<Priority>(mWriteDirection), this);
}
return OT_ERROR_NONE;
mWriteDirection = kUnknown;
exit:
return error;
}
NcpFrameBuffer::FrameTag NcpFrameBuffer::InFrameGetLastTag(void) const
@@ -327,9 +421,22 @@ NcpFrameBuffer::FrameTag NcpFrameBuffer::InFrameGetLastTag(void) const
return mWriteFrameTag;
}
bool NcpFrameBuffer::HasFrame(Priority aPriority) const
{
return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority];
}
bool NcpFrameBuffer::IsEmpty(void) const
{
return (mReadFrameStart == mWriteFrameStart);
return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow);
}
void NcpFrameBuffer::OutFrameSelectReadDirection(void)
{
if (mReadState == kReadStateNotActive)
{
mReadDirection = HasFrame(kPriorityHigh) ? kBackward : kForward;
}
}
// Start/Prepare a new segment for reading.
@@ -344,24 +451,24 @@ otError NcpFrameBuffer::OutFramePrepareSegment(void)
mReadSegmentHead = mReadSegmentTail;
// Ensure there is something to read (i.e. segment head is not at start of frame being written).
VerifyOrExit(mReadSegmentHead != mWriteFrameStart, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(mReadSegmentHead != mWriteFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
// Read the segment header.
header = ReadUint16At(mReadSegmentHead);
header = ReadUint16At(mReadSegmentHead, mReadDirection);
// Check if this segment is the start of a frame.
if (header & kSegmentHeaderNewFrameFlag)
{
// Ensure that this segment is start of current frame, otherwise the current frame is finished.
VerifyOrExit(mReadSegmentHead == mReadFrameStart, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(mReadSegmentHead == mReadFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
}
// Find tail/end of current segment.
mReadSegmentTail = Advance(mReadSegmentHead,
kSegmentHeaderSize + (header & kSegmentHeaderLengthMask));
mReadSegmentTail = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask),
mReadDirection);
// Update the current read pointer to skip the segment header.
mReadPointer = Advance(mReadSegmentHead, kSegmentHeaderSize);
mReadPointer = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize, mReadDirection);
// Check if there are data bytes to be read in this segment (i.e. read pointer not at the tail).
if (mReadPointer != mReadSegmentTail)
@@ -399,15 +506,15 @@ otError NcpFrameBuffer::OutFramePrepareMessage(void)
uint16_t header;
// Read the segment header
header = ReadUint16At(mReadSegmentHead);
header = ReadUint16At(mReadSegmentHead, mReadDirection);
// Ensure that the segment header indicates that there is an associated message or return `NotFound` error.
VerifyOrExit((header & kSegmentHeaderMessageIndicatorFlag) != 0, error = OT_ERROR_NOT_FOUND);
// Update the current message from the queue.
mReadMessage = (mReadMessage == NULL) ?
otMessageQueueGetHead(&mMessageQueue) :
otMessageQueueGetNext(&mMessageQueue, mReadMessage);
otMessageQueueGetHead(&mMessageQueue[mReadDirection]) :
otMessageQueueGetNext(&mMessageQueue[mReadDirection], mReadMessage);
VerifyOrExit(mReadMessage != NULL, error = OT_ERROR_NOT_FOUND);
@@ -456,20 +563,25 @@ otError NcpFrameBuffer::OutFrameBegin(void)
{
otError error = OT_ERROR_NONE;
mReadMessage = NULL;
VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
OutFrameSelectReadDirection();
// Move the segment head and tail to start of frame.
mReadSegmentHead = mReadSegmentTail = mReadFrameStart;
mReadSegmentHead = mReadSegmentTail = mReadFrameStart[mReadDirection];
mReadMessage = NULL;
// Prepare the current segment for reading.
error = OutFramePrepareSegment();
exit:
return error;
}
bool NcpFrameBuffer::OutFrameHasEnded(void)
{
return (mReadState == kReadStateDone);
return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive);
}
uint8_t NcpFrameBuffer::OutFrameReadByte(void)
@@ -479,6 +591,10 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void)
switch (mReadState)
{
case kReadStateNotActive:
// Fall through
case kReadStateDone:
retval = kReadByteAfterFrameHasEnded;
@@ -487,9 +603,9 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void)
case kReadStateInSegment:
// Read a byte from current read pointer and move the read pointer forward.
// Read a byte from current read pointer and move the read pointer by 1 byte in the read direction.
retval = *mReadPointer;
mReadPointer = Next(mReadPointer);
mReadPointer = GetUpdatedBufPtr(mReadPointer, 1, mReadDirection);
// Check if at end of current segment.
if (mReadPointer == mReadSegmentTail)
@@ -508,7 +624,7 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void)
case kReadStateInMessage:
// Read a byte from current read pointer and move the read pointer forward.
// Read a byte from current read pointer and move the read pointer by 1 byte.
retval = *mReadPointer;
mReadPointer++;
@@ -553,23 +669,25 @@ otError NcpFrameBuffer::OutFrameRemove(void)
VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
OutFrameSelectReadDirection();
// Save the frame start pointer as the tag associated with the frame being removed.
tag = mReadFrameStart;
tag = mReadFrameStart[mReadDirection];
// Begin at the start of current frame and move through all segments.
bufPtr = mReadFrameStart;
bufPtr = mReadFrameStart[mReadDirection];
while (bufPtr != mWriteFrameStart)
while (bufPtr != mWriteFrameStart[mReadDirection])
{
// Read the segment header
header = ReadUint16At(bufPtr);
header = ReadUint16At(bufPtr, mReadDirection);
// If the current segment defines a new frame, and it is not the start of current frame, then we have reached
// end of current frame.
if (header & kSegmentHeaderNewFrameFlag)
{
if (bufPtr != mReadFrameStart)
if (bufPtr != mReadFrameStart[mReadDirection])
{
break;
}
@@ -578,31 +696,55 @@ otError NcpFrameBuffer::OutFrameRemove(void)
// If current segment has an appended message, remove it from message queue and free it.
if (header & kSegmentHeaderMessageIndicatorFlag)
{
if ((message = otMessageQueueGetHead(&mMessageQueue)) != NULL)
if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != NULL)
{
otMessageQueueDequeue(&mMessageQueue, message);
otMessageQueueDequeue(&mMessageQueue[mReadDirection], message);
otMessageFree(message);
}
}
// Move the pointer to next segment.
bufPtr = Advance(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask));
bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
}
mReadFrameStart = bufPtr;
mReadFrameStart[mReadDirection] = bufPtr;
mReadState = kReadStateDone;
UpdateReadWriteStartPointers();
mReadState = kReadStateNotActive;
mReadFrameLength = kUnknownFrameLength;
if (mFrameRemovedCallback != NULL)
{
mFrameRemovedCallback(mFrameRemovedContext, tag, this);
mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast<Priority>(mReadDirection), this);
}
exit:
return error;
}
void NcpFrameBuffer::UpdateReadWriteStartPointers(void)
{
// If there is no fully written high priority frame, and not in middle of writing a new frame either.
if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh))
{
// Move the high priority pointers to be right behind the low priority start.
mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward);
mReadFrameStart[kPriorityHigh] = mWriteFrameStart[kPriorityHigh];
ExitNow();
}
// If there is no fully written low priority frame, and not in middle of writing a new frame either.
if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow))
{
// Move the low priority pointers to be 1 byte after the high priority start.
mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward);
mReadFrameStart[kPriorityLow] = mWriteFrameStart[kPriorityLow];
}
exit:
return;
}
uint16_t NcpFrameBuffer::OutFrameGetLength(void)
{
@@ -616,20 +758,22 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void)
VerifyOrExit(!IsEmpty(), frameLength = 0);
OutFrameSelectReadDirection();
// Calculate frame length by adding length of all segments and messages within the current frame.
bufPtr = mReadFrameStart;
bufPtr = mReadFrameStart[mReadDirection];
while (bufPtr != mWriteFrameStart)
while (bufPtr != mWriteFrameStart[mReadDirection])
{
// Read the segment header
header = ReadUint16At(bufPtr);
header = ReadUint16At(bufPtr, mReadDirection);
// If the current segment defines a new frame, and it is not the start of current frame, then we have reached
// end of current frame.
if (header & kSegmentHeaderNewFrameFlag)
{
if (bufPtr != mReadFrameStart)
if (bufPtr != mReadFrameStart[mReadDirection])
{
break;
}
@@ -639,8 +783,8 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void)
if (header & kSegmentHeaderMessageIndicatorFlag)
{
message = (message == NULL) ?
otMessageQueueGetHead(&mMessageQueue) :
otMessageQueueGetNext(&mMessageQueue, message);
otMessageQueueGetHead(&mMessageQueue[mReadDirection]) :
otMessageQueueGetNext(&mMessageQueue[mReadDirection], message);
if (message != NULL)
{
@@ -652,22 +796,27 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void)
frameLength += (header & kSegmentHeaderLengthMask);
// Move the pointer to next segment.
bufPtr = Advance(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask));
bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
}
// Remember the calculated frame length for current frame.
mReadFrameLength = frameLength;
// Remember the calculated frame length for current active frame.
if (mReadState != kReadStateNotActive)
{
mReadFrameLength = frameLength;
}
exit:
return frameLength;
}
NcpFrameBuffer::FrameTag NcpFrameBuffer::OutFrameGetTag(void) const
NcpFrameBuffer::FrameTag NcpFrameBuffer::OutFrameGetTag(void)
{
OutFrameSelectReadDirection();
// If buffer is empty use `kInvalidTag`, otherwise use the frame start pointer as the tag associated with
// current out frame being read
return IsEmpty() ? kInvalidTag : mReadFrameStart;
return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection];
}
} // namespace ot
+223 -104
View File
@@ -39,11 +39,33 @@
namespace ot {
/**
* This class implements a buffer/queue for storing NCP frames.
*
* A frame can consist of a sequence of data bytes and/or the content of an `otMessage` or a combination of the two.
* NcpFrameBuffer implements priority FIFO logic for storing and reading frames. Two priority levels of high and low
* are supported. Within same priority level first-in-first-out order is preserved. High priority frames are read
* ahead of the low priority ones.
*
*/
class NcpFrameBuffer
{
public:
/**
* Defines the frame tag type. Frame tags can be compared with one another using operator `==`.
* Defines the priority of frame. High priority frames are read before low priority frames. Within same priority
* level FIFO order is preserved.
*
*/
enum Priority
{
kPriorityLow = 0, //< Indicates low/normal priority for a frame.
kPriorityHigh = 1, //< Indicates high priority for a frame.
};
/**
* Defines the (abstract) frame tag type. Frame tags can be compared with one another using operator `==`.
*
*/
typedef const void *FrameTag;
@@ -58,18 +80,19 @@ public:
* Defines a function pointer callback which is invoked to inform a change in `NcpFrameBuffer` either when a new
* frame is added/written to `NcpFrameBuffer` or when a frame is removed from `NcpFrameBuffer`.
*
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aTag The tag associated with the frame which is added or removed.
* @param[in] aNcpFrameBuffer A pointer to the `NcpFrameBuffer`.
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aTag The tag associated with the frame which is added or removed.
* @param[in] aPriority The priority of frame.
* @param[in] aNcpFrameBuffer A pointer to the `NcpFrameBuffer`.
*
*/
typedef void (*BufferCallback)(void *aContext, FrameTag aTag, NcpFrameBuffer *aNcpFrameBuffer);
typedef void (*BufferCallback)(void *aContext, FrameTag aTag, Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer);
/**
* This constructor creates an NCP frame buffer.
*
* @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer.
* @param[in] aBufferLength The buffer size (in bytes).
* @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer.
* @param[in] aBufferLength The buffer size (in bytes).
*
*/
NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLength);
@@ -85,8 +108,8 @@ public:
*
* Subsequent calls to this method will overwrite the previous callback and its context.
*
* @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer.
* @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback.
* @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer.
* @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback.
*
*/
void SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext);
@@ -96,8 +119,8 @@ public:
*
* Subsequent calls to this method will overwrite the previous callback and its context.
*
* @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer.
* @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback.
* @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer.
* @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback.
*
*/
void SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext);
@@ -105,40 +128,54 @@ public:
/**
* This method begins a new input frame to be added/written to the frame buffer.
* If there is a previous frame being written (`InFrameEnd()` has not yet been called on the frame), this method
* will discard and clear the previous unfinished frame.
* If there is a previous frame being written (for which `InFrameEnd()` has not yet been called), calling
* `InFrameBegin()` will discard and clear the previous unfinished frame.
*
* @retval OT_ERROR_NONE Successfully started a new frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame.
* @param[in] aPriority Priority level of new input frame.
*
* @retval OT_ERROR_NONE Successfully started a new frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame.
*
*/
otError InFrameBegin(void);
otError InFrameBegin(Priority aPriority);
/**
* This method adds data to the current input frame being written to the buffer.
* This method adds data to the current input frame being written.
*
* If no buffer space is available, this method will discard and clear the frame before returning an error status.
* Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
* method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
*
* @param[in] aDataBuffer A pointer to data buffer.
* @param[in] aDataBufferLength The length of the data buffer.
* If no buffer space is available, this method will discard and clear the current input frame being written before
* returning the error status `OT_ERROR_NO_BUFS`.
*
* @retval OT_ERROR_NONE Successfully added new data to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data.
* @param[in] aDataBuffer A pointer to data buffer.
* @param[in] aDataBufferLength The length of the data buffer.
*
* @retval OT_ERROR_NONE Successfully added new data to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data.
* @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame.
*
*/
otError InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength);
/**
* This method adds a message to the current input frame being written to the buffer.
* This method adds a message to the current input frame being written.
*
* Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
* method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
*
* If no buffer space is available, this method will discard and clear the frame before returning error status
* `OT_ERROR_NO_BUFS`.
*
* If no buffer space is available, this method will discard and clear the frame before returning an error status.
* In case of success, the passed-in message @p aMessage will be owned by the frame buffer instance and will be
* freed when either the the frame is removed or discarded.
* freed when either the the frame is removed or discarded. In case of failure @p aMessage remains unchanged.
*
* @param[in] aMessage A message to be added to current frame.
* @param[in] aMessage A message to be added to current frame.
*
* @retval OT_ERROR_NONE Successfully added the message to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message.
* @retval OT_ERROR_NONE Successfully added the message to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message.
* @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame.
* @retval OT_ERROR_INVALID_ARGS If @p aMessage is NULL.
*
*/
otError InFrameFeedMessage(otMessage *aMessage);
@@ -146,10 +183,15 @@ public:
/**
* This method finalizes/ends the current input frame being written to the buffer.
*
* If no buffer space is available, this method will discard and clear the frame before returning an error status.
* Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
* method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
* If no buffer space is available, this method will discard and clear the frame before returning error status
* `OT_ERROR_NO_BUFS`.
*
* @retval OT_ERROR_NONE Successfully added the message to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message.
* @retval OT_ERROR_NONE Successfully added the message to the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message.
* @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame.
*
*/
otError InFrameEnd(void);
@@ -157,7 +199,7 @@ public:
/**
* This method returns the tag assigned to last successfully written/added frame to NcpBuffer (i.e., last input
* frame for which `InFrameEnd()` was called and returned success status). The tag is a unique value (within
* currently queued frames) associated with each frame in the `NcpFrameBuffer`. The tag can be used to identify the
* currently queued frames) associated with a frame in the `NcpFrameBuffer`. The tag can be used to identify the
* same frame when it is read and removed from the NcpBuffer. Tags can be compared using operator `==`.
*
* @returns The tag of last successfully written frame, or `kInvalidTag` if no frame is written so far.
@@ -165,25 +207,28 @@ public:
FrameTag InFrameGetLastTag(void) const;
/**
* This method checks if the buffer is empty. An non-empty buffer contains at least one full frame for reading.
* This method checks if the buffer is empty. A non-empty buffer contains at least one full frame for reading.
*
* @retval true Buffer is not empty and contains at least one full frame for reading.
* @retval false Buffer is empty and contains no frame for reading.
* @retval TRUE Buffer is not empty and contains at least one full frame for reading.
* @retval FALSE Buffer is empty and contains no frame for reading.
*
*/
bool IsEmpty(void) const;
/**
* This method begins/prepares a new output frame to be read from the frame buffer.
* This method begins/prepares an output frame to be read from the frame buffer if there is no current active output
* frame, or resets the read offset if there is a current active output frame.
*
* The NCP buffer maintains a read offset for the current frame being read. Before reading any bytes from the frame
* this method should be called to prepare the frame and set the read offset.
*
* If part of current frame has already been read, a sub-sequent call to this method will reset the read offset
* back to beginning of current output frame.
* If part or even all of current frame has been read, a sub-sequent call to this method will reset the read
* offset back to beginning of current output frame (note that the current output frame will remain unchanged even
* in case where a higher priority frame was written to buffer while reading current output frame). A prepared
* output frame will stay active as current output frame until it is explicitly removed using `OutFrameRemove()`.
*
* @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading.
* @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading.
* @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading.
* @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading.
*
*/
otError OutFrameBegin(void);
@@ -191,11 +236,13 @@ public:
/**
* This method checks if the current output frame (being read) has ended.
*
* The NCP buffer maintains a read offset for the current output frame being read. This method returns true if the
* read offset is at the end of the frame and there are no more bytes available to read from current frame.
* The NCP buffer maintains a read offset for the current output frame being read. This method returns `true` if
* the read offset has reached the end of the frame and there are no more bytes available to read, or if there is
* no currently active output frame.
*
* @retval true Frame has ended (no more bytes available to read from current output frame).
* @retval false Frame has data (can read more bytes from current output frame).
* @retval TRUE Frame has ended (no more bytes available to read from current output frame), or
* there is currently no prepared/active output frame.
* @retval FALSE Frame still has more data to read.
*
*/
bool OutFrameHasEnded(void);
@@ -207,72 +254,74 @@ public:
* the next byte from the current frame and moves the read offset forward. If read offset is already at the end
* current output frame, this method returns zero.
*
* @returns The next byte from the current output frame or zero if frame has ended.
* @returns The next byte from the current output frame or zero if current output frame has ended or there is
* prepared/active output from.
*
*/
uint8_t OutFrameReadByte(void);
/**
* This method reads bytes from the current output frame.
* This method reads and copies bytes from the current output frame into a given buffer.
*
* The NCP buffer maintains a read offset for the current output frame being read. This method attempts to read
* the given number of bytes (@p aDataBufferLength) from the current frame and copies the bytes into the given
* data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are less bytes
* remaining in current frame, the available bytes are read/copied. This methods returns the actual number of bytes
* read.
* data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are fewer bytes
* remaining in current frame than the request @p aReadLength, the available bytes are read/copied. This methods
* returns the actual number of bytes read.
*
* @param[in] aDataBuffer A pointer to a data buffer.
* @param[in] aReadLength Number of bytes to read.
* @param[in] aDataBuffer A pointer to a data buffer.
* @param[in] aReadLength Number of bytes to read.
*
* @returns The number of bytes read and copied into data buffer.
* @returns The number of bytes read and copied into data buffer.
*
*/
uint16_t OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer);
/**
* This method removes the current/front output frame from the buffer.
* This method removes the current or front output frame from the buffer.
*
* The NCP buffer stores the frames in FIFO order. This method removes the front frame (which may be the current
* output frame being read) from the buffer. There is no need to prepare/begin reading the current frame before
* removing it, so the front frame can be removed without a previous call to `OutFrameBegin()`.
* If there is an active output from being read (an output frame was prepared earlier with a successful call to
* `OutFrameBegin()`), this method removes the current active output frame. If there is no current active frame,
* the front frame in the queue (the next frame which would have been read) will be removed.
*
* When a frame is removed all its associated messages will be freed.
*
* If the remove operation is successful, this method will invoke the `FrameRemovedCallback` (if provided).
*
* @retval OT_ERROR_NONE Successfully removed the front frame.
* @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove.
* @retval OT_ERROR_NONE Successfully removed the front frame.
* @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove.
*
*/
otError OutFrameRemove(void);
/**
* This method returns the number of bytes (length) of current/front frame in the NCP frame buffer.
* This method returns the number of bytes (length) of current or front frame in the NCP frame buffer.
*
* The NCP buffer stores the frames in FIFO order. This method returns the length of the front frame (which may
* be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current
* frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`.
* If there is an active output from being read (an output frame was prepared earlier with successful call to
* `OutFrameBegin()`), this method returns the length of the current output frame. If there is no current active
* frame, the length of the front frame in the queue (the next frame which would have been read) will be returned.
*
* If there is no frame in buffer, this method returns zero.
*
* @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer.
* @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer.
*
*/
uint16_t OutFrameGetLength(void);
/**
* This method returns the tag value associated to current/front frame in the NCP frame buffer.
* This method returns the tag value associated to current or front frame in the NCP frame buffer.
*
* The NCP buffer stores the frames in FIFO order. This method returns the tag of the front frame (which may
* be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current
* frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`.
* If there is an active output from being read (an output frame was prepared earlier with successful call to
* `OutFrameBegin()`), this method returns the tag associated with current output frame. If there is no current
* active frame, the tag associated with the front frame in the queue (the next frame which would have been read)
* will be returned.
*
* If there is no frame in buffer, this method returns `kInvalidTag`.
*
* @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer.
* @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer.
*
*/
FrameTag OutFrameGetTag(void) const;
FrameTag OutFrameGetTag(void);
private:
@@ -280,19 +329,20 @@ private:
* NcpFrameBuffer Implementation
* -----------------------------
*
* NcpFrameBuffer internally stores a frame as a sequence of data segments. The data segments are stored in the
* the main buffer `mBuffer`. mBuffer is utilized as a circular buffer.
* NcpFrameBuffer internally stores a frame as a sequence of data segments. Each segment stores a portion of frame.
* The data segments are stored in the the main buffer `mBuffer`. mBuffer is utilized as a circular buffer.
* Messages (which are added using `InFrameFeedMessaged()`) are not copied in the `mBuffer` but instead are
* enqueued in a message queue `mMessageQueue`.
* The content of messages (which are added using `InFrameFeedMessage()`) are not directly copied in the `mBuffer`
* but instead they are enqueued in a message queue `mMessageQueue`.
*
* The data segments include a header before the data portion. The header is 2 bytes long is formated as follows
* Every data segments starts with a header before the data portion. The header is 2 bytes long with the following
* format:
*
* Bit 0-13: Give the length of the data segment (max segment len is 2^14 = 16,384 bytes).
* Bit 14: Flag bit set to indicate that this segment has an associated `Message` (appended to its end).
* Bit 15: Flag bit set to indicate that this segment defines the start of a new frame.
*
* Bit 15 Bit 14 Bits: 0 - 13
* Bit 15 Bit 14 Bits: 0 - 13
* +--------------+--------------+--------------------------------------------------------+
* | New Frame | Has Message | Length of segment (excluding the header) |
* +--------------+--------------+--------------------------------------------------------+
@@ -311,7 +361,7 @@ private:
* This frame is stored as two segments:
*
* - Segment #1 contains "HelloThere" with a header of `0xC00A` which shows that this segment contains 10 data
* bytes, and it starts a new frame, and also must include a message from the message queue.
* bytes, and it starts a new frame, and also must includes an appended message from the message queue.
*
* - Segment #2 contains "Bye" with a header value of `0x0003` showing length of 3 and no appended message.
*
@@ -322,24 +372,77 @@ private:
* Segment #1 Header Segment #2 Header
*
*
* Buffer pointers:
* NcpFrameBuffer uses the `mBuffer` as a circular/ring buffer. To support two frame priorities the buffer is
* divided in two high-priority and low-priority regions. The high priority frames are stored in buffer in backward
* direction while the low-priority frames use the buffer in forward direction. This model ensures the available
* buffer space is utilized efficiently between all frame types.
*
* mReadFrameStart[kPriorityLow]
* |
* | mWriteFrameStart[kPriorityLow]
* | |
* V Low Priority Frames V
* --------------+------------------------------+------------------------------+------------------
* ... | <-------- | --------> | ...
* --------------+------------------------------+------------------------------+------------------
* ^ High Priority Frames ^
* | |
* | mReadFrameStart[kPriorityHigh]
* |
* mWriteFrameStart[kPriorityHigh]
*
*
*
* When frames are removed, if possible, the `mReadFrameStart` and `mWriteFrameStart` pointers of two priority
* levels are moved closer to avoid gaps.
*
* For an output frame (frame being read), NcpFrameBuffer maintains a `ReadState` along with a set of pointers
* into the buffer:
*
* mReadFrameStart[priority]: Start of current/front frame.
* |
* | mReadSegmentHead: Start of current segment of active output frame.
* | |
* | | mReadPointer: Pointer to next byte to read in current segment.
* | | |
* | | | mReadSegmentTail: End of current segment.
* | | | |
* V V V V
* ---------+------------+--------------------------+------+----------------+-----------------------------------
* ... | Segment 1 | Segment 2 | ... | Last Segment | ... (possible) next frame
* ---------+------------+--------------------------+------+----------------+-----------------------------------
* \ | | /
* | \------------v-----------/ |
* | Current Segment |
* | |
* \---------------------------V-----------------------------/
* Current OutFrame (being read)
*
* Note that the diagram above shows the pointers for a low-priority frame (with pointers increasing in forward
* direction).
*
* The `ReadState` indicates the state of current output frame and its read offset (e.g., read offset is in middle
* of a segment or it is in an appended message, or we are done with entire frame).
*
* For a input frame (frame being written), the following pointers are maintained:
*
* mWriteFrameWrite[priority]: Start of current/next frame being written.
* |
* | mWriteSegmentHead: Start of current segment of active input frame.
* | |
* | | mWriteSegmentTail: Pointer to next byte to write in current segment.
* | | |
* | | |
* | | |
* V V V
* ------------------+------------------+-------------------------------------------------------------------
* Previous Frames | Segment 1 | Segment 2 : . . .
* ------------------+------------------+-------------------------------------------------------------------
* \ /
* | |
* \------------------V-----------------/
* Current InFrame (being written)
*
* mReadFrameStart
* |
* | mReadSegmentHead mWriteFrameStart
* | | |
* | | mReadPointer | mWriteSegmentHead
* | | | | |
* mBuffer | | | mReadSegmentTail | | mWriteSegmentTail mBufferEnd
* | | | | | | | | |
* V V V V V V V V V
* +-------+---------+---------+-------+--------------------------+---------+---------+----------------------+-
* | ... | Seg 1 | Seg 2 | Seg 3 | . . . | Seg 1 | Seg 2 : . . . |
* +-------+---------+---------+-------+---------------------------+---------+--------+----------------------+-
* \ \ / / \ /
* | Cur segment | | |
* | | | |
* Current OutFrame (being read) Current InFrame (being written)
*
*/
@@ -354,29 +457,43 @@ private:
kSegmentHeaderNoFlag = 0, // No flags are set.
kSegmentHeaderNewFrameFlag = (1 << 15), // Indicates that this segment starts a new frame.
kSegmentHeaderMessageIndicatorFlag = (1 << 14), // Indicates this segment ends with a Message.
kNumPrios = (kPriorityHigh + 1), // Number of priorities.
};
enum ReadState
{
kReadStateInSegment, // In middle of a data segment while reading current (out) frame.
kReadStateInMessage, // In middle of a message while reading current (out) frame.
kReadStateDone, // Current (out) frame is read fully.
kReadStateNotActive, // No current prepared out frame.
kReadStateInSegment, // In middle of a data segment while reading current frame.
kReadStateInMessage, // In middle of a message while reading current frame.
kReadStateDone, // Current (out) frame is read fully.
};
enum Direction
{
kForward = kPriorityLow,
kBackward = kPriorityHigh,
kUnknown,
};
// Private methods
uint8_t * Next(uint8_t *aBufferPtr) const;
uint8_t * Advance(uint8_t *aBufPtr, uint16_t aOffset) const;
uint16_t GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr) const;
uint8_t * GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const;
uint16_t GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr, Direction aDirection) const;
uint16_t ReadUint16At(uint8_t *aBufPtr);
void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue);
uint16_t ReadUint16At(uint8_t *aBufPtr, Direction aDirection);
void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection);
bool HasFrame(Priority aPriority) const;
void UpdateReadWriteStartPointers(void);
otError InFrameFeedByte(uint8_t aByte);
otError InFrameBeginSegment(void);
void InFrameEndSegment(uint16_t aSegmentHeaderFlags);
void InFrameDiscard(void);
bool InFrameIsWriting(Priority aPriority) const;
void OutFrameSelectReadDirection(void);
otError OutFramePrepareSegment(void);
void OutFrameMoveToNextSegment(void);
otError OutFramePrepareMessage(void);
@@ -391,18 +508,20 @@ private:
BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed.
void * mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`.
otMessageQueue mMessageQueue; // Main message queue.
otMessageQueue mMessageQueue[kNumPrios]; // Main message queues.
Direction mWriteDirection; // Direction (priority) for 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 * mWriteFrameStart[kNumPrios]; // 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.
FrameTag mWriteFrameTag; // Tag associated with last successfully written frame.
Direction mReadDirection; // Direction (priority) for current frame being read.
ReadState mReadState; // Read state.
uint16_t mReadFrameLength; // Length of current frame being read.
uint8_t * mReadFrameStart; // Pointer to start of current frame being read.
uint8_t * mReadFrameStart[kNumPrios]; // 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).
+3 -1
View File
@@ -246,10 +246,12 @@ void NcpSpi::SpiTransactionProcess(void)
}
}
void NcpSpi::HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpFrameBuffer)
void NcpSpi::HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag,
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer)
{
OT_UNUSED_VARIABLE(aNcpFrameBuffer);
OT_UNUSED_VARIABLE(aTag);
OT_UNUSED_VARIABLE(aPriority);
static_cast<NcpSpi *>(aContext)->mPrepareTxFrameTask.Post();
}
+1 -1
View File
@@ -82,7 +82,7 @@ private:
void SpiTransactionProcess(void);
static void HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag,
NcpFrameBuffer *aNcpFrameBuffer);
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer);
static void PrepareTxFrame(Tasklet &aTasklet);
void PrepareTxFrame(void);
+2 -1
View File
@@ -106,10 +106,11 @@ NcpUart::NcpUart(otInstance *aInstance):
}
void NcpUart::HandleFrameAddedToNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag,
NcpFrameBuffer *aNcpFrameBuffer)
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer)
{
OT_UNUSED_VARIABLE(aNcpFrameBuffer);
OT_UNUSED_VARIABLE(aTag);
OT_UNUSED_VARIABLE(aPriority);
static_cast<NcpUart *>(aContext)->HandleFrameAddedToNcpBuffer();
}
+1 -1
View File
@@ -105,7 +105,7 @@ private:
static void HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength);
static void HandleError(void *context, otError aError, uint8_t *aBuf, uint16_t aBufLength);
static void HandleFrameAddedToNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag,
NcpFrameBuffer *aNcpFrameBuffer);
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer);
Hdlc::Encoder mFrameEncoder;
Hdlc::Decoder mFrameDecoder;
+409 -108
View File
@@ -42,9 +42,10 @@ namespace ot {
// This module implements unit-test for NcpFrameBuffer class.
// Test related constants:
enum
{
kTestBufferSize = 2500,
kTestBufferSize = 800,
kTestIterationAttemps = 10000,
kTagArraySize = 1000,
};
@@ -66,36 +67,54 @@ struct CallbackContext
CallbackContext sContext;
NcpFrameBuffer::FrameTag sTagHistoryArray[kTagArraySize];
uint32_t sTagHistoryHead = 0;
uint32_t sTagHistoryTail = 0;
enum
{
kNumPrios = 2, // Number of priority levels.
kTestFrame1Size = sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) + sizeof(sHelloText),
kTestFrame2Size = sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText),
kTestFrame3Size = sizeof(sMysteryText),
kTestFrame4Size = sizeof(sOpenThreadText),
};
NcpFrameBuffer::FrameTag sTagHistoryArray[kNumPrios][kTagArraySize];
uint32_t sTagHistoryHead[kNumPrios] = {0};
uint32_t sTagHistoryTail[kNumPrios] = {0};
NcpFrameBuffer::FrameTag sExpectedRemovedTag = NcpFrameBuffer::kInvalidTag;
void ClearTagHistory(void)
{
sTagHistoryHead = sTagHistoryTail;
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
sTagHistoryHead[priority] = sTagHistoryTail[priority];
}
}
void AddTagToHistory(NcpFrameBuffer::FrameTag aTag)
void AddTagToHistory(NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority)
{
sTagHistoryArray[sTagHistoryTail] = aTag;
uint8_t priority = static_cast<uint8_t>(aPriority);
if (++sTagHistoryTail == kTagArraySize)
sTagHistoryArray[priority][sTagHistoryTail[priority]] = aTag;
if (++sTagHistoryTail[priority] == kTagArraySize)
{
sTagHistoryTail = 0;
sTagHistoryTail[priority] = 0;
}
VerifyOrQuit(sTagHistoryTail != sTagHistoryHead, "Ran out of space in `TagHistoryArray`, increase its size.");
VerifyOrQuit(sTagHistoryTail[priority] != sTagHistoryHead[priority],
"Ran out of space in `TagHistoryArray`, increase its size.");
}
void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag)
void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority)
{
VerifyOrQuit(sTagHistoryHead != sTagHistoryTail, "Tag history is empty,");
VerifyOrQuit(aTag == sTagHistoryArray[sTagHistoryHead], "Removed tag does not match the added one");
uint8_t priority = static_cast<uint8_t>(aPriority);
if (++sTagHistoryHead == kTagArraySize)
VerifyOrQuit(sTagHistoryHead[priority] != sTagHistoryTail[priority], "Tag history is empty,");
VerifyOrQuit(aTag == sTagHistoryArray[priority][sTagHistoryHead[priority]], "Removed tag does not match the added one");
if (++sTagHistoryHead[priority] == kTagArraySize)
{
sTagHistoryHead = 0;
sTagHistoryHead[priority] = 0;
}
if (sExpectedRemovedTag != NcpFrameBuffer::kInvalidTag)
@@ -105,7 +124,8 @@ void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag)
}
}
void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer)
void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority,
NcpFrameBuffer *aNcpBuffer)
{
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
@@ -113,19 +133,22 @@ void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameB
VerifyOrQuit(callbackContext != NULL, "Null context in the callback");
VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback");
VerifyOrQuit(aTag == aNcpBuffer->InFrameGetLastTag(), "InFrameGetLastTag() does not match the tag from callback");
AddTagToHistory(aTag);
AddTagToHistory(aTag, aPriority);
callbackContext->mFrameAddedCount++;
}
void FrameRemovedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer)
void FrameRemovedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority,
NcpFrameBuffer *aNcpBuffer)
{
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
VerifyOrQuit(aNcpBuffer != NULL, "Null NcpFrameBuffer in the callback");
VerifyOrQuit(callbackContext != NULL, "Null context in the callback");
VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback");
VerifyAndRemoveTagFromHistory(aTag);
VerifyAndRemoveTagFromHistory(aTag, aPriority);
callbackContext->mFrameRemovedCount++;
}
@@ -183,7 +206,7 @@ void ReadAndVerifyContent(NcpFrameBuffer &aNcpBuffer, const uint8_t *aContentBuf
}
}
void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer)
void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority)
{
Message *message;
CallbackContext oldContext;
@@ -194,13 +217,12 @@ void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer)
message->Write(0, sizeof(sMottoText), sMottoText);
oldContext = sContext;
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message), "InFrameFeedMessage() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
@@ -210,27 +232,24 @@ void VerifyAndRemoveFrame1(NcpFrameBuffer &aNcpBuffer)
CallbackContext oldContext = sContext;
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText)
+ sizeof(sHelloText), "GetLength() is incorrect.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer)
void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority)
{
Message *message1;
Message *message2;
@@ -246,7 +265,7 @@ void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer)
SuccessOrQuit(message2->SetLength(sizeof(sHelloText)), "Could not set the length of message.");
message2->Write(0, sizeof(sHelloText), sHelloText);
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message2), "InFrameFeedMessage() failed.");
@@ -260,25 +279,23 @@ void VerifyAndRemoveFrame2(NcpFrameBuffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText),
"GetLength() is incorrect.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect.");
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer)
void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority)
{
Message *message1;
CallbackContext oldContext = sContext;
@@ -289,7 +306,7 @@ void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer)
// An empty message with no content.
SuccessOrQuit(message1->SetLength(0), "Could not set the length of message.");
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
@@ -302,15 +319,44 @@ void VerifyAndRemoveFrame3(NcpFrameBuffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect.");
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame4(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority)
{
CallbackContext oldContext = sContext;
SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed.");
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void VerifyAndRemoveFrame4(NcpFrameBuffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect.");
ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect.");
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
@@ -352,21 +398,47 @@ void TestNcpFrameBuffer(void)
VerifyOrQuit(ncpBuffer.OutFrameGetTag() == NcpFrameBuffer::kInvalidTag, "Incorrect OutFrameTag after init.");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 2: Write a frame 1 ");
printf("\nTest 2: Write and read a single frame");
WriteTestFrame1(ncpBuffer);
DumpBuffer("\nBuffer after frame1", buffer, kTestBufferSize);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
DumpBuffer("\nBuffer after frame1 (low priority)", buffer, kTestBufferSize);
printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
VerifyAndRemoveFrame1(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
DumpBuffer("\nBuffer after frame1 (high priority)", buffer, kTestBufferSize);
printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
VerifyAndRemoveFrame1(ncpBuffer);
printf("\nIterations: ");
// Repeat this multiple times.
// Always add as low priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
}
// Always add as high priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
}
// Every 5th add as high priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer, ((j % 5) == 0) ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
@@ -376,12 +448,12 @@ void TestNcpFrameBuffer(void)
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 3: Multiple frames write and read ");
printf("\nTest 3: Multiple frames write and read (same priority)");
WriteTestFrame2(ncpBuffer);
WriteTestFrame3(ncpBuffer);
WriteTestFrame2(ncpBuffer);
WriteTestFrame2(ncpBuffer);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
DumpBuffer("\nBuffer after multiple frames", buffer, kTestBufferSize);
@@ -397,15 +469,15 @@ void TestNcpFrameBuffer(void)
{
printf("*");
WriteTestFrame2(ncpBuffer);
WriteTestFrame3(ncpBuffer);
WriteTestFrame2(ncpBuffer);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame2(ncpBuffer);
WriteTestFrame3(ncpBuffer);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
@@ -417,14 +489,75 @@ void TestNcpFrameBuffer(void)
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 4: Frame discard when buffer full and partial read restart");
printf("\nTest 4: Multiple frames write and read (mixed priority)");
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 5: Frame discard when buffer full and partial read restart");
printf("\nIterations: ");
for (j = 0; j < kTestIterationAttemps; j++)
{
WriteTestFrame2(ncpBuffer);
WriteTestFrame3(ncpBuffer);
bool frame1IsHighPriority = ((j % 3) == 0);
ncpBuffer.InFrameBegin();
printf("*");
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
ncpBuffer.InFrameBegin((j % 2) == 0 ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow);
ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText));
message = sMessagePool->New(Message::kTypeIp6, 0);
@@ -434,20 +567,11 @@ void TestNcpFrameBuffer(void)
ncpBuffer.InFrameFeedMessage(message);
// Now cause a restart the current frame and test if it's discarded ok.
WriteTestFrame2(ncpBuffer);
// Start writing a new frame in middle of an unfinished frame. Ensure the first one is discarded.
WriteTestFrame1(ncpBuffer,
frame1IsHighPriority ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow);
if (j == 0)
{
DumpBuffer("\nAfter frame gets discarded", buffer, kTestBufferSize);
printf("\nIterations: ");
}
else
{
printf("*");
}
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
// Start reading few bytes from the frame
ncpBuffer.OutFrameBegin();
@@ -456,8 +580,17 @@ void TestNcpFrameBuffer(void)
ncpBuffer.OutFrameReadByte();
// Now reset the read pointer and read/verify the frame from start.
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
if (frame1IsHighPriority)
{
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
}
else
{
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
}
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
}
@@ -465,9 +598,9 @@ void TestNcpFrameBuffer(void)
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 5: Clear() and empty buffer method tests");
printf("\nTest 6: Clear() and empty buffer method tests");
WriteTestFrame1(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
ncpBuffer.Clear();
ClearTagHistory();
@@ -480,7 +613,7 @@ void TestNcpFrameBuffer(void)
"Remove() returned incorrect error status when buffer is empty.");
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0, "OutFrameGetLength() returned non-zero length when buffer is empty.");
WriteTestFrame1(ncpBuffer);
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
@@ -492,9 +625,9 @@ void TestNcpFrameBuffer(void)
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 6: OutFrameRead() in parts\n");
printf("\nTest 7: OutFrameRead() in parts\n");
ncpBuffer.InFrameBegin();
ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow);
ncpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText));
ncpBuffer.InFrameEnd();
@@ -512,8 +645,159 @@ void TestNcpFrameBuffer(void)
}
VerifyOrQuit(readOffset == sizeof(sMottoText), "Read len does not match expected length.");
ncpBuffer.OutFrameRemove();
printf("\n -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 8: Remove a frame without reading it first");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
SuccessOrQuit(ncpBuffer.OutFrameRemove(), "Remove() failed.");
VerifyAndRemoveFrame2(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 9: Check length when front frame gets changed (a higher priority frame is added)");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame3Size, "GetLength() is incorrect.");
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 10: Active out frame remaining unchanged when a higher priority frame is written while reading it");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat test reversing frame priority orders.
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 11: Read and remove in middle of an active input frame write");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed.");
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed.");
SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed.");
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat the test reversing priorities
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow), "InFrameFeedBegin() failed.");
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed.");
SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed.");
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat the test with same priorities
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed.");
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed.");
SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed.");
VerifyAndRemoveFrame4(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 12: Check returned error status");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed.");
VerifyOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer)) == OT_ERROR_NO_BUFS, "Incorrect error status");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed.");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
// Ensure writes with starting `InFrameBegin()` fail
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 1) == OT_ERROR_INVALID_STATE, "Incorrect error status");
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE, "Incorrect error status");
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE, "Incorrect error status");
VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE, "Incorrect error status");
message = sMessagePool->New(Message::kTypeIp6, 0);
VerifyOrQuit(message != NULL, "Null Message");
SuccessOrQuit(message->SetLength(sizeof(sMysteryText)), "Could not set the length of message.");
message->Write(0, sizeof(sMysteryText), sMysteryText);
VerifyOrQuit(ncpBuffer.InFrameFeedMessage(message) == OT_ERROR_INVALID_STATE, "Incorrect error status");
SuccessOrQuit(message->Free(), "Failed to free allocated message");
VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE, "Incorrect error status");
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() failed");
VerifyOrQuit(ncpBuffer.OutFrameBegin() == OT_ERROR_NOT_FOUND, "OutFrameBegin() failed on empty queue");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() failed");
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 13: Ensure we can utilize the full buffer size when frames removed during write");
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameBegin() failed.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4), "InFrameFeedData() failed.");
SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed.");
SuccessOrQuit(ncpBuffer.OutFrameRemove(), "OutFrameRemove() failed.");
// Repeat the test with a low priority buffer write
WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow), "InFrameBegin() failed.");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4), "InFrameFeedData() failed.");
SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed.");
SuccessOrQuit(ncpBuffer.OutFrameRemove(), "OutFrameRemove() failed.");
printf(" -- PASS\n");
testFreeInstance(sInstance);
}
@@ -536,11 +820,12 @@ enum
kLensArraySize = 500, // Size of "Lengths" array.
kMaxFrameLen = 400, // Maximum frame length
kReadProbability = 50, // Probability (in percent) to randomly choose to read vs write frame
kHighPriorityProbablity = 20, // Probability (in percent) to write a high priority frame
kUseTrueRandomNumberGenerator = 1, // To use true random number generator or not.
};
uint8_t sFrameBuffer[kFuzTestBufferSize];
uint32_t sFrameBufferTailIndex = 0;
uint8_t sFrameBuffer[kNumPrios][kFuzTestBufferSize];
uint32_t sFrameBufferTailIndex[kNumPrios] = {0};
uint32_t GetRandom(uint32_t max)
{
@@ -558,25 +843,26 @@ uint32_t GetRandom(uint32_t max)
return value % max;
}
otError WriteRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer)
otError WriteRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority)
{
otError error;
uint8_t byte;
uint8_t priority = static_cast<uint8_t>(aPriority);
CallbackContext oldContext = sContext;
uint32_t tail = sFrameBufferTailIndex;
uint32_t tail = sFrameBufferTailIndex[priority];
SuccessOrExit(error = aNcpBuffer.InFrameBegin());
SuccessOrExit(error = aNcpBuffer.InFrameBegin(aPriority));
while (aLength--)
{
byte = static_cast<uint8_t>(GetRandom(256));
SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte)));
sFrameBuffer[tail++] = byte;
sFrameBuffer[priority][tail++] = byte;
}
SuccessOrExit(error = aNcpBuffer.InFrameEnd());
sFrameBufferTailIndex = tail;
sFrameBufferTailIndex[priority] = tail;
// check the callbacks
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
@@ -586,7 +872,7 @@ exit:
return error;
}
otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer)
otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer, uint8_t priority)
{
CallbackContext oldContext = sContext;
@@ -594,13 +880,13 @@ otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer)
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength, "OutFrameGetLength() does not match");
// Read and verify that the content is same as sFrameBuffer values...
ReadAndVerifyContent(aNcpBuffer, sFrameBuffer, static_cast<uint16_t>(aLength));
ReadAndVerifyContent(aNcpBuffer, sFrameBuffer[priority], static_cast<uint16_t>(aLength));
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "OutFrameRemove failed");
sFrameBufferTailIndex -= aLength;
memmove(sFrameBuffer, sFrameBuffer + aLength, sFrameBufferTailIndex);
sFrameBufferTailIndex[priority] -= aLength;
memmove(sFrameBuffer[priority], sFrameBuffer[priority] + aLength, sFrameBufferTailIndex[priority]);
// If successful check the callbacks
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
@@ -616,9 +902,9 @@ void TestFuzzNcpFrameBuffer(void)
uint8_t buffer[kFuzTestBufferSize];
NcpFrameBuffer ncpBuffer(buffer, kFuzTestBufferSize);
uint32_t lensArray[kLensArraySize]; // Keeps track of length of written frames so far
uint32_t lensArrayStart;
uint32_t lensArrayCount;
uint32_t lensArray[kNumPrios][kLensArraySize]; // Keeps track of length of written frames so far
uint32_t lensArrayStart[kNumPrios];
uint32_t lensArrayCount[kNumPrios];
sInstance = testInitInstance();
sMessagePool = &sInstance->mIp6.mMessagePool;
@@ -626,8 +912,8 @@ void TestFuzzNcpFrameBuffer(void)
memset(buffer, 0, sizeof(buffer));
memset(lensArray, 0, sizeof(lensArray));
lensArrayStart = 0;
lensArrayCount = 0;
memset(lensArrayStart, 0, sizeof(lensArrayStart));
memset(lensArrayCount, 0, sizeof(lensArrayCount));
sContext.mFrameAddedCount = 0;
sContext.mFrameRemovedCount = 0;
@@ -640,11 +926,11 @@ void TestFuzzNcpFrameBuffer(void)
{
bool shouldRead;
if (lensArrayCount == 0)
if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
{
shouldRead = false;
}
else if (lensArrayCount == kLensArraySize - 1)
else if (lensArrayCount[0] == kLensArraySize - 1 || lensArrayCount[1] == kLensArraySize - 1)
{
shouldRead = true;
}
@@ -656,25 +942,40 @@ void TestFuzzNcpFrameBuffer(void)
if (shouldRead)
{
uint32_t len = lensArray[lensArrayStart];
uint32_t len;
uint8_t priority;
lensArrayStart = (lensArrayStart + 1) % kLensArraySize;
lensArrayCount--;
priority = (lensArrayCount[NcpFrameBuffer::kPriorityHigh] != 0) ?
NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow;
printf("R%d ", len);
len = lensArray[priority][lensArrayStart[priority]];
lensArrayStart[priority] = (lensArrayStart[priority] + 1) % kLensArraySize;
lensArrayCount[priority]--;
SuccessOrQuit(ReadRandomFrame(len, ncpBuffer), "Failed to read random frame.");
printf("R%c%d ", priority == NcpFrameBuffer::kPriorityHigh ? 'H' : 'L', len);
SuccessOrQuit(ReadRandomFrame(len, ncpBuffer, priority), "Failed to read random frame.");
}
else
{
uint32_t len = GetRandom(kMaxFrameLen) + 1;
NcpFrameBuffer::Priority priority;
if (WriteRandomFrame(len, ncpBuffer) == OT_ERROR_NONE)
if (GetRandom(100) < kHighPriorityProbablity)
{
lensArray[(lensArrayStart + lensArrayCount) % kLensArraySize] = len;
lensArrayCount++;
priority = NcpFrameBuffer::kPriorityHigh;
}
else
{
priority = NcpFrameBuffer::kPriorityLow;
}
printf("W%d ", len);
if (WriteRandomFrame(len, ncpBuffer, priority) == OT_ERROR_NONE)
{
lensArray[priority][(lensArrayStart[priority] + lensArrayCount[priority]) % kLensArraySize] = len;
lensArrayCount[priority]++;
printf("W%c%d ", priority == NcpFrameBuffer::kPriorityHigh ? 'H' : 'L', len);
}
else
{
@@ -682,7 +983,7 @@ void TestFuzzNcpFrameBuffer(void)
}
}
if (lensArrayCount == 0)
if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
{
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty failed.");
printf("EMPTY ");
+16 -18
View File
@@ -43,25 +43,23 @@ extern "C" {
// Enable main functions
#define ENABLE_TEST_MAIN
#define SuccessOrQuit(ERR, MSG) \
do { \
if ((ERR) != OT_ERROR_NONE) \
{ \
fprintf(stderr, "%s FAILED: ", __FUNCTION__); \
fputs(MSG, stderr); \
exit(-1); \
} \
} while (0)
#define SuccessOrQuit(ERR, MSG) \
do { \
if ((ERR) != OT_ERROR_NONE) \
{ \
fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \
exit(-1); \
} \
} while (false)
#define VerifyOrQuit(TST, MSG) \
do { \
if (!(TST)) \
{ \
fprintf(stderr, "%s FAILED: ", __FUNCTION__); \
fputs(MSG, stderr); \
exit(-1); \
} \
} while (0)
#define VerifyOrQuit(TST, MSG) \
do { \
if (!(TST)) \
{ \
fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \
exit(-1); \
} \
} while (false)
//#define CompileTimeAssert(COND, MSG) typedef char __C_ASSERT__[(COND)?1:-1]