/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file CommBuffers.c * * @date 08-Oct-2019 * @author S. Nash * * @brief CommBuffers service module. Provides data buffering functionality. \n * Data coming in via CAN/UART Rx interrupts can be placed in a buffer to \n * be processed at a later time. Data is double buffered to prevent \n * contention between Rx/Tx interrupts and the processing thread(s). \n * Data to be transmitted via CAN/UART can be placed in a buffer to be sent \n * when the transmitter becomes available. \n * If a buffer becomes too full to service more data, a s/w fault is triggered. * **************************************************************************/ #include // for memcpy() #include "CommBuffers.h" // ********** private definitions ********** #define COMM_BUFFER_LENGTH 512 // max bytes in each comm buffer (double if you count double buffers) #define DOUBLE_BUFFERS 2 // need 2 buffers for double buffering // ********** private data ********** static volatile U32 commBufferByteCount[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ]; // for each buffer, how many bytes does it contain? (also index to next available) static volatile U32 activeDoubleBuffer[ NUM_OF_COMM_BUFFERS ]; // for each buffer, which double buffer is being fed right now? static U08 commBuffers[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ][ COMM_BUFFER_LENGTH ]; // each is double buffered to avoid thread contention static volatile BOOL bufferGetLock[ NUM_OF_COMM_BUFFERS ]; // prevent getter from accessing active buffer while add in progress // ********** private function prototypes ********** static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ); static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ); /************************************************************************* * @brief initCommBuffers * The initCommBuffers function initializes the CommBuffers module. * @details * Inputs : none * Outputs : CommBuffers module initialized. * @param none * @return none *************************************************************************/ void initCommBuffers( void ) { S32 b,d,i; // reset and zero out all buffers for ( b = 0; b < NUM_OF_COMM_BUFFERS; b++ ) { activeDoubleBuffer[ b ] = 0; for ( d = 0; d < DOUBLE_BUFFERS; d++ ) { commBufferByteCount[ b ][ d ] = 0; for ( i = 0; i < COMM_BUFFER_LENGTH; i++ ) { commBuffers[ b ][ d ][ i ] = 0; } } } } /************************************************************************* * @brief addToCommBuffer * The addToCommBuffer function adds data of specified length to a specified \n * communication buffer. S/W fault if buffer too full to add data. \n * This function will always add to the active double buffer. \n * This function should only be called from the background, general, or \n * priority tasks (BG or IRQ) for thread safety. * @details * Inputs : commBufferByteCount[], activeDoubleBuffer[] * Outputs : commBuffers[], commBufferByteCount[] * @param buffer : which comm buffer to add data to * @param data : pointer to byte array containing data to add * @param len : length of data (in bytes) * @return TRUE if data added to buffer successfully, FALSE if not *************************************************************************/ BOOL addToCommBuffer( COMM_BUFFER_T buffer, U08* data, U32 len ) { BOOL result = FALSE; // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { U32 activeBuffer; U32 currentActiveBufCount; // where to start adding new data to buffer (after existing data) // add requires brief thread protection because there may be multiple sources for transmits trying to add data to a buffer. _disable_IRQ(); bufferGetLock[ buffer ] = TRUE; activeBuffer = activeDoubleBuffer[ buffer ]; currentActiveBufCount = commBufferByteCount[ buffer ][ activeBuffer ]; // check to make sure buffer is not too full to service this add if ( len <= ( COMM_BUFFER_LENGTH - currentActiveBufCount ) ) { U08 *buffPtr; // buffer destination for added data // set destination pointer to end of active buffer data buffPtr = &commBuffers[ buffer ][ activeBuffer ][ currentActiveBufCount ]; // copy source data to destination buffer memcpy( buffPtr, data, len ); // adjust buffer count per this data add (also reserves space to add data before releasing thread protection) commBufferByteCount[ buffer ][ activeBuffer ] += len; // release thread protection bufferGetLock[ buffer ] = FALSE; _enable_IRQ(); // data successfully added to buffer result = TRUE; } else // buffer too full to add this much data { // release thread protection bufferGetLock[ buffer ] = FALSE; _enable_IRQ(); SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_TOO_MUCH_DATA, len ) } } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* * @brief getFromCommBuffer * The getFromCommBuffer function fills a given byte array with a given \n * number of bytes from a given buffer and returns the number of bytes \n * retrieved from the buffer. This function will draw from the inactive \n * double buffer first and, if needed, switch double buffers to draw the \n * rest of the requested data. * Only one function in one thread should be calling this function for a given \n * buffer. * @details * Inputs : commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * Outputs : commBuffers[], commBufferByteCount[], activeDoubleBuffer[], \n * and the given data array is populated with data from the buffer. * @param buffer : which comm buffer to retrieve data from * @param data : pointer to byte array to stuff data into * @param len : # of bytes to retrieve into given data array. * @return the number of bytes retrieved. *************************************************************************/ U32 getFromCommBuffer( COMM_BUFFER_T buffer, U08* data, U32 len ) { U32 result = 0; // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { // verify requested # of bytes to get are in the buffer if ( ( len <= ( COMM_BUFFER_LENGTH * DOUBLE_BUFFERS ) ) && ( len <= numberOfBytesInCommBuffer( buffer ) ) ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; U32 sizeOfFirstConsumption = MIN( len, bytesInInactiveBuffer ); // see what we can get from inactive buffer getDataFromInactiveBuffer( buffer, data, sizeOfFirstConsumption ); // will switch double buffers if we empty inactive buffer // will return # of bytes consumed result = sizeOfFirstConsumption; // do we need more from active buffer? if ( len > bytesInInactiveBuffer ) { U32 remNumOfBytes = len - sizeOfFirstConsumption; U08 *remPtr = data + sizeOfFirstConsumption; getDataFromInactiveBuffer( buffer, remPtr, remNumOfBytes ); // will return # of bytes consumed result += remNumOfBytes; } } } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_GET_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* * @brief peekFromCommBuffer * The peekFromCommBuffer function fills a given byte array with a given \n * number of bytes from a given buffer. This function does NOT consume \n * the bytes - it only peeks at them. A call to numberOfBytesInCommBuffer() \n * should be made before calling this function to determine how many bytes \n * are currently in the buffer. Do not call this function with a "len" \n * longer than what is currently in the buffer. * @details * Inputs : commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * Outputs : given array populated with requested # of bytes from the buffer. * @param buffer : which comm buffer to retrieve data from * @param data : pointer to byte array to stuff data into * @param len : # of bytes to retrieve into given data array. * @return the number of bytes retrieved. *************************************************************************/ U32 peekFromCommBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ) { U32 numOfBytesPeeked = 0; // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { // verify requested # of bytes to peek are in the buffer if ( ( len <= ( COMM_BUFFER_LENGTH * DOUBLE_BUFFERS ) ) && ( len <= numberOfBytesInCommBuffer( buffer ) ) ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; if ( len <= bytesInInactiveBuffer ) { memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], len ); numOfBytesPeeked = len; } else // will need to get the rest from active buffer { U32 remNumOfBytes = len - bytesInInactiveBuffer; U08 *remPtr = data + bytesInInactiveBuffer; memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], bytesInInactiveBuffer ); memcpy( remPtr, &commBuffers[ buffer ][ activeBuffer ][ 0 ], remNumOfBytes ); numOfBytesPeeked = bytesInInactiveBuffer + remNumOfBytes; } } } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_PEEK_INVALID_BUFFER, buffer ) } return numOfBytesPeeked; } /************************************************************************* * @brief numberOfBytesInCommBuffer * The numberOfBytesInCommBuffer function determines how many bytes \n * are currently contained in a given comm buffer. Both double buffers \n * are considered for this. * @details * Inputs : activeDoubleBuffer[], commBufferByteCount[] * Outputs : none * @param buffer : which comm buffer to get byte count for * @return the number of bytes in the given comm buffer. *************************************************************************/ U32 numberOfBytesInCommBuffer( COMM_BUFFER_T buffer ) { U32 result = 0; // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); if ( FALSE == bufferGetLock[ buffer ] ) { result = commBufferByteCount[ buffer ][ inactiveBuffer ] + commBufferByteCount[ buffer ][ activeBuffer ]; } else { result = commBufferByteCount[ buffer ][ inactiveBuffer ]; } } else // invalid buffer { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_COUNT_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* * @brief switchDoubleBuffer * The switchDoubleBuffer function switches the active and inactive buffers \n * for the given buffer. \n * This function should only be called when the current inactive buffer has \n * been emptied. Any unconsumed data in inactive buffer will be lost. * @details * Inputs : activeDoubleBuffer[] * Outputs : activeDoubleBuffer[], commBufferByteCount[] * @param buffer : which comm buffer to switch double buffers on * @return the new active buffer for the given buffer. *************************************************************************/ static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); // ensure inactive buffer is reset before making active commBufferByteCount[ buffer ][ inactiveBuffer ] = 0; // switch buffers activeDoubleBuffer[ buffer ] = inactiveBuffer; // return the new active buffer (was just inactive) return inactiveBuffer; } /************************************************************************* * @brief getDataFromInactiveBuffer * The getDataFromInactiveBuffer function retrieves a given number of bytes \n * from the inactive buffer of a given buffer. This function should only be \n * called by getFromCommBuffer(). Params will be pre-validated there. * @details * Inputs : commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * Outputs : commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * @param buffer : which comm buffer get data from * @param data : pointer to byte array to populate with data * @param len : # of bytes to get from comm buffer * @return none *************************************************************************/ static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; // get the requested data from inactive buffer memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], len ); if ( len < bytesInInactiveBuffer ) { U08 *endPtr = (&commBuffers[ buffer ][ inactiveBuffer ][ 0 ] + len); // move un-consumed data in inactive buffer to start of inactive buffer memcpy( &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], endPtr, ( bytesInInactiveBuffer - len ) ); // reduce byte count for inactive buffer by # of bytes consumed commBufferByteCount[ buffer ][ inactiveBuffer ] -= len; } else { // inactive buffer has been emptied so switch double buffers switchDoubleBuffer( buffer ); // switch will zero count off inactive buffer } }