# include "can_common_declarations.h"
uint32_t safety_tx_blocked = 0 ;
uint32_t safety_rx_invalid = 0 ;
uint32_t tx_buffer_overflow = 0 ;
uint32_t rx_buffer_overflow = 0 ;
can_health_t can_health [ CAN_HEALTH_ARRAY_SIZE ] = { { 0 } , { 0 } , { 0 } } ;
// Ignition detected from CAN meessages
bool ignition_can = false ;
uint32_t ignition_can_cnt = 0U ;
int can_live = 0 ;
int pending_can_live = 0 ;
int can_silent = ALL_CAN_SILENT ;
bool can_loopback = false ;
// ********************* instantiate queues *********************
# define can_buffer(x, size) \
static CANPacket_t elems_ # # x [ size ] ; \
extern can_ring can_ # # x ; \
can_ring can_ # # x = { . w_ptr = 0 , . r_ptr = 0 , . fifo_size = ( size ) , . elems = ( CANPacket_t * ) & ( elems_ # # x ) } ;
# define CAN_RX_BUFFER_SIZE 4096U
# define CAN_TX_BUFFER_SIZE 416U
# ifdef STM32H7
// ITCM RAM and DTCM RAM are the fastest for Cortex-M7 core access
__attribute__ ( ( section ( " .axisram " ) ) ) can_buffer ( rx_q , CAN_RX_BUFFER_SIZE )
__attribute__ ( ( section ( " .itcmram " ) ) ) can_buffer ( tx1_q , CAN_TX_BUFFER_SIZE )
__attribute__ ( ( section ( " .itcmram " ) ) ) can_buffer ( tx2_q , CAN_TX_BUFFER_SIZE )
# else
can_buffer ( rx_q , CAN_RX_BUFFER_SIZE )
can_buffer ( tx1_q , CAN_TX_BUFFER_SIZE )
can_buffer ( tx2_q , CAN_TX_BUFFER_SIZE )
# endif
can_buffer ( tx3_q , CAN_TX_BUFFER_SIZE )
// FIXME:
// cppcheck-suppress misra-c2012-9.3
can_ring * can_queues [ CAN_QUEUES_ARRAY_SIZE ] = { & can_tx1_q , & can_tx2_q , & can_tx3_q } ;
// ********************* interrupt safe queue *********************
bool can_pop ( can_ring * q , CANPacket_t * elem ) {
bool ret = 0 ;
ENTER_CRITICAL ( ) ;
if ( q - > w_ptr ! = q - > r_ptr ) {
* elem = q - > elems [ q - > r_ptr ] ;
if ( ( q - > r_ptr + 1U ) = = q - > fifo_size ) {
q - > r_ptr = 0 ;
} else {
q - > r_ptr + = 1U ;
}
ret = 1 ;
}
EXIT_CRITICAL ( ) ;
return ret ;
}
bool can_push ( can_ring * q , const CANPacket_t * elem ) {
bool ret = false ;
uint32_t next_w_ptr ;
ENTER_CRITICAL ( ) ;
if ( ( q - > w_ptr + 1U ) = = q - > fifo_size ) {
next_w_ptr = 0 ;
} else {
next_w_ptr = q - > w_ptr + 1U ;
}
if ( next_w_ptr ! = q - > r_ptr ) {
q - > elems [ q - > w_ptr ] = * elem ;
q - > w_ptr = next_w_ptr ;
ret = true ;
}
EXIT_CRITICAL ( ) ;
if ( ! ret ) {
# ifdef DEBUG
print ( " can_push to " ) ;
if ( q = = & can_rx_q ) {
print ( " can_rx_q " ) ;
} else if ( q = = & can_tx1_q ) {
print ( " can_tx1_q " ) ;
} else if ( q = = & can_tx2_q ) {
print ( " can_tx2_q " ) ;
} else if ( q = = & can_tx3_q ) {
print ( " can_tx3_q " ) ;
} else {
print ( " unknown " ) ;
}
print ( " failed! \n " ) ;
# endif
}
return ret ;
}
uint32_t can_slots_empty ( const can_ring * q ) {
uint32_t ret = 0 ;
ENTER_CRITICAL ( ) ;
if ( q - > w_ptr > = q - > r_ptr ) {
ret = q - > fifo_size - 1U - q - > w_ptr + q - > r_ptr ;
} else {
ret = q - > r_ptr - q - > w_ptr - 1U ;
}
EXIT_CRITICAL ( ) ;
return ret ;
}
void can_clear ( can_ring * q ) {
ENTER_CRITICAL ( ) ;
q - > w_ptr = 0 ;
q - > r_ptr = 0 ;
EXIT_CRITICAL ( ) ;
// handle TX buffer full with zero ECUs awake on the bus
refresh_can_tx_slots_available ( ) ;
}
// assign CAN numbering
// bus num: CAN Bus numbers in panda, sent to/from USB
// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for but 1)
// cans: Look up MCU can interface from bus number
// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc);
// bus_lookup: Translates from 'can number' to 'bus number'.
// can_num_lookup: Translates from 'bus number' to 'can number'.
// forwarding bus: If >= 0, forward all messages from this bus to the specified bus.
// Helpers
// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3
bus_config_t bus_config [ BUS_CONFIG_ARRAY_SIZE ] = {
{ . bus_lookup = 0U , . can_num_lookup = 0U , . forwarding_bus = - 1 , . can_speed = 5000U , . can_data_speed = 20000U , . canfd_auto = false , . canfd_enabled = false , . brs_enabled = false , . canfd_non_iso = false } ,
{ . bus_lookup = 1U , . can_num_lookup = 1U , . forwarding_bus = - 1 , . can_speed = 5000U , . can_data_speed = 20000U , . canfd_auto = false , . canfd_enabled = false , . brs_enabled = false , . canfd_non_iso = false } ,
{ . bus_lookup = 2U , . can_num_lookup = 2U , . forwarding_bus = - 1 , . can_speed = 5000U , . can_data_speed = 20000U , . canfd_auto = false , . canfd_enabled = false , . brs_enabled = false , . canfd_non_iso = false } ,
{ . bus_lookup = 0xFFU , . can_num_lookup = 0xFFU , . forwarding_bus = - 1 , . can_speed = 333U , . can_data_speed = 333U , . canfd_auto = false , . canfd_enabled = false , . brs_enabled = false , . canfd_non_iso = false } ,
} ;
void can_init_all ( void ) {
for ( uint8_t i = 0U ; i < PANDA_CAN_CNT ; i + + ) {
if ( ! current_board - > has_canfd ) {
bus_config [ i ] . can_data_speed = 0U ;
}
can_clear ( can_queues [ i ] ) ;
( void ) can_init ( i ) ;
}
}
void can_set_orientation ( bool flipped ) {
bus_config [ 0 ] . bus_lookup = flipped ? 2U : 0U ;
bus_config [ 0 ] . can_num_lookup = flipped ? 2U : 0U ;
bus_config [ 2 ] . bus_lookup = flipped ? 0U : 2U ;
bus_config [ 2 ] . can_num_lookup = flipped ? 0U : 2U ;
}
# ifdef PANDA_JUNGLE
void can_set_forwarding ( uint8_t from , uint8_t to ) {
bus_config [ from ] . forwarding_bus = to ;
}
# endif
void ignition_can_hook ( CANPacket_t * to_push ) {
int bus = GET_BUS ( to_push ) ;
if ( bus = = 0 ) {
int addr = GET_ADDR ( to_push ) ;
int len = GET_LEN ( to_push ) ;
// GM exception
if ( ( addr = = 0x1F1 ) & & ( len = = 8 ) ) {
// SystemPowerMode (2=Run, 3=Crank Request)
ignition_can = ( GET_BYTE ( to_push , 0 ) & 0x2U ) ! = 0U ;
ignition_can_cnt = 0U ;
}
// Rivian R1S/T GEN1 exception
if ( ( addr = = 0x152 ) & & ( len = = 8 ) ) {
// 0x152 overlaps with Subaru pre-global which has this bit as the high beam
int counter = GET_BYTE ( to_push , 1 ) & 0xFU ; // max is only 14
static int prev_counter_rivian = - 1 ;
if ( ( counter = = ( ( prev_counter_rivian + 1 ) % 15 ) ) & & ( prev_counter_rivian ! = - 1 ) ) {
// VDM_OutputSignals->VDM_EpasPowerMode
ignition_can = ( ( GET_BYTE ( to_push , 7 ) > > 4U ) & 0x3U ) = = 1U ; // VDM_EpasPowerMode_Drive_On=1
ignition_can_cnt = 0U ;
}
prev_counter_rivian = counter ;
}
// Tesla Model 3/Y exception
if ( ( addr = = 0x221 ) & & ( len = = 8 ) ) {
// 0x221 overlaps with Rivian which has random data on byte 0
int counter = GET_BYTE ( to_push , 6 ) > > 4 ;
static int prev_counter_tesla = - 1 ;
if ( ( counter = = ( ( prev_counter_tesla + 1 ) % 16 ) ) & & ( prev_counter_tesla ! = - 1 ) ) {
// VCFRONT_LVPowerState->VCFRONT_vehiclePowerState
int power_state = ( GET_BYTE ( to_push , 0 ) > > 5U ) & 0x3U ;
ignition_can = power_state = = 0x3 ; // VEHICLE_POWER_STATE_DRIVE=3
ignition_can_cnt = 0U ;
}
prev_counter_tesla = counter ;
}
// Mazda exception
if ( ( addr = = 0x9E ) & & ( len = = 8 ) ) {
ignition_can = ( GET_BYTE ( to_push , 0 ) > > 5 ) = = 0x6U ;
ignition_can_cnt = 0U ;
}
}
}
bool can_tx_check_min_slots_free ( uint32_t min ) {
return
( can_slots_empty ( & can_tx1_q ) > = min ) & &
( can_slots_empty ( & can_tx2_q ) > = min ) & &
( can_slots_empty ( & can_tx3_q ) > = min ) ;
}
uint8_t calculate_checksum ( const uint8_t * dat , uint32_t len ) {
uint8_t checksum = 0U ;
for ( uint32_t i = 0U ; i < len ; i + + ) {
checksum ^ = dat [ i ] ;
}
return checksum ;
}
void can_set_checksum ( CANPacket_t * packet ) {
packet - > checksum = 0U ;
packet - > checksum = calculate_checksum ( ( uint8_t * ) packet , CANPACKET_HEAD_SIZE + GET_LEN ( packet ) ) ;
}
bool can_check_checksum ( CANPacket_t * packet ) {
return ( calculate_checksum ( ( uint8_t * ) packet , CANPACKET_HEAD_SIZE + GET_LEN ( packet ) ) = = 0U ) ;
}
void can_send ( CANPacket_t * to_push , uint8_t bus_number , bool skip_tx_hook ) {
if ( skip_tx_hook | | safety_tx_hook ( to_push ) ! = 0 ) {
if ( bus_number < PANDA_BUS_CNT ) {
// add CAN packet to send queue
tx_buffer_overflow + = can_push ( can_queues [ bus_number ] , to_push ) ? 0U : 1U ;
process_can ( CAN_NUM_FROM_BUS_NUM ( bus_number ) ) ;
}
} else {
safety_tx_blocked + = 1U ;
to_push - > returned = 0U ;
to_push - > rejected = 1U ;
// data changed
can_set_checksum ( to_push ) ;
rx_buffer_overflow + = can_push ( & can_rx_q , to_push ) ? 0U : 1U ;
}
}
bool is_speed_valid ( uint32_t speed , const uint32_t * all_speeds , uint8_t len ) {
bool ret = false ;
for ( uint8_t i = 0U ; i < len ; i + + ) {
if ( all_speeds [ i ] = = speed ) {
ret = true ;
}
}
return ret ;
}