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 implements NCP frame buffer class.
31  */
32 
33 #include "spinel_buffer.hpp"
34 
35 #include <assert.h>
36 
37 #include "lib/utils/utils.hpp"
38 
39 namespace ot {
40 namespace Spinel {
41 
42 const Buffer::FrameTag Buffer::kInvalidTag = nullptr;
43 
Buffer(uint8_t * aBuffer,uint16_t aBufferLength)44 Buffer::Buffer(uint8_t *aBuffer, uint16_t aBufferLength)
45     : mBuffer(aBuffer)
46     , mBufferEnd(aBuffer + aBufferLength)
47     , mBufferLength(aBufferLength)
48 {
49 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
50     for (otMessageQueue &queue : mMessageQueue)
51     {
52         otMessageQueueInit(&queue);
53     }
54 
55     otMessageQueueInit(&mWriteFrameMessageQueue);
56 #endif
57 
58     SetFrameAddedCallback(nullptr, nullptr);
59     SetFrameRemovedCallback(nullptr, nullptr);
60     Clear();
61 }
62 
Clear(void)63 void Buffer::Clear(void)
64 {
65 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
66     otMessage *message;
67 #endif
68 
69     // Write (InFrame) related variables
70     mWriteFrameStart[kPriorityLow]  = mBuffer;
71     mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
72     mWriteDirection                 = kUnknown;
73     mWriteSegmentHead               = mBuffer;
74     mWriteSegmentTail               = mBuffer;
75     mWriteFrameTag                  = kInvalidTag;
76 
77     // Read (OutFrame) related variables
78     mReadDirection   = kForward;
79     mReadState       = kReadStateNotActive;
80     mReadFrameLength = kUnknownFrameLength;
81 
82     mReadFrameStart[kPriorityLow]  = mBuffer;
83     mReadFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward);
84     mReadSegmentHead               = mBuffer;
85     mReadSegmentTail               = mBuffer;
86     mReadPointer                   = mBuffer;
87 
88 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
89     mReadMessage       = nullptr;
90     mReadMessageOffset = 0;
91     mReadMessageTail   = mMessageBuffer;
92 
93     // Free all messages in the queues.
94 
95     while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != nullptr)
96     {
97         otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
98 
99         // Note that messages associated with current (unfinished) input frame
100         // are not yet owned by the `Buffer` and therefore should not
101         // be freed.
102     }
103 
104     for (otMessageQueue &queue : mMessageQueue)
105     {
106         while ((message = otMessageQueueGetHead(&queue)) != nullptr)
107         {
108             otMessageQueueDequeue(&queue, message);
109             otMessageFree(message);
110         }
111     }
112 #endif
113 }
114 
SetFrameAddedCallback(BufferCallback aFrameAddedCallback,void * aFrameAddedContext)115 void Buffer::SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext)
116 {
117     mFrameAddedCallback = aFrameAddedCallback;
118     mFrameAddedContext  = aFrameAddedContext;
119 }
120 
SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback,void * aFrameRemovedContext)121 void Buffer::SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext)
122 {
123     mFrameRemovedCallback = aFrameRemovedCallback;
124     mFrameRemovedContext  = aFrameRemovedContext;
125 }
126 
127 // Returns an updated buffer pointer by moving forward/backward (based on `aDirection`) from `aBufPtr` by a given
128 // offset. The resulting buffer pointer is ensured to stay within the `mBuffer` boundaries.
GetUpdatedBufPtr(uint8_t * aBufPtr,uint16_t aOffset,Direction aDirection) const129 uint8_t *Buffer::GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const
130 {
131     uint8_t *ptr = aBufPtr;
132 
133     switch (aDirection)
134     {
135     case kForward:
136         ptr += aOffset;
137 
138         while (ptr >= mBufferEnd)
139         {
140             ptr -= mBufferLength;
141         }
142 
143         break;
144 
145     case kBackward:
146         ptr -= aOffset;
147 
148         while (ptr < mBuffer)
149         {
150             ptr += mBufferLength;
151         }
152 
153         break;
154 
155     case kUnknown:
156         assert(false);
157         OT_UNREACHABLE_CODE(break);
158     }
159 
160     return ptr;
161 }
162 
163 // Gets the distance between two buffer pointers (adjusts for the wrap-around) given a direction (forward or backward).
GetDistance(const uint8_t * aStartPtr,const uint8_t * aEndPtr,Direction aDirection) const164 uint16_t Buffer::GetDistance(const uint8_t *aStartPtr, const uint8_t *aEndPtr, Direction aDirection) const
165 {
166     size_t distance = 0;
167 
168     switch (aDirection)
169     {
170     case kForward:
171 
172         if (aEndPtr >= aStartPtr)
173         {
174             distance = static_cast<size_t>(aEndPtr - aStartPtr);
175         }
176         else
177         {
178             distance = static_cast<size_t>(mBufferEnd - aStartPtr);
179             distance += static_cast<size_t>(aEndPtr - mBuffer);
180         }
181 
182         break;
183 
184     case kBackward:
185 
186         if (aEndPtr <= aStartPtr)
187         {
188             distance = static_cast<size_t>(aStartPtr - aEndPtr);
189         }
190         else
191         {
192             distance = static_cast<size_t>(mBufferEnd - aEndPtr);
193             distance += static_cast<size_t>(aStartPtr - mBuffer);
194         }
195 
196         break;
197 
198     case kUnknown:
199         assert(false);
200         OT_UNREACHABLE_CODE(break);
201     }
202 
203     return static_cast<uint16_t>(distance);
204 }
205 
206 // Writes a uint16 value at the given buffer pointer (big-endian style).
WriteUint16At(uint8_t * aBufPtr,uint16_t aValue,Direction aDirection)207 void Buffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection)
208 {
209     *aBufPtr                                  = (aValue >> 8);
210     *GetUpdatedBufPtr(aBufPtr, 1, aDirection) = (aValue & 0xff);
211 }
212 
213 // Reads a uint16 value at the given buffer pointer (big-endian style).
ReadUint16At(uint8_t * aBufPtr,Direction aDirection)214 uint16_t Buffer::ReadUint16At(uint8_t *aBufPtr, Direction aDirection)
215 {
216     uint16_t value;
217 
218     value = static_cast<uint16_t>((*aBufPtr) << 8);
219     value += *GetUpdatedBufPtr(aBufPtr, 1, aDirection);
220 
221     return value;
222 }
223 
224 // Appends a byte at the write tail and updates the tail, discards the frame if buffer gets full.
InFrameAppend(uint8_t aByte)225 otError Buffer::InFrameAppend(uint8_t aByte)
226 {
227     otError  error = OT_ERROR_NONE;
228     uint8_t *newTail;
229 
230     assert(mWriteDirection != kUnknown);
231 
232     newTail = GetUpdatedBufPtr(mWriteSegmentTail, 1, mWriteDirection);
233 
234     // Ensure the `newTail` has not reached the `mWriteFrameStart` for other direction (other priority level).
235     if (newTail != mWriteFrameStart[(mWriteDirection == kForward) ? kBackward : kForward])
236     {
237         *mWriteSegmentTail = aByte;
238         mWriteSegmentTail  = newTail;
239     }
240     else
241     {
242         error = OT_ERROR_NO_BUFS;
243         InFrameDiscard();
244     }
245 
246     return error;
247 }
248 
249 // This method begins a new segment (if one is not already open).
InFrameBeginSegment(void)250 otError Buffer::InFrameBeginSegment(void)
251 {
252     otError  error       = OT_ERROR_NONE;
253     uint16_t headerFlags = kSegmentHeaderNoFlag;
254 
255     // Verify that segment is not yet started (i.e., head and tail are the same).
256     EXPECT(mWriteSegmentHead == mWriteSegmentTail, NO_ACTION);
257 
258     // Check if this is the start of a new frame (i.e., frame start is same as segment head).
259     if (mWriteFrameStart[mWriteDirection] == mWriteSegmentHead)
260     {
261         headerFlags |= kSegmentHeaderNewFrameFlag;
262     }
263 
264     // Reserve space for the segment header.
265     for (uint16_t i = kSegmentHeaderSize; i; i--)
266     {
267         EXPECT_NO_ERROR(error = InFrameAppend(0));
268     }
269 
270     // Write the flags at the segment head.
271     WriteUint16At(mWriteSegmentHead, headerFlags, mWriteDirection);
272 
273 exit:
274     return error;
275 }
276 
277 // This function closes/ends the current segment.
InFrameEndSegment(uint16_t aSegmentHeaderFlags)278 void Buffer::InFrameEndSegment(uint16_t aSegmentHeaderFlags)
279 {
280     uint16_t segmentLength;
281     uint16_t header;
282 
283     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
284 
285     if (segmentLength >= kSegmentHeaderSize)
286     {
287         // Reduce the header size.
288         segmentLength -= kSegmentHeaderSize;
289 
290         // Update the length and the flags in segment header (at segment head pointer).
291         header = ReadUint16At(mWriteSegmentHead, mWriteDirection);
292         header |= (segmentLength & kSegmentHeaderLengthMask);
293         header |= aSegmentHeaderFlags;
294         WriteUint16At(mWriteSegmentHead, header, mWriteDirection);
295 
296         // Move the segment head to current tail (to be ready for a possible next segment).
297         mWriteSegmentHead = mWriteSegmentTail;
298     }
299     else
300     {
301         // Remove the current segment (move the tail back to head).
302         mWriteSegmentTail = mWriteSegmentHead;
303     }
304 }
305 
306 // This method discards the current frame being written.
InFrameDiscard(void)307 void Buffer::InFrameDiscard(void)
308 {
309 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
310     otMessage *message;
311 #endif
312 
313     EXPECT(mWriteDirection != kUnknown, NO_ACTION);
314 
315     // Move the write segment head and tail pointers back to frame start.
316     mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
317 
318 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
319     while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != nullptr)
320     {
321         otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
322 
323         // Note that messages associated with current (unfinished) input frame
324         // being discarded, are not yet owned by the `Buffer` and
325         // therefore should not be freed.
326     }
327 #endif
328 
329     mWriteDirection = kUnknown;
330 
331 exit:
332     UpdateReadWriteStartPointers();
333 }
334 
335 // Returns `true` if in middle of writing a frame with given priority.
InFrameIsWriting(Priority aPriority) const336 bool Buffer::InFrameIsWriting(Priority aPriority) const
337 {
338     return (mWriteDirection == static_cast<Direction>(aPriority));
339 }
340 
InFrameBegin(Priority aPriority)341 void Buffer::InFrameBegin(Priority aPriority)
342 {
343     // Discard any previous unfinished frame.
344     InFrameDiscard();
345 
346     switch (aPriority)
347     {
348     case kPriorityHigh:
349         mWriteDirection = kBackward;
350         break;
351 
352     case kPriorityLow:
353         mWriteDirection = kForward;
354         break;
355     }
356 
357     // Set up the segment head and tail
358     mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection];
359 }
360 
InFrameFeedByte(uint8_t aByte)361 otError Buffer::InFrameFeedByte(uint8_t aByte)
362 {
363     otError error = OT_ERROR_NONE;
364 
365     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
366 
367     // Begin a new segment (if we are not in middle of segment already).
368     EXPECT_NO_ERROR(error = InFrameBeginSegment());
369 
370     error = InFrameAppend(aByte);
371 
372 exit:
373     return error;
374 }
375 
InFrameFeedData(const uint8_t * aDataBuffer,uint16_t aDataBufferLength)376 otError Buffer::InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
377 {
378     otError error = OT_ERROR_NONE;
379 
380     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
381 
382     // Begin a new segment (if we are not in middle of segment already).
383     EXPECT_NO_ERROR(error = InFrameBeginSegment());
384 
385     // Write the data buffer
386     while (aDataBufferLength--)
387     {
388         EXPECT_NO_ERROR(error = InFrameAppend(*aDataBuffer++));
389     }
390 
391 exit:
392     return error;
393 }
394 
395 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
InFrameFeedMessage(otMessage * aMessage)396 otError Buffer::InFrameFeedMessage(otMessage *aMessage)
397 {
398     otError error = OT_ERROR_NONE;
399 
400     EXPECT(aMessage != nullptr, error = OT_ERROR_INVALID_ARGS);
401     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
402 
403     // Begin a new segment (if we are not in middle of segment already).
404     EXPECT_NO_ERROR(error = InFrameBeginSegment());
405 
406     // Enqueue the message in the current write frame queue.
407     otMessageQueueEnqueue(&mWriteFrameMessageQueue, aMessage);
408 
409     // End/Close the current segment marking the flag that it contains an associated message.
410     InFrameEndSegment(kSegmentHeaderMessageIndicatorFlag);
411 
412 exit:
413     return error;
414 }
415 #endif
416 
InFrameGetPosition(WritePosition & aPosition)417 otError Buffer::InFrameGetPosition(WritePosition &aPosition)
418 {
419     otError error = OT_ERROR_NONE;
420 
421     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
422 
423     // Begin a new segment (if we are not in middle of segment already).
424     EXPECT_NO_ERROR(error = InFrameBeginSegment());
425 
426     aPosition.mPosition    = mWriteSegmentTail;
427     aPosition.mSegmentHead = mWriteSegmentHead;
428 
429 exit:
430     return error;
431 }
432 
InFrameOverwrite(const WritePosition & aPosition,const uint8_t * aDataBuffer,uint16_t aDataBufferLength)433 otError Buffer::InFrameOverwrite(const WritePosition &aPosition, const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
434 {
435     otError  error = OT_ERROR_NONE;
436     uint8_t *bufPtr;
437     uint16_t segmentLength;
438     uint16_t distance;
439 
440     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
441 
442     EXPECT(aPosition.mSegmentHead == mWriteSegmentHead, error = OT_ERROR_INVALID_ARGS);
443 
444     // Ensure the overwrite does not go beyond current segment tail.
445     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
446     distance      = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
447     EXPECT(distance + aDataBufferLength <= segmentLength, error = OT_ERROR_INVALID_ARGS);
448 
449     bufPtr = aPosition.mPosition;
450     while (aDataBufferLength > 0)
451     {
452         *bufPtr = *aDataBuffer;
453 
454         aDataBuffer++;
455         aDataBufferLength--;
456 
457         bufPtr = GetUpdatedBufPtr(bufPtr, 1, mWriteDirection);
458     }
459 
460 exit:
461     return error;
462 }
463 
InFrameGetDistance(const WritePosition & aPosition) const464 uint16_t Buffer::InFrameGetDistance(const WritePosition &aPosition) const
465 {
466     uint16_t distance = 0;
467     uint16_t segmentLength;
468     uint16_t offset;
469 
470     EXPECT(mWriteDirection != kUnknown, NO_ACTION);
471     EXPECT(aPosition.mSegmentHead == mWriteSegmentHead, NO_ACTION);
472 
473     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
474     offset        = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
475     EXPECT(offset < segmentLength, NO_ACTION);
476 
477     distance = GetDistance(aPosition.mPosition, mWriteSegmentTail, mWriteDirection);
478 
479 exit:
480     return distance;
481 }
482 
InFrameReset(const WritePosition & aPosition)483 otError Buffer::InFrameReset(const WritePosition &aPosition)
484 {
485     otError  error = OT_ERROR_NONE;
486     uint16_t segmentLength;
487     uint16_t offset;
488 
489     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
490     EXPECT(aPosition.mSegmentHead == mWriteSegmentHead, error = OT_ERROR_INVALID_ARGS);
491 
492     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
493     offset        = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
494     EXPECT(offset < segmentLength, error = OT_ERROR_INVALID_ARGS);
495 
496     mWriteSegmentTail = aPosition.mPosition;
497 
498 exit:
499     return error;
500 }
501 
InFrameEnd(void)502 otError Buffer::InFrameEnd(void)
503 {
504 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
505     otMessage *message;
506 #endif
507     otError error = OT_ERROR_NONE;
508 
509     EXPECT(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
510 
511     // End/Close the current segment (if any).
512     InFrameEndSegment(kSegmentHeaderNoFlag);
513 
514     // Save and use the frame start pointer as the tag associated with the frame.
515     mWriteFrameTag = mWriteFrameStart[mWriteDirection];
516 
517     // Update the frame start pointer to current segment head to be ready for next frame.
518     mWriteFrameStart[mWriteDirection] = mWriteSegmentHead;
519 
520 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
521     // Move all the messages from the frame queue to the main queue.
522     while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != nullptr)
523     {
524         otMessageQueueDequeue(&mWriteFrameMessageQueue, message);
525         otMessageQueueEnqueue(&mMessageQueue[mWriteDirection], message);
526     }
527 #endif
528 
529     if (mFrameAddedCallback != nullptr)
530     {
531         mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, static_cast<Priority>(mWriteDirection), this);
532     }
533 
534     mWriteDirection = kUnknown;
535 
536 exit:
537     return error;
538 }
539 
InFrameGetLastTag(void) const540 Buffer::FrameTag Buffer::InFrameGetLastTag(void) const { return mWriteFrameTag; }
541 
HasFrame(Priority aPriority) const542 bool Buffer::HasFrame(Priority aPriority) const { return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority]; }
543 
IsEmpty(void) const544 bool Buffer::IsEmpty(void) const { return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow); }
545 
OutFrameSelectReadDirection(void)546 void Buffer::OutFrameSelectReadDirection(void)
547 {
548     if (mReadState == kReadStateNotActive)
549     {
550         mReadDirection = HasFrame(kPriorityHigh) ? kBackward : kForward;
551     }
552 }
553 
554 // Start/Prepare a new segment for reading.
OutFramePrepareSegment(void)555 otError Buffer::OutFramePrepareSegment(void)
556 {
557     otError  error = OT_ERROR_NONE;
558     uint16_t header;
559 
560     while (true)
561     {
562         // Go to the next segment (set the segment head to current segment's end/tail).
563         mReadSegmentHead = mReadSegmentTail;
564 
565         // Ensure there is something to read (i.e. segment head is not at start of frame being written).
566         EXPECT(mReadSegmentHead != mWriteFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
567 
568         // Read the segment header.
569         header = ReadUint16At(mReadSegmentHead, mReadDirection);
570 
571         // Check if this segment is the start of a frame.
572         if (header & kSegmentHeaderNewFrameFlag)
573         {
574             // Ensure that this segment is start of current frame, otherwise the current frame is finished.
575             EXPECT(mReadSegmentHead == mReadFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
576         }
577 
578         // Find tail/end of current segment.
579         mReadSegmentTail = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask),
580                                             mReadDirection);
581 
582         // Update the current read pointer to skip the segment header.
583         mReadPointer = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize, mReadDirection);
584 
585         // Check if there are data bytes to be read in this segment (i.e. read pointer not at the tail).
586         if (mReadPointer != mReadSegmentTail)
587         {
588             // Update the state to `InSegment` and return.
589             mReadState = kReadStateInSegment;
590 
591             EXIT_NOW();
592         }
593 
594 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
595         // No data in this segment,  prepare any appended/associated message of this segment.
596         if (OutFramePrepareMessage() == OT_ERROR_NONE)
597         {
598             EXIT_NOW();
599         }
600 
601         // If there is no message (`PrepareMessage()` returned an error), loop back to prepare the next segment.
602 #endif
603     }
604 
605 exit:
606 
607     if (error != OT_ERROR_NONE)
608     {
609         mReadState = kReadStateDone;
610     }
611 
612     return error;
613 }
614 
615 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
616 // This method prepares an associated message in current segment and fills the message buffer. It returns
617 // ThreadError_NotFound if there is no message or if the message has no content.
OutFramePrepareMessage(void)618 otError Buffer::OutFramePrepareMessage(void)
619 {
620     otError  error = OT_ERROR_NONE;
621     uint16_t header;
622 
623     // Read the segment header
624     header = ReadUint16At(mReadSegmentHead, mReadDirection);
625 
626     // Ensure that the segment header indicates that there is an associated message or return `NotFound` error.
627     EXPECT((header & kSegmentHeaderMessageIndicatorFlag) != 0, error = OT_ERROR_NOT_FOUND);
628 
629     // Update the current message from the queue.
630     mReadMessage = (mReadMessage == nullptr) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
631                                              : otMessageQueueGetNext(&mMessageQueue[mReadDirection], mReadMessage);
632 
633     EXPECT(mReadMessage != nullptr, error = OT_ERROR_NOT_FOUND);
634 
635     // Reset the offset for reading the message.
636     mReadMessageOffset = 0;
637 
638     // Fill the content from current message into the message buffer.
639     EXPECT_NO_ERROR(error = OutFrameFillMessageBuffer());
640 
641     // If all successful, set the state to `InMessage`.
642     mReadState = kReadStateInMessage;
643 
644 exit:
645     return error;
646 }
647 
648 // This method fills content from current message into the message buffer. It returns OT_ERROR_NOT_FOUND if no more
649 // content in the current message.
OutFrameFillMessageBuffer(void)650 otError Buffer::OutFrameFillMessageBuffer(void)
651 {
652     otError error = OT_ERROR_NONE;
653     int     readLength;
654 
655     EXPECT(mReadMessage != nullptr, error = OT_ERROR_NOT_FOUND);
656 
657     EXPECT(mReadMessageOffset < otMessageGetLength(mReadMessage), error = OT_ERROR_NOT_FOUND);
658 
659     // Read portion of current message from the offset into message buffer.
660     readLength = otMessageRead(mReadMessage, mReadMessageOffset, mMessageBuffer, sizeof(mMessageBuffer));
661 
662     EXPECT(readLength > 0, error = OT_ERROR_NOT_FOUND);
663 
664     // Update the message offset, set up the message tail, and set read pointer to start of message buffer.
665 
666     mReadMessageOffset += readLength;
667 
668     mReadMessageTail = mMessageBuffer + readLength;
669 
670     mReadPointer = mMessageBuffer;
671 
672 exit:
673     return error;
674 }
675 #endif // #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
676 
OutFrameBegin(void)677 otError Buffer::OutFrameBegin(void)
678 {
679     otError error = OT_ERROR_NONE;
680 
681     EXPECT(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
682 
683     OutFrameSelectReadDirection();
684 
685     // Move the segment head and tail to start of frame.
686     mReadSegmentHead = mReadSegmentTail = mReadFrameStart[mReadDirection];
687 
688 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
689     mReadMessage = nullptr;
690 #endif
691 
692     // Prepare the current segment for reading.
693     error = OutFramePrepareSegment();
694 
695 exit:
696     return error;
697 }
698 
OutFrameHasEnded(void)699 bool Buffer::OutFrameHasEnded(void) { return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive); }
700 
OutFrameReadByte(void)701 uint8_t Buffer::OutFrameReadByte(void)
702 {
703     otError error;
704     uint8_t retval = kReadByteAfterFrameHasEnded;
705 
706     switch (mReadState)
707     {
708     case kReadStateNotActive:
709         OT_FALL_THROUGH;
710 
711     case kReadStateDone:
712 
713         retval = kReadByteAfterFrameHasEnded;
714 
715         break;
716 
717     case kReadStateInSegment:
718 
719         // Read a byte from current read pointer and move the read pointer by 1 byte in the read direction.
720         retval       = *mReadPointer;
721         mReadPointer = GetUpdatedBufPtr(mReadPointer, 1, mReadDirection);
722 
723         // Check if at end of current segment.
724         if (mReadPointer == mReadSegmentTail)
725         {
726 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
727             // Prepare any message associated with this segment.
728             error = OutFramePrepareMessage();
729 #else
730             error = OT_ERROR_NOT_FOUND;
731 #endif
732 
733             // If there is no message, move to next segment (if any).
734             if (error != OT_ERROR_NONE)
735             {
736                 IGNORE_RETURN(OutFramePrepareSegment());
737             }
738         }
739 
740         break;
741 
742     case kReadStateInMessage:
743 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
744         // Read a byte from current read pointer and move the read pointer by 1 byte.
745         retval = *mReadPointer;
746         mReadPointer++;
747 
748         // Check if at the end of content in message buffer.
749         if (mReadPointer == mReadMessageTail)
750         {
751             // Fill more bytes from current message into message buffer.
752             error = OutFrameFillMessageBuffer();
753 
754             // If no more bytes in the message, move to next segment (if any).
755             if (error != OT_ERROR_NONE)
756             {
757                 IGNORE_RETURN(OutFramePrepareSegment());
758             }
759         }
760 #endif
761         break;
762     }
763 
764     return retval;
765 }
766 
OutFrameRead(uint16_t aReadLength,uint8_t * aDataBuffer)767 uint16_t Buffer::OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer)
768 {
769     uint16_t bytesRead = 0;
770 
771     for (bytesRead = 0; (bytesRead < aReadLength) && !OutFrameHasEnded(); bytesRead++)
772     {
773         *aDataBuffer++ = OutFrameReadByte();
774     }
775 
776     return bytesRead;
777 }
778 
OutFrameRemove(void)779 otError Buffer::OutFrameRemove(void)
780 {
781     otError  error = OT_ERROR_NONE;
782     uint8_t *bufPtr;
783     uint16_t header;
784     uint8_t  numSegments;
785     FrameTag tag;
786 
787     EXPECT(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
788 
789     OutFrameSelectReadDirection();
790 
791     // Save the frame start pointer as the tag associated with the frame being removed.
792     tag = mReadFrameStart[mReadDirection];
793 
794     // Begin at the start of current frame and move through all segments.
795 
796     bufPtr      = mReadFrameStart[mReadDirection];
797     numSegments = 0;
798 
799     while (bufPtr != mWriteFrameStart[mReadDirection])
800     {
801         // Read the segment header
802         header = ReadUint16At(bufPtr, mReadDirection);
803 
804         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
805         // end of current frame.
806         if (header & kSegmentHeaderNewFrameFlag)
807         {
808             if (bufPtr != mReadFrameStart[mReadDirection])
809             {
810                 break;
811             }
812         }
813 
814 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
815         // If current segment has an appended message, remove it from message queue and free it.
816         if (header & kSegmentHeaderMessageIndicatorFlag)
817         {
818             otMessage *message;
819 
820             if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != nullptr)
821             {
822                 otMessageQueueDequeue(&mMessageQueue[mReadDirection], message);
823                 otMessageFree(message);
824             }
825         }
826 #endif
827 
828         // Move the pointer to next segment.
829         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
830 
831         numSegments++;
832 
833         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
834         // corrupted.
835         assert(numSegments <= kMaxSegments);
836     }
837 
838     mReadFrameStart[mReadDirection] = bufPtr;
839 
840     UpdateReadWriteStartPointers();
841 
842     mReadState       = kReadStateNotActive;
843     mReadFrameLength = kUnknownFrameLength;
844 
845     if (mFrameRemovedCallback != nullptr)
846     {
847         mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast<Priority>(mReadDirection), this);
848     }
849 
850 exit:
851     return error;
852 }
853 
UpdateReadWriteStartPointers(void)854 void Buffer::UpdateReadWriteStartPointers(void)
855 {
856     // If there is no fully written high priority frame, and not in middle of writing a new frame either.
857     if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh))
858     {
859         // Move the high priority pointers to be right behind the low priority start.
860         mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward);
861         mReadFrameStart[kPriorityHigh]  = mWriteFrameStart[kPriorityHigh];
862         EXIT_NOW();
863     }
864 
865     // If there is no fully written low priority frame, and not in middle of writing a new frame either.
866     if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow))
867     {
868         // Move the low priority pointers to be 1 byte after the high priority start.
869         mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward);
870         mReadFrameStart[kPriorityLow]  = mWriteFrameStart[kPriorityLow];
871     }
872 
873 exit:
874     return;
875 }
876 
OutFrameGetLength(void)877 uint16_t Buffer::OutFrameGetLength(void)
878 {
879     uint16_t frameLength = 0;
880     uint16_t header;
881     uint8_t *bufPtr;
882     uint8_t  numSegments;
883 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
884     otMessage *message = nullptr;
885 #endif
886 
887     // If the frame length was calculated before, return the previously calculated length.
888     EXPECT(mReadFrameLength == kUnknownFrameLength, frameLength = mReadFrameLength);
889 
890     EXPECT(!IsEmpty(), frameLength = 0);
891 
892     OutFrameSelectReadDirection();
893 
894     // Calculate frame length by adding length of all segments and messages within the current frame.
895 
896     bufPtr      = mReadFrameStart[mReadDirection];
897     numSegments = 0;
898 
899     while (bufPtr != mWriteFrameStart[mReadDirection])
900     {
901         // Read the segment header
902         header = ReadUint16At(bufPtr, mReadDirection);
903 
904         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
905         // end of current frame.
906         if (header & kSegmentHeaderNewFrameFlag)
907         {
908             if (bufPtr != mReadFrameStart[mReadDirection])
909             {
910                 break;
911             }
912         }
913 
914 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
915         // If current segment has an associated message, add its length to frame length.
916         if (header & kSegmentHeaderMessageIndicatorFlag)
917         {
918             message = (message == nullptr) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
919                                            : otMessageQueueGetNext(&mMessageQueue[mReadDirection], message);
920 
921             if (message != nullptr)
922             {
923                 frameLength += otMessageGetLength(message);
924             }
925         }
926 #endif
927 
928         // Add the length of current segment to the frame length.
929         frameLength += (header & kSegmentHeaderLengthMask);
930 
931         // Move the pointer to next segment.
932         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
933 
934         numSegments++;
935 
936         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
937         // corrupted.
938         assert(numSegments <= kMaxSegments);
939     }
940 
941     // Remember the calculated frame length for current active frame.
942     if (mReadState != kReadStateNotActive)
943     {
944         mReadFrameLength = frameLength;
945     }
946 
947 exit:
948     return frameLength;
949 }
950 
OutFrameGetTag(void)951 Buffer::FrameTag Buffer::OutFrameGetTag(void)
952 {
953     OutFrameSelectReadDirection();
954 
955     // If buffer is empty use `kInvalidTag`, otherwise use the frame start pointer as the tag associated with
956     // current out frame being read
957 
958     return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection];
959 }
960 
961 } // namespace Spinel
962 } // namespace ot
963