diff --git a/src/lib/hdlc/hdlc.hpp b/src/lib/hdlc/hdlc.hpp index 4edf57845..062e23faf 100644 --- a/src/lib/hdlc/hdlc.hpp +++ b/src/lib/hdlc/hdlc.hpp @@ -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(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(mWritePointer - mWriteFrameStart - kHeaderSize); } + uint16_t GetLength(void) const { return static_cast(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(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(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(mBuffer + kSize - mWritePointer); } @@ -265,7 +334,9 @@ public: */ void DiscardFrame(void) { - mWritePointer = mWriteFrameStart + kHeaderSize; + SetSkipLength(0); + + mWritePointer = GetFrame(); mRemainingLength = static_cast(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]; diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index f4a429556..74a13de67 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -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(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(mSpiTxFrameBuffer); - transfer[1].rx_buf = reinterpret_cast(mSpiRxFrameBuffer); - transfer[1].len = aLength + kSpiFrameHeaderSize + mSpiAlignAllowance; + transfer[1].rx_buf = reinterpret_cast(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); diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp index d2a54f21a..0fbf571a0 100644 --- a/src/posix/platform/spi_interface.hpp +++ b/src/posix/platform/spi_interface.hpp @@ -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; diff --git a/tests/unit/test_hdlc.cpp b/tests/unit/test_hdlc.cpp index 577216f85..df4121f4e 100644 --- a/tests/unit/test_hdlc.cpp +++ b/tests/unit/test_hdlc.cpp @@ -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"); }