#pragma once #include "opendbc/safety/modes/tesla_preap_pedal_transport.h" static bool panda_ignition_line(void) { #ifdef PANDA_IGNORE_IGNITION_LINE return false; #else return harness_check_ignition(); #endif } extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used // Prototypes void set_safety_mode(uint16_t mode, uint16_t param); bool is_car_safety_mode(uint16_t mode); static int get_health_pkt(void *dat) { COMPILE_TIME_ASSERT(sizeof(struct health_t) <= USBPACKET_MAX_SIZE); struct health_t * health = (struct health_t*)dat; health->uptime_pkt = uptime_cnt; health->voltage_pkt = (uint16_t)MIN(current_board->read_voltage_mV(), 0xFFFFU); health->current_pkt = (uint16_t)MIN(current_board->read_current_mA(), 0xFFFFU); health->flags_pkt = 0U; health->flags_pkt |= panda_ignition_line() ? HEALTH_FLAG_IGNITION_LINE : 0U; health->flags_pkt |= ignition_can ? HEALTH_FLAG_IGNITION_CAN : 0U; health->flags_pkt |= controls_allowed ? HEALTH_FLAG_CONTROLS_ALLOWED : 0U; health->flags_pkt |= power_save_enabled ? HEALTH_FLAG_POWER_SAVE_ENABLED : 0U; health->flags_pkt |= heartbeat_lost ? HEALTH_FLAG_HEARTBEAT_LOST : 0U; health->flags_pkt |= safety_rx_checks_invalid ? HEALTH_FLAG_SAFETY_RX_CHECKS_INVALID : 0U; health->flags_pkt |= bootkick_reset_triggered ? HEALTH_FLAG_SOM_RESET_TRIGGERED : 0U; health->flags_pkt |= reset_flags; health->safety_tx_blocked_pkt = safety_tx_blocked; health->safety_rx_invalid_pkt = safety_rx_invalid; health->tx_buffer_overflow_pkt = tx_buffer_overflow; health->rx_buffer_overflow_pkt = rx_buffer_overflow; health->car_harness_status_pkt = harness.status; health->safety_mode_pkt = (uint8_t)(current_safety_mode); health->safety_param_pkt = current_safety_param; health->alternative_experience_pkt = alternative_experience; health->spi_error_count_pkt = spi_error_count; health->fault_status_pkt = fault_status; health->faults_pkt = faults; float interrupt_load_scaled = (CLAMP(interrupt_load, 0.0f, 1.0f) * 255.0f) + 0.5f; health->interrupt_load_pkt = (uint8_t)interrupt_load_scaled; health->fan_power = fan_state.power; health->sbu1_voltage_mV = harness.sbu1_voltage_mV; health->sbu2_voltage_mV = harness.sbu2_voltage_mV; health->sound_output_level_pkt = sound_output_level; #ifdef STM32H7 float temperature_encoded = (CLAMP(dts_get_temperature(), -40.0f, 214.5f) + 40.0f) + 0.5f; health->temperature_pkt = (uint8_t)temperature_encoded; #else health->temperature_pkt = 0U; #endif return sizeof(*health); } // Endpoint 2 is only used by the bootstub for flashing. void comms_endpoint2_write(const uint8_t *data, uint32_t len) { UNUSED(data); UNUSED(len); } int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { unsigned int resp_len = 0; uint32_t time; switch (req->request) { // Fork-local frozen Dom Pre-AP calibration. Configuration is never CAN TX. case 0xb7: (void)tesla_preap_pedal_config_word(req->param1, req->param2, !is_car_safety_mode(current_safety_mode) && !controls_allowed && !heartbeat_engaged); break; case 0xb8: resp_len = tesla_preap_pedal_config_read(resp); break; // Fork-local read-only AOL capability and actual native permissions. // Older firmware returns no matching packet, so the host fails closed. case 0xd5: { aol_safety_health_t status = { .magic = AOL_SAFETY_PROTOCOL_MAGIC, .version = AOL_SAFETY_PROTOCOL_VERSION, .request_mask = aol_get_request_mask(), .permission_mask = aol_get_permission_mask(), .safety_mode = (uint8_t)current_safety_mode, .safety_param = current_safety_param, .capability_flags = (aol_policy != NULL) ? (0x1U | (aol_optional_release_capable ? 0x2U : 0U)) : 0U, }; for (unsigned int i = 0U; i < sizeof(status); i++) { resp[i] = ((uint8_t *)&status)[i]; } resp_len = sizeof(status); } break; // **** 0xa8: get microsecond timer case 0xa8: time = microsecond_timer_get(); resp[0] = (time & 0x000000FFU); resp[1] = ((time & 0x0000FF00U) >> 8U); resp[2] = ((time & 0x00FF0000U) >> 16U); resp[3] = ((time & 0xFF000000U) >> 24U); resp_len = 4U; break; // **** 0xb0: set IR power case 0xb0: current_board->set_ir_power(req->param1); break; // **** 0xb1: set fan power case 0xb1: fan_set_power(req->param1); break; // **** 0xb2: get fan rpm case 0xb2: resp[0] = (fan_state.rpm & 0x00FFU); resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U); resp_len = 2; break; // **** 0xb5: request deep sleep, wakes on CAN or SBU #if defined(ALLOW_DEBUG) && defined(STM32H7) case 0xb5: set_safety_mode(SAFETY_SILENT, 0U); set_power_save_state(true); stop_mode_requested = true; break; #endif // **** 0xb6: read debug logs case 0xb6: while ((resp_len < req->length) && (resp_len < USBPACKET_MAX_SIZE) && debug_get_char((char*)&resp[resp_len])) { ++resp_len; } break; // **** 0xc0: reset communications state case 0xc0: comms_can_reset(); break; // **** 0xc1: get hardware type case 0xc1: resp[0] = hw_type; resp_len = 1; break; // **** 0xc2: CAN health stats case 0xc2: COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); if (req->param1 < 3U) { update_can_health_pkt(req->param1, 0U); can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; resp_len = sizeof(can_health[req->param1]); (void)memcpy(resp, (uint8_t*)(&can_health[req->param1]), resp_len); } break; // **** 0xc3: fetch MCU UID case 0xc3: (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); resp_len = 12; break; // **** 0xc4: get interrupt call rate case 0xc4: if (req->param1 < NUM_INTERRUPTS) { uint32_t load = interrupts[req->param1].call_rate; resp[0] = (load & 0x000000FFU); resp[1] = ((load & 0x0000FF00U) >> 8U); resp[2] = ((load & 0x00FF0000U) >> 16U); resp[3] = ((load & 0xFF000000U) >> 24U); resp_len = 4U; } break; // **** 0xc5: DEBUG: drive relay case 0xc5: set_intercept_relay((req->param1 & 0x1U), (req->param1 & 0x2U)); break; // **** 0xc6: DEBUG: read SOM GPIO case 0xc6: resp[0] = current_board->read_som_gpio(); resp_len = 1; break; // **** 0xd0: fetch serial (aka the provisioned dongle ID) case 0xd0: // addresses are OTP if (req->param1 == 1U) { (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); resp_len = 0x10; } else { get_provision_chunk(resp); resp_len = PROVISION_CHUNK_LEN; } break; // **** 0xd1: enter bootloader mode case 0xd1: // this allows reflashing of the bootstub switch (req->param1) { case 0: // only allow bootloader entry on debug builds #ifdef ALLOW_DEBUG print("-> entering bootloader\n"); enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; NVIC_SystemReset(); #endif break; case 1: print("-> entering softloader\n"); enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; NVIC_SystemReset(); break; default: print("Bootloader mode invalid\n"); break; } break; // **** 0xd2: get health packet case 0xd2: resp_len = get_health_pkt(resp); break; // **** 0xd3: get first 64 bytes of signature case 0xd3: { resp_len = 64; char * code = (char*)_app_start; int code_len = _app_start[0]; (void)memcpy(resp, &code[code_len], resp_len); } break; // **** 0xd4: get second 64 bytes of signature case 0xd4: { resp_len = 64; char * code = (char*)_app_start; int code_len = _app_start[0]; (void)memcpy(resp, &code[code_len + 64], resp_len); } break; // **** 0xd6: get version case 0xd6: COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); (void)memcpy(resp, gitversion, sizeof(gitversion)); resp_len = sizeof(gitversion) - 1U; break; // **** 0xd8: reset ST case 0xd8: NVIC_SystemReset(); break; // **** 0xdb: set OBD CAN multiplexing mode case 0xdb: current_board->set_can_mode((req->param1 == 1U) ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL); break; // **** 0xdc: set safety mode case 0xdc: set_safety_mode(req->param1, (uint16_t)req->param2); break; // **** 0xdd: get health and CAN packet versions case 0xdd: { uint32_t versions[2] = {HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH}; (void)memcpy(resp, (uint8_t *)versions, sizeof(versions)); resp_len = sizeof(versions); break; } // **** 0xde: set can bitrate case 0xde: if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { bus_config[req->param1].can_speed = req->param2; bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); UNUSED(ret); } break; // **** 0xdf: set alternative experience case 0xdf: // you can only set this if you are in a non car safety mode if (!is_car_safety_mode(current_safety_mode)) { alternative_experience = req->param1; } break; // **** 0xe5: set CAN loopback (for testing) case 0xe5: can_loopback = req->param1 > 0U; can_init_all(); break; // **** 0xe6: set custom clock source period and pulse length case 0xe6: clock_source_set_timer_params(req->param1, req->param2); break; // **** 0xe7: set power save state case 0xe7: set_power_save_state(req->param1 != 0U); break; // **** 0xe8: set can-fd auto swithing mode case 0xe8: bus_config[req->param1].canfd_auto = req->param2 > 0U; break; // **** 0xf1: Clear CAN ring buffer. case 0xf1: if (req->param1 == 0xFFFFU) { print("Clearing CAN Rx queue\n"); can_clear(&can_rx_q); } else if (req->param1 < PANDA_CAN_CNT) { print("Clearing CAN Tx queue\n"); can_clear(can_queues[req->param1]); } else { print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); } break; // **** 0xf3: Heartbeat. Resets heartbeat counter. case 0xf3: { heartbeat_counter = 0U; heartbeat_tick_counter = 0U; heartbeat_lost = false; heartbeat_disabled = false; heartbeat_engaged = (req->param1 == 1U); break; } // **** 0xf6: set siren enabled case 0xf6: siren_enabled = (req->param1 != 0U); break; // **** 0xf8: disable heartbeat checks case 0xf8: if (!is_car_safety_mode(current_safety_mode)) { heartbeat_disabled = true; } break; // A separate short-lived axis request leaves the legacy heartbeat protocol // unchanged for every car without the exact Honda AOL safety parameter. case 0xf5: aol_set_host_request((uint8_t)req->param1); break; // **** 0xf9: set CAN FD data bitrate case 0xf9: if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { bus_config[req->param1].can_data_speed = req->param2; bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); UNUSED(ret); } break; // **** 0xfc: set CAN FD non-ISO mode case 0xfc: if (req->param1 < PANDA_CAN_CNT) { bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); UNUSED(ret); } break; default: print("NO HANDLER "); puth(req->request); print("\n"); break; } return resp_len; }