[spinel] move the MultiFrameBuffer implementation to multi_frame_buffer.hpp (#9150)

The `MultiFrameBuffer` is not only used by `hdlc.hpp`, it is also used
by `spi_interface.hpp` and `radio_spinel.hpp`. This commit moves the
`MultiFrameBuffer` implementation from `hdlc.hpp` to
`multi_frame_buffer.hpp` as a dependent module.
This commit is contained in:
Zhanglong Xia
2023-06-12 11:32:23 -07:00
committed by GitHub
parent 90c090f1ef
commit 47f15e3483
13 changed files with 553 additions and 516 deletions
+1
View File
@@ -34,6 +34,7 @@ target_include_directories(openthread-hdlc
PUBLIC
${OT_PUBLIC_INCLUDES}
PRIVATE
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/core
)
+1
View File
@@ -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)
+9 -9
View File
@@ -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)
+11 -414
View File
@@ -39,10 +39,7 @@
#include <openthread/error.h>
#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 <uint16_t kSize> 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<uint16_t>(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 <uint16_t kSize> 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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(mWriteFrameStart - mBuffer);
if (len > 0)
{
memmove(mBuffer, mWriteFrameStart, static_cast<uint16_t>(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
+1
View File
@@ -70,6 +70,7 @@ include_HEADERS = \
$(NULL)
noinst_HEADERS = \
multi_frame_buffer.hpp \
radio_spinel.hpp \
radio_spinel_impl.hpp \
spinel_buffer.hpp \
+451
View File
@@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <openthread/error.h>
#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 <uint16_t kSize> 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<uint16_t>(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 <uint16_t kSize> 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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(mWriteFrameStart - mBuffer);
if (len > 0)
{
memmove(mBuffer, mWriteFrameStart, static_cast<uint16_t>(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_
+24 -23
View File
@@ -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<kMaxFrameSize> RxFrameBuffer;
typedef MultiFrameBuffer<kMaxFrameSize> 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_
+9 -8
View File
@@ -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<kHdlcTxBufferSize> mHdlcBuffer;
Hdlc::Encoder mFrameEncoder;
Hdlc::Decoder mFrameDecoder;
HdlcTxState mState;
uint8_t mByte;
Hdlc::FrameBuffer<kRxBufferSize> mRxBuffer;
bool mHdlcSendImmediate;
Tasklet mHdlcSendTask;
Spinel::FrameBuffer<kHdlcTxBufferSize> mHdlcBuffer;
Hdlc::Encoder mFrameEncoder;
Hdlc::Decoder mFrameDecoder;
HdlcTxState mState;
uint8_t mByte;
Spinel::FrameBuffer<kRxBufferSize> mRxBuffer;
bool mHdlcSendImmediate;
Tasklet mHdlcSendTask;
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
BufferEncrypterReader mTxFrameBufferEncrypterReader;
+5 -9
View File
@@ -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<kMaxFrameSize> encoderBuffer;
Hdlc::Encoder hdlcEncoder(encoderBuffer);
otError error = OT_ERROR_NONE;
Spinel::FrameBuffer<kMaxFrameSize> 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();
+5 -7
View File
@@ -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;
+2 -6
View File
@@ -59,14 +59,10 @@
#include <linux/ioctl.h>
#include <linux/spi/spidev.h>
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();
}
+5 -12
View File
@@ -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 <openthread/openthread-system.h>
@@ -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;
+29 -28
View File
@@ -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<kBufferSize> frameBuffer;
Spinel::FrameBuffer<kBufferSize> 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<kBufferSize> frameBuffer;
uint8_t *frame = nullptr;
uint8_t *newFrame = nullptr;
uint16_t length;
uint16_t newLength;
Spinel::MultiFrameBuffer<kBufferSize> 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<kBufferSize> encoderBuffer;
Hdlc::MultiFrameBuffer<kBufferSize> 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<kBufferSize> encoderBuffer;
Spinel::MultiFrameBuffer<kBufferSize> 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<uint32_t>(rand()) % max; }
void TestFuzzEncoderDecoder(void)
{
uint16_t length;
uint8_t frame[kMaxFrameLength];
Hdlc::FrameBuffer<kBufferSize> encoderBuffer;
Hdlc::FrameBuffer<kBufferSize> decoderBuffer;
DecoderContext decoderContext;
Hdlc::Encoder encoder(encoderBuffer);
Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext);
uint16_t length;
uint8_t frame[kMaxFrameLength];
Spinel::FrameBuffer<kBufferSize> encoderBuffer;
Spinel::FrameBuffer<kBufferSize> 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");