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); 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 = current_board->read_voltage_mV(); health->current_pkt = current_board->read_current_mA(); // Use the GPIO pin to determine ignition or use a CAN based logic health->ignition_line_pkt = (uint8_t)(current_board->check_ignition()); health->ignition_can_pkt = ignition_can; health->controls_allowed_pkt = controls_allowed; 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->power_save_enabled_pkt = power_save_status == POWER_SAVE_STATUS_ENABLED; health->heartbeat_lost_pkt = heartbeat_lost; health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid; health->spi_checksum_error_count_pkt = spi_checksum_error_count; health->fault_status_pkt = fault_status; health->faults_pkt = faults; health->interrupt_load_pkt = interrupt_load; health->fan_power = fan_state.power; health->fan_stall_count = fan_state.total_stall_count; health->sbu1_voltage_mV = harness.sbu1_voltage_mV; health->sbu2_voltage_mV = harness.sbu2_voltage_mV; health->som_reset_triggered = bootkick_reset_triggered; return sizeof(*health); } // send on serial, first byte to select the ring void comms_endpoint2_write(const uint8_t *data, uint32_t len) { uart_ring *ur = get_ring_by_number(data[0]); if ((len != 0U) && (ur != NULL)) { if ((data[0] < 2U) || (data[0] >= 4U)) { for (uint32_t i = 1; i < len; i++) { while (!put_char(ur, data[i])) { // wait } } } } } int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { unsigned int resp_len = 0; uart_ring *ur = NULL; uint32_t time; #ifdef DEBUG_COMMS print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); print("- request "); puth(req->request); print("\n"); print("- param1 "); puth(req->param1); print("\n"); print("- param2 "); puth(req->param2); print("\n"); #endif switch (req->request) { // **** 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; // **** 0xc0: reset communications 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, &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: if (current_board->has_obd) { if (req->param1 == 1U) { // Enable OBD CAN current_board->set_can_mode(CAN_MODE_OBD_CAN2); } else { // Disable OBD CAN current_board->set_can_mode(CAN_MODE_NORMAL); } } break; // **** 0xdc: set safety mode case 0xdc: set_safety_mode(req->param1, (uint16_t)req->param2); break; // **** 0xdd: get healthpacket and CANPacket versions case 0xdd: resp[0] = HEALTH_PACKET_VERSION; resp[1] = CAN_PACKET_VERSION; resp[2] = CAN_HEALTH_PACKET_VERSION; resp_len = 3; break; // **** 0xde: set can bitrate case 0xde: if ((req->param1 < PANDA_BUS_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; // **** 0xe0: uart read case 0xe0: ur = get_ring_by_number(req->param1); if (!ur) { break; } // read uint16_t req_length = MIN(req->length, USBPACKET_MAX_SIZE); while ((resp_len < req_length) && get_char(ur, (char*)&resp[resp_len])) { ++resp_len; } break; // **** 0xe1: uart set baud rate case 0xe1: ur = get_ring_by_number(req->param1); if (!ur) { break; } uart_set_baud(ur->uart, req->param2); break; // **** 0xe2: uart set parity case 0xe2: ur = get_ring_by_number(req->param1); if (!ur) { break; } switch (req->param2) { case 0: // disable parity, 8-bit ur->uart->CR1 &= ~(USART_CR1_PCE | USART_CR1_M); break; case 1: // even parity, 9-bit ur->uart->CR1 &= ~USART_CR1_PS; ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; break; case 2: // odd parity, 9-bit ur->uart->CR1 |= USART_CR1_PS; ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; break; default: break; } break; // **** 0xe4: uart set baud rate extended case 0xe4: ur = get_ring_by_number(req->param1); if (!ur) { break; } uart_set_baud(ur->uart, (int)req->param2*300); break; // **** 0xe5: set CAN loopback (for testing) case 0xe5: can_loopback = req->param1 > 0U; can_init_all(); break; // **** 0xe6: set custom clock source period case 0xe6: clock_source_set_period(req->param1); break; // **** 0xe7: set power save state case 0xe7: set_power_save_state(req->param1); 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_BUS_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; // **** 0xf2: Clear UART ring buffer. case 0xf2: { uart_ring * rb = get_ring_by_number(req->param1); if (rb != NULL) { print("Clearing UART queue.\n"); clear_uart_buff(rb); } break; } // **** 0xf3: Heartbeat. Resets heartbeat counter. case 0xf3: { heartbeat_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; // **** 0xf7: set green led enabled case 0xf7: green_led_enabled = (req->param1 != 0U); break; // **** 0xf8: disable heartbeat checks case 0xf8: if (!is_car_safety_mode(current_safety_mode)) { heartbeat_disabled = true; } break; // **** 0xf9: set CAN FD data bitrate case 0xf9: if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd && 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) && current_board->has_canfd) { 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; }