Add callback overrides for stream buffer and message buffers (#437)

* Let each stream/message can use its own sbSEND_COMPLETED

In FreeRTOS.h, set the default value of configUSE_SB_COMPLETED_CALLBACK
to zero, and add additional space for the function pointer when
the buffer created statically.

In stream_buffer.c, modify the macro of sbSEND_COMPLETED which let
the stream buffer to use its own implementation, and then add an
pointer to the stream buffer's structure, and modify the
implementation of the buffer creating and initializing

Co-authored-by: eddie9712 <qw1562435@gmail.com>
diff --git a/.github/lexicon.txt b/.github/lexicon.txt
index 85aa17a..7510135 100644
--- a/.github/lexicon.txt
+++ b/.github/lexicon.txt
@@ -1693,10 +1693,12 @@
 pxramstack
 pxreadycoroutinelists
 pxreadytaskslists
+pxreceivecompletedcallback
 pxregions
 pxresult
 pxrxedmessage
 pxsemaphorebuffer
+pxsendcompletedcallback
 pxstack
 pxstackbase
 pxstackbuffer
@@ -2796,6 +2798,7 @@
 xinterruptdescriptortable
 xisfeasable
 xisfeasible
+xisinsideisr
 xismessagebuffer
 xisprivileged
 xitemvalue
diff --git a/History.txt b/History.txt
index ba456f7..7f57de1 100644
--- a/History.txt
+++ b/History.txt
@@ -13,6 +13,17 @@
 	  are both typedefs of the same struct xLIST_ITEM. This addresses some issues

 	  observed when strict-aliasing and link time optimization are enabled.

 	  To maintain backwards compatibility, configUSE_MINI_LIST_ITEM defaults to 1.

+	+ Add the ability to override send and receive completed callbacks for each

+	  instance of a stream buffer or message buffer. The feature can be controlled 

+	  by setting  the configuration option configUSE_SB_COMPLETED_CALLBACK in 

+	  FreeRTOSConfig.h. When the option is set to 1, APIs

+	  xStreamBufferCreateWithCallback() or xStreamBufferCreateStaticWithCallback()

+	  (and likewise APIs from message buffer) can be used to create a stream buffer 

+	  or message buffer instance with application provided callback overrides. When

+	  the option is set to 0, then the default callbacks as defined by

+	  sbSEND_COMPLETED() and sbRECEIVE_COMPLETED() macros are invoked. To maintain 

+	  backwards compatibility, configUSE_SB_COMPLETED_CALLBACK defaults to 0. The 

+	  functionaility is currently not supported for MPU enabled ports.

 

 Changes between FreeRTOS V10.4.5 and FreeRTOS V10.4.6 released November 12 2021

 

diff --git a/include/FreeRTOS.h b/include/FreeRTOS.h
index c961158..5ce4e28 100644
--- a/include/FreeRTOS.h
+++ b/include/FreeRTOS.h
@@ -890,6 +890,12 @@
     #define configUSE_POSIX_ERRNO    0

 #endif

 

+#ifndef configUSE_SB_COMPLETED_CALLBACK

+

+/* By default per-instance callbacks are not enabled for stream buffer or message buffer. */

+    #define configUSE_SB_COMPLETED_CALLBACK    0

+#endif

+

 #ifndef portTICK_TYPE_IS_ATOMIC

     #define portTICK_TYPE_IS_ATOMIC    0

 #endif

@@ -1356,6 +1362,9 @@
     #if ( configUSE_TRACE_FACILITY == 1 )

         UBaseType_t uxDummy4;

     #endif

+    #if ( configUSE_SB_COMPLETED_CALLBACK == 1 )

+        void * pvDummy5[ 2 ];

+    #endif

 } StaticStreamBuffer_t;

 

 /* Message buffers are built on stream buffers. */

