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