• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "common/code_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     VerifyOrExit(mWriteSegmentHead == mWriteSegmentTail);
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         SuccessOrExit(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     VerifyOrExit(mWriteDirection != kUnknown);
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     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
366 
367     // Begin a new segment (if we are not in middle of segment already).
368     SuccessOrExit(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     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
381 
382     // Begin a new segment (if we are not in middle of segment already).
383     SuccessOrExit(error = InFrameBeginSegment());
384 
385     // Write the data buffer
386     while (aDataBufferLength--)
387     {
388         SuccessOrExit(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     VerifyOrExit(aMessage != nullptr, error = OT_ERROR_INVALID_ARGS);
401     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
402 
403     // Begin a new segment (if we are not in middle of segment already).
404     SuccessOrExit(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     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
422 
423     // Begin a new segment (if we are not in middle of segment already).
424     SuccessOrExit(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     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
441 
442     VerifyOrExit(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     VerifyOrExit(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     VerifyOrExit(mWriteDirection != kUnknown);
471     VerifyOrExit(aPosition.mSegmentHead == mWriteSegmentHead);
472 
473     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
474     offset        = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
475     VerifyOrExit(offset < segmentLength);
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     VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE);
490     VerifyOrExit(aPosition.mSegmentHead == mWriteSegmentHead, error = OT_ERROR_INVALID_ARGS);
491 
492     segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection);
493     offset        = GetDistance(mWriteSegmentHead, aPosition.mPosition, mWriteDirection);
494     VerifyOrExit(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     VerifyOrExit(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         VerifyOrExit(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             VerifyOrExit(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             ExitNow();
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             ExitNow();
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     VerifyOrExit((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     VerifyOrExit(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     SuccessOrExit(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     VerifyOrExit(mReadMessage != nullptr, error = OT_ERROR_NOT_FOUND);
656 
657     VerifyOrExit(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     VerifyOrExit(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     VerifyOrExit(!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                 IgnoreReturnValue(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                 IgnoreReturnValue(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     OT_UNUSED_VARIABLE(numSegments);
788 
789     VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND);
790 
791     OutFrameSelectReadDirection();
792 
793     // Save the frame start pointer as the tag associated with the frame being removed.
794     tag = mReadFrameStart[mReadDirection];
795 
796     // Begin at the start of current frame and move through all segments.
797 
798     bufPtr      = mReadFrameStart[mReadDirection];
799     numSegments = 0;
800 
801     while (bufPtr != mWriteFrameStart[mReadDirection])
802     {
803         // Read the segment header
804         header = ReadUint16At(bufPtr, mReadDirection);
805 
806         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
807         // end of current frame.
808         if (header & kSegmentHeaderNewFrameFlag)
809         {
810             if (bufPtr != mReadFrameStart[mReadDirection])
811             {
812                 break;
813             }
814         }
815 
816 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
817         // If current segment has an appended message, remove it from message queue and free it.
818         if (header & kSegmentHeaderMessageIndicatorFlag)
819         {
820             otMessage *message;
821 
822             if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != nullptr)
823             {
824                 otMessageQueueDequeue(&mMessageQueue[mReadDirection], message);
825                 otMessageFree(message);
826             }
827         }
828 #endif
829 
830         // Move the pointer to next segment.
831         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
832 
833         numSegments++;
834 
835         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
836         // corrupted.
837         assert(numSegments <= kMaxSegments);
838     }
839 
840     mReadFrameStart[mReadDirection] = bufPtr;
841 
842     UpdateReadWriteStartPointers();
843 
844     mReadState       = kReadStateNotActive;
845     mReadFrameLength = kUnknownFrameLength;
846 
847     if (mFrameRemovedCallback != nullptr)
848     {
849         mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast<Priority>(mReadDirection), this);
850     }
851 
852 exit:
853     return error;
854 }
855 
UpdateReadWriteStartPointers(void)856 void Buffer::UpdateReadWriteStartPointers(void)
857 {
858     // If there is no fully written high priority frame, and not in middle of writing a new frame either.
859     if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh))
860     {
861         // Move the high priority pointers to be right behind the low priority start.
862         mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward);
863         mReadFrameStart[kPriorityHigh]  = mWriteFrameStart[kPriorityHigh];
864         ExitNow();
865     }
866 
867     // If there is no fully written low priority frame, and not in middle of writing a new frame either.
868     if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow))
869     {
870         // Move the low priority pointers to be 1 byte after the high priority start.
871         mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward);
872         mReadFrameStart[kPriorityLow]  = mWriteFrameStart[kPriorityLow];
873     }
874 
875 exit:
876     return;
877 }
878 
OutFrameGetLength(void)879 uint16_t Buffer::OutFrameGetLength(void)
880 {
881     uint16_t frameLength = 0;
882     uint16_t header;
883     uint8_t *bufPtr;
884     uint8_t  numSegments;
885 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
886     otMessage *message = nullptr;
887 #endif
888 
889     OT_UNUSED_VARIABLE(numSegments);
890 
891     // If the frame length was calculated before, return the previously calculated length.
892     VerifyOrExit(mReadFrameLength == kUnknownFrameLength, frameLength = mReadFrameLength);
893 
894     VerifyOrExit(!IsEmpty(), frameLength = 0);
895 
896     OutFrameSelectReadDirection();
897 
898     // Calculate frame length by adding length of all segments and messages within the current frame.
899 
900     bufPtr      = mReadFrameStart[mReadDirection];
901     numSegments = 0;
902 
903     while (bufPtr != mWriteFrameStart[mReadDirection])
904     {
905         // Read the segment header
906         header = ReadUint16At(bufPtr, mReadDirection);
907 
908         // If the current segment defines a new frame, and it is not the start of current frame, then we have reached
909         // end of current frame.
910         if (header & kSegmentHeaderNewFrameFlag)
911         {
912             if (bufPtr != mReadFrameStart[mReadDirection])
913             {
914                 break;
915             }
916         }
917 
918 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
919         // If current segment has an associated message, add its length to frame length.
920         if (header & kSegmentHeaderMessageIndicatorFlag)
921         {
922             message = (message == nullptr) ? otMessageQueueGetHead(&mMessageQueue[mReadDirection])
923                                            : otMessageQueueGetNext(&mMessageQueue[mReadDirection], message);
924 
925             if (message != nullptr)
926             {
927                 frameLength += otMessageGetLength(message);
928             }
929         }
930 #endif
931 
932         // Add the length of current segment to the frame length.
933         frameLength += (header & kSegmentHeaderLengthMask);
934 
935         // Move the pointer to next segment.
936         bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection);
937 
938         numSegments++;
939 
940         // If this assert fails, it is a likely indicator that the internal structure of the NCP buffer has been
941         // corrupted.
942         assert(numSegments <= kMaxSegments);
943     }
944 
945     // Remember the calculated frame length for current active frame.
946     if (mReadState != kReadStateNotActive)
947     {
948         mReadFrameLength = frameLength;
949     }
950 
951 exit:
952     return frameLength;
953 }
954 
OutFrameGetTag(void)955 Buffer::FrameTag Buffer::OutFrameGetTag(void)
956 {
957     OutFrameSelectReadDirection();
958 
959     // If buffer is empty use `kInvalidTag`, otherwise use the frame start pointer as the tag associated with
960     // current out frame being read
961 
962     return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection];
963 }
964 
965 } // namespace Spinel
966 } // namespace ot
967