diff --git a/include/message_buffer.h b/include/message_buffer.h
index b1962fa..32284c4 100644
--- a/include/message_buffer.h
+++ b/include/message_buffer.h
@@ -107,6 +107,18 @@
  * 32-bit architecture, so on most 32-bit architectures a 10 byte message will

  * take up 14 bytes of message buffer space.

  *

+ * @param pxSendCompletedCallback Callback invoked when a send operation to the

+ * message buffer is complete. If the parameter is NULL or xMessageBufferCreate()

+ * is called without the parameter, then it will use the default implementation

+ * provided by sbSEND_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

+ * @param pxReceiveCompletedCallback Callback invoked when a receive operation from

+ * the message buffer is complete. If the parameter is NULL or xMessageBufferCreate()

+ * is called without the parameter, it will use the default implementation provided

+ * by sbRECEIVE_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

  * @return If NULL is returned, then the message buffer cannot be created

  * because there is insufficient heap memory available for FreeRTOS to allocate

  * the message buffer data structures and storage area.  A non-NULL value being

@@ -143,7 +155,12 @@
  * \ingroup MessageBufferManagement

  */

 #define xMessageBufferCreate( xBufferSizeBytes ) \

-    ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE )

+    ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE, NULL, NULL )

+

+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )

+    #define xMessageBufferCreateWithCallback( xBufferSizeBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \

+    ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE, pxSendCompletedCallback, pxReceiveCompletedCallback )

+#endif

 

 /**

  * message_buffer.h

@@ -172,6 +189,16 @@
  * StaticMessageBuffer_t, which will be used to hold the message buffer's data

  * structure.

  *

+ * @param pxSendCompletedCallback Callback invoked when a new message is sent to the message buffer.

+ * If the parameter is NULL or xMessageBufferCreate() is called without the parameter, then it will use the default

+ * implementation provided by sbSEND_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

+ * @param pxReceiveCompletedCallback Callback invoked when a message is read from a

+ * message buffer. If the parameter is NULL or xMessageBufferCreate() is called without the parameter, it will

+ * use the default implementation provided by sbRECEIVE_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

  * @return If the message buffer is created successfully then a handle to the

  * created message buffer is returned. If either pucMessageBufferStorageArea or

  * pxStaticmessageBuffer are NULL then NULL is returned.

@@ -210,7 +237,12 @@
  * \ingroup MessageBufferManagement

  */

 #define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) \

-    ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer )

+    ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer, NULL, NULL )

+

+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )

+    #define xMessageBufferCreateStaticWithCallback( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \

+    ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback )

+#endif

 

 /**

  * message_buffer.h

diff --git a/include/mpu_prototypes.h b/include/mpu_prototypes.h
index 2c7b0e3..1893db8 100644
--- a/include/mpu_prototypes.h
+++ b/include/mpu_prototypes.h
@@ -248,12 +248,16 @@
                                              size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL;

 StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,

                                                      size_t xTriggerLevelBytes,

-                                                     BaseType_t xIsMessageBuffer ) FREERTOS_SYSTEM_CALL;

+                                                     BaseType_t xIsMessageBuffer,

+                                                     StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                     StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;

 StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,

                                                            size_t xTriggerLevelBytes,

                                                            BaseType_t xIsMessageBuffer,

                                                            uint8_t * const pucStreamBufferStorageArea,

-                                                           StaticStreamBuffer_t * const pxStaticStreamBuffer ) FREERTOS_SYSTEM_CALL;

+                                                           StaticStreamBuffer_t * const pxStaticStreamBuffer,

+                                                           StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                           StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;

 

 

 

diff --git a/include/stream_buffer.h b/include/stream_buffer.h
index a00fd7c..a3a263d 100644
--- a/include/stream_buffer.h
+++ b/include/stream_buffer.h
@@ -71,6 +71,12 @@
 struct StreamBufferDef_t;

 typedef struct StreamBufferDef_t * StreamBufferHandle_t;

 

+/**

+ *  Type used as a stream buffer's optional callback.

+ */

