| /* |
| * 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 contains definitions for the NCP frame buffer class. |
| */ |
| |
| #ifndef NCP_FRAME_BUFFER_HPP_ |
| #define NCP_FRAME_BUFFER_HPP_ |
| |
| #include <openthread/config.h> |
| #include <openthread/message.h> |
| #include <openthread/types.h> |
| |
| namespace ot { |
| |
| class NcpFrameBuffer |
| { |
| public: |
| /** |
| * Defines the frame tag type. Frame tags can be compared with one another using operator `==`. |
| * |
| */ |
| typedef const void *FrameTag; |
| |
| /** |
| * Defines the tag to indicate an invalid tag (e.g., when there is no frame). |
| * |
| */ |
| static const FrameTag kInvalidTag; |
| |
| /** |
| * Defines a function pointer callback which is invoked to inform a change in `NcpFrameBuffer` either when a new |
| * frame is added/written to `NcpFrameBuffer` or when a frame is removed from `NcpFrameBuffer`. |
| * |
| * @param[in] aContext A pointer to arbitrary context information. |
| * @param[in] aTag The tag associated with the frame which is added or removed. |
| * @param[in] aNcpFrameBuffer A pointer to the `NcpFrameBuffer`. |
| * |
| */ |
| typedef void (*BufferCallback)(void *aContext, FrameTag aTag, NcpFrameBuffer *aNcpFrameBuffer); |
| |
| /** |
| * This constructor creates an NCP frame buffer. |
| * |
| * @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer. |
| * @param[in] aBufferLength The buffer size (in bytes). |
| * |
| */ |
| NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLength); |
| |
| /** |
| * This method clears the NCP frame buffer. All the frames are cleared/removed. |
| * |
| */ |
| void Clear(void); |
| |
| /** |
| * This method sets the `FrameAdded` callback and its context. |
| * |
| * Subsequent calls to this method will overwrite the previous callback and its context. |
| * |
| * @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer. |
| * @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback. |
| * |
| */ |
| void SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext); |
| |
| /** |
| * This method sets the `FrameRemoved` callback and its context. |
| * |
| * Subsequent calls to this method will overwrite the previous callback and its context. |
| * |
| * @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer. |
| * @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback. |
| * |
| */ |
| void SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext); |
| |
| /** |
| * This method begins a new input frame to be added/written to the frame buffer. |
| |
| * If there is a previous frame being written (`InFrameEnd()` has not yet been called on the frame), this method |
| * will discard and clear the previous unfinished frame. |
| * |
| * @retval OT_ERROR_NONE Successfully started a new frame. |
| * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame. |
| * |
| */ |
| otError InFrameBegin(void); |
| |
| /** |
| * This method adds data to the current input frame being written to the buffer. |
| * |
| * If no buffer space is available, this method will discard and clear the frame before returning an error status. |
| * |
| * @param[in] aDataBuffer A pointer to data buffer. |
| * @param[in] aDataBufferLength The length of the data buffer. |
| * |
| * @retval OT_ERROR_NONE Successfully added new data to the frame. |
| * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data. |
| * |
| */ |
| otError InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength); |
| |
| /** |
| * This method adds a message to the current input frame being written to the buffer. |
| * |
| * If no buffer space is available, this method will discard and clear the frame before returning an error status. |
| * In case of success, the passed-in message @p aMessage will be owned by the frame buffer instance and will be |
| * freed when either the the frame is removed or discarded. |
| * |
| * @param[in] aMessage A message to be added to current frame. |
| * |
| * @retval OT_ERROR_NONE Successfully added the message to the frame. |
| * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. |
| * |
| */ |
| otError InFrameFeedMessage(otMessage *aMessage); |
| |
| /** |
| * This method finalizes/ends the current input frame being written to the buffer. |
| * |
| * If no buffer space is available, this method will discard and clear the frame before returning an error status. |
| * |
| * @retval OT_ERROR_NONE Successfully added the message to the frame. |
| * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. |
| * |
| */ |
| otError InFrameEnd(void); |
| |
| /** |
| * This method returns the tag assigned to last successfully written/added frame to NcpBuffer (i.e., last input |
| * frame for which `InFrameEnd()` was called and returned success status). The tag is a unique value (within |
| * currently queued frames) associated with each frame in the `NcpFrameBuffer`. The tag can be used to identify the |
| * same frame when it is read and removed from the NcpBuffer. Tags can be compared using operator `==`. |
| * |
| * @returns The tag of last successfully written frame, or `kInvalidTag` if no frame is written so far. |
| */ |
| FrameTag InFrameGetLastTag(void) const; |
| |
| /** |
| * This method checks if the buffer is empty. An non-empty buffer contains at least one full frame for reading. |
| * |
| * @retval true Buffer is not empty and contains at least one full frame for reading. |
| * @retval false Buffer is empty and contains no frame for reading. |
| * |
| */ |
| bool IsEmpty(void) const; |
| |
| /** |
| * This method begins/prepares a new output frame to be read from the frame buffer. |
| * |
| * The NCP buffer maintains a read offset for the current frame being read. Before reading any bytes from the frame |
| * this method should be called to prepare the frame and set the read offset. |
| * |
| * If part of current frame has already been read, a sub-sequent call to this method will reset the read offset |
| * back to beginning of current output frame. |
| * |
| * @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading. |
| * @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading. |
| * |
| */ |
| otError OutFrameBegin(void); |
| |
| /** |
| * This method checks if the current output frame (being read) has ended. |
| * |
| * The NCP buffer maintains a read offset for the current output frame being read. This method returns true if the |
| * read offset is at the end of the frame and there are no more bytes available to read from current frame. |
| * |
| * @retval true Frame has ended (no more bytes available to read from current output frame). |
| * @retval false Frame has data (can read more bytes from current output frame). |
| * |
| */ |
| bool OutFrameHasEnded(void); |
| |
| /** |
| * This method reads and returns the next byte from the current output frame. |
| * |
| * The NCP buffer maintains a read offset for the current output frame being read. This method reads and returns |
| * the next byte from the current frame and moves the read offset forward. If read offset is already at the end |
| * current output frame, this method returns zero. |
| * |
| * @returns The next byte from the current output frame or zero if frame has ended. |
| * |
| */ |
| uint8_t OutFrameReadByte(void); |
| |
| /** |
| * This method reads bytes from the current output frame. |
| * |
| * The NCP buffer maintains a read offset for the current output frame being read. This method attempts to read |
| * the given number of bytes (@p aDataBufferLength) from the current frame and copies the bytes into the given |
| * data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are less bytes |
| * remaining in current frame, the available bytes are read/copied. This methods returns the actual number of bytes |
| * read. |
| * |
| * @param[in] aDataBuffer A pointer to a data buffer. |
| * @param[in] aReadLength Number of bytes to read. |
| * |
| * @returns The number of bytes read and copied into data buffer. |
| * |
| */ |
| uint16_t OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer); |
| |
| /** |
| * This method removes the current/front output frame from the buffer. |
| * |
| * The NCP buffer stores the frames in FIFO order. This method removes the front frame (which may be the current |
| * output frame being read) from the buffer. There is no need to prepare/begin reading the current frame before |
| * removing it, so the front frame can be removed without a previous call to `OutFrameBegin()`. |
| * |
| * When a frame is removed all its associated messages will be freed. |
| * |
| * If the remove operation is successful, this method will invoke the `FrameRemovedCallback` (if provided). |
| * |
| * @retval OT_ERROR_NONE Successfully removed the front frame. |
| * @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove. |
| * |
| */ |
| otError OutFrameRemove(void); |
| |
| /** |
| * This method returns the number of bytes (length) of current/front frame in the NCP frame buffer. |
| * |
| * The NCP buffer stores the frames in FIFO order. This method returns the length of the front frame (which may |
| * be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current |
| * frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`. |
| * |
| * If there is no frame in buffer, this method returns zero. |
| * |
| * @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer. |
| * |
| */ |
| uint16_t OutFrameGetLength(void); |
| |
| /** |
| * This method returns the tag value associated to current/front frame in the NCP frame buffer. |
| * |
| * The NCP buffer stores the frames in FIFO order. This method returns the tag of the front frame (which may |
| * be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current |
| * frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`. |
| * |
| * If there is no frame in buffer, this method returns `kInvalidTag`. |
| * |
| * @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer. |
| * |
| */ |
| FrameTag OutFrameGetTag(void) const; |
| |
| private: |
| |
| /* |
| * NcpFrameBuffer Implementation |
| * ----------------------------- |
| * |
| * NcpFrameBuffer internally stores a frame as a sequence of data segments. The data segments are stored in the |
| * the main buffer `mBuffer`. mBuffer is utilized as a circular buffer. |
| |
| * Messages (which are added using `InFrameFeedMessaged()`) are not copied in the `mBuffer` but instead are |
| * enqueued in a message queue `mMessageQueue`. |
| * |
| * The data segments include a header before the data portion. The header is 2 bytes long is formated as follows |
| * |
| * Bit 0-13: Give the length of the data segment (max segment len is 2^14 = 16,384 bytes). |
| * Bit 14: Flag bit set to indicate that this segment has an associated `Message` (appended to its end). |
| * Bit 15: Flag bit set to indicate that this segment defines the start of a new frame. |
| * |
| * Bit 15 Bit 14 Bits: 0 - 13 |
| * +--------------+--------------+--------------------------------------------------------+ |
| * | New Frame | Has Message | Length of segment (excluding the header) | |
| * +--------------+--------------+--------------------------------------------------------+ |
| * |
| * The header is encoded in big-endian (msb first) style. |
| |
| * Consider the following calls to create a frame: |
| * |
| * ncpBuffer.InFrameBegin(); |
| * ncpBuffer.InFrameFeedData("Hello", 5); |
| * ncpBuffer.InFrameFeedData("There", 5); |
| * ncpBuffer.InFrameFeedMessage(*someMessage); |
| * ncpBuffer.InFrameFeedData("Bye", 3); |
| * ncpBuffer.InFrameEnd(); |
| * |
| * This frame is stored as two segments: |
| * |
| * - Segment #1 contains "HelloThere" with a header of `0xC00A` which shows that this segment contains 10 data |
| * bytes, and it starts a new frame, and also must include a message from the message queue. |
| * |
| * - Segment #2 contains "Bye" with a header value of `0x0003` showing length of 3 and no appended message. |
| * |
| * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ |
| * | C0 | 0A | `H` | 'e' | 'l' | 'l' | 'o' | 'T' | 'h' | 'e' | 'r' | 'e' | 00 | 03 | 'B' | 'y' | 'e' | |
| * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ |
| * \ / \ / |
| * Segment #1 Header Segment #2 Header |
| * |
| * |
| * Buffer pointers: |
| * |
| * mReadFrameStart |
| * | |
| * | mReadSegmentHead mWriteFrameStart |
| * | | | |
| * | | mReadPointer | mWriteSegmentHead |
| * | | | | | |
| * mBuffer | | | mReadSegmentTail | | mWriteSegmentTail mBufferEnd |
| * | | | | | | | | | |
| * V V V V V V V V V |
| * +-------+---------+---------+-------+--------------------------+---------+---------+----------------------+- |
| * | ... | Seg 1 | Seg 2 | Seg 3 | . . . | Seg 1 | Seg 2 : . . . | |
| * +-------+---------+---------+-------+---------------------------+---------+--------+----------------------+- |
| * \ \ / / \ / |
| * | Cur segment | | | |
| * | | | | |
| * Current OutFrame (being read) Current InFrame (being written) |
| * |
| */ |
| |
| enum |
| { |
| kReadByteAfterFrameHasEnded = 0, // Value returned by ReadByte() when frame has ended. |
| kMessageReadBufferSize = 16, // Size of message buffer array. |
| kUnknownFrameLength = 0xffff, // Value used when frame length is unknown. |
| kSegmentHeaderSize = 2, // Length of the segment header. |
| kSegmentHeaderLengthMask = 0x3fff, // Bit mask to get the length from the segment header |
| |
| kSegmentHeaderNoFlag = 0, // No flags are set. |
| kSegmentHeaderNewFrameFlag = (1 << 15), // Indicates that this segment starts a new frame. |
| kSegmentHeaderMessageIndicatorFlag = (1 << 14), // Indicates this segment ends with a Message. |
| }; |
| |
| enum ReadState |
| { |
| kReadStateInSegment, // In middle of a data segment while reading current (out) frame. |
| kReadStateInMessage, // In middle of a message while reading current (out) frame. |
| kReadStateDone, // Current (out) frame is read fully. |
| }; |
| |
| // Private methods |
| |
| uint8_t * Next(uint8_t *aBufferPtr) const; |
| uint8_t * Advance(uint8_t *aBufPtr, uint16_t aOffset) const; |
| uint16_t GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr) const; |
| |
| uint16_t ReadUint16At(uint8_t *aBufPtr); |
| void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue); |
| |
| otError InFrameFeedByte(uint8_t aByte); |
| otError InFrameBeginSegment(void); |
| void InFrameEndSegment(uint16_t aSegmentHeaderFlags); |
| void InFrameDiscard(void); |
| |
| otError OutFramePrepareSegment(void); |
| void OutFrameMoveToNextSegment(void); |
| otError OutFramePrepareMessage(void); |
| otError OutFrameFillMessageBuffer(void); |
| |
| uint8_t * const mBuffer; // Pointer to the buffer used to store the data. |
| uint8_t * const mBufferEnd; // Points to after the end of buffer. |
| const uint16_t mBufferLength; // Length of the the buffer. |
| |
| BufferCallback mFrameAddedCallback; // Callback to signal when a new frame is added |
| void * mFrameAddedContext; // Context passed to `mFrameAddedCallback`. |
| BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed. |
| void * mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`. |
| |
| otMessageQueue mMessageQueue; // Main message queue. |
| |
| otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written. |
| uint8_t * mWriteFrameStart; // Pointer to start of current frame being written. |
| uint8_t * mWriteSegmentHead; // Pointer to start of current segment in the frame being written. |
| uint8_t * mWriteSegmentTail; // Pointer to end of current segment in the frame being written. |
| FrameTag mWriteFrameTag; // Tag associated with last successfully written frame. |
| |
| ReadState mReadState; // Read state. |
| uint16_t mReadFrameLength; // Length of current frame being read. |
| |
| uint8_t * mReadFrameStart; // Pointer to start of current frame being read. |
| uint8_t * mReadSegmentHead; // Pointer to start of current segment in the frame being read. |
| uint8_t * mReadSegmentTail; // Pointer to end of current segment in the frame being read. |
| uint8_t * mReadPointer; // Pointer to next byte to read (either in segment or in msg buffer). |
| |
| otMessage * mReadMessage; // Current Message in the frame being read. |
| uint16_t mReadMessageOffset; // Offset within current message being read. |
| |
| uint8_t mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read. |
| uint8_t * mReadMessageTail; // Pointer to end of current part in mMessageBuffer. |
| }; |
| |
| } // namespace ot |
| |
| #endif // NCP_FRAME_BUFFER_HPP_ |