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
540 {
541     return mWriteFrameTag;
542 }
543 
HasFrame(Priority aPriority) const544 bool Buffer::HasFrame(Priority aPriority) const
545 {
546     return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority];
547 }
548 
IsEmpty(void) const549 bool Buffer::IsEmpty(void) const
550 {
551     return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow);
552 }
553 
OutFrameSelectReadDirection(void)554 void Buffer::OutFrameSelectReadDirection(void)
555 {
556     if (mReadState == kReadStateNotActive)
557     {
558         mReadDirection = HasFrame(kPriorityHigh) ? kBackward : kForward;
559     }
560 }
561 
562 // Start/Prepare a new segment for reading.
OutFramePrepareSegment(void)563 otError Buffer::OutFramePrepareSegment(void)
564 {
565     otError  error = OT_ERROR_NONE;
566     uint16_t header;
567 
568     while (true)
569     {
570         // Go to the next segment (set the segment head to current segment's end/tail).
571         mReadSegmentHead = mReadSegmentTail;
572 
573         // Ensure there is something to read (i.e. segment head is not at start of frame being written).
574         VerifyOrExit(mReadSegmentHead != mWriteFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
575 
576         // Read the segment header.
577         header = ReadUint16At(mReadSegmentHead, mReadDirection);
578 
579         // Check if this segment is the start of a frame.
580         if (header & kSegmentHeaderNewFrameFlag)
581         {
582             // Ensure that this segment is start of current frame, otherwise the current frame is finished.
583             VerifyOrExit(mReadSegmentHead == mReadFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND);
584         }
585 
586         // Find tail/end of current segment.
587         mReadSegmentTail = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask),
588                                             mReadDirection);
589 
590         // Update the current read pointer to skip the segment header.
591         mReadPointer = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize, mReadDirection);
592 
593         // Check if there are data bytes to be read in this segment (i.e. read pointer not at the tail).
594         if (mReadPointer != mReadSegmentTail)
595         {
596             // Update the state to `InSegment` and return.
597             mReadState = kReadStateInSegment;
598 
599             ExitNow();
600         }
601 
602 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
603         // No data in this segment,  prepare any appended/associated message of this segment.
604         if (OutFramePrepareMessage() == OT_ERROR_NONE)
605         {
606             ExitNow();
607         }
608 
609         // If there is no message (`PrepareMessage()` returned an error), loop back to prepare the next segment.
610 #endif
611     }
612 
613 exit:
614 
615     if (error != OT_ERROR_NONE)
616     {
617         mReadState = kReadStateDone;
618     }
619 
620     return error;
621 }
622 
623 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
624 // This method prepares an associated message in current segment and fills the message buffer. It returns
625 // ThreadError_NotFound if there is no message or if the message has no content.
OutFramePrepareMessage(void)626 otError Buffer::OutFramePrepareMessage(void)
627 {
628     otError  error = OT_ERROR_NONE;
629     uint16_t header;
630 
631     // Read the segment header
632     header = ReadUint16At(mReadSegmentHead, mReadDirection);
633 
634     // Ensure that the segment header indicates that there is an associated message or return `NotFound` error.
635     VerifyOrExit((header & kSegmentHeaderMessageIndicatorFlag) != 0, error = OT_ERROR_NOT_FOUND);
636 
637     // Update the current message from the queue.
638     mReadMessage = (mReadMessage == nullptr) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
639                                              : otMessageQueueGetNext(&mMessageQueue[mReadDirection], mReadMessage);
640 
641     VerifyOrExit(mReadMessage != nullptr, error = OT_ERROR_NOT_FOUND);
642 
643     // Reset the offset for reading the message.
644     mReadMessageOffset = 0;
645 
646     // Fill the content from current message into the message buffer.
647     SuccessOrExit(error = OutFrameFillMessageBuffer());
648 
649     // If all successful, set the state to `InMessage`.
650     mReadState = kReadStateInMessage;
651 
652 exit:
653     return error;
654 }
655 
656 // This method fills content from current message into the message buffer. It returns OT_ERROR_NOT_FOUND if no more
657 // content in the current message.
OutFrameFillMessageBuffer(void)658 otError Buffer::OutFrameFillMessageBuffer(void)
659 {
660     otError error = OT_ERROR_NONE;
661     int     readLength;
662 
663     VerifyOrExit(mReadMessage != nullptr, error = OT_ERROR_NOT_FOUND);
664 
665     VerifyOrExit(mReadMessageOffset < otMessageGetLength(mReadMessage), error = OT_ERROR_NOT_FOUND);
666 
667     // Read portion of current message from the offset into message buffer.
668     readLength = otMessageRead(mReadMessage, mReadMessageOffset, mMessageBuffer, sizeof(mMessageBuffer));
669 
670     VerifyOrExit(readLength > 0, error = OT_ERROR_NOT_FOUND);
671 
672     // Update the message offset, set up the message tail, and set read pointer to start of message buffer.
673 
674     mReadMessageOffset += readLength;
675 
676     mReadMessageTail = mMessageBuffer + readLength;
677 
678     mReadPointer = mMessageBuffer;
679 
680 exit:
681     return error;
682 }
683 #endif // #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
684 
OutFrameBegin(void)685 otError Buffer::OutFrameBegin(void)
686 {
687     otError error = OT_ERROR_NONE;
688 
689     VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
690 
691     OutFrameSelectReadDirection();
692 
693     // Move the segment head and tail to start of frame.
694     mReadSegmentHead = mReadSegmentTail = mReadFrameStart[mReadDirection];
695 
696 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
697     mReadMessage = nullptr;
698 #endif
699 
700     // Prepare the current segment for reading.
701     error = OutFramePrepareSegment();
702 
703 exit:
704     return error;
705 }
706 
OutFrameHasEnded(void)707 bool Buffer::OutFrameHasEnded(void)
708 {
709     return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive);
710 }
711 
OutFrameReadByte(void)712 uint8_t Buffer::OutFrameReadByte(void)
713 {
714     otError error;
715     uint8_t retval = kReadByteAfterFrameHasEnded;
716 
717     switch (mReadState)
718     {
719     case kReadStateNotActive:
720         OT_FALL_THROUGH;
721 
722     case kReadStateDone:
723 
724         retval = kReadByteAfterFrameHasEnded;
725 
726         break;
727 
728     case kReadStateInSegment:
729 
730         // Read a byte from current read pointer and move the read pointer by 1 byte in the read direction.
731         retval       = *mReadPointer;
732         mReadPointer = GetUpdatedBufPtr(mReadPointer, 1, mReadDirection);
733 
734         // Check if at end of current segment.
735         if (mReadPointer == mReadSegmentTail)
736         {
737 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
738             // Prepare any message associated with this segment.
739             error = OutFramePrepareMessage();
740 #else
741             error = OT_ERROR_NOT_FOUND;
742 #endif
743 
744             // If there is no message, move to next segment (if any).
745             if (error != OT_ERROR_NONE)
746             {
747                 IgnoreError(OutFramePrepareSegment());
748             }
749         }
750 
751         break;
752 
753     case kReadStateInMessage:
754 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
755         // Read a byte from current read pointer and move the read pointer by 1 byte.
756         retval = *mReadPointer;
757         mReadPointer++;
758 
759         // Check if at the end of content in message buffer.
760         if (mReadPointer == mReadMessageTail)
761         {
762             // Fill more bytes from current message into message buffer.
763             error = OutFrameFillMessageBuffer();
764 
765             // If no more bytes in the message, move to next segment (if any).
766             if (error != OT_ERROR_NONE)
767             {
768                 IgnoreError(OutFramePrepareSegment());
769             }
770         }
771 #endif
772         break;
773     }
774 
775     return retval;
776 }
777 
OutFrameRead(uint16_t aReadLength,uint8_t * aDataBuffer)778 uint16_t Buffer::OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer)
779 {
780     uint16_t bytesRead = 0;
781 
782     for (bytesRead = 0; (bytesRead < aReadLength) && !OutFrameHasEnded(); bytesRead++)
783     {
784         *aDataBuffer++ = OutFrameReadByte();
785     }
786 
787     return bytesRead;
788 }
789 
OutFrameRemove(void)790 otError Buffer::OutFrameRemove(void)
791 {
792     otError  error = OT_ERROR_NONE;
793     uint8_t *bufPtr;
794     uint16_t header;
795     uint8_t  numSegments;
796     FrameTag tag;
797 
798     VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
799 
800     OutFrameSelectReadDirection();
801 
802     // Save the frame start pointer as the tag associated with the frame being removed.
803     tag = mReadFrameStart[mReadDirection];
804 
805     // Begin at the start of current frame and move through all segments.
806 
807     bufPtr      = mReadFrameStart[mReadDirection];
808     numSegments = 0;
809 
810     while (bufPtr != mWriteFrameStart[mReadDirection])
811     {
812         // Read the segment header
813         header = ReadUint16At(bufPtr, mReadDirection);
814 
815         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
816         // end of current frame.
817         if (header & kSegmentHeaderNewFrameFlag)
818         {
819             if (bufPtr != mReadFrameStart[mReadDirection])
820             {
821                 break;
822             }
823         }
824 
825 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
826         // If current segment has an appended message, remove it from message queue and free it.
827         if (header & kSegmentHeaderMessageIndicatorFlag)
828         {
829             otMessage *message;
830 
831             if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != nullptr)
832             {
833                 otMessageQueueDequeue(&mMessageQueue[mReadDirection], message);
834                 otMessageFree(message);
835             }
836         }
837 #endif
838 
839         // Move the pointer to next segment.
840         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
841 
842         numSegments++;
843 
844         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
845         // corrupted.
846         OT_ASSERT(numSegments <= kMaxSegments);
847     }
848 
849     mReadFrameStart[mReadDirection] = bufPtr;
850 
851     UpdateReadWriteStartPointers();
852 
853     mReadState       = kReadStateNotActive;
854     mReadFrameLength = kUnknownFrameLength;
855 
856     if (mFrameRemovedCallback != nullptr)
857     {
858         mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast<Priority>(mReadDirection), this);
859     }
860 
861 exit:
862     return error;
863 }
864 
UpdateReadWriteStartPointers(void)865 void Buffer::UpdateReadWriteStartPointers(void)
866 {
867     // If there is no fully written high priority frame, and not in middle of writing a new frame either.
868     if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh))
869     {
870         // Move the high priority pointers to be right behind the low priority start.
871         mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward);
872         mReadFrameStart[kPriorityHigh]  = mWriteFrameStart[kPriorityHigh];
873         ExitNow();
874     }
875 
876     // If there is no fully written low priority frame, and not in middle of writing a new frame either.
877     if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow))
878     {
879         // Move the low priority pointers to be 1 byte after the high priority start.
880         mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward);
881         mReadFrameStart[kPriorityLow]  = mWriteFrameStart[kPriorityLow];
882     }
883 
884 exit:
885     return;
886 }
887 
OutFrameGetLength(void)888 uint16_t Buffer::OutFrameGetLength(void)
889 {
890     uint16_t frameLength = 0;
891     uint16_t header;
892     uint8_t *bufPtr;
893     uint8_t  numSegments;
894 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
895     otMessage *message = nullptr;
896 #endif
897 
898     // If the frame length was calculated before, return the previously calculated length.
899     VerifyOrExit(mReadFrameLength == kUnknownFrameLength, frameLength = mReadFrameLength);
900 
901     VerifyOrExit(!IsEmpty(), frameLength = 0);
902 
903     OutFrameSelectReadDirection();
904 
905     // Calculate frame length by adding length of all segments and messages within the current frame.
906 
907     bufPtr      = mReadFrameStart[mReadDirection];
908     numSegments = 0;
909 
910     while (bufPtr != mWriteFrameStart[mReadDirection])
911     {
912         // Read the segment header
913         header = ReadUint16At(bufPtr, mReadDirection);
914 
915         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
916         // end of current frame.
917         if (header & kSegmentHeaderNewFrameFlag)
918         {
919             if (bufPtr != mReadFrameStart[mReadDirection])
920             {
921                 break;
922             }
923         }
924 
925 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
926         // If current segment has an associated message, add its length to frame length.
927         if (header & kSegmentHeaderMessageIndicatorFlag)
928         {
929             message = (message == nullptr) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
930                                            : otMessageQueueGetNext(&mMessageQueue[mReadDirection], message);
931 
932             if (message != nullptr)
933             {
934                 frameLength += otMessageGetLength(message);
935             }
936         }
937 #endif
938 
939         // Add the length of current segment to the frame length.
940         frameLength += (header & kSegmentHeaderLengthMask);
941 
942         // Move the pointer to next segment.
943         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
944 
945         numSegments++;
946 
947         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
948         // corrupted.
949         OT_ASSERT(numSegments <= kMaxSegments);
950     }
951 
952     // Remember the calculated frame length for current active frame.
953     if (mReadState != kReadStateNotActive)
954     {
955         mReadFrameLength = frameLength;
956     }
957 
958 exit:
959     return frameLength;
960 }
961 
OutFrameGetTag(void)962 Buffer::FrameTag Buffer::OutFrameGetTag(void)
963 {
964     OutFrameSelectReadDirection();
965 
966     // If buffer is empty use `kInvalidTag`, otherwise use the frame start pointer as the tag associated with
967     // current out frame being read
968 
969     return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection];
970 }
971 
972 } // namespace Spinel
973 } // namespace ot
974