+typedef void (* StreamBufferCallbackFunction_t)( StreamBufferHandle_t xStreamBuffer,

+                                                 BaseType_t xIsInsideISR,

+                                                 BaseType_t * const pxHigherPriorityTaskWoken );

 

 /**

  * stream_buffer.h

@@ -103,6 +109,16 @@
  * trigger level of 1 being used.  It is not valid to specify a trigger level

  * that is greater than the buffer size.

  *

+ * @param pxSendCompletedCallback Callback invoked when number of bytes at least equal to

+ * trigger level is sent to the stream buffer. If the parameter is NULL, it will use the default

+ * implementation provided by sbSEND_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

+ * @param pxReceiveCompletedCallback Callback invoked when more than zero bytes are read from a

+ * stream buffer. If the parameter is NULL, it will use the default

+ * implementation provided by sbRECEIVE_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

  * @return If NULL is returned, then the stream buffer cannot be created

  * because there is insufficient heap memory available for FreeRTOS to allocate

  * the stream buffer data structures and storage area.  A non-NULL value being

@@ -137,7 +153,14 @@
  * \defgroup xStreamBufferCreate xStreamBufferCreate

  * \ingroup StreamBufferManagement

  */

-#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes )    xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE )

+

+#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) \

+    xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, NULL, NULL )

+

+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )

+    #define xStreamBufferCreateWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \

+    xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pxSendCompletedCallback, pxReceiveCompletedCallback )

+#endif

 

 /**

  * stream_buffer.h

@@ -179,6 +202,16 @@
  * StaticStreamBuffer_t, which will be used to hold the stream buffer's data

  * structure.

  *

+ * @param pxSendCompletedCallback Callback invoked when number of bytes at least equal to

+ * trigger level is sent to the stream buffer. If the parameter is NULL, it will use the default

+ * implementation provided by sbSEND_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

+ * @param pxReceiveCompletedCallback Callback invoked when more than zero bytes are read from a

+ * stream buffer. If the parameter is NULL, it will use the default

+ * implementation provided by sbRECEIVE_COMPLETED macro. To enable the callback,

+ * configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.

+ *

  * @return If the stream buffer is created successfully then a handle to the

  * created stream buffer is returned. If either pucStreamBufferStorageArea or

  * pxStaticstreamBuffer are NULL then NULL is returned.

@@ -218,8 +251,14 @@
  * \defgroup xStreamBufferCreateStatic xStreamBufferCreateStatic

  * \ingroup StreamBufferManagement

  */

+

 #define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) \

-    xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer )

+    xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer, NULL, NULL )

+

+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )

+    #define xStreamBufferCreateStaticWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \

+    xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback )

+#endif

 

 /**

  * stream_buffer.h

@@ -843,13 +882,18 @@
 /* Functions below here are not part of the public API. */

 StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes,

                                                  size_t xTriggerLevelBytes,

-                                                 BaseType_t xIsMessageBuffer ) PRIVILEGED_FUNCTION;

+                                                 BaseType_t xIsMessageBuffer,

+                                                 StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                 StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;

+

 

 StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,

                                                        size_t xTriggerLevelBytes,

                                                        BaseType_t xIsMessageBuffer,

                                                        uint8_t * const pucStreamBufferStorageArea,

-                                                       StaticStreamBuffer_t * const pxStaticStreamBuffer ) PRIVILEGED_FUNCTION;

+                                                       StaticStreamBuffer_t * const pxStaticStreamBuffer,

+                                                       StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                       StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;

 

 size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;

 

diff --git a/portable/Common/mpu_wrappers.c b/portable/Common/mpu_wrappers.c
index c9f23f1..74990c3 100644
--- a/portable/Common/mpu_wrappers.c
+++ b/portable/Common/mpu_wrappers.c
@@ -1429,14 +1429,36 @@
     #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

         StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,

                                                              size_t xTriggerLevelBytes,

-                                                             BaseType_t xIsMessageBuffer ) /* FREERTOS_SYSTEM_CALL */

+                                                             BaseType_t xIsMessageBuffer,

