/************************************************************************** * * 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 Messaging.c * * @author (last) Sean * @date (last) 30-Jul-2024 * * @author (original) Sean * @date (original) 30-Jul-2024 * ***************************************************************************/ #include #include // For memcpy() #include "reg_system.h" // Used to access system register to reset processor on request #include "Compatible.h" #include "Messaging.h" #include "OperationModes.h" #include "Utilities.h" /** * @addtogroup Messaging * @{ */ // ********** private definitions ********** #define MAX_MSGS_BLOCKED_FOR_XMIT 8 ///< Maximum number of messages to block transmission for. #pragma pack(push,1) /// Payload record structure for block message transmission request. typedef struct { U16 blockedMessages[ MAX_MSGS_BLOCKED_FOR_XMIT ]; ///< Blocked messages. } BLOCKED_MSGS_DATA_T; #pragma pack(pop) // ********** private data ********** static BOOL testerLoggedIn = FALSE; ///< Flag indicates whether an external tester (connected PC) has sent a valid login message. static volatile U16 nextSeqNo = 1; ///< Value of sequence number to use for next transmitted message. /// List of message IDs that are requested not to be transmitted. static BLOCKED_MSGS_DATA_T blockedMessagesForXmit = { 0, 0, 0, 0, 0, 0, 0, 0 }; // ********** private function prototypes ********** static BOOL sendTestAckResponseMsg( MSG_ID_T msgID, BOOL ack ); static BOOL sendAckResponseMsg( MSG_ID_T msgID, COMM_BUFFER_T buffer, BOOL ack ); static BOOL sendUIResponseMsg( MSG_ID_T msgID, BOOL accepted, U32 reason ); static void sendInstitutionalRecordToUI( HD_INSTITUTIONAL_LOCAL_RECORD_T* instit ); /*********************************************************************//** * @brief * The serializeMessage function serializes a given message into a given * array of bytes. A sequence # is added to the message here and the ACK * bit of the sequence # is set if ACK is required per parameter. A sync byte * is inserted at the beginning of the message and an 8-bit CRC is appended to * the end of the message. The message is queued for transmission in the given buffer. * @details Inputs: blockedMessagesForXmit * @details Outputs: given data array populated with serialized message data and queued for transmit. * @param msg message to serialize * @param buffer outgoing buffer that message should be queued in * @param ackReq is an acknowledgement from receiver required? * @return size (in bytes) of serialized message populated in given data array. *************************************************************************/ U32 serializeMessage( MESSAGE_T msg, COMM_BUFFER_T buffer, BOOL ackReq ) { BOOL result = 0; BOOL blocked = FALSE; U32 msgSize = 0; U32 sizeMod, sizePad; U32 i; U08 crc; U08 data[ MAX_ACK_MSG_SIZE ]; // Byte array to populate with message data // Check to see if tester has requested this message not be transmited if ( TRUE == isTestingActivated() ) { U32 i; for ( i = 0; i < MAX_MSGS_BLOCKED_FOR_XMIT; i++ ) { if ( msg.hdr.msgID == blockedMessagesForXmit.blockedMessages[ i ] ) { blocked = TRUE; break; } } } // Serialize and queue message for transmission unless this message is blocked if ( blocked != TRUE ) { // Prefix data with message sync byte data[ msgSize++ ] = MESSAGE_SYNC_BYTE; // Set sequence # and ACK bit (unless this is an ACK to a received message) if ( msg.hdr.msgID != MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK ) { // Thread protect next sequence # access & increment _disable_IRQ(); msg.hdr.seqNo = nextSeqNo; nextSeqNo = INC_WRAP( nextSeqNo, MIN_MSG_SEQ_NO, MAX_MSG_SEQ_NO ); _enable_IRQ(); if ( TRUE == ackReq ) { msg.hdr.seqNo *= -1; } } // Calculate message CRC crc = crc8( (U08*)(&msg), sizeof( MESSAGE_HEADER_T ) + msg.hdr.payloadLen ); // Serialize message header data memcpy( &data[ msgSize ], &( msg.hdr ), sizeof( MESSAGE_HEADER_T ) ); msgSize += sizeof( MESSAGE_HEADER_T ); // Serialize message payload (only used bytes per payloadLen field) memcpy( &data[ msgSize ], &( msg.payload ), msg.hdr.payloadLen ); msgSize += msg.hdr.payloadLen; // Add 8-bit CRC data[ msgSize++ ] = crc; // Pad with zero bytes to get length a multiple of CAN_MESSAGE_PAYLOAD_SIZE (8) sizeMod = msgSize % CAN_MESSAGE_PAYLOAD_SIZE; sizePad = ( sizeMod == 0 ? 0 : CAN_MESSAGE_PAYLOAD_SIZE - sizeMod ); for ( i = 0; i < sizePad; i++ ) { data[ msgSize++ ] = 0; } #ifndef _RELEASE_ // if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_ACK_ERRORS ) != SW_CONFIG_ENABLE_VALUE ) #endif { // If ACK required, add to pending ACK list if ( TRUE == ackReq ) { if ( FALSE == addMsgToPendingACKList( &msg, buffer, data, msgSize ) ) { // SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MSG_PENDING_ACK_LIST_FULL, (U32)(msg.hdr.msgID) ) } } } // Add serialized message data to appropriate out-going comm buffer result = addToCommBuffer( buffer, data, msgSize ); } else { result = TRUE; // If message blocked, return successful transmission } return result; } /*********************************************************************//** * @brief * The sendACKMsg function constructs and queues for transmit an ACK message * for a given received message. * @details Inputs: none * @details Outputs: ACK message queued for transmit on broadcast CAN channel. * @param message message to send an ACK for * @return TRUE if ACK message queued successfully, FALSE if not *************************************************************************/ BOOL sendACKMsg( MESSAGE_T *message ) { BOOL result; MESSAGE_T msg; // Create a message record blankMessage( &msg ); // Send ACK back with same seq. #, but w/o ACK bit msg.hdr.seqNo = message->hdr.seqNo * -1; // ACK messages always have this ID msg.hdr.msgID = MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK; // ACK messages always have no payload msg.hdr.payloadLen = 0; // Serialize and queue the message for transmit on broadcast channel result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_TD_BROADCAST, ACK_NOT_REQUIRED ); return result; } /*********************************************************************//** * @brief * The sendAckResponseMsg function constructs a simple response * message for a handled message and queues it for transmit on the * appropriate CAN channel. * @details Inputs: none * @details Outputs: response message constructed and queued for transmit. * @param msgID ID of handled message that we are responding to * @param buffer outgoing buffer that message should be queued in * @param ack TRUE if test message was handled successfully, FALSE if not * @return TRUE if response message successfully queued for transmit, FALSE if not *************************************************************************/ static BOOL sendAckResponseMsg( MSG_ID_T msgID, COMM_BUFFER_T buffer, BOOL ack ) { BOOL result; MESSAGE_T msg; ACK_RESPONSE_PAYLOAD_T cmd; cmd.acknowledgement = ack; // Create a message record blankMessage( &msg ); msg.hdr.msgID = msgID; msg.hdr.payloadLen = sizeof( ACK_RESPONSE_PAYLOAD_T ); memcpy( &msg.payload, &cmd, sizeof( ACK_RESPONSE_PAYLOAD_T ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, buffer, ACK_NOT_REQUIRED ); return result; } /*********************************************************************//** * @brief * The sendUIResponseMsg function constructs an UI response message for a * handled UI message and queues it for transmit on the appropriate CAN channel. * @details Inputs: none * @details Outputs: response message constructed and queued for transmit. * @param msgID ID of handled message that we are responding to * @param accepted T/F - request accepted? * @param reason reason code if rejected * @return TRUE if response message successfully queued for transmit, FALSE if not *************************************************************************/ static BOOL sendUIResponseMsg( MSG_ID_T msgID, BOOL accepted, U32 reason ) { BOOL result; MESSAGE_T msg; UI_RESPONSE_PAYLOAD_T cmd; cmd.accepted = accepted; cmd.rejectionReason = reason; // Create a message record blankMessage( &msg ); msg.hdr.msgID = msgID; msg.hdr.payloadLen = sizeof( UI_RESPONSE_PAYLOAD_T ); memcpy( &msg.payload, &cmd, sizeof( UI_RESPONSE_PAYLOAD_T ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_TD_2_UI, ACK_REQUIRED ); return result; } // *********************************************************************** // ***************** Message Sending Helper Functions ******************** // *********************************************************************** /*********************************************************************//** * @brief * The sendInstitutionalRecordToUI function sends the institutional record to UI * @details Inputs: none * @details Outputs: none * @param instit a pointer to the local institutional recored in the system * messages that is without calibration time and crc * @return none *************************************************************************/ static void sendInstitutionalRecordToUI( HD_INSTITUTIONAL_LOCAL_RECORD_T* instit ) { MESSAGE_T msg; U08 *payloadPtr = msg.payload; U32 accept = 1; U32 reason = 0; // Create a message record blankMessage( &msg ); msg.hdr.msgID = MSG_ID_HD_INSTITUTIONAL_RECORD_RESPONSE; msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ) + sizeof( HD_INSTITUTIONAL_LOCAL_RECORD_T ); memcpy( payloadPtr, &accept, sizeof( U32 ) ); payloadPtr += sizeof( U32 ); memcpy( payloadPtr, &reason, sizeof( U32 ) ); payloadPtr += sizeof( U32 ); memcpy( payloadPtr, instit, sizeof( HD_INSTITUTIONAL_LOCAL_RECORD_T ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer serializeMessage( msg, COMM_BUFFER_OUT_CAN_TD_2_UI, ACK_NOT_REQUIRED ); } /*********************************************************************//** * @brief * The handleUITDResetInServiceModeRequest function handles the UI request * to reset HD in service mode * @details Inputs: none * @details Outputs: none * @param message a pointer to the message to handle * @return none *************************************************************************/ void handleUITDResetInServiceModeRequest( MESSAGE_T* message ) { // Verify payload length if ( ( 0 == message->hdr.payloadLen ) && ( MODE_SERV == getCurrentOperationMode() ) ) { #ifndef _VECTORCAST_ systemREG1->SYSECR = (0x2) << 14; // Reset processor #endif } // Respond to request sendAckResponseMsg( (MSG_ID_T)message->hdr.msgID, COMM_BUFFER_OUT_CAN_TD_2_UI, FALSE ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The isTestingActivated function determines whether a tester has successfully * logged in to activate testing functionality. * @details Inputs: testerLoggedIn * @details Outputs: none * @return TRUE if a tester has logged in to activate testing, FALSE if not *************************************************************************/ BOOL isTestingActivated( void ) { return testerLoggedIn; } /*********************************************************************//** * @brief * The setTesterStatusToLoggedOut function sets the status of the tester to * logged out. * @details Inputs: none * @details Outputs: testerLoggedIn * @return none *************************************************************************/ void setTesterStatusToLoggedOut( void ) { testerLoggedIn = FALSE; } /*********************************************************************//** * @brief * The sendTestAckResponseMsg function constructs a simple response * message for a handled test message and queues it for transmit on the * appropriate UART channel. * @details Inputs: none * @details Outputs: response message constructed and queued for transmit. * @param msgID ID of handled message that we are responding to * @param ack TRUE if test message was handled successfully, FALSE if not * @return TRUE if response message successfully queued for transmit, FALSE if not *************************************************************************/ static BOOL sendTestAckResponseMsg( MSG_ID_T msgID, BOOL ack ) { BOOL result; MESSAGE_T msg; // Create a message record blankMessage( &msg ); msg.hdr.msgID = msgID; msg.hdr.payloadLen = sizeof( U08 ); msg.payload[ 0 ] = (U08)ack; // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_PC, ACK_NOT_REQUIRED ); return result; } /*********************************************************************//** * @brief * The sendEvent function constructs an TD event message to the UI and * queues the msg for transmit on the appropriate CAN channel. * @details Inputs: none * @details Outputs: TD event msg constructed and queued. * @param event Enumeration of event type that occurred * @param dat1 First data associated with event * @param dat2 Second data associated with event * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ BOOL sendEvent( TD_EVENT_ID_T event, EVENT_DATA_T dat1, EVENT_DATA_T dat2 ) { BOOL result; MESSAGE_T msg; EVENT_PAYLOAD_T eventStruct; eventStruct.event = (U32)event; eventStruct.dataType1 = (U32)dat1.dataType; eventStruct.data1 = dat1.data; eventStruct.dataType2 = (U32)dat2.dataType; eventStruct.data2 = dat2.data; // Create a message record blankMessage( &msg ); msg.hdr.msgID = MSG_ID_HD_EVENT; // The payload length is the event ID, 2 event datas and the events data types for each of the event data msg.hdr.payloadLen = sizeof( EVENT_PAYLOAD_T ); memcpy( &msg.payload, &eventStruct, sizeof( EVENT_PAYLOAD_T ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_TD_2_UI, ACK_NOT_REQUIRED ); return result; } /*********************************************************************//** * @brief * The handleTesterLogInRequest function handles a request to login as a * tester. * @details Inputs: none * @details Outputs: message handled * @param message a pointer to the message to handle * @return none *************************************************************************/ void handleTesterLogInRequest( MESSAGE_T *message ) { // Verify pass code // TODO - placeholder - how do we want to authenticate tester? if ( ( 3 == message->hdr.payloadLen ) && ( 0x31 == message->payload[ 0 ] ) && ( 0x32 == message->payload[ 1 ] ) && ( 0x33 == message->payload[ 2 ] ) ) { testerLoggedIn = TRUE; checkInFromUI(); // Allow tasks to begin normal processing when tester has logged in // Set the dialin checkin time stamp until the first checkin message is received from dailin setDialinCheckInTimeStamp(); } else { testerLoggedIn = FALSE; } // Respond to would be tester sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, testerLoggedIn ); } /**@}*/