/************************************************************************** * * Copyright (c) 2024-2024 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 * * @author (last) Vinayakam Mani * @date (last) 05-Aug-2024 * * @author (original) Vinayakam Mani * @date (original) 05-Aug-2024 * ***************************************************************************/ #include // for memcpy() //#include "CommBuffers.h" #include "Messaging.h" #include "SystemCommDD.h" #include "Timers.h" /** * @addtogroup CommBuffers * @{ */ // ********** private definitions ********** #define COMM_BUFFER_LENGTH 768 ///< Comm buffer length in bytes (double if you count double buffers). #define DOUBLE_BUFFERS 2 ///< Two buffers for double buffering. #define BUFFER_OVERFLOW_PERSISTENCE_MS 5000 ///< Interval which buffer overflows must persist before fault. // ********** private data ********** static volatile U32 commBufferByteCount[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ]; ///< Comm buffer byte count. static volatile U32 activeDoubleBuffer[ NUM_OF_COMM_BUFFERS ]; ///< Active buffer that being fed to. static U08 commBuffers[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ][ COMM_BUFFER_LENGTH ]; ///< Double buffered comm buffer to avoid thread contention. static U32 firstBufferOverflowTimeStamp = 0; ///< Time stamp of a prior overflow event - allows for an overflow persistence check. // ********** private function prototypes ********** static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ); static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ); /*********************************************************************//** * @brief * The initCommBuffers function initializes the CommBuffers module. * @details \b Inputs: none * @details \b Outputs: CommBuffers module initialized. * @return none *************************************************************************/ void initCommBuffers( void ) { S32 b; // reset and zero out all buffers for ( b = 0; b < NUM_OF_COMM_BUFFERS; b++ ) { clearBuffer( (COMM_BUFFER_T)b ); } } /*********************************************************************//** * @brief * The clearBuffer function clears (empties) a given buffer. * @details \b Inputs: none * @details \b Outputs: given buffer is cleared * @param buffer the buffer to clear * @warning Caller should ensure buffer won't be used while this function * is clearing the buffer. * @return none *************************************************************************/ void clearBuffer( COMM_BUFFER_T buffer ) { if ( buffer < NUM_OF_COMM_BUFFERS ) { S32 d; // thread protection for queue operations _disable_IRQ(); activeDoubleBuffer[ buffer ] = 0; for ( d = 0; d < DOUBLE_BUFFERS; d++ ) { commBufferByteCount[ buffer ][ d ] = 0; memset( &commBuffers[ buffer ][ d ][ 0 ], 0, COMM_BUFFER_LENGTH ); } // release thread protection _enable_IRQ(); } } /*********************************************************************//** * @brief * The addToCommBuffer function adds data of specified length to a specified * communication buffer. This function will always add to the active double buffer. * @details \b Inputs: commBufferByteCount[], activeDoubleBuffer[] * @details \b Outputs: commBuffers[], commBufferByteCount[] * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when buffer too full to add data and/or * when invalid buffer given. * @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) * @warning This function should only be called * from the background, general, or priority tasks (BG or IRQ) for thread safety. * @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 ) { BOOL bufferFull = FALSE; U32 activeBuffer; U32 currentActiveBufCount; // where to start adding new data to buffer (after existing data) // prevent adding to out-going CAN buffer if DG is only node on CAN bus if ( ( FALSE == isOnlyCANNode() ) || ( FALSE == isCANBoxForXmit( (CAN_MESSAGE_BOX_T)buffer ) ) ) { // thread protection for queue operations _disable_IRQ(); 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; // data successfully added to buffer result = TRUE; } else // buffer too full to add this much data { bufferFull = TRUE; } // release thread protection _enable_IRQ(); } // if buffer was full, check persistence - trigger s/w fault if persists if ( TRUE == bufferFull ) { clearBuffer( buffer ); // not first overflow? if ( firstBufferOverflowTimeStamp != 0 ) { // if buffer overflows persists, fault if ( calcTimeSince( firstBufferOverflowTimeStamp ) > BUFFER_OVERFLOW_PERSISTENCE_MS ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_TOO_MUCH_DATA, (U32)buffer ) } } else // first overflow - set time stamp for persistence check { firstBufferOverflowTimeStamp = getMSTimerCount(); } } else { // if good for persistence time period, reset persistence check if ( ( firstBufferOverflowTimeStamp != 0 ) && ( calcTimeSince( firstBufferOverflowTimeStamp ) > BUFFER_OVERFLOW_PERSISTENCE_MS ) ) { firstBufferOverflowTimeStamp = 0; } } } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_INVALID_BUFFER, buffer ) } return result; } /*********************************************************************//** * @brief * The getFromCommBuffer function fills a given byte array with a given number * of bytes from a given buffer and returns the number of bytes retrieved from * the buffer. This function will draw from the inactive double buffer first and, * if needed, switch double buffers to draw the rest of the requested data. * @details \b Inputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * @details \b Outputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[], * 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 number of bytes to retrieve into given data array * @warning Only one function in one thread should be calling this function for * a given buffer. * @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 ) { // thread protection for queue operations _disable_IRQ(); // 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 = GET_TOGGLE( activeBuffer, 0, 1 ); 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 > sizeOfFirstConsumption ) { U32 remNumOfBytes = len - sizeOfFirstConsumption; U08 *remPtr = data + sizeOfFirstConsumption; getDataFromInactiveBuffer( buffer, remPtr, remNumOfBytes ); // will return # of bytes consumed result += remNumOfBytes; } } // release thread protection _enable_IRQ(); } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_GET_INVALID_BUFFER, buffer ) } return result; } /*********************************************************************//** * @brief * The peekFromCommBuffer function fills a given byte array with a given number of * bytes from a given buffer. This function does NOT consume the bytes - it only * peeks at them. A call to numberOfBytesInCommBuffer() should be made before * calling this function to determine how many bytes are currently in the buffer. * Do not call this function with a "len" longer than what is currently in the buffer. * @details \b Inputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * @details \b Outputs: given array populated with requested number 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 number 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 ) { // thread protection for queue operations _disable_IRQ(); // 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 = GET_TOGGLE( activeBuffer, 0, 1 ); 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; } } // release thread protection _enable_IRQ(); } else // invalid buffer given { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_PEEK_INVALID_BUFFER, buffer ) } return numOfBytesPeeked; } /*********************************************************************//** * @brief * The numberOfBytesInCommBuffer function determines how many bytes are currently * contained in a given comm buffer. Both double buffers are considered for this. * @details \b Inputs: activeDoubleBuffer[], commBufferByteCount[] * @details \b 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 ); result = commBufferByteCount[ buffer ][ inactiveBuffer ] + commBufferByteCount[ buffer ][ activeBuffer ]; } else // invalid buffer { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_COUNT_INVALID_BUFFER, buffer ) } return result; } /*********************************************************************//** * @brief * The switchDoubleBuffer function switches the active and inactive buffers for the * given buffer. This function should only be called when the current inactive buffer * has been emptied. Any unconsumed data in inactive buffer will be lost. * @details \b Inputs: activeDoubleBuffer[] * @details \b 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 = GET_TOGGLE( activeBuffer, 0, 1 ); // 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 * The getDataFromInactiveBuffer function retrieves a given number of bytes * from the inactive buffer of a given buffer. This function should only be * called by getFromCommBuffer(). Params will be pre-validated there. * @details \b Inputs: commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * @details \b Outputs: commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * @param buffer which comm buffer get data from * @param data pointer to byte array to populate with data * @param len number 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 = GET_TOGGLE( activeBuffer, 0, 1 ); 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 } } /**@}*/