+                                                             StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                             StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) /* FREERTOS_SYSTEM_CALL */

         {

             StreamBufferHandle_t xReturn;

             BaseType_t xRunningPrivileged;

 

-            xPortRaisePrivilege( xRunningPrivileged );

-            xReturn = xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer );

-            vPortResetPrivilege( xRunningPrivileged );

+            /**

+             * Streambuffer application level callback functionality is disabled for MPU

+             * enabled ports.

+             */

+            configASSERT( ( pxSendCompletedCallback == NULL ) &&

+                          ( pxReceiveCompletedCallback == NULL ) );

+

+            if( ( pxSendCompletedCallback == NULL ) &&

+                ( pxReceiveCompletedCallback == NULL ) )

+            {

+                xPortRaisePrivilege( xRunningPrivileged );

+                xReturn = xStreamBufferGenericCreate( xBufferSizeBytes,

+                                                      xTriggerLevelBytes,

+                                                      xIsMessageBuffer,

+                                                      NULL,

+                                                      NULL );

+                vPortResetPrivilege( xRunningPrivileged );

+            }

+            else

+            {

+                traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer );

+                xReturn = NULL;

+            }

 

             return xReturn;

         }

@@ -1448,14 +1470,38 @@
                                                                    size_t xTriggerLevelBytes,

                                                                    BaseType_t xIsMessageBuffer,

                                                                    uint8_t * const pucStreamBufferStorageArea,

-                                                                   StaticStreamBuffer_t * const pxStaticStreamBuffer ) /* FREERTOS_SYSTEM_CALL */

+                                                                   StaticStreamBuffer_t * const pxStaticStreamBuffer,

+                                                                   StreamBufferCallbackFunction_t pxSendCompletedCallback,

+                                                                   StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) /* FREERTOS_SYSTEM_CALL */

         {

             StreamBufferHandle_t xReturn;

             BaseType_t xRunningPrivileged;

 

-            xPortRaisePrivilege( xRunningPrivileged );

-            xReturn = xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, pucStreamBufferStorageArea, pxStaticStreamBuffer );

-            vPortResetPrivilege( xRunningPrivileged );

+            /**

+             * Streambuffer application level callback functionality is disabled for MPU

+             * enabled ports.

+             */

+            configASSERT( ( pxSendCompletedCallback == NULL ) &&

+                          ( pxReceiveCompletedCallback == NULL ) );

+

+            if( ( pxSendCompletedCallback == NULL ) &&

+                ( pxReceiveCompletedCallback == NULL ) )

+            {

+                xPortRaisePrivilege( xRunningPrivileged );

+                xReturn = xStreamBufferGenericCreateStatic( xBufferSizeBytes,

+                                                            xTriggerLevelBytes,

+                                                            xIsMessageBuffer,

+                                                            pucStreamBufferStorageArea,

+                                                            pxStaticStreamBuffer,

+                                                            NULL,

+                                                            NULL );

+                vPortResetPrivilege( xRunningPrivileged );

+            }

+            else

+            {

+                traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer );

+                xReturn = NULL;

+            }

 

             return xReturn;

         }

diff --git a/stream_buffer.c b/stream_buffer.c
index fe6e77d..b9d598e 100644
--- a/stream_buffer.c
+++ b/stream_buffer.c
@@ -69,6 +69,25 @@
     ( void ) xTaskResumeAll();
 #endif /* sbRECEIVE_COMPLETED */
 
+/* If user has provided a per-instance receive complete callback, then
+ * invoke the callback else use the receive complete macro which is provided by default for all instances.
+ */
+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+    #define prvRECEIVE_COMPLETED( pxStreamBuffer )                                       \
+    {                                                                                    \
+        if( pxStreamBuffer->pxReceiveCompletedCallback != NULL )                         \
+        {                                                                                \
+            pxStreamBuffer->pxReceiveCompletedCallback( pxStreamBuffer, pdFALSE, NULL ); \
+        }                                                                                \
+        else                                                                             \
+        {                                                                                \
+            sbRECEIVE_COMPLETED( pxStreamBuffer );                                       \
+        }                                                                                \
+    }
+#else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+    #define prvRECEIVE_COMPLETED( pxStreamBuffer )    sbRECEIVE_COMPLETED( pxStreamBuffer )
+#endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+
 #ifndef sbRECEIVE_COMPLETED_FROM_ISR
     #define sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer,                            \
                                           pxHigherPriorityTaskWoken )                \
