blob: 84581d1ee6bb02b0a83c5cf5dc76e8b1ef1acd89 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 an HDLC-lite encoder and decoder.
*/
#ifndef HDLC_HPP_
#define HDLC_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 ot::Hdlc
*
* @brief
* This namespace includes definitions for the HDLC-lite encoder and decoder.
*
*/
namespace Hdlc {
/**
* This class defines a frame write pointer used by `Hdlc::Encoder` or `Hdlc::Decoder`.
*
* This class 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:
/**
* This method 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); }
/**
* This method 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;
}
/**
* This method 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.
};
/**
* This class 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:
/**
* This constructor initializes the `FrameBuffer` object.
*
*/
FrameBuffer(void)
: FrameWritePointer()
{
Clear();
}
/**
* This method clears the buffer, moving the write pointer to the beginning of the buffer.
*
*/
void Clear(void)
{
mWritePointer = mBuffer;
mRemainingLength = sizeof(mBuffer);
}
/**
* This method 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); }
/**
* This method 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); }
/**
* This method 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];
};
/**
* This class 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:
/**
* This constructor initializes the `MultiFrameBuffer` object.
*
*/
MultiFrameBuffer(void)
: FrameWritePointer()
{
Clear();
}
/**
* This method 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));
}
/**
* This method 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()); }
/**
* 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 <= GetArrayEnd(mBuffer))
{
mWritePointer = GetFrame() + aLength;
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
error = OT_ERROR_NONE;
}
return error;
}
/**
* This method gets the length (number of bytes) in the current frame being written into the buffer.
*
* @returns The length (number of bytes) in the frame.
*
*/
uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - GetFrame()); }
/**
* This method sets the length (number of bytes) of reserved buffer in front of the current frame being written.
*
* param[in] aSkipLength The length of reserved buffer.
*
* @retval OT_ERROR_NONE Successfully set the length of reserved buffer.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a reserved buffer of length @p aLength.
*
*/
otError SetSkipLength(uint16_t aSkipLength)
{
otError error = OT_ERROR_NO_BUFS;
if (mWriteFrameStart + kHeaderSize + aSkipLength <= GetArrayEnd(mBuffer))
{
Encoding::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset);
mWritePointer = GetFrame();
mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
error = OT_ERROR_NONE;
}
return error;
}
/**
* This method gets the length (number of bytes) of reserved buffer in front of the current frame being written.
*
* @returns The length (number of bytes) of the reserved buffer.
*
*/
uint16_t GetSkipLength(void) const
{
return Encoding::LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset);
}
/**
* This method gets a pointer to the start of the current frame.
*
* @returns A pointer to the start of the frame.
*
*/
uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); }
/**
* This method gets the maximum length of the current frame.
*
* @returns The maximum length of the current frame.
*
*/
uint16_t GetFrameMaxLength(void) const { return static_cast<uint16_t>(mBuffer + kSize - GetFrame()); }
/**
* This method saves the current frame and prepares the write pointer for a next frame to be written into the
* 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);
}
/**
* This method 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);
}
/**
* This method 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); }
/**
* This method 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;
}
/**
* This method 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.
};
/**
* This class implements the HDLC-lite encoder.
*
*/
class Encoder
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aWritePointer The `FrameWritePointer` used by `Encoder` to write the encoded frames.
*
*/
explicit Encoder(FrameWritePointer &aWritePointer);
/**
* This method begins an HDLC frame.
*
* @retval OT_ERROR_NONE Successfully started the HDLC frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start the HDLC frame.
*
*/
otError BeginFrame(void);
/**
* This method encodes a single byte into current frame.
*
* If there is no space to add the byte, the write pointer in frame buffer remains the same.
*
* @param[in] aByte A byte value to encode and add to frame.
*
* @retval OT_ERROR_NONE Successfully encoded and added the byte to frame buffer.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode and add the byte.
*
*/
otError Encode(uint8_t aByte);
/**
* This method encodes a given block of data into current frame.
*
* This method returns success only if there is space in buffer to encode the entire block of data. If there is no
* space to encode the entire block of data, the write pointer in frame buffer remains the same.
*
* @param[in] aData A pointer to a buffer containing the data to encode.
* @param[in] aLength The number of bytes in @p aData.
*
* @retval OT_ERROR_NONE Successfully encoded and added the data to frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add the frame.
*
*/
otError Encode(const uint8_t *aData, uint16_t aLength);
/**
* This method ends/finalizes the HDLC frame.
*
* @retval OT_ERROR_NONE Successfully ended the HDLC frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to end the HDLC frame.
*
*/
otError EndFrame(void);
private:
FrameWritePointer &mWritePointer;
uint16_t mFcs;
};
/**
* This class implements the HDLC-lite decoder.
*
*/
class Decoder
{
public:
/**
* This function pointer is called when either a complete frame has been decoded or an error occurs during
* decoding.
*
* The decoded frame (or the partially decoded frame in case of an error) is available in `aFrameWritePointer`
* buffer given in `Decoder` constructor.
*
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aError OT_ERROR_NONE if the frame was decoded successfully,
* OT_ERROR_PARSE if the Frame Check Sequence (FCS) was incorrect in decoded frame,
* OT_ERROR_NO_BUFS insufficient buffer space available to save the decoded frame.
*
*/
typedef void (*FrameHandler)(void *aContext, otError aError);
/**
* This constructor initializes the decoder.
*
* @param[in] aFrameWritePointer The `FrameWritePointer` used by `Decoder` to write the decoded frames.
* @param[in] aFrameHandler The frame handler callback function pointer.
* @param[in] aContext A pointer to arbitrary context information.
*
*/
Decoder(FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandler, void *aContext);
/**
* This method feeds a block of data into the decoder.
*
* If during decoding, a full HDLC frame is successfully decoded or an error occurs, the `FrameHandler` callback
* is called. The decoded frame (or the partially decoded frame in case of an error) is available in
* `aFrameWritePointer` buffer from the constructor. The `Decoder` user (if required) must update/reset the write
* pointer from this callback for the next frame to be decoded.
*
* @param[in] aData A pointer to a buffer containing data to be fed to decoder.
* @param[in] aLength The number of bytes in @p aData.
*
*/
void Decode(const uint8_t *aData, uint16_t aLength);
/**
* This method resets internal states of the decoder.
*
*/
void Reset(void);
private:
enum State
{
kStateNoSync,
kStateSync,
kStateEscaped,
};
State mState;
FrameWritePointer &mWritePointer;
FrameHandler mFrameHandler;
void * mContext;
uint16_t mFcs;
uint16_t mDecodedLength;
};
} // namespace Hdlc
} // namespace ot
#endif // HDLC_HPP_