diff --git a/src/lib/hdlc/CMakeLists.txt b/src/lib/hdlc/CMakeLists.txt index 433bc2f93..1838e2315 100644 --- a/src/lib/hdlc/CMakeLists.txt +++ b/src/lib/hdlc/CMakeLists.txt @@ -34,6 +34,7 @@ target_include_directories(openthread-hdlc PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE + ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/core ) diff --git a/src/lib/hdlc/Makefile.am b/src/lib/hdlc/Makefile.am index c410f1469..b908f591b 100644 --- a/src/lib/hdlc/Makefile.am +++ b/src/lib/hdlc/Makefile.am @@ -32,6 +32,7 @@ noinst_LIBRARIES = libopenthread-hdlc.a libopenthread_hdlc_a_CPPFLAGS = \ -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ -I$(top_srcdir)/src/core \ $(NULL) diff --git a/src/lib/hdlc/hdlc.cpp b/src/lib/hdlc/hdlc.cpp index b4798fdbd..26b1bd5ca 100644 --- a/src/lib/hdlc/hdlc.cpp +++ b/src/lib/hdlc/hdlc.cpp @@ -117,7 +117,7 @@ static bool HdlcByteNeedsEscape(uint8_t aByte) return rval; } -Encoder::Encoder(FrameWritePointer &aWritePointer) +Encoder::Encoder(Spinel::FrameWritePointer &aWritePointer) : mWritePointer(aWritePointer) , mFcs(0) { @@ -154,9 +154,9 @@ exit: otError Encoder::Encode(const uint8_t *aData, uint16_t aLength) { - otError error = OT_ERROR_NONE; - uint16_t oldFcs = mFcs; - FrameWritePointer oldPointer = mWritePointer; + otError error = OT_ERROR_NONE; + uint16_t oldFcs = mFcs; + Spinel::FrameWritePointer oldPointer = mWritePointer; while (aLength--) { @@ -176,10 +176,10 @@ exit: otError Encoder::EndFrame(void) { - otError error = OT_ERROR_NONE; - FrameWritePointer oldPointer = mWritePointer; - uint16_t oldFcs = mFcs; - uint16_t fcs = mFcs; + otError error = OT_ERROR_NONE; + Spinel::FrameWritePointer oldPointer = mWritePointer; + uint16_t oldFcs = mFcs; + uint16_t fcs = mFcs; fcs ^= 0xffff; @@ -199,7 +199,7 @@ exit: return error; } -Decoder::Decoder(FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandler, void *aContext) +Decoder::Decoder(Spinel::FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandler, void *aContext) : mState(kStateNoSync) , mWritePointer(aFrameWritePointer) , mFrameHandler(aFrameHandler) diff --git a/src/lib/hdlc/hdlc.hpp b/src/lib/hdlc/hdlc.hpp index 0268f2c26..37ebb324c 100644 --- a/src/lib/hdlc/hdlc.hpp +++ b/src/lib/hdlc/hdlc.hpp @@ -39,10 +39,7 @@ #include -#include "common/array.hpp" -#include "common/code_utils.hpp" -#include "common/debug.hpp" -#include "common/encoding.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" namespace ot { @@ -55,406 +52,6 @@ namespace ot { */ namespace Hdlc { -/** - * Defines a frame write pointer used by `Hdlc::Encoder` or `Hdlc::Decoder`. - * - * Defines the minimum set of APIs used by `Encoder/Decoder` for writing an encoded/decoded frame. It is - * simply a wrapper over a pointer into a buffer indicating where next byte should be written. Along with a write - * pointer, this class stores a remaining length variable indicating number of remaining bytes that can be written into - * the buffer. - * - * @note This class does NOT define the underlying buffer space or how it is being managed. - * - * `Encoder` or `Decoder` users are expected to use sub-classes of this class adding the buffer space and implementing - * the frame buffer management scheme. - * - * Two template sub-class `FrameBuffer` and `MultiFrameBuffer` are defined which respectively allow storing a single - * frame or multiple frames (FIFO queue of frame) in a buffer of a given size. - * - */ -class FrameWritePointer -{ -public: - /** - * Indicates whether there is buffer space available to write @p aWriteLength bytes. - * - * param[in] aWriteLength Number of bytes to write. - * - * @retval TRUE Enough buffer space is available to write the requested number of bytes. - * @retval FALSE Insufficient buffer space to write the requested number of bytes. - * - */ - bool CanWrite(uint16_t aWriteLength) const { return (mRemainingLength >= aWriteLength); } - - /** - * Writes a byte into the buffer and updates the write pointer (if space is available). - * - * @retval OT_ERROR_NONE Successfully wrote the byte and updated the pointer. - * @retval OT_ERROR_NO_BUFS Insufficient buffer space to write the byte. - * - */ - otError WriteByte(uint8_t aByte) - { - return CanWrite(sizeof(uint8_t)) ? (*mWritePointer++ = aByte, mRemainingLength--, OT_ERROR_NONE) - : OT_ERROR_NO_BUFS; - } - - /** - * Undoes the last @p aUndoLength writes, removing them from frame. - * - * @note Caller should ensure that @p aUndoLength is less than or equal to the number of previously written bytes - * into the frame. This method does not perform any checks and its behavior is undefined if @p aUndoLength is - * larger than the number of bytes previously written into the frame. - * - * @param[in] aUndoLength Number of bytes to remove (number of last `WriteByte()` calls to undo). - * - */ - void UndoLastWrites(uint16_t aUndoLength) - { - mWritePointer -= aUndoLength; - mRemainingLength += aUndoLength; - } - -protected: - FrameWritePointer(void) - : mWritePointer(nullptr) - , mRemainingLength(0) - { - } - - uint8_t *mWritePointer; ///< A pointer to current write position in the buffer. - uint16_t mRemainingLength; ///< Number of remaining bytes available to write. -}; - -/** - * Defines a template frame buffer of a given size for storing a single frame. - * - * The template parameter `kSize` specifies the size of the buffer. - * - */ -template class FrameBuffer : public FrameWritePointer -{ -public: - /** - * Initializes the `FrameBuffer` object. - * - */ - FrameBuffer(void) - : FrameWritePointer() - { - Clear(); - } - - /** - * Clears the buffer, moving the write pointer to the beginning of the buffer. - * - */ - void Clear(void) - { - mWritePointer = mBuffer; - mRemainingLength = sizeof(mBuffer); - } - - /** - * Indicates whether the buffer is empty or contains a frame. - * - * @retval TRUE Buffer is empty - * @retval FALSE Buffer contains a frame - * - */ - bool IsEmpty(void) const { return (mWritePointer == mBuffer); } - - /** - * Gets the length (number of bytes) in the frame. - * - * @returns The length (number of bytes) in the frame. - * - */ - uint16_t GetLength(void) const { return static_cast(mWritePointer - mBuffer); } - - /** - * Gets a pointer to start of the frame. - * - * @returns A pointer to start of the frame. - * - */ - uint8_t *GetFrame(void) { return mBuffer; } - -private: - uint8_t mBuffer[kSize]; -}; - -/** - * Defines a template frame buffer of a given size for storing multiple frames. - * - * The template parameter `kSize` specifies the total size of the buffer. - * - * Unlike `FrameBuffer` class where a single frame can be stored, this class is capable of saving multiple frames - * in a FIFO queue format. - * - */ -template class MultiFrameBuffer : public FrameWritePointer -{ -public: - /** - * Initializes the `MultiFrameBuffer` object. - * - */ - MultiFrameBuffer(void) - : FrameWritePointer() - { - Clear(); - } - - /** - * Clears the buffer, removing current frame and all previously saved frames. - * - * It moves the write pointer to the beginning of the buffer. - * - */ - void Clear(void) - { - mWriteFrameStart = mBuffer; - mWritePointer = mBuffer + kHeaderSize; - mRemainingLength = kSize - kHeaderSize; - - IgnoreError(SetSkipLength(0)); - } - - /** - * Indicates whether the current frame (being written) is empty or not. - * - * @retval TRUE Current frame is empty. - * @retval FALSE Current frame is not empty. - * - */ - bool HasFrame(void) const { return (mWritePointer != GetFrame()); } - - /** - * 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 <= GetArrayEnd(mBuffer)) - { - mWritePointer = GetFrame() + aLength; - mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); - error = OT_ERROR_NONE; - } - - return error; - } - - /** - * Gets the length (number of bytes) in the current frame being written into the buffer. - * - * @returns The length (number of bytes) in the frame. - * - */ - uint16_t GetLength(void) const { return static_cast(mWritePointer - GetFrame()); } - - /** - * 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 <= GetArrayEnd(mBuffer)) - { - Encoding::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); - mWritePointer = GetFrame(); - mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); - error = OT_ERROR_NONE; - } - - return error; - } - - /** - * 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); - } - - /** - * Gets a pointer to the start of the current frame. - * - * @returns A pointer to the start of the frame. - * - */ - uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); } - - /** - * 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()); } - - /** - * Saves the current frame and prepares the write pointer for a next frame to be written into the - * buffer. - * - * Saved frame can be retrieved later using `GetNextSavedFrame()`. - * - */ - void SaveFrame(void) - { - Encoding::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), mWriteFrameStart + kHeaderTotalLengthOffset); - mWriteFrameStart = mWritePointer; - IgnoreError(SetSkipLength(0)); - mWritePointer = GetFrame(); - mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); - } - - /** - * Discards the current frame and prepares the write pointer for a next frame to be written into the - * buffer. - * - */ - void DiscardFrame(void) - { - IgnoreError(SetSkipLength(0)); - - mWritePointer = GetFrame(); - mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); - } - - /** - * Indicates whether there are any saved frames in the buffer. - * - * @retval TRUE There is at least one saved frame in the buffer. - * @retval FALSE There is no saved frame in the buffer. - * - */ - bool HasSavedFrame(void) const { return (mWriteFrameStart != mBuffer); } - - /** - * Iterates through previously saved frames in the buffer, getting a next frame in the queue. - * - * @param[in,out] aFrame On entry, should point to a previous saved frame or nullptr to get the first frame. - * On exit, the pointer variable is updated to next frame or set to nullptr if there are - * none. - * @param[in,out] 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. - * - */ - otError GetNextSavedFrame(uint8_t *&aFrame, uint16_t &aLength) - { - otError error = OT_ERROR_NONE; - - OT_ASSERT(aFrame == nullptr || (mBuffer <= aFrame && aFrame < GetArrayEnd(mBuffer))); - - aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength; - - if (aFrame != mWriteFrameStart) - { - uint16_t totalLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); - uint16_t skipLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); - - aLength = totalLength - skipLength; - aFrame += kHeaderSize + skipLength; - } - else - { - aLength = 0; - aFrame = nullptr; - error = OT_ERROR_NOT_FOUND; - } - - return error; - } - - /** - * Clears all saved frames from the buffer and adjusts all the pointers. - * - * @note This method moves the pointers into the buffer and also copies the content. Any previously retrieved - * pointer to buffer (from `GetFrame()` or `GetNextSavedFrame()`) should be considered invalid after calling this - * method. - * - */ - void ClearSavedFrames(void) - { - uint16_t len = static_cast(mWriteFrameStart - mBuffer); - - if (len > 0) - { - memmove(mBuffer, mWriteFrameStart, static_cast(mWritePointer - mWriteFrameStart)); - mWritePointer -= len; - mWriteFrameStart -= len; - mRemainingLength += len; - } - } - -private: - /* - * The diagram below illustrates the format of a saved frame. - * - * +---------+-------------+------------+----------------+----------------------------+ - * | 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. - * - * Saved frame #1 Saved frame #2 Current frame being written - * / \ / \ / \ - * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ - * | header #1 | ... | header #2 | ... | header | ... | ... | - * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ - * ^ ^ ^\ /^ - * | | | mRemainingLength | - * mBuffer[0] mWriteFrameStart | | - * | mBuffer[kSize] - * mWritePointer - */ - - enum - { - kHeaderTotalLengthOffset = 0, - kHeaderSkipLengthOffset = sizeof(uint16_t), - kHeaderSize = sizeof(uint16_t) + sizeof(uint16_t), - }; - - uint8_t mBuffer[kSize]; - uint8_t *mWriteFrameStart; // Pointer to start of current frame being written. -}; - /** * Implements the HDLC-lite encoder. * @@ -468,7 +65,7 @@ public: * @param[in] aWritePointer The `FrameWritePointer` used by `Encoder` to write the encoded frames. * */ - explicit Encoder(FrameWritePointer &aWritePointer); + explicit Encoder(Spinel::FrameWritePointer &aWritePointer); /** * Begins an HDLC frame. @@ -517,8 +114,8 @@ public: otError EndFrame(void); private: - FrameWritePointer &mWritePointer; - uint16_t mFcs; + Spinel::FrameWritePointer &mWritePointer; + uint16_t mFcs; }; /** @@ -551,7 +148,7 @@ public: * @param[in] aContext A pointer to arbitrary context information. * */ - Decoder(FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandler, void *aContext); + Decoder(Spinel::FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandler, void *aContext); /** * Feeds a block of data into the decoder. @@ -581,12 +178,12 @@ private: kStateEscaped, }; - State mState; - FrameWritePointer &mWritePointer; - FrameHandler mFrameHandler; - void *mContext; - uint16_t mFcs; - uint16_t mDecodedLength; + State mState; + Spinel::FrameWritePointer &mWritePointer; + FrameHandler mFrameHandler; + void *mContext; + uint16_t mFcs; + uint16_t mDecodedLength; }; } // namespace Hdlc diff --git a/src/lib/spinel/Makefile.am b/src/lib/spinel/Makefile.am index 1540424fb..736f6d89b 100644 --- a/src/lib/spinel/Makefile.am +++ b/src/lib/spinel/Makefile.am @@ -70,6 +70,7 @@ include_HEADERS = \ $(NULL) noinst_HEADERS = \ + multi_frame_buffer.hpp \ radio_spinel.hpp \ radio_spinel_impl.hpp \ spinel_buffer.hpp \ diff --git a/src/lib/spinel/multi_frame_buffer.hpp b/src/lib/spinel/multi_frame_buffer.hpp new file mode 100644 index 000000000..9a374cd6a --- /dev/null +++ b/src/lib/spinel/multi_frame_buffer.hpp @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for the multiple frame buffer. + */ + +#ifndef SPINEL_MULTI_FRAME_BUFFER_HPP_ +#define SPINEL_MULTI_FRAME_BUFFER_HPP_ + +#include +#include +#include + +#include + +#include "common/array.hpp" +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/encoding.hpp" + +namespace ot { +namespace Spinel { + +/** + * Defines a frame write pointer. + * + * Defines the minimum set of APIs used by `Encoder/Decoder` for writing an encoded/decoded frame. It is + * simply a wrapper over a pointer into a buffer indicating where next byte should be written. Along with a write + * pointer, this class stores a remaining length variable indicating number of remaining bytes that can be written into + * the buffer. + * + * @note This class does NOT define the underlying buffer space or how it is being managed. + * + * Two template sub-class `FrameBuffer` and `MultiFrameBuffer` are defined which respectively allow storing a single + * frame or multiple frames (FIFO queue of frame) in a buffer of a given size. + * + */ +class FrameWritePointer +{ +public: + /** + * Indicates whether there is buffer space available to write @p aWriteLength bytes. + * + * param[in] aWriteLength Number of bytes to write. + * + * @retval TRUE Enough buffer space is available to write the requested number of bytes. + * @retval FALSE Insufficient buffer space to write the requested number of bytes. + * + */ + bool CanWrite(uint16_t aWriteLength) const { return (mRemainingLength >= aWriteLength); } + + /** + * Writes a byte into the buffer and updates the write pointer (if space is available). + * + * @param[in] aByte A byte to be written to the buffer. + * + * @retval OT_ERROR_NONE Successfully wrote the byte and updated the pointer. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space to write the byte. + * + */ + otError WriteByte(uint8_t aByte) + { + return CanWrite(sizeof(uint8_t)) ? (*mWritePointer++ = aByte, mRemainingLength--, OT_ERROR_NONE) + : OT_ERROR_NO_BUFS; + } + + /** + * Undoes the last @p aUndoLength writes, removing them from frame. + * + * @note Caller should ensure that @p aUndoLength is less than or equal to the number of previously written bytes + * into the frame. This method does not perform any checks and its behavior is undefined if @p aUndoLength is + * larger than the number of bytes previously written into the frame. + * + * @param[in] aUndoLength Number of bytes to remove (number of last `WriteByte()` calls to undo). + * + */ + void UndoLastWrites(uint16_t aUndoLength) + { + mWritePointer -= aUndoLength; + mRemainingLength += aUndoLength; + } + +protected: + FrameWritePointer(void) + : mWritePointer(nullptr) + , mRemainingLength(0) + { + } + + uint8_t *mWritePointer; ///< A pointer to current write position in the buffer. + uint16_t mRemainingLength; ///< Number of remaining bytes available to write. +}; + +/** + * Defines a template frame buffer of a given size for storing a single frame. + * + * @tparam kSize The size of the frame buffer. + * + */ +template class FrameBuffer : public FrameWritePointer +{ +public: + /** + * Initializes the `FrameBuffer` object. + * + */ + FrameBuffer(void) + : FrameWritePointer() + { + Clear(); + } + + /** + * Clears the buffer, moving the write pointer to the beginning of the buffer. + * + */ + void Clear(void) + { + mWritePointer = mBuffer; + mRemainingLength = sizeof(mBuffer); + } + + /** + * Indicates whether the buffer is empty or contains a frame. + * + * @retval TRUE Buffer is empty. + * @retval FALSE Buffer contains a frame. + * + */ + bool IsEmpty(void) const { return (mWritePointer == mBuffer); } + + /** + * Gets the length (number of bytes) in the frame. + * + * @returns The length (number of bytes) in the frame. + * + */ + uint16_t GetLength(void) const { return static_cast(mWritePointer - mBuffer); } + + /** + * Gets a pointer to start of the frame. + * + * @returns A pointer to start of the frame. + * + */ + uint8_t *GetFrame(void) { return mBuffer; } + +private: + uint8_t mBuffer[kSize]; +}; + +/** + * Defines a template frame buffer of a given size for storing multiple frames. + * + * Unlike `FrameBuffer` class where a single frame can be stored, this class is capable of saving multiple frames + * in a FIFO queue format. + * + * @tparam kSize The total size of the buffer. + * + */ +template class MultiFrameBuffer : public FrameWritePointer +{ +public: + /** + * Initializes the `MultiFrameBuffer` object. + * + */ + MultiFrameBuffer(void) + : FrameWritePointer() + { + Clear(); + } + + /** + * Clears the buffer, removing current frame and all previously saved frames. + * + * It moves the write pointer to the beginning of the buffer. + * + */ + void Clear(void) + { + mWriteFrameStart = mBuffer; + mWritePointer = mBuffer + kHeaderSize; + mRemainingLength = kSize - kHeaderSize; + + IgnoreError(SetSkipLength(0)); + } + + /** + * Indicates whether the current frame (being written) is empty or not. + * + * @retval TRUE Current frame is empty. + * @retval FALSE Current frame is not empty. + * + */ + bool HasFrame(void) const { return (mWritePointer != GetFrame()); } + + /** + * 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 <= GetArrayEnd(mBuffer)) + { + mWritePointer = GetFrame() + aLength; + mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); + error = OT_ERROR_NONE; + } + + return error; + } + + /** + * Gets the length (number of bytes) in the current frame being written into the buffer. + * + * @returns The length (number of bytes) in the frame. + * + */ + uint16_t GetLength(void) const { return static_cast(mWritePointer - GetFrame()); } + + /** + * 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 <= GetArrayEnd(mBuffer)) + { + Encoding::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); + mWritePointer = GetFrame(); + mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); + error = OT_ERROR_NONE; + } + + return error; + } + + /** + * 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); + } + + /** + * Gets a pointer to the start of the current frame. + * + * @returns A pointer to the start of the frame. + * + */ + uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); } + + /** + * 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()); } + + /** + * Saves the current frame and prepares the write pointer for a next frame to be written into the + * buffer. + * + * Saved frame can be retrieved later using `GetNextSavedFrame()`. + * + */ + void SaveFrame(void) + { + Encoding::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), mWriteFrameStart + kHeaderTotalLengthOffset); + mWriteFrameStart = mWritePointer; + IgnoreError(SetSkipLength(0)); + mWritePointer = GetFrame(); + mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); + } + + /** + * Discards the current frame and prepares the write pointer for a next frame to be written into the + * buffer. + * + */ + void DiscardFrame(void) + { + IgnoreError(SetSkipLength(0)); + + mWritePointer = GetFrame(); + mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); + } + + /** + * Indicates whether there are any saved frames in the buffer. + * + * @retval TRUE There is at least one saved frame in the buffer. + * @retval FALSE There is no saved frame in the buffer. + * + */ + bool HasSavedFrame(void) const { return (mWriteFrameStart != mBuffer); } + + /** + * Iterates through previously saved frames in the buffer, getting a next frame in the queue. + * + * @param[in,out] aFrame On entry, should point to a previous saved frame or nullptr to get the first frame. + * On exit, the pointer variable is updated to next frame or set to nullptr if there are + * none. + * @param[in,out] 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. + * + */ + otError GetNextSavedFrame(uint8_t *&aFrame, uint16_t &aLength) + { + otError error = OT_ERROR_NONE; + + OT_ASSERT(aFrame == nullptr || (mBuffer <= aFrame && aFrame < GetArrayEnd(mBuffer))); + + aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength; + + if (aFrame != mWriteFrameStart) + { + uint16_t totalLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); + uint16_t skipLength = Encoding::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); + + aLength = totalLength - skipLength; + aFrame += kHeaderSize + skipLength; + } + else + { + aLength = 0; + aFrame = nullptr; + error = OT_ERROR_NOT_FOUND; + } + + return error; + } + + /** + * Clears all saved frames from the buffer and adjusts all the pointers. + * + * @note This method moves the pointers into the buffer and also copies the content. Any previously retrieved + * pointer to buffer (from `GetFrame()` or `GetNextSavedFrame()`) should be considered invalid after calling this + * method. + * + */ + void ClearSavedFrames(void) + { + uint16_t len = static_cast(mWriteFrameStart - mBuffer); + + if (len > 0) + { + memmove(mBuffer, mWriteFrameStart, static_cast(mWritePointer - mWriteFrameStart)); + mWritePointer -= len; + mWriteFrameStart -= len; + mRemainingLength += len; + } + } + +private: + /* + * The diagram below illustrates the format of a saved frame. + * + * +---------+-------------+------------+----------------+----------------------------+ + * | 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. + * + * Saved frame #1 Saved frame #2 Current frame being written + * / \ / \ / \ + * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ + * | header #1 | ... | header #2 | ... | header | ... | ... | + * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ + * ^ ^ ^\ /^ + * | | | mRemainingLength | + * mBuffer[0] mWriteFrameStart | | + * | mBuffer[kSize] + * mWritePointer + */ + + enum + { + kHeaderTotalLengthOffset = 0, + kHeaderSkipLengthOffset = sizeof(uint16_t), + kHeaderSize = sizeof(uint16_t) + sizeof(uint16_t), + }; + + uint8_t mBuffer[kSize]; + uint8_t *mWriteFrameStart; // Pointer to start of current frame being written. +}; + +} // namespace Spinel +} // namespace ot +#endif // SPINEL_MULTI_FRAME_BUFFER_HPP_ diff --git a/src/lib/spinel/spinel_interface.hpp b/src/lib/spinel/spinel_interface.hpp index 32989ee9f..344de7e43 100644 --- a/src/lib/spinel/spinel_interface.hpp +++ b/src/lib/spinel/spinel_interface.hpp @@ -32,10 +32,10 @@ * */ -#ifndef POSIX_APP_SPINEL_INTERFACE_HPP_ -#define POSIX_APP_SPINEL_INTERFACE_HPP_ +#ifndef SPINEL_SPINEL_INTERFACE_HPP_ +#define SPINEL_SPINEL_INTERFACE_HPP_ -#include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" #include "lib/spinel/spinel.h" #include "lib/url/url.hpp" @@ -53,31 +53,14 @@ public: /** * Defines a receive frame buffer to store received spinel frame(s). * - * @note The receive frame buffer is an `Hdlc::MultiFrameBuffer` and therefore it is capable of storing multiple + * @note The receive frame buffer is an `Spinel::MultiFrameBuffer` and therefore it is capable of storing multiple * frames in a FIFO queue manner. * */ - typedef Hdlc::MultiFrameBuffer RxFrameBuffer; + typedef MultiFrameBuffer RxFrameBuffer; typedef void (*ReceiveFrameCallback)(void *aContext); - /** - * Indicates whether or not the frame is the Spinel SPINEL_CMD_RESET frame. - * - * @param[in] aFrame A pointer to buffer containing the spinel frame. - * @param[in] aLength The length (number of bytes) in the frame. - * - * @retval true If the frame is a Spinel SPINEL_CMD_RESET frame. - * @retval false If the frame is not a Spinel SPINEL_CMD_RESET frame. - * - */ - static bool IsSpinelResetCommand(const uint8_t *aFrame, uint16_t aLength) - { - static constexpr uint8_t kSpinelResetCommand[] = {SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET}; - return (aLength >= sizeof(kSpinelResetCommand)) && - (memcmp(aFrame, kSpinelResetCommand, sizeof(kSpinelResetCommand)) == 0); - } - /** * Initializes the interface to the Radio Co-processor (RCP) * @@ -161,8 +144,26 @@ public: * */ virtual ~SpinelInterface() = default; + +protected: + /** + * Indicates whether or not the frame is the Spinel SPINEL_CMD_RESET frame. + * + * @param[in] aFrame A pointer to buffer containing the spinel frame. + * @param[in] aLength The length (number of bytes) in the frame. + * + * @retval true If the frame is a Spinel SPINEL_CMD_RESET frame. + * @retval false If the frame is not a Spinel SPINEL_CMD_RESET frame. + * + */ + bool IsSpinelResetCommand(const uint8_t *aFrame, uint16_t aLength) + { + static constexpr uint8_t kSpinelResetCommand[] = {SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET}; + return (aLength >= sizeof(kSpinelResetCommand)) && + (memcmp(aFrame, kSpinelResetCommand, sizeof(kSpinelResetCommand)) == 0); + } }; } // namespace Spinel } // namespace ot -#endif // POSIX_APP_SPINEL_INTERFACE_HPP_ +#endif // SPINEL_SPINEL_INTERFACE_HPP_ diff --git a/src/ncp/ncp_hdlc.hpp b/src/ncp/ncp_hdlc.hpp index 1cce0e46c..d99ef96de 100644 --- a/src/ncp/ncp_hdlc.hpp +++ b/src/ncp/ncp_hdlc.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" #include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" #include "ncp/ncp_base.hpp" #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER @@ -128,14 +129,14 @@ private: Spinel::Buffer *aBuffer); otNcpHdlcSendCallback mSendCallback; - Hdlc::FrameBuffer mHdlcBuffer; - Hdlc::Encoder mFrameEncoder; - Hdlc::Decoder mFrameDecoder; - HdlcTxState mState; - uint8_t mByte; - Hdlc::FrameBuffer mRxBuffer; - bool mHdlcSendImmediate; - Tasklet mHdlcSendTask; + Spinel::FrameBuffer mHdlcBuffer; + Hdlc::Encoder mFrameEncoder; + Hdlc::Decoder mFrameDecoder; + HdlcTxState mState; + uint8_t mByte; + Spinel::FrameBuffer mRxBuffer; + bool mHdlcSendImmediate; + Tasklet mHdlcSendTask; #if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER BufferEncrypterReader mTxFrameBufferEncrypterReader; diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 050a1c69d..b1fb297f8 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -125,14 +125,10 @@ #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART -using ot::Spinel::SpinelInterface; - namespace ot { namespace Posix { -HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback, - void *aCallbackContext, - SpinelInterface::RxFrameBuffer &aFrameBuffer) +HdlcInterface::HdlcInterface(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer) : mReceiveFrameCallback(aCallback) , mReceiveFrameContext(aCallbackContext) , mReceiveFrameBuffer(aFrameBuffer) @@ -203,9 +199,9 @@ void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength) { mHdlcDeco otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) { - otError error = OT_ERROR_NONE; - Hdlc::FrameBuffer encoderBuffer; - Hdlc::Encoder hdlcEncoder(encoderBuffer); + otError error = OT_ERROR_NONE; + Spinel::FrameBuffer encoderBuffer; + Hdlc::Encoder hdlcEncoder(encoderBuffer); SuccessOrExit(error = hdlcEncoder.BeginFrame()); SuccessOrExit(error = hdlcEncoder.Encode(aFrame, aLength)); @@ -214,7 +210,7 @@ otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) error = Write(encoderBuffer.GetFrame(), encoderBuffer.GetLength()); exit: - if ((error == OT_ERROR_NONE) && ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + if ((error == OT_ERROR_NONE) && IsSpinelResetCommand(aFrame, aLength)) { mHdlcDecoder.Reset(); error = ResetConnection(); diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index 3b0aa4440..04266a667 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -37,6 +37,7 @@ #include "openthread-posix-config.h" #include "platform-posix.h" #include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" #include "lib/spinel/openthread-spinel-config.h" #include "lib/spinel/spinel_interface.hpp" @@ -58,9 +59,7 @@ public: * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. * */ - HdlcInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, - void *aCallbackContext, - Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); + HdlcInterface(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); /** * This destructor deinitializes the object. @@ -234,7 +233,6 @@ private: enum { - kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). kResetTimeout = 5000, ///< Maximum wait time in Milliseconds for file to become ready (see `ResetConnection`). kOpenFileDelay = 500, ///< Delay between open file calls, in Milliseconds (see `ResetConnection`). @@ -242,9 +240,9 @@ private: 2000, ///< Delay for removing RCP device from host OS after hard reset (see `ResetConnection`). }; - Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; - void *mReceiveFrameContext; - Spinel::SpinelInterface::RxFrameBuffer &mReceiveFrameBuffer; + ReceiveFrameCallback mReceiveFrameCallback; + void *mReceiveFrameContext; + RxFrameBuffer &mReceiveFrameBuffer; int mSockFd; uint32_t mBaudRate; diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index 17479b8a4..6c915cf68 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -59,14 +59,10 @@ #include #include -using ot::Spinel::SpinelInterface; - namespace ot { namespace Posix { -SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback, - void *aCallbackContext, - SpinelInterface::RxFrameBuffer &aFrameBuffer) +SpiInterface::SpiInterface(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer) : mReceiveFrameCallback(aCallback) , mReceiveFrameContext(aCallbackContext) , mRxFrameBuffer(aFrameBuffer) @@ -805,7 +801,7 @@ otError SpiInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) VerifyOrExit(aLength < (kMaxFrameSize - kSpiFrameHeaderSize), error = OT_ERROR_NO_BUFS); - if (ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + if (IsSpinelResetCommand(aFrame, aLength)) { ResetStates(); } diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp index fa77dbfcc..d6e4f9406 100644 --- a/src/posix/platform/spi_interface.hpp +++ b/src/posix/platform/spi_interface.hpp @@ -37,7 +37,7 @@ #include "openthread-posix-config.h" #include "platform-posix.h" -#include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" #include "lib/spinel/spinel_interface.hpp" #include @@ -62,9 +62,7 @@ public: * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. * */ - SpiInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, - void *aCallbackContext, - Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); + SpiInterface(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); /** * This destructor deinitializes the object. @@ -206,14 +204,9 @@ private: kSlowRetryTimeoutUs = 33 * kUsecPerMsec, }; - enum - { - kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, - }; - - Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; - void *mReceiveFrameContext; - Spinel::SpinelInterface::RxFrameBuffer &mRxFrameBuffer; + ReceiveFrameCallback mReceiveFrameCallback; + void *mReceiveFrameContext; + RxFrameBuffer &mRxFrameBuffer; int mSpiDevFd; int mResetGpioValueFd; diff --git a/tests/unit/test_hdlc.cpp b/tests/unit/test_hdlc.cpp index f23ae9999..f9e395c1c 100644 --- a/tests/unit/test_hdlc.cpp +++ b/tests/unit/test_hdlc.cpp @@ -31,6 +31,7 @@ #include "common/code_utils.hpp" #include "common/instance.hpp" #include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/multi_frame_buffer.hpp" #include "test_util.h" @@ -60,7 +61,7 @@ static const uint8_t sSkipText[] = "Skip text"; static const uint8_t sHdlcSpecials[] = {kFlagSequence, kFlagXOn, kFlagXOff, kFlagSequence, kEscapeSequence, kFlagSpecial}; -otError WriteToBuffer(const uint8_t *aText, Hdlc::FrameWritePointer &aWritePointer) +otError WriteToBuffer(const uint8_t *aText, Spinel::FrameWritePointer &aWritePointer) { otError error = OT_ERROR_NONE; @@ -75,9 +76,9 @@ exit: void TestHdlcFrameBuffer(void) { - Hdlc::FrameBuffer frameBuffer; + Spinel::FrameBuffer frameBuffer; - printf("Testing Hdlc::FrameBuffer"); + printf("Testing Spinel::FrameBuffer"); VerifyOrQuit(frameBuffer.IsEmpty(), "after constructor"); VerifyOrQuit(frameBuffer.GetLength() == 0, "after constructor"); @@ -123,15 +124,15 @@ void TestHdlcFrameBuffer(void) printf(" -- PASS\n"); } -void TestHdlcMultiFrameBuffer(void) +void TestSpinelMultiFrameBuffer(void) { - Hdlc::MultiFrameBuffer frameBuffer; - uint8_t *frame = nullptr; - uint8_t *newFrame = nullptr; - uint16_t length; - uint16_t newLength; + Spinel::MultiFrameBuffer frameBuffer; + uint8_t *frame = nullptr; + uint8_t *newFrame = nullptr; + uint16_t length; + uint16_t newLength; - printf("Testing Hdlc::MultiFrameBuffer"); + printf("Testing Spinel::MultiFrameBuffer"); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check state after constructor @@ -447,16 +448,16 @@ void ProcessDecodedFrame(void *aContext, otError aError) void TestEncoderDecoder(void) { - otError error; - uint8_t byte; - Hdlc::MultiFrameBuffer encoderBuffer; - Hdlc::MultiFrameBuffer decoderBuffer; - DecoderContext decoderContext; - Hdlc::Encoder encoder(encoderBuffer); - Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext); - uint8_t *frame; - uint16_t length; - uint8_t badShortFrame[3] = {kFlagSequence, 0xaa, kFlagSequence}; + otError error; + uint8_t byte; + Spinel::MultiFrameBuffer encoderBuffer; + Spinel::MultiFrameBuffer decoderBuffer; + DecoderContext decoderContext; + Hdlc::Encoder encoder(encoderBuffer); + Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext); + uint8_t *frame; + uint16_t length; + uint8_t badShortFrame[3] = {kFlagSequence, 0xaa, kFlagSequence}; printf("Testing Hdlc::Encoder and Hdlc::Decoder"); @@ -595,13 +596,13 @@ uint32_t GetRandom(uint32_t max) { return static_cast(rand()) % max; } void TestFuzzEncoderDecoder(void) { - uint16_t length; - uint8_t frame[kMaxFrameLength]; - Hdlc::FrameBuffer encoderBuffer; - Hdlc::FrameBuffer decoderBuffer; - DecoderContext decoderContext; - Hdlc::Encoder encoder(encoderBuffer); - Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext); + uint16_t length; + uint8_t frame[kMaxFrameLength]; + Spinel::FrameBuffer encoderBuffer; + Spinel::FrameBuffer decoderBuffer; + DecoderContext decoderContext; + Hdlc::Encoder encoder(encoderBuffer); + Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext); printf("Testing Hdlc::Encoder and Hdlc::Decoder with randomly generated frames"); @@ -647,7 +648,7 @@ void TestFuzzEncoderDecoder(void) int main(void) { ot::Ncp::TestHdlcFrameBuffer(); - ot::Ncp::TestHdlcMultiFrameBuffer(); + ot::Ncp::TestSpinelMultiFrameBuffer(); ot::Ncp::TestEncoderDecoder(); ot::Ncp::TestFuzzEncoderDecoder(); printf("\nAll tests passed.\n");