1062 lines
44 KiB
C
1062 lines
44 KiB
C
|
/*
|
||
|
* FreeRTOS Kernel V10.2.1
|
||
|
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||
|
* this software and associated documentation files (the "Software"), to deal in
|
||
|
* the Software without restriction, including without limitation the rights to
|
||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||
|
* subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
* http://www.FreeRTOS.org
|
||
|
* http://aws.amazon.com/freertos
|
||
|
*
|
||
|
* 1 tab == 4 spaces!
|
||
|
*/
|
||
|
|
||
|
/* Standard includes. */
|
||
|
#include "stdio.h"
|
||
|
#include "string.h"
|
||
|
|
||
|
/* FreeRTOS includes. */
|
||
|
#include "FreeRTOS.h"
|
||
|
#include "task.h"
|
||
|
#include "stream_buffer.h"
|
||
|
|
||
|
/* Demo app includes. */
|
||
|
#include "StreamBufferDemo.h"
|
||
|
|
||
|
/* The number of bytes of storage in the stream buffers used in this test. */
|
||
|
#define sbSTREAM_BUFFER_LENGTH_BYTES ( ( size_t ) 30 )
|
||
|
|
||
|
/* Stream buffer length one. */
|
||
|
#define sbSTREAM_BUFFER_LENGTH_ONE ( ( size_t ) 1 )
|
||
|
|
||
|
/* Start and end ASCII characters used in data sent to the buffers. */
|
||
|
#define sbASCII_SPACE 32
|
||
|
#define sbASCII_TILDA 126
|
||
|
|
||
|
/* Defines the number of tasks to create in this test and demo. */
|
||
|
#define sbNUMBER_OF_ECHO_CLIENTS ( 2 )
|
||
|
#define sbNUMBER_OF_SENDER_TASKS ( 2 )
|
||
|
|
||
|
/* Priority of the test tasks. The send and receive go from low to high
|
||
|
priority tasks, and from high to low priority tasks. */
|
||
|
#define sbLOWER_PRIORITY ( tskIDLE_PRIORITY )
|
||
|
#define sbHIGHER_PRIORITY ( tskIDLE_PRIORITY + 1 )
|
||
|
|
||
|
/* Block times used when sending and receiving from the stream buffers. */
|
||
|
#define sbRX_TX_BLOCK_TIME pdMS_TO_TICKS( 125UL )
|
||
|
|
||
|
/* A block time of 0 means "don't block". */
|
||
|
#define sbDONT_BLOCK ( 0 )
|
||
|
|
||
|
/* The trigger level sets the number of bytes that must be present in the
|
||
|
stream buffer before a task that is blocked on the stream buffer is moved out of
|
||
|
the Blocked state so it can read the bytes. */
|
||
|
#define sbTRIGGER_LEVEL_1 ( 1 )
|
||
|
|
||
|
/* The size of the stack allocated to the tasks that run as part of this demo/
|
||
|
test. The stack size is over generous in most cases. */
|
||
|
#define sbSTACK_SIZE ( configMINIMAL_STACK_SIZE + ( configMINIMAL_STACK_SIZE >> 1 ) )
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/*
|
||
|
* Performs various tests that do not require multiple tasks to interact.
|
||
|
*/
|
||
|
static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer );
|
||
|
|
||
|
/*
|
||
|
* Tests sending and receiving various lengths of data via a stream buffer.
|
||
|
* The echo client sends the data to the echo server, which then sends the
|
||
|
* data back to the echo client, which checks it receives exactly what it
|
||
|
* sent.
|
||
|
*/
|
||
|
static void prvEchoClient( void *pvParameters );
|
||
|
static void prvEchoServer( void *pvParameters );
|
||
|
|
||
|
/*
|
||
|
* Tasks that send and receive to a stream buffer at a low priority and without
|
||
|
* blocking, so the send and receive functions interleave in time as the tasks
|
||
|
* are switched in and out.
|
||
|
*/
|
||
|
static void prvNonBlockingReceiverTask( void *pvParameters );
|
||
|
static void prvNonBlockingSenderTask( void *pvParameters );
|
||
|
|
||
|
/* Performs an assert() like check in a way that won't get removed when
|
||
|
performing a code coverage analysis. */
|
||
|
static void prvCheckExpectedState( BaseType_t xState );
|
||
|
|
||
|
/*
|
||
|
* A task that creates a stream buffer with a specific trigger level, then
|
||
|
* receives a string from an interrupt (the RTOS tick hook) byte by byte to
|
||
|
* check it is only unblocked when the specified trigger level is reached.
|
||
|
*/
|
||
|
static void prvInterruptTriggerLevelTest( void *pvParameters );
|
||
|
|
||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||
|
/* This file tests both statically and dynamically allocated stream buffers.
|
||
|
Allocate the structures and buffers to be used by the statically allocated
|
||
|
objects, which get used in the echo tests. */
|
||
|
static void prvReceiverTask( void *pvParameters );
|
||
|
static void prvSenderTask( void *pvParameters );
|
||
|
|
||
|
static StaticStreamBuffer_t xStaticStreamBuffers[ sbNUMBER_OF_ECHO_CLIENTS ];
|
||
|
static uint8_t ucBufferStorage[ sbNUMBER_OF_SENDER_TASKS ][ sbSTREAM_BUFFER_LENGTH_BYTES + 1 ];
|
||
|
static uint32_t ulSenderLoopCounters[ sbNUMBER_OF_SENDER_TASKS ] = { 0 };
|
||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/* The buffers used by the echo client and server tasks. */
|
||
|
typedef struct ECHO_STREAM_BUFFERS
|
||
|
{
|
||
|
/* Handles to the data structures that describe the stream buffers. */
|
||
|
StreamBufferHandle_t xEchoClientBuffer;
|
||
|
StreamBufferHandle_t xEchoServerBuffer;
|
||
|
} EchoStreamBuffers_t;
|
||
|
static volatile uint32_t ulEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
|
||
|
|
||
|
/* The non-blocking tasks monitor their operation, and if no errors have been
|
||
|
found, increment ulNonBlockingRxCounter. xAreStreamBufferTasksStillRunning()
|
||
|
then checks ulNonBlockingRxCounter and only returns pdPASS if
|
||
|
ulNonBlockingRxCounter is still incrementing. */
|
||
|
static volatile uint32_t ulNonBlockingRxCounter = 0;
|
||
|
|
||
|
/* The task that receives characters from the tick interrupt in order to test
|
||
|
different trigger levels monitors its own behaviour. If it has not detected any
|
||
|
error then it increments ulInterruptTriggerCounter to indicate to the check task
|
||
|
that it is still operating correctly. */
|
||
|
static volatile uint32_t ulInterruptTriggerCounter = 0UL;
|
||
|
|
||
|
/* The stream buffer used from the tick interrupt. This sends one byte at a time
|
||
|
to a test task to test the trigger level operation. The variable is set to NULL
|
||
|
in between test runs. */
|
||
|
static volatile StreamBufferHandle_t xInterruptStreamBuffer = NULL;
|
||
|
|
||
|
/* The data sent from the tick interrupt to the task that tests the trigger
|
||
|
level functionality. */
|
||
|
static const char *pcDataSentFromInterrupt = "12345678";
|
||
|
|
||
|
/* Data that is longer than the buffer that is sent to the buffers as a stream
|
||
|
of bytes. Parts of which are written to the stream buffer to test writing
|
||
|
different lengths at different offsets, to many bytes, part streams, streams
|
||
|
that wrap, etc.. Two messages are defined to ensure left over data is not
|
||
|
accidentally read out of the buffer. */
|
||
|
static const char *pc55ByteString = "One two three four five six seven eight nine ten eleven";
|
||
|
static const char *pc54ByteString = "01234567891abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ";
|
||
|
|
||
|
/* Used to log the status of the tests contained within this file for reporting
|
||
|
to a monitoring task ('check' task). */
|
||
|
static BaseType_t xErrorStatus = pdPASS;
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
void vStartStreamBufferTasks( void )
|
||
|
{
|
||
|
StreamBufferHandle_t xStreamBuffer;
|
||
|
|
||
|
/* The echo servers sets up the stream buffers before creating the echo
|
||
|
client tasks. One set of tasks has the server as the higher priority, and
|
||
|
the other has the client as the higher priority. */
|
||
|
xTaskCreate( prvEchoServer, "1StrEchoServer", sbSTACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
|
||
|
xTaskCreate( prvEchoServer, "2StrEchoServer", sbSTACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );
|
||
|
|
||
|
/* The non blocking tasks run continuously and will interleave with each
|
||
|
other, so must be created at the lowest priority. The stream buffer they
|
||
|
use is created and passed in using the task's parameter. */
|
||
|
xStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
|
||
|
xTaskCreate( prvNonBlockingReceiverTask, "StrNonBlkRx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );
|
||
|
xTaskCreate( prvNonBlockingSenderTask, "StrNonBlkTx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );
|
||
|
|
||
|
/* The task that receives bytes from an interrupt to test that it unblocks
|
||
|
at a specific trigger level must run at a high priority to minimise the risk
|
||
|
of it receiving more characters before it can execute again after being
|
||
|
unblocked. */
|
||
|
xTaskCreate( prvInterruptTriggerLevelTest, "StrTrig", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL );
|
||
|
|
||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||
|
{
|
||
|
/* The sender tasks set up the stream buffers before creating the
|
||
|
receiver tasks. Priorities must be 0 and 1 as the priority is used to
|
||
|
index into the xStaticStreamBuffers and ucBufferStorage arrays. */
|
||
|
xTaskCreate( prvSenderTask, "Str1Sender", sbSTACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
|
||
|
xTaskCreate( prvSenderTask, "Str2Sender", sbSTACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );
|
||
|
}
|
||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvCheckExpectedState( BaseType_t xState )
|
||
|
{
|
||
|
configASSERT( xState );
|
||
|
if( xState == pdFAIL )
|
||
|
{
|
||
|
xErrorStatus = pdFAIL;
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer )
|
||
|
{
|
||
|
size_t xReturned, xItem, xExpectedSpace;
|
||
|
const size_t xMax6ByteMessages = sbSTREAM_BUFFER_LENGTH_BYTES / 6;
|
||
|
const size_t x6ByteLength = 6, x17ByteLength = 17, xFullBufferSize = sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2;
|
||
|
uint8_t *pucFullBuffer, *pucData, *pucReadData;
|
||
|
TickType_t xTimeBeforeCall, xTimeAfterCall;
|
||
|
const TickType_t xBlockTime = pdMS_TO_TICKS( 15 ), xAllowableMargin = pdMS_TO_TICKS( 3 ), xMinimalBlockTime = 2;
|
||
|
UBaseType_t uxOriginalPriority;
|
||
|
|
||
|
/* Remove warning in case configASSERT() is not defined. */
|
||
|
( void ) xAllowableMargin;
|
||
|
|
||
|
/* To minimise stack and heap usage a full size buffer is allocated from the
|
||
|
heap, then buffers which hold smaller amounts of data are overlayed with the
|
||
|
larger buffer - just make sure not to use both at once! */
|
||
|
pucFullBuffer = pvPortMalloc( xFullBufferSize );
|
||
|
configASSERT( pucFullBuffer );
|
||
|
|
||
|
pucData = pucFullBuffer;
|
||
|
pucReadData = pucData + x17ByteLength;
|
||
|
|
||
|
/* Nothing has been added or removed yet, so expect the free space to be
|
||
|
exactly as created. */
|
||
|
xExpectedSpace = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xExpectedSpace == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
|
||
|
|
||
|
|
||
|
/* The buffer is 30 bytes long. 6 5 byte messages should fit before the
|
||
|
buffer is completely full. */
|
||
|
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
|
||
|
{
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
|
||
|
|
||
|
/* Generate recognisable data to write to the buffer. This is just
|
||
|
ascii characters that shows which loop iteration the data was written
|
||
|
in. The 'FromISR' version is used to give it some exercise as a block
|
||
|
time is not used, so the call must be inside a critical section so it
|
||
|
runs with ports that don't support interrupt nesting (and therefore
|
||
|
don't have interrupt safe critical sections). */
|
||
|
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
|
||
|
taskENTER_CRITICAL();
|
||
|
{
|
||
|
xReturned = xStreamBufferSendFromISR( xStreamBuffer, ( void * ) pucData, x6ByteLength, NULL );
|
||
|
}
|
||
|
taskEXIT_CRITICAL();
|
||
|
prvCheckExpectedState( xReturned == x6ByteLength );
|
||
|
|
||
|
/* The space in the buffer will have reduced by the amount of user data
|
||
|
written into the buffer. */
|
||
|
xExpectedSpace -= x6ByteLength;
|
||
|
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == xExpectedSpace );
|
||
|
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
|
||
|
/* +1 as it is zero indexed. */
|
||
|
prvCheckExpectedState( xReturned == ( ( xItem + 1 ) * x6ByteLength ) );
|
||
|
}
|
||
|
|
||
|
/* Now the buffer should be full, and attempting to add anything will should
|
||
|
fail. */
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), sbDONT_BLOCK );
|
||
|
prvCheckExpectedState( xReturned == 0 );
|
||
|
|
||
|
/* Adding with a timeout should also fail after the appropriate time. The
|
||
|
priority is temporarily boosted in this part of the test to keep the
|
||
|
allowable margin to a minimum. */
|
||
|
uxOriginalPriority = uxTaskPriorityGet( NULL );
|
||
|
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
|
||
|
xTimeBeforeCall = xTaskGetTickCount();
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), xBlockTime );
|
||
|
xTimeAfterCall = xTaskGetTickCount();
|
||
|
vTaskPrioritySet( NULL, uxOriginalPriority );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) >= xBlockTime );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) < ( xBlockTime + xAllowableMargin ) );
|
||
|
prvCheckExpectedState( xReturned == 0 );
|
||
|
|
||
|
/* The buffer is now full of data in the form "000000", "111111", etc. Make
|
||
|
sure the data is read out as expected. */
|
||
|
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
|
||
|
{
|
||
|
/* Generate the data that is expected to be read out for this loop
|
||
|
iteration. */
|
||
|
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
|
||
|
|
||
|
/* Read the next 6 bytes out. The 'FromISR' version is used to give it
|
||
|
some exercise as a block time is not used, so a it must be called from
|
||
|
a critical section so this will work on ports that don't support
|
||
|
interrupt nesting (so don't have interrupt safe critical sections). */
|
||
|
taskENTER_CRITICAL();
|
||
|
{
|
||
|
xReturned = xStreamBufferReceiveFromISR( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, NULL );
|
||
|
}
|
||
|
taskEXIT_CRITICAL();
|
||
|
prvCheckExpectedState( xReturned == x6ByteLength );
|
||
|
|
||
|
/* Does the data read out match that expected? */
|
||
|
prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x6ByteLength ) == 0 );
|
||
|
|
||
|
/* The space in the buffer will have increased by the amount of user
|
||
|
data removed from the buffer. */
|
||
|
xExpectedSpace += x6ByteLength;
|
||
|
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == xExpectedSpace );
|
||
|
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == ( sbSTREAM_BUFFER_LENGTH_BYTES - xExpectedSpace ) );
|
||
|
}
|
||
|
|
||
|
/* The buffer should be empty again. */
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
|
||
|
xExpectedSpace = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xExpectedSpace == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
|
||
|
/* Reading with a timeout should also fail after the appropriate time. The
|
||
|
priority is temporarily boosted in this part of the test to keep the
|
||
|
allowable margin to a minimum. */
|
||
|
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
|
||
|
xTimeBeforeCall = xTaskGetTickCount();
|
||
|
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, xBlockTime );
|
||
|
xTimeAfterCall = xTaskGetTickCount();
|
||
|
vTaskPrioritySet( NULL, uxOriginalPriority );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) >= xBlockTime );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) < ( xBlockTime + xAllowableMargin ) );
|
||
|
prvCheckExpectedState( xReturned == 0 );
|
||
|
|
||
|
|
||
|
/* In the next loop 17 bytes are written to then read out on each
|
||
|
iteration. As 30 is not divisible by 17 the data will wrap around. */
|
||
|
xExpectedSpace = sbSTREAM_BUFFER_LENGTH_BYTES - x17ByteLength;
|
||
|
|
||
|
for( xItem = 0; xItem < 100; xItem++ )
|
||
|
{
|
||
|
/* Generate recognisable data to write to the queue. This is just
|
||
|
ascii characters that shows which loop iteration the data was written
|
||
|
in. */
|
||
|
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x17ByteLength );
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, x17ByteLength, sbDONT_BLOCK );
|
||
|
prvCheckExpectedState( xReturned == x17ByteLength );
|
||
|
|
||
|
/* The space in the buffer will have reduced by the amount of user data
|
||
|
written into the buffer. */
|
||
|
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == xExpectedSpace );
|
||
|
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == x17ByteLength );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
|
||
|
|
||
|
/* Read the 17 bytes out again. */
|
||
|
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x17ByteLength, sbDONT_BLOCK );
|
||
|
prvCheckExpectedState( xReturned == x17ByteLength );
|
||
|
|
||
|
/* Does the data read out match that expected? */
|
||
|
prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x17ByteLength ) == 0 );
|
||
|
|
||
|
/* Full buffer space available again. */
|
||
|
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
|
||
|
prvCheckExpectedState( xReturned == 0 );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
|
||
|
}
|
||
|
|
||
|
/* Fill the buffer with one message, check it is full, then read it back
|
||
|
again and check the correct data is received. */
|
||
|
xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
|
||
|
xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
|
||
|
prvCheckExpectedState( memcmp( pc55ByteString, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
|
||
|
|
||
|
/* Fill the buffer one bytes at a time. */
|
||
|
for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
|
||
|
{
|
||
|
/* Block time is only for test coverage, the task should never actually
|
||
|
block here. */
|
||
|
xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xItem ] ), sizeof( char ), sbRX_TX_BLOCK_TIME );
|
||
|
}
|
||
|
|
||
|
/* The buffer should now be full. */
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
|
||
|
|
||
|
/* Read the message out in one go, even though it was written in individual
|
||
|
bytes. Try reading much more data than is actually available to ensure only
|
||
|
the available bytes are returned (otherwise this read will write outside of
|
||
|
the memory allocated anyway!). */
|
||
|
xReturned = xStreamBufferReceive( xStreamBuffer, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, sbRX_TX_BLOCK_TIME );
|
||
|
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
prvCheckExpectedState( memcmp( ( const void * ) pc54ByteString, ( const void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
|
||
|
|
||
|
/* Now do the opposite, write in one go and read out in single bytes. */
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbRX_TX_BLOCK_TIME );
|
||
|
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
|
||
|
prvCheckExpectedState( xStreamBufferBytesAvailable( xStreamBuffer ) == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
prvCheckExpectedState( xStreamBufferSpacesAvailable( xStreamBuffer ) == 0 );
|
||
|
|
||
|
/* Read from the buffer one byte at a time. */
|
||
|
for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
|
||
|
{
|
||
|
/* Block time is only for test coverage, the task should never actually
|
||
|
block here. */
|
||
|
xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sizeof( char ), sbRX_TX_BLOCK_TIME );
|
||
|
prvCheckExpectedState( pc55ByteString[ xItem ] == pucFullBuffer[ 0 ] );
|
||
|
}
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
|
||
|
|
||
|
/* Try writing more bytes than there is space. */
|
||
|
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
|
||
|
xTimeBeforeCall = xTaskGetTickCount();
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
|
||
|
xTimeAfterCall = xTaskGetTickCount();
|
||
|
vTaskPrioritySet( NULL, uxOriginalPriority );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) >= xMinimalBlockTime );
|
||
|
prvCheckExpectedState( ( xTimeAfterCall - xTimeBeforeCall ) < ( xMinimalBlockTime + xAllowableMargin ) );
|
||
|
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
|
||
|
|
||
|
/* No space now though. */
|
||
|
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
|
||
|
prvCheckExpectedState( xReturned == 0 );
|
||
|
|
||
|
/* Ensure data was written as expected even when there was an attempt to
|
||
|
write more than was available. This also tries to read more bytes than are
|
||
|
available. */
|
||
|
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, xFullBufferSize, xMinimalBlockTime );
|
||
|
prvCheckExpectedState( memcmp( ( const void * ) pucFullBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
|
||
|
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
|
||
|
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
|
||
|
|
||
|
/* Clean up with data in the buffer to ensure the tests that follow don't
|
||
|
see the data (the data should be discarded). */
|
||
|
( void ) xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES / ( size_t ) 2, sbDONT_BLOCK );
|
||
|
vPortFree( pucFullBuffer );
|
||
|
xStreamBufferReset( xStreamBuffer );
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvNonBlockingSenderTask( void *pvParameters )
|
||
|
{
|
||
|
StreamBufferHandle_t xStreamBuffer;
|
||
|
size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
|
||
|
const size_t xStringLength = strlen( pc54ByteString );
|
||
|
|
||
|
/* In this case the stream buffer has already been created and is passed
|
||
|
into the task using the task's parameter. */
|
||
|
xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
|
||
|
|
||
|
/* Keep sending the string to the stream buffer as many bytes as possible in
|
||
|
each go. Doesn't block so calls can interleave with the non-blocking
|
||
|
receives performed by prvNonBlockingReceiverTask(). */
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* The whole string cannot be sent at once, so xNextChar is an index to
|
||
|
the position within the string that has been sent so far. How many
|
||
|
bytes are there left to send before the end of the string? */
|
||
|
xBytesToSend = xStringLength - xNextChar;
|
||
|
|
||
|
/* Attempt to send right up to the end of the string. */
|
||
|
xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xNextChar ] ), xBytesToSend, sbDONT_BLOCK );
|
||
|
prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );
|
||
|
|
||
|
/* Move the index up the string to the next character to be sent,
|
||
|
wrapping if the end of the string has been reached. */
|
||
|
xNextChar += xBytesActuallySent;
|
||
|
prvCheckExpectedState( xNextChar <= xStringLength );
|
||
|
|
||
|
if( xNextChar == xStringLength )
|
||
|
{
|
||
|
xNextChar = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvNonBlockingReceiverTask( void *pvParameters )
|
||
|
{
|
||
|
StreamBufferHandle_t xStreamBuffer;
|
||
|
size_t xNextChar = 0, xReceiveLength, xBytesToTest, xStartIndex;
|
||
|
const size_t xStringLength = strlen( pc54ByteString );
|
||
|
char cRxString[ 12 ]; /* Holds received characters. */
|
||
|
BaseType_t xNonBlockingReceiveError = pdFALSE;
|
||
|
|
||
|
/* In this case the stream buffer has already been created and is passed
|
||
|
into the task using the task's parameter. */
|
||
|
xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
|
||
|
|
||
|
/* Expects to receive the pc54ByteString over and over again. Sends and
|
||
|
receives are not blocking so will interleave. */
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* Attempt to receive as many bytes as possible, up to the limit of the
|
||
|
Rx buffer size. */
|
||
|
xReceiveLength = xStreamBufferReceive( xStreamBuffer, ( void * ) cRxString, sizeof( cRxString ), sbDONT_BLOCK );
|
||
|
|
||
|
if( xReceiveLength > 0 )
|
||
|
{
|
||
|
/* xNextChar is the index into pc54ByteString that has been received
|
||
|
already. If xReceiveLength bytes are added to that, will it go off
|
||
|
the end of the string? If so, then first test up to the end of the
|
||
|
string, then go back to the start of pc54ByteString to test the
|
||
|
remains of the received data. */
|
||
|
xBytesToTest = xReceiveLength;
|
||
|
if( ( xNextChar + xBytesToTest ) > xStringLength )
|
||
|
{
|
||
|
/* Cap to test the received data to the end of the string. */
|
||
|
xBytesToTest = xStringLength - xNextChar;
|
||
|
|
||
|
if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) cRxString, xBytesToTest ) != 0 )
|
||
|
{
|
||
|
xNonBlockingReceiveError = pdTRUE;
|
||
|
}
|
||
|
|
||
|
/* Then move back to the start of the string to test the
|
||
|
remaining received bytes. */
|
||
|
xNextChar = 0;
|
||
|
xStartIndex = xBytesToTest;
|
||
|
xBytesToTest = xReceiveLength - xBytesToTest;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* The string didn't wrap in the buffer, so start comparing from
|
||
|
the start of the received data. */
|
||
|
xStartIndex = 0;
|
||
|
}
|
||
|
|
||
|
/* Test the received bytes are as expected, then move the index
|
||
|
along the string to the next expected char to receive. */
|
||
|
if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) &( cRxString[ xStartIndex ] ), xBytesToTest ) != 0 )
|
||
|
{
|
||
|
xNonBlockingReceiveError = pdTRUE;
|
||
|
}
|
||
|
|
||
|
if( xNonBlockingReceiveError == pdFALSE )
|
||
|
{
|
||
|
/* No errors detected so increment the counter that lets the
|
||
|
check task know this test is still functioning correctly. */
|
||
|
ulNonBlockingRxCounter++;
|
||
|
}
|
||
|
|
||
|
xNextChar += xBytesToTest;
|
||
|
if( xNextChar >= xStringLength )
|
||
|
{
|
||
|
xNextChar = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||
|
|
||
|
static void prvSenderTask( void *pvParameters )
|
||
|
{
|
||
|
StreamBufferHandle_t xStreamBuffer, xTempStreamBuffer;
|
||
|
static uint8_t ucTempBuffer[ 10 ]; /* Just used to exercise stream buffer creating and deletion. */
|
||
|
const TickType_t xTicksToWait = sbRX_TX_BLOCK_TIME, xShortDelay = pdMS_TO_TICKS( 50 );
|
||
|
StaticStreamBuffer_t xStaticStreamBuffer;
|
||
|
size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
|
||
|
const size_t xStringLength = strlen( pc55ByteString );
|
||
|
|
||
|
/* The task's priority is used as an index into the loop counters used to
|
||
|
indicate this task is still running. */
|
||
|
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
|
||
|
|
||
|
/* Make sure a change in priority does not inadvertently result in an
|
||
|
invalid array index. */
|
||
|
prvCheckExpectedState( uxIndex < sbNUMBER_OF_ECHO_CLIENTS );
|
||
|
|
||
|
/* Avoid compiler warnings about unused parameters. */
|
||
|
( void ) pvParameters;
|
||
|
|
||
|
xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ) / sbNUMBER_OF_SENDER_TASKS, /* The number of bytes in each buffer in the array. */
|
||
|
sbTRIGGER_LEVEL_1, /* The number of bytes to be in the buffer before a task blocked to wait for data is unblocked. */
|
||
|
&( ucBufferStorage[ uxIndex ][ 0 ] ), /* The address of the buffer to use within the array. */
|
||
|
&( xStaticStreamBuffers[ uxIndex ] ) ); /* The static stream buffer structure to use within the array. */
|
||
|
|
||
|
/* Now the stream buffer has been created the receiver task can be
|
||
|
created. If this sender task has the higher priority then the receiver
|
||
|
task is created at the lower priority - if this sender task has the
|
||
|
lower priority then the receiver task is created at the higher
|
||
|
priority. */
|
||
|
if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
|
||
|
{
|
||
|
/* Here prvSingleTaskTests() performs various tests on a stream buffer
|
||
|
that was created statically. */
|
||
|
prvSingleTaskTests( xStreamBuffer );
|
||
|
xTaskCreate( prvReceiverTask, "StrReceiver", sbSTACK_SIZE, ( void * ) xStreamBuffer, sbHIGHER_PRIORITY, NULL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xTaskCreate( prvReceiverTask, "StrReceiver", sbSTACK_SIZE, ( void * ) xStreamBuffer, sbLOWER_PRIORITY, NULL );
|
||
|
}
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* The whole string cannot be sent at once, so xNextChar is an index
|
||
|
to the position within the string that has been sent so far. How
|
||
|
many bytes are there left to send before the end of the string? */
|
||
|
xBytesToSend = xStringLength - xNextChar;
|
||
|
|
||
|
/* Attempt to send right up to the end of the string. */
|
||
|
xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc55ByteString[ xNextChar ] ), xBytesToSend, xTicksToWait );
|
||
|
prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );
|
||
|
|
||
|
/* Move the index up the string to the next character to be sent,
|
||
|
wrapping if the end of the string has been reached. */
|
||
|
xNextChar += xBytesActuallySent;
|
||
|
prvCheckExpectedState( xNextChar <= xStringLength );
|
||
|
|
||
|
if( xNextChar == xStringLength )
|
||
|
{
|
||
|
xNextChar = 0;
|
||
|
}
|
||
|
|
||
|
/* Increment a loop counter so a check task can tell this task is
|
||
|
still running as expected. */
|
||
|
ulSenderLoopCounters[ uxIndex ]++;
|
||
|
|
||
|
if( uxTaskPriorityGet( NULL ) == sbHIGHER_PRIORITY )
|
||
|
{
|
||
|
/* Allow other tasks to run. */
|
||
|
vTaskDelay( xShortDelay );
|
||
|
}
|
||
|
|
||
|
/* This stream buffer is just created and deleted to ensure no
|
||
|
issues when attempting to delete a stream buffer that was
|
||
|
created using statically allocated memory. To save stack space
|
||
|
the buffer is set to point to the pc55ByteString, which is a const
|
||
|
string, but no data is written into the buffer so any valid address
|
||
|
will do. */
|
||
|
xTempStreamBuffer = xStreamBufferCreateStatic( sizeof( ucTempBuffer ), sbTRIGGER_LEVEL_1, ucTempBuffer, &xStaticStreamBuffer );
|
||
|
xStreamBufferReset( xTempStreamBuffer );
|
||
|
vStreamBufferDelete( xTempStreamBuffer );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||
|
|
||
|
static void prvReceiverTask( void *pvParameters )
|
||
|
{
|
||
|
StreamBufferHandle_t const pxStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
|
||
|
char cRxString[ 12 ]; /* Large enough to hold a 32-bit number in ASCII. */
|
||
|
const TickType_t xTicksToWait = pdMS_TO_TICKS( 5UL );
|
||
|
const size_t xStringLength = strlen( pc55ByteString );
|
||
|
size_t xNextChar = 0, xReceivedLength, xBytesToReceive;
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* Attempt to receive the number of bytes to the end of the string,
|
||
|
or the number of byte that can be placed into the rx buffer,
|
||
|
whichever is smallest. */
|
||
|
xBytesToReceive = configMIN( ( xStringLength - xNextChar ), sizeof( cRxString ) );
|
||
|
|
||
|
do
|
||
|
{
|
||
|
xReceivedLength = xStreamBufferReceive( pxStreamBuffer, ( void * ) cRxString, xBytesToReceive, xTicksToWait );
|
||
|
|
||
|
} while( xReceivedLength == 0 );
|
||
|
|
||
|
/* Ensure the received string matches the expected string. */
|
||
|
prvCheckExpectedState( memcmp( ( void * ) cRxString, ( const void * ) &( pc55ByteString[ xNextChar ] ), xReceivedLength ) == 0 );
|
||
|
|
||
|
/* Move the index into the string up to the end of the bytes
|
||
|
received so far - wrapping if the end of the string has been
|
||
|
reached. */
|
||
|
xNextChar += xReceivedLength;
|
||
|
if( xNextChar >= xStringLength )
|
||
|
{
|
||
|
xNextChar = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvEchoClient( void *pvParameters )
|
||
|
{
|
||
|
size_t xSendLength = 0, ux;
|
||
|
char *pcStringToSend, *pcStringReceived, cNextChar = sbASCII_SPACE;
|
||
|
const TickType_t xTicksToWait = pdMS_TO_TICKS( 50 );
|
||
|
StreamBufferHandle_t xTempStreamBuffer;
|
||
|
|
||
|
/* The task's priority is used as an index into the loop counters used to
|
||
|
indicate this task is still running. */
|
||
|
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
|
||
|
|
||
|
/* Pointers to the client and server stream buffers are passed into this task
|
||
|
using the task's parameter. */
|
||
|
EchoStreamBuffers_t *pxStreamBuffers = ( EchoStreamBuffers_t * ) pvParameters;
|
||
|
|
||
|
/* Prevent compiler warnings. */
|
||
|
( void ) pvParameters;
|
||
|
|
||
|
/* Create the buffer into which strings to send to the server will be
|
||
|
created, and the buffer into which strings echoed back from the server will
|
||
|
be copied. */
|
||
|
pcStringToSend = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
pcStringReceived = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
|
||
|
configASSERT( pcStringToSend );
|
||
|
configASSERT( pcStringReceived );
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* Generate the length of the next string to send. */
|
||
|
xSendLength++;
|
||
|
|
||
|
/* The stream buffer is being used to hold variable length data, so
|
||
|
each data item requires sizeof( size_t ) bytes to hold the data's
|
||
|
length, hence the sizeof() in the if() condition below. */
|
||
|
if( xSendLength > ( sbSTREAM_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) )
|
||
|
{
|
||
|
/* Back to a string length of 1. */
|
||
|
xSendLength = sizeof( char );
|
||
|
}
|
||
|
|
||
|
memset( pcStringToSend, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
|
||
|
for( ux = 0; ux < xSendLength; ux++ )
|
||
|
{
|
||
|
pcStringToSend[ ux ] = cNextChar;
|
||
|
|
||
|
cNextChar++;
|
||
|
|
||
|
if( cNextChar > sbASCII_TILDA )
|
||
|
{
|
||
|
cNextChar = sbASCII_SPACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Send the generated string to the buffer. */
|
||
|
do
|
||
|
{
|
||
|
ux = xStreamBufferSend( pxStreamBuffers->xEchoClientBuffer, ( void * ) pcStringToSend, xSendLength, xTicksToWait );
|
||
|
|
||
|
} while( ux == 0 );
|
||
|
|
||
|
/* Wait for the string to be echoed back. */
|
||
|
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
xStreamBufferReceive( pxStreamBuffers->xEchoServerBuffer, ( void * ) pcStringReceived, xSendLength, portMAX_DELAY );
|
||
|
|
||
|
prvCheckExpectedState( strcmp( pcStringToSend, pcStringReceived ) == 0 );
|
||
|
|
||
|
/* Maintain a count of the number of times this code executes so a
|
||
|
check task can determine if this task is still functioning as
|
||
|
expected or not. As there are two client tasks, and the priorities
|
||
|
used are 0 and 1, the task's priority is used as an index into the
|
||
|
loop count array. */
|
||
|
ulEchoLoopCounters[ uxIndex ]++;
|
||
|
|
||
|
/* This stream buffer is just created and deleted to ensure no memory
|
||
|
leaks. */
|
||
|
xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
|
||
|
prvSingleTaskTests( xTempStreamBuffer );
|
||
|
vStreamBufferDelete( xTempStreamBuffer );
|
||
|
|
||
|
/* The following are tests for a stream buffer of size one. */
|
||
|
/* Create a buffer of size one. */
|
||
|
xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_ONE, sbTRIGGER_LEVEL_1 );
|
||
|
/* Ensure that the buffer was created successfully. */
|
||
|
configASSERT( xTempStreamBuffer );
|
||
|
|
||
|
/* Send one byte to the buffer. */
|
||
|
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
|
||
|
/* Ensure that the byte was sent successfully. */
|
||
|
configASSERT( ux == 1 );
|
||
|
/* Try sending another byte to the buffer. */
|
||
|
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
|
||
|
/* Make sure that send failed as the buffer is full. */
|
||
|
configASSERT( ux == 0 );
|
||
|
|
||
|
/* Receive one byte from the buffer. */
|
||
|
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
|
||
|
/* Ensure that the receive was successful. */
|
||
|
configASSERT( ux == 1 );
|
||
|
/* Ensure that the correct data was received. */
|
||
|
configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );
|
||
|
/* Try receiving another byte from the buffer. */
|
||
|
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
|
||
|
/* Ensure that the receive failed as the buffer is empty. */
|
||
|
configASSERT( ux == 0 );
|
||
|
|
||
|
/* Try sending two bytes to the buffer. Since the size of the
|
||
|
* buffer is one, we must not be able to send more than one. */
|
||
|
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 2, sbDONT_BLOCK );
|
||
|
/* Ensure that only one byte was sent. */
|
||
|
configASSERT( ux == 1 );
|
||
|
|
||
|
/* Try receiving two bytes from the buffer. Since the size of the
|
||
|
* buffer is one, we must not be able to get more than one. */
|
||
|
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 2, sbDONT_BLOCK );
|
||
|
/* Ensure that only one byte was received. */
|
||
|
configASSERT( ux == 1 );
|
||
|
/* Ensure that the correct data was received. */
|
||
|
configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );
|
||
|
|
||
|
/* Delete the buffer. */
|
||
|
vStreamBufferDelete( xTempStreamBuffer );
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvEchoServer( void *pvParameters )
|
||
|
{
|
||
|
size_t xReceivedLength;
|
||
|
char *pcReceivedString;
|
||
|
EchoStreamBuffers_t xStreamBuffers;
|
||
|
TickType_t xTimeOnEntering;
|
||
|
const TickType_t xTicksToBlock = pdMS_TO_TICKS( 350UL );
|
||
|
|
||
|
/* Prevent compiler warnings about unused parameters. */
|
||
|
( void ) pvParameters;
|
||
|
|
||
|
/* Create the stream buffer used to send data from the client to the server,
|
||
|
and the stream buffer used to echo the data from the server back to the
|
||
|
client. */
|
||
|
xStreamBuffers.xEchoClientBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
|
||
|
xStreamBuffers.xEchoServerBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
|
||
|
configASSERT( xStreamBuffers.xEchoClientBuffer );
|
||
|
configASSERT( xStreamBuffers.xEchoServerBuffer );
|
||
|
|
||
|
/* Create the buffer into which received strings will be copied. */
|
||
|
pcReceivedString = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
configASSERT( pcReceivedString );
|
||
|
|
||
|
/* Don't expect to receive anything yet! */
|
||
|
xTimeOnEntering = xTaskGetTickCount();
|
||
|
xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, xTicksToBlock );
|
||
|
prvCheckExpectedState( ( xTaskGetTickCount() - xTimeOnEntering ) >= xTicksToBlock );
|
||
|
prvCheckExpectedState( xReceivedLength == 0 );
|
||
|
|
||
|
/* Now the stream buffers have been created the echo client task can be
|
||
|
created. If this server task has the higher priority then the client task
|
||
|
is created at the lower priority - if this server task has the lower
|
||
|
priority then the client task is created at the higher priority. */
|
||
|
if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
|
||
|
{
|
||
|
xTaskCreate( prvEchoClient, "EchoClient", sbSTACK_SIZE, ( void * ) &xStreamBuffers, sbHIGHER_PRIORITY, NULL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Here prvSingleTaskTests() performs various tests on a stream buffer
|
||
|
that was created dynamically. */
|
||
|
prvSingleTaskTests( xStreamBuffers.xEchoClientBuffer );
|
||
|
xTaskCreate( prvEchoClient, "EchoClient", sbSTACK_SIZE, ( void * ) &xStreamBuffers, sbLOWER_PRIORITY, NULL );
|
||
|
}
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
memset( pcReceivedString, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
|
||
|
|
||
|
/* Has any data been sent by the client? */
|
||
|
xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, portMAX_DELAY );
|
||
|
|
||
|
/* Should always receive data as max delay was used. */
|
||
|
prvCheckExpectedState( xReceivedLength > 0 );
|
||
|
|
||
|
/* Echo the received data back to the client. */
|
||
|
xStreamBufferSend( xStreamBuffers.xEchoServerBuffer, ( void * ) pcReceivedString, xReceivedLength, portMAX_DELAY );
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
void vPeriodicStreamBufferProcessing( void )
|
||
|
{
|
||
|
static size_t xNextChar = 0;
|
||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||
|
|
||
|
/* Called from the tick interrupt hook. If the global stream buffer
|
||
|
variable is not NULL then the prvInterruptTriggerTest() task expects a byte
|
||
|
to be sent to the stream buffer on each tick interrupt. */
|
||
|
if( xInterruptStreamBuffer != NULL )
|
||
|
{
|
||
|
/* One character from the pcDataSentFromInterrupt string is sent on each
|
||
|
interrupt. The task blocked on the stream buffer should not be
|
||
|
unblocked until the defined trigger level is hit. */
|
||
|
xStreamBufferSendFromISR( xInterruptStreamBuffer, ( const void * ) &( pcDataSentFromInterrupt[ xNextChar ] ), sizeof( char ), &xHigherPriorityTaskWoken );
|
||
|
|
||
|
if( xNextChar < strlen( pcDataSentFromInterrupt ) )
|
||
|
{
|
||
|
xNextChar++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Start at the beginning of the string being sent again. */
|
||
|
xNextChar = 0;
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvInterruptTriggerLevelTest( void *pvParameters )
|
||
|
{
|
||
|
StreamBufferHandle_t xStreamBuffer;
|
||
|
size_t xTriggerLevel = 1, xBytesReceived;
|
||
|
const size_t xStreamBufferSizeBytes = ( size_t ) 8, xMaxTriggerLevel = ( size_t ) 6, xMinTriggerLevel = ( size_t ) 1;
|
||
|
const TickType_t xReadBlockTime = 4, xCycleBlockTime = pdMS_TO_TICKS( 100 );
|
||
|
uint8_t ucRxData[ 8 ];
|
||
|
BaseType_t xErrorDetected = pdFALSE;
|
||
|
#ifndef configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN
|
||
|
const size_t xAllowableMargin = ( size_t ) 0;
|
||
|
#else
|
||
|
const size_t xAllowableMargin = ( size_t ) configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN;
|
||
|
#endif
|
||
|
|
||
|
/* Remove compiler warning about unused parameter. */
|
||
|
( void ) pvParameters;
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
for( xTriggerLevel = xMinTriggerLevel; xTriggerLevel < xMaxTriggerLevel; xTriggerLevel++ )
|
||
|
{
|
||
|
/* Create the stream buffer that will be used from inside the tick
|
||
|
interrupt. */
|
||
|
xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
|
||
|
configASSERT( xStreamBuffer );
|
||
|
|
||
|
/* Now the stream buffer has been created it can be assigned to the
|
||
|
file scope variable, which will allow the tick interrupt to start
|
||
|
using it. */
|
||
|
taskENTER_CRITICAL();
|
||
|
{
|
||
|
xInterruptStreamBuffer = xStreamBuffer;
|
||
|
}
|
||
|
taskEXIT_CRITICAL();
|
||
|
|
||
|
xBytesReceived = xStreamBufferReceive( xStreamBuffer, ( void * ) ucRxData, sizeof( ucRxData ), xReadBlockTime );
|
||
|
|
||
|
/* Set the file scope variable back to NULL so the interrupt doesn't
|
||
|
try to use it again. */
|
||
|
taskENTER_CRITICAL();
|
||
|
{
|
||
|
xInterruptStreamBuffer = NULL;
|
||
|
}
|
||
|
taskEXIT_CRITICAL();
|
||
|
|
||
|
/* Now check the number of bytes received equals the trigger level,
|
||
|
except in the case that the read timed out before the trigger level
|
||
|
was reached. */
|
||
|
if( xBytesReceived < xTriggerLevel )
|
||
|
{
|
||
|
/* This should only happen if the trigger level was greater than
|
||
|
the block time. */
|
||
|
if( xTriggerLevel < xReadBlockTime )
|
||
|
{
|
||
|
xErrorDetected = pdTRUE;
|
||
|
}
|
||
|
}
|
||
|
else if( ( xBytesReceived - xTriggerLevel ) > xAllowableMargin )
|
||
|
{
|
||
|
/* A margin may be required here if there are other high priority
|
||
|
tasks prevent the task that reads from the message buffer running
|
||
|
immediately. */
|
||
|
xErrorDetected = pdTRUE;
|
||
|
}
|
||
|
|
||
|
if( xBytesReceived > sizeof( ucRxData ) )
|
||
|
{
|
||
|
xErrorDetected = pdTRUE;
|
||
|
}
|
||
|
else if( memcmp( ( void * ) ucRxData, ( const void * ) pcDataSentFromInterrupt, xBytesReceived ) != 0 )
|
||
|
{
|
||
|
/* Received data didn't match that expected. */
|
||
|
xErrorDetected = pdTRUE;
|
||
|
}
|
||
|
|
||
|
if( xErrorDetected == pdFALSE )
|
||
|
{
|
||
|
/* Increment the cycle counter so the 'check' task knows this test
|
||
|
is still running without error. */
|
||
|
ulInterruptTriggerCounter++;
|
||
|
}
|
||
|
|
||
|
/* Tidy up ready for the next loop. */
|
||
|
vStreamBufferDelete( xStreamBuffer );
|
||
|
vTaskDelay( xCycleBlockTime );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
BaseType_t xAreStreamBufferTasksStillRunning( void )
|
||
|
{
|
||
|
static uint32_t ulLastEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
|
||
|
static uint32_t ulLastNonBlockingRxCounter = 0;
|
||
|
static uint32_t ulLastInterruptTriggerCounter = 0;
|
||
|
BaseType_t x;
|
||
|
|
||
|
for( x = 0; x < sbNUMBER_OF_ECHO_CLIENTS; x++ )
|
||
|
{
|
||
|
if( ulLastEchoLoopCounters[ x ] == ulEchoLoopCounters[ x ] )
|
||
|
{
|
||
|
xErrorStatus = pdFAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulLastEchoLoopCounters[ x ] = ulEchoLoopCounters[ x ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( ulNonBlockingRxCounter == ulLastNonBlockingRxCounter )
|
||
|
{
|
||
|
xErrorStatus = pdFAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulLastNonBlockingRxCounter = ulNonBlockingRxCounter;
|
||
|
}
|
||
|
|
||
|
if( ulLastInterruptTriggerCounter == ulInterruptTriggerCounter )
|
||
|
{
|
||
|
xErrorStatus = pdFAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulLastInterruptTriggerCounter = ulInterruptTriggerCounter;
|
||
|
}
|
||
|
|
||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||
|
{
|
||
|
static uint32_t ulLastSenderLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
|
||
|
|
||
|
for( x = 0; x < sbNUMBER_OF_SENDER_TASKS; x++ )
|
||
|
{
|
||
|
if( ulLastSenderLoopCounters[ x ] == ulSenderLoopCounters[ x ] )
|
||
|
{
|
||
|
xErrorStatus = pdFAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulLastSenderLoopCounters[ x ] = ulSenderLoopCounters[ x ];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||
|
|
||
|
return xErrorStatus;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|