1 /* 2 * Copyright (c) 2016, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /** 29 * @file 30 * This file contains definitions for the NCP frame buffer class. 31 */ 32 33 #ifndef NCP_FRAME_BUFFER_HPP_ 34 #define NCP_FRAME_BUFFER_HPP_ 35 36 #include "openthread-spinel-config.h" 37 38 #include <openthread/message.h> 39 40 namespace ot { 41 namespace Spinel { 42 43 /** 44 * Implements a buffer/queue for storing Ncp frames. 45 * 46 * A frame can consist of a sequence of data bytes and/or the content of an `otMessage` or a combination of the two. 47 * `Buffer` implements priority FIFO logic for storing and reading frames. Two priority levels of high and low 48 * are supported. Within same priority level first-in-first-out order is preserved. High priority frames are read 49 * ahead of any low priority ones. 50 * 51 */ 52 class Buffer 53 { 54 friend class Encoder; 55 56 public: 57 /** 58 * Defines the priority of a frame. High priority frames are read before low priority frames. Within same priority 59 * level FIFO order is preserved. 60 * 61 */ 62 enum Priority 63 { 64 kPriorityLow = 0, ///< Indicates low/normal priority for a frame. 65 kPriorityHigh = 1, ///< Indicates high priority for a frame. 66 }; 67 68 /** 69 * Defines the (abstract) frame tag type. The tag is a unique value (within currently queued frames) associated 70 * with a frame in the `Buffer`. Frame tags can be compared with one another using operator `==`. 71 * 72 */ 73 typedef const void *FrameTag; 74 75 /** 76 * Defines the tag value indicating an invalid tag (e.g., when there is no frame). 77 * 78 */ 79 static const FrameTag kInvalidTag; 80 81 /** 82 * Defines the structure to hold a write position for an input frame (frame being written). 83 * 84 * It should be considered as an opaque data structure to users of `Buffer`. 85 * 86 */ 87 struct WritePosition 88 { 89 public: 90 /** 91 * The constructor for a `WritePosition` object. 92 * 93 */ WritePositionot::Spinel::Buffer::WritePosition94 WritePosition(void) 95 : mPosition(nullptr) 96 , mSegmentHead(nullptr) 97 { 98 } 99 100 private: 101 uint8_t *mPosition; // Pointer into buffer corresponding to saved write position. 102 uint8_t *mSegmentHead; // Pointer to segment head. 103 104 friend class Buffer; 105 }; 106 107 /** 108 * Defines a function pointer callback which is invoked to inform a change in `Buffer` either when a new 109 * frame is added/written to `Buffer` or when a frame is removed from `Buffer`. 110 * 111 * @param[in] aContext A pointer to arbitrary context information. 112 * @param[in] aTag The tag associated with the frame which is added or removed. 113 * @param[in] aPriority The priority of frame. 114 * @param[in] aBuffer A pointer to the `Buffer`. 115 * 116 */ 117 typedef void (*BufferCallback)(void *aContext, FrameTag aTag, Priority aPriority, Buffer *aBuffer); 118 119 /** 120 * Initializes an NCP frame buffer. 121 * 122 * @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer. 123 * @param[in] aBufferLength The buffer size (in bytes). 124 * 125 */ 126 Buffer(uint8_t *aBuffer, uint16_t aBufferLength); 127 128 /** 129 * Clears the NCP frame buffer. All the frames are cleared/removed. 130 * 131 */ 132 void Clear(void); 133 134 /** 135 * Sets the FrameAdded callback and its context. 136 * 137 * Subsequent calls to this method will overwrite the previous callback and its context. 138 * 139 * @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer. 140 * @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback. 141 * 142 */ 143 void SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext); 144 145 /** 146 * Sets the FrameRemoved callback and its context. 147 * 148 * Subsequent calls to this method will overwrite the previous callback and its context. 149 * 150 * @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer. 151 * @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback. 152 * 153 */ 154 void SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext); 155 156 /** 157 * Begins a new input frame (InFrame) to be added/written to the frame buffer. 158 159 * If there is a previous frame being written (for which `InFrameEnd()` has not yet been called), calling 160 * `InFrameBegin()` will discard and clear the previous unfinished frame. 161 * 162 * @param[in] aPriority Priority level of the new input frame. 163 * 164 */ 165 void InFrameBegin(Priority aPriority); 166 167 /** 168 * Adds a single byte to current input frame. 169 * 170 * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this 171 * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. 172 * 173 * If no buffer space is available, this method will discard and clear the current input frame and return the 174 * error status `OT_ERROR_NO_BUFS`. 175 * 176 * @param[in] aByte The byte value to add to input frame. 177 * 178 * @retval OT_ERROR_NONE Successfully added given byte to the frame. 179 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add the byte. 180 * @retval OT_ERROR_INVALID_STATE `InFrameBegin()` has not been called earlier to start the frame. 181 * 182 */ 183 otError InFrameFeedByte(uint8_t aByte); 184 185 /** 186 * Adds data to the current input frame. 187 * 188 * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this 189 * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. 190 * 191 * If no buffer space is available, this method will discard and clear the current input frame and return the 192 * error status `OT_ERROR_NO_BUFS`. 193 * 194 * @param[in] aDataBuffer A pointer to data buffer. 195 * @param[in] aDataBufferLength The length of the data buffer. 196 * 197 * @retval OT_ERROR_NONE Successfully added new data to the frame. 198 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data. 199 * @retval OT_ERROR_INVALID_STATE `InFrameBegin()` has not been called earlier to start the frame. 200 * 201 */ 202 otError InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength); 203 204 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE 205 /** 206 * Adds a message to the current input frame. 207 * 208 * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this 209 * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. 210 * 211 * If no buffer space is available, this method will discard and clear the frame and return error status 212 * `OT_ERROR_NO_BUFS`. 213 * 214 * The ownership of the passed-in message @p aMessage changes to `Buffer` ONLY when the entire frame is 215 * successfully finished (i.e., with a successful call to `InFrameEnd()` for the current input frame), and in this 216 * case the `otMessage` instance will be freed once the frame is removed (using `OutFrameRemove()`) from NCP buffer. 217 * However, if the input frame gets discarded before it is finished (e.g., running out of buffer space), the 218 * `otMessage` instance remains unchanged. 219 * 220 * @param[in] aMessage A message to be added to current frame. 221 * 222 * @retval OT_ERROR_NONE Successfully added the message to the frame. 223 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add the message. 224 * @retval OT_ERROR_INVALID_STATE `InFrameBegin()` has not been called earlier to start the frame. 225 * @retval OT_ERROR_INVALID_ARGS If @p aMessage is nullptr. 226 * 227 */ 228 otError InFrameFeedMessage(otMessage *aMessage); 229 #endif 230 231 /** 232 * Gets the current write position in the input frame. 233 * 234 * The write position is returned in @p aPosition. The saved position can later be used to overwrite the frame 235 * content (using `InFrameOverwrite()`) or discard a portion of written frame and move the write pointer back to 236 * the saved position (using `InFrameReset()`). 237 * 238 * @param[out] aPosition A reference to a `WritePosition` to save the current write position. 239 * 240 * @retval OT_ERROR_NONE Successfully saved current write position in @p aPosition. 241 * @retval OT_ERROR_INVALID_STATE `InFrameBegin()` has not been called earlier to start the frame. 242 * 243 */ 244 otError InFrameGetPosition(WritePosition &aPosition); 245 246 /** 247 * Overwrites the previously written content in the current input frame at a given write position. 248 * 249 * The write position @p aPostion must belong to the same input frame saved earlier with `InFrameGetPosition()`. 250 * Does not allow writing beyond the current end of the input frame (i.e., it can only write over 251 * previously added content). If writing @p aDataBufferLength bytes from write position @p aPosition goes beyond 252 * the end, this method does not change the input frame and returns error status `OT_ERROR_INVALID_ARGS`. 253 * Cannot be used if the input frame has an added `otMessage` (i.e., a previous call to 254 * `InFrameFeedMessage()`). 255 * 256 * @param[in] aPosition A reference to the write position. 257 * @param[in] aDataBuffer A pointer to data buffer. 258 * @param[in] aDataBufferLength The length of the data buffer. 259 * 260 * @retval OT_ERROR_NONE Successfully overwrote the data at the given write position. 261 * @retval OT_ERROR_INVALID_STATE No input frame (`InFrameBegin()` has not been called). 262 * @retval OT_ERROR_INVALID_ARGS The given write position is not valid (i.e., if it does not belong to same 263 * input frame), or the input frame has an added `otMessage`, or the write 264 * operation will go beyond the current end of the input frame. 265 * 266 */ 267 otError InFrameOverwrite(const WritePosition &aPosition, const uint8_t *aDataBuffer, uint16_t aDataBufferLength); 268 269 /** 270 * Resets the write position of input frame back to a previously saved position. Any previously 271 * added content after the write position is discarded. 272 * 273 * The write position @p aPosition must belong to the same input frame saved earlier with `InFrameGetPosition()`. 274 * Cannot be used if the input frame has an added `otMessage` (i.e., a previous call to 275 * `InFrameFeedMessage()`). 276 * 277 * @param[in] aPosition A reference to write position 278 * 279 * @retval OT_ERROR_NONE Successfully reset the write position of current input frame.. 280 * @retval OT_ERROR_INVALID_STATE No input frame (`InFrameBegin()` has not been called). 281 * @retval OT_ERROR_INVALID_ARGS The given write position is not valid (does not belong to same input frame), or 282 * the input frame has an added `otMessage`. 283 * 284 */ 285 otError InFrameReset(const WritePosition &aPosition); 286 287 /** 288 * Gets the distance (number of bytes) from a given saved position to current end of frame. 289 * 290 * The write position @p aPosition must belong to the same input frame saved earlier with `InFrameGetPosition()`. 291 * Cannot be used if the input frame has an added `otMessage` (i.e., a previous call to 292 * `InFrameFeedMessage()`). In case of invalid argument, this method returns zero. 293 * 294 * @param[in] aPosition A reference to write position 295 * 296 * @returns The distance (number of bytes) from a write position to current end of frame, or zero for invalid 297 * arguments. 298 * 299 */ 300 uint16_t InFrameGetDistance(const WritePosition &aPosition) const; 301 302 /** 303 * Finalizes/ends the current input frame being written to the buffer. 304 * 305 * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this 306 * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. 307 * 308 * If no buffer space is available, this method will discard and clear the frame and return error status 309 * `OT_ERROR_NO_BUFS`. 310 * 311 * @retval OT_ERROR_NONE Successfully ended the input frame. 312 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. 313 * @retval OT_ERROR_INVALID_STATE `InFrameBegin()` has not been called earlier to start the frame. 314 * 315 */ 316 otError InFrameEnd(void); 317 318 /** 319 * Returns the tag assigned to last successfully written/added frame to NcpBuffer (i.e., last input 320 * frame for which `InFrameEnd()` was called and returned success status). The tag is a unique value (within 321 * currently queued frames) associated with a frame in the `Buffer`. The tag can be used to identify the 322 * same frame when it is read and removed from the NcpBuffer. Tags can be compared using operator `==`. 323 * 324 * @returns The tag of the last successfully written frame, or `kInvalidTag` if no frame is written so far. 325 */ 326 FrameTag InFrameGetLastTag(void) const; 327 328 /** 329 * Checks if the buffer is empty. A non-empty buffer contains at least one full frame for reading. 330 * 331 * @retval TRUE Buffer is not empty and contains at least one full frame for reading. 332 * @retval FALSE Buffer is empty and contains no frame for reading. 333 * 334 */ 335 bool IsEmpty(void) const; 336 337 /** 338 * Begins/prepares an output frame to be read from the frame buffer if there is no current active output 339 * frame, or resets the read offset if there is a current active output frame. 340 * 341 * The NCP buffer maintains a read offset for the current frame being read. Before reading any bytes from the frame 342 * this method should be called to prepare the frame and set the read offset. 343 * 344 * If part or even all of current frame has been read, a sub-sequent call to this method will reset the read 345 * offset back to beginning of current output frame (note that the current output frame will remain unchanged even 346 * in case where a higher priority frame was written to buffer while reading current output frame). A prepared 347 * output frame will stay active as current output frame until it is explicitly removed using `OutFrameRemove()`. 348 * 349 * @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading. 350 * @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading. 351 * 352 */ 353 otError OutFrameBegin(void); 354 355 /** 356 * Checks if the current output frame (being read) has ended. 357 * 358 * The NCP buffer maintains a read offset for the current output frame being read. This method returns `true` if 359 * the read offset has reached the end of the frame and there are no more bytes available to read, or if there is 360 * no currently active output frame. 361 * 362 * @retval TRUE Frame has ended (no more bytes available to read from current output frame), or 363 * there is currently no prepared/active output frame. 364 * @retval FALSE Frame still has more data to read. 365 * 366 */ 367 bool OutFrameHasEnded(void); 368 369 /** 370 * Reads and returns the next byte from the current output frame. 371 * 372 * The NCP buffer maintains a read offset for the current output frame being read. This method reads and returns 373 * the next byte from the current frame and moves the read offset forward. If read offset is already at the end 374 * current output frame, this method returns zero. 375 * 376 * @returns The next byte from the current output frame or zero if current output frame has ended or there is 377 * prepared/active output from. 378 * 379 */ 380 uint8_t OutFrameReadByte(void); 381 382 /** 383 * Reads and copies bytes from the current output frame into a given buffer. 384 * 385 * The NCP buffer maintains a read offset for the current output frame being read. This method attempts to read 386 * the given number of bytes (@p aReadLength) from the current frame and copies the bytes into the given 387 * data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are fewer bytes 388 * remaining in current frame than the requested @p aReadLength, the available bytes are read/copied. This methods 389 * returns the number of bytes read from frame and copied into @p aDataBuffer. 390 * 391 * @param[in] aReadLength Number of bytes to read. 392 * @param[out] aDataBuffer A pointer to a data buffer. 393 * 394 * @returns The number of bytes read and copied into data buffer. 395 * 396 */ 397 uint16_t OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer); 398 399 /** 400 * Removes the current or front output frame from the buffer. 401 * 402 * If there is an active output from being read (an output frame was prepared earlier with a successful call to 403 * `OutFrameBegin()`), this method removes the current active output frame. If there is no current active frame, 404 * the front frame in the queue (the next frame which would have been read) will be removed. 405 * 406 * When a frame is removed all its associated messages will be freed. 407 * 408 * If the remove operation is successful, this method will invoke the `FrameRemovedCallback` (if not nullptr) before 409 * returning the success state. 410 * 411 * @retval OT_ERROR_NONE Successfully removed the front frame. 412 * @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove. 413 * 414 */ 415 otError OutFrameRemove(void); 416 417 /** 418 * Returns the number of bytes (length) of current or front frame in the NCP frame buffer. 419 * 420 * If there is an active output from being read (an output frame was prepared earlier with successful call to 421 * `OutFrameBegin()`), this method returns the length of the current output frame. If there is no current active 422 * frame, the length of the front frame in the queue (the next frame which would have been read) will be returned. 423 * 424 * If there is no frame in buffer, this method returns zero. 425 * 426 * @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer. 427 * 428 */ 429 uint16_t OutFrameGetLength(void); 430 431 /** 432 * Returns the tag value associated to current or front frame in the NCP frame buffer. 433 * 434 * If there is an active output from being read (an output frame was prepared earlier with successful call to 435 * `OutFrameBegin()`), this method returns the tag associated with current output frame. If there is no current 436 * active frame, the tag associated with the front frame in the queue (the next frame which would have been read) 437 * will be returned. 438 * 439 * If there is no frame in buffer, this method returns `kInvalidTag`. 440 * 441 * @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer. 442 * 443 */ 444 FrameTag OutFrameGetTag(void); 445 446 private: 447 /* 448 * `Buffer` Implementation 449 * ------------------------------- 450 * 451 * `Buffer` internally stores a frame as a sequence of data segments. Each segment stores a portion of 452 * frame. The data segments are stored in the main buffer `mBuffer`. `mBuffer` is utilized as a circular buffer. 453 454 * The content of messages (which are added using `InFrameFeedMessage()`) are not directly copied in the `mBuffer` 455 * but instead they are enqueued in a message queue `mMessageQueue`. 456 * 457 * Every data segments starts with a header before the data portion. The header is 2 bytes long with the following 458 * format: 459 * 460 * Bit 0-13: Give the length of the data segment (max segment len is 2^14 = 16,384 bytes). 461 * Bit 14: Flag bit set to indicate that this segment has an associated `Message` (appended to its end). 462 * Bit 15: Flag bit set to indicate that this segment defines the start of a new frame. 463 * 464 * Bit 15 Bit 14 Bits: 0 - 13 465 * +--------------+--------------+--------------------------------------------------------+ 466 * | New Frame | Has Message | Length of segment (excluding the header) | 467 * +--------------+--------------+--------------------------------------------------------+ 468 * 469 * The header is encoded in big-endian (msb first) style. 470 471 * Consider the following calls to create a frame: 472 * 473 * ncpBuffer.InFrameBegin(); 474 * ncpBuffer.InFrameFeedData("Hello", 5); 475 * ncpBuffer.InFrameFeedData("There", 5); 476 * ncpBuffer.InFrameFeedMessage(*someMessage); 477 * ncpBuffer.InFrameFeedData("Bye", 3); 478 * ncpBuffer.InFrameEnd(); 479 * 480 * This frame is stored as two segments: 481 * 482 * - Segment #1 contains "HelloThere" with a header value `0xC00A` which shows that this segment contains 10 483 * data bytes, and it starts a new frame, and also includes an appended message from the message queue. 484 * 485 * - Segment #2 contains "Bye" with a header value of `0x0003` showing length of 3 and no appended message. 486 * 487 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ 488 * | C0 | 0A | 'H' | 'e' | 'l' | 'l' | 'o' | 'T' | 'h' | 'e' | 'r' | 'e' | 00 | 03 | 'B' | 'y' | 'e' | 489 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ 490 * \ / \ / 491 * Segment #1 Header Segment #2 Header 492 * 493 * 494 * `Buffer` uses the `mBuffer` as a circular/ring buffer. To support two frame priorities the buffer is 495 * divided into two high-priority and low-priority regions. The high priority frames are stored in buffer in 496 * backward direction while the low-priority frames use the buffer in forward direction. This model ensures the 497 * available buffer space is utilized efficiently between all frame types. 498 * 499 * mReadFrameStart[kPriorityLow] 500 * | 501 * | mWriteFrameStart[kPriorityLow] 502 * | | 503 * V Low Priority Frames V 504 * --------------+------------------------------+------------------------------+------------------ 505 * ... | <-------- | --------> | ... 506 * --------------+------------------------------+------------------------------+------------------ 507 * ^ High Priority Frames ^ 508 * | | 509 * | mReadFrameStart[kPriorityHigh] 510 * | 511 * mWriteFrameStart[kPriorityHigh] 512 * 513 * 514 * 515 * When frames are removed, if possible, the `mReadFrameStart` and `mWriteFrameStart` pointers of the two priority 516 * levels are moved closer to avoid gaps. 517 * 518 * For an output frame (frame being read), Buffer maintains a `ReadState` along with a set of pointers 519 * into the buffer: 520 * 521 * mReadFrameStart[priority]: Start of the current/front frame. 522 * | 523 * | mReadSegmentHead: Start of the current segment. 524 * | | 525 * | | mReadPointer: Pointer to the next byte to read. 526 * | | | 527 * | | | mReadSegmentTail: End of the current segment. 528 * | | | | 529 * V V V V 530 * ---------+------------+--------------------------+------+----------------+----------------------------------- 531 * ... | Segment 1 | Segment 2 | ... | Last Segment | ... (possible) next frame 532 * ---------+------------+--------------------------+------+----------------+----------------------------------- 533 * \ | | / 534 * | \------------v-----------/ | 535 * | Current Segment | 536 * | | 537 * \---------------------------V-----------------------------/ 538 * Current OutFrame (being read) 539 * 540 * Note that the diagram above shows the pointers for a low-priority frame (with pointers increasing in forward 541 * direction). 542 * 543 * The `ReadState` indicates the state of current output frame and its read offset (e.g., if read offset is in 544 * middle of a segment or if it is is middle of an appended message, or if we are done with entire frame). 545 * 546 * For an input frame (frame being written), the following pointers are maintained: 547 * 548 * mWriteFrameWrite[priority]: Start of the current/next frame being written. 549 * | 550 * | mWriteSegmentHead: Start of the current segment of the active input frame. 551 * | | 552 * | | mWriteSegmentTail: Pointer to the next byte to write. 553 * | | | 554 * | | | 555 * | | | 556 * V V V 557 * ------------------+------------------+------------------------------------------------------------------- 558 * Previous Frames | Segment 1 | Segment 2 : . . . 559 * ------------------+------------------+------------------------------------------------------------------- 560 * \ / 561 * | | 562 * \------------------V-----------------/ 563 * Current InFrame (being written) 564 * 565 * 566 */ 567 568 enum 569 { 570 kReadByteAfterFrameHasEnded = 0, // Value returned by ReadByte() when frame has ended. 571 kMessageReadBufferSize = 16, // Size of message buffer array `mMessageBuffer`. 572 kUnknownFrameLength = 0xffff, // Value used when frame length is unknown. 573 kSegmentHeaderSize = 2, // Length of the segment header. 574 kSegmentHeaderLengthMask = 0x3fff, // Bit mask to get the length from the segment header 575 kMaxSegments = 10, // Max number of segments allowed in a frame 576 577 kSegmentHeaderNoFlag = 0, // No flags are set. 578 kSegmentHeaderNewFrameFlag = (1 << 15), // Indicates that this segment starts a new frame. 579 kSegmentHeaderMessageIndicatorFlag = (1 << 14), // Indicates this segment ends with a Message. 580 581 kNumPrios = (kPriorityHigh + 1), // Number of priorities. 582 }; 583 584 enum ReadState 585 { 586 kReadStateNotActive, // No current prepared output frame. 587 kReadStateInSegment, // In middle of a data segment while reading current frame. 588 kReadStateInMessage, // In middle of a message while reading current frame. 589 kReadStateDone, // Current output frame is read fully. 590 }; 591 592 enum Direction 593 { 594 kForward = kPriorityLow, 595 kBackward = kPriorityHigh, 596 kUnknown, 597 }; 598 599 uint8_t *GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const; 600 uint16_t GetDistance(const uint8_t *aStartPtr, const uint8_t *aEndPtr, Direction aDirection) const; 601 602 uint16_t ReadUint16At(uint8_t *aBufPtr, Direction aDirection); 603 void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection); 604 605 bool HasFrame(Priority aPriority) const; 606 void UpdateReadWriteStartPointers(void); 607 608 otError InFrameAppend(uint8_t aByte); 609 otError InFrameBeginSegment(void); 610 void InFrameEndSegment(uint16_t aSegmentHeaderFlags); 611 void InFrameDiscard(void); 612 bool InFrameIsWriting(Priority aPriority) const; 613 614 void OutFrameSelectReadDirection(void); 615 otError OutFramePrepareSegment(void); 616 void OutFrameMoveToNextSegment(void); 617 618 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE 619 otError OutFramePrepareMessage(void); 620 otError OutFrameFillMessageBuffer(void); 621 #endif 622 623 uint8_t *const mBuffer; // Pointer to the buffer used to store the data. 624 uint8_t *const mBufferEnd; // Points to after the end of buffer. 625 const uint16_t mBufferLength; // Length of the buffer. 626 627 BufferCallback mFrameAddedCallback; // Callback to signal when a new frame is added 628 void *mFrameAddedContext; // Context passed to `mFrameAddedCallback`. 629 BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed. 630 void *mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`. 631 632 Direction mWriteDirection; // Direction (priority) for current frame being read. 633 uint8_t *mWriteFrameStart[kNumPrios]; // Pointer to start of current frame being written. 634 uint8_t *mWriteSegmentHead; // Pointer to start of current segment in the frame being written. 635 uint8_t *mWriteSegmentTail; // Pointer to end of current segment in the frame being written. 636 FrameTag mWriteFrameTag; // Tag associated with last successfully written frame. 637 638 Direction mReadDirection; // Direction (priority) for current frame being read. 639 ReadState mReadState; // Read state. 640 uint16_t mReadFrameLength; // Length of current frame being read. 641 642 uint8_t *mReadFrameStart[kNumPrios]; // Pointer to start of current frame being read. 643 uint8_t *mReadSegmentHead; // Pointer to start of current segment in the frame being read. 644 uint8_t *mReadSegmentTail; // Pointer to end of current segment in the frame being read. 645 uint8_t *mReadPointer; // Pointer to next byte to read (either in segment or in msg buffer). 646 647 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE 648 otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written. 649 otMessageQueue mMessageQueue[kNumPrios]; // Main message queues. 650 otMessage *mReadMessage; // Current Message in the frame being read. 651 uint16_t mReadMessageOffset; // Offset within current message being read. 652 uint8_t mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read. 653 uint8_t *mReadMessageTail; // Pointer to end of current part in mMessageBuffer. 654 #endif 655 }; 656 657 } // namespace Spinel 658 } // namespace ot 659 660 #endif // NCP_FRAME_BUFFER_HPP_ 661