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 = adc_get_mV(ADCCHAN_VIN) * VIN_READOUT_DIVIDER; health->current_pkt = current_board->read_current(); // 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 = (uint8_t)(ignition_can); health->controls_allowed_pkt = controls_allowed; health->gas_interceptor_detected_pkt = gas_interceptor_detected; 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->gmlan_send_errs_pkt = gmlan_send_errs; 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 = (uint8_t)(power_save_status == POWER_SAVE_STATUS_ENABLED); health->heartbeat_lost_pkt = (uint8_t)(heartbeat_lost); health->safety_rx_checks_invalid = safety_rx_checks_invalid; health->spi_checksum_error_count = spi_checksum_error_count; health->fault_status_pkt = fault_status; health->faults_pkt = faults; health->interrupt_load = 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; return sizeof(*health); } int get_rtc_pkt(void *dat) { timestamp_t t = rtc_get_time(); (void)memcpy(dat, &t, sizeof(t)); return sizeof(t); } // send on serial, first byte to select the ring void comms_endpoint2_write(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) || safety_tx_lin_hook(data[0] - 2U, &data[1], len - 1U)) { for (uint32_t i = 1; i < len; i++) { while (!putc(ur, data[i])) { // wait } } } } } int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { unsigned int resp_len = 0; uart_ring *ur = NULL; timestamp_t t; 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) { // **** 0xa0: get rtc time case 0xa0: resp_len = get_rtc_pkt(resp); break; // **** 0xa1: set rtc year case 0xa1: t = rtc_get_time(); t.year = req->param1; rtc_set_time(t); break; // **** 0xa2: set rtc month case 0xa2: t = rtc_get_time(); t.month = req->param1; rtc_set_time(t); break; // **** 0xa3: set rtc day case 0xa3: t = rtc_get_time(); t.day = req->param1; rtc_set_time(t); break; // **** 0xa4: set rtc weekday case 0xa4: t = rtc_get_time(); t.weekday = req->param1; rtc_set_time(t); break; // **** 0xa5: set rtc hour case 0xa5: t = rtc_get_time(); t.hour = req->param1; rtc_set_time(t); break; // **** 0xa6: set rtc minute case 0xa6: t = rtc_get_time(); t.minute = req->param1; rtc_set_time(t); break; // **** 0xa7: set rtc second case 0xa7: t = rtc_get_time(); t.second = req->param1; rtc_set_time(t); 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; // **** 0xb3: set phone power case 0xb3: current_board->set_phone_power(req->param1 > 0U); 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) { 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; // **** 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; // **** 0xd9: set ESP power case 0xd9: if (req->param1 == 1U) { current_board->set_gps_mode(GPS_ENABLED); } else if (req->param1 == 2U) { current_board->set_gps_mode(GPS_BOOTMODE); } else { current_board->set_gps_mode(GPS_DISABLED); } break; // **** 0xda: reset ESP, with optional boot mode case 0xda: current_board->set_gps_mode(GPS_DISABLED); delay(1000000); if (req->param1 == 1U) { current_board->set_gps_mode(GPS_BOOTMODE); } else { current_board->set_gps_mode(GPS_ENABLED); } delay(1000000); current_board->set_gps_mode(GPS_ENABLED); break; // **** 0xdb: set GMLAN (white/grey) or OBD CAN (black) 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); } } else { if (req->param1 == 1U) { // GMLAN ON if (req->param2 == 1U) { can_set_gmlan(1); } else if (req->param2 == 2U) { can_set_gmlan(2); } else { print("Invalid bus num for GMLAN CAN set\n"); } } else { can_set_gmlan(-1); } } 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; } // TODO: Remove this again and fix boardd code to hande the message bursts instead of single chars if (ur == &uart_ring_gps) { dma_pointer_handler(ur, DMA2_Stream5->NDTR); } // read while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && getc(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; // **** 0xe7: set power save state case 0xe7: set_power_save_state(req->param1); break; // **** 0xf0: k-line/l-line wake-up pulse for KWP2000 fast initialization case 0xf0: if(current_board->has_lin) { bool k = (req->param1 == 0U) || (req->param1 == 2U); bool l = (req->param1 == 1U) || (req->param1 == 2U); if (bitbang_wakeup(k, l)) { resp_len = -1; // do not clear NAK yet (wait for bit banging to finish) } } 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; } // **** 0xf4: k-line/l-line 5 baud initialization case 0xf4: if(current_board->has_lin) { bool k = (req->param1 == 0U) || (req->param1 == 2U); bool l = (req->param1 == 1U) || (req->param1 == 2U); uint8_t five_baud_addr = (req->param2 & 0xFFU); if (bitbang_five_baud_addr(k, l, five_baud_addr)) { resp_len = -1; // do not clear NAK yet (wait for bit banging to finish) } } 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; // **** 0xfb: allow highest power saving mode (stop) to be entered case 0xfb: deepsleep_allowed = true; 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; // *** 0xfd: read logs case 0xfd: if (req->param1 == 1U) { logging_find_read_index(); } resp_len = logging_read(resp); break; default: print("NO HANDLER "); puth(req->request); print("\n"); break; } return resp_len; }