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