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