@@ -90,9 +109,28 @@
     }
 #endif /* sbRECEIVE_COMPLETED_FROM_ISR */
 
+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+    #define prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer,                                                   \
+                                           pxHigherPriorityTaskWoken )                                       \
+    {                                                                                                        \
+        if( pxStreamBuffer->pxReceiveCompletedCallback != NULL )                                             \
+        {                                                                                                    \
+            pxStreamBuffer->pxReceiveCompletedCallback( pxStreamBuffer, pdTRUE, pxHigherPriorityTaskWoken ); \
+        }                                                                                                    \
+        else                                                                                                 \
+        {                                                                                                    \
+            sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );                       \
+        }                                                                                                    \
+    }
+#else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+    #define prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \
+    sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken )
+#endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+
 /* If the user has not provided an application specific Tx notification macro,
- * or #defined the notification macro away, them provide a default implementation
- * that uses task notifications. */
+ * or #defined the notification macro away, then provide a default
+ * implementation that uses task notifications.
+ */
 #ifndef sbSEND_COMPLETED
     #define sbSEND_COMPLETED( pxStreamBuffer )                               \
     vTaskSuspendAll();                                                       \
@@ -108,6 +146,26 @@
     ( void ) xTaskResumeAll();
 #endif /* sbSEND_COMPLETED */
 
+/* If user has provided a per-instance send completed callback, then
+ * invoke the callback else use the send complete macro which is provided by default for all instances.
+ */
+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+    #define prvSEND_COMPLETED( pxStreamBuffer )                                       \
+    {                                                                                 \
+        if( pxStreamBuffer->pxSendCompletedCallback != NULL )                         \
+        {                                                                             \
+            pxStreamBuffer->pxSendCompletedCallback( pxStreamBuffer, pdFALSE, NULL ); \
+        }                                                                             \
+        else                                                                          \
+        {                                                                             \
+            sbSEND_COMPLETED( pxStreamBuffer );                                       \
+        }                                                                             \
+    }
+#else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+    #define prvSEND_COMPLETED( pxStreamBuffer )    sbSEND_COMPLETED( pxStreamBuffer )
+#endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+
+
 #ifndef sbSEND_COMPLETE_FROM_ISR
     #define sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken )       \
     {                                                                                   \
@@ -127,6 +185,25 @@
         portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );                    \
     }
 #endif /* sbSEND_COMPLETE_FROM_ISR */
+
+
+#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+    #define prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken )                        \
+    {                                                                                                     \
+        if( pxStreamBuffer->pxSendCompletedCallback != NULL )                                             \
+        {                                                                                                 \
+            pxStreamBuffer->pxSendCompletedCallback( pxStreamBuffer, pdTRUE, pxHigherPriorityTaskWoken ); \
+        }                                                                                                 \
+        else                                                                                              \
+        {                                                                                                 \
+            sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );                        \
+        }                                                                                                 \
+    }
+#else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+    #define prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \
+    sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken )
+#endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */
+
 /*lint -restore (9026) */
 
 /* The number of bytes used to hold the length of a message in the buffer. */
@@ -153,6 +230,11 @@
     #if ( configUSE_TRACE_FACILITY == 1 )
         UBaseType_t uxStreamBufferNumber; /* Used for tracing purposes. */
     #endif
