#include "spinel_buffer.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
namespace ot {
namespace Spinel {
const Buffer::FrameTag Buffer::kInvalidTag = NULL;
Buffer::Buffer(uint8_t *aBuffer, uint16_t aBufferLength)
: mBuffer(aBuffer)
, mBufferEnd(aBuffer + aBufferLength)
, mBufferLength(aBufferLength)
{
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
otMessageQueueInit(&mMessageQueue[priority]);
}
otMessageQueueInit(&mWriteFrameMessageQueue);
#endif
SetFrameAddedCallback(NULL, NULL);
SetFrameRemovedCallback(NULL, NULL);
Clear();
}
void Buffer::Clear(void)
{
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otMessage *message;
#endif
mWriteFrameStart[kPriorityLow] = mBuffer;
mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
mWriteDirection = kUnknown;
mWriteSegmentHead = mBuffer;
mWriteSegmentTail = mBuffer;
mWriteFrameTag = kInvalidTag;
mReadDirection = kForward;
mReadState = kReadStateNotActive;
mReadFrameLength = kUnknownFrameLength;
mReadFrameStart[kPriorityLow] = mBuffer;
mReadFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
mReadSegmentHead = mBuffer;
mReadSegmentTail = mBuffer;
mReadPointer = mBuffer;
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
mReadMessage = NULL;
mReadMessageOffset = 0;
mReadMessageTail = mMessageBuffer;
while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL)
{
otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
}
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
while ((message = otMessageQueueGetHead(&mMessageQueue[priority])) != NULL)
{
otMessageQueueDequeue(&mMessageQueue[priority], message);
otMessageFree(message);
}
}
#endif
}
void Buffer::SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext)
{
mFrameAddedCallback = aFrameAddedCallback;
mFrameAddedContext = aFrameAddedContext;
}
void Buffer::SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext)
{
mFrameRemovedCallback = aFrameRemovedCallback;
mFrameRemovedContext = aFrameRemovedContext;
}
uint8_t *Buffer::GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const
{
uint8_t *ptr = aBufPtr;
switch (aDirection)
{
case kForward:
ptr += aOffset;
while (ptr >= mBufferEnd)
{
ptr -= mBufferLength;
}
break;
case kBackward:
ptr -= aOffset;
while (ptr < mBuffer)
{
ptr += mBufferLength;
}
break;
case kUnknown:
OT_ASSERT(false);
OT_UNREACHABLE_CODE(break);
}
return ptr;
}
uint16_t Buffer::GetDistance(const uint8_t *aStartPtr, const uint8_t *aEndPtr, Direction aDirection) const
{
size_t distance = 0;
switch (aDirection)
{
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:
OT_ASSERT(false);
OT_UNREACHABLE_CODE(break);
}
return static_cast<uint16_t>(distance);
}
void Buffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection)
{
*aBufPtr = (aValue >> 8);
*GetUpdatedBufPtr(aBufPtr, 1, aDirection) = (aValue & 0xff);
}
uint16_t Buffer::ReadUint16At(uint8_t *aBufPtr, Direction aDirection)
{
uint16_t value;
value = static_cast<uint16_t>((*aBufPtr) << 8);
value += *GetUpdatedBufPtr(aBufPtr, 1, aDirection);
return value;
}
otError Buffer::InFrameAppend(uint8_t aByte)
{
otError error = OT_ERROR_NONE;
uint8_t *newTail;
OT_ASSERT(mWriteDirection != kUnknown);
newTail = GetUpdatedBufPtr(mWriteSegmentTail, 1, mWriteDirection);
if (newTail != mWriteFrameStart[(mWriteDirection == kForward) ? kBackward : kForward])
{
*mWriteSegmentTail = aByte;
mWriteSegmentTail = newTail;
}
else
{
error = OT_ERROR_NO_BUFS;
InFrameDiscard();
}
return error;
}
otError Buffer::InFrameBeginSegment(void)
{
otError error = OT_ERROR_NONE;
uint16_t headerFlags = kSegmentHeaderNoFlag;
VerifyOrExit(mWriteSegmentHead == mWriteSegmentTail, OT_NOOP);
if (mWriteFrameStart[mWriteDirection] == mWriteSegmentHead)
{
headerFlags |= kSegmentHeaderNewFrameFlag;
}
for (uint16_t i = kSegmentHeaderSize; i; i--)
{
SuccessOrExit(error = InFrameAppend(0));
}
WriteUint16At(mWriteSegmentHead, headerFlags, mWriteDirection);
exit:
return error;
}
void Buffer::InFrameEndSegment(uint16_t aSegmentHeaderFlags)
{
uint16_t segmentLength;
uint16_t header;
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
if (segmentLength >= kSegmentHeaderSize)
{
segmentLength -= kSegmentHeaderSize;
header = ReadUint16At(mWriteSegmentHead, mWriteDirection);
header |= (segmentLength & kSegmentHeaderLengthMask);
header |= aSegmentHeaderFlags;
WriteUint16At(mWriteSegmentHead, header, mWriteDirection);
mWriteSegmentHead = mWriteSegmentTail;
}
else
{
mWriteSegmentTail = mWriteSegmentHead;
}
}
void Buffer::InFrameDiscard(void)
{
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otMessage *message;
#endif
VerifyOrExit(mWriteDirection != kUnknown, OT_NOOP);
mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL)
{
otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
}
#endif
mWriteDirection = kUnknown;
exit:
UpdateReadWriteStartPointers();
}
bool Buffer::InFrameIsWriting(Priority aPriority) const
{
return (mWriteDirection == static_cast<Direction>(aPriority));
}
void Buffer::InFrameBegin(Priority aPriority)
{
InFrameDiscard();
switch (aPriority)
{
case kPriorityHigh:
mWriteDirection = kBackward;
break;
case kPriorityLow:
mWriteDirection = kForward;
break;
}
mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
}
otError Buffer::InFrameFeedByte(uint8_t aByte)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = InFrameBeginSegment());
error = InFrameAppend(aByte);
exit:
return error;
}
otError Buffer::InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = InFrameBeginSegment());
while (aDataBufferLength--)
{
SuccessOrExit(error = InFrameAppend(*aDataBuffer++));
}
exit:
return error;
}
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otError Buffer::InFrameFeedMessage(otMessage *aMessage)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aMessage != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = InFrameBeginSegment());
otMessageQueueEnqueue(&mWriteFrameMessageQueue, aMessage);
InFrameEndSegment(kSegmentHeaderMessageIndicatorFlag);
exit:
return error;
}
#endif
otError Buffer::InFrameGetPosition(WritePosition &aPosition)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = InFrameBeginSegment());
aPosition.mPosition = mWriteSegmentTail;
aPosition.mSegmentHead = mWriteSegmentHead;
exit:
return error;
}
otError Buffer::InFrameOverwrite(const WritePosition &aPosition, const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
{
otError error = OT_ERROR_NONE;
uint8_t *bufPtr;
uint16_t segmentLength;
uint16_t distance;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(aPosition.mSegmentHead == mWriteSegmentHead, error = OT_ERROR_INVALID_ARGS);
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
distance = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
VerifyOrExit(distance + aDataBufferLength <= segmentLength, error = OT_ERROR_INVALID_ARGS);
bufPtr = aPosition.mPosition;
while (aDataBufferLength > 0)
{
*bufPtr = *aDataBuffer;
aDataBuffer++;
aDataBufferLength--;
bufPtr = GetUpdatedBufPtr(bufPtr, 1, mWriteDirection);
}
exit:
return error;
}
uint16_t Buffer::InFrameGetDistance(const WritePosition &aPosition) const
{
uint16_t distance = 0;
uint16_t segmentLength;
uint16_t offset;
VerifyOrExit(mWriteDirection != kUnknown, OT_NOOP);
VerifyOrExit(aPosition.mSegmentHead == mWriteSegmentHead, OT_NOOP);
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
offset = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
VerifyOrExit(offset < segmentLength, OT_NOOP);
distance = GetDistance(aPosition.mPosition, mWriteSegmentTail, mWriteDirection);
exit:
return distance;
}
otError Buffer::InFrameReset(const WritePosition &aPosition)
{
otError error = OT_ERROR_NONE;
uint16_t segmentLength;
uint16_t offset;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(aPosition.mSegmentHead == mWriteSegmentHead, error = OT_ERROR_INVALID_ARGS);
segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
offset = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
VerifyOrExit(offset < segmentLength, error = OT_ERROR_INVALID_ARGS);
mWriteSegmentTail = aPosition.mPosition;
exit:
return error;
}
otError Buffer::InFrameEnd(void)
{
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otMessage *message;
#endif
otError error = OT_ERROR_NONE;
VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
InFrameEndSegment(kSegmentHeaderNoFlag);
mWriteFrameTag = mWriteFrameStart[mWriteDirection];
mWriteFrameStart[mWriteDirection] = mWriteSegmentHead;
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL)
{
otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
otMessageQueueEnqueue(&mMessageQueue[mWriteDirection], message);
}
#endif
if (mFrameAddedCallback != NULL)
{
mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, static_cast<Priority>(mWriteDirection), this);
}
mWriteDirection = kUnknown;
exit:
return error;
}
Buffer::FrameTag Buffer::InFrameGetLastTag(void) const
{
return mWriteFrameTag;
}
bool Buffer::HasFrame(Priority aPriority) const
{
return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority];
}
bool Buffer::IsEmpty(void) const
{
return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow);
}
void Buffer::OutFrameSelectReadDirection(void)
{
if (mReadState == kReadStateNotActive)
{
mReadDirection = HasFrame(kPriorityHigh) ? kBackward : kForward;
}
}
otError Buffer::OutFramePrepareSegment(void)
{
otError error = OT_ERROR_NONE;
uint16_t header;
while (true)
{
mReadSegmentHead = mReadSegmentTail;
VerifyOrExit(mReadSegmentHead != mWriteFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
header = ReadUint16At(mReadSegmentHead, mReadDirection);
if (header & kSegmentHeaderNewFrameFlag)
{
VerifyOrExit(mReadSegmentHead == mReadFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
}
mReadSegmentTail = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask),
mReadDirection);
mReadPointer = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize, mReadDirection);
if (mReadPointer != mReadSegmentTail)
{
mReadState = kReadStateInSegment;
ExitNow();
}
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
if (OutFramePrepareMessage() == OT_ERROR_NONE)
{
ExitNow();
}
#endif
}
exit:
if (error != OT_ERROR_NONE)
{
mReadState = kReadStateDone;
}
return error;
}
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otError Buffer::OutFramePrepareMessage(void)
{
otError error = OT_ERROR_NONE;
uint16_t header;
header = ReadUint16At(mReadSegmentHead, mReadDirection);
VerifyOrExit((header & kSegmentHeaderMessageIndicatorFlag) != 0, error = OT_ERROR_NOT_FOUND);
mReadMessage = (mReadMessage == NULL) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
: otMessageQueueGetNext(&mMessageQueue[mReadDirection], mReadMessage);
VerifyOrExit(mReadMessage != NULL, error = OT_ERROR_NOT_FOUND);
mReadMessageOffset = 0;
SuccessOrExit(error = OutFrameFillMessageBuffer());
mReadState = kReadStateInMessage;
exit:
return error;
}
otError Buffer::OutFrameFillMessageBuffer(void)
{
otError error = OT_ERROR_NONE;
int readLength;
VerifyOrExit(mReadMessage != NULL, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(mReadMessageOffset < otMessageGetLength(mReadMessage), error = OT_ERROR_NOT_FOUND);
readLength = otMessageRead(mReadMessage, mReadMessageOffset, mMessageBuffer, sizeof(mMessageBuffer));
VerifyOrExit(readLength > 0, error = OT_ERROR_NOT_FOUND);
mReadMessageOffset += readLength;
mReadMessageTail = mMessageBuffer + readLength;
mReadPointer = mMessageBuffer;
exit:
return error;
}
#endif
otError Buffer::OutFrameBegin(void)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
OutFrameSelectReadDirection();
mReadSegmentHead = mReadSegmentTail = mReadFrameStart[mReadDirection];
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
mReadMessage = NULL;
#endif
error = OutFramePrepareSegment();
exit:
return error;
}
bool Buffer::OutFrameHasEnded(void)
{
return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive);
}
uint8_t Buffer::OutFrameReadByte(void)
{
otError error;
uint8_t retval = kReadByteAfterFrameHasEnded;
switch (mReadState)
{
case kReadStateNotActive:
case kReadStateDone:
retval = kReadByteAfterFrameHasEnded;
break;
case kReadStateInSegment:
retval = *mReadPointer;
mReadPointer = GetUpdatedBufPtr(mReadPointer, 1, mReadDirection);
if (mReadPointer == mReadSegmentTail)
{
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
error = OutFramePrepareMessage();
#else
error = OT_ERROR_NOT_FOUND;
#endif
if (error != OT_ERROR_NONE)
{
IgnoreError(OutFramePrepareSegment());
}
}
break;
case kReadStateInMessage:
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
retval = *mReadPointer;
mReadPointer++;
if (mReadPointer == mReadMessageTail)
{
error = OutFrameFillMessageBuffer();
if (error != OT_ERROR_NONE)
{
IgnoreError(OutFramePrepareSegment());
}
}
#endif
break;
}
return retval;
}
uint16_t Buffer::OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer)
{
uint16_t bytesRead = 0;
for (bytesRead = 0; (bytesRead < aReadLength) && !OutFrameHasEnded(); bytesRead++)
{
*aDataBuffer++ = OutFrameReadByte();
}
return bytesRead;
}
otError Buffer::OutFrameRemove(void)
{
otError error = OT_ERROR_NONE;
uint8_t *bufPtr;
uint16_t header;
uint8_t numSegments;
FrameTag tag;
VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
OutFrameSelectReadDirection();
tag = mReadFrameStart[mReadDirection];
bufPtr = mReadFrameStart[mReadDirection];
numSegments = 0;
while (bufPtr != mWriteFrameStart[mReadDirection])
{
header = ReadUint16At(bufPtr, mReadDirection);
if (header & kSegmentHeaderNewFrameFlag)
{
if (bufPtr != mReadFrameStart[mReadDirection])
{
break;
}
}
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
if (header & kSegmentHeaderMessageIndicatorFlag)
{
otMessage *message;
if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != NULL)
{
otMessageQueueDequeue(&mMessageQueue[mReadDirection], message);
otMessageFree(message);
}
}
#endif
bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
numSegments++;
OT_ASSERT(numSegments <= kMaxSegments);
}
mReadFrameStart[mReadDirection] = bufPtr;
UpdateReadWriteStartPointers();
mReadState = kReadStateNotActive;
mReadFrameLength = kUnknownFrameLength;
if (mFrameRemovedCallback != NULL)
{
mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast<Priority>(mReadDirection), this);
}
exit:
return error;
}
void Buffer::UpdateReadWriteStartPointers(void)
{
if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh))
{
mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward);
mReadFrameStart[kPriorityHigh] = mWriteFrameStart[kPriorityHigh];
ExitNow();
}
if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow))
{
mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward);
mReadFrameStart[kPriorityLow] = mWriteFrameStart[kPriorityLow];
}
exit:
return;
}
uint16_t Buffer::OutFrameGetLength(void)
{
uint16_t frameLength = 0;
uint16_t header;
uint8_t *bufPtr;
uint8_t numSegments;
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
otMessage *message = NULL;
#endif
VerifyOrExit(mReadFrameLength == kUnknownFrameLength, frameLength = mReadFrameLength);
VerifyOrExit(!IsEmpty(), frameLength = 0);
OutFrameSelectReadDirection();
bufPtr = mReadFrameStart[mReadDirection];
numSegments = 0;
while (bufPtr != mWriteFrameStart[mReadDirection])
{
header = ReadUint16At(bufPtr, mReadDirection);
if (header & kSegmentHeaderNewFrameFlag)
{
if (bufPtr != mReadFrameStart[mReadDirection])
{
break;
}
}
#if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
if (header & kSegmentHeaderMessageIndicatorFlag)
{
message = (message == NULL) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
: otMessageQueueGetNext(&mMessageQueue[mReadDirection], message);
if (message != NULL)
{
frameLength += otMessageGetLength(message);
}
}
#endif
frameLength += (header & kSegmentHeaderLengthMask);
bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
numSegments++;
OT_ASSERT(numSegments <= kMaxSegments);
}
if (mReadState != kReadStateNotActive)
{
mReadFrameLength = frameLength;
}
exit:
return frameLength;
}
Buffer::FrameTag Buffer::OutFrameGetTag(void)
{
OutFrameSelectReadDirection();
return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection];
}
} }