-
Notifications
You must be signed in to change notification settings - Fork 7.4k
/
ringbuf.c
1364 lines (1210 loc) · 61.6 KB
/
ringbuf.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/list.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
// ------------------------------------------------- Macros and Types --------------------------------------------------
//32-bit alignment macros
#define rbALIGN_MASK (0x03)
#define rbALIGN_SIZE( xSize ) ( ( xSize + rbALIGN_MASK ) & ~rbALIGN_MASK )
#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) ( pvPtr ) & rbALIGN_MASK ) == 0 )
//Ring buffer flags
#define rbALLOW_SPLIT_FLAG ( ( UBaseType_t ) 1 ) //The ring buffer allows items to be split
#define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer
#define rbBUFFER_FULL_FLAG ( ( UBaseType_t ) 4 ) //The ring buffer is currently full (write pointer == free pointer)
#define rbBUFFER_STATIC_FLAG ( ( UBaseType_t ) 8 ) //The ring buffer is statically allocated
#define rbUSING_QUEUE_SET ( ( UBaseType_t ) 16 ) //The ring buffer has been added to a queue set
//Item flags
#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite
#define rbITEM_DUMMY_DATA_FLAG ( ( UBaseType_t ) 2 ) //Data from here to end of the ring buffer is dummy data. Restart reading at start of head of the buffer
#define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around
#define rbITEM_WRITTEN_FLAG ( ( UBaseType_t ) 8 ) //Item has been written to by the application, thus can be read
typedef struct {
//This size of this structure must be 32-bit aligned
size_t xItemLen;
UBaseType_t uxItemFlags;
} ItemHeader_t;
#define rbHEADER_SIZE sizeof(ItemHeader_t)
typedef struct RingbufferDefinition Ringbuffer_t;
typedef BaseType_t (*CheckItemFitsFunction_t)(Ringbuffer_t *pxRingbuffer, size_t xItemSize);
typedef void (*CopyItemFunction_t)(Ringbuffer_t *pxRingbuffer, const uint8_t *pcItem, size_t xItemSize);
typedef BaseType_t (*CheckItemAvailFunction_t) (Ringbuffer_t *pxRingbuffer);
typedef void *(*GetItemFunction_t)(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xMaxSize, size_t *pxItemSize);
typedef void (*ReturnItemFunction_t)(Ringbuffer_t *pxRingbuffer, uint8_t *pvItem);
typedef size_t (*GetCurMaxSizeFunction_t)(Ringbuffer_t *pxRingbuffer);
typedef struct RingbufferDefinition {
size_t xSize; //Size of the data storage
size_t xMaxItemSize; //Maximum item size
UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer
CheckItemFitsFunction_t xCheckItemFits; //Function to check if item can currently fit in ring buffer
CopyItemFunction_t vCopyItem; //Function to copy item to ring buffer
GetItemFunction_t pvGetItem; //Function to get item from ring buffer
ReturnItemFunction_t vReturnItem; //Function to return item to ring buffer
GetCurMaxSizeFunction_t xGetCurMaxSize; //Function to get current free size
uint8_t *pucAcquire; //Acquire Pointer. Points to where the next item should be acquired.
uint8_t *pucWrite; //Write Pointer. Points to where the next item should be written
uint8_t *pucRead; //Read Pointer. Points to where the next item should be read from
uint8_t *pucFree; //Free Pointer. Points to the last item that has yet to be returned to the ring buffer
uint8_t *pucHead; //Pointer to the start of the ring buffer storage area
uint8_t *pucTail; //Pointer to the end of the ring buffer storage area
BaseType_t xItemsWaiting; //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read
List_t xTasksWaitingToSend; //List of tasks that are blocked waiting to send/acquire onto this ring buffer. Stored in priority order.
List_t xTasksWaitingToReceive; //List of tasks that are blocked waiting to receive from this ring buffer. Stored in priority order.
QueueSetHandle_t xQueueSet; //Ring buffer's read queue set handle.
portMUX_TYPE mux; //Spinlock required for SMP
} Ringbuffer_t;
#if __GNUC_PREREQ(4, 6)
_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t");
#endif
// ------------------------------------------------ Forward Declares ---------------------------------------------------
/*
* WARNING: All of the following static functions (except generic functions)
* ARE NOT THREAD SAFE. Therefore they should only be called within a critical
* section (using spin locks)
*/
//Initialize a ring buffer after space has been allocated for it
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
Ringbuffer_t *pxNewRingbuffer,
uint8_t *pucRingbufferStorage);
//Calculate current amount of free space (in bytes) in the ring buffer
static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer);
//Checks if an item/data is currently available for retrieval
static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer);
//Checks if an item will currently fit in a no-split/allow-split ring buffer
static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize);
//Checks if an item will currently fit in a byte buffer
static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize);
/*
Copies an item to a no-split ring buffer
Entry:
- Must have already guaranteed there is sufficient space for item by calling prvCheckItemFitsDefault()
Exit:
- New item copied into ring buffer
- pucAcquire and pucWrite updated.
- Dummy item added if necessary
*/
static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
/*
Copies an item to a allow-split ring buffer
Entry:
- Must have already guaranteed there is sufficient space for item by calling prvCheckItemFitsDefault()
Exit:
- New item copied into ring buffer
- pucAcquire and pucWrite updated
- Item may be split
*/
static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
//Copies an item to a byte buffer. Only call this function after calling prvCheckItemFitsByteBuffer()
static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
//Retrieve item from no-split/allow-split ring buffer. *pxIsSplit is set to pdTRUE if the retrieved item is split
/*
Entry:
- Must have already guaranteed that there is an item available for retrieval by calling prvCheckItemAvail()
- Guaranteed that pucREAD points to a valid item (i.e., not a dummy item)
Exit:
- Item is returned. Only first half returned if split
- pucREAD updated to point to next valid item to read, or equals to pucWrite if there are no more valid items to read
- pucREAD update must skip over dummy items
*/
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxIsSplit,
size_t xUnusedParam,
size_t *pxItemSize);
//Retrieve data from byte buffer. If xMaxSize is 0, all continuous data is retrieved
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxUnusedParam,
size_t xMaxSize,
size_t *pxItemSize);
/*
Return an item to a split/no-split ring buffer
Exit:
- Item is marked free rbITEM_FREE_FLAG
- pucFree is progressed as far as possible, skipping over already freed items or dummy items
*/
static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem);
//Return data to a byte buffer
static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem);
//Get the maximum size an item that can currently have if sent to a no-split ring buffer
static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer);
//Get the maximum size an item that can currently have if sent to a allow-split ring buffer
static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer);
//Get the maximum size an item that can currently have if sent to a byte buffer
static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
/*
Generic function used to send or acquire an item/buffer.
- If sending, set ppvItem to NULL. pvItem remains unchanged on failure.
- If acquiring, set pvItem to NULL. ppvItem remains unchanged on failure.
*/
static BaseType_t prvSendAcquireGeneric(Ringbuffer_t *pxRingbuffer,
const void *pvItem,
void **ppvItem,
size_t xItemSize,
TickType_t xTicksToWait);
/*
Generic function used to retrieve an item/data from ring buffers. If called on
an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of
a split item will be retrieved. xMaxSize will only take effect if called on
byte buffers. xItemSize must remain unchanged if no item is retrieved.
*/
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize,
TickType_t xTicksToWait);
//From ISR version of prvReceiveGeneric()
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize);
// ------------------------------------------------ Static Functions ---------------------------------------------------
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
Ringbuffer_t *pxNewRingbuffer,
uint8_t *pucRingbufferStorage)
{
//Initialize values
pxNewRingbuffer->xSize = xBufferSize;
pxNewRingbuffer->pucHead = pucRingbufferStorage;
pxNewRingbuffer->pucTail = pucRingbufferStorage + xBufferSize;
pxNewRingbuffer->pucFree = pucRingbufferStorage;
pxNewRingbuffer->pucRead = pucRingbufferStorage;
pxNewRingbuffer->pucWrite = pucRingbufferStorage;
pxNewRingbuffer->pucAcquire = pucRingbufferStorage;
pxNewRingbuffer->xItemsWaiting = 0;
pxNewRingbuffer->uxRingbufferFlags = 0;
//Initialize type dependent values and function pointers
if (xBufferType == RINGBUF_TYPE_NOSPLIT) {
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxNewRingbuffer->vCopyItem = prvCopyItemNoSplit;
pxNewRingbuffer->pvGetItem = prvGetItemDefault;
pxNewRingbuffer->vReturnItem = prvReturnItemDefault;
/*
* Worst case scenario is when the read/write/acquire/free pointers are all
* pointing to the halfway point of the buffer.
*/
pxNewRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxNewRingbuffer->xSize / 2) - rbHEADER_SIZE;
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit;
} else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) {
pxNewRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG;
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault;
pxNewRingbuffer->vCopyItem = prvCopyItemAllowSplit;
pxNewRingbuffer->pvGetItem = prvGetItemDefault;
pxNewRingbuffer->vReturnItem = prvReturnItemDefault;
//Worst case an item is split into two, incurring two headers of overhead
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize - (sizeof(ItemHeader_t) * 2);
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit;
} else { //Byte Buffer
pxNewRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG;
pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer;
pxNewRingbuffer->vCopyItem = prvCopyItemByteBuf;
pxNewRingbuffer->pvGetItem = prvGetItemByteBuf;
pxNewRingbuffer->vReturnItem = prvReturnItemByteBuf;
//Byte buffers do not incur any overhead
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize;
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
}
vListInitialise(&pxNewRingbuffer->xTasksWaitingToSend);
vListInitialise(&pxNewRingbuffer->xTasksWaitingToReceive);
pxNewRingbuffer->xQueueSet = NULL;
portMUX_INITIALIZE(&pxNewRingbuffer->mux);
}
static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer)
{
size_t xReturn;
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
xReturn = 0;
} else {
BaseType_t xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucAcquire;
//Check if xFreeSize has underflowed
if (xFreeSize <= 0) {
xFreeSize += pxRingbuffer->xSize;
}
xReturn = xFreeSize;
}
configASSERT(xReturn <= pxRingbuffer->xSize);
return xReturn;
}
static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize)
{
//Check arguments and buffer state
configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucAcquire)); //pucAcquire is always aligned in no-split/allow-split ring buffers
configASSERT(pxRingbuffer->pucAcquire >= pxRingbuffer->pucHead && pxRingbuffer->pucAcquire < pxRingbuffer->pucTail); //Check write pointer is within bounds
size_t xTotalItemSize = rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE; //Rounded up aligned item size with header
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucFree) {
//Buffer is either complete empty or completely full
return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE;
}
if (pxRingbuffer->pucFree > pxRingbuffer->pucAcquire) {
//Free space does not wrap around
return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucAcquire) ? pdTRUE : pdFALSE;
}
//Free space wraps around
if (xTotalItemSize <= pxRingbuffer->pucTail - pxRingbuffer->pucAcquire) {
return pdTRUE; //Item fits without wrapping around
}
//Check if item fits by wrapping
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
//Allow split wrapping incurs an extra header
return (xTotalItemSize + rbHEADER_SIZE <= pxRingbuffer->xSize - (pxRingbuffer->pucAcquire - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE;
} else {
return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucHead) ? pdTRUE : pdFALSE;
}
}
static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize)
{
//Check arguments and buffer state
configASSERT(pxRingbuffer->pucAcquire >= pxRingbuffer->pucHead && pxRingbuffer->pucAcquire < pxRingbuffer->pucTail); //Check acquire pointer is within bounds
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucFree) {
//Buffer is either complete empty or completely full
return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE;
}
if (pxRingbuffer->pucFree > pxRingbuffer->pucAcquire) {
//Free space does not wrap around
return (xItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucAcquire) ? pdTRUE : pdFALSE;
}
//Free space wraps around
return (xItemSize <= pxRingbuffer->xSize - (pxRingbuffer->pucAcquire - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE;
}
static uint8_t* prvAcquireItemNoSplit(Ringbuffer_t *pxRingbuffer, size_t xItemSize)
{
//Check arguments and buffer state
size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize); //Rounded up aligned item size
size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucAcquire; //Length from pucAcquire until end of buffer
configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucAcquire)); //pucAcquire is always aligned in no-split ring buffers
configASSERT(pxRingbuffer->pucAcquire >= pxRingbuffer->pucHead && pxRingbuffer->pucAcquire < pxRingbuffer->pucTail); //Check write pointer is within bounds
configASSERT(xRemLen >= rbHEADER_SIZE); //Remaining length must be able to at least fit an item header
//If remaining length can't fit item, set as dummy data and wrap around
if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) {
ItemHeader_t *pxDummy = (ItemHeader_t *)pxRingbuffer->pucAcquire;
pxDummy->uxItemFlags = rbITEM_DUMMY_DATA_FLAG; //Set remaining length as dummy data
pxDummy->xItemLen = 0; //Dummy data should have no length
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; //Reset acquire pointer to wrap around
}
//Item should be guaranteed to fit at this point. Set item header and copy data
ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucAcquire;
pxHeader->xItemLen = xItemSize;
pxHeader->uxItemFlags = 0;
//hold the buffer address without touching pucWrite
uint8_t* item_address = pxRingbuffer->pucAcquire + rbHEADER_SIZE;
pxRingbuffer->pucAcquire += rbHEADER_SIZE + xAlignedItemSize; //Advance pucAcquire past header and the item to next aligned address
//After the allocation, add some padding after the buffer and correct the flags
//If current remaining length can't fit a header, wrap around write pointer
if (pxRingbuffer->pucTail - pxRingbuffer->pucAcquire < rbHEADER_SIZE) {
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; //Wrap around pucAcquire
}
//Check if buffer is full
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucFree) {
//Mark the buffer as full to distinguish with an empty buffer
pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG;
}
return item_address;
}
static void prvSendItemDoneNoSplit(Ringbuffer_t *pxRingbuffer, uint8_t* pucItem)
{
//Check arguments and buffer state
configASSERT(rbCHECK_ALIGNED(pucItem));
configASSERT(pucItem >= pxRingbuffer->pucHead);
configASSERT(pucItem <= pxRingbuffer->pucTail); //Inclusive of pucTail in the case of zero length item at the very end
//Get and check header of the item
ItemHeader_t *pxCurHeader = (ItemHeader_t *)(pucItem - rbHEADER_SIZE);
configASSERT(pxCurHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
configASSERT((pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) == 0); //Dummy items should never have been written
configASSERT((pxCurHeader->uxItemFlags & rbITEM_WRITTEN_FLAG) == 0); //Indicates item has already been written before
pxCurHeader->uxItemFlags &= ~rbITEM_SPLIT_FLAG; //Clear wrap flag if set (not strictly necessary)
pxCurHeader->uxItemFlags |= rbITEM_WRITTEN_FLAG; //Mark as written
pxRingbuffer->xItemsWaiting++;
/*
* Items might not be written in the order they were acquired. Move the
* write pointer up to the next item that has not been marked as written (by
* written flag) or up till the acquire pointer. When advancing the write
* pointer, items that have already been written or items with dummy data
* should be skipped over
*/
pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucWrite;
//Skip over Items that have already been written or are dummy items
while (((pxCurHeader->uxItemFlags & rbITEM_WRITTEN_FLAG) || (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG)) && pxRingbuffer->pucWrite != pxRingbuffer->pucAcquire) {
if (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) {
pxCurHeader->uxItemFlags |= rbITEM_WRITTEN_FLAG; //Mark as freed (not strictly necessary but adds redundancy)
pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Wrap around due to dummy data
} else {
//Item with data that has already been written, advance write pointer past this item
size_t xAlignedItemSize = rbALIGN_SIZE(pxCurHeader->xItemLen);
pxRingbuffer->pucWrite += xAlignedItemSize + rbHEADER_SIZE;
//Redundancy check to ensure write pointer has not overshot buffer bounds
configASSERT(pxRingbuffer->pucWrite <= pxRingbuffer->pucHead + pxRingbuffer->xSize);
}
//Check if pucWrite requires wrap around
if ((pxRingbuffer->pucTail - pxRingbuffer->pucWrite) < rbHEADER_SIZE) {
pxRingbuffer->pucWrite = pxRingbuffer->pucHead;
}
pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucWrite; //Update header to point to item
}
}
static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
{
uint8_t* item_addr = prvAcquireItemNoSplit(pxRingbuffer, xItemSize);
memcpy(item_addr, pucItem, xItemSize);
prvSendItemDoneNoSplit(pxRingbuffer, item_addr);
}
static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
{
//Check arguments and buffer state
size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize); //Rounded up aligned item size
size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucAcquire; //Length from pucAcquire until end of buffer
configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucAcquire)); //pucAcquire is always aligned in split ring buffers
configASSERT(pxRingbuffer->pucAcquire >= pxRingbuffer->pucHead && pxRingbuffer->pucAcquire < pxRingbuffer->pucTail); //Check write pointer is within bounds
configASSERT(xRemLen >= rbHEADER_SIZE); //Remaining length must be able to at least fit an item header
//Split item if necessary
if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) {
//Write first part of the item
ItemHeader_t *pxFirstHeader = (ItemHeader_t *)pxRingbuffer->pucAcquire;
pxFirstHeader->uxItemFlags = 0;
pxFirstHeader->xItemLen = xRemLen - rbHEADER_SIZE; //Fill remaining length with first part
pxRingbuffer->pucAcquire += rbHEADER_SIZE; //Advance pucAcquire past header
xRemLen -= rbHEADER_SIZE;
if (xRemLen > 0) {
memcpy(pxRingbuffer->pucAcquire, pucItem, xRemLen);
pxRingbuffer->xItemsWaiting++;
//Update item arguments to account for data already copied
pucItem += xRemLen;
xItemSize -= xRemLen;
xAlignedItemSize -= xRemLen;
pxFirstHeader->uxItemFlags |= rbITEM_SPLIT_FLAG; //There must be more data
} else {
//Remaining length was only large enough to fit header
pxFirstHeader->uxItemFlags |= rbITEM_DUMMY_DATA_FLAG; //Item will completely be stored in 2nd part
}
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; //Reset acquire pointer to start of buffer
}
//Item (whole or second part) should be guaranteed to fit at this point
ItemHeader_t *pxSecondHeader = (ItemHeader_t *)pxRingbuffer->pucAcquire;
pxSecondHeader->xItemLen = xItemSize;
pxSecondHeader->uxItemFlags = 0;
pxRingbuffer->pucAcquire += rbHEADER_SIZE; //Advance acquire pointer past header
memcpy(pxRingbuffer->pucAcquire, pucItem, xItemSize);
pxRingbuffer->xItemsWaiting++;
pxRingbuffer->pucAcquire += xAlignedItemSize; //Advance pucAcquire past item to next aligned address
//If current remaining length can't fit a header, wrap around write pointer
if (pxRingbuffer->pucTail - pxRingbuffer->pucAcquire < rbHEADER_SIZE) {
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; //Wrap around pucAcquire
}
//Check if buffer is full
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucFree) {
//Mark the buffer as full to distinguish with an empty buffer
pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG;
}
//currently the Split mode is not supported, pucWrite tracks the pucAcquire
pxRingbuffer->pucWrite = pxRingbuffer->pucAcquire;
}
static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
{
//Check arguments and buffer state
configASSERT(pxRingbuffer->pucAcquire >= pxRingbuffer->pucHead && pxRingbuffer->pucAcquire < pxRingbuffer->pucTail); //Check acquire pointer is within bounds
size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucAcquire; //Length from pucAcquire until end of buffer
if (xRemLen < xItemSize) {
//Copy as much as possible into remaining length
memcpy(pxRingbuffer->pucAcquire, pucItem, xRemLen);
pxRingbuffer->xItemsWaiting += xRemLen;
//Update item arguments to account for data already written
pucItem += xRemLen;
xItemSize -= xRemLen;
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; //Reset acquire pointer to start of buffer
}
//Copy all or remaining portion of the item
memcpy(pxRingbuffer->pucAcquire, pucItem, xItemSize);
pxRingbuffer->xItemsWaiting += xItemSize;
pxRingbuffer->pucAcquire += xItemSize;
//Wrap around pucAcquire if it reaches the end
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucTail) {
pxRingbuffer->pucAcquire = pxRingbuffer->pucHead;
}
//Check if buffer is full
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucFree) {
pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG; //Mark the buffer as full to avoid confusion with an empty buffer
}
//Currently, acquiring memory is not supported in byte mode. pucWrite tracks the pucAcquire.
pxRingbuffer->pucWrite = pxRingbuffer->pucAcquire;
}
static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
{
if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && pxRingbuffer->pucRead != pxRingbuffer->pucFree) {
return pdFALSE; //Byte buffers do not allow multiple retrievals before return
}
if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) {
return pdTRUE; //Items/data available for retrieval
} else {
return pdFALSE; //No items/data available for retrieval
}
}
static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxIsSplit,
size_t xUnusedParam,
size_t *pxItemSize)
{
//Check arguments and buffer state
ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
configASSERT(pxIsSplit != NULL);
configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read
configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucRead)); //pucRead is always aligned in split ring buffers
configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail); //Check read pointer is within bounds
configASSERT((pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize) || (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG));
uint8_t *pcReturn;
//Wrap around if dummy data (dummy data indicates wrap around in no-split buffers)
if (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) {
pxRingbuffer->pucRead = pxRingbuffer->pucHead;
//Check for errors with the next item
pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
configASSERT(pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
}
pcReturn = pxRingbuffer->pucRead + rbHEADER_SIZE; //Get pointer to part of item containing data (point past the header)
if (pxHeader->xItemLen == 0) {
//Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail);
} else {
//Exclusive of pucTail if length is larger than zero, pcReturn should never point to pucTail
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail);
}
*pxItemSize = pxHeader->xItemLen; //Get length of item
pxRingbuffer->xItemsWaiting --; //Update item count
*pxIsSplit = (pxHeader->uxItemFlags & rbITEM_SPLIT_FLAG) ? pdTRUE : pdFALSE;
pxRingbuffer->pucRead += rbHEADER_SIZE + rbALIGN_SIZE(pxHeader->xItemLen); //Update pucRead
//Check if pucRead requires wrap around
if ((pxRingbuffer->pucTail - pxRingbuffer->pucRead) < rbHEADER_SIZE) {
pxRingbuffer->pucRead = pxRingbuffer->pucHead;
}
return (void *)pcReturn;
}
static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer,
BaseType_t *pxUnusedParam,
size_t xMaxSize,
size_t *pxItemSize)
{
//Check arguments and buffer state
configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read
configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail); //Check read pointer is within bounds
configASSERT(pxRingbuffer->pucRead == pxRingbuffer->pucFree);
uint8_t *ret = pxRingbuffer->pucRead;
if ((pxRingbuffer->pucRead > pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG)) { //Available data wraps around
//Return contiguous piece from read pointer until buffer tail, or xMaxSize
if (xMaxSize == 0 || pxRingbuffer->pucTail - pxRingbuffer->pucRead <= xMaxSize) {
//All contiguous data from read pointer to tail
*pxItemSize = pxRingbuffer->pucTail - pxRingbuffer->pucRead;
pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucTail - pxRingbuffer->pucRead;
pxRingbuffer->pucRead = pxRingbuffer->pucHead; //Wrap around read pointer
} else {
//Return xMaxSize amount of data
*pxItemSize = xMaxSize;
pxRingbuffer->xItemsWaiting -= xMaxSize;
pxRingbuffer->pucRead += xMaxSize; //Advance read pointer past retrieved data
}
} else { //Available data is contiguous between read and write pointer
if (xMaxSize == 0 || pxRingbuffer->pucWrite - pxRingbuffer->pucRead <= xMaxSize) {
//Return all contiguous data from read to write pointer
*pxItemSize = pxRingbuffer->pucWrite - pxRingbuffer->pucRead;
pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucWrite - pxRingbuffer->pucRead;
pxRingbuffer->pucRead = pxRingbuffer->pucWrite;
} else {
//Return xMaxSize data from read pointer
*pxItemSize = xMaxSize;
pxRingbuffer->xItemsWaiting -= xMaxSize;
pxRingbuffer->pucRead += xMaxSize; //Advance read pointer past retrieved data
}
}
return (void *)ret;
}
static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem)
{
//Check arguments and buffer state
configASSERT(rbCHECK_ALIGNED(pucItem));
configASSERT(pucItem >= pxRingbuffer->pucHead);
configASSERT(pucItem <= pxRingbuffer->pucTail); //Inclusive of pucTail in the case of zero length item at the very end
//Get and check header of the item
ItemHeader_t *pxCurHeader = (ItemHeader_t *)(pucItem - rbHEADER_SIZE);
configASSERT(pxCurHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
configASSERT((pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) == 0); //Dummy items should never have been read
configASSERT((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) == 0); //Indicates item has already been returned before
pxCurHeader->uxItemFlags &= ~rbITEM_SPLIT_FLAG; //Clear wrap flag if set (not strictly necessary)
pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG; //Mark as free
/*
* Items might not be returned in the order they were retrieved. Move the free pointer
* up to the next item that has not been marked as free (by free flag) or up
* till the read pointer. When advancing the free pointer, items that have already been
* freed or items with dummy data should be skipped over
*/
pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree;
//Skip over Items that have already been freed or are dummy items
while (((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) || (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG)) && pxRingbuffer->pucFree != pxRingbuffer->pucRead) {
if (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) {
pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG; //Mark as freed (not strictly necessary but adds redundancy)
pxRingbuffer->pucFree = pxRingbuffer->pucHead; //Wrap around due to dummy data
} else {
//Item with data that has already been freed, advance free pointer past this item
size_t xAlignedItemSize = rbALIGN_SIZE(pxCurHeader->xItemLen);
pxRingbuffer->pucFree += xAlignedItemSize + rbHEADER_SIZE;
//Redundancy check to ensure free pointer has not overshot buffer bounds
configASSERT(pxRingbuffer->pucFree <= pxRingbuffer->pucHead + pxRingbuffer->xSize);
}
//Check if pucFree requires wrap around
if ((pxRingbuffer->pucTail - pxRingbuffer->pucFree) < rbHEADER_SIZE) {
pxRingbuffer->pucFree = pxRingbuffer->pucHead;
}
pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree; //Update header to point to item
}
//Check if the buffer full flag should be reset
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
if (pxRingbuffer->pucFree != pxRingbuffer->pucAcquire) {
pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
} else if (pxRingbuffer->pucFree == pxRingbuffer->pucAcquire && pxRingbuffer->pucFree == pxRingbuffer->pucRead) {
//Special case where a full buffer is completely freed in one go
pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
}
}
}
static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem)
{
//Check pointer points to address inside buffer
configASSERT((uint8_t *)pucItem >= pxRingbuffer->pucHead);
configASSERT((uint8_t *)pucItem < pxRingbuffer->pucTail);
//Free the read memory. Simply moves free pointer to read pointer as byte buffers do not allow multiple outstanding reads
pxRingbuffer->pucFree = pxRingbuffer->pucRead;
//If buffer was full before, reset full flag as free pointer has moved
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
}
}
static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer)
{
BaseType_t xFreeSize;
//Check if buffer is full
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
return 0;
}
if (pxRingbuffer->pucAcquire < pxRingbuffer->pucFree) {
//Free space is contiguous between pucAcquire and pucFree
xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucAcquire;
} else {
//Free space wraps around (or overlapped at pucHead), select largest
//contiguous free space as no-split items require contiguous space
size_t xSize1 = pxRingbuffer->pucTail - pxRingbuffer->pucAcquire;
size_t xSize2 = pxRingbuffer->pucFree - pxRingbuffer->pucHead;
xFreeSize = (xSize1 > xSize2) ? xSize1 : xSize2;
}
//No-split ring buffer items need space for a header
xFreeSize -= rbHEADER_SIZE;
//Check for xFreeSize < 0 before checking xFreeSize > pxRingbuffer->xMaxItemSize
//to avoid incorrect comparison operation when xFreeSize is negative
if (xFreeSize < 0) {
//Occurs when free space is less than header size
xFreeSize = 0;
} else if (xFreeSize > pxRingbuffer->xMaxItemSize) {
//Limit free size to be within bounds
xFreeSize = pxRingbuffer->xMaxItemSize;
}
return xFreeSize;
}
static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer)
{
BaseType_t xFreeSize;
//Check if buffer is full
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
return 0;
}
if (pxRingbuffer->pucAcquire == pxRingbuffer->pucHead && pxRingbuffer->pucFree == pxRingbuffer->pucHead) {
//Check for special case where pucAcquire and pucFree are both at pucHead
xFreeSize = pxRingbuffer->xSize - rbHEADER_SIZE;
} else if (pxRingbuffer->pucAcquire < pxRingbuffer->pucFree) {
//Free space is contiguous between pucAcquire and pucFree, requires single header
xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucAcquire) - rbHEADER_SIZE;
} else {
//Free space wraps around, requires two headers
xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucHead) +
(pxRingbuffer->pucTail - pxRingbuffer->pucAcquire) -
(rbHEADER_SIZE * 2);
}
//Check for xFreeSize < 0 before checking xFreeSize > pxRingbuffer->xMaxItemSize
//to avoid incorrect comparison operation when xFreeSize is negative
if (xFreeSize < 0) {
xFreeSize = 0;
} else if (xFreeSize > pxRingbuffer->xMaxItemSize) {
//Limit free size to be within bounds
xFreeSize = pxRingbuffer->xMaxItemSize;
}
return xFreeSize;
}
static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer)
{
BaseType_t xFreeSize;
//Check if buffer is full
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
return 0;
}
/*
* Return whatever space is available depending on relative positions of the free
* pointer and Acquire pointer. There is no overhead of headers in this mode
*/
xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucAcquire;
if (xFreeSize <= 0) {
xFreeSize += pxRingbuffer->xSize;
}
return xFreeSize;
}
static BaseType_t prvSendAcquireGeneric(Ringbuffer_t *pxRingbuffer,
const void *pvItem,
void **ppvItem,
size_t xItemSize,
TickType_t xTicksToWait)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xExitLoop = pdFALSE;
BaseType_t xEntryTimeSet = pdFALSE;
BaseType_t xNotifyQueueSet = pdFALSE;
TimeOut_t xTimeOut;
while (xExitLoop == pdFALSE) {
portENTER_CRITICAL(&pxRingbuffer->mux);
if (pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
//xItemSize will fit. Copy or acquire the buffer immediately
if (ppvItem) {
//Acquire the buffer
*ppvItem = prvAcquireItemNoSplit(pxRingbuffer, xItemSize);
} else {
//Copy item into buffer
pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize);
if (pxRingbuffer->xQueueSet) {
//If ring buffer was added to a queue set, notify the queue set
xNotifyQueueSet = pdTRUE;
} else {
//If a task was waiting for data to arrive on the ring buffer, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToReceive) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToReceive) == pdTRUE) {
//The unblocked task will preempt us. Trigger a yield here.
portYIELD_WITHIN_API();
}
}
}
}
xReturn = pdTRUE;
xExitLoop = pdTRUE;
goto loop_end;
} else if (xTicksToWait == (TickType_t) 0) {
//No block time. Return immediately.
xExitLoop = pdTRUE;
goto loop_end;
} else if (xEntryTimeSet == pdFALSE) {
//This is our first block. Set entry time
vTaskInternalSetTimeOutState(&xTimeOut);
xEntryTimeSet = pdTRUE;
}
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
//Not timed out yet. Block the current task
vTaskPlaceOnEventList(&pxRingbuffer->xTasksWaitingToSend, xTicksToWait);
portYIELD_WITHIN_API();
} else {
//We have timed out
xExitLoop = pdTRUE;
}
loop_end:
portEXIT_CRITICAL(&pxRingbuffer->mux);
}
//Defer notifying the queue set until we are outside the loop and critical section.
if (xNotifyQueueSet == pdTRUE) {
xQueueSend((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, 0);
}
return xReturn;
}
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize,
TickType_t xTicksToWait)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xExitLoop = pdFALSE;
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
#ifdef __clang_analyzer__
// Teach clang-tidy that if NULL pointers are provided, this function will never dereference them
if (!pvItem1 || !pvItem2 || !xItemSize1 || !xItemSize2) {
return pdFALSE;
}
#endif /*__clang_analyzer__ */
while (xExitLoop == pdFALSE) {
portENTER_CRITICAL(&pxRingbuffer->mux);
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
//Item/data is available for retrieval
BaseType_t xIsSplit = pdFALSE;
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
//Read up to xMaxSize bytes from byte buffer
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
} else {
//Get (first) item from no-split/allow-split buffers
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
}
//If split buffer, check for split items
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
if (xIsSplit == pdTRUE) {
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
configASSERT(xIsSplit == pdFALSE); //Second part should not have wrapped flag
} else {
*pvItem2 = NULL;
}
}
xReturn = pdTRUE;
xExitLoop = pdTRUE;
goto loop_end;
} else if (xTicksToWait == (TickType_t) 0) {
//No block time. Return immediately.
xExitLoop = pdTRUE;
goto loop_end;
} else if (xEntryTimeSet == pdFALSE) {
//This is our first block. Set entry time
vTaskInternalSetTimeOutState(&xTimeOut);
xEntryTimeSet = pdTRUE;
}
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
//Not timed out yet. Block the current task
vTaskPlaceOnEventList(&pxRingbuffer->xTasksWaitingToReceive, xTicksToWait);
portYIELD_WITHIN_API();
} else {
//We have timed out.
xExitLoop = pdTRUE;
}
loop_end:
portEXIT_CRITICAL(&pxRingbuffer->mux);
}
return xReturn;
}
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
size_t *xItemSize1,
size_t *xItemSize2,
size_t xMaxSize)
{
BaseType_t xReturn = pdFALSE;
#ifdef __clang_analyzer__
// Teach clang-tidy that if NULL pointers are provided, this function will never dereference them
if (!pvItem1 || !pvItem2 || !xItemSize1 || !xItemSize2) {
return pdFALSE;
}
#endif /*__clang_analyzer__ */
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
BaseType_t xIsSplit = pdFALSE;
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
//Read up to xMaxSize bytes from byte buffer
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
} else {
//Get (first) item from no-split/allow-split buffers
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
}
//If split buffer, check for split items
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
if (xIsSplit == pdTRUE) {
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
configASSERT(xIsSplit == pdFALSE); //Second part should not have wrapped flag
} else {
*pvItem2 = NULL;
}
}
xReturn = pdTRUE;
} else {
xReturn = pdFALSE;
}
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
return xReturn;
}
// ------------------------------------------------ Public Functions ---------------------------------------------------
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType)
{
configASSERT(xBufferSize > 0);
configASSERT(xBufferType < RINGBUF_TYPE_MAX);
//Allocate memory
if (xBufferType != RINGBUF_TYPE_BYTEBUF) {
xBufferSize = rbALIGN_SIZE(xBufferSize); //xBufferSize is rounded up for no-split/allow-split buffers
}
Ringbuffer_t *pxNewRingbuffer = calloc(1, sizeof(Ringbuffer_t));
uint8_t *pucRingbufferStorage = malloc(xBufferSize);
if (pxNewRingbuffer == NULL || pucRingbufferStorage == NULL) {
goto err;
}
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
return (RingbufHandle_t)pxNewRingbuffer;
err:
//An error has occurred, Free memory and return NULL
free(pxNewRingbuffer);
free(pucRingbufferStorage);
return NULL;
}
RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum)
{
return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
}
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
RingbufferType_t xBufferType,
uint8_t *pucRingbufferStorage,
StaticRingbuffer_t *pxStaticRingbuffer)
{
//Check arguments
configASSERT(xBufferSize > 0);
configASSERT(xBufferType < RINGBUF_TYPE_MAX);
configASSERT(pucRingbufferStorage != NULL && pxStaticRingbuffer != NULL);
if (xBufferType != RINGBUF_TYPE_BYTEBUF) {
//No-split/allow-split buffer sizes must be 32-bit aligned
configASSERT(rbCHECK_ALIGNED(xBufferSize));
}
Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer;
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG;
return (RingbufHandle_t)pxNewRingbuffer;
}
BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, size_t xItemSize, TickType_t xTicksToWait)
{
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
//Check arguments
configASSERT(pxRingbuffer);
configASSERT(ppvItem != NULL);
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0); //Send acquire currently only supported in NoSplit buffers
*ppvItem = NULL;
if (xItemSize > pxRingbuffer->xMaxItemSize) {
return pdFALSE; //Data will never ever fit in the queue.
}
if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) {
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
}
return prvSendAcquireGeneric(pxRingbuffer, NULL, ppvItem, xItemSize, xTicksToWait);
}
BaseType_t xRingbufferSendComplete(RingbufHandle_t xRingbuffer, void *pvItem)
{