# include "selfdrive/boardd/panda.h"
# include <unistd.h>
# include <cassert>
# include <stdexcept>
# include "cereal/messaging/messaging.h"
# include "common/swaglog.h"
# include "common/util.h"
Panda : : Panda ( std : : string serial , uint32_t bus_offset ) : bus_offset ( bus_offset ) {
// try USB first, then SPI
try {
handle = std : : make_unique < PandaUsbHandle > ( serial ) ;
LOGW ( " connected to %s over USB " , serial . c_str ( ) ) ;
} catch ( std : : exception & e ) {
# ifndef __APPLE__
handle = std : : make_unique < PandaSpiHandle > ( serial ) ;
LOGW ( " connected to %s over SPI " , serial . c_str ( ) ) ;
# endif
}
hw_type = get_hw_type ( ) ;
has_rtc = ( hw_type = = cereal : : PandaState : : PandaType : : UNO ) | |
( hw_type = = cereal : : PandaState : : PandaType : : DOS ) | |
( hw_type = = cereal : : PandaState : : PandaType : : TRES ) ;
can_reset_communications ( ) ;
return ;
}
bool Panda : : connected ( ) {
return handle - > connected ;
}
bool Panda : : comms_healthy ( ) {
return handle - > comms_healthy ;
}
std : : string Panda : : hw_serial ( ) {
return handle - > hw_serial ;
}
std : : vector < std : : string > Panda : : list ( bool usb_only ) {
std : : vector < std : : string > serials = PandaUsbHandle : : list ( ) ;
# ifndef __APPLE__
if ( ! usb_only ) {
for ( auto s : PandaSpiHandle : : list ( ) ) {
if ( std : : find ( serials . begin ( ) , serials . end ( ) , s ) = = serials . end ( ) ) {
serials . push_back ( s ) ;
}
}
}
# endif
return serials ;
}
void Panda : : set_safety_model ( cereal : : CarParams : : SafetyModel safety_model , uint16_t safety_param ) {
handle - > control_write ( 0xdc , ( uint16_t ) safety_model , safety_param ) ;
}
void Panda : : set_alternative_experience ( uint16_t alternative_experience ) {
handle - > control_write ( 0xdf , alternative_experience , 0 ) ;
}
cereal : : PandaState : : PandaType Panda : : get_hw_type ( ) {
unsigned char hw_query [ 1 ] = { 0 } ;
handle - > control_read ( 0xc1 , 0 , 0 , hw_query , 1 ) ;
return ( cereal : : PandaState : : PandaType ) ( hw_query [ 0 ] ) ;
}
void Panda : : set_rtc ( struct tm sys_time ) {
// tm struct has year defined as years since 1900
handle - > control_write ( 0xa1 , ( uint16_t ) ( 1900 + sys_time . tm_year ) , 0 ) ;
handle - > control_write ( 0xa2 , ( uint16_t ) ( 1 + sys_time . tm_mon ) , 0 ) ;
handle - > control_write ( 0xa3 , ( uint16_t ) sys_time . tm_mday , 0 ) ;
// handle->control_write(0xa4, (uint16_t)(1 + sys_time.tm_wday), 0);
handle - > control_write ( 0xa5 , ( uint16_t ) sys_time . tm_hour , 0 ) ;
handle - > control_write ( 0xa6 , ( uint16_t ) sys_time . tm_min , 0 ) ;
handle - > control_write ( 0xa7 , ( uint16_t ) sys_time . tm_sec , 0 ) ;
}
struct tm Panda : : get_rtc ( ) {
struct __attribute__ ( ( packed ) ) timestamp_t {
uint16_t year ; // Starts at 0
uint8_t month ;
uint8_t day ;
uint8_t weekday ;
uint8_t hour ;
uint8_t minute ;
uint8_t second ;
} rtc_time = { 0 } ;
handle - > control_read ( 0xa0 , 0 , 0 , ( unsigned char * ) & rtc_time , sizeof ( rtc_time ) ) ;
struct tm new_time = { 0 } ;
new_time . tm_year = rtc_time . year - 1900 ; // tm struct has year defined as years since 1900
new_time . tm_mon = rtc_time . month - 1 ;
new_time . tm_mday = rtc_time . day ;
new_time . tm_hour = rtc_time . hour ;
new_time . tm_min = rtc_time . minute ;
new_time . tm_sec = rtc_time . second ;
return new_time ;
}
void Panda : : set_fan_speed ( uint16_t fan_speed ) {
handle - > control_write ( 0xb1 , fan_speed , 0 ) ;
}
uint16_t Panda : : get_fan_speed ( ) {
uint16_t fan_speed_rpm = 0 ;
handle - > control_read ( 0xb2 , 0 , 0 , ( unsigned char * ) & fan_speed_rpm , sizeof ( fan_speed_rpm ) ) ;
return fan_speed_rpm ;
}
void Panda : : set_ir_pwr ( uint16_t ir_pwr ) {
handle - > control_write ( 0xb0 , ir_pwr , 0 ) ;
}
std : : optional < health_t > Panda : : get_state ( ) {
health_t health { 0 } ;
int err = handle - > control_read ( 0xd2 , 0 , 0 , ( unsigned char * ) & health , sizeof ( health ) ) ;
return err > = 0 ? std : : make_optional ( health ) : std : : nullopt ;
}
std : : optional < can_health_t > Panda : : get_can_state ( uint16_t can_number ) {
can_health_t can_health { 0 } ;
int err = handle - > control_read ( 0xc2 , can_number , 0 , ( unsigned char * ) & can_health , sizeof ( can_health ) ) ;
return err > = 0 ? std : : make_optional ( can_health ) : std : : nullopt ;
}
void Panda : : set_loopback ( bool loopback ) {
handle - > control_write ( 0xe5 , loopback , 0 ) ;
}
std : : optional < std : : vector < uint8_t > > Panda : : get_firmware_version ( ) {
std : : vector < uint8_t > fw_sig_buf ( 128 ) ;
int read_1 = handle - > control_read ( 0xd3 , 0 , 0 , & fw_sig_buf [ 0 ] , 64 ) ;
int read_2 = handle - > control_read ( 0xd4 , 0 , 0 , & fw_sig_buf [ 64 ] , 64 ) ;
return ( ( read_1 = = 64 ) & & ( read_2 = = 64 ) ) ? std : : make_optional ( fw_sig_buf ) : std : : nullopt ;
}
std : : optional < std : : string > Panda : : get_serial ( ) {
char serial_buf [ 17 ] = { ' \0 ' } ;
int err = handle - > control_read ( 0xd0 , 0 , 0 , ( uint8_t * ) serial_buf , 16 ) ;
return err > = 0 ? std : : make_optional ( serial_buf ) : std : : nullopt ;
}
bool Panda : : up_to_date ( ) {
if ( auto fw_sig = get_firmware_version ( ) ) {
for ( auto fn : { " panda.bin.signed " , " panda_h7.bin.signed " } ) {
auto content = util : : read_file ( std : : string ( " ../../panda/board/obj/ " ) + fn ) ;
if ( content . size ( ) > = fw_sig - > size ( ) & &
memcmp ( content . data ( ) + content . size ( ) - fw_sig - > size ( ) , fw_sig - > data ( ) , fw_sig - > size ( ) ) = = 0 ) {
return true ;
}
}
}
return false ;
}
void Panda : : set_power_saving ( bool power_saving ) {
handle - > control_write ( 0xe7 , power_saving , 0 ) ;
}
void Panda : : enable_deepsleep ( ) {
handle - > control_write ( 0xfb , 0 , 0 ) ;
}
void Panda : : send_heartbeat ( bool engaged ) {
handle - > control_write ( 0xf3 , engaged , 0 ) ;
}
void Panda : : set_can_speed_kbps ( uint16_t bus , uint16_t speed ) {
handle - > control_write ( 0xde , bus , ( speed * 10 ) ) ;
}
void Panda : : set_data_speed_kbps ( uint16_t bus , uint16_t speed ) {
handle - > control_write ( 0xf9 , bus , ( speed * 10 ) ) ;
}
void Panda : : set_canfd_non_iso ( uint16_t bus , bool non_iso ) {
handle - > control_write ( 0xfc , bus , non_iso ) ;
}
static uint8_t len_to_dlc ( uint8_t len ) {
if ( len < = 8 ) {
return len ;
}
if ( len < = 24 ) {
return 8 + ( ( len - 8 ) / 4 ) + ( ( len % 4 ) ? 1 : 0 ) ;
} else {
return 11 + ( len / 16 ) + ( ( len % 16 ) ? 1 : 0 ) ;
}
}
void Panda : : pack_can_buffer ( const capnp : : List < cereal : : CanData > : : Reader & can_data_list ,
std : : function < void ( uint8_t * , size_t ) > write_func ) {
int32_t pos = 0 ;
uint8_t send_buf [ 2 * USB_TX_SOFT_LIMIT ] ;
for ( auto cmsg : can_data_list ) {
// check if the message is intended for this panda
uint8_t bus = cmsg . getSrc ( ) ;
if ( bus < bus_offset | | bus > = ( bus_offset + PANDA_BUS_CNT ) ) {
continue ;
}
auto can_data = cmsg . getDat ( ) ;
uint8_t data_len_code = len_to_dlc ( can_data . size ( ) ) ;
assert ( can_data . size ( ) < = 64 ) ;
assert ( can_data . size ( ) = = dlc_to_len [ data_len_code ] ) ;
can_header header = { } ;
header . addr = cmsg . getAddress ( ) ;
header . extended = ( cmsg . getAddress ( ) > = 0x800 ) ? 1 : 0 ;
header . data_len_code = data_len_code ;
header . bus = bus - bus_offset ;
header . checksum = 0 ;
memcpy ( & send_buf [ pos ] , ( uint8_t * ) & header , sizeof ( can_header ) ) ;
memcpy ( & send_buf [ pos + sizeof ( can_header ) ] , ( uint8_t * ) can_data . begin ( ) , can_data . size ( ) ) ;
uint32_t msg_size = sizeof ( can_header ) + can_data . size ( ) ;
// set checksum
( ( can_header * ) & send_buf [ pos ] ) - > checksum = calculate_checksum ( & send_buf [ pos ] , msg_size ) ;
pos + = msg_size ;
if ( pos > = USB_TX_SOFT_LIMIT ) {
write_func ( send_buf , pos ) ;
pos = 0 ;
}
}
// send remaining packets
if ( pos > 0 ) write_func ( send_buf , pos ) ;
}
void Panda : : can_send ( capnp : : List < cereal : : CanData > : : Reader can_data_list ) {
pack_can_buffer ( can_data_list , [ = ] ( uint8_t * data , size_t size ) {
handle - > bulk_write ( 3 , data , size , 5 ) ;
} ) ;
}
bool Panda : : can_receive ( std : : vector < can_frame > & out_vec ) {
// Check if enough space left in buffer to store RECV_SIZE data
assert ( receive_buffer_size + RECV_SIZE < = sizeof ( receive_buffer ) ) ;
int recv = handle - > bulk_read ( 0x81 , & receive_buffer [ receive_buffer_size ] , RECV_SIZE ) ;
if ( ! comms_healthy ( ) ) {
return false ;
}
if ( recv = = RECV_SIZE ) {
LOGW ( " Panda receive buffer full " ) ;
}
receive_buffer_size + = recv ;
return ( recv < = 0 ) ? true : unpack_can_buffer ( receive_buffer , receive_buffer_size , out_vec ) ;
}
void Panda : : can_reset_communications ( ) {
handle - > control_write ( 0xc0 , 0 , 0 ) ;
}
bool Panda : : unpack_can_buffer ( uint8_t * data , uint32_t & size , std : : vector < can_frame > & out_vec ) {
int pos = 0 ;
while ( pos < = size - sizeof ( can_header ) ) {
can_header header ;
memcpy ( & header , & data [ pos ] , sizeof ( can_header ) ) ;
const uint8_t data_len = dlc_to_len [ header . data_len_code ] ;
if ( pos + sizeof ( can_header ) + data_len > size ) {
// we don't have all the data for this message yet
break ;
}
can_frame & canData = out_vec . emplace_back ( ) ;
canData . busTime = 0 ;
canData . address = header . addr ;
canData . src = header . bus + bus_offset ;
if ( header . rejected ) {
canData . src + = CAN_REJECTED_BUS_OFFSET ;
}
if ( header . returned ) {
canData . src + = CAN_RETURNED_BUS_OFFSET ;
}
if ( calculate_checksum ( & data [ pos ] , sizeof ( can_header ) + data_len ) ! = 0 ) {
LOGE ( " Panda CAN checksum failed " ) ;
size = 0 ;
return false ;
}
canData . dat . assign ( ( char * ) & data [ pos + sizeof ( can_header ) ] , data_len ) ;
pos + = sizeof ( can_header ) + data_len ;
}
// move the overflowing data to the beginning of the buffer for the next round
memmove ( data , & data [ pos ] , size - pos ) ;
size - = pos ;
return true ;
}
uint8_t Panda : : calculate_checksum ( uint8_t * data , uint32_t len ) {
uint8_t checksum = 0U ;
for ( uint32_t i = 0U ; i < len ; i + + ) {
checksum ^ = data [ i ] ;
}
return checksum ;
}