[posix-host] use MultiFrameBuffer as SPI receive buffer (#4577)

This commit improves MultiFrameBuffer so that MultiFrameBuffer can
reserve a space in front of the frame. SPI interface uses
MultiFrameBuffer to store received frame directly, which avoids
copying data from receive buffer to MultiFrameBuffer.
This commit is contained in:
Zhanglong Xia
2020-03-10 19:13:23 -07:00
committed by GitHub
parent 2e09531745
commit 27ec1a7648
4 changed files with 269 additions and 75 deletions
+103 -15
View File
@@ -216,6 +216,8 @@ public:
mWriteFrameStart = mBuffer;
mWritePointer = mBuffer + kHeaderSize;
mRemainingLength = kSize - kHeaderSize;
SetSkipLength(0);
}
/**
@@ -225,7 +227,30 @@ public:
* @retval FALSE Current frame is not empty.
*
*/
bool HasFrame(void) const { return (mWritePointer != mWriteFrameStart + kHeaderSize); }
bool HasFrame(void) const { return (mWritePointer != GetFrame()); }
/**
* This method sets the length (number of bytes) of the current frame being written.
*
* param[in] aLength The length of current frame.
*
* @retval OT_ERROR_NONE Successfully set the length of the current frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a frame of length @p aLength.
*
*/
otError SetLength(uint16_t aLength)
{
otError error = OT_ERROR_NO_BUFS;
if (GetFrame() + aLength <= OT_ARRAY_END(mBuffer))
{
mWritePointer = GetFrame() + aLength;
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
error = OT_ERROR_NONE;
}
return error;
}
/**
* This method gets the length (number of bytes) in the current frame being written into the buffer.
@@ -233,7 +258,42 @@ public:
* @returns The length (number of bytes) in the frame.
*
*/
uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - mWriteFrameStart - kHeaderSize); }
uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - GetFrame()); }
/**
* This method sets the length (number of bytes) of reserved buffer in front of the current frame being written.
*
* param[in] aSkipLength The length of reserved buffer.
*
* @retval OT_ERROR_NONE Successfully set the length of reserved buffer.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a reserved buffer of length @p aLength.
*
*/
otError SetSkipLength(uint16_t aSkipLength)
{
otError error = OT_ERROR_NO_BUFS;
if (mWriteFrameStart + kHeaderSize + aSkipLength <= OT_ARRAY_END(mBuffer))
{
Encoding::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset);
mWritePointer = GetFrame();
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
error = OT_ERROR_NONE;
}
return error;
}
/**
* This method gets the length (number of bytes) of reserved buffer in front of the current frame being written.
*
* @returns The length (number of bytes) of the reserved buffer.
*
*/
uint16_t GetSkipLength(void) const
{
return Encoding::LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset);
}
/**
* This method gets a pointer to the start of the current frame.
@@ -241,7 +301,15 @@ public:
* @returns A pointer to the start of the frame.
*
*/
uint8_t *GetFrame(void) { return mWriteFrameStart + kHeaderSize; }
uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); }
/**
* This method gets the maximum length of the current frame.
*
* @returns The maximum length of the current frame.
*
*/
uint16_t GetFrameMaxLength(void) const { return static_cast<uint16_t>(mBuffer + kSize - GetFrame()); }
/**
* This method saves the current frame and prepares the write pointer for a next frame to be written into the
@@ -252,9 +320,10 @@ public:
*/
void SaveFrame(void)
{
Encoding::LittleEndian::WriteUint16(GetLength(), mWriteFrameStart);
Encoding::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), mWriteFrameStart + kHeaderTotalLengthOffset);
mWriteFrameStart = mWritePointer;
mWritePointer += kHeaderSize;
SetSkipLength(0);
mWritePointer = GetFrame();
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
}
@@ -265,7 +334,9 @@ public:
*/
void DiscardFrame(void)
{
mWritePointer = mWriteFrameStart + kHeaderSize;
SetSkipLength(0);
mWritePointer = GetFrame();
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
}
@@ -283,7 +354,8 @@ public:
*
* @param[inout] aFrame On entry, should point to a previous saved frame or NULL to get the first frame.
* On exit, the pointer variable is updated to next frame or set to NULL if there are none.
* @param[out] aLength A reference to a variable to return the frame length (number of bytes).
* @param[inout] aLength On entry, should be a reference to the frame length of the previous saved frame.
* On exit, the reference is updated to the frame length (number of bytes) of next frame.
*
* @retval OT_ERROR_NONE Updated @aFrame and @aLength successfully with the next saved frame.
* @retval OT_ERROR_NOT_FOUND No more saved frame in the buffer.
@@ -295,12 +367,15 @@ public:
assert(aFrame == NULL || (mBuffer <= aFrame && aFrame < OT_ARRAY_END(mBuffer)));
aFrame = (aFrame == NULL) ? mBuffer : aFrame + Encoding::LittleEndian::ReadUint16(aFrame - kHeaderSize);
aFrame = (aFrame == NULL) ? mBuffer : aFrame + aLength;
if (aFrame != mWriteFrameStart)
{
aLength = Encoding::LittleEndian::ReadUint16(aFrame);
aFrame += kHeaderSize;
uint16_t totalLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset);
uint16_t skipLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset);
aLength = totalLength - skipLength;
aFrame += kHeaderSize + skipLength;
}
else
{
@@ -335,11 +410,22 @@ public:
private:
/*
* The diagram below illustrates how the frames are saved in the buffer.
* The diagram below illustrates the format of a saved frame.
*
* Each saved frame contains a header which is 2 bytes long and specifies the frame length (the length does not
* include the header itself). The frame length is stored in header bytes as a `uint16_t` value using little-endian
* encoding.
* +---------+-------------+------------+----------------+----------------------------+
* | Octets: | 2 | 2 | SkipLength | TotalLength - SkipLength |
* +---------+-------------+------------+----------------+----------------------------+
* | Fields: | TotalLength | SkipLength | ReservedBuffer | FrameBuffer |
* +---------+-------------+------------+----------------+----------------------------+
*
* - "TotalLength" : The total length of the `ReservedBuffer` and `FrameBuffer`. It is stored in header bytes
* as a `uint16_t` value using little-endian encoding.
* - "SkipLength" : The length of the `ReservedBuffer`. It is stored in header bytes as a `uint16_t` value
* using little-endian encoding.
* - "ReservedBuffer": A reserved buffer in front of `FrameBuffer`. User can use it to store extra header, etc.
* - "FrameBuffer" : Frame buffer.
*
* The diagram below illustrates how the frames are saved in the buffer.
*
* The diagram shows `mBuffer` and different pointers into the buffer. It represents buffer state when there are
* two saved frames in the buffer.
@@ -358,7 +444,9 @@ private:
enum
{
kHeaderSize = sizeof(uint16_t),
kHeaderTotalLengthOffset = 0,
kHeaderSkipLengthOffset = sizeof(uint16_t),
kHeaderSize = sizeof(uint16_t) + sizeof(uint16_t),
};
uint8_t mBuffer[kSize];
+55 -49
View File
@@ -266,18 +266,20 @@ void SpiInterface::TrigerReset(void)
otLogNotePlat("Triggered hardware reset");
}
uint8_t *SpiInterface::GetRealRxFrameStart(void)
uint8_t *SpiInterface::GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength)
{
uint8_t * ret = mSpiRxFrameBuffer;
const uint8_t *end = mSpiRxFrameBuffer + mSpiAlignAllowance;
uint8_t * start = aSpiRxFrameBuffer;
const uint8_t *end = aSpiRxFrameBuffer + aAlignAllowance;
for (; ret != end && ret[0] == 0xff; ret++)
for (; start != end && start[0] == 0xff; start++)
;
return ret;
aSkipLength = static_cast<uint16_t>(start - aSpiRxFrameBuffer);
return start;
}
otError SpiInterface::DoSpiTransfer(uint32_t aLength)
otError SpiInterface::DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength)
{
int ret;
struct spi_ioc_transfer transfer[2];
@@ -296,8 +298,8 @@ otError SpiInterface::DoSpiTransfer(uint32_t aLength)
// This part is the actual SPI transfer.
transfer[1].tx_buf = reinterpret_cast<uintptr_t>(mSpiTxFrameBuffer);
transfer[1].rx_buf = reinterpret_cast<uintptr_t>(mSpiRxFrameBuffer);
transfer[1].len = aLength + kSpiFrameHeaderSize + mSpiAlignAllowance;
transfer[1].rx_buf = reinterpret_cast<uintptr_t>(aSpiRxFrameBuffer);
transfer[1].len = aTransferLength;
transfer[1].speed_hz = mSpiSpeedHz;
transfer[1].delay_usecs = 0;
transfer[1].bits_per_word = kSpiBitsPerWord;
@@ -317,7 +319,7 @@ otError SpiInterface::DoSpiTransfer(uint32_t aLength)
if (ret != -1)
{
otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, transfer[1].len);
otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer, transfer[1].len);
otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", aSpiRxFrameBuffer, transfer[1].len);
mSpiFrameCount++;
}
@@ -327,13 +329,16 @@ otError SpiInterface::DoSpiTransfer(uint32_t aLength)
otError SpiInterface::PushPullSpi(void)
{
otError error;
uint8_t * spiRxFrameBuffer = NULL;
otError error = OT_ERROR_FAILED;
uint16_t spiTransferBytes = 0;
uint8_t successfulExchanges = 0;
bool discardRxFrame = true;
uint8_t * spiRxFrameBuffer;
uint8_t * spiRxFrame;
uint8_t slaveHeader;
uint16_t slaveAcceptLen;
Ncp::SpiFrame txFrame(mSpiTxFrameBuffer);
uint16_t skipAlignAllowanceLength;
if (mSpiValidFrameCount == 0)
{
@@ -387,8 +392,20 @@ otError SpiInterface::PushPullSpi(void)
txFrame.SetHeaderAcceptLen(spiTransferBytes);
// Set skip length to make MultiFrameBuffer to reserve a space in front of the frame buffer.
SuccessOrExit(error = mRxFrameBuffer.SetSkipLength(kSpiFrameHeaderSize));
// Check whether the remaining frame buffer has enough space to store the data to be received.
VerifyOrExit(mRxFrameBuffer.GetFrameMaxLength() >= spiTransferBytes + mSpiAlignAllowance);
// Point to the start of the reserved buffer.
spiRxFrameBuffer = mRxFrameBuffer.GetFrame() - kSpiFrameHeaderSize;
// Set the total number of bytes to be transmitted.
spiTransferBytes += kSpiFrameHeaderSize + mSpiAlignAllowance;
// Perform the SPI transaction.
error = DoSpiTransfer(spiTransferBytes);
error = DoSpiTransfer(spiRxFrameBuffer, spiTransferBytes);
if (error != OT_ERROR_NONE)
{
@@ -405,10 +422,10 @@ otError SpiInterface::PushPullSpi(void)
}
// Account for misalignment (0xFF bytes at the start)
spiRxFrameBuffer = GetRealRxFrameStart();
spiRxFrame = GetRealRxFrameStart(spiRxFrameBuffer, mSpiAlignAllowance, skipAlignAllowanceLength);
{
Ncp::SpiFrame rxFrame(spiRxFrameBuffer);
Ncp::SpiFrame rxFrame(spiRxFrame);
otLogDebgPlat("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
@@ -418,8 +435,8 @@ otError SpiInterface::PushPullSpi(void)
slaveHeader = rxFrame.GetHeaderFlagByte();
if ((slaveHeader == 0xFF) || (slaveHeader == 0x00))
{
if ((slaveHeader == spiRxFrameBuffer[1]) && (slaveHeader == spiRxFrameBuffer[2]) &&
(slaveHeader == spiRxFrameBuffer[3]) && (slaveHeader == spiRxFrameBuffer[4]))
if ((slaveHeader == spiRxFrame[1]) && (slaveHeader == spiRxFrame[2]) && (slaveHeader == spiRxFrame[3]) &&
(slaveHeader == spiRxFrame[4]))
{
// Device is off or in a bad state. In some cases may be induced by flow control.
if (mSpiSlaveDataLen == 0)
@@ -438,12 +455,10 @@ otError SpiInterface::PushPullSpi(void)
// Header is full of garbage
mSpiGarbageFrameCount++;
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrameBuffer[0], spiRxFrameBuffer[1],
spiRxFrameBuffer[2], spiRxFrameBuffer[3], spiRxFrameBuffer[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1],
spiRxFrame[2], spiRxFrame[3], spiRxFrame[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", spiRxFrameBuffer, spiTransferBytes);
}
mSpiTxRefusedCount++;
@@ -459,12 +474,10 @@ otError SpiInterface::PushPullSpi(void)
mSpiTxRefusedCount++;
mSpiSlaveDataLen = 0;
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrameBuffer[0], spiRxFrameBuffer[1],
spiRxFrameBuffer[2], spiRxFrameBuffer[3], spiRxFrameBuffer[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
spiRxFrame[3], spiRxFrame[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", spiRxFrameBuffer, spiTransferBytes);
ExitNow();
}
@@ -487,7 +500,15 @@ otError SpiInterface::PushPullSpi(void)
mSpiRxFrameCount++;
successfulExchanges++;
HandleReceivedFrame(rxFrame);
// Set the skip length to skip align bytes and SPI frame header.
SuccessOrExit(error = mRxFrameBuffer.SetSkipLength(skipAlignAllowanceLength + kSpiFrameHeaderSize));
// Set the received frame length.
SuccessOrExit(error = mRxFrameBuffer.SetLength(rxFrame.GetHeaderDataLen()));
// Upper layer will free the frame buffer.
discardRxFrame = false;
mCallbacks.HandleReceivedFrame();
}
}
@@ -526,6 +547,11 @@ otError SpiInterface::PushPullSpi(void)
}
exit:
if (discardRxFrame)
{
mRxFrameBuffer.DiscardFrame();
}
return error;
}
@@ -737,26 +763,6 @@ exit:
return error;
}
void SpiInterface::HandleReceivedFrame(Ncp::SpiFrame &aSpiFrame)
{
const uint8_t *spinelFrame = aSpiFrame.GetData();
for (uint16_t i = 0; i < aSpiFrame.GetHeaderDataLen(); i++)
{
if (mRxFrameBuffer.WriteByte(spinelFrame[i]) != OT_ERROR_NONE)
{
mRxFrameBuffer.DiscardFrame();
otLogNotePlat("No enough memory buffers, drop packet");
ExitNow();
}
}
mCallbacks.HandleReceivedFrame();
exit:
return;
}
void SpiInterface::LogError(const char *aString)
{
OT_UNUSED_VARIABLE(aString);
+2 -5
View File
@@ -146,12 +146,11 @@ private:
void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed);
void TrigerReset(void);
uint8_t *GetRealRxFrameStart(void);
otError DoSpiTransfer(uint32_t aLength);
uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
otError PushPullSpi(void);
bool CheckInterrupt(void);
void HandleReceivedFrame(Ncp::SpiFrame &aSpiFrame);
void LogStats(void);
void LogError(const char *aString);
void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce);
@@ -212,8 +211,6 @@ private:
uint64_t mSpiTxFrameCount;
uint64_t mSpiTxFrameByteCount;
uint8_t mSpiRxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax];
bool mSpiTxIsReady;
uint16_t mSpiTxRefusedCount;
uint16_t mSpiTxPayloadSize;
+109 -6
View File
@@ -42,6 +42,7 @@ enum
kBufferSize = 1500, // Frame buffer size
kMaxFrameLength = 500, // Maximum allowed frame length (used when randomly generating frames)
kFuzzTestIteration = 50000, // Number of iteration during fuzz test (randomly generating frames)
kFrameHeaderSize = 4, // Frame header size
kFlagXOn = 0x11,
kFlagXOff = 0x13,
@@ -55,6 +56,7 @@ static const uint8_t sOpenThreadText[] = "OpenThread Rocks";
static const uint8_t sHelloText[] = "Hello there!";
static const uint8_t sMottoText[] = "Think good thoughts, say good words, do good deeds!";
static const uint8_t sHexText[] = "0123456789abcdef";
static const uint8_t sSkipText[] = "Skip text";
static const uint8_t sHdlcSpecials[] = {kFlagSequence, kFlagXOn, kFlagXOff,
kFlagSequence, kEscapeSequence, kFlagSpecial};
@@ -130,6 +132,7 @@ void TestHdlcMultiFrameBuffer(void)
uint8_t * frame = NULL;
uint8_t * newFrame = NULL;
uint16_t length;
uint16_t newLength;
printf("Testing Hdlc::MultiFrameBuffer");
@@ -221,8 +224,9 @@ void TestHdlcMultiFrameBuffer(void)
VerifyOrQuit(length == sizeof(sHexText) - 1, "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sHexText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
newFrame = frame;
VerifyOrQuit(frameBuffer.GetNextSavedFrame(newFrame, length) == OT_ERROR_NOT_FOUND,
newFrame = frame;
newLength = length;
VerifyOrQuit(frameBuffer.GetNextSavedFrame(newFrame, newLength) == OT_ERROR_NOT_FOUND,
"GetNextSavedFrame() incorrect behavior after all frames were read");
VerifyOrQuit(newFrame == NULL, "GetNextSavedFrame() incorrect behavior after all frames were read");
@@ -286,8 +290,10 @@ void TestHdlcMultiFrameBuffer(void)
VerifyOrQuit(!frameBuffer.HasFrame(), "HasFrame() incorrect behavior after Clear()");
VerifyOrQuit(!frameBuffer.HasSavedFrame(), "HasFrame() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - 1) == false, "CanWrite() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - 2) == true, "CanWrite() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - (kFrameHeaderSize - 1)) == false,
"CanWrite() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - kFrameHeaderSize) == true,
"CanWrite() incorrect behavior after Clear()");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Verify behavior of `ClearSavedFrames()`
@@ -333,9 +339,9 @@ void TestHdlcMultiFrameBuffer(void)
VerifyOrQuit(!frameBuffer.HasFrame(), "HasFrame() incorrect behavior after all frames are read and discarded");
VerifyOrQuit(!frameBuffer.HasSavedFrame(), "HasFrame() incorrect behavior after all read or discarded");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - 1) == false,
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - (kFrameHeaderSize - 1)) == false,
"CanWrite() incorrect behavior after all read or discarded");
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - 2) == true,
VerifyOrQuit(frameBuffer.CanWrite(kBufferSize - kFrameHeaderSize) == true,
"CanWrite() incorrect behavior after all read of discarded");
SuccessOrQuit(WriteToBuffer(sHelloText, frameBuffer), "WriteByte() failed");
@@ -352,6 +358,103 @@ void TestHdlcMultiFrameBuffer(void)
VerifyOrQuit(length == sizeof(sHelloText) - 1, "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sHelloText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Verify behavior of `SetSkipLength()` and `GetSkipLength()`
frameBuffer.Clear();
VerifyOrQuit(frameBuffer.GetSkipLength() == 0, "GetSkipLength() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.SetSkipLength(sizeof(sSkipText)) == OT_ERROR_NONE, "SetSkipLength() failed");
SuccessOrQuit(WriteToBuffer(sMottoText, frameBuffer), "WriteByte() failed");
VerifyOrQuit(memcmp(frameBuffer.GetFrame(), sMottoText, frameBuffer.GetLength()) == 0,
"GetFrame() content is incorrect");
memcpy(frameBuffer.GetFrame() - sizeof(sSkipText), sSkipText, sizeof(sSkipText));
VerifyOrQuit(frameBuffer.GetSkipLength() == sizeof(sSkipText), "GetSkipLength() failed");
VerifyOrQuit(frameBuffer.GetLength() == sizeof(sMottoText) - 1, "GetLength() failed");
VerifyOrQuit(memcmp(frameBuffer.GetFrame(), sMottoText, frameBuffer.GetLength()) == 0,
"GetFrame() content is incorrect");
frameBuffer.SaveFrame();
VerifyOrQuit(!frameBuffer.HasFrame(), "HasFrame() incorrect behavior after SaveFrame()");
VerifyOrQuit(frameBuffer.HasSavedFrame(), "HasFrame() incorrect behavior after SaveFrame()");
VerifyOrQuit(frameBuffer.GetSkipLength() == 0, "GetSkipLength() incorrect behavior after SaveFrame()");
VerifyOrQuit(frameBuffer.SetSkipLength(sizeof(sSkipText)) == OT_ERROR_NONE, "SetSkipLength() failed");
SuccessOrQuit(WriteToBuffer(sOpenThreadText, frameBuffer), "WriteByte() failed");
VerifyOrQuit(memcmp(frameBuffer.GetFrame(), sOpenThreadText, frameBuffer.GetLength()) == 0,
"GetFrame() content is incorrect");
memcpy(frameBuffer.GetFrame() - sizeof(sSkipText), sSkipText, sizeof(sSkipText));
VerifyOrQuit(frameBuffer.GetSkipLength() == sizeof(sSkipText), "GetSkipLength() failed");
VerifyOrQuit(frameBuffer.GetLength() == sizeof(sOpenThreadText) - 1, "GetLength() failed");
VerifyOrQuit(memcmp(frameBuffer.GetFrame(), sOpenThreadText, frameBuffer.GetLength()) == 0,
"GetFrame() content is incorrect");
frameBuffer.SaveFrame();
VerifyOrQuit(!frameBuffer.HasFrame(), "HasFrame() incorrect behavior after SaveFrame()");
VerifyOrQuit(frameBuffer.HasSavedFrame(), "HasFrame() incorrect behavior after SaveFrame()");
VerifyOrQuit(frameBuffer.GetSkipLength() == 0, "GetSkipLength() incorrect behavior after SaveFrame()");
frame = NULL;
SuccessOrQuit(frameBuffer.GetNextSavedFrame(frame, length), "GetNextSavedFrame() failed unexpectedly");
VerifyOrQuit(length == sizeof(sMottoText) - 1, "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sMottoText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
VerifyOrQuit(memcmp(frame - sizeof(sSkipText), sSkipText, sizeof(sSkipText)) == 0,
"GetNextSavedFrame() reserved frame buffer content is incorrect");
SuccessOrQuit(frameBuffer.GetNextSavedFrame(frame, length), "GetNextSavedFrame() failed unexpectedly");
VerifyOrQuit(length == sizeof(sOpenThreadText) - 1, "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sOpenThreadText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
VerifyOrQuit(memcmp(frame - sizeof(sSkipText), sSkipText, sizeof(sSkipText)) == 0,
"GetNextSavedFrame() reserved frame buffer content is incorrect");
frameBuffer.Clear();
VerifyOrQuit(frameBuffer.SetSkipLength(kBufferSize - (kFrameHeaderSize - 1)) == OT_ERROR_NO_BUFS,
"SetSkipLength() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.SetSkipLength(kBufferSize - kFrameHeaderSize) == OT_ERROR_NONE,
"SetSkipLength() incorrect behavior after Clear()");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Verify behavior of `SetLength()` and `GetLength()`
frameBuffer.Clear();
VerifyOrQuit((frame = frameBuffer.GetFrame()) != NULL, "GetFrame() failed");
memcpy(frame, sHelloText, sizeof(sHelloText));
VerifyOrQuit(frameBuffer.SetLength(sizeof(sHelloText)) == OT_ERROR_NONE, "SetLength() failed");
VerifyOrQuit(frameBuffer.GetLength() == sizeof(sHelloText), "GetLength() failed");
VerifyOrQuit(frameBuffer.HasFrame(), "HasFrame() is incorrect");
frameBuffer.SaveFrame();
VerifyOrQuit((frame = frameBuffer.GetFrame()) != NULL, "GetFrame() failed");
memcpy(frame, sMottoText, sizeof(sMottoText));
VerifyOrQuit(frameBuffer.SetLength(sizeof(sMottoText)) == OT_ERROR_NONE, "SetLength() failed");
VerifyOrQuit(frameBuffer.GetLength() == sizeof(sMottoText), "GetLength() failed");
VerifyOrQuit(frameBuffer.HasFrame(), "HasFrame() is incorrect");
frameBuffer.SaveFrame();
VerifyOrQuit((frame = frameBuffer.GetFrame()) != NULL, "GetFrame() failed");
memcpy(frame, sHexText, sizeof(sHexText));
VerifyOrQuit(frameBuffer.SetLength(sizeof(sHexText)) == OT_ERROR_NONE, "SetLength() failed");
VerifyOrQuit(frameBuffer.GetLength() == sizeof(sHexText), "GetLength() failed");
frameBuffer.DiscardFrame();
VerifyOrQuit(!frameBuffer.HasFrame(), "HasFrame() is incorrect");
frame = NULL;
SuccessOrQuit(frameBuffer.GetNextSavedFrame(frame, length), "GetNextSavedFrame() failed unexpectedly");
VerifyOrQuit(length == sizeof(sHelloText), "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sHelloText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
SuccessOrQuit(frameBuffer.GetNextSavedFrame(frame, length), "GetNextSavedFrame() failed unexpectedly");
VerifyOrQuit(length == sizeof(sMottoText), "GetNextSavedFrame() length is incorrect");
VerifyOrQuit(memcmp(frame, sMottoText, length) == 0, "GetNextSavedFrame() frame content is incorrect");
SuccessOrQuit(!frameBuffer.GetNextSavedFrame(frame, length), "GetNextSavedFrame() failed unexpectedly");
frameBuffer.Clear();
VerifyOrQuit(frameBuffer.SetLength(kBufferSize - (kFrameHeaderSize - 1)) == OT_ERROR_NO_BUFS,
"SetLength() incorrect behavior after Clear()");
VerifyOrQuit(frameBuffer.SetLength(kBufferSize - kFrameHeaderSize) == OT_ERROR_NONE,
"SetLength() incorrect behavior after Clear()");
printf(" -- PASS\n");
}