+
+    #if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+        StreamBufferCallbackFunction_t pxSendCompletedCallback;    /* Optional callback called on send complete. sbSEND_COMPLETED is called if this is NULL. */
+        StreamBufferCallbackFunction_t pxReceiveCompletedCallback; /* Optional callback called on receive complete.  sbRECEIVE_COMPLETED is called if this is NULL. */
+    #endif
 } StreamBuffer_t;
 
 /*
@@ -226,15 +308,17 @@
                                           uint8_t * const pucBuffer,
                                           size_t xBufferSizeBytes,
                                           size_t xTriggerLevelBytes,
-                                          uint8_t ucFlags ) PRIVILEGED_FUNCTION;
+                                          uint8_t ucFlags,
+                                          StreamBufferCallbackFunction_t pxSendCompletedCallback,
+                                          StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
 
 /*-----------------------------------------------------------*/
-
 #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
-
     StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes,
                                                      size_t xTriggerLevelBytes,
-                                                     BaseType_t xIsMessageBuffer )
+                                                     BaseType_t xIsMessageBuffer,
+                                                     StreamBufferCallbackFunction_t pxSendCompletedCallback,
+                                                     StreamBufferCallbackFunction_t pxReceiveCompletedCallback )
     {
         uint8_t * pucAllocatedMemory;
         uint8_t ucFlags;
@@ -289,7 +373,9 @@
                                           pucAllocatedMemory + sizeof( StreamBuffer_t ), /* Storage area follows. */ /*lint !e9016 Indexing past structure valid for uint8_t pointer, also storage area has no alignment requirement. */
                                           xBufferSizeBytes,
                                           xTriggerLevelBytes,
-                                          ucFlags );
+                                          ucFlags,
+                                          pxSendCompletedCallback,
+                                          pxReceiveCompletedCallback );
 
             traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pucAllocatedMemory ), xIsMessageBuffer );
         }
@@ -300,7 +386,6 @@
 
         return ( StreamBufferHandle_t ) pucAllocatedMemory; /*lint !e9087 !e826 Safe cast as allocated memory is aligned. */
     }
-
 #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
 /*-----------------------------------------------------------*/
 
@@ -310,7 +395,9 @@
                                                            size_t xTriggerLevelBytes,
                                                            BaseType_t xIsMessageBuffer,
                                                            uint8_t * const pucStreamBufferStorageArea,
-                                                           StaticStreamBuffer_t * const pxStaticStreamBuffer )
+                                                           StaticStreamBuffer_t * const pxStaticStreamBuffer,
+                                                           StreamBufferCallbackFunction_t pxSendCompletedCallback,
+                                                           StreamBufferCallbackFunction_t pxReceiveCompletedCallback )
     {
         StreamBuffer_t * const pxStreamBuffer = ( StreamBuffer_t * ) pxStaticStreamBuffer; /*lint !e740 !e9087 Safe cast as StaticStreamBuffer_t is opaque Streambuffer_t. */
         StreamBufferHandle_t xReturn;
@@ -360,7 +447,9 @@
                                           pucStreamBufferStorageArea,
                                           xBufferSizeBytes,
                                           xTriggerLevelBytes,
-                                          ucFlags );
+                                          ucFlags,
+                                          pxSendCompletedCallback,
+                                          pxReceiveCompletedCallback );
 
             /* Remember this was statically allocated in case it is ever deleted
              * again. */
@@ -378,7 +467,6 @@
 
         return xReturn;
     }
-
 #endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
 /*-----------------------------------------------------------*/
 
@@ -419,6 +507,7 @@
 {
     StreamBuffer_t * const pxStreamBuffer = xStreamBuffer;
     BaseType_t xReturn = pdFAIL;
+    StreamBufferCallbackFunction_t pxSendCallback = NULL, pxReceiveCallback = NULL;
 
     #if ( configUSE_TRACE_FACILITY == 1 )
         UBaseType_t uxStreamBufferNumber;
@@ -437,25 +526,32 @@
     /* Can only reset a message buffer if there are no tasks blocked on it. */
     taskENTER_CRITICAL();
     {
-        if( pxStreamBuffer->xTaskWaitingToReceive == NULL )
+        if( ( pxStreamBuffer->xTaskWaitingToReceive == NULL ) && ( pxStreamBuffer->xTaskWaitingToSend == NULL ) )
         {
-            if( pxStreamBuffer->xTaskWaitingToSend == NULL )
+            #if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
             {
-                prvInitialiseNewStreamBuffer( pxStreamBuffer,
-                                              pxStreamBuffer->pucBuffer,
-                                              pxStreamBuffer->xLength,
-                                              pxStreamBuffer->xTriggerLevelBytes,
-                                              pxStreamBuffer->ucFlags );
-                xReturn = pdPASS;
-
-                #if ( configUSE_TRACE_FACILITY == 1 )
-                {
-                    pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber;
-                }
-                #endif
-
-                traceSTREAM_BUFFER_RESET( xStreamBuffer );
+                pxSendCallback = pxStreamBuffer->pxSendCompletedCallback;
+                pxReceiveCallback = pxStreamBuffer->pxReceiveCompletedCallback;
             }
+            #endif
+
+            prvInitialiseNewStreamBuffer( pxStreamBuffer,
+                                          pxStreamBuffer->pucBuffer,
+                                          pxStreamBuffer->xLength,
+                                          pxStreamBuffer->xTriggerLevelBytes,
+                                          pxStreamBuffer->ucFlags,
+                                          pxSendCallback,
+                                          pxReceiveCallback );
+
+            #if ( configUSE_TRACE_FACILITY == 1 )
+            {
+                pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber;
+            }
+            #endif
+
+            traceSTREAM_BUFFER_RESET( xStreamBuffer );
+
+            xReturn = pdPASS;
         }
     }
     taskEXIT_CRITICAL();
@@ -653,7 +749,7 @@
         /* Was a task waiting for the data? */
         if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes )
         {
-            sbSEND_COMPLETED( pxStreamBuffer );
+            prvSEND_COMPLETED( pxStreamBuffer );
         }
         else
         {
@@ -703,7 +799,7 @@
         /* Was a task waiting for the data? */
         if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes )
         {
-            sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );
+            prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );
         }
         else
         {
@@ -858,7 +954,7 @@
         if( xReceivedLength != ( size_t ) 0 )
         {
             traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength );
-            sbRECEIVE_COMPLETED( pxStreamBuffer );
+            prvRECEIVE_COMPLETED( xStreamBuffer );
         }
         else
         {
@@ -954,7 +1050,7 @@
         /* Was a task waiting for space in the buffer? */
         if( xReceivedLength != ( size_t ) 0 )
         {
-            sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );
+            prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken );
         }
         else
         {
@@ -1260,7 +1356,9 @@
                                           uint8_t * const pucBuffer,
                                           size_t xBufferSizeBytes,
                                           size_t xTriggerLevelBytes,
-                                          uint8_t ucFlags )
+                                          uint8_t ucFlags,
+                                          StreamBufferCallbackFunction_t pxSendCompletedCallback,
+                                          StreamBufferCallbackFunction_t pxReceiveCompletedCallback )
 {
     /* Assert here is deliberately writing to the entire buffer to ensure it can
      * be written to without generating exceptions, and is setting the buffer to a
@@ -1280,6 +1378,17 @@
     pxStreamBuffer->xLength = xBufferSizeBytes;
     pxStreamBuffer->xTriggerLevelBytes = xTriggerLevelBytes;
     pxStreamBuffer->ucFlags = ucFlags;
+    #if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
+    {
+        pxStreamBuffer->pxSendCompletedCallback = pxSendCompletedCallback;
+        pxStreamBuffer->pxReceiveCompletedCallback = pxReceiveCompletedCallback;
+    }
+    #else
+    {
+        ( void ) pxSendCompletedCallback;
+        ( void ) pxReceiveCompletedCallback;
+    }
+    #endif
 }
 
 #if ( configUSE_TRACE_FACILITY == 1 )