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