mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 20:54:24 +00:00
esp_ringbuf: Replace internal semaphores with task event lists
This commit replaces the existing TX/RX semaphores with FreeRTOS task event lists (similar to how FreeRTOS queues implement task blocking). Using task event lists allows the ring buffer object to be smaller and various ring buffer functions to be faster. The ring buffer queue set impelementation was also updated as a result.
This commit is contained in:
@@ -1,22 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FREERTOS_RINGBUF_H
|
#pragma once
|
||||||
#define FREERTOS_RINGBUF_H
|
|
||||||
|
|
||||||
#ifndef INC_FREERTOS_H
|
#include "freertos/FreeRTOS.h"
|
||||||
#error "include FreeRTOS.h" must appear in source files before "include ringbuf.h"
|
#include "freertos/queue.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <freertos/queue.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type by which ring buffers are referenced. For example, a call to xRingbufferCreate()
|
* Type by which ring buffers are referenced. For example, a call to xRingbufferCreate()
|
||||||
* returns a RingbufHandle_t variable that can then be used as a parameter to
|
* returns a RingbufHandle_t variable that can then be used as a parameter to
|
||||||
@@ -57,18 +53,17 @@ typedef enum {
|
|||||||
* buffer's control data structure.
|
* buffer's control data structure.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
|
|
||||||
typedef struct xSTATIC_RINGBUFFER {
|
typedef struct xSTATIC_RINGBUFFER {
|
||||||
/** @cond */ //Doxygen command to hide this structure from API Reference
|
/** @cond */ //Doxygen command to hide this structure from API Reference
|
||||||
size_t xDummy1[2];
|
size_t xDummy1[2];
|
||||||
UBaseType_t uxDummy2;
|
UBaseType_t uxDummy2;
|
||||||
BaseType_t xDummy3;
|
BaseType_t xDummy3;
|
||||||
void *pvDummy4[11];
|
void *pvDummy4[11];
|
||||||
StaticSemaphore_t xDummy5[2];
|
StaticList_t xDummy5[2];
|
||||||
|
void * pvDummy6;
|
||||||
portMUX_TYPE muxDummy;
|
portMUX_TYPE muxDummy;
|
||||||
/** @endcond */
|
/** @endcond */
|
||||||
} StaticRingbuffer_t;
|
} StaticRingbuffer_t;
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a ring buffer
|
* @brief Create a ring buffer
|
||||||
@@ -111,12 +106,10 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
|
|||||||
*
|
*
|
||||||
* @return A handle to the created ring buffer
|
* @return A handle to the created ring buffer
|
||||||
*/
|
*/
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
|
|
||||||
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
|
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
|
||||||
RingbufferType_t xBufferType,
|
RingbufferType_t xBufferType,
|
||||||
uint8_t *pucRingbufferStorage,
|
uint8_t *pucRingbufferStorage,
|
||||||
StaticRingbuffer_t *pxStaticRingbuffer);
|
StaticRingbuffer_t *pxStaticRingbuffer);
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insert an item into the ring buffer
|
* @brief Insert an item into the ring buffer
|
||||||
@@ -429,14 +422,14 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
|
|||||||
size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer);
|
size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add the ring buffer's read semaphore to a queue set.
|
* @brief Add the ring buffer to a queue set. Notified when data has been written to the ring buffer
|
||||||
*
|
*
|
||||||
* The ring buffer's read semaphore indicates that data has been written
|
* This function adds the ring buffer to a queue set, thus allowing a task to
|
||||||
* to the ring buffer. This function adds the ring buffer's read semaphore to
|
* block on multiple queues/ring buffers. The queue set is notified when the new
|
||||||
* a queue set.
|
* data becomes available to read on the ring buffer.
|
||||||
*
|
*
|
||||||
* @param[in] xRingbuffer Ring buffer to add to the queue set
|
* @param[in] xRingbuffer Ring buffer to add to the queue set
|
||||||
* @param[in] xQueueSet Queue set to add the ring buffer's read semaphore to
|
* @param[in] xQueueSet Queue set to add the ring buffer to
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - pdTRUE on success, pdFALSE otherwise
|
* - pdTRUE on success, pdFALSE otherwise
|
||||||
@@ -445,29 +438,32 @@ BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHan
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if the selected queue set member is the ring buffer's read semaphore
|
* @brief Check if the selected queue set member is a particular ring buffer
|
||||||
*
|
*
|
||||||
* This API checks if queue set member returned from xQueueSelectFromSet()
|
* This API checks if queue set member returned from xQueueSelectFromSet() is
|
||||||
* is the read semaphore of this ring buffer. If so, this indicates the ring buffer
|
* a particular ring buffer. If so, this indicates the ring buffer has items
|
||||||
* has items waiting to be retrieved.
|
* waiting to be retrieved.
|
||||||
*
|
*
|
||||||
* @param[in] xRingbuffer Ring buffer which should be checked
|
* @param[in] xRingbuffer Ring buffer to check
|
||||||
* @param[in] xMember Member returned from xQueueSelectFromSet
|
* @param[in] xMember Member returned from xQueueSelectFromSet
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - pdTRUE when semaphore belongs to ring buffer
|
* - pdTRUE when selected queue set member is the ring buffer
|
||||||
* - pdFALSE otherwise.
|
* - pdFALSE otherwise.
|
||||||
*/
|
*/
|
||||||
BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember);
|
static inline BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember)
|
||||||
|
{
|
||||||
|
return (xMember == (QueueSetMemberHandle_t)xRingbuffer) ? pdTRUE : pdFALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove the ring buffer's read semaphore from a queue set.
|
* @brief Remove the ring buffer from a queue set
|
||||||
*
|
*
|
||||||
* This specifically removes a ring buffer's read semaphore from a queue set. The
|
* This function removes a ring buffer from a queue set. The ring buffer must have been previously added to the queue
|
||||||
* read semaphore is used to indicate when data has been written to the ring buffer
|
* set using xRingbufferAddToQueueSetRead().
|
||||||
*
|
*
|
||||||
* @param[in] xRingbuffer Ring buffer to remove from the queue set
|
* @param[in] xRingbuffer Ring buffer to remove from the queue set
|
||||||
* @param[in] xQueueSet Queue set to remove the ring buffer's read semaphore from
|
* @param[in] xQueueSet Queue set to remove the ring buffer from
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - pdTRUE on success
|
* - pdTRUE on success
|
||||||
@@ -506,5 +502,3 @@ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* FREERTOS_RINGBUF_H */
|
|
||||||
|
@@ -12,7 +12,6 @@ entries:
|
|||||||
ringbuf: vRingbufferGetInfo (default)
|
ringbuf: vRingbufferGetInfo (default)
|
||||||
ringbuf: vRingbufferReturnItem (default)
|
ringbuf: vRingbufferReturnItem (default)
|
||||||
ringbuf: xRingbufferAddToQueueSetRead (default)
|
ringbuf: xRingbufferAddToQueueSetRead (default)
|
||||||
ringbuf: xRingbufferCanRead (default)
|
|
||||||
ringbuf: xRingbufferCreate (default)
|
ringbuf: xRingbufferCreate (default)
|
||||||
ringbuf: xRingbufferCreateStatic (default)
|
ringbuf: xRingbufferCreateStatic (default)
|
||||||
ringbuf: xRingbufferReceive (default)
|
ringbuf: xRingbufferReceive (default)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -7,10 +7,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/list.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/queue.h"
|
||||||
#include "freertos/ringbuf.h"
|
#include "freertos/ringbuf.h"
|
||||||
|
|
||||||
|
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||||
|
|
||||||
//32-bit alignment macros
|
//32-bit alignment macros
|
||||||
#define rbALIGN_MASK (0x03)
|
#define rbALIGN_MASK (0x03)
|
||||||
#define rbALIGN_SIZE( xSize ) ( ( xSize + rbALIGN_MASK ) & ~rbALIGN_MASK )
|
#define rbALIGN_SIZE( xSize ) ( ( xSize + rbALIGN_MASK ) & ~rbALIGN_MASK )
|
||||||
@@ -21,6 +24,7 @@
|
|||||||
#define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer
|
#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_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 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
|
//Item flags
|
||||||
#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite
|
#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite
|
||||||
@@ -28,15 +32,6 @@
|
|||||||
#define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around
|
#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
|
#define rbITEM_WRITTEN_FLAG ( ( UBaseType_t ) 8 ) //Item has been written to by the application, thus can be read
|
||||||
|
|
||||||
//Static allocation related
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
||||||
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xTransSemStatic) )
|
|
||||||
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xRecvSemStatic) )
|
|
||||||
#else
|
|
||||||
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xTransSemHandle )
|
|
||||||
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xRecvSemHandle )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
//This size of this structure must be 32-bit aligned
|
//This size of this structure must be 32-bit aligned
|
||||||
size_t xItemLen;
|
size_t xItemLen;
|
||||||
@@ -71,52 +66,25 @@ typedef struct RingbufferDefinition {
|
|||||||
uint8_t *pucTail; //Pointer to the end 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
|
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.
|
||||||
* TransSem: Binary semaphore used to indicate to a blocked transmitting tasks
|
List_t xTasksWaitingToReceive; //List of tasks that are blocked waiting to receive from this ring buffer. Stored in priority order.
|
||||||
* that more free space has become available or that the block has
|
QueueSetHandle_t xQueueSet; //Ring buffer's read queue set handle.
|
||||||
* timed out.
|
|
||||||
*
|
|
||||||
* RecvSem: Binary semaphore used to indicate to a blocked receiving task that
|
|
||||||
* new data/item has been written to the ring buffer.
|
|
||||||
*
|
|
||||||
* Note - When static allocation is enabled, the two semaphores are always
|
|
||||||
* statically stored in the ring buffer's control structure
|
|
||||||
* regardless of whether the ring buffer is allocated dynamically or
|
|
||||||
* statically. When static allocation is disabled, the two semaphores
|
|
||||||
* are allocated dynamically and their handles stored instead, thus
|
|
||||||
* making the ring buffer's control structure slightly smaller when
|
|
||||||
* static allocation is disabled.
|
|
||||||
*/
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
||||||
StaticSemaphore_t xTransSemStatic;
|
|
||||||
StaticSemaphore_t xRecvSemStatic;
|
|
||||||
#else
|
|
||||||
SemaphoreHandle_t xTransSemHandle;
|
|
||||||
SemaphoreHandle_t xRecvSemHandle;
|
|
||||||
#endif
|
|
||||||
portMUX_TYPE mux; //Spinlock required for SMP
|
portMUX_TYPE mux; //Spinlock required for SMP
|
||||||
} Ringbuffer_t;
|
} Ringbuffer_t;
|
||||||
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
||||||
#if __GNUC_PREREQ(4, 6)
|
#if __GNUC_PREREQ(4, 6)
|
||||||
_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t");
|
_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in
|
|
||||||
FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we
|
|
||||||
would need to set the maximum to the maximum amount of times a null-byte unit first in the buffer,
|
|
||||||
which is quite high and so would waste a fair amount of memory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* --------------------------- Static Declarations -------------------------- */
|
// ------------------------------------------------ Forward Declares ---------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* WARNING: All of the following static functions (except generic functions)
|
* WARNING: All of the following static functions (except generic functions)
|
||||||
* ARE NOT THREAD SAFE. Therefore they should only be called within a critical
|
* ARE NOT THREAD SAFE. Therefore they should only be called within a critical
|
||||||
* section (using spin locks)
|
* section (using spin locks)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//Initialize a ring buffer after space has been allocated for it
|
//Initialize a ring buffer after space has been allocated for it
|
||||||
static void prvInitializeNewRingbuffer(size_t xBufferSize,
|
static void prvInitializeNewRingbuffer(size_t xBufferSize,
|
||||||
RingbufferType_t xBufferType,
|
RingbufferType_t xBufferType,
|
||||||
@@ -201,12 +169,23 @@ static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer);
|
|||||||
//Get the maximum size an item that can currently have if sent to a byte buffer
|
//Get the maximum size an item that can currently have if sent to a byte buffer
|
||||||
static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
|
static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Generic function used to retrieve an item/data from ring buffers. If called on
|
Generic function used to send or acquire an item/buffer.
|
||||||
* an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of
|
- If sending, set ppvItem to NULL. pvItem remains unchanged on failure.
|
||||||
* a split item will be retrieved. xMaxSize will only take effect if called on
|
- If acquiring, set pvItem to NULL. ppvItem remains unchanged on failure.
|
||||||
* byte buffers.
|
*/
|
||||||
*/
|
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,
|
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
|
||||||
void **pvItem1,
|
void **pvItem1,
|
||||||
void **pvItem2,
|
void **pvItem2,
|
||||||
@@ -215,7 +194,7 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
|
|||||||
size_t xMaxSize,
|
size_t xMaxSize,
|
||||||
TickType_t xTicksToWait);
|
TickType_t xTicksToWait);
|
||||||
|
|
||||||
//Generic function used to retrieve an item/data from ring buffers in an ISR
|
//From ISR version of prvReceiveGeneric()
|
||||||
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
|
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
|
||||||
void **pvItem1,
|
void **pvItem1,
|
||||||
void **pvItem2,
|
void **pvItem2,
|
||||||
@@ -223,7 +202,7 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
|
|||||||
size_t *xItemSize2,
|
size_t *xItemSize2,
|
||||||
size_t xMaxSize);
|
size_t xMaxSize);
|
||||||
|
|
||||||
/* --------------------------- Static Definitions --------------------------- */
|
// ------------------------------------------------ Static Functions ---------------------------------------------------
|
||||||
|
|
||||||
static void prvInitializeNewRingbuffer(size_t xBufferSize,
|
static void prvInitializeNewRingbuffer(size_t xBufferSize,
|
||||||
RingbufferType_t xBufferType,
|
RingbufferType_t xBufferType,
|
||||||
@@ -272,7 +251,11 @@ static void prvInitializeNewRingbuffer(size_t xBufferSize,
|
|||||||
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize;
|
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize;
|
||||||
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
|
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
|
||||||
}
|
}
|
||||||
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxNewRingbuffer));
|
|
||||||
|
vListInitialise(&pxNewRingbuffer->xTasksWaitingToSend);
|
||||||
|
vListInitialise(&pxNewRingbuffer->xTasksWaitingToReceive);
|
||||||
|
pxNewRingbuffer->xQueueSet = NULL;
|
||||||
|
|
||||||
portMUX_INITIALIZE(&pxNewRingbuffer->mux);
|
portMUX_INITIALIZE(&pxNewRingbuffer->mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,6 +742,73 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer)
|
|||||||
return xFreeSize;
|
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,
|
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
|
||||||
void **pvItem1,
|
void **pvItem1,
|
||||||
void **pvItem2,
|
void **pvItem2,
|
||||||
@@ -768,30 +818,31 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
|
|||||||
TickType_t xTicksToWait)
|
TickType_t xTicksToWait)
|
||||||
{
|
{
|
||||||
BaseType_t xReturn = pdFALSE;
|
BaseType_t xReturn = pdFALSE;
|
||||||
BaseType_t xReturnSemaphore = pdFALSE;
|
BaseType_t xExitLoop = pdFALSE;
|
||||||
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
|
BaseType_t xEntryTimeSet = pdFALSE;
|
||||||
TickType_t xTicksRemaining = xTicksToWait;
|
TimeOut_t xTimeOut;
|
||||||
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
|
|
||||||
//Block until more free space becomes available or timeout
|
|
||||||
if (xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
|
|
||||||
xReturn = pdFALSE; //Timed out attempting to get semaphore
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Semaphore obtained, check if item can be retrieved
|
#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);
|
portENTER_CRITICAL(&pxRingbuffer->mux);
|
||||||
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
||||||
//Item is available for retrieval
|
//Item/data is available for retrieval
|
||||||
BaseType_t xIsSplit = pdFALSE;
|
BaseType_t xIsSplit = pdFALSE;
|
||||||
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
|
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
|
||||||
//Second argument (pxIsSplit) is unused for byte buffers
|
//Read up to xMaxSize bytes from byte buffer
|
||||||
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
|
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
|
||||||
} else {
|
} else {
|
||||||
//Third argument (xMaxSize) is unused for no-split/allow-split buffers
|
//Get (first) item from no-split/allow-split buffers
|
||||||
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
|
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
|
||||||
}
|
}
|
||||||
//Check for item split if configured to do so
|
//If split buffer, check for split items
|
||||||
if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && (pvItem2 != NULL) && (xItemSize2 != NULL)) {
|
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
|
||||||
if (xIsSplit == pdTRUE) {
|
if (xIsSplit == pdTRUE) {
|
||||||
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
|
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
|
||||||
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
|
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
|
||||||
@@ -801,26 +852,30 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
xReturn = pdTRUE;
|
xReturn = pdTRUE;
|
||||||
if (pxRingbuffer->xItemsWaiting > 0) {
|
xExitLoop = pdTRUE;
|
||||||
xReturnSemaphore = pdTRUE;
|
goto loop_end;
|
||||||
}
|
} else if (xTicksToWait == (TickType_t) 0) {
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
//No block time. Return immediately.
|
||||||
break;
|
xExitLoop = pdTRUE;
|
||||||
|
goto loop_end;
|
||||||
|
} else if (xEntryTimeSet == pdFALSE) {
|
||||||
|
//This is our first block. Set entry time
|
||||||
|
vTaskInternalSetTimeOutState(&xTimeOut);
|
||||||
|
xEntryTimeSet = pdTRUE;
|
||||||
}
|
}
|
||||||
//No item available for retrieval, adjust ticks and take the semaphore again
|
|
||||||
if (xTicksToWait != portMAX_DELAY) {
|
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
|
||||||
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
|
//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);
|
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
||||||
/*
|
|
||||||
* Gap between critical section and re-acquiring of the semaphore. If
|
|
||||||
* semaphore is given now, priority inversion might occur (see docs)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xReturnSemaphore == pdTRUE) {
|
|
||||||
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)); //Give semaphore back so other tasks can retrieve
|
|
||||||
}
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,20 +887,26 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
|
|||||||
size_t xMaxSize)
|
size_t xMaxSize)
|
||||||
{
|
{
|
||||||
BaseType_t xReturn = pdFALSE;
|
BaseType_t xReturn = pdFALSE;
|
||||||
BaseType_t xReturnSemaphore = 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);
|
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
if(prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
||||||
BaseType_t xIsSplit = pdFALSE;
|
BaseType_t xIsSplit = pdFALSE;
|
||||||
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
|
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
|
||||||
//Second argument (pxIsSplit) is unused for byte buffers
|
//Read up to xMaxSize bytes from byte buffer
|
||||||
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
|
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
|
||||||
} else {
|
} else {
|
||||||
//Third argument (xMaxSize) is unused for no-split/allow-split buffers
|
//Get (first) item from no-split/allow-split buffers
|
||||||
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
|
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
|
||||||
}
|
}
|
||||||
//Check for item split if configured to do so
|
//If split buffer, check for split items
|
||||||
if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && pvItem2 != NULL && xItemSize2 != NULL) {
|
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
|
||||||
if (xIsSplit == pdTRUE) {
|
if (xIsSplit == pdTRUE) {
|
||||||
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
|
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
|
||||||
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
|
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
|
||||||
@@ -855,19 +916,15 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
xReturn = pdTRUE;
|
xReturn = pdTRUE;
|
||||||
if (pxRingbuffer->xItemsWaiting > 0) {
|
} else {
|
||||||
xReturnSemaphore = pdTRUE;
|
xReturn = pdFALSE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
|
|
||||||
if (xReturnSemaphore == pdTRUE) {
|
|
||||||
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), NULL); //Give semaphore back so other tasks can retrieve
|
|
||||||
}
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------- Public Definitions --------------------------- */
|
// ------------------------------------------------ Public Functions ---------------------------------------------------
|
||||||
|
|
||||||
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType)
|
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType)
|
||||||
{
|
{
|
||||||
@@ -884,25 +941,6 @@ RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferTy
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initialize Semaphores
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
|
|
||||||
//We don't use the handles for static semaphores, and xSemaphoreCreateBinaryStatic will never fail thus no need to check static case
|
|
||||||
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
|
|
||||||
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
|
|
||||||
#else
|
|
||||||
pxNewRingbuffer->xTransSemHandle = xSemaphoreCreateBinary();
|
|
||||||
pxNewRingbuffer->xRecvSemHandle = xSemaphoreCreateBinary();
|
|
||||||
if (pxNewRingbuffer->xTransSemHandle == NULL || pxNewRingbuffer->xRecvSemHandle == NULL) {
|
|
||||||
if (pxNewRingbuffer->xTransSemHandle != NULL) {
|
|
||||||
vSemaphoreDelete(pxNewRingbuffer->xTransSemHandle);
|
|
||||||
}
|
|
||||||
if (pxNewRingbuffer->xRecvSemHandle != NULL) {
|
|
||||||
vSemaphoreDelete(pxNewRingbuffer->xRecvSemHandle);
|
|
||||||
}
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
|
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
|
||||||
return (RingbufHandle_t)pxNewRingbuffer;
|
return (RingbufHandle_t)pxNewRingbuffer;
|
||||||
|
|
||||||
@@ -918,7 +956,6 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum)
|
|||||||
return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
|
return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
||||||
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
|
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
|
||||||
RingbufferType_t xBufferType,
|
RingbufferType_t xBufferType,
|
||||||
uint8_t *pucRingbufferStorage,
|
uint8_t *pucRingbufferStorage,
|
||||||
@@ -934,22 +971,19 @@ RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer;
|
Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer;
|
||||||
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
|
|
||||||
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
|
|
||||||
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
|
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
|
||||||
pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG;
|
pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG;
|
||||||
return (RingbufHandle_t)pxNewRingbuffer;
|
return (RingbufHandle_t)pxNewRingbuffer;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, size_t xItemSize, TickType_t xTicksToWait)
|
BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, size_t xItemSize, TickType_t xTicksToWait)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
|
|
||||||
|
//Check arguments
|
||||||
configASSERT(pxRingbuffer);
|
configASSERT(pxRingbuffer);
|
||||||
configASSERT(ppvItem != NULL || xItemSize == 0);
|
configASSERT(ppvItem != NULL || xItemSize == 0);
|
||||||
//currently only supported in NoSplit buffers
|
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0); //Send acquire currently only supported in NoSplit buffers
|
||||||
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0);
|
|
||||||
|
|
||||||
*ppvItem = NULL;
|
*ppvItem = NULL;
|
||||||
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
||||||
@@ -959,61 +993,38 @@ BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, s
|
|||||||
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attempt to send an item
|
return prvSendAcquireGeneric(pxRingbuffer, NULL, ppvItem, xItemSize, xTicksToWait);
|
||||||
BaseType_t xReturn = pdFALSE;
|
|
||||||
BaseType_t xReturnSemaphore = pdFALSE;
|
|
||||||
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
|
|
||||||
TickType_t xTicksRemaining = xTicksToWait;
|
|
||||||
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
|
|
||||||
//Block until more free space becomes available or timeout
|
|
||||||
if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
|
|
||||||
xReturn = pdFALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Semaphore obtained, check if item can fit
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
|
|
||||||
//Item will fit, copy item
|
|
||||||
*ppvItem = prvAcquireItemNoSplit(pxRingbuffer, xItemSize);
|
|
||||||
xReturn = pdTRUE;
|
|
||||||
//Check if the free semaphore should be returned to allow other tasks to send
|
|
||||||
if (prvGetFreeSize(pxRingbuffer) > 0) {
|
|
||||||
xReturnSemaphore = pdTRUE;
|
|
||||||
}
|
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//Item doesn't fit, adjust ticks and take the semaphore again
|
|
||||||
if (xTicksToWait != portMAX_DELAY) {
|
|
||||||
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
|
|
||||||
}
|
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
/*
|
|
||||||
* Gap between critical section and re-acquiring of the semaphore. If
|
|
||||||
* semaphore is given now, priority inversion might occur (see docs)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xReturnSemaphore == pdTRUE) {
|
|
||||||
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can acquire
|
|
||||||
}
|
|
||||||
return xReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t xRingbufferSendComplete(RingbufHandle_t xRingbuffer, void *pvItem)
|
BaseType_t xRingbufferSendComplete(RingbufHandle_t xRingbuffer, void *pvItem)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
|
BaseType_t xNotifyQueueSet = pdFALSE;
|
||||||
|
|
||||||
|
//Check arguments
|
||||||
configASSERT(pxRingbuffer);
|
configASSERT(pxRingbuffer);
|
||||||
configASSERT(pvItem != NULL);
|
configASSERT(pvItem != NULL);
|
||||||
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0);
|
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0);
|
||||||
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
portENTER_CRITICAL(&pxRingbuffer->mux);
|
||||||
prvSendItemDoneNoSplit(pxRingbuffer, pvItem);
|
prvSendItemDoneNoSplit(pxRingbuffer, pvItem);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
||||||
|
|
||||||
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
|
if (xNotifyQueueSet == pdTRUE) {
|
||||||
|
xQueueSend((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, 0);
|
||||||
|
}
|
||||||
return pdTRUE;
|
return pdTRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1022,8 +1033,9 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
|
|||||||
size_t xItemSize,
|
size_t xItemSize,
|
||||||
TickType_t xTicksToWait)
|
TickType_t xTicksToWait)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
|
|
||||||
|
//Check arguments
|
||||||
configASSERT(pxRingbuffer);
|
configASSERT(pxRingbuffer);
|
||||||
configASSERT(pvItem != NULL || xItemSize == 0);
|
configASSERT(pvItem != NULL || xItemSize == 0);
|
||||||
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
||||||
@@ -1033,49 +1045,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
|
|||||||
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attempt to send an item
|
return prvSendAcquireGeneric(pxRingbuffer, pvItem, NULL, xItemSize, xTicksToWait);
|
||||||
BaseType_t xReturn = pdFALSE;
|
|
||||||
BaseType_t xReturnSemaphore = pdFALSE;
|
|
||||||
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
|
|
||||||
TickType_t xTicksRemaining = xTicksToWait;
|
|
||||||
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
|
|
||||||
//Block until more free space becomes available or timeout
|
|
||||||
if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
|
|
||||||
xReturn = pdFALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//Semaphore obtained, check if item can fit
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
|
|
||||||
//Item will fit, copy item
|
|
||||||
pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize);
|
|
||||||
xReturn = pdTRUE;
|
|
||||||
//Check if the free semaphore should be returned to allow other tasks to send
|
|
||||||
if (prvGetFreeSize(pxRingbuffer) > 0) {
|
|
||||||
xReturnSemaphore = pdTRUE;
|
|
||||||
}
|
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//Item doesn't fit, adjust ticks and take the semaphore again
|
|
||||||
if (xTicksToWait != portMAX_DELAY) {
|
|
||||||
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
|
|
||||||
}
|
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
|
||||||
/*
|
|
||||||
* Gap between critical section and re-acquiring of the semaphore. If
|
|
||||||
* semaphore is given now, priority inversion might occur (see docs)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xReturnSemaphore == pdTRUE) {
|
|
||||||
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can send
|
|
||||||
}
|
|
||||||
if (xReturn == pdTRUE) {
|
|
||||||
//Indicate item was successfully sent
|
|
||||||
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
|
|
||||||
}
|
|
||||||
return xReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
|
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
|
||||||
@@ -1083,8 +1053,11 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
|
|||||||
size_t xItemSize,
|
size_t xItemSize,
|
||||||
BaseType_t *pxHigherPriorityTaskWoken)
|
BaseType_t *pxHigherPriorityTaskWoken)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
|
BaseType_t xNotifyQueueSet = pdFALSE;
|
||||||
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
//Check arguments
|
||||||
configASSERT(pxRingbuffer);
|
configASSERT(pxRingbuffer);
|
||||||
configASSERT(pvItem != NULL || xItemSize == 0);
|
configASSERT(pvItem != NULL || xItemSize == 0);
|
||||||
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
||||||
@@ -1094,45 +1067,45 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
|
|||||||
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attempt to send an item
|
|
||||||
BaseType_t xReturn;
|
|
||||||
BaseType_t xReturnSemaphore = pdFALSE;
|
|
||||||
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
|
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
if (pxRingbuffer->xCheckItemFits(xRingbuffer, xItemSize) == pdTRUE) {
|
if (pxRingbuffer->xCheckItemFits(xRingbuffer, xItemSize) == pdTRUE) {
|
||||||
pxRingbuffer->vCopyItem(xRingbuffer, pvItem, xItemSize);
|
pxRingbuffer->vCopyItem(xRingbuffer, pvItem, xItemSize);
|
||||||
xReturn = pdTRUE;
|
if (pxRingbuffer->xQueueSet) {
|
||||||
//Check if the free semaphore should be returned to allow other tasks to send
|
//If ring buffer was added to a queue set, notify the queue set
|
||||||
if (prvGetFreeSize(pxRingbuffer) > 0) {
|
xNotifyQueueSet = pdTRUE;
|
||||||
xReturnSemaphore = 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. Record that a context switch is required.
|
||||||
|
if (pxHigherPriorityTaskWoken != NULL) {
|
||||||
|
*pxHigherPriorityTaskWoken = pdTRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
xReturn = pdTRUE;
|
||||||
} else {
|
} else {
|
||||||
xReturn = pdFALSE;
|
xReturn = pdFALSE;
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
|
|
||||||
if (xReturnSemaphore == pdTRUE) {
|
if (xNotifyQueueSet == pdTRUE) {
|
||||||
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send
|
xQueueSendFromISR((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, pxHigherPriorityTaskWoken);
|
||||||
}
|
|
||||||
if (xReturn == pdTRUE) {
|
|
||||||
//Indicate item was successfully sent
|
|
||||||
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
|
|
||||||
}
|
}
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait)
|
void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
//Check arguments
|
||||||
|
configASSERT(pxRingbuffer && pxItemSize);
|
||||||
|
|
||||||
//Attempt to retrieve an item
|
//Attempt to retrieve an item
|
||||||
void *pvTempItem;
|
void *pvTempItem;
|
||||||
size_t xTempSize;
|
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, 0, xTicksToWait) == pdTRUE) {
|
||||||
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0, xTicksToWait) == pdTRUE) {
|
|
||||||
if (pxItemSize != NULL) {
|
|
||||||
*pxItemSize = xTempSize;
|
|
||||||
}
|
|
||||||
return pvTempItem;
|
return pvTempItem;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1141,17 +1114,14 @@ void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickTy
|
|||||||
|
|
||||||
void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
|
void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
//Check arguments
|
||||||
|
configASSERT(pxRingbuffer && pxItemSize);
|
||||||
|
|
||||||
//Attempt to retrieve an item
|
//Attempt to retrieve an item
|
||||||
void *pvTempItem;
|
void *pvTempItem;
|
||||||
size_t xTempSize;
|
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, 0) == pdTRUE) {
|
||||||
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0) == pdTRUE) {
|
|
||||||
if (pxItemSize != NULL) {
|
|
||||||
*pxItemSize = xTempSize;
|
|
||||||
}
|
|
||||||
return pvTempItem;
|
return pvTempItem;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1165,37 +1135,13 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer,
|
|||||||
size_t *pxTailItemSize,
|
size_t *pxTailItemSize,
|
||||||
TickType_t xTicksToWait)
|
TickType_t xTicksToWait)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
|
|
||||||
configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
|
|
||||||
|
|
||||||
//Attempt to retrieve multiple items
|
//Check arguments
|
||||||
void *pvTempHeadItem, *pvTempTailItem;
|
configASSERT(pxRingbuffer && ppvHeadItem && ppvTailItem && pxHeadItemSize && pxTailItemSize);
|
||||||
size_t xTempHeadSize, xTempTailSize;
|
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
|
||||||
if (prvReceiveGeneric(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0, xTicksToWait) == pdTRUE) {
|
|
||||||
//At least one item was retrieved
|
return prvReceiveGeneric(pxRingbuffer, ppvHeadItem, ppvTailItem, pxHeadItemSize, pxTailItemSize, 0, xTicksToWait);
|
||||||
*ppvHeadItem = pvTempHeadItem;
|
|
||||||
if(pxHeadItemSize != NULL){
|
|
||||||
*pxHeadItemSize = xTempHeadSize;
|
|
||||||
}
|
|
||||||
//Check to see if a second item was also retrieved
|
|
||||||
if (pvTempTailItem != NULL) {
|
|
||||||
*ppvTailItem = pvTempTailItem;
|
|
||||||
if (pxTailItemSize != NULL) {
|
|
||||||
*pxTailItemSize = xTempTailSize;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*ppvTailItem = NULL;
|
|
||||||
}
|
|
||||||
return pdTRUE;
|
|
||||||
} else {
|
|
||||||
//No items retrieved
|
|
||||||
*ppvHeadItem = NULL;
|
|
||||||
*ppvTailItem = NULL;
|
|
||||||
return pdFALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
|
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
|
||||||
@@ -1204,36 +1150,13 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
|
|||||||
size_t *pxHeadItemSize,
|
size_t *pxHeadItemSize,
|
||||||
size_t *pxTailItemSize)
|
size_t *pxTailItemSize)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
|
|
||||||
configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
|
|
||||||
|
|
||||||
//Attempt to retrieve multiple items
|
//Check arguments
|
||||||
void *pvTempHeadItem = NULL, *pvTempTailItem = NULL;
|
configASSERT(pxRingbuffer && ppvHeadItem && ppvTailItem && pxHeadItemSize && pxTailItemSize);
|
||||||
size_t xTempHeadSize, xTempTailSize;
|
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
|
||||||
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0) == pdTRUE) {
|
|
||||||
//At least one item was received
|
return prvReceiveGenericFromISR(pxRingbuffer, ppvHeadItem, ppvTailItem, pxHeadItemSize, pxTailItemSize, 0);
|
||||||
*ppvHeadItem = pvTempHeadItem;
|
|
||||||
if (pxHeadItemSize != NULL) {
|
|
||||||
*pxHeadItemSize = xTempHeadSize;
|
|
||||||
}
|
|
||||||
//Check to see if a second item was also retrieved
|
|
||||||
if (pvTempTailItem != NULL) {
|
|
||||||
*ppvTailItem = pvTempTailItem;
|
|
||||||
if (pxTailItemSize != NULL) {
|
|
||||||
*pxTailItemSize = xTempTailSize;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*ppvTailItem = NULL;
|
|
||||||
}
|
|
||||||
return pdTRUE;
|
|
||||||
} else {
|
|
||||||
*ppvHeadItem = NULL;
|
|
||||||
*ppvTailItem = NULL;
|
|
||||||
return pdFALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
|
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
|
||||||
@@ -1241,21 +1164,18 @@ void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
|
|||||||
TickType_t xTicksToWait,
|
TickType_t xTicksToWait,
|
||||||
size_t xMaxSize)
|
size_t xMaxSize)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
//Check arguments
|
||||||
|
configASSERT(pxRingbuffer && pxItemSize);
|
||||||
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
|
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
|
||||||
|
|
||||||
if (xMaxSize == 0) {
|
if (xMaxSize == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attempt to retrieve up to xMaxSize bytes
|
//Attempt to retrieve up to xMaxSize bytes
|
||||||
void *pvTempItem;
|
void *pvTempItem;
|
||||||
size_t xTempSize;
|
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) {
|
||||||
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) {
|
|
||||||
if (pxItemSize != NULL) {
|
|
||||||
*pxItemSize = xTempSize;
|
|
||||||
}
|
|
||||||
return pvTempItem;
|
return pvTempItem;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1264,21 +1184,18 @@ void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
|
|||||||
|
|
||||||
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize)
|
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize)
|
||||||
{
|
{
|
||||||
//Check arguments
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
//Check arguments
|
||||||
|
configASSERT(pxRingbuffer && pxItemSize);
|
||||||
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
|
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
|
||||||
|
|
||||||
if (xMaxSize == 0) {
|
if (xMaxSize == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attempt to retrieve up to xMaxSize bytes
|
//Attempt to retrieve up to xMaxSize bytes
|
||||||
void *pvTempItem;
|
void *pvTempItem;
|
||||||
size_t xTempSize;
|
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, xMaxSize) == pdTRUE) {
|
||||||
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize) == pdTRUE) {
|
|
||||||
if (pxItemSize != NULL) {
|
|
||||||
*pxItemSize = xTempSize;
|
|
||||||
}
|
|
||||||
return pvTempItem;
|
return pvTempItem;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1293,8 +1210,14 @@ void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem)
|
|||||||
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
portENTER_CRITICAL(&pxRingbuffer->mux);
|
||||||
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
|
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
|
||||||
|
//If a task was waiting for space to send, unblock it immediately.
|
||||||
|
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToSend) == pdFALSE) {
|
||||||
|
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToSend) == pdTRUE) {
|
||||||
|
//The unblocked task will preempt us. Trigger a yield here.
|
||||||
|
portYIELD_WITHIN_API();
|
||||||
|
}
|
||||||
|
}
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
||||||
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken)
|
void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken)
|
||||||
@@ -1305,8 +1228,16 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas
|
|||||||
|
|
||||||
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
|
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
|
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
|
||||||
|
//If a task was waiting for space to send, unblock it immediately.
|
||||||
|
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToSend) == pdFALSE) {
|
||||||
|
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToSend) == pdTRUE) {
|
||||||
|
//The unblocked task will preempt us. Record that a context switch is required.
|
||||||
|
if (pxHigherPriorityTaskWoken != NULL) {
|
||||||
|
*pxHigherPriorityTaskWoken = pdTRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
|
||||||
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vRingbufferDelete(RingbufHandle_t xRingbuffer)
|
void vRingbufferDelete(RingbufHandle_t xRingbuffer)
|
||||||
@@ -1314,9 +1245,6 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer)
|
|||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
configASSERT(pxRingbuffer);
|
||||||
|
|
||||||
vSemaphoreDelete(rbGET_TX_SEM_HANDLE(pxRingbuffer));
|
|
||||||
vSemaphoreDelete(rbGET_RX_SEM_HANDLE(pxRingbuffer));
|
|
||||||
|
|
||||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_STATIC_FLAG) {
|
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_STATIC_FLAG) {
|
||||||
//Ring buffer was statically allocated, no need to free
|
//Ring buffer was statically allocated, no need to free
|
||||||
@@ -1349,46 +1277,48 @@ size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer)
|
|||||||
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
|
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
|
||||||
{
|
{
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
|
||||||
BaseType_t xReturn;
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
configASSERT(pxRingbuffer && xQueueSet);
|
||||||
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
portENTER_CRITICAL(&pxRingbuffer->mux);
|
||||||
//Cannot add semaphore to queue set if semaphore is not empty. Temporarily hold semaphore
|
if (pxRingbuffer->xQueueSet != NULL || prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
||||||
BaseType_t result = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
|
/*
|
||||||
xReturn = xQueueAddToSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
|
- Cannot add ring buffer to more than one queue set
|
||||||
if (result == pdTRUE) {
|
- It is dangerous to add a ring buffer to a queue set if the ring buffer currently has data to be read.
|
||||||
//Return semaphore if temporarily held
|
*/
|
||||||
result = xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
|
xReturn = pdFALSE;
|
||||||
configASSERT(result == pdTRUE);
|
} else {
|
||||||
|
//Add ring buffer to queue set
|
||||||
|
pxRingbuffer->xQueueSet = xQueueSet;
|
||||||
|
xReturn = pdTRUE;
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
||||||
return xReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember)
|
return xReturn;
|
||||||
{
|
|
||||||
//Check if the selected queue set member is the ring buffer's read semaphore
|
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
return (rbGET_RX_SEM_HANDLE(pxRingbuffer) == xMember) ? pdTRUE : pdFALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
|
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
|
||||||
{
|
{
|
||||||
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
|
||||||
configASSERT(pxRingbuffer);
|
|
||||||
|
|
||||||
BaseType_t xReturn;
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
configASSERT(pxRingbuffer && xQueueSet);
|
||||||
|
|
||||||
portENTER_CRITICAL(&pxRingbuffer->mux);
|
portENTER_CRITICAL(&pxRingbuffer->mux);
|
||||||
//Cannot remove semaphore from queue set if semaphore is not empty. Temporarily hold semaphore
|
if (pxRingbuffer->xQueueSet != xQueueSet || prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
|
||||||
BaseType_t result = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
|
/*
|
||||||
xReturn = xQueueRemoveFromSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
|
- Ring buffer was never added to this queue set
|
||||||
if (result == pdTRUE) {
|
- It is dangerous to remove a ring buffer from a queue set if the ring buffer currently has data to be read.
|
||||||
//Return semaphore if temporarily held
|
*/
|
||||||
result = xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
|
xReturn = pdFALSE;
|
||||||
configASSERT(result == pdTRUE);
|
} else {
|
||||||
|
//Remove ring buffer from queue set
|
||||||
|
pxRingbuffer->xQueueSet = NULL;
|
||||||
|
xReturn = pdTRUE;
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
portEXIT_CRITICAL(&pxRingbuffer->mux);
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -368,17 +368,6 @@ The code snippet below demonstrates a ring buffer being allocated entirely in ex
|
|||||||
free(buffer_struct);
|
free(buffer_struct);
|
||||||
free(buffer_storage);
|
free(buffer_storage);
|
||||||
|
|
||||||
Priority Inversion
|
|
||||||
^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest priority task will always be serviced first.** However due to the usage of binary semaphores in the ring buffer's underlying implementation, priority inversion may occur under very specific circumstances.
|
|
||||||
|
|
||||||
The ring buffer governs sending by a binary semaphore which is given whenever space is freed on the ring buffer. The highest priority task waiting to send will repeatedly take the semaphore until sufficient free space becomes available or until it times out. Ideally this should prevent any lower priority tasks from being serviced as the semaphore should always be given to the highest priority task.
|
|
||||||
|
|
||||||
However, in between iterations of acquiring the semaphore, there is a **gap in the critical section** which may permit another task (on the other core or with an even higher priority) to free some space on the ring buffer and as a result give the semaphore. Therefore, the semaphore will be given before the highest priority task can re-acquire the semaphore. This will result in the **semaphore being acquired by the second-highest priority task** waiting to send, hence causing priority inversion.
|
|
||||||
|
|
||||||
This side effect will not affect ring buffer performance drastically given if the number of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating near maximum capacity.
|
|
||||||
|
|
||||||
|
|
||||||
.. ------------------------------------------- ESP-IDF Tick and Idle Hooks ---------------------------------------------
|
.. ------------------------------------------- ESP-IDF Tick and Idle Hooks ---------------------------------------------
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user