/************************************************************************** * * 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 SystemComm.c * * @author (last) Sean * @date (last) 30-Jul-2024 * * @author (original) Sean * @date (original) 30-Jul-2024 * ***************************************************************************/ #include // For memcpy() #include "can.h" #include "sci.h" #include "sys_dma.h" #include "Comm.h" #include "Interrupts.h" #include "Messaging.h" #include "SystemComm.h" #ifdef _TD_ #include "SystemCommTD.h" #include "OperationModes.h" #endif #ifdef _DD_ #include "SystemCommDD.h" #include "OperationModes.h" #include "FPOperationModes.h" #endif #include "Timers.h" #include "Utilities.h" /** * @addtogroup SystemComm * @{ */ // ********** private definitions ********** #define CAN_XMIT_PACKET_TIMEOUT_MS 200 ///< If transmitted CAN frame does not cause a transmit complete interrupt within this time, re-send or move on #define MAX_XMIT_RETRIES 5 ///< Maximum number of retries on no transmit complete interrupt timeout #define MSG_NOT_ACKED_TIMEOUT_MS 250 ///< Maximum time for a Denali message that requires ACK to be ACK'd #define MSG_NOT_ACKED_MAX_RETRIES 10 ///< Maximum number of times a message that requires ACK that was not ACK'd can be re-sent before alarm #define PENDING_ACK_LIST_SIZE 25 ///< Maximum number of Denali messages that can be pending ACK at any given time #define PENDING_ACK_LIST_OVERRIDE_UI_CHANNEL 1 ///< Value for determining UI channel when Pending ACKs are overriden. #define PENDING_ACK_LIST_OVERRIDE_DD_CHANNEL 2 ///< Value for determining DD channel when Pending ACKs are overriden. #define PENDING_ACK_LIST_OVERRIDE_TD_CHANNEL 3 ///< Value for determining TD channel when Pending ACKs are overriden. #define PENDING_ACK_LIST_OVERRIDE_RO_CHANNEL 4 ///< Value for determining RO channel when Pending ACKs are overriden. #pragma pack(push, 1) /// Record for transmitted message that is pending acknowledgment from receiver. typedef struct { BOOL used; ///< Used. U16 seqNo; ///< Message sequence number. U16 retries; ///< Number of retries. U32 timeStamp; ///< Time stamp. COMM_BUFFER_T channel; ///< Channel ID. U32 msgSize; ///< Message size. U08 msg[ MAX_ACK_MSG_SIZE ]; ///< Message. } PENDING_ACK_RECORD_T; #pragma pack(pop) // ********** private data ********** static U08 lastCANPacketSent[ CAN_FRAME_PAYLOAD_SIZE ]; ///< Keep last packet sent on CAN bus in case we need to re-send. static CAN_MESSAGE_BOX_T lastCANPacketSentChannel; ///< Keep channel last packet was sent on CAN bus in case we need to re-send. static U32 lastCANPacketSentTimeStamp; ///< Keep time last packet sent on CAN bus so we can timeout on transmission attempt. static U32 canXmitRetryCtr; ///< Counter for CAN transmit retries. static volatile PENDING_ACK_RECORD_T pendingAckList[ PENDING_ACK_LIST_SIZE ]; ///< List of outgoing messages that are awaiting an ACK static OVERRIDE_U32_T pendingACKOverride = { 0, 0, 0, 0 }; ///< Pending ACK override data structure. // ********** private function prototypes ********** static COMM_BUFFER_T findNextHighestPriorityCANPacketToTransmit( void ); static U32 transmitNextCANPacket( void ); static void processIncomingData( void ); static S32 parseMessageFromBuffer( U08 *data, U32 len ); static void consumeBufferPaddingBeforeSync( COMM_BUFFER_T buffer ); static void processReceivedMessages( void ); static void matchACKtoPendingACKList( S16 seqNo ); static void checkPendingACKList( void ); /*********************************************************************//** * @brief * The initSystemComm function initializes the SystemComm unit. * @details \b Inputs: none * @details \b Outputs: SystemComm unit variables initialized. * @return none *************************************************************************/ void initSystemComm( void ) { U32 i; lastCANPacketSentTimeStamp = 0; canXmitRetryCtr = 0; lastCANPacketSentChannel = (CAN_MESSAGE_BOX_T)0; // Initialize pending ACK list for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) { pendingAckList[ i ].used = FALSE; } } /*********************************************************************//** * @brief * The execSystemCommRx function manages received data from other sub-systems. * @note This function should be called periodically to keep up with incoming * data on CAN bus. * @details \b Inputs: none * @details \b Outputs: Incoming messages parsed and processed. * @return none *************************************************************************/ void execSystemCommRx( void ) { // Parse messages from comm buffers and queue them processIncomingData(); // Process received messages in the queue processReceivedMessages(); // Check for sub-system comm timeouts checkForCommTimeouts(); // Check ACK list for messages that need to be re-sent because they have not been ACK'd checkPendingACKList(); if ( TRUE == hasDialinCheckInExpired() ) { // It has been a while since the user logged in but not activity has been received from Dialin so set the tester's status to log out setTesterStatusToLoggedOut(); } } /*********************************************************************//** * @brief * The execSystemCommTx function manages data to be transmitted to other * sub-systems. * @note This function should be called periodically to keep up with outgoing * data being queued for transmit on CAN bus. * @details \b Inputs: lastCANPacketSentTimeStamp, canXmitRetryCtr, lastCANPacketSentChannel * @details \b Outputs: Outgoing message transmission from outgoing CAN buffers * is initiated * @return none *************************************************************************/ void execSystemCommTx( void ) { // Do not transmit if no other nodes on CAN bus if ( FALSE == isOnlyCANNode() ) { // If CAN transmitter is idle, start transmitting any pending packets if ( FALSE == isCAN1TransmitInProgress() ) { transmitNextCANPacket(); } else { // Generally, transmitter should not be busy at time of this function call - check timeout just in case so we do not get stuck waiting forever if ( TRUE == didTimeout( lastCANPacketSentTimeStamp, CAN_XMIT_PACKET_TIMEOUT_MS ) ) { // Assume last packet was not successfully transmitted. Re-send last packet. if ( ++canXmitRetryCtr <= MAX_XMIT_RETRIES ) { // Ensure we have a previous CAN packet/channel to resend - canTransmit() channel param MUST be valid if ( ( lastCANPacketSentChannel > COMM_BUFFER_NOT_USED ) && ( lastCANPacketSentChannel < NUM_OF_COMM_BUFFERS ) ) { canTransmit( canREG1, lastCANPacketSentChannel, lastCANPacketSent ); } } // We must be only node on CAN bus - nobody is ACKing our transmitted frames else { setOnlyCANNode( TRUE ); // Set only CAN node flag canXmitRetryCtr = MAX_XMIT_RETRIES; signalCANXmitsCompleted(); // Clear pending xmit flag clearCANXmitBuffers(); // Clear xmit buffers - nothing is going out right now } // end - are we retrying xmit or are we alone on CAN bus } // end - pending xmit timeout? } // end - transmit in progress or not } // end - DG not alone on CAN bus } /*********************************************************************//** * @brief * The handleCANMsgInterrupt function handles a CAN frame interrupt from * a given CAN mail box. * This may have occurred because a CAN frame transmission has completed * or because a CAN frame has been received. The appropriate handler is * called. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid CAN mail box given. * @details \b Inputs: none * @details \b Outputs: message interrupt handled * @param srcCANBox which CAN mail box triggered this interrupt * @return none *************************************************************************/ void handleCANMsgInterrupt( CAN_MESSAGE_BOX_T srcCANBox ) { // Message interrupt is for a transmit message box? if ( TRUE == isCANBoxForXmit( srcCANBox ) ) { U32 bytesXmitted; bytesXmitted = transmitNextCANPacket(); // If nothing more to send, signal that transmitter is available if ( 0 == bytesXmitted ) { signalCANXmitsCompleted(); } } else if ( TRUE == isCANBoxForRecv( srcCANBox ) ) { U08 data[ CAN_FRAME_PAYLOAD_SIZE ]; // Get CAN packet received on given CAN message box if ( FALSE != canIsRxMessageArrived( canREG1, srcCANBox ) ) { U32 result = canGetData( canREG1, srcCANBox, data ); // If packet retrieved, add to buffer if ( result != 0 ) { // Add CAN packet to appropriate comm buffer based on the message box it came in on (s/b same #) addToCommBuffer( srcCANBox, data, CAN_FRAME_PAYLOAD_SIZE ); } } } else { // Should not get here - not an active message box #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CAN_MESSAGE_BOX, srcCANBox ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CAN_MESSAGE_BOX, srcCANBox ) #endif } } /*********************************************************************//** * @brief * The isCANBoxForXmit function determines whether a given CAN mail box * is configured for transmit. * @details \b Inputs: CAN_OUT_BUFFERS[] * @details \b Outputs: none * @param srcCANBox which CAN mail box to determine configuration for * @return TRUE if the given CAN mail box is configured for transmit, FALSE if not. *************************************************************************/ BOOL isCANBoxForXmit( CAN_MESSAGE_BOX_T srcCANBox ) { BOOL result = FALSE; U32 i; for ( i = 0; i < NUM_OF_CAN_OUT_BUFFERS; i++ ) { if ( getOutBufferID( i ) == srcCANBox ) { result = TRUE; break; } } return result; } /*********************************************************************//** * @brief * The isCANBoxForRecv function determines whether a given CAN mail box * is configured for receiving. * @details \b Inputs: CAN_IN_BUFFERS[] * @details \b Outputs: none * @param srcCANBox which CAN mail box to determine configuration for * @return TRUE if the given CAN mail box is configured for receiving, FALSE if not. *************************************************************************/ BOOL isCANBoxForRecv( CAN_MESSAGE_BOX_T srcCANBox ) { BOOL result = FALSE; U32 i; for ( i = 0; i < NUM_OF_CAN_IN_BUFFERS; i++ ) { if ( getInBufferID( i ) == srcCANBox ) { result = TRUE; break; } } return result; } /************************************************************************* ********************** TRANSMIT SUPPORT FUNCTIONS ************************ *************************************************************************/ /*********************************************************************//** * @brief * The findNextHighestPriorityCANPacketToTransmit function determines which * outgoing buffer with data in it has the highest priority. * @details \b Inputs: Outgoing CAN buffer * @details \b Outputs: none * @return Highest priority CAN buffer with data to transmit, * COMM_BUFFER_NOT_USED if there is no data to be transmitted. *************************************************************************/ static COMM_BUFFER_T findNextHighestPriorityCANPacketToTransmit( void ) { COMM_BUFFER_T result = COMM_BUFFER_NOT_USED; U32 i; // Search for next priority CAN packet to transmit for ( i = 0; i < NUM_OF_CAN_OUT_BUFFERS; i++ ) { if ( numberOfBytesInCommBuffer( getOutBufferID( i ) ) >= CAN_FRAME_PAYLOAD_SIZE ) { result = getOutBufferID( i ); break; // Found highest priority packet to transmit - we are done } } return result; } /*********************************************************************//** * @brief * The transmitNextCANPacket function creates a frame from the next 8 bytes * to transmit (if there is data to transmit) and initiates a CAN transmit * on the appropriate CAN channel. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid byte count or * if frame transmit fails. * @details \b Inputs: Output CAN buffers * @details \b Outputs: none * @return number of bytes transmitted (8 or 0) *************************************************************************/ static U32 transmitNextCANPacket( void ) { U32 result = 0; COMM_BUFFER_T buffer = findNextHighestPriorityCANPacketToTransmit(); // If a buffer is found with a packet to transmit, get packet from buffer and transmit it if ( buffer != COMM_BUFFER_NOT_USED ) { U08 data[ CAN_FRAME_PAYLOAD_SIZE ]; U32 dataSize = getFromCommBuffer( buffer, data, CAN_FRAME_PAYLOAD_SIZE ); CAN_MESSAGE_BOX_T mBox = buffer; // CAN message boxes and comm buffers are aligned // If there is another CAN packet to send, send it if ( dataSize == CAN_FRAME_PAYLOAD_SIZE ) { // We are transmitting another packet - signal transmitter is busy signalCANXmitsInitiated(); // Remember packet data being transmitted here in case transmission fails and we need to re-send memcpy( lastCANPacketSent, data, CAN_FRAME_PAYLOAD_SIZE ); lastCANPacketSentChannel = mBox; lastCANPacketSentTimeStamp = getMSTimerCount(); if ( 0 != canTransmit( canREG1, mBox, data ) ) { result = CAN_FRAME_PAYLOAD_SIZE; } else { signalCANXmitsCompleted(); #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_SYSTEM_CMMM_CAN_TRANSMIT_REJECTED, (U32)mBox ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_SYSTEM_CMMM_CAN_TRANSMIT_REJECTED, (U32)mBox ) #endif } } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_SYSTEM_COMM_INVALID_FRAME_SIZE, ((U32)buffer << 16) | dataSize ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_SYSTEM_COMM_INVALID_FRAME_SIZE, ((U32)buffer << 16) | dataSize ) #endif } } return result; } /************************************************************************* ********************** RECEIVE SUPPORT FUNCTIONS ************************* *************************************************************************/ /*********************************************************************//** * @brief * The processIncomingData function parses out messages from the incoming * CAN buffers and adds them to the Received Message Queue. * @details \b Inputs: none * @details \b Outputs: canXmitRetryCtr * @return none *************************************************************************/ static void processIncomingData( void ) { U08 data[ 512 ]; // Message work space U32 i; BOOL badCRCDetected = FALSE; // Queue any received messages for ( i = 0; i < NUM_OF_CAN_IN_BUFFERS; i++ ) { BOOL messagesInBuffer = TRUE; // Assume true at first to get into while loop while ( TRUE == messagesInBuffer ) { U32 numOfBytesInBuffer; // Assume false so we do not get stuck in loop. Set to true only if we find another complete message in buffer messagesInBuffer = FALSE; // Since messages can have 8-byte alignment padding left unconsumed by last get, get padding out of buffer consumeBufferPaddingBeforeSync( getInBufferID( i ) ); // Do we have enough bytes in buffer for smallest message? numOfBytesInBuffer = numberOfBytesInCommBuffer( getInBufferID( i ) ); if ( numOfBytesInBuffer >= MESSAGE_OVERHEAD_SIZE ) { // Peek at minimum of all bytes available or max message size (+1 for sync byte) U32 bytesPeeked = peekFromCommBuffer( getInBufferID( i ), data, MIN( numOfBytesInBuffer, sizeof( MESSAGE_WRAPPER_T ) + 1 ) ); S32 msgSize = parseMessageFromBuffer( data, bytesPeeked ); setOnlyCANNode( FALSE ); // Since we are getting a message, this indicates we are not the only node on the CAN bus canXmitRetryCtr = 0; if ( msgSize > 0 ) // Valid, complete message found? { MESSAGE_WRAPPER_T rcvMsg; U08 *dataPtr = data+1; // Skip over sync byte // Consume message (+sync byte) msgSize = getFromCommBuffer( getInBufferID( i ), data, msgSize + 1 ); // Convert received message data to a message and add to message queue messagesInBuffer = TRUE; // Keep processing this buffer // Blank the new message record blankMessageInWrapper( &rcvMsg ); // Copy message header portion of message data to the new message memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof( MESSAGE_HEADER_T ) ); dataPtr += sizeof( MESSAGE_HEADER_T ); // Copy message payload portion of message data to the new message memcpy( &(rcvMsg.msg.payload), dataPtr, rcvMsg.msg.hdr.payloadLen ); dataPtr += rcvMsg.msg.hdr.payloadLen; // Copy CRC portion of message data to the new message rcvMsg.crc = *dataPtr; // Set incoming buffer that message came into rcvMsg.msg.in_buffer = getInBufferID( i ); // Add new message to queue for later processing addToMsgQueue( MSG_Q_IN, &rcvMsg ); #ifdef _TD_ // If message from DD broadcast channel, update DD comm status if ( COMM_BUFFER_IN_CAN_DD_BROADCAST == getInBufferID( i ) ) { checkInFromDD(); } // If message from UI channel, mark UI communication so TD can begin transmitting if ( ( COMM_BUFFER_IN_CAN_UI_2_TD == getInBufferID( i ) ) || ( COMM_BUFFER_IN_CAN_UI_BROADCAST == getInBufferID( i ) ) ) { checkInFromUI(); } #endif #ifdef _DD_ // If message from TD broadcast channel, update TD comm status if ( COMM_BUFFER_IN_CAN_TD_BROADCAST == getInBufferID( i ) ) { checkInFromTD(); } #endif } else if ( -1 == msgSize ) // Candidate message with bad CRC found? { badCRCDetected = TRUE; getFromCommBuffer( getInBufferID( i ), data, 1 ); // Consume sync byte so we can re-sync messagesInBuffer = TRUE; // Keep processing this buffer } // Looks like there is a complete message in the comm buffer } // Enough data left in comm buffer to possibly be a complete message } // While loop to get all complete messages for each comm buffer } // For loop to check all comm buffers for messages // If any bad CRCs detected, see if too many if ( TRUE == badCRCDetected ) { checkTooManyBadMsgCRCs(); } } /*********************************************************************//** * @brief * The consumeBufferPaddingBeforeSync function removes any bytes in a given * buffer that lie before a sync byte to re-sync with the data. * @details \b Inputs: none * @details \b Outputs: Any bytes in buffers preceding a sync byte at front * of buffer is consumed. * @param buffer the CAN buffer to re-sync * @return none *************************************************************************/ static void consumeBufferPaddingBeforeSync( COMM_BUFFER_T buffer ) { U08 data; U32 numOfBytesInBuffer = numberOfBytesInCommBuffer( buffer ); // Consume bytes out of buffer 1 at a time until we find the sync byte or it is empty while ( numOfBytesInBuffer > 0 ) { peekFromCommBuffer( buffer, &data, 1 ); if ( MESSAGE_SYNC_BYTE == data ) { break; // We found a sync - we are done } else // Not a sync byte, so consume it { getFromCommBuffer( buffer, &data, 1 ); numOfBytesInBuffer = numberOfBytesInCommBuffer( buffer ); } } } /*********************************************************************//** * @brief * The parseMessageFromBuffer function looks for a complete message in a * buffer (pointed to by given data pointer). If a message is found, its * size is returned. * @note The message is not consumed (removed) from the buffer. Calling * function is expected to retrieve the message by consuming the returned * message size from the given buffer. * @details \b Inputs: none * @details \b Outputs: none * @param data pointer to start of a CAN buffer to search for a message in * @param len number of bytes in the data to search * @return The size of the message, if found, zero if no complete message * is found, -1 if message found but CRC fails. *************************************************************************/ static S32 parseMessageFromBuffer( U08 *data, U32 len ) { U32 i; U32 payloadSize; U32 msgSize; S32 result = 0; for ( i = 0; i < len; i++ ) { // Find sync byte if ( MESSAGE_SYNC_BYTE == data[ i ] ) { U32 pos = i + 1; // Skip past sync byte implemented U32 remSize = len - pos; // If a minimum sized msg would fit in remaining, continue if ( remSize >= MESSAGE_OVERHEAD_SIZE ) { payloadSize = data[ pos + sizeof(MESSAGE_HEADER_T) - sizeof(U08) ]; msgSize = MESSAGE_OVERHEAD_SIZE + payloadSize; // We now know the size of the message - we can now know if full message is contained in buffer if ( msgSize <= remSize ) { // Check CRC to make sure it is a valid message if ( data[i+msgSize] == crc8( &data[pos], msgSize - 1 ) ) { result = msgSize; // We found a complete, valid message of this size } else // CRC failed { result = -1; // We found a complete, invalid message } } } break; } } return result; } /*********************************************************************//** * @brief * The processReceivedMessages function processes all messages in the * received message queue. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ static void processReceivedMessages( void ) { BOOL isThereMsgRcvd = TRUE; // Assume TRUE at first to get into while loop MESSAGE_WRAPPER_T message; while ( TRUE == isThereMsgRcvd ) { // See if any messages received isThereMsgRcvd = getFromMsgQueue( MSG_Q_IN, &message ); if ( TRUE == isThereMsgRcvd ) { // CRC should be good because we checked it during parsing before adding to queue - but check it again for good measure if ( message.crc == crc8( (U08*)(&message), sizeof(MESSAGE_HEADER_T) + message.msg.hdr.payloadLen ) ) { // If ACK, mark pending message ACK'd if ( MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK == message.msg.hdr.msgID ) { matchACKtoPendingACKList( message.msg.hdr.seqNo ); } else { // If received message requires ACK, queue one up if ( message.msg.hdr.seqNo < 0 ) { sendACKMsg( &message.msg ); } // Process the received message processReceivedMessage( &message.msg ); } } else // CRC failed { checkTooManyBadMsgCRCs(); } } } } /*********************************************************************//** * @brief * The addMsgToPendingACKList function adds a given message to the pending * ACK list. Messages in this list will require receipt of an ACK message * for this particular message within a limited time. * @note List operations are thread protected by disabling IRQ interrupts. * @details \b Inputs: pendingAckList[] * @details \b Outputs: pendingAckList[] * @param msg Pointer to the message record (needed for header info) * @param channel CAN channel that the message was transmitted on (needed for re-send attempts) * @param msgData Pointer to serialized message data to add to pending ACK list (needed for re-send attempts) * @param len Number of bytes of message data (needed for re-send attempts) * @return TRUE if the message was added to the list successfully, FALSE if not *************************************************************************/ BOOL addMsgToPendingACKList( MESSAGE_T *msg, COMM_BUFFER_T channel, U08 *msgData, U32 len ) { BOOL result = FALSE; U32 i; // Find first open slot in pending ACK list and add given msg data to it for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) { _disable_IRQ(); // Slot selection needs interrupt protection if ( FALSE == pendingAckList[ i ].used ) { S16 seqNo = msg->hdr.seqNo * -1; // Remove ACK bit from seq # pendingAckList[ i ].used = TRUE; _enable_IRQ(); pendingAckList[ i ].seqNo = seqNo; pendingAckList[ i ].channel = channel; pendingAckList[ i ].timeStamp = getMSTimerCount(); pendingAckList[ i ].retries = MSG_NOT_ACKED_MAX_RETRIES; pendingAckList[ i ].msgSize = len; memcpy( (U08*)pendingAckList[ i ].msg, msgData, len ); result = TRUE; break; } else { _enable_IRQ(); } } return result; } /*********************************************************************//** * @brief * The matchACKtoPendingACKList function searches the pending ACK list to * see if the sequence # from a received ACK msg matches any transmitted * messages pending an ACK. If found, the list entry is removed (no longer * pending). * @details \b Inputs: pendingAckList[] * @details \b Outputs: pendingAckList[] * @param seqNo sequence number to match to an entry in the list * @return none *************************************************************************/ static void matchACKtoPendingACKList( S16 seqNo ) { U32 i; // Find match for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) { if ( ( TRUE == pendingAckList[ i ].used ) && ( pendingAckList[ i ].seqNo == seqNo ) ) { #ifdef _TD_ // Remove message pending ACK from list if ( ( ( getU32OverrideValue( &pendingACKOverride ) != PENDING_ACK_LIST_OVERRIDE_UI_CHANNEL ) || ( pendingAckList[ i ].channel != COMM_BUFFER_OUT_CAN_TD_2_UI ) ) && ( ( getU32OverrideValue( &pendingACKOverride ) != PENDING_ACK_LIST_OVERRIDE_DD_CHANNEL ) || ( pendingAckList[ i ].channel != COMM_BUFFER_OUT_CAN_TD_2_DD ) ) ) #endif #ifdef _DD_ //TODO : validate code changes // Remove message pending ACK from list if ( ( getU32OverrideValue( &pendingACKOverride ) != PENDING_ACK_LIST_OVERRIDE_TD_CHANNEL ) || ( pendingAckList[ i ].channel != COMM_BUFFER_OUT_CAN_DD_2_TD ) ) #endif { pendingAckList[ i ].used = FALSE; } break; } } } /*********************************************************************//** * @brief * The checkPendingACKList function searches the pending ACK list to * see if any have expired. Any such messages will be queued for retransmission * if max retries not yet reached * @details \b Alarm: ALARM_ID_XX_CAN_MESSAGE_NOT_ACKED_BY_XX if expired and max retries reached. * @details \b Inputs: pendingAckList[] * @details \b Outputs: pendingAckList[] * @return none *************************************************************************/ static void checkPendingACKList( void ) { U32 i; // Find expired messages pending ACK for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) { // Pending ACK expired? if ( ( TRUE == pendingAckList[ i ].used ) && ( TRUE == didTimeout( pendingAckList[ i ].timeStamp, MSG_NOT_ACKED_TIMEOUT_MS ) ) ) { // If retries left, reset and resend pending message if ( pendingAckList[ i ].retries > 0 ) { // Re-queue message for transmit pendingAckList[ i ].retries--; pendingAckList[ i ].timeStamp = getMSTimerCount(); addToCommBuffer( pendingAckList[ i ].channel, (U08*)pendingAckList[ i ].msg, pendingAckList[ i ].msgSize ); } // If no retries left, alarm else { U16 msgID; memcpy( &msgID, (U08*)&pendingAckList[ i ].msg[ sizeof( U08 ) + sizeof( U16) ], sizeof( U16 ) ); #ifdef _TD_ if ( pendingAckList[ i ].channel != COMM_BUFFER_OUT_CAN_TD_2_DD ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_CAN_MESSAGE_NOT_ACKED_BY_UI, (U32)msgID ); } else { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_CAN_MESSAGE_NOT_ACKED_BY_DD, (U32)msgID ); } #endif #ifdef _DD_ if ( pendingAckList[ i ].channel != COMM_BUFFER_OUT_CAN_DD_2_TD ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DD_CAN_MESSAGE_NOT_ACKED_BY_TD, (U32)msgID ); } else { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DD_CAN_MESSAGE_NOT_ACKED_BY_FP, (U32)msgID ); } #endif pendingAckList[ i ].used = FALSE; // Take pending message off of list } } } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetPendingACKOverride function overrides the pendingACKOverride variable. * @details \b Inputs: none * @details \b Outputs: pendingACKOverride * @param value Value to override pendingACKOverride with * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetPendingACKOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; pendingACKOverride.ovData = value; pendingACKOverride.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetPendingACKOverride function resets the override of the * pendingACKOverride variable. * @details \b Inputs: none * @details \b Outputs: pendingACKOverride * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetPendingACKOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; pendingACKOverride.override = OVERRIDE_RESET; pendingACKOverride.ovData = pendingACKOverride.ovInitData; } return result; } /**@}*/