From acaf57a2081859dd64f614e6e4a8450b9fc7f06e Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 01:36:38 +0800 Subject: [PATCH 01/45] refactor: split header-only drivers into source + header files Split all header-only driver files into separate .c and .h files: - bootkick, can_common, clock_source, fake_siren, fan, fdcan - gpio, harness, interrupts, led, pwm, registers - simple_watchdog, spi, timers, uart, usb Changes: - Move function implementations to .c files - Keep function declarations and #define in .h files - Add proper include guards to all header files - Add necessary #include directives Addresses #2171 --- board/drivers/bootkick.c | 69 +++ board/drivers/bootkick.h | 70 +-- board/drivers/can_common.c | 228 +++++++++ board/drivers/can_common.h | 313 +++--------- board/drivers/clock_source.c | 39 ++ board/drivers/clock_source.h | 50 +- board/drivers/drivers.h | 119 +---- board/drivers/fake_siren.c | 109 ++++ board/drivers/fake_siren.h | 123 +---- board/drivers/fan.c | 49 ++ board/drivers/fan.h | 52 +- board/drivers/fdcan.c | 270 ++++++++++ board/drivers/fdcan.h | 277 +--------- board/drivers/gpio.c | 80 +++ board/drivers/gpio.h | 89 +--- board/drivers/harness.c | 107 ++++ board/drivers/harness.h | 111 +--- board/drivers/interrupts.c | 82 +++ board/drivers/interrupts.h | 84 +-- board/drivers/led.c | 31 ++ board/drivers/led.h | 32 +- board/drivers/pwm.c | 58 +++ board/drivers/pwm.h | 60 +-- board/drivers/registers.c | 78 +++ board/drivers/registers.h | 84 +-- board/drivers/simple_watchdog.c | 22 + board/drivers/simple_watchdog.h | 24 +- board/drivers/spi.c | 241 +++++++++ board/drivers/spi.h | 248 +-------- board/drivers/timers.c | 34 ++ board/drivers/timers.h | 36 +- board/drivers/uart.c | 139 +++++ board/drivers/uart.h | 184 ++----- board/drivers/usb.c | 877 +++++++++++++++++++++++++++++++ board/drivers/usb.h | 882 +------------------------------- 35 files changed, 2752 insertions(+), 2599 deletions(-) create mode 100644 board/drivers/bootkick.c create mode 100644 board/drivers/can_common.c create mode 100644 board/drivers/clock_source.c create mode 100644 board/drivers/fake_siren.c create mode 100644 board/drivers/fan.c create mode 100644 board/drivers/fdcan.c create mode 100644 board/drivers/gpio.c create mode 100644 board/drivers/harness.c create mode 100644 board/drivers/interrupts.c create mode 100644 board/drivers/led.c create mode 100644 board/drivers/pwm.c create mode 100644 board/drivers/registers.c create mode 100644 board/drivers/simple_watchdog.c create mode 100644 board/drivers/spi.c create mode 100644 board/drivers/timers.c create mode 100644 board/drivers/uart.c create mode 100644 board/drivers/usb.c diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c new file mode 100644 index 00000000000..4c4c64bea24 --- /dev/null +++ b/board/drivers/bootkick.c @@ -0,0 +1,69 @@ +#include "drivers/bootkick.h" +#include "board/drivers/drivers.h" + +bool bootkick_reset_triggered = false; + +void bootkick_tick(bool ignition, bool recent_heartbeat) { + static uint16_t bootkick_last_serial_ptr = 0; + static uint8_t waiting_to_boot_countdown = 0; + static uint8_t boot_reset_countdown = 0; + static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; + static bool bootkick_ign_prev = false; + static BootState boot_state = BOOT_BOOTKICK; + BootState boot_state_prev = boot_state; + const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); + + if ((ignition && !bootkick_ign_prev) || harness_inserted) { + // bootkick on rising edge of ignition or harness insertion + boot_state = BOOT_BOOTKICK; + } else if (recent_heartbeat) { + // disable bootkick once openpilot is up + boot_state = BOOT_STANDBY; + } else { + + } + + /* + Ensure SOM boots in case it goes into QDL mode. Reset behavior: + * shouldn't trigger on the first boot after power-on + * only try reset once per bootkick, i.e. don't keep trying until booted + * only try once per panda boot, since openpilot will reset panda on startup + * once BOOT_RESET is triggered, it stays until countdown is finished + */ + if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { + waiting_to_boot_countdown = 20U; + } + if (waiting_to_boot_countdown > 0U) { + bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; + if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { + waiting_to_boot_countdown = 0U; + } else { + // try a reset + if (waiting_to_boot_countdown == 1U) { + boot_reset_countdown = 5U; + } + } + } + + // handle reset state + if (boot_reset_countdown > 0U) { + boot_state = BOOT_RESET; + bootkick_reset_triggered = true; + } else { + if (boot_state == BOOT_RESET) { + boot_state = BOOT_BOOTKICK; + } + } + + // update state + bootkick_ign_prev = ignition; + bootkick_harness_status_prev = harness.status; + bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; + if (waiting_to_boot_countdown > 0U) { + waiting_to_boot_countdown--; + } + if (boot_reset_countdown > 0U) { + boot_reset_countdown--; + } + current_board->set_bootkick(boot_state); +} diff --git a/board/drivers/bootkick.h b/board/drivers/bootkick.h index 4bf990391b6..b80d0e1edeb 100644 --- a/board/drivers/bootkick.h +++ b/board/drivers/bootkick.h @@ -1,68 +1,10 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_BOOTKICK_H +#define DRIVERS_BOOTKICK_H -bool bootkick_reset_triggered = false; +#include -void bootkick_tick(bool ignition, bool recent_heartbeat) { - static uint16_t bootkick_last_serial_ptr = 0; - static uint8_t waiting_to_boot_countdown = 0; - static uint8_t boot_reset_countdown = 0; - static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; - static bool bootkick_ign_prev = false; - static BootState boot_state = BOOT_BOOTKICK; - BootState boot_state_prev = boot_state; - const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); +extern bool bootkick_reset_triggered; - if ((ignition && !bootkick_ign_prev) || harness_inserted) { - // bootkick on rising edge of ignition or harness insertion - boot_state = BOOT_BOOTKICK; - } else if (recent_heartbeat) { - // disable bootkick once openpilot is up - boot_state = BOOT_STANDBY; - } else { +void bootkick_tick(bool ignition, bool recent_heartbeat); - } - - /* - Ensure SOM boots in case it goes into QDL mode. Reset behavior: - * shouldn't trigger on the first boot after power-on - * only try reset once per bootkick, i.e. don't keep trying until booted - * only try once per panda boot, since openpilot will reset panda on startup - * once BOOT_RESET is triggered, it stays until countdown is finished - */ - if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { - waiting_to_boot_countdown = 20U; - } - if (waiting_to_boot_countdown > 0U) { - bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; - if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { - waiting_to_boot_countdown = 0U; - } else { - // try a reset - if (waiting_to_boot_countdown == 1U) { - boot_reset_countdown = 5U; - } - } - } - - // handle reset state - if (boot_reset_countdown > 0U) { - boot_state = BOOT_RESET; - bootkick_reset_triggered = true; - } else { - if (boot_state == BOOT_RESET) { - boot_state = BOOT_BOOTKICK; - } - } - - // update state - bootkick_ign_prev = ignition; - bootkick_harness_status_prev = harness.status; - bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; - if (waiting_to_boot_countdown > 0U) { - waiting_to_boot_countdown--; - } - if (boot_reset_countdown > 0U) { - boot_reset_countdown--; - } - current_board->set_bootkick(boot_state); -} +#endif diff --git a/board/drivers/can_common.c b/board/drivers/can_common.c new file mode 100644 index 00000000000..cf65b71411a --- /dev/null +++ b/board/drivers/can_common.c @@ -0,0 +1,228 @@ +#include "drivers/can_common.h" +#include "board/drivers/drivers.h" +#include "board/drivers/interrupts.h" +#include "board/drivers/registers.h" +#include "board/drivers/fdcan.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[PANDA_CAN_CNT] = {{0}, {0}, {0}}; + +bool ignition_can = false; +uint32_t ignition_can_cnt = 0U; + +bool can_silent = true; +bool can_loopback = false; + +#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) }; + +#ifdef STM32H7 +__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) + +can_ring *can_queues[PANDA_CAN_CNT] = {&can_tx1_q, &can_tx2_q, &can_tx3_q}; + +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(); + refresh_can_tx_slots_available(); +} + +bus_config_t bus_config[PANDA_CAN_CNT] = { + { .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 }, +}; + +void can_init_all(void) { + for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { + bus_config[i].canfd_enabled = false; + 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 *msg) { + if (msg->bus == 0U) { + int len = GET_LEN(msg); + + if ((msg->addr == 0x1F1U) && (len == 8)) { + ignition_can = (msg->data[0] & 0x2U) != 0U; + ignition_can_cnt = 0U; + } + + if ((msg->addr == 0x152U) && (len == 8)) { + int counter = msg->data[1] & 0xFU; + + static int prev_counter_rivian = -1; + if ((counter == ((prev_counter_rivian + 1) % 15)) && (prev_counter_rivian != -1)) { + ignition_can = ((msg->data[7] >> 4U) & 0x3U) == 1U; + ignition_can_cnt = 0U; + } + prev_counter_rivian = counter; + } + + if ((msg->addr == 0x221U) && (len == 8)) { + int counter = msg->data[6] >> 4; + + static int prev_counter_tesla = -1; + if ((counter == ((prev_counter_tesla + 1) % 16)) && (prev_counter_tesla != -1)) { + int power_state = (msg->data[0] >> 5U) & 0x3U; + ignition_can = power_state == 0x3; + ignition_can_cnt = 0U; + } + prev_counter_tesla = counter; + } + + if ((msg->addr == 0x9EU) && (len == 8)) { + ignition_can = (msg->data[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_CAN_CNT) { + 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; + + 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; +} diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 11524dd1426..37ba1f97eb1 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -1,257 +1,72 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_CAN_COMMON_H +#define DRIVERS_CAN_COMMON_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[PANDA_CAN_CNT] = {{0}, {0}, {0}}; - -// Ignition detected from CAN meessages -bool ignition_can = false; -uint32_t ignition_can_cnt = 0U; - -bool can_silent = true; -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) }; +#include +#include +#include "board/can.h" +#include "board/health.h" #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 // kept for PC -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[PANDA_CAN_CNT] = {&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[PANDA_CAN_CNT] = { - { .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 }, -}; - -void can_init_all(void) { - for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { - bus_config[i].canfd_enabled = false; - 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; -} - +#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU) +#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U)) + +typedef struct { + volatile uint32_t w_ptr; + volatile uint32_t r_ptr; + uint32_t fifo_size; + CANPacket_t *elems; +} can_ring; + +typedef struct { + uint8_t bus_lookup; + uint8_t can_num_lookup; + int8_t forwarding_bus; + uint32_t can_speed; + uint32_t can_data_speed; + bool canfd_auto; + bool canfd_enabled; + bool brs_enabled; + bool canfd_non_iso; +} bus_config_t; + +extern uint32_t safety_tx_blocked; +extern uint32_t safety_rx_invalid; +extern uint32_t tx_buffer_overflow; +extern uint32_t rx_buffer_overflow; + +extern can_health_t can_health[PANDA_CAN_CNT]; + +extern bool ignition_can; +extern uint32_t ignition_can_cnt; + +extern bool can_silent; +extern bool can_loopback; + +extern can_ring *can_queues[PANDA_CAN_CNT]; +extern bus_config_t bus_config[PANDA_CAN_CNT]; + +#define CANIF_FROM_CAN_NUM(num) (cans[num]) +#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup) +#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup) + +bool can_pop(can_ring *q, CANPacket_t *elem); +bool can_push(can_ring *q, const CANPacket_t *elem); +uint32_t can_slots_empty(const can_ring *q); +void can_clear(can_ring *q); + +void can_init_all(void); +void can_set_orientation(bool flipped); #ifdef PANDA_JUNGLE -void can_set_forwarding(uint8_t from, uint8_t to) { - bus_config[from].forwarding_bus = to; -} +void can_set_forwarding(uint8_t from, uint8_t to); #endif +void ignition_can_hook(CANPacket_t *to_push); +bool can_tx_check_min_slots_free(uint32_t min); +uint8_t calculate_checksum(const uint8_t *dat, uint32_t len); +void can_set_checksum(CANPacket_t *packet); +bool can_check_checksum(CANPacket_t *packet); +void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook); +bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len); -void ignition_can_hook(CANPacket_t *msg) { - if (msg->bus == 0U) { - int len = GET_LEN(msg); - - // GM exception - if ((msg->addr == 0x1F1U) && (len == 8)) { - // SystemPowerMode (2=Run, 3=Crank Request) - ignition_can = (msg->data[0] & 0x2U) != 0U; - ignition_can_cnt = 0U; - } - - // Rivian R1S/T GEN1 exception - if ((msg->addr == 0x152U) && (len == 8)) { - // 0x152 overlaps with Subaru pre-global which has this bit as the high beam - int counter = msg->data[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 = ((msg->data[7] >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1 - ignition_can_cnt = 0U; - } - prev_counter_rivian = counter; - } - - // Tesla Model 3/Y exception - if ((msg->addr == 0x221U) && (len == 8)) { - // 0x221 overlaps with Rivian which has random data on byte 0 - int counter = msg->data[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 = (msg->data[0] >> 5U) & 0x3U; - ignition_can = power_state == 0x3; // VEHICLE_POWER_STATE_DRIVE=3 - ignition_can_cnt = 0U; - } - prev_counter_tesla = counter; - } - - // Mazda exception - if ((msg->addr == 0x9EU) && (len == 8)) { - ignition_can = (msg->data[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_CAN_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; -} +#endif diff --git a/board/drivers/clock_source.c b/board/drivers/clock_source.c new file mode 100644 index 00000000000..27954ae18ca --- /dev/null +++ b/board/drivers/clock_source.c @@ -0,0 +1,39 @@ +#include "drivers/clock_source.h" +#include "board/drivers/drivers.h" +#include "board/drivers/registers.h" +#include "board/drivers/gpio.h" + +void clock_source_set_timer_params(uint16_t param1, uint16_t param2) { + register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); + register_set(&(TIM1->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); +} + +void clock_source_init(bool enable_channel1) { + register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); + register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); + register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); + register_set(&(TIM1->CCER), TIM_CCER_CC1E, 0xFFFFU); + register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); + register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); + register_set(&(TIM1->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); + register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); + register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); + + NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_DisableIRQ(TIM1_CC_IRQn); + + if (enable_channel1) { + set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); + } + set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); + set_gpio_alternate(GPIOB, 15, GPIO_AF1_TIM1); + + register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); + register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); + + register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + register_set_bits(&(TIM1->CCER), TIM_CCER_CC2NE | TIM_CCER_CC3NE); +} diff --git a/board/drivers/clock_source.h b/board/drivers/clock_source.h index 4e7ed72ca4e..28af08c18aa 100644 --- a/board/drivers/clock_source.h +++ b/board/drivers/clock_source.h @@ -1,47 +1,13 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_CLOCK_SOURCE_H +#define DRIVERS_CLOCK_SOURCE_H + +#include +#include #define CLOCK_SOURCE_PERIOD_MS 50U #define CLOCK_SOURCE_PULSE_LEN_MS 2U -void clock_source_set_timer_params(uint16_t param1, uint16_t param2) { - // Pulse length of each channel - register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); - register_set(&(TIM1->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - // Timer period - register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); -} - -void clock_source_init(bool enable_channel1) { - // Setup timer - register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms - register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period - register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare - register_set(&(TIM1->CCER), TIM_CCER_CC1E, 0xFFFFU); // Enable compare 1 - register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value - register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value - register_set(&(TIM1->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value - register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts - register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); // Enable timer - - // No interrupts - NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); - NVIC_DisableIRQ(TIM1_CC_IRQn); - - // Set GPIO as timer channels - if (enable_channel1) { - set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); - } - set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); - set_gpio_alternate(GPIOB, 15, GPIO_AF1_TIM1); - - // Set PWM mode - register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); - register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); - - // Enable output - register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); +void clock_source_set_timer_params(uint16_t param1, uint16_t param2); +void clock_source_init(bool enable_channel1); - // Enable complementary compares - register_set_bits(&(TIM1->CCER), TIM_CCER_CC2NE | TIM_CCER_CC3NE); -} +#endif diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index c61506707a0..4242bfe6ccd 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -14,78 +14,10 @@ extern bool bootkick_reset_triggered; void bootkick_tick(bool ignition, bool recent_heartbeat); // ******************** can_common ******************** - -typedef struct { - volatile uint32_t w_ptr; - volatile uint32_t r_ptr; - uint32_t fifo_size; - CANPacket_t *elems; -} can_ring; - -typedef struct { - uint8_t bus_lookup; - uint8_t can_num_lookup; - int8_t forwarding_bus; - uint32_t can_speed; - uint32_t can_data_speed; - bool canfd_auto; - bool canfd_enabled; - bool brs_enabled; - bool canfd_non_iso; -} bus_config_t; - -extern uint32_t safety_tx_blocked; -extern uint32_t safety_rx_invalid; -extern uint32_t tx_buffer_overflow; -extern uint32_t rx_buffer_overflow; - -extern can_health_t can_health[PANDA_CAN_CNT]; - -// Ignition detected from CAN meessages -extern bool ignition_can; -extern uint32_t ignition_can_cnt; - -extern bool can_silent; -extern bool can_loopback; - -// ******************* functions prototypes ********************* -bool can_init(uint8_t can_number); -void process_can(uint8_t can_number); - -// ********************* instantiate queues ********************* -extern can_ring *can_queues[PANDA_CAN_CNT]; - -// helpers -#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU) -#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U)) - -// ********************* interrupt safe queue ********************* -bool can_pop(can_ring *q, CANPacket_t *elem); -bool can_push(can_ring *q, const CANPacket_t *elem); -uint32_t can_slots_empty(const can_ring *q); -extern bus_config_t bus_config[PANDA_CAN_CNT]; - -#define CANIF_FROM_CAN_NUM(num) (cans[num]) -#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup) -#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup) - -void can_init_all(void); -void can_set_orientation(bool flipped); -#ifdef PANDA_JUNGLE -void can_set_forwarding(uint8_t from, uint8_t to); -#endif -void ignition_can_hook(CANPacket_t *to_push); -bool can_tx_check_min_slots_free(uint32_t min); -uint8_t calculate_checksum(const uint8_t *dat, uint32_t len); -void can_set_checksum(CANPacket_t *packet); -bool can_check_checksum(CANPacket_t *packet); -void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook); -bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len); +// Type definitions and function declarations moved to drivers/can_common.h // ******************** clock_source ******************** - -void clock_source_set_timer_params(uint16_t param1, uint16_t param2); -void clock_source_init(bool enable_channel1); +// Function declarations moved to drivers/clock_source.h // ******************** fan ******************** @@ -238,50 +170,7 @@ void spi_rx_done(void); void spi_tx_done(bool reset); // ******************** uart ******************** -#ifdef STM32H7 - -// ***************************** Definitions ***************************** -#define FIFO_SIZE_INT 0x400U - -typedef struct uart_ring { - volatile uint16_t w_ptr_tx; - volatile uint16_t r_ptr_tx; - uint8_t *elems_tx; - uint32_t tx_fifo_size; - volatile uint16_t w_ptr_rx; - volatile uint16_t r_ptr_rx; - uint8_t *elems_rx; - uint32_t rx_fifo_size; - USART_TypeDef *uart; - void (*callback)(struct uart_ring*); - bool overwrite; -} uart_ring; - -// ***************************** Function prototypes ***************************** -void debug_ring_callback(uart_ring *ring); -void uart_tx_ring(uart_ring *q); -uart_ring *get_ring_by_number(int a); -// ************************* Low-level buffer functions ************************* -bool get_char(uart_ring *q, char *elem); -bool injectc(uart_ring *q, char elem); -bool put_char(uart_ring *q, char elem); -void clear_uart_buff(uart_ring *q); -// ************************ High-level debug functions ********************** -void putch(const char a); -void print(const char *a); -void puthx(uint32_t i, uint8_t len); -void puth(unsigned int i); -#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) -static void puth4(unsigned int i); -#endif -#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) -static void hexdump(const void *a, int l); -#endif - -#endif // STM32H7 +// Type definitions and function declarations moved to drivers/uart.h // ******************** usb ******************** - -void usb_init(void); -void refresh_can_tx_slots_available(void); -void can_tx_comms_resume_usb(void); +// Function declarations moved to drivers/usb.h diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c new file mode 100644 index 00000000000..599f6c874db --- /dev/null +++ b/board/drivers/fake_siren.c @@ -0,0 +1,109 @@ +#include "drivers/fake_siren.h" +#include "board/drivers/drivers.h" +#include "board/drivers/registers.h" +#include "board/stm32h7/lli2c.h" + +void siren_tim7_init(void) { + register_set(&TIM7->PSC, 0U, 0xFFFFU); + register_set(&TIM7->ARR, 133U, 0xFFFFU); + register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); + TIM7->SR = 0U; + TIM7->CR1 |= TIM_CR1_CEN; +} + +void siren_dac_init(void) { + DAC1->DHR8R1 = (1U << 7); + DAC1->DHR8R2 = (1U << 7); + register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); +} + +void siren_dma_init(void) { + static const uint8_t fake_siren_lut[360] = { 134U, 135U, 137U, 138U, 139U, 140U, 141U, 143U, 144U, 145U, 146U, 148U, 149U, 150U, 151U, 152U, 154U, 155U, 156U, 157U, 158U, 159U, 160U, 162U, 163U, 164U, 165U, 166U, 167U, 168U, 169U, 170U, 171U, 172U, 174U, 175U, 176U, 177U, 177U, 178U, 179U, 180U, 181U, 182U, 183U, 184U, 185U, 186U, 186U, 187U, 188U, 189U, 190U, 190U, 191U, 192U, 193U, 193U, 194U, 195U, 195U, 196U, 196U, 197U, 197U, 198U, 199U, 199U, 199U, 200U, 200U, 201U, 201U, 202U, 202U, 202U, 203U, 203U, 203U, 203U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 205U, 205U, 205U, 205U, 205U, 205U, 205U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 203U, 203U, 203U, 203U, 202U, 202U, 202U, 201U, 201U, 200U, 200U, 199U, 199U, 199U, 198U, 197U, 197U, 196U, 196U, 195U, 195U, 194U, 193U, 193U, 192U, 191U, 190U, 190U, 189U, 188U, 187U, 186U, 186U, 185U, 184U, 183U, 182U, 181U, 180U, 179U, 178U, 177U, 177U, 176U, 175U, 174U, 172U, 171U, 170U, 169U, 168U, 167U, 166U, 165U, 164U, 163U, 162U, 160U, 159U, 158U, 157U, 156U, 155U, 154U, 152U, 151U, 150U, 149U, 148U, 146U, 145U, 144U, 143U, 141U, 140U, 139U, 138U, 137U, 135U, 134U, 133U, 132U, 130U, 129U, 128U, 127U, 125U, 124U, 123U, 122U, 121U, 119U, 118U, 117U, 116U, 115U, 113U, 112U, 111U, 110U, 109U, 108U, 106U, 105U, 104U, 103U, 102U, 101U, 100U, 99U, 98U, 97U, 96U, 95U, 94U, 93U, 92U, 91U, 90U, 89U, 88U, 87U, 86U, 85U, 84U, 83U, 82U, 82U, 81U, 80U, 79U, 78U, 78U, 77U, 76U, 76U, 75U, 74U, 74U, 73U, 72U, 72U, 71U, 71U, 70U, 70U, 69U, 69U, 68U, 68U, 67U, 67U, 67U, 66U, 66U, 66U, 65U, 65U, 65U, 65U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 63U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 65U, 65U, 65U, 65U, 66U, 66U, 66U, 67U, 67U, 67U, 68U, 68U, 69U, 69U, 70U, 70U, 71U, 71U, 72U, 72U, 73U, 74U, 74U, 75U, 76U, 76U, 77U, 78U, 78U, 79U, 80U, 81U, 82U, 82U, 83U, 84U, 85U, 86U, 87U, 88U, 89U, 90U, 91U, 92U, 93U, 94U, 95U, 96U, 97U, 98U, 99U, 100U, 101U, 102U, 103U, 104U, 105U, 106U, 108U, 109U, 110U, 111U, 112U, 113U, 115U, 116U, 117U, 118U, 119U, 121U, 122U, 123U, 124U, 125U, 127U, 128U, 129U, 130U, 132U, 133U }; + register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); + register_set(&DMA1_Stream1->PAR, (uint32_t)&(DAC1->DHR8R1), 0xFFFFFFFFU); + register_set(&DMA1_Stream1->M0AR, (uint32_t)fake_siren_lut, 0xFFFFFFFFU); + register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); + DMA1_Stream1->NDTR = sizeof(fake_siren_lut); + DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | (1U << DMA_SxCR_DIR_Pos); +} + +void fake_siren_codec_enable(bool enabled) { + if (enabled) { + bool success = true; + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2B, (1U << 1)); + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2C, (1U << 1)); + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3D, 0x17, 0b11111); + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3E, 0x17, 0b11111); + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x37, 0b101, 0b111); + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7)); + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x51, (1U << 7)); + if (!success) { + print("Siren codec enable failed\n"); + fault_occurred(FAULT_SIREN_MALFUNCTION); + } + } else { + for (uint8_t i=0U; i<10U; i++) { + if (i2c_clear_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7))) { + break; + } + } + } +} + +static void fake_i2c_siren_init(void) { + siren_dac_init(); + siren_dma_init(); + siren_tim7_init(); + i2c_init(I2C5); + fake_siren_codec_enable(false); +} + +void fake_i2c_siren_set(bool enabled) { + static bool initialized = false; + static bool fake_siren_enabled = false; + + if (!initialized) { + fake_i2c_siren_init(); + initialized = true; + } + + if (enabled != fake_siren_enabled) { + fake_siren_codec_enable(enabled); + } + + if (enabled) { + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } else { + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } + fake_siren_enabled = enabled; +} + +void fake_siren_set(bool enabled) { + static bool initialized = false; + static bool fake_siren_enabled = false; + + if (!initialized) { + siren_tim7_init(); + initialized = true; + } + + if (enabled != fake_siren_enabled) { + if (enabled) { + sound_stop_dac(); + siren_dac_init(); + siren_dma_init(); + current_board->set_amp_enabled(true); + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } else { + current_board->set_amp_enabled(false); + sound_stop_dac(); + sound_init_dac(); + register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); + } + fake_siren_enabled = enabled; + } +} diff --git a/board/drivers/fake_siren.h b/board/drivers/fake_siren.h index 3a4d8a36a57..2587a8a6cae 100644 --- a/board/drivers/fake_siren.h +++ b/board/drivers/fake_siren.h @@ -1,115 +1,16 @@ -#include "board/stm32h7/lli2c.h" +#ifndef DRIVERS_FAKE_SIREN_H +#define DRIVERS_FAKE_SIREN_H -#define CODEC_I2C_ADDR 0x10 - -void siren_tim7_init(void) { - // Init trigger timer (around 2.5kHz) - register_set(&TIM7->PSC, 0U, 0xFFFFU); - register_set(&TIM7->ARR, 133U, 0xFFFFU); - register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); - register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); - TIM7->SR = 0U; - TIM7->CR1 |= TIM_CR1_CEN; -} - -void siren_dac_init(void) { - DAC1->DHR8R1 = (1U << 7); - DAC1->DHR8R2 = (1U << 7); - register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); - register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); - register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); -} - -void siren_dma_init(void) { - // 1Vpp sine wave with 1V offset - static const uint8_t fake_siren_lut[360] = { 134U, 135U, 137U, 138U, 139U, 140U, 141U, 143U, 144U, 145U, 146U, 148U, 149U, 150U, 151U, 152U, 154U, 155U, 156U, 157U, 158U, 159U, 160U, 162U, 163U, 164U, 165U, 166U, 167U, 168U, 169U, 170U, 171U, 172U, 174U, 175U, 176U, 177U, 177U, 178U, 179U, 180U, 181U, 182U, 183U, 184U, 185U, 186U, 186U, 187U, 188U, 189U, 190U, 190U, 191U, 192U, 193U, 193U, 194U, 195U, 195U, 196U, 196U, 197U, 197U, 198U, 199U, 199U, 199U, 200U, 200U, 201U, 201U, 202U, 202U, 202U, 203U, 203U, 203U, 203U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 205U, 205U, 205U, 205U, 205U, 205U, 205U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 203U, 203U, 203U, 203U, 202U, 202U, 202U, 201U, 201U, 200U, 200U, 199U, 199U, 199U, 198U, 197U, 197U, 196U, 196U, 195U, 195U, 194U, 193U, 193U, 192U, 191U, 190U, 190U, 189U, 188U, 187U, 186U, 186U, 185U, 184U, 183U, 182U, 181U, 180U, 179U, 178U, 177U, 177U, 176U, 175U, 174U, 172U, 171U, 170U, 169U, 168U, 167U, 166U, 165U, 164U, 163U, 162U, 160U, 159U, 158U, 157U, 156U, 155U, 154U, 152U, 151U, 150U, 149U, 148U, 146U, 145U, 144U, 143U, 141U, 140U, 139U, 138U, 137U, 135U, 134U, 133U, 132U, 130U, 129U, 128U, 127U, 125U, 124U, 123U, 122U, 121U, 119U, 118U, 117U, 116U, 115U, 113U, 112U, 111U, 110U, 109U, 108U, 106U, 105U, 104U, 103U, 102U, 101U, 100U, 99U, 98U, 97U, 96U, 95U, 94U, 93U, 92U, 91U, 90U, 89U, 88U, 87U, 86U, 85U, 84U, 83U, 82U, 82U, 81U, 80U, 79U, 78U, 78U, 77U, 76U, 76U, 75U, 74U, 74U, 73U, 72U, 72U, 71U, 71U, 70U, 70U, 69U, 69U, 68U, 68U, 67U, 67U, 67U, 66U, 66U, 66U, 65U, 65U, 65U, 65U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 63U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 65U, 65U, 65U, 65U, 66U, 66U, 66U, 67U, 67U, 67U, 68U, 68U, 69U, 69U, 70U, 70U, 71U, 71U, 72U, 72U, 73U, 74U, 74U, 75U, 76U, 76U, 77U, 78U, 78U, 79U, 80U, 81U, 82U, 82U, 83U, 84U, 85U, 86U, 87U, 88U, 89U, 90U, 91U, 92U, 93U, 94U, 95U, 96U, 97U, 98U, 99U, 100U, 101U, 102U, 103U, 104U, 105U, 106U, 108U, 109U, 110U, 111U, 112U, 113U, 115U, 116U, 117U, 118U, 119U, 121U, 122U, 123U, 124U, 125U, 127U, 128U, 129U, 130U, 132U, 133U }; - // Setup DMAMUX (DAC_CH1_DMA as input) - register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); - register_set(&DMA1_Stream1->PAR, (uint32_t)&(DAC1->DHR8R1), 0xFFFFFFFFU); - // Setup DMA - register_set(&DMA1_Stream1->M0AR, (uint32_t)fake_siren_lut, 0xFFFFFFFFU); - register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); - DMA1_Stream1->NDTR = sizeof(fake_siren_lut); - DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | (1U << DMA_SxCR_DIR_Pos); -} - -void fake_siren_codec_enable(bool enabled) { - if (enabled) { - bool success = true; - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2B, (1U << 1)); // Left speaker mix from INA1 - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2C, (1U << 1)); // Right speaker mix from INA1 - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3D, 0x17, 0b11111); // Left speaker volume - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3E, 0x17, 0b11111); // Right speaker volume - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x37, 0b101, 0b111); // INA gain - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7)); // Enable INA - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x51, (1U << 7)); // Disable global shutdown - if (!success) { - print("Siren codec enable failed\n"); - fault_occurred(FAULT_SIREN_MALFUNCTION); - } - } else { - // Disable INA input. Make sure to retry a few times if the I2C bus is busy. - for (uint8_t i=0U; i<10U; i++) { - if (i2c_clear_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7))) { - break; - } - } - } -} - -static void fake_i2c_siren_init(void) { - siren_dac_init(); - siren_dma_init(); - siren_tim7_init(); - // Enable the I2C to the codec - i2c_init(I2C5); - fake_siren_codec_enable(false); -} +#include +#include -void fake_i2c_siren_set(bool enabled) { - static bool initialized = false; - static bool fake_siren_enabled = false; - - if (!initialized) { - fake_i2c_siren_init(); - initialized = true; - } - - if (enabled != fake_siren_enabled) { - fake_siren_codec_enable(enabled); - } - - if (enabled) { - register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } else { - register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } - fake_siren_enabled = enabled; -} - -void fake_siren_set(bool enabled) { - static bool initialized = false; - static bool fake_siren_enabled = false; +#define CODEC_I2C_ADDR 0x10 - if (!initialized) { - siren_tim7_init(); - initialized = true; - } +void siren_tim7_init(void); +void siren_dac_init(void); +void siren_dma_init(void); +void fake_siren_codec_enable(bool enabled); +void fake_i2c_siren_set(bool enabled); +void fake_siren_set(bool enabled); - if (enabled != fake_siren_enabled) { - if (enabled) { - sound_stop_dac(); - siren_dac_init(); - siren_dma_init(); - current_board->set_amp_enabled(true); - register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } else { - current_board->set_amp_enabled(false); - // Stop modified 8-bit DAC and start normal 12-bit DAC - sound_stop_dac(); - sound_init_dac(); - register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); - } - fake_siren_enabled = enabled; - } -} +#endif diff --git a/board/drivers/fan.c b/board/drivers/fan.c new file mode 100644 index 00000000000..6e3b9f8be59 --- /dev/null +++ b/board/drivers/fan.c @@ -0,0 +1,49 @@ +#include "drivers/fan.h" +#include "board/drivers/drivers.h" + +struct fan_state_t fan_state; + +static const uint8_t FAN_TICK_FREQ = 8U; + +void fan_set_power(uint8_t percentage) { + if (percentage > 0U) { + fan_state.power = CLAMP(percentage, 20U, 100U); + } else { + fan_state.power = 0U; + } +} + +void fan_init(void) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + llfan_init(); +} + +// Call this at FAN_TICK_FREQ +void fan_tick(void) { + if (current_board->has_fan) { + // Measure fan RPM + uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation + fan_state.tach_counter = 0U; + fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; + + #ifdef DEBUG_FAN + puth(fan_state.target_rpm); + print(" "); puth(fan_rpm_fast); + print(" "); puth(fan_state.power); + print("\n"); + #endif + + // Cooldown counter to prevent noise on tachometer line. + if (fan_state.power > 0U) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + } else { + if (fan_state.cooldown_counter > 0U) { + fan_state.cooldown_counter--; + } + } + + // Set PWM and enable line + pwm_set(TIM3, 3, fan_state.power); + current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); + } +} diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 389fdd3f409..90ee7c413e3 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -1,48 +1,10 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_FAN_H +#define DRIVERS_FAN_H -struct fan_state_t fan_state; +#include -static const uint8_t FAN_TICK_FREQ = 8U; +void fan_set_power(uint8_t percentage); +void fan_init(void); +void fan_tick(void); -void fan_set_power(uint8_t percentage) { - if (percentage > 0U) { - fan_state.power = CLAMP(percentage, 20U, 100U); - } else { - fan_state.power = 0U; - } -} - -void fan_init(void) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - llfan_init(); -} - -// Call this at FAN_TICK_FREQ -void fan_tick(void) { - if (current_board->has_fan) { - // Measure fan RPM - uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation - fan_state.tach_counter = 0U; - fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; - - #ifdef DEBUG_FAN - puth(fan_state.target_rpm); - print(" "); puth(fan_rpm_fast); - print(" "); puth(fan_state.power); - print("\n"); - #endif - - // Cooldown counter to prevent noise on tachometer line. - if (fan_state.power > 0U) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - } else { - if (fan_state.cooldown_counter > 0U) { - fan_state.cooldown_counter--; - } - } - - // Set PWM and enable line - pwm_set(TIM3, 3, fan_state.power); - current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); - } -} +#endif diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c new file mode 100644 index 00000000000..d5cc8fcfe95 --- /dev/null +++ b/board/drivers/fdcan.c @@ -0,0 +1,270 @@ +#include "drivers/fdcan.h" +#include "board/drivers/drivers.h" + +FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; + +static bool can_set_speed(uint8_t can_number) { + bool ret = true; + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + ret &= llcan_set_speed( + FDCANx, + bus_config[bus_number].can_speed, + bus_config[bus_number].can_data_speed, + bus_config[bus_number].canfd_non_iso, + can_loopback, + can_silent + ); + return ret; +} + +void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number) { + static uint32_t last_reset = 0U; + uint32_t time = microsecond_timer_get(); + + // Resetting CAN core is a slow blocking operation, limit frequency + if (get_ts_elapsed(time, last_reset) > 100000U) { // 10 Hz + can_health[can_number].can_core_reset_cnt += 1U; + can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset + llcan_clear_send(FDCANx); + last_reset = time; + } +} + +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { + uint8_t can_irq_number[PANDA_CAN_CNT][2] = { + { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, + { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, + { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, + }; + + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint32_t psr_reg = FDCANx->PSR; + uint32_t ecr_reg = FDCANx->ECR; + + can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos); + can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; + can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos); + can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos); + + can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos); + if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { + can_health[can_number].last_stored_error = can_health[can_number].last_error; + } + + can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos); + if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) { + can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error; + } + + can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos); + can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos); + + can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; + can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; + + if (ir_reg != 0U) { + // Clear error interrupts + FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); + can_health[can_number].total_error_cnt += 1U; + // Check for RX FIFO overflow + if ((ir_reg & (FDCAN_IR_RF0L)) != 0U) { + can_health[can_number].total_rx_lost_cnt += 1U; + } + // Cases: + // 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster + // 2. H7 gets stuck in bus off recovery state indefinitely + if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) || + ((ir_reg & FDCAN_IR_BO) != 0U)) { + can_clear_send(FDCANx, can_number); + } + } +} + +// ***************************** CAN ***************************** +// FDFDCANx_IT1 IRQ Handler (TX) +void process_can(uint8_t can_number) { + if (can_number != 0xffU) { + ENTER_CRITICAL(); + + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag + + if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0U) { + CANPacket_t to_send; + if (can_pop(can_queues[bus_number], &to_send)) { + if (can_check_checksum(&to_send)) { + can_health[can_number].total_tx_cnt += 1U; + + uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); + // get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1) + uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1FU; + // only send if we have received a packet + canfd_fifo *fifo; + fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); + + fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); + + // If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen + bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U); + uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL; + + uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; + fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; + + uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); + } + + FDCANx->TXBAR = (1UL << tx_index); + + // Send back to USB + CANPacket_t to_push; + + to_push.fd = fd; + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = to_send.extended; + to_push.addr = to_send.addr; + to_push.bus = bus_number; + to_push.data_len_code = to_send.data_len_code; + (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_push); + + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + } else { + can_health[can_number].total_tx_checksum_error_cnt += 1U; + } + + refresh_can_tx_slots_available(); + } + } + EXIT_CRITICAL(); + } +} + +// FDFDCANx_IT0 IRQ Handler (RX and errors) +// blink blue when we are receiving CAN messages +void can_rx(uint8_t can_number) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + uint32_t ir_reg = FDCANx->IR; + + // Clear all new messages from Rx FIFO 0 + FDCANx->IR |= FDCAN_IR_RF0N; + while ((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) { + can_health[can_number].total_rx_cnt += 1U; + // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) + uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU); + + // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) + if ((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { + rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); + can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost + } + + uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); + CANPacket_t to_push; + canfd_fifo *fifo; + + // getting address + fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); + + bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); + bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); + + to_push.fd = canfd_frame; + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (fifo->header[0] >> 30) & 0x1U; + to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); + to_push.bus = bus_number; + to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); + + uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]); + } + can_set_checksum(&to_push); + + // forwarding (panda only) + int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); + if (bus_fwd_num < 0) { + bus_fwd_num = bus_config[can_number].forwarding_bus; + } + if (bus_fwd_num != -1) { + CANPacket_t to_send; + + to_send.fd = to_push.fd; + to_send.returned = 0U; + to_send.rejected = 0U; + to_send.extended = to_push.extended; + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_send); + + can_send(&to_send, bus_fwd_num, true); + can_health[can_number].total_fwd_cnt += 1U; + } + + safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; + ignition_can_hook(&to_push); + + led_set(LED_BLUE, true); + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + + // Enable CAN FD and BRS if CAN FD message was received + if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) { + bus_config[can_number].canfd_enabled = true; + } + if (!(bus_config[can_number].brs_enabled) && (brs_frame)) { + bus_config[can_number].brs_enabled = true; + } + + // update read index + FDCANx->RXF0A = rx_fifo_idx; + } + + // Error handling + if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0U) { + update_can_health_pkt(can_number, ir_reg); + } +} + +static void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); } +static void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); } + +static void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); } +static void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); } + +static void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); } +static void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); } + +bool can_init(uint8_t can_number) { + bool ret = false; + + REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + + if (can_number != 0xffU) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + ret &= can_set_speed(can_number); + ret &= llcan_init(FDCANx); + // in case there are queued up messages + process_can(can_number); + } + return ret; +} diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index 4921d531a5c..dacf8dd60f8 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -1,269 +1,14 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_FDCAN_H +#define DRIVERS_FDCAN_H -FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; +#include +#include +#include "stm32h7xx_hal_fdcan.h" -static bool can_set_speed(uint8_t can_number) { - bool ret = true; - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); +void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number); +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg); +void process_can(uint8_t can_number); +void can_rx(uint8_t can_number); +bool can_init(uint8_t can_number); - ret &= llcan_set_speed( - FDCANx, - bus_config[bus_number].can_speed, - bus_config[bus_number].can_data_speed, - bus_config[bus_number].canfd_non_iso, - can_loopback, - can_silent - ); - return ret; -} - -void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number) { - static uint32_t last_reset = 0U; - uint32_t time = microsecond_timer_get(); - - // Resetting CAN core is a slow blocking operation, limit frequency - if (get_ts_elapsed(time, last_reset) > 100000U) { // 10 Hz - can_health[can_number].can_core_reset_cnt += 1U; - can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset - llcan_clear_send(FDCANx); - last_reset = time; - } -} - -void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { - uint8_t can_irq_number[PANDA_CAN_CNT][2] = { - { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, - { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, - { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, - }; - - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint32_t psr_reg = FDCANx->PSR; - uint32_t ecr_reg = FDCANx->ECR; - - can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos); - can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; - can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos); - can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos); - - can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos); - if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { - can_health[can_number].last_stored_error = can_health[can_number].last_error; - } - - can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos); - if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) { - can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error; - } - - can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos); - can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos); - - can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; - can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; - - if (ir_reg != 0U) { - // Clear error interrupts - FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); - can_health[can_number].total_error_cnt += 1U; - // Check for RX FIFO overflow - if ((ir_reg & (FDCAN_IR_RF0L)) != 0U) { - can_health[can_number].total_rx_lost_cnt += 1U; - } - // Cases: - // 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster - // 2. H7 gets stuck in bus off recovery state indefinitely - if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) || - ((ir_reg & FDCAN_IR_BO) != 0U)) { - can_clear_send(FDCANx, can_number); - } - } -} - -// ***************************** CAN ***************************** -// FDFDCANx_IT1 IRQ Handler (TX) -void process_can(uint8_t can_number) { - if (can_number != 0xffU) { - ENTER_CRITICAL(); - - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - - FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag - - if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0U) { - CANPacket_t to_send; - if (can_pop(can_queues[bus_number], &to_send)) { - if (can_check_checksum(&to_send)) { - can_health[can_number].total_tx_cnt += 1U; - - uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); - // get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1) - uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1FU; - // only send if we have received a packet - canfd_fifo *fifo; - fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); - - fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); - - // If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen - bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U); - uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL; - - uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; - fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; - - uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); - data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U; - for (unsigned int i = 0; i < data_len_w; i++) { - BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); - } - - FDCANx->TXBAR = (1UL << tx_index); - - // Send back to USB - CANPacket_t to_push; - - to_push.fd = fd; - to_push.returned = 1U; - to_push.rejected = 0U; - to_push.extended = to_send.extended; - to_push.addr = to_send.addr; - to_push.bus = bus_number; - to_push.data_len_code = to_send.data_len_code; - (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); - can_set_checksum(&to_push); - - rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; - } else { - can_health[can_number].total_tx_checksum_error_cnt += 1U; - } - - refresh_can_tx_slots_available(); - } - } - EXIT_CRITICAL(); - } -} - -// FDFDCANx_IT0 IRQ Handler (RX and errors) -// blink blue when we are receiving CAN messages -void can_rx(uint8_t can_number) { - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - - uint32_t ir_reg = FDCANx->IR; - - // Clear all new messages from Rx FIFO 0 - FDCANx->IR |= FDCAN_IR_RF0N; - while ((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) { - can_health[can_number].total_rx_cnt += 1U; - // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) - uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU); - - // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) - if ((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { - rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); - can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost - } - - uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); - CANPacket_t to_push; - canfd_fifo *fifo; - - // getting address - fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); - - bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); - bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); - - to_push.fd = canfd_frame; - to_push.returned = 0U; - to_push.rejected = 0U; - to_push.extended = (fifo->header[0] >> 30) & 0x1U; - to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); - to_push.bus = bus_number; - to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); - - uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); - data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; - for (unsigned int i = 0; i < data_len_w; i++) { - WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]); - } - can_set_checksum(&to_push); - - // forwarding (panda only) - int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); - if (bus_fwd_num < 0) { - bus_fwd_num = bus_config[can_number].forwarding_bus; - } - if (bus_fwd_num != -1) { - CANPacket_t to_send; - - to_send.fd = to_push.fd; - to_send.returned = 0U; - to_send.rejected = 0U; - to_send.extended = to_push.extended; - to_send.addr = to_push.addr; - to_send.bus = to_push.bus; - to_send.data_len_code = to_push.data_len_code; - (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); - can_set_checksum(&to_send); - - can_send(&to_send, bus_fwd_num, true); - can_health[can_number].total_fwd_cnt += 1U; - } - - safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; - ignition_can_hook(&to_push); - - led_set(LED_BLUE, true); - rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; - - // Enable CAN FD and BRS if CAN FD message was received - if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) { - bus_config[can_number].canfd_enabled = true; - } - if (!(bus_config[can_number].brs_enabled) && (brs_frame)) { - bus_config[can_number].brs_enabled = true; - } - - // update read index - FDCANx->RXF0A = rx_fifo_idx; - } - - // Error handling - if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0U) { - update_can_health_pkt(can_number, ir_reg); - } -} - -static void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); } -static void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); } - -static void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); } -static void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); } - -static void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); } -static void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); } - -bool can_init(uint8_t can_number) { - bool ret = false; - - REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) - REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) - REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) - REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) - REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) - REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) - - if (can_number != 0xffU) { - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - ret &= can_set_speed(can_number); - ret &= llcan_init(FDCANx); - // in case there are queued up messages - process_can(can_number); - } - return ret; -} +#endif diff --git a/board/drivers/gpio.c b/board/drivers/gpio.c new file mode 100644 index 00000000000..3c2e2debd42 --- /dev/null +++ b/board/drivers/gpio.c @@ -0,0 +1,80 @@ +#include "drivers/gpio.h" +#include "board/drivers/drivers.h" +#include "board/drivers/interrupts.h" +#include "board/drivers/registers.h" + +void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->MODER; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { + ENTER_CRITICAL(); + if (enabled) { + register_set_bits(&(GPIO->ODR), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->ODR), (1UL << pin)); + } + set_gpio_mode(GPIO, pin, MODE_OUTPUT); + EXIT_CRITICAL(); +} + +void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ + ENTER_CRITICAL(); + if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { + register_set_bits(&(GPIO->OTYPER), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->OTYPER), (1U << pin)); + } + EXIT_CRITICAL(); +} + +void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->AFR[pin >> 3U]; + tmp &= ~(0xFU << ((pin & 7U) * 4U)); + tmp |= mode << ((pin & 7U) * 4U); + register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU); + set_gpio_mode(GPIO, pin, MODE_ALTERNATE); + EXIT_CRITICAL(); +} + +void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->PUPDR; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin) { + return (GPIO->IDR & (1UL << pin)) == (1UL << pin); +} + +#ifdef PANDA_JUNGLE +void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, enabled); + } +} + +void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, (bitmask >> i) & 1U); + } +} +#endif + +bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { + set_gpio_mode(GPIO, pin, MODE_INPUT); + set_gpio_pullup(GPIO, pin, mode); + for (volatile int i=0; i +#include + #define MODE_INPUT 0 #define MODE_OUTPUT 1 #define MODE_ALTERNATE 2 @@ -10,58 +16,12 @@ #define OUTPUT_TYPE_PUSH_PULL 0U #define OUTPUT_TYPE_OPEN_DRAIN 1U -void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->MODER; - tmp &= ~(3U << (pin * 2U)); - tmp |= (mode << (pin * 2U)); - register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); - EXIT_CRITICAL(); -} - -void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { - ENTER_CRITICAL(); - if (enabled) { - register_set_bits(&(GPIO->ODR), (1UL << pin)); - } else { - register_clear_bits(&(GPIO->ODR), (1UL << pin)); - } - set_gpio_mode(GPIO, pin, MODE_OUTPUT); - EXIT_CRITICAL(); -} - -void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ - ENTER_CRITICAL(); - if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { - register_set_bits(&(GPIO->OTYPER), (1UL << pin)); - } else { - register_clear_bits(&(GPIO->OTYPER), (1U << pin)); - } - EXIT_CRITICAL(); -} - -void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->AFR[pin >> 3U]; - tmp &= ~(0xFU << ((pin & 7U) * 4U)); - tmp |= mode << ((pin & 7U) * 4U); - register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU); - set_gpio_mode(GPIO, pin, MODE_ALTERNATE); - EXIT_CRITICAL(); -} - -void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->PUPDR; - tmp &= ~(3U << (pin * 2U)); - tmp |= (mode << (pin * 2U)); - register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); - EXIT_CRITICAL(); -} - -int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin) { - return (GPIO->IDR & (1UL << pin)) == (1UL << pin); -} +void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled); +void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type); +void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin); #ifdef PANDA_JUNGLE typedef struct { @@ -69,26 +29,11 @@ typedef struct { uint8_t pin; } gpio_t; -void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled) { - for (uint8_t i = 0; i < num_pins; i++) { - set_gpio_output(pins[i].bank, pins[i].pin, enabled); - } -} - -void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask) { - for (uint8_t i = 0; i < num_pins; i++) { - set_gpio_output(pins[i].bank, pins[i].pin, (bitmask >> i) & 1U); - } -} +void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled); +void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask); #endif -// Detection with internal pullup #define PULL_EFFECTIVE_DELAY 4096 -bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { - set_gpio_mode(GPIO, pin, MODE_INPUT); - set_gpio_pullup(GPIO, pin, mode); - for (volatile int i=0; iharness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay); + } else { + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); + } + + if (!(drive_relay || ignition_relay)) { + harness.relay_driven = false; + } +} + +bool harness_check_ignition(void) { + bool ret = false; + + // wait until we're not reading the analog voltages anymore + while (harness.sbu_adc_lock) {} + + switch(harness.status){ + case HARNESS_STATUS_NORMAL: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); + break; + case HARNESS_STATUS_FLIPPED: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); + break; + default: + break; + } + return ret; +} + +static uint8_t harness_detect_orientation(void) { + uint8_t ret = harness.status; + + #ifndef BOOTSTUB + // We can't detect orientation if the relay is being driven + if (!harness.relay_driven) { + harness.sbu_adc_lock = true; + set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG); + set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG); + + harness.sbu1_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU1); + harness.sbu2_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU2); + uint16_t detection_threshold = current_board->avdd_mV / 2U; + + // Detect connection and orientation + if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){ + if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) { + // orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign)) + ret = HARNESS_STATUS_FLIPPED; + } else { + // orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign)) + // (SBU1->SBU2 is the normal orientation connection per USB-C cable spec) + ret = HARNESS_STATUS_NORMAL; + } + } else { + ret = HARNESS_STATUS_NC; + } + + // Pins are not 5V tolerant in ADC mode + set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); + set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT); + harness.sbu_adc_lock = false; + } + #endif + + return ret; +} + +void harness_tick(void) { + harness.status = harness_detect_orientation(); +} + +void harness_init(void) { + // init OBD_SBUx_RELAY + set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, 1); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, 1); + + // detect initial orientation + harness.status = harness_detect_orientation(); + + // keep buses connected by default + set_intercept_relay(false, false); +} diff --git a/board/drivers/harness.h b/board/drivers/harness.h index d501ab949fd..02d54883cdb 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -1,106 +1,11 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_HARNESS_H +#define DRIVERS_HARNESS_H -struct harness_t harness; +#include -// The ignition relay is only used for testing purposes -void set_intercept_relay(bool intercept, bool ignition_relay) { - bool drive_relay = intercept; - if (harness.status == HARNESS_STATUS_NC) { - // no harness, no relay to drive - drive_relay = false; - } +void set_intercept_relay(bool intercept, bool ignition_relay); +bool harness_check_ignition(void); +void harness_tick(void); +void harness_init(void); - if (drive_relay || ignition_relay) { - harness.relay_driven = true; - } - - // wait until we're not reading the analog voltages anymore - while (harness.sbu_adc_lock) {} - - if (harness.status == HARNESS_STATUS_NORMAL) { - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay); - } else { - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); - } - - if (!(drive_relay || ignition_relay)) { - harness.relay_driven = false; - } -} - -bool harness_check_ignition(void) { - bool ret = false; - - // wait until we're not reading the analog voltages anymore - while (harness.sbu_adc_lock) {} - - switch(harness.status){ - case HARNESS_STATUS_NORMAL: - ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); - break; - case HARNESS_STATUS_FLIPPED: - ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); - break; - default: - break; - } - return ret; -} - -static uint8_t harness_detect_orientation(void) { - uint8_t ret = harness.status; - - #ifndef BOOTSTUB - // We can't detect orientation if the relay is being driven - if (!harness.relay_driven) { - harness.sbu_adc_lock = true; - set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG); - set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG); - - harness.sbu1_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU1); - harness.sbu2_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU2); - uint16_t detection_threshold = current_board->avdd_mV / 2U; - - // Detect connection and orientation - if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){ - if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) { - // orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign)) - ret = HARNESS_STATUS_FLIPPED; - } else { - // orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign)) - // (SBU1->SBU2 is the normal orientation connection per USB-C cable spec) - ret = HARNESS_STATUS_NORMAL; - } - } else { - ret = HARNESS_STATUS_NC; - } - - // Pins are not 5V tolerant in ADC mode - set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); - set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT); - harness.sbu_adc_lock = false; - } - #endif - - return ret; -} - -void harness_tick(void) { - harness.status = harness_detect_orientation(); -} - -void harness_init(void) { - // init OBD_SBUx_RELAY - set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, 1); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, 1); - - // detect initial orientation - harness.status = harness_detect_orientation(); - - // keep buses connected by default - set_intercept_relay(false, false); -} +#endif diff --git a/board/drivers/interrupts.c b/board/drivers/interrupts.c new file mode 100644 index 00000000000..90a5229a5b4 --- /dev/null +++ b/board/drivers/interrupts.c @@ -0,0 +1,82 @@ +#include "drivers/interrupts.h" +#include "board/drivers/drivers.h" + +interrupt interrupts[NUM_INTERRUPTS]; + +static bool check_interrupt_rate = false; + +static uint32_t idle_time = 0U; +static uint32_t busy_time = 0U; +float interrupt_load = 0.0f; + +void unused_interrupt_handler(void) { + // Something is wrong if this handler is called! + print("Unused interrupt handler called!\n"); + fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); +} + +void handle_interrupt(IRQn_Type irq_type){ + static uint8_t interrupt_depth = 0U; + static uint32_t last_time = 0U; + ENTER_CRITICAL(); + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + idle_time += get_ts_elapsed(time, last_time); + last_time = time; + } + interrupt_depth += 1U; + EXIT_CRITICAL(); + + interrupts[irq_type].call_counter++; + interrupts[irq_type].handler(); + + // Check that the interrupts don't fire too often + if (check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)) { + fault_occurred(interrupts[irq_type].call_rate_fault); + } + + ENTER_CRITICAL(); + interrupt_depth -= 1U; + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + busy_time += get_ts_elapsed(time, last_time); + last_time = time; + } + EXIT_CRITICAL(); +} + +// Every second +void interrupt_timer_handler(void) { + if (INTERRUPT_TIMER->SR != 0U) { + for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { + // Log IRQ call rate faults + if (check_interrupt_rate && (interrupts[i].call_counter > interrupts[i].max_call_rate)) { + print("Interrupt 0x"); puth(i); print(" fired too often (0x"); puth(interrupts[i].call_counter); print("/s)!\n"); + } + + // Reset interrupt counters + interrupts[i].call_rate = interrupts[i].call_counter; + interrupts[i].call_counter = 0U; + } + + // Calculate interrupt load + // The bootstub does not have the FPU enabled, so can't do float operations. +#if !defined(BOOTSTUB) + interrupt_load = ((busy_time + idle_time) > 0U) ? ((float) (((float) busy_time) / (busy_time + idle_time))) : 0.0f; +#endif + idle_time = 0U; + busy_time = 0U; + } + INTERRUPT_TIMER->SR = 0; +} + +void init_interrupts(bool check_rate_limit){ + check_interrupt_rate = check_rate_limit; + + for(uint16_t i=0U; i -interrupt interrupts[NUM_INTERRUPTS]; +void unused_interrupt_handler(void); +void handle_interrupt(IRQn_Type irq_type); +void interrupt_timer_handler(void); +void init_interrupts(bool check_rate_limit); -static bool check_interrupt_rate = false; - -static uint32_t idle_time = 0U; -static uint32_t busy_time = 0U; -float interrupt_load = 0.0f; - -void handle_interrupt(IRQn_Type irq_type){ - static uint8_t interrupt_depth = 0U; - static uint32_t last_time = 0U; - ENTER_CRITICAL(); - if (interrupt_depth == 0U) { - uint32_t time = microsecond_timer_get(); - idle_time += get_ts_elapsed(time, last_time); - last_time = time; - } - interrupt_depth += 1U; - EXIT_CRITICAL(); - - interrupts[irq_type].call_counter++; - interrupts[irq_type].handler(); - - // Check that the interrupts don't fire too often - if (check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)) { - fault_occurred(interrupts[irq_type].call_rate_fault); - } - - ENTER_CRITICAL(); - interrupt_depth -= 1U; - if (interrupt_depth == 0U) { - uint32_t time = microsecond_timer_get(); - busy_time += get_ts_elapsed(time, last_time); - last_time = time; - } - EXIT_CRITICAL(); -} - -// Every second -void interrupt_timer_handler(void) { - if (INTERRUPT_TIMER->SR != 0U) { - for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { - // Log IRQ call rate faults - if (check_interrupt_rate && (interrupts[i].call_counter > interrupts[i].max_call_rate)) { - print("Interrupt 0x"); puth(i); print(" fired too often (0x"); puth(interrupts[i].call_counter); print("/s)!\n"); - } - - // Reset interrupt counters - interrupts[i].call_rate = interrupts[i].call_counter; - interrupts[i].call_counter = 0U; - } - - // Calculate interrupt load - // The bootstub does not have the FPU enabled, so can't do float operations. -#if !defined(BOOTSTUB) - interrupt_load = ((busy_time + idle_time) > 0U) ? ((float) (((float) busy_time) / (busy_time + idle_time))) : 0.0f; #endif - idle_time = 0U; - busy_time = 0U; - } - INTERRUPT_TIMER->SR = 0; -} - -void init_interrupts(bool check_rate_limit){ - check_interrupt_rate = check_rate_limit; - - for(uint16_t i=0U; iled_pwm_channels[color] != 0U) { + pwm_set(TIM3, current_board->led_pwm_channels[color], 100U - (enabled ? LED_PWM_POWER : 0U)); + } else { + set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); + } + } +} + +void led_init(void) { + for (uint8_t i = 0U; i<3U; i++){ + set_gpio_pullup(current_board->led_GPIO[i], current_board->led_pin[i], PULL_NONE); + set_gpio_output_type(current_board->led_GPIO[i], current_board->led_pin[i], OUTPUT_TYPE_OPEN_DRAIN); + + if (current_board->led_pwm_channels[i] != 0U) { + set_gpio_alternate(current_board->led_GPIO[i], current_board->led_pin[i], GPIO_AF2_TIM3); + pwm_init(TIM3, current_board->led_pwm_channels[i]); + } else { + set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); + } + + led_set(i, false); + } +} diff --git a/board/drivers/led.h b/board/drivers/led.h index 7cea94e854f..cdc5b35a857 100644 --- a/board/drivers/led.h +++ b/board/drivers/led.h @@ -1,3 +1,8 @@ +#ifndef DRIVERS_LED_H +#define DRIVERS_LED_H + +#include +#include #define LED_RED 0U #define LED_GREEN 1U @@ -5,28 +10,7 @@ #define LED_PWM_POWER 2U -void led_set(uint8_t color, bool enabled) { - if (color < 3U) { - if (current_board->led_pwm_channels[color] != 0U) { - pwm_set(TIM3, current_board->led_pwm_channels[color], 100U - (enabled ? LED_PWM_POWER : 0U)); - } else { - set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); - } - } -} - -void led_init(void) { - for (uint8_t i = 0U; i<3U; i++){ - set_gpio_pullup(current_board->led_GPIO[i], current_board->led_pin[i], PULL_NONE); - set_gpio_output_type(current_board->led_GPIO[i], current_board->led_pin[i], OUTPUT_TYPE_OPEN_DRAIN); - - if (current_board->led_pwm_channels[i] != 0U) { - set_gpio_alternate(current_board->led_GPIO[i], current_board->led_pin[i], GPIO_AF2_TIM3); - pwm_init(TIM3, current_board->led_pwm_channels[i]); - } else { - set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); - } +void led_set(uint8_t color, bool enabled); +void led_init(void); - led_set(i, false); - } -} +#endif diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c new file mode 100644 index 00000000000..a2d1c009b27 --- /dev/null +++ b/board/drivers/pwm.c @@ -0,0 +1,58 @@ +// PWM driver implementation +#include "drivers/pwm.h" +#include "drivers/registers.h" + +#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz + +void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ + // Enable timer and auto-reload + register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); + + // Set channel as PWM mode 1 and enable output + switch(channel){ + case 1U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); + break; + case 2U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); + break; + case 3U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); + break; + case 4U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); + break; + default: + break; + } + + // Set max counter value + register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); + + // Update registers and clear counter + TIM->EGR |= TIM_EGR_UG; +} + +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ + uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); + switch(channel){ + case 1U: + register_set(&(TIM->CCR1), comp_value, 0xFFFFU); + break; + case 2U: + register_set(&(TIM->CCR2), comp_value, 0xFFFFU); + break; + case 3U: + register_set(&(TIM->CCR3), comp_value, 0xFFFFU); + break; + case 4U: + register_set(&(TIM->CCR4), comp_value, 0xFFFFU); + break; + default: + break; + } +} diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h index 3d0d73efdc6..e9bd2b07f68 100644 --- a/board/drivers/pwm.h +++ b/board/drivers/pwm.h @@ -1,56 +1,12 @@ -#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz +#ifndef DRIVERS_PWM_H +#define DRIVERS_PWM_H -// TODO: Implement for 32-bit timers - -void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ - // Enable timer and auto-reload - register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); +#include +#include "stm32fx_def.h" - // Set channel as PWM mode 1 and enable output - switch(channel){ - case 1U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); - break; - case 2U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); - break; - case 3U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); - break; - case 4U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); - break; - default: - break; - } - - // Set max counter value - register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); +// TODO: Implement for 32-bit timers - // Update registers and clear counter - TIM->EGR |= TIM_EGR_UG; -} +void pwm_init(TIM_TypeDef *TIM, uint8_t channel); +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); -void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ - uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); - switch(channel){ - case 1U: - register_set(&(TIM->CCR1), comp_value, 0xFFFFU); - break; - case 2U: - register_set(&(TIM->CCR2), comp_value, 0xFFFFU); - break; - case 3U: - register_set(&(TIM->CCR3), comp_value, 0xFFFFU); - break; - case 4U: - register_set(&(TIM->CCR4), comp_value, 0xFFFFU); - break; - default: - break; - } -} +#endif diff --git a/board/drivers/registers.c b/board/drivers/registers.c new file mode 100644 index 00000000000..36e325b4bab --- /dev/null +++ b/board/drivers/registers.c @@ -0,0 +1,78 @@ +#include "drivers/registers.h" +#include "board/drivers/drivers.h" + +typedef struct reg { + volatile uint32_t *address; + uint32_t value; + uint32_t check_mask; + bool logged_fault; +} reg; + +#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr))) + +static reg register_map[REGISTER_MAP_SIZE]; + +// Hash spread in first and second iterations seems to be reasonable. +// See: tests/development/register_hashmap_spread.py +// Also, check the collision warnings in the debug output, and minimize those. +static uint16_t hash_addr(uint32_t input){ + return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); +} + +// Do not put bits in the check mask that get changed by the hardware +void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){ + ENTER_CRITICAL() + // Set bits in register that are also in the mask + (*addr) = ((*addr) & (~mask)) | (val & mask); + + // Add these values to the map + uint16_t hash = hash_addr((uint32_t) addr); + uint16_t tries = REGISTER_MAP_SIZE; + while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} + if (tries != 0U){ + register_map[hash].address = addr; + register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); + register_map[hash].check_mask |= mask; + } else { + #ifdef DEBUG_FAULTS + print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); + #endif + } + EXIT_CRITICAL() +} + +// Set individual bits. Also add them to the check_mask. +// Do not use this to change bits that get reset by the hardware +void register_set_bits(volatile uint32_t *addr, uint32_t val) { + register_set(addr, val, val); +} + +// Clear individual bits. Also add them to the check_mask. +// Do not use this to clear bits that get set by the hardware +void register_clear_bits(volatile uint32_t *addr, uint32_t val) { + register_set(addr, (~val), val); +} + +// To be called periodically +void check_registers(void){ + for(uint16_t i=0U; i +#include -#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr))) +void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask); +void register_set_bits(volatile uint32_t *addr, uint32_t val); +void register_clear_bits(volatile uint32_t *addr, uint32_t val); +void check_registers(void); +void init_registers(void); -static reg register_map[REGISTER_MAP_SIZE]; - -// Hash spread in first and second iterations seems to be reasonable. -// See: tests/development/register_hashmap_spread.py -// Also, check the collision warnings in the debug output, and minimize those. -static uint16_t hash_addr(uint32_t input){ - return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); -} - -// Do not put bits in the check mask that get changed by the hardware -void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){ - ENTER_CRITICAL() - // Set bits in register that are also in the mask - (*addr) = ((*addr) & (~mask)) | (val & mask); - - // Add these values to the map - uint16_t hash = hash_addr((uint32_t) addr); - uint16_t tries = REGISTER_MAP_SIZE; - while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} - if (tries != 0U){ - register_map[hash].address = addr; - register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); - register_map[hash].check_mask |= mask; - } else { - #ifdef DEBUG_FAULTS - print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); - #endif - } - EXIT_CRITICAL() -} - -// Set individual bits. Also add them to the check_mask. -// Do not use this to change bits that get reset by the hardware -void register_set_bits(volatile uint32_t *addr, uint32_t val) { - register_set(addr, val, val); -} - -// Clear individual bits. Also add them to the check_mask. -// Do not use this to clear bits that get set by the hardware -void register_clear_bits(volatile uint32_t *addr, uint32_t val) { - register_set(addr, (~val), val); -} - -// To be called periodically -void check_registers(void){ - for(uint16_t i=0U; i wd_state.threshold) { + print("WD timeout 0x"); puth(et); print("\n"); + fault_occurred(wd_state.fault); + } + + wd_state.last_ts = ts; +} + +void simple_watchdog_init(uint32_t fault, uint32_t threshold) { + wd_state.fault = fault; + wd_state.threshold = threshold; + wd_state.last_ts = microsecond_timer_get(); +} diff --git a/board/drivers/simple_watchdog.h b/board/drivers/simple_watchdog.h index 7668402fec4..aaf77a4cbdc 100644 --- a/board/drivers/simple_watchdog.h +++ b/board/drivers/simple_watchdog.h @@ -1,21 +1,9 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_SIMPLE_WATCHDOG_H +#define DRIVERS_SIMPLE_WATCHDOG_H -static simple_watchdog_state_t wd_state; +#include -void simple_watchdog_kick(void) { - uint32_t ts = microsecond_timer_get(); +void simple_watchdog_kick(void); +void simple_watchdog_init(uint32_t fault, uint32_t threshold); - uint32_t et = get_ts_elapsed(ts, wd_state.last_ts); - if (et > wd_state.threshold) { - print("WD timeout 0x"); puth(et); print("\n"); - fault_occurred(wd_state.fault); - } - - wd_state.last_ts = ts; -} - -void simple_watchdog_init(uint32_t fault, uint32_t threshold) { - wd_state.fault = fault; - wd_state.threshold = threshold; - wd_state.last_ts = microsecond_timer_get(); -} +#endif diff --git a/board/drivers/spi.c b/board/drivers/spi.c new file mode 100644 index 00000000000..a6375a5ba42 --- /dev/null +++ b/board/drivers/spi.c @@ -0,0 +1,241 @@ +#include "drivers/spi.h" +#include "board/drivers/drivers.h" + +// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 +#ifdef STM32H7 +__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; +__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; +#else +uint8_t spi_buf_rx[SPI_BUF_SIZE]; +uint8_t spi_buf_tx[SPI_BUF_SIZE]; +#endif + +#define SPI_HEADER_SIZE 7U + +// low level SPI prototypes +void llspi_init(void); +void llspi_mosi_dma(uint8_t *addr, int len); +void llspi_miso_dma(uint8_t *addr, int len); + +uint16_t spi_error_count = 0; + +static uint8_t spi_state = SPI_STATE_HEADER; +static uint16_t spi_data_len_mosi; +static bool spi_can_tx_ready = false; +static const unsigned char version_text[] = "VERSION"; + +static uint16_t spi_version_packet(uint8_t *out) { + // this protocol version request is a stable portion of + // the panda's SPI protocol. its contents match that of the + // panda USB descriptors and are sufficent to list/enumerate + // a panda, determine panda type, and bootstub status. + + // the response is: + // VERSION + 2 byte data length + data + CRC8 + + // echo "VERSION" + (void)memcpy(out, version_text, 7); + + // write response + uint16_t data_len = 0; + uint16_t data_pos = 7U + 2U; + + // write serial + (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); + data_len += 12U; + + // HW type + out[data_pos + data_len] = hw_type; + data_len += 1U; + + // bootstub + out[data_pos + data_len] = USB_PID & 0xFFU; + data_len += 1U; + + // SPI protocol version + out[data_pos + data_len] = 0x2; + data_len += 1U; + + // data length + out[7] = data_len & 0xFFU; + out[8] = (data_len >> 8) & 0xFFU; + + // CRC8 + uint16_t resp_len = data_pos + data_len; + out[resp_len] = crc_checksum(out, resp_len, 0xD5U); + resp_len += 1U; + + return resp_len; +} + +void spi_init(void) { + // platform init + llspi_init(); + + // Start the first packet! + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); +} + +static bool validate_checksum(const uint8_t *data, uint16_t len) { + // TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards + uint8_t checksum = SPI_CHECKSUM_START; + for(uint16_t i = 0U; i < len; i++){ + checksum ^= data[i]; + } + return checksum == 0U; +} + +void spi_rx_done(void) { + uint16_t response_len = 0U; + uint8_t next_rx_state = SPI_STATE_HEADER_NACK; + bool checksum_valid = false; + static uint8_t spi_endpoint; + static uint16_t spi_data_len_miso; + + // parse header + spi_endpoint = spi_buf_rx[1]; + spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; + spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; + + if (memcmp(spi_buf_rx, version_text, 7) == 0) { + response_len = spi_version_packet(spi_buf_tx); + next_rx_state = SPI_STATE_HEADER_NACK;; + } else if (spi_state == SPI_STATE_HEADER) { + checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); + if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { + // response: ACK and start receiving data portion + spi_buf_tx[0] = SPI_HACK; + next_rx_state = SPI_STATE_HEADER_ACK; + response_len = 1U; + } else { + // response: NACK and reset state machine + #ifdef DEBUG_SPI + print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); + #endif + spi_buf_tx[0] = SPI_NACK; + next_rx_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } + } else if (spi_state == SPI_STATE_DATA_RX) { + // We got everything! Based on the endpoint specified, call the appropriate handler + bool response_ack = false; + checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); + if (checksum_valid) { + if (spi_endpoint == 0U) { + if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { + ControlPacket_t ctrl = {0}; + (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); + response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); + response_ack = true; + } else { + print("SPI: insufficient data for control handler\n"); + } + } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { + if (spi_data_len_mosi == 0U) { + response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); + response_ack = true; + } else { + print("SPI: did not expect data for can_read\n"); + } + } else if (spi_endpoint == 2U) { + comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else if (spi_endpoint == 3U) { + if (spi_data_len_mosi > 0U) { + if (spi_can_tx_ready) { + spi_can_tx_ready = false; + comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else { + response_ack = false; + print("SPI: CAN NACK\n"); + } + } else { + print("SPI: did expect data for can_write\n"); + } + } else if (spi_endpoint == 0xABU) { + // test endpoint: mimics panda -> device transfer + response_len = spi_data_len_miso; + response_ack = true; + } else if (spi_endpoint == 0xACU) { + // test endpoint: mimics device -> panda transfer (with NACK) + response_ack = false; + } else { + print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); + } + } else { + // Checksum was incorrect + response_ack = false; + #ifdef DEBUG_SPI + print("- incorrect data checksum "); + puth4(spi_data_len_mosi); + print("\n"); + hexdump(spi_buf_rx, SPI_HEADER_SIZE); + hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64)); + print("\n"); + #endif + } + + if (!response_ack) { + spi_buf_tx[0] = SPI_NACK; + next_rx_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } else { + // Setup response header + spi_buf_tx[0] = SPI_DACK; + spi_buf_tx[1] = response_len & 0xFFU; + spi_buf_tx[2] = (response_len >> 8) & 0xFFU; + + // Add checksum + uint8_t checksum = SPI_CHECKSUM_START; + for(uint16_t i = 0U; i < (response_len + 3U); i++) { + checksum ^= spi_buf_tx[i]; + } + spi_buf_tx[response_len + 3U] = checksum; + response_len += 4U; + + next_rx_state = SPI_STATE_DATA_TX; + } + } else { + print("SPI: RX unexpected state: "); puth(spi_state); print("\n"); + } + + // send out response + if (response_len == 0U) { + print("SPI: no response\n"); + spi_buf_tx[0] = SPI_NACK; + spi_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } + llspi_miso_dma(spi_buf_tx, response_len); + + spi_state = next_rx_state; + if (!checksum_valid) { + spi_error_count += 1U; + } +} + +void spi_tx_done(bool reset) { + if ((spi_state == SPI_STATE_HEADER_NACK) || reset) { + // Reset state + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + } else if (spi_state == SPI_STATE_HEADER_ACK) { + // ACK was sent, queue up the RX buf for the data + checksum + spi_state = SPI_STATE_DATA_RX; + llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U); + } else if (spi_state == SPI_STATE_DATA_TX) { + // Reset state + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + } else { + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + print("SPI: TX unexpected state: "); puth(spi_state); print("\n"); + } +} + +void can_tx_comms_resume_spi(void) { + spi_can_tx_ready = true; +} diff --git a/board/drivers/spi.h b/board/drivers/spi.h index 283d845e0ac..af87f1f40d2 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -1,15 +1,8 @@ -#pragma once +#ifndef DRIVERS_SPI_H +#define DRIVERS_SPI_H -#include "board/drivers/drivers.h" - -// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 -#ifdef STM32H7 -__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; -__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; -#else -uint8_t spi_buf_rx[SPI_BUF_SIZE]; -uint8_t spi_buf_tx[SPI_BUF_SIZE]; -#endif +#include +#include #define SPI_CHECKSUM_START 0xABU #define SPI_SYNC_BYTE 0x5AU @@ -27,232 +20,9 @@ enum { SPI_STATE_DATA_TX }; -uint16_t spi_error_count = 0; - -#define SPI_HEADER_SIZE 7U - -// low level SPI prototypes -void llspi_init(void); -void llspi_mosi_dma(uint8_t *addr, int len); -void llspi_miso_dma(uint8_t *addr, int len); - -static uint8_t spi_state = SPI_STATE_HEADER; -static uint16_t spi_data_len_mosi; -static bool spi_can_tx_ready = false; -static const unsigned char version_text[] = "VERSION"; - -static uint16_t spi_version_packet(uint8_t *out) { - // this protocol version request is a stable portion of - // the panda's SPI protocol. its contents match that of the - // panda USB descriptors and are sufficent to list/enumerate - // a panda, determine panda type, and bootstub status. - - // the response is: - // VERSION + 2 byte data length + data + CRC8 - - // echo "VERSION" - (void)memcpy(out, version_text, 7); - - // write response - uint16_t data_len = 0; - uint16_t data_pos = 7U + 2U; - - // write serial - (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); - data_len += 12U; - - // HW type - out[data_pos + data_len] = hw_type; - data_len += 1U; - - // bootstub - out[data_pos + data_len] = USB_PID & 0xFFU; - data_len += 1U; - - // SPI protocol version - out[data_pos + data_len] = 0x2; - data_len += 1U; - - // data length - out[7] = data_len & 0xFFU; - out[8] = (data_len >> 8) & 0xFFU; - - // CRC8 - uint16_t resp_len = data_pos + data_len; - out[resp_len] = crc_checksum(out, resp_len, 0xD5U); - resp_len += 1U; +void spi_init(void); +void spi_rx_done(void); +void spi_tx_done(bool reset); +void can_tx_comms_resume_spi(void); - return resp_len; -} - -void spi_init(void) { - // platform init - llspi_init(); - - // Start the first packet! - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); -} - -static bool validate_checksum(const uint8_t *data, uint16_t len) { - // TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards - uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < len; i++){ - checksum ^= data[i]; - } - return checksum == 0U; -} - -void spi_rx_done(void) { - uint16_t response_len = 0U; - uint8_t next_rx_state = SPI_STATE_HEADER_NACK; - bool checksum_valid = false; - static uint8_t spi_endpoint; - static uint16_t spi_data_len_miso; - - // parse header - spi_endpoint = spi_buf_rx[1]; - spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; - spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; - - if (memcmp(spi_buf_rx, version_text, 7) == 0) { - response_len = spi_version_packet(spi_buf_tx); - next_rx_state = SPI_STATE_HEADER_NACK;; - } else if (spi_state == SPI_STATE_HEADER) { - checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); - if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { - // response: ACK and start receiving data portion - spi_buf_tx[0] = SPI_HACK; - next_rx_state = SPI_STATE_HEADER_ACK; - response_len = 1U; - } else { - // response: NACK and reset state machine - #ifdef DEBUG_SPI - print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); - #endif - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } - } else if (spi_state == SPI_STATE_DATA_RX) { - // We got everything! Based on the endpoint specified, call the appropriate handler - bool response_ack = false; - checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); - if (checksum_valid) { - if (spi_endpoint == 0U) { - if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { - ControlPacket_t ctrl = {0}; - (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); - response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); - response_ack = true; - } else { - print("SPI: insufficient data for control handler\n"); - } - } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { - if (spi_data_len_mosi == 0U) { - response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); - response_ack = true; - } else { - print("SPI: did not expect data for can_read\n"); - } - } else if (spi_endpoint == 2U) { - comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); - response_ack = true; - } else if (spi_endpoint == 3U) { - if (spi_data_len_mosi > 0U) { - if (spi_can_tx_ready) { - spi_can_tx_ready = false; - comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); - response_ack = true; - } else { - response_ack = false; - print("SPI: CAN NACK\n"); - } - } else { - print("SPI: did expect data for can_write\n"); - } - } else if (spi_endpoint == 0xABU) { - // test endpoint: mimics panda -> device transfer - response_len = spi_data_len_miso; - response_ack = true; - } else if (spi_endpoint == 0xACU) { - // test endpoint: mimics device -> panda transfer (with NACK) - response_ack = false; - } else { - print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); - } - } else { - // Checksum was incorrect - response_ack = false; - #ifdef DEBUG_SPI - print("- incorrect data checksum "); - puth4(spi_data_len_mosi); - print("\n"); - hexdump(spi_buf_rx, SPI_HEADER_SIZE); - hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64)); - print("\n"); - #endif - } - - if (!response_ack) { - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } else { - // Setup response header - spi_buf_tx[0] = SPI_DACK; - spi_buf_tx[1] = response_len & 0xFFU; - spi_buf_tx[2] = (response_len >> 8) & 0xFFU; - - // Add checksum - uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < (response_len + 3U); i++) { - checksum ^= spi_buf_tx[i]; - } - spi_buf_tx[response_len + 3U] = checksum; - response_len += 4U; - - next_rx_state = SPI_STATE_DATA_TX; - } - } else { - print("SPI: RX unexpected state: "); puth(spi_state); print("\n"); - } - - // send out response - if (response_len == 0U) { - print("SPI: no response\n"); - spi_buf_tx[0] = SPI_NACK; - spi_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } - llspi_miso_dma(spi_buf_tx, response_len); - - spi_state = next_rx_state; - if (!checksum_valid) { - spi_error_count += 1U; - } -} - -void spi_tx_done(bool reset) { - if ((spi_state == SPI_STATE_HEADER_NACK) || reset) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else if (spi_state == SPI_STATE_HEADER_ACK) { - // ACK was sent, queue up the RX buf for the data + checksum - spi_state = SPI_STATE_DATA_RX; - llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U); - } else if (spi_state == SPI_STATE_DATA_TX) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else { - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - print("SPI: TX unexpected state: "); puth(spi_state); print("\n"); - } -} - -void can_tx_comms_resume_spi(void) { - spi_can_tx_ready = true; -} +#endif diff --git a/board/drivers/timers.c b/board/drivers/timers.c new file mode 100644 index 00000000000..1fb46e33e67 --- /dev/null +++ b/board/drivers/timers.c @@ -0,0 +1,34 @@ +#include "drivers/timers.h" +#include "board/drivers/drivers.h" + +static void timer_init(TIM_TypeDef *TIM, int psc) { + register_set(&(TIM->PSC), (psc-1), 0xFFFFU); + register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); + TIM->SR = 0; +} + +void microsecond_timer_init(void) { + MICROSECOND_TIMER->PSC = (APB1_TIMER_FREQ - 1U); + MICROSECOND_TIMER->CR1 = TIM_CR1_CEN; + MICROSECOND_TIMER->EGR = TIM_EGR_UG; +} + +uint32_t microsecond_timer_get(void) { + return MICROSECOND_TIMER->CNT; +} + +void interrupt_timer_init(void) { + enable_interrupt_timer(); + REGISTER_INTERRUPT(INTERRUPT_TIMER_IRQ, interrupt_timer_handler, 2U, FAULT_INTERRUPT_RATE_INTERRUPTS) + register_set(&(INTERRUPT_TIMER->PSC), ((uint16_t)(15.25*APB1_TIMER_FREQ)-1U), 0xFFFFU); + register_set(&(INTERRUPT_TIMER->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(INTERRUPT_TIMER->CR1), TIM_CR1_CEN, 0x3FU); + INTERRUPT_TIMER->SR = 0; + NVIC_EnableIRQ(INTERRUPT_TIMER_IRQ); +} + +void tick_timer_init(void) { + timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); + NVIC_EnableIRQ(TICK_TIMER_IRQ); +} diff --git a/board/drivers/timers.h b/board/drivers/timers.h index 4c046a2b49b..e0fbc898240 100644 --- a/board/drivers/timers.h +++ b/board/drivers/timers.h @@ -1,31 +1,11 @@ -static void timer_init(TIM_TypeDef *TIM, int psc) { - register_set(&(TIM->PSC), (psc-1), 0xFFFFU); - register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); - register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); - TIM->SR = 0; -} +#ifndef DRIVERS_TIMERS_H +#define DRIVERS_TIMERS_H -void microsecond_timer_init(void) { - MICROSECOND_TIMER->PSC = (APB1_TIMER_FREQ - 1U); - MICROSECOND_TIMER->CR1 = TIM_CR1_CEN; - MICROSECOND_TIMER->EGR = TIM_EGR_UG; -} +#include -uint32_t microsecond_timer_get(void) { - return MICROSECOND_TIMER->CNT; -} +void microsecond_timer_init(void); +uint32_t microsecond_timer_get(void); +void interrupt_timer_init(void); +void tick_timer_init(void); -void interrupt_timer_init(void) { - enable_interrupt_timer(); - REGISTER_INTERRUPT(INTERRUPT_TIMER_IRQ, interrupt_timer_handler, 2U, FAULT_INTERRUPT_RATE_INTERRUPTS) - register_set(&(INTERRUPT_TIMER->PSC), ((uint16_t)(15.25*APB1_TIMER_FREQ)-1U), 0xFFFFU); - register_set(&(INTERRUPT_TIMER->DIER), TIM_DIER_UIE, 0x5F5FU); - register_set(&(INTERRUPT_TIMER->CR1), TIM_CR1_CEN, 0x3FU); - INTERRUPT_TIMER->SR = 0; - NVIC_EnableIRQ(INTERRUPT_TIMER_IRQ); -} - -void tick_timer_init(void) { - timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); - NVIC_EnableIRQ(TICK_TIMER_IRQ); -} +#endif diff --git a/board/drivers/uart.c b/board/drivers/uart.c new file mode 100644 index 00000000000..85549524dda --- /dev/null +++ b/board/drivers/uart.c @@ -0,0 +1,139 @@ +#include "drivers/uart.h" +#include "board/drivers/drivers.h" +#include "board/drivers/interrupts.h" + +#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \ + static uint8_t elems_rx_##x[size_rx]; \ + static uint8_t elems_tx_##x[size_tx]; \ + extern uart_ring uart_ring_##x; \ + uart_ring uart_ring_##x = { \ + .w_ptr_tx = 0, \ + .r_ptr_tx = 0, \ + .elems_tx = ((uint8_t *)&(elems_tx_##x)), \ + .tx_fifo_size = (size_tx), \ + .w_ptr_rx = 0, \ + .r_ptr_rx = 0, \ + .elems_rx = ((uint8_t *)&(elems_rx_##x)), \ + .rx_fifo_size = (size_rx), \ + .uart = (uart_ptr), \ + .callback = (callback_ptr), \ + .overwrite = (overwrite_mode) \ + }; + +UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, true) +UART_BUFFER(som_debug, FIFO_SIZE_INT, FIFO_SIZE_INT, UART7, NULL, true) + +uart_ring *get_ring_by_number(int a) { + uart_ring *ring = NULL; + switch(a) { + case 0: + ring = &uart_ring_debug; + break; + case 4: + ring = &uart_ring_som_debug; + break; + default: + ring = NULL; + break; + } + return ring; +} + +bool get_char(uart_ring *q, char *elem) { + bool ret = false; + + ENTER_CRITICAL(); + if (q->w_ptr_rx != q->r_ptr_rx) { + if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool injectc(uart_ring *q, char elem) { + int ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = elem; + q->w_ptr_rx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool put_char(uart_ring *q, char elem) { + bool ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size; + + if ((next_w_ptr == q->r_ptr_tx) && q->overwrite) { + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_tx) { + q->elems_tx[q->w_ptr_tx] = elem; + q->w_ptr_tx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + uart_tx_ring(q); + + return ret; +} + +void putch(const char a) { + (void)injectc(&uart_ring_debug, a); +} + +void print(const char *a) { + for (const char *in = a; *in; in++) { + if (*in == '\n') putch('\r'); + putch(*in); + } +} + +void puthx(uint32_t i, uint8_t len) { + const char c[] = "0123456789abcdef"; + for (int pos = ((int)len * 4) - 4; pos > -4; pos -= 4) { + putch(c[(i >> (unsigned int)(pos)) & 0xFU]); + } +} + +void puth(unsigned int i) { + puthx(i, 8U); +} + +#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) +void puth4(unsigned int i) { + puthx(i, 4U); +} +#endif + +#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) +void hexdump(const void *a, int l) { + if (a != NULL) { + for (int i=0; i < l; i++) { + if ((i != 0) && ((i & 0xf) == 0)) print("\n"); + puthx(((const unsigned char*)a)[i], 2U); + print(" "); + } + } + print("\n"); +} +#endif diff --git a/board/drivers/uart.h b/board/drivers/uart.h index 250c2ea0010..a72804fb3c5 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -1,149 +1,43 @@ -#include "board/drivers/drivers.h" - -// ***************************** Definitions ***************************** - -#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \ - static uint8_t elems_rx_##x[size_rx]; \ - static uint8_t elems_tx_##x[size_tx]; \ - extern uart_ring uart_ring_##x; \ - uart_ring uart_ring_##x = { \ - .w_ptr_tx = 0, \ - .r_ptr_tx = 0, \ - .elems_tx = ((uint8_t *)&(elems_tx_##x)), \ - .tx_fifo_size = (size_tx), \ - .w_ptr_rx = 0, \ - .r_ptr_rx = 0, \ - .elems_rx = ((uint8_t *)&(elems_rx_##x)), \ - .rx_fifo_size = (size_rx), \ - .uart = (uart_ptr), \ - .callback = (callback_ptr), \ - .overwrite = (overwrite_mode) \ - }; - -// ******************************** UART buffers ******************************** - -// debug = USART2 -UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, true) - -// SOM debug = UART7 -UART_BUFFER(som_debug, FIFO_SIZE_INT, FIFO_SIZE_INT, UART7, NULL, true) - -uart_ring *get_ring_by_number(int a) { - uart_ring *ring = NULL; - switch(a) { - case 0: - ring = &uart_ring_debug; - break; - case 4: - ring = &uart_ring_som_debug; - break; - default: - ring = NULL; - break; - } - return ring; -} - -// ************************* Low-level buffer functions ************************* -bool get_char(uart_ring *q, char *elem) { - bool ret = false; - - ENTER_CRITICAL(); - if (q->w_ptr_rx != q->r_ptr_rx) { - if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; - q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; - ret = true; - } - EXIT_CRITICAL(); - - return ret; -} - -bool injectc(uart_ring *q, char elem) { - int ret = false; - uint16_t next_w_ptr; - - ENTER_CRITICAL(); - next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; - - if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { - // overwrite mode: drop oldest byte - q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; - } - - if (next_w_ptr != q->r_ptr_rx) { - q->elems_rx[q->w_ptr_rx] = elem; - q->w_ptr_rx = next_w_ptr; - ret = true; - } - EXIT_CRITICAL(); - - return ret; -} - -bool put_char(uart_ring *q, char elem) { - bool ret = false; - uint16_t next_w_ptr; - - ENTER_CRITICAL(); - next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size; - - if ((next_w_ptr == q->r_ptr_tx) && q->overwrite) { - // overwrite mode: drop oldest byte - q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; - } - - if (next_w_ptr != q->r_ptr_tx) { - q->elems_tx[q->w_ptr_tx] = elem; - q->w_ptr_tx = next_w_ptr; - ret = true; - } - EXIT_CRITICAL(); - - uart_tx_ring(q); - - return ret; -} - -// ************************ High-level debug functions ********************** -void putch(const char a) { - // misra-c2012-17.7: serial debug function, ok to ignore output - (void)injectc(&uart_ring_debug, a); -} - -void print(const char *a) { - for (const char *in = a; *in; in++) { - if (*in == '\n') putch('\r'); - putch(*in); - } -} - -void puthx(uint32_t i, uint8_t len) { - const char c[] = "0123456789abcdef"; - for (int pos = ((int)len * 4) - 4; pos > -4; pos -= 4) { - putch(c[(i >> (unsigned int)(pos)) & 0xFU]); - } -} - -void puth(unsigned int i) { - puthx(i, 8U); -} - +#ifndef DRIVERS_UART_H +#define DRIVERS_UART_H + +#include +#include + +#define FIFO_SIZE_INT 0x400U + +typedef struct uart_ring { + volatile uint16_t w_ptr_tx; + volatile uint16_t r_ptr_tx; + uint8_t *elems_tx; + uint32_t tx_fifo_size; + volatile uint16_t w_ptr_rx; + volatile uint16_t r_ptr_rx; + uint8_t *elems_rx; + uint32_t rx_fifo_size; + USART_TypeDef *uart; + void (*callback)(struct uart_ring*); + bool overwrite; +} uart_ring; + +void debug_ring_callback(uart_ring *ring); +void uart_tx_ring(uart_ring *q); +uart_ring *get_ring_by_number(int a); + +bool get_char(uart_ring *q, char *elem); +bool injectc(uart_ring *q, char elem); +bool put_char(uart_ring *q, char elem); +void clear_uart_buff(uart_ring *q); + +void putch(const char a); +void print(const char *a); +void puthx(uint32_t i, uint8_t len); +void puth(unsigned int i); #if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) -static void puth4(unsigned int i) { - puthx(i, 4U); -} +void puth4(unsigned int i); +#endif +#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) +void hexdump(const void *a, int l); #endif -#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS) -static void hexdump(const void *a, int l) { - if (a != NULL) { - for (int i=0; i < l; i++) { - if ((i != 0) && ((i & 0xf) == 0)) print("\n"); - puthx(((const unsigned char*)a)[i], 2U); - print(" "); - } - } - print("\n"); -} #endif diff --git a/board/drivers/usb.c b/board/drivers/usb.c new file mode 100644 index 00000000000..d9cfba4ad05 --- /dev/null +++ b/board/drivers/usb.c @@ -0,0 +1,877 @@ +#include "drivers/usb.h" +#include "board/drivers/drivers.h" + +typedef union { + uint16_t w; + struct BW { + uint8_t msb; + uint8_t lsb; + } + bw; +} uint16_t_uint8_t; + +typedef union _USB_Setup { + uint32_t d8[2]; + struct _SetupPkt_Struc + { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t_uint8_t wValue; + uint16_t_uint8_t wIndex; + uint16_t_uint8_t wLength; + } b; +} USB_Setup_TypeDef; + +// **** supporting defines **** +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_SET_INTERFACE 0x0B + +#define USB_DESC_TYPE_DEVICE 0x01 +#define USB_DESC_TYPE_CONFIGURATION 0x02 +#define USB_DESC_TYPE_STRING 0x03 +#define USB_DESC_TYPE_INTERFACE 0x04 +#define USB_DESC_TYPE_ENDPOINT 0x05 +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f + +// offsets for configuration strings +#define STRING_OFFSET_LANGID 0x00 +#define STRING_OFFSET_IMANUFACTURER 0x01 +#define STRING_OFFSET_IPRODUCT 0x02 +#define STRING_OFFSET_ISERIAL 0x03 +#define STRING_OFFSET_ICONFIGURATION 0x04 + +// WinUSB requests +#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04 +#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05 +#define WINUSB_REQ_GET_DESCRIPTOR 0x07 + +#define STS_DATA_UPDT 2 +#define STS_SETUP_UPDT 6 + +// for the repeating interfaces +#define DSCR_INTERFACE_LEN 9 +#define DSCR_ENDPOINT_LEN 7 +#define DSCR_CONFIG_LEN 9 +#define DSCR_DEVICE_LEN 18 + +// endpoint types +#define ENDPOINT_TYPE_BULK 2 +#define ENDPOINT_TYPE_INT 3 + +// These are arbitrary values used in bRequest +#define MS_VENDOR_CODE 0x20 +#define WEBUSB_VENDOR_CODE 0x30 + +// BOS constants +#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05 +#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F +#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E + +// Convert machine byte order to USB byte order +#define TOUSBORDER(num)\ + ((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU) + +// take in string length and return the first 2 bytes of a string descriptor +#define STRING_DESCRIPTOR_HEADER(size)\ + (((((size) * 2) + 2) & 0xFF) | 0x0300) + +#define ENDPOINT_RCV 0x80 +#define ENDPOINT_SND 0x00 + +static uint8_t response[USBPACKET_MAX_SIZE]; + +// current packet +static USB_Setup_TypeDef setup; +static uint8_t* ep0_txdata = NULL; +static uint16_t ep0_txlen = 0; +static bool outep3_processing = false; + +// Store the current interface alt setting. +static int current_int0_alt_setting = 0; + +// packet read and write + +static void *USB_ReadPacket(void *dest, uint16_t len) { + uint32_t *dest_copy = (uint32_t *)dest; + uint32_t count32b = ((uint32_t)len + 3U) / 4U; + + for (uint32_t i = 0; i < count32b; i++) { + *dest_copy = USBx_DFIFO(0U); + dest_copy++; + } + return ((void *)dest_copy); +} + +static void USB_WritePacket(const void *src, uint16_t len, uint32_t ep) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint32_t numpacket = ((uint32_t)len + (USBPACKET_MAX_SIZE - 1U)) / USBPACKET_MAX_SIZE; + uint32_t count32b = 0; + count32b = ((uint32_t)len + 3U) / 4U; + + // TODO: revisit this + USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | + (len & USB_OTG_DIEPTSIZ_XFRSIZ); + USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); + + // load the FIFO + if (src != NULL) { + const uint32_t *src_copy = (const uint32_t *)src; + for (uint32_t i = 0; i < count32b; i++) { + USBx_DFIFO(ep) = *src_copy; + src_copy++; + } + } +} + +// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) +// so use TX FIFO empty interrupt to send larger amounts of data +static void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint16_t wplen = MIN(len, 0x40); + USB_WritePacket(src, wplen, 0); + + if (wplen < len) { + ep0_txdata = &src[wplen]; + ep0_txlen = len - wplen; + USBx_DEVICE->DIEPEMPMSK |= 1; + } else { + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + +static void usb_reset(void) { + // unmask endpoint interrupts, so many sets + USBx_DEVICE->DAINT = 0xFFFFFFFFU; + USBx_DEVICE->DAINTMSK = 0xFFFFFFFFU; + //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); + + // all interrupts for debugging + USBx_DEVICE->DIEPMSK = 0xFFFFFFFFU; + USBx_DEVICE->DOEPMSK = 0xFFFFFFFFU; + + // clear interrupts + USBx_INEP(0U)->DIEPINT = 0xFF; + USBx_OUTEP(0U)->DOEPINT = 0xFF; + + // unset the address + USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; + + // set up USB FIFOs + // RX start address is fixed to 0 + USBx->GRXFSIZ = 0x40; + + // 0x100 to offset past GRXFSIZ + USBx->DIEPTXF0_HNPTXFSIZ = (0x40UL << 16) | 0x40U; + + // EP1, massive + USBx->DIEPTXF[0] = (0x40UL << 16) | 0x80U; + + // flush TX fifo + USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); + // flush RX FIFO + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); + + // no global NAK + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; + + // ready to receive setup packets + USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (3U << 3); +} + +static char to_hex_char(uint8_t a) { + char ret; + if (a < 10U) { + ret = '0' + a; + } else { + ret = 'a' + (a - 10U); + } + return ret; +} + +static void usb_setup(void) { + static uint8_t device_desc[] = { + DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size + TOUSBORDER(USB_VID), // idVendor + TOUSBORDER(USB_PID), // idProduct + 0x00, 0x00, // bcdDevice + 0x01, 0x02, // Manufacturer, Product + 0x03, 0x01 // Serial Number, Num Configurations + }; + + static uint8_t device_qualifier[] = { + 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 + 0x01, 0x00 // bNumConfigurations, bReserved + }; + + static uint8_t configuration_desc[] = { + DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, + TOUSBORDER(0x0045U), // Total Len (uint16) + 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration + 0xc0, 0x32, // Attributes, Max Power + // interface 0 ALT 0 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval (NA) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // interface 0 ALT 1 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x05, // Polling Interval (5 frames) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + }; + + // STRING_DESCRIPTOR_HEADER is for uint16 string descriptors + // it takes in a string length, which is bytes/2 because unicode + static uint16_t string_language_desc[] = { + STRING_DESCRIPTOR_HEADER(1), + 0x0409 // american english + }; + + // these strings are all uint16's so that we don't need to spam ,0 after every character + static uint16_t string_manufacturer_desc[] = { + STRING_DESCRIPTOR_HEADER(8), + 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' + }; + + static uint16_t string_product_desc[] = { + STRING_DESCRIPTOR_HEADER(5), + 'p', 'a', 'n', 'd', 'a' + }; + + // a string containing the default configuration index + static uint16_t string_configuration_desc[] = { + STRING_DESCRIPTOR_HEADER(2), + '0', '1' // "01" + }; + + // WCID (auto install WinUSB driver) + // https://github.com/pbatard/libwdi/wiki/WCID-Devices + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file + // WinUSB 1.0 descriptors, this is mostly used by Windows XP + static uint8_t string_238_desc[] = { + 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType + 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) + MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad + }; + + static uint8_t winusb_ext_compatid_os_desc[] = { + 0x28, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x04, 0x00, // wIndex + 0x01, // bCount + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + 0x00, // bFirstInterfaceNumber + 0x00, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved + }; + + static uint8_t winusb_ext_prop_os_desc[] = { + 0x8e, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x05, 0x00, // wIndex + 0x01, 0x00, // wCount + // first property + 0x84, 0x00, 0x00, 0x00, // dwSize + 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType + 0x28, 0x00, // wPropertyNameLength + 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) + 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength + '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) + }; + + /* + Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata + comments are from the wicg spec + References used: + https://wicg.github.io/webusb/#webusb-platform-capability-descriptor + https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ + */ + static uint8_t binary_object_store_desc[] = { + // BOS header + BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header + BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType + 0x39, 0x00, // wTotalLength (LSB, MSB) + 0x02, // bNumDeviceCaps (WebUSB + WinUSB) + + // ------------------------------------------------- + // WebUSB descriptor + // header + 0x18, // bLength, Size of this descriptor. Must be set to 24. + 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor + 0x05, // bDevCapabilityType, PLATFORM capability + 0x00, // bReserved, This field is reserved and shall be set to zero. + + // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, 0xA0, 0x47, + 0x8B, 0xFD, 0xA0, 0x76, + 0x88, 0x15, 0xB6, 0x65, + // + + 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. + WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. + // there used to be a concept of "allowed origins", but it was removed from the spec + // it was intended to be a security feature, but then the entire security model relies on domain ownership + // https://github.com/WICG/webusb/issues/49 + // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. + // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index + 0x03, // iLandingPage, URL descriptor index of the device's landing page. + + // ------------------------------------------------- + // WinUSB descriptor + // header + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) + // Indicates the device supports the Microsoft OS 2.0 descriptor + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) + + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) + MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration + }; + + // WinUSB 2.0 descriptor. This is what modern systems use + // https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + // http://janaxelson.com/files/ms_os_20_descriptors.c + // https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 + static uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { + // Microsoft OS 2.0 descriptor set header (table 10) + 0x0A, 0x00, // Descriptor size (10 bytes) + 0x00, 0x00, // MS OS 2.0 descriptor set header + + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set + + // Microsoft OS 2.0 compatible ID descriptor + 0x14, 0x00, // Descriptor size (20 bytes) + 0x03, 0x00, // MS OS 2.0 compatible ID descriptor + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID + + // Registry property descriptor + 0x80, 0x00, // Descriptor size (130 bytes) + 0x04, 0x00, // Registry Property descriptor + 0x01, 0x00, // Strings are null-terminated Unicode + 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" + + // bPropertyName (DeviceInterfaceGUID) + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, + + 0x4E, 0x00, // Size of Property Data (78 bytes) + + // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} + '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 + 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 + '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 + '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 + '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes + }; + + int resp_len; + ControlPacket_t control_req; + + // setup packet is ready + switch (setup.b.bRequest) { + case USB_REQ_SET_CONFIGURATION: + // enable other endpoints, has to be here? + USBx_INEP(1U)->DIEPCTL = (0x40U & USB_OTG_DIEPCTL_MPSIZ) | (2UL << 18) | (1UL << 22) | + USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; + USBx_INEP(1U)->DIEPINT = 0xFF; + + USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(2U)->DOEPINT = 0xFF; + + USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(3U)->DOEPINT = 0xFF; + + // mark ready to receive + USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_ADDRESS: + // set now? + USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7fU) << 4); + + #ifdef DEBUG_USB + print(" set address\n"); + #endif + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + break; + case USB_REQ_GET_DESCRIPTOR: + switch (setup.b.wValue.bw.lsb) { + case USB_DESC_TYPE_DEVICE: + //print(" writing device descriptor\n"); + + // set bcdDevice to hardware type + device_desc[13] = hw_type; + // setup transfer + USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + //print("D"); + break; + case USB_DESC_TYPE_CONFIGURATION: + USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_DEVICE_QUALIFIER: + USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_STRING: + switch (setup.b.wValue.bw.msb) { + case STRING_OFFSET_LANGID: + USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IMANUFACTURER: + USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IPRODUCT: + USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ISERIAL: + response[0] = 0x02 + (12 * 4); + response[1] = 0x03; + + // 96 bits = 12 bytes + for (int i = 0; i < 12; i++){ + uint8_t cc = ((uint8_t *)UID_BASE)[i]; + response[2 + (i * 4)] = to_hex_char((cc >> 4) & 0xFU); + response[2 + (i * 4) + 1] = '\0'; + response[2 + (i * 4) + 2] = to_hex_char((cc >> 0) & 0xFU); + response[2 + (i * 4) + 3] = '\0'; + } + + USB_WritePacket(response, MIN(response[0], setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ICONFIGURATION: + USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); + break; + case 238: + USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); + break; + default: + // nothing + USB_WritePacket(0, 0, 0); + break; + } + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_BINARY_OBJECT_STORE: + USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // nothing here? + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case USB_REQ_GET_STATUS: + // empty response? + response[0] = 0; + response[1] = 0; + USB_WritePacket((void*)&response, 2, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_INTERFACE: + // Store the alt setting number for IN EP behavior. + current_int0_alt_setting = setup.b.wValue.w; + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case WEBUSB_VENDOR_CODE: + // probably asking for allowed origins, which was removed from the spec + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case MS_VENDOR_CODE: + switch (setup.b.wIndex.w) { + // winusb 2.0 descriptor from BOS + case WINUSB_REQ_GET_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); + break; + // Extended Compat ID OS Descriptor + case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); + break; + // Extended Properties OS Descriptor + case WINUSB_REQ_GET_EXT_PROPS_OS: + USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); + break; + default: + USB_WritePacket_EP0(0, 0); + } + break; + default: + control_req.request = setup.b.bRequest; + control_req.param1 = setup.b.wValue.w; + control_req.param2 = setup.b.wIndex.w; + control_req.length = setup.b.wLength.w; + + resp_len = comms_control_handler(&control_req, response); + USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + + + +// ***************************** USB port ***************************** + +void usb_irqhandler(void) { + //USBx->GINTMSK = 0; + static uint8_t usbdata[0x100] __attribute__((aligned(4))); + unsigned int gintsts = USBx->GINTSTS; + unsigned int gotgint = USBx->GOTGINT; + unsigned int daint = USBx_DEVICE->DAINT; + + // gintsts SUSPEND? 04008428 + #ifdef DEBUG_USB + puth(gintsts); + print(" "); + /*puth(USBx->GCCFG); + print(" ");*/ + puth(gotgint); + print(" ep "); + puth(daint); + print(" USB interrupt!\n"); + #endif + + if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0U) { + print("connector ID status change\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0U) { + #ifdef DEBUG_USB + print("USB reset\n"); + #endif + usb_reset(); + } + + if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0U) { + #ifdef DEBUG_USB + print("enumeration done\n"); + #endif + // Full speed, ENUMSPD + //puth(USBx_DEVICE->DSTS); + } + + if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0U) { + #ifdef DEBUG_USB + print("OTG int:"); + puth(USBx->GOTGINT); + print("\n"); + #endif + + // getting ADTOCHG + //USBx->GOTGINT = USBx->GOTGINT; + } + + // RX FIFO first + if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0U) { + // 1. Read the Receive status pop register + volatile unsigned int rxst = USBx->GRXSTSP; + int status = (rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17; + + #ifdef DEBUG_USB + print(" RX FIFO:"); + puth(rxst); + print(" status: "); + puth(status); + print(" len: "); + puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); + print("\n"); + #endif + + if (status == STS_DATA_UPDT) { + int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); + int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; + (void)USB_ReadPacket(&usbdata, len); + #ifdef DEBUG_USB + print(" data "); + puth(len); + print("\n"); + hexdump(&usbdata, len); + #endif + + if (endpoint == 2) { + comms_endpoint2_write((uint8_t *) usbdata, len); + } + + if (endpoint == 3) { + outep3_processing = true; + comms_can_write(usbdata, len); + } + } else if (status == STS_SETUP_UPDT) { + (void)USB_ReadPacket(&setup, 8); + #ifdef DEBUG_USB + print(" setup "); + hexdump(&setup, 8); + print("\n"); + #endif + } else { + // status is neither STS_DATA_UPDT or STS_SETUP_UPDT, skip + } + } + + /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { + // host + print("HPRT:"); + puth(USBx_HOST_PORT->HPRT); + print("\n"); + if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; + } + + }*/ + + if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { + // no global NAK, why is this getting set? + #ifdef DEBUG_USB + print("GLOBAL NAK\n"); + #endif + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; + } + + if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0U) { + // we want to do "A-device host negotiation protocol" since we are the A-device + /*print("start request\n"); + puth(USBx->GOTGCTL); + print("\n");*/ + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; + //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; + } + + // out endpoint hit + if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0U) { + #ifdef DEBUG_USB + print(" 0:"); + puth(USBx_OUTEP(0U)->DOEPINT); + print(" 2:"); + puth(USBx_OUTEP(2U)->DOEPINT); + print(" 3:"); + puth(USBx_OUTEP(3U)->DOEPINT); + print(" "); + puth(USBx_OUTEP(3U)->DOEPCTL); + print(" 4:"); + puth(USBx_OUTEP(4)->DOEPINT); + print(" OUT ENDPOINT\n"); + #endif + + if ((USBx_OUTEP(2U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { + #ifdef DEBUG_USB + print(" OUT2 PACKET XFRC\n"); + #endif + USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + + if ((USBx_OUTEP(3U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { + #ifdef DEBUG_USB + print(" OUT3 PACKET XFRC\n"); + #endif + // NAK cleared by process_can (if tx buffers have room) + outep3_processing = false; + refresh_can_tx_slots_available(); + } else if ((USBx_OUTEP(3U)->DOEPINT & 0x2000U) != 0U) { + #ifdef DEBUG_USB + print(" OUT3 PACKET WTF\n"); + #endif + // if NAK was set trigger this, unknown interrupt + // TODO: why was this here? fires when TX buffers when we can't clear NAK + // USBx_OUTEP(3U)->DOEPTSIZ = (1U << 19) | 0x40U; + // USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } else if ((USBx_OUTEP(3U)->DOEPINT) != 0U) { + #ifdef DEBUG_USB + print("OUTEP3 error "); + puth(USBx_OUTEP(3U)->DOEPINT); + print("\n"); + #endif + } else { + // USBx_OUTEP(3U)->DOEPINT is 0, ok to skip + } + + if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0U) { + // ready for next packet + USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (1U << 3); + } + + // respond to setup packets + if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0U) { + usb_setup(); + } + + USBx_OUTEP(0U)->DOEPINT = USBx_OUTEP(0U)->DOEPINT; + USBx_OUTEP(2U)->DOEPINT = USBx_OUTEP(2U)->DOEPINT; + USBx_OUTEP(3U)->DOEPINT = USBx_OUTEP(3U)->DOEPINT; + } + + // interrupt endpoint hit (Page 1221) + if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0U) { + #ifdef DEBUG_USB + print(" "); + puth(USBx_INEP(0U)->DIEPINT); + print(" "); + puth(USBx_INEP(1U)->DIEPINT); + print(" IN ENDPOINT\n"); + #endif + + // Should likely check the EP of the IN request even if there is + // only one IN endpoint. + + // No need to set NAK in OTG_DIEPCTL0 when nothing to send, + // Appears USB core automatically sets NAK. WritePacket clears it. + + // Handle the two interface alternate settings. Setting 0 has EP1 + // as bulk. Setting 1 has EP1 as interrupt. The code to handle + // these two EP variations are very similar and can be + // restructured for smaller code footprint. Keeping split out for + // now for clarity. + + //TODO add default case. Should it NAK? + switch (current_int0_alt_setting) { + case 0: ////// Bulk config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + USB_WritePacket((void *)response, comms_can_read(response, 0x40), 1); + } + break; + + case 1: ////// Interrupt config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + int len = comms_can_read(response, 0x40); + if (len > 0) { + USB_WritePacket((void *)response, len, 1); + } + } + break; + default: + print("current_int0_alt_setting value invalid\n"); + break; + } + + if ((USBx_INEP(0U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + + if ((ep0_txlen != 0U) && ((USBx_INEP(0U)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40U)) { + uint16_t len = MIN(ep0_txlen, 0x40); + USB_WritePacket(ep0_txdata, len, 0); + ep0_txdata = &ep0_txdata[len]; + ep0_txlen -= len; + if (ep0_txlen == 0U) { + ep0_txdata = NULL; + USBx_DEVICE->DIEPEMPMSK &= ~1; + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + } + } + + // clear interrupts + USBx_INEP(0U)->DIEPINT = USBx_INEP(0U)->DIEPINT; // Why ep0? + USBx_INEP(1U)->DIEPINT = USBx_INEP(1U)->DIEPINT; + } + + // clear all interrupts we handled + USBx_DEVICE->DAINT = daint; + USBx->GOTGINT = gotgint; + USBx->GINTSTS = gintsts; + + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); +} + +void can_tx_comms_resume_usb(void) { + ENTER_CRITICAL(); + if (!outep3_processing && (USBx_OUTEP(3U)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0U) { + USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + EXIT_CRITICAL(); +} diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 47c91c993b9..fc3aa60adf7 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -1,878 +1,10 @@ -#include "board/drivers/drivers.h" +#ifndef DRIVERS_USB_H +#define DRIVERS_USB_H -// IRQs: OTG_FS +#include +#include -typedef union { - uint16_t w; - struct BW { - uint8_t msb; - uint8_t lsb; - } - bw; -} uint16_t_uint8_t; +void usb_irqhandler(void); +void can_tx_comms_resume_usb(void); -typedef union _USB_Setup { - uint32_t d8[2]; - struct _SetupPkt_Struc - { - uint8_t bmRequestType; - uint8_t bRequest; - uint16_t_uint8_t wValue; - uint16_t_uint8_t wIndex; - uint16_t_uint8_t wLength; - } b; -} USB_Setup_TypeDef; - -// **** supporting defines **** -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_SET_INTERFACE 0x0B - -#define USB_DESC_TYPE_DEVICE 0x01 -#define USB_DESC_TYPE_CONFIGURATION 0x02 -#define USB_DESC_TYPE_STRING 0x03 -#define USB_DESC_TYPE_INTERFACE 0x04 -#define USB_DESC_TYPE_ENDPOINT 0x05 -#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 -#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f - -// offsets for configuration strings -#define STRING_OFFSET_LANGID 0x00 -#define STRING_OFFSET_IMANUFACTURER 0x01 -#define STRING_OFFSET_IPRODUCT 0x02 -#define STRING_OFFSET_ISERIAL 0x03 -#define STRING_OFFSET_ICONFIGURATION 0x04 - -// WinUSB requests -#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04 -#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05 -#define WINUSB_REQ_GET_DESCRIPTOR 0x07 - -#define STS_DATA_UPDT 2 -#define STS_SETUP_UPDT 6 - -// for the repeating interfaces -#define DSCR_INTERFACE_LEN 9 -#define DSCR_ENDPOINT_LEN 7 -#define DSCR_CONFIG_LEN 9 -#define DSCR_DEVICE_LEN 18 - -// endpoint types -#define ENDPOINT_TYPE_BULK 2 -#define ENDPOINT_TYPE_INT 3 - -// These are arbitrary values used in bRequest -#define MS_VENDOR_CODE 0x20 -#define WEBUSB_VENDOR_CODE 0x30 - -// BOS constants -#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05 -#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F -#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E - -// Convert machine byte order to USB byte order -#define TOUSBORDER(num)\ - ((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU) - -// take in string length and return the first 2 bytes of a string descriptor -#define STRING_DESCRIPTOR_HEADER(size)\ - (((((size) * 2) + 2) & 0xFF) | 0x0300) - -#define ENDPOINT_RCV 0x80 -#define ENDPOINT_SND 0x00 - -static uint8_t response[USBPACKET_MAX_SIZE]; - -// current packet -static USB_Setup_TypeDef setup; -static uint8_t* ep0_txdata = NULL; -static uint16_t ep0_txlen = 0; -static bool outep3_processing = false; - -// Store the current interface alt setting. -static int current_int0_alt_setting = 0; - -// packet read and write - -static void *USB_ReadPacket(void *dest, uint16_t len) { - uint32_t *dest_copy = (uint32_t *)dest; - uint32_t count32b = ((uint32_t)len + 3U) / 4U; - - for (uint32_t i = 0; i < count32b; i++) { - *dest_copy = USBx_DFIFO(0U); - dest_copy++; - } - return ((void *)dest_copy); -} - -static void USB_WritePacket(const void *src, uint16_t len, uint32_t ep) { - #ifdef DEBUG_USB - print("writing "); - hexdump(src, len); - #endif - - uint32_t numpacket = ((uint32_t)len + (USBPACKET_MAX_SIZE - 1U)) / USBPACKET_MAX_SIZE; - uint32_t count32b = 0; - count32b = ((uint32_t)len + 3U) / 4U; - - // TODO: revisit this - USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | - (len & USB_OTG_DIEPTSIZ_XFRSIZ); - USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); - - // load the FIFO - if (src != NULL) { - const uint32_t *src_copy = (const uint32_t *)src; - for (uint32_t i = 0; i < count32b; i++) { - USBx_DFIFO(ep) = *src_copy; - src_copy++; - } - } -} - -// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) -// so use TX FIFO empty interrupt to send larger amounts of data -static void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { - #ifdef DEBUG_USB - print("writing "); - hexdump(src, len); - #endif - - uint16_t wplen = MIN(len, 0x40); - USB_WritePacket(src, wplen, 0); - - if (wplen < len) { - ep0_txdata = &src[wplen]; - ep0_txlen = len - wplen; - USBx_DEVICE->DIEPEMPMSK |= 1; - } else { - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } -} - -static void usb_reset(void) { - // unmask endpoint interrupts, so many sets - USBx_DEVICE->DAINT = 0xFFFFFFFFU; - USBx_DEVICE->DAINTMSK = 0xFFFFFFFFU; - //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); - //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); - //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); - - // all interrupts for debugging - USBx_DEVICE->DIEPMSK = 0xFFFFFFFFU; - USBx_DEVICE->DOEPMSK = 0xFFFFFFFFU; - - // clear interrupts - USBx_INEP(0U)->DIEPINT = 0xFF; - USBx_OUTEP(0U)->DOEPINT = 0xFF; - - // unset the address - USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; - - // set up USB FIFOs - // RX start address is fixed to 0 - USBx->GRXFSIZ = 0x40; - - // 0x100 to offset past GRXFSIZ - USBx->DIEPTXF0_HNPTXFSIZ = (0x40UL << 16) | 0x40U; - - // EP1, massive - USBx->DIEPTXF[0] = (0x40UL << 16) | 0x80U; - - // flush TX fifo - USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); - // flush RX FIFO - USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); - - // no global NAK - USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; - - // ready to receive setup packets - USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (3U << 3); -} - -static char to_hex_char(uint8_t a) { - char ret; - if (a < 10U) { - ret = '0' + a; - } else { - ret = 'a' + (a - 10U); - } - return ret; -} - -static void usb_setup(void) { - static uint8_t device_desc[] = { - DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type - 0x10, 0x02, // bcdUSB max version of USB supported (2.1) - 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size - TOUSBORDER(USB_VID), // idVendor - TOUSBORDER(USB_PID), // idProduct - 0x00, 0x00, // bcdDevice - 0x01, 0x02, // Manufacturer, Product - 0x03, 0x01 // Serial Number, Num Configurations - }; - - static uint8_t device_qualifier[] = { - 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type - 0x10, 0x02, // bcdUSB max version of USB supported (2.1) - 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 - 0x01, 0x00 // bNumConfigurations, bReserved - }; - - static uint8_t configuration_desc[] = { - DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, - TOUSBORDER(0x0045U), // Total Len (uint16) - 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration - 0xc0, 0x32, // Attributes, Max Power - // interface 0 ALT 0 - DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type - 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count - 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol - 0x00, // Interface - // endpoint 1, read CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval (NA) - // endpoint 2, send serial - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // endpoint 3, send CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // interface 0 ALT 1 - DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type - 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count - 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol - 0x00, // Interface - // endpoint 1, read CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x05, // Polling Interval (5 frames) - // endpoint 2, send serial - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // endpoint 3, send CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - }; - - // STRING_DESCRIPTOR_HEADER is for uint16 string descriptors - // it takes in a string length, which is bytes/2 because unicode - static uint16_t string_language_desc[] = { - STRING_DESCRIPTOR_HEADER(1), - 0x0409 // american english - }; - - // these strings are all uint16's so that we don't need to spam ,0 after every character - static uint16_t string_manufacturer_desc[] = { - STRING_DESCRIPTOR_HEADER(8), - 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' - }; - - static uint16_t string_product_desc[] = { - STRING_DESCRIPTOR_HEADER(5), - 'p', 'a', 'n', 'd', 'a' - }; - - // a string containing the default configuration index - static uint16_t string_configuration_desc[] = { - STRING_DESCRIPTOR_HEADER(2), - '0', '1' // "01" - }; - - // WCID (auto install WinUSB driver) - // https://github.com/pbatard/libwdi/wiki/WCID-Devices - // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file - // WinUSB 1.0 descriptors, this is mostly used by Windows XP - static uint8_t string_238_desc[] = { - 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType - 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) - MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad - }; - - static uint8_t winusb_ext_compatid_os_desc[] = { - 0x28, 0x00, 0x00, 0x00, // dwLength - 0x00, 0x01, // bcdVersion - 0x04, 0x00, // wIndex - 0x01, // bCount - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved - 0x00, // bFirstInterfaceNumber - 0x00, // Reserved - 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved - }; - - static uint8_t winusb_ext_prop_os_desc[] = { - 0x8e, 0x00, 0x00, 0x00, // dwLength - 0x00, 0x01, // bcdVersion - 0x05, 0x00, // wIndex - 0x01, 0x00, // wCount - // first property - 0x84, 0x00, 0x00, 0x00, // dwSize - 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType - 0x28, 0x00, // wPropertyNameLength - 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) - 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength - '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) - }; - - /* - Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata - comments are from the wicg spec - References used: - https://wicg.github.io/webusb/#webusb-platform-capability-descriptor - https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c - https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ - */ - static uint8_t binary_object_store_desc[] = { - // BOS header - BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header - BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType - 0x39, 0x00, // wTotalLength (LSB, MSB) - 0x02, // bNumDeviceCaps (WebUSB + WinUSB) - - // ------------------------------------------------- - // WebUSB descriptor - // header - 0x18, // bLength, Size of this descriptor. Must be set to 24. - 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor - 0x05, // bDevCapabilityType, PLATFORM capability - 0x00, // bReserved, This field is reserved and shall be set to zero. - - // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. - 0x38, 0xB6, 0x08, 0x34, - 0xA9, 0x09, 0xA0, 0x47, - 0x8B, 0xFD, 0xA0, 0x76, - 0x88, 0x15, 0xB6, 0x65, - // - - 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. - WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. - // there used to be a concept of "allowed origins", but it was removed from the spec - // it was intended to be a security feature, but then the entire security model relies on domain ownership - // https://github.com/WICG/webusb/issues/49 - // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. - // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index - 0x03, // iLandingPage, URL descriptor index of the device’s landing page. - - // ------------------------------------------------- - // WinUSB descriptor - // header - 0x1C, // Descriptor size (28 bytes) - 0x10, // Descriptor type (Device Capability) - 0x05, // Capability type (Platform) - 0x00, // Reserved - - // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) - // Indicates the device supports the Microsoft OS 2.0 descriptor - 0xDF, 0x60, 0xDD, 0xD8, - 0x89, 0x45, 0xC7, 0x4C, - 0x9C, 0xD2, 0x65, 0x9D, - 0x9E, 0x64, 0x8A, 0x9F, - - 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) - - WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) - MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration - }; - - // WinUSB 2.0 descriptor. This is what modern systems use - // https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c - // http://janaxelson.com/files/ms_os_20_descriptors.c - // https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 - static uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { - // Microsoft OS 2.0 descriptor set header (table 10) - 0x0A, 0x00, // Descriptor size (10 bytes) - 0x00, 0x00, // MS OS 2.0 descriptor set header - - 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) - WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set - - // Microsoft OS 2.0 compatible ID descriptor - 0x14, 0x00, // Descriptor size (20 bytes) - 0x03, 0x00, // MS OS 2.0 compatible ID descriptor - 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID - - // Registry property descriptor - 0x80, 0x00, // Descriptor size (130 bytes) - 0x04, 0x00, // Registry Property descriptor - 0x01, 0x00, // Strings are null-terminated Unicode - 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" - - // bPropertyName (DeviceInterfaceGUID) - 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, - 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, - 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, - - 0x4E, 0x00, // Size of Property Data (78 bytes) - - // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} - '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 - 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 - '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 - '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 - '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes - }; - - int resp_len; - ControlPacket_t control_req; - - // setup packet is ready - switch (setup.b.bRequest) { - case USB_REQ_SET_CONFIGURATION: - // enable other endpoints, has to be here? - USBx_INEP(1U)->DIEPCTL = (0x40U & USB_OTG_DIEPCTL_MPSIZ) | (2UL << 18) | (1UL << 22) | - USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; - USBx_INEP(1U)->DIEPINT = 0xFF; - - USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; - USBx_OUTEP(2U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | - USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; - USBx_OUTEP(2U)->DOEPINT = 0xFF; - - USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; - USBx_OUTEP(3U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | - USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; - USBx_OUTEP(3U)->DOEPINT = 0xFF; - - // mark ready to receive - USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_REQ_SET_ADDRESS: - // set now? - USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7fU) << 4); - - #ifdef DEBUG_USB - print(" set address\n"); - #endif - - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - - break; - case USB_REQ_GET_DESCRIPTOR: - switch (setup.b.wValue.bw.lsb) { - case USB_DESC_TYPE_DEVICE: - //print(" writing device descriptor\n"); - - // set bcdDevice to hardware type - device_desc[13] = hw_type; - // setup transfer - USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - - //print("D"); - break; - case USB_DESC_TYPE_CONFIGURATION: - USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_DEVICE_QUALIFIER: - USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_STRING: - switch (setup.b.wValue.bw.msb) { - case STRING_OFFSET_LANGID: - USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_IMANUFACTURER: - USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_IPRODUCT: - USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_ISERIAL: - response[0] = 0x02 + (12 * 4); - response[1] = 0x03; - - // 96 bits = 12 bytes - for (int i = 0; i < 12; i++){ - uint8_t cc = ((uint8_t *)UID_BASE)[i]; - response[2 + (i * 4)] = to_hex_char((cc >> 4) & 0xFU); - response[2 + (i * 4) + 1] = '\0'; - response[2 + (i * 4) + 2] = to_hex_char((cc >> 0) & 0xFU); - response[2 + (i * 4) + 3] = '\0'; - } - - USB_WritePacket(response, MIN(response[0], setup.b.wLength.w), 0); - break; - case STRING_OFFSET_ICONFIGURATION: - USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); - break; - case 238: - USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); - break; - default: - // nothing - USB_WritePacket(0, 0, 0); - break; - } - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_BINARY_OBJECT_STORE: - USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - default: - // nothing here? - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - } - break; - case USB_REQ_GET_STATUS: - // empty response? - response[0] = 0; - response[1] = 0; - USB_WritePacket((void*)&response, 2, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_REQ_SET_INTERFACE: - // Store the alt setting number for IN EP behavior. - current_int0_alt_setting = setup.b.wValue.w; - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case WEBUSB_VENDOR_CODE: - // probably asking for allowed origins, which was removed from the spec - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case MS_VENDOR_CODE: - switch (setup.b.wIndex.w) { - // winusb 2.0 descriptor from BOS - case WINUSB_REQ_GET_DESCRIPTOR: - USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); - break; - // Extended Compat ID OS Descriptor - case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: - USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); - break; - // Extended Properties OS Descriptor - case WINUSB_REQ_GET_EXT_PROPS_OS: - USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); - break; - default: - USB_WritePacket_EP0(0, 0); - } - break; - default: - control_req.request = setup.b.bRequest; - control_req.param1 = setup.b.wValue.w; - control_req.param2 = setup.b.wIndex.w; - control_req.length = setup.b.wLength.w; - - resp_len = comms_control_handler(&control_req, response); - USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } -} - - - -// ***************************** USB port ***************************** - -void usb_irqhandler(void) { - //USBx->GINTMSK = 0; - static uint8_t usbdata[0x100] __attribute__((aligned(4))); - unsigned int gintsts = USBx->GINTSTS; - unsigned int gotgint = USBx->GOTGINT; - unsigned int daint = USBx_DEVICE->DAINT; - - // gintsts SUSPEND? 04008428 - #ifdef DEBUG_USB - puth(gintsts); - print(" "); - /*puth(USBx->GCCFG); - print(" ");*/ - puth(gotgint); - print(" ep "); - puth(daint); - print(" USB interrupt!\n"); - #endif - - if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0U) { - print("connector ID status change\n"); - } - - if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0U) { - #ifdef DEBUG_USB - print("USB reset\n"); - #endif - usb_reset(); - } - - if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0U) { - #ifdef DEBUG_USB - print("enumeration done\n"); - #endif - // Full speed, ENUMSPD - //puth(USBx_DEVICE->DSTS); - } - - if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0U) { - #ifdef DEBUG_USB - print("OTG int:"); - puth(USBx->GOTGINT); - print("\n"); - #endif - - // getting ADTOCHG - //USBx->GOTGINT = USBx->GOTGINT; - } - - // RX FIFO first - if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0U) { - // 1. Read the Receive status pop register - volatile unsigned int rxst = USBx->GRXSTSP; - int status = (rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17; - - #ifdef DEBUG_USB - print(" RX FIFO:"); - puth(rxst); - print(" status: "); - puth(status); - print(" len: "); - puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); - print("\n"); - #endif - - if (status == STS_DATA_UPDT) { - int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); - int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; - (void)USB_ReadPacket(&usbdata, len); - #ifdef DEBUG_USB - print(" data "); - puth(len); - print("\n"); - hexdump(&usbdata, len); - #endif - - if (endpoint == 2) { - comms_endpoint2_write((uint8_t *) usbdata, len); - } - - if (endpoint == 3) { - outep3_processing = true; - comms_can_write(usbdata, len); - } - } else if (status == STS_SETUP_UPDT) { - (void)USB_ReadPacket(&setup, 8); - #ifdef DEBUG_USB - print(" setup "); - hexdump(&setup, 8); - print("\n"); - #endif - } else { - // status is neither STS_DATA_UPDT or STS_SETUP_UPDT, skip - } - } - - /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { - // host - print("HPRT:"); - puth(USBx_HOST_PORT->HPRT); - print("\n"); - if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { - USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; - USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; - } - - }*/ - - if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { - // no global NAK, why is this getting set? - #ifdef DEBUG_USB - print("GLOBAL NAK\n"); - #endif - USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; - } - - if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0U) { - // we want to do "A-device host negotiation protocol" since we are the A-device - /*print("start request\n"); - puth(USBx->GOTGCTL); - print("\n");*/ - //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; - //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; - } - - // out endpoint hit - if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0U) { - #ifdef DEBUG_USB - print(" 0:"); - puth(USBx_OUTEP(0U)->DOEPINT); - print(" 2:"); - puth(USBx_OUTEP(2U)->DOEPINT); - print(" 3:"); - puth(USBx_OUTEP(3U)->DOEPINT); - print(" "); - puth(USBx_OUTEP(3U)->DOEPCTL); - print(" 4:"); - puth(USBx_OUTEP(4)->DOEPINT); - print(" OUT ENDPOINT\n"); - #endif - - if ((USBx_OUTEP(2U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { - #ifdef DEBUG_USB - print(" OUT2 PACKET XFRC\n"); - #endif - USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; - USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - } - - if ((USBx_OUTEP(3U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { - #ifdef DEBUG_USB - print(" OUT3 PACKET XFRC\n"); - #endif - // NAK cleared by process_can (if tx buffers have room) - outep3_processing = false; - refresh_can_tx_slots_available(); - } else if ((USBx_OUTEP(3U)->DOEPINT & 0x2000U) != 0U) { - #ifdef DEBUG_USB - print(" OUT3 PACKET WTF\n"); - #endif - // if NAK was set trigger this, unknown interrupt - // TODO: why was this here? fires when TX buffers when we can't clear NAK - // USBx_OUTEP(3U)->DOEPTSIZ = (1U << 19) | 0x40U; - // USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } else if ((USBx_OUTEP(3U)->DOEPINT) != 0U) { - #ifdef DEBUG_USB - print("OUTEP3 error "); - puth(USBx_OUTEP(3U)->DOEPINT); - print("\n"); - #endif - } else { - // USBx_OUTEP(3U)->DOEPINT is 0, ok to skip - } - - if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0U) { - // ready for next packet - USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (1U << 3); - } - - // respond to setup packets - if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0U) { - usb_setup(); - } - - USBx_OUTEP(0U)->DOEPINT = USBx_OUTEP(0U)->DOEPINT; - USBx_OUTEP(2U)->DOEPINT = USBx_OUTEP(2U)->DOEPINT; - USBx_OUTEP(3U)->DOEPINT = USBx_OUTEP(3U)->DOEPINT; - } - - // interrupt endpoint hit (Page 1221) - if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0U) { - #ifdef DEBUG_USB - print(" "); - puth(USBx_INEP(0U)->DIEPINT); - print(" "); - puth(USBx_INEP(1U)->DIEPINT); - print(" IN ENDPOINT\n"); - #endif - - // Should likely check the EP of the IN request even if there is - // only one IN endpoint. - - // No need to set NAK in OTG_DIEPCTL0 when nothing to send, - // Appears USB core automatically sets NAK. WritePacket clears it. - - // Handle the two interface alternate settings. Setting 0 has EP1 - // as bulk. Setting 1 has EP1 as interrupt. The code to handle - // these two EP variations are very similar and can be - // restructured for smaller code footprint. Keeping split out for - // now for clarity. - - //TODO add default case. Should it NAK? - switch (current_int0_alt_setting) { - case 0: ////// Bulk config - // *** IN token received when TxFIFO is empty - if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - // TODO: always assuming max len, can we get the length? - USB_WritePacket((void *)response, comms_can_read(response, 0x40), 1); - } - break; - - case 1: ////// Interrupt config - // *** IN token received when TxFIFO is empty - if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - // TODO: always assuming max len, can we get the length? - int len = comms_can_read(response, 0x40); - if (len > 0) { - USB_WritePacket((void *)response, len, 1); - } - } - break; - default: - print("current_int0_alt_setting value invalid\n"); - break; - } - - if ((USBx_INEP(0U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - - if ((ep0_txlen != 0U) && ((USBx_INEP(0U)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40U)) { - uint16_t len = MIN(ep0_txlen, 0x40); - USB_WritePacket(ep0_txdata, len, 0); - ep0_txdata = &ep0_txdata[len]; - ep0_txlen -= len; - if (ep0_txlen == 0U) { - ep0_txdata = NULL; - USBx_DEVICE->DIEPEMPMSK &= ~1; - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } - } - } - - // clear interrupts - USBx_INEP(0U)->DIEPINT = USBx_INEP(0U)->DIEPINT; // Why ep0? - USBx_INEP(1U)->DIEPINT = USBx_INEP(1U)->DIEPINT; - } - - // clear all interrupts we handled - USBx_DEVICE->DAINT = daint; - USBx->GOTGINT = gotgint; - USBx->GINTSTS = gintsts; - - //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); -} - -void can_tx_comms_resume_usb(void) { - ENTER_CRITICAL(); - if (!outep3_processing && (USBx_OUTEP(3U)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0U) { - USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; - USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - } - EXIT_CRITICAL(); -} +#endif From b8dc669084de39dbde53590a6e0442c7aa9b45b2 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 14:49:01 +0800 Subject: [PATCH 02/45] fix: add missing declarations for fan_state and REGISTER_INTERRUPT - Move fan_state_t struct and extern fan_state to fan.h - Move REGISTER_INTERRUPT macro and interrupt struct to interrupts.h - This fixes compilation errors where llfan.h and llusb.h couldn't find these definitions --- board/drivers/fan.h | 11 +++++++++++ board/drivers/interrupts.h | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 90ee7c413e3..78992d3ae4c 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -3,8 +3,19 @@ #include +struct fan_state_t { + uint16_t tach_counter; + uint16_t rpm; + uint8_t power; + float error_integral; + uint8_t cooldown_counter; +}; +extern struct fan_state_t fan_state; + void fan_set_power(uint8_t percentage); +void llfan_init(void); void fan_init(void); +// Call this at FAN_TICK_FREQ void fan_tick(void); #endif diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index c29b073175d..140d3cf8c5e 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -3,6 +3,27 @@ #include +typedef struct interrupt { + IRQn_Type irq_type; + void (*handler)(void); + uint32_t call_counter; + uint32_t call_rate; + uint32_t max_call_rate; // Call rate is defined as the amount of calls each second + uint32_t call_rate_fault; +} interrupt; + +extern interrupt interrupts[NUM_INTERRUPTS]; + +#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ + interrupts[irq_num].irq_type = (irq_num); \ + interrupts[irq_num].handler = (func_ptr); \ + interrupts[irq_num].call_counter = 0U; \ + interrupts[irq_num].call_rate = 0U; \ + interrupts[irq_num].max_call_rate = (call_rate_max); \ + interrupts[irq_num].call_rate_fault = (rate_fault); + +extern float interrupt_load; + void unused_interrupt_handler(void); void handle_interrupt(IRQn_Type irq_type); void interrupt_timer_handler(void); From d035e0a1a54b0fd3d375ecfaa962e8f26f9bfab0 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 14:57:35 +0800 Subject: [PATCH 03/45] fix: resolve compilation errors after header/source split - Move REGISTER_INTERRUPT macro to interrupts.h - Add extern declarations for uart_ring buffers in uart.h - Remove duplicate interrupt definitions from drivers.h --- board/drivers/drivers.h | 32 -------------------------------- board/drivers/harness.h | 27 +++++++++++++++++++++++++++ board/drivers/interrupts.h | 7 +++++++ board/drivers/spi.h | 10 ++++++++++ board/drivers/uart.h | 4 ++++ 5 files changed, 48 insertions(+), 32 deletions(-) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index 4242bfe6ccd..a53595b33f5 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -89,38 +89,6 @@ bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); -// ******************** interrupts ******************** - -typedef struct interrupt { - IRQn_Type irq_type; - void (*handler)(void); - uint32_t call_counter; - uint32_t call_rate; - uint32_t max_call_rate; // Call rate is defined as the amount of calls each second - uint32_t call_rate_fault; -} interrupt; - -void interrupt_timer_init(void); -uint32_t microsecond_timer_get(void); -void unused_interrupt_handler(void); - -extern interrupt interrupts[NUM_INTERRUPTS]; - -#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ - interrupts[irq_num].irq_type = (irq_num); \ - interrupts[irq_num].handler = (func_ptr); \ - interrupts[irq_num].call_counter = 0U; \ - interrupts[irq_num].call_rate = 0U; \ - interrupts[irq_num].max_call_rate = (call_rate_max); \ - interrupts[irq_num].call_rate_fault = (rate_fault); - -extern float interrupt_load; - -void handle_interrupt(IRQn_Type irq_type); -// Every second -void interrupt_timer_handler(void); -void init_interrupts(bool check_rate_limit); - #endif // STM32H7 // ******************** registers ******************** diff --git a/board/drivers/harness.h b/board/drivers/harness.h index 02d54883cdb..c9a83ca39da 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -2,6 +2,33 @@ #define DRIVERS_HARNESS_H #include +#include + +#define HARNESS_STATUS_NC 0U +#define HARNESS_STATUS_NORMAL 1U +#define HARNESS_STATUS_FLIPPED 2U + +struct harness_t { + uint8_t status; + uint16_t sbu1_voltage_mV; + uint16_t sbu2_voltage_mV; + bool relay_driven; + bool sbu_adc_lock; +}; +extern struct harness_t harness; + +struct harness_configuration { + GPIO_TypeDef * const GPIO_SBU1; + GPIO_TypeDef * const GPIO_SBU2; + GPIO_TypeDef * const GPIO_relay_SBU1; + GPIO_TypeDef * const GPIO_relay_SBU2; + const uint8_t pin_SBU1; + const uint8_t pin_SBU2; + const uint8_t pin_relay_SBU1; + const uint8_t pin_relay_SBU2; + const adc_signal_t adc_signal_SBU1; + const adc_signal_t adc_signal_SBU2; +}; void set_intercept_relay(bool intercept, bool ignition_relay); bool harness_check_ignition(void); diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index 140d3cf8c5e..dec4acc0107 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -2,6 +2,9 @@ #define DRIVERS_INTERRUPTS_H #include +#include + +#ifdef STM32H7 typedef struct interrupt { IRQn_Type irq_type; @@ -24,9 +27,13 @@ extern interrupt interrupts[NUM_INTERRUPTS]; extern float interrupt_load; +void interrupt_timer_init(void); +uint32_t microsecond_timer_get(void); void unused_interrupt_handler(void); void handle_interrupt(IRQn_Type irq_type); void interrupt_timer_handler(void); void init_interrupts(bool check_rate_limit); +#endif // STM32H7 + #endif diff --git a/board/drivers/spi.h b/board/drivers/spi.h index af87f1f40d2..f6a6416131f 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -4,6 +4,16 @@ #include #include +// got max rate from hitting a non-existent endpoint +// in a tight loop, plus some buffer +#define SPI_IRQ_RATE 16000U + +#define SPI_BUF_SIZE 4096U +extern uint8_t spi_buf_rx[SPI_BUF_SIZE]; +extern uint8_t spi_buf_tx[SPI_BUF_SIZE]; + +extern uint16_t spi_error_count; + #define SPI_CHECKSUM_START 0xABU #define SPI_SYNC_BYTE 0x5AU #define SPI_HACK 0x79U diff --git a/board/drivers/uart.h b/board/drivers/uart.h index a72804fb3c5..e747dfac960 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -20,6 +20,10 @@ typedef struct uart_ring { bool overwrite; } uart_ring; +// UART ring buffers (defined in uart.c) +extern uart_ring uart_ring_debug; +extern uart_ring uart_ring_som_debug; + void debug_ring_callback(uart_ring *ring); void uart_tx_ring(uart_ring *q); uart_ring *get_ring_by_number(int a); From b03d594d0a68316c3ddc48b5fa1475daf4e29cdf Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 14:59:07 +0800 Subject: [PATCH 04/45] fix: update SConscript to compile driver .c files and add missing declarations - Add driver_sources list to SConscript for both bootstub and main builds - Add harness_t struct and extern harness to harness.h - Add SPI_IRQ_RATE, SPI_BUF_SIZE and extern declarations to spi.h --- SConscript | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/SConscript b/SConscript index 6732204d455..2bed3e5d02d 100644 --- a/SConscript +++ b/SConscript @@ -100,6 +100,27 @@ def build_project(project_name, project, main, extra_flags): startup = env.Object(project["STARTUP_FILE"]) + # Driver source files (split from header-only) + driver_sources = [ + "./board/drivers/bootkick.c", + "./board/drivers/can_common.c", + "./board/drivers/clock_source.c", + "./board/drivers/fake_siren.c", + "./board/drivers/fan.c", + "./board/drivers/fdcan.c", + "./board/drivers/gpio.c", + "./board/drivers/harness.c", + "./board/drivers/interrupts.c", + "./board/drivers/led.c", + "./board/drivers/pwm.c", + "./board/drivers/registers.c", + "./board/drivers/simple_watchdog.c", + "./board/drivers/spi.c", + "./board/drivers/timers.c", + "./board/drivers/uart.c", + "./board/drivers/usb.c", + ] + # Build bootstub bs_env = env.Clone() bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") @@ -108,14 +129,14 @@ def build_project(project_name, project, main, extra_flags): "./board/crypto/rsa.c", "./board/crypto/sha.c", "./board/bootstub.c", - ]) + ] + driver_sources) bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf) # Build + sign main (aka app) main_elf = env.Program(f"{project_dir}/main.elf", [ startup, main - ], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) + ] + driver_sources, LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf) sign_py = File(f"./board/crypto/sign.py").srcnode().relpath env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}") From b52dbbac7f902ade1f64290b636eae0f2e6fe343 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 15:01:03 +0800 Subject: [PATCH 05/45] fix: add drivers.h includes for type definitions --- board/drivers/led.c | 2 +- board/drivers/pwm.c | 2 +- board/drivers/pwm.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/board/drivers/led.c b/board/drivers/led.c index b8989ce8de3..8bc7f551674 100644 --- a/board/drivers/led.c +++ b/board/drivers/led.c @@ -1,8 +1,8 @@ // LED driver implementation +#include "board/drivers/drivers.h" #include "drivers/led.h" #include "drivers/pwm.h" #include "drivers/gpio.h" -#include "board.h" void led_set(uint8_t color, bool enabled) { if (color < 3U) { diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c index a2d1c009b27..5994b2483fd 100644 --- a/board/drivers/pwm.c +++ b/board/drivers/pwm.c @@ -1,6 +1,6 @@ // PWM driver implementation +#include "board/drivers/drivers.h" #include "drivers/pwm.h" -#include "drivers/registers.h" #define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h index e9bd2b07f68..bb05c0f096b 100644 --- a/board/drivers/pwm.h +++ b/board/drivers/pwm.h @@ -2,7 +2,8 @@ #define DRIVERS_PWM_H #include -#include "stm32fx_def.h" + +#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz // TODO: Implement for 32-bit timers From 3f7e548c6f1b33e7fe2f64c73d8adc877024fdfb Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 15:33:25 +0800 Subject: [PATCH 06/45] fix: use separate object directories for bootstub and main driver builds SCons was complaining about 'Two environments with different actions' because bootstub and main both compile driver sources with different flags (-DBOOTSTUB vs no flag). Now bootstub uses a separate output directory for its driver object files. --- SConscript | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SConscript b/SConscript index 2bed3e5d02d..a76e0f1a3af 100644 --- a/SConscript +++ b/SConscript @@ -124,19 +124,23 @@ def build_project(project_name, project, main, extra_flags): # Build bootstub bs_env = env.Clone() bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") + # Use separate object directory for bootstub driver objects + bs_env['OBJPREFIX'] = Dir(f'./board/obj/{project_name}/bootstub/') + bs_driver_objs = bs_env.Object(driver_sources) bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [ startup, "./board/crypto/rsa.c", "./board/crypto/sha.c", "./board/bootstub.c", - ] + driver_sources) + ] + bs_driver_objs) bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf) # Build + sign main (aka app) + main_driver_objs = env.Object(driver_sources) main_elf = env.Program(f"{project_dir}/main.elf", [ startup, main - ] + driver_sources, LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) + ] + main_driver_objs, LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf) sign_py = File(f"./board/crypto/sign.py").srcnode().relpath env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}") From 13db127302b96976d1651c0df6584958f1624f15 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 16:04:24 +0800 Subject: [PATCH 07/45] fix: add uart_ring type to drivers.h for BOOTSTUB, fix fdcan.h includes --- board/drivers/drivers.h | 23 ++++++++++++++++++++++- board/drivers/fdcan.h | 4 +--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index a53595b33f5..dcdd13f9d95 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -138,7 +138,28 @@ void spi_rx_done(void); void spi_tx_done(bool reset); // ******************** uart ******************** -// Type definitions and function declarations moved to drivers/uart.h + +#define FIFO_SIZE_INT 0x400U + +typedef struct uart_ring { + volatile uint16_t w_ptr_tx; + volatile uint16_t r_ptr_tx; + uint8_t *elems_tx; + uint32_t tx_fifo_size; + volatile uint16_t w_ptr_rx; + volatile uint16_t r_ptr_rx; + uint8_t *elems_rx; + uint32_t rx_fifo_size; + USART_TypeDef *uart; + void (*callback)(struct uart_ring*); + bool overwrite; +} uart_ring; + +// UART ring buffers (defined in uart.c or stm32h7_config.h for BOOTSTUB) +extern uart_ring uart_ring_debug; +extern uart_ring uart_ring_som_debug; + +// Function declarations in drivers/uart.h // ******************** usb ******************** // Function declarations moved to drivers/usb.h diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index dacf8dd60f8..daa851150a4 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -1,9 +1,7 @@ #ifndef DRIVERS_FDCAN_H #define DRIVERS_FDCAN_H -#include -#include -#include "stm32h7xx_hal_fdcan.h" +#include "board/drivers/drivers.h" void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number); void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg); From 10dbfb314007d05c2289a05e092f67472afd08b8 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 16:28:55 +0800 Subject: [PATCH 08/45] fix: include drivers.h before uart_ring_som_debug definition in BOOTSTUB mode --- board/stm32h7/stm32h7_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index 6fd6eae2d38..f615c8b1926 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -59,6 +59,7 @@ separate IRQs for RX and TX. #include "board/drivers/registers.h" #include "board/drivers/interrupts.h" +#include "board/drivers/drivers.h" #ifdef BOOTSTUB uart_ring uart_ring_som_debug; From 1c2b550103823385c70488d6720d33857eeb9931 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 16:34:27 +0800 Subject: [PATCH 09/45] fix: remove duplicate struct definitions from harness.h and uart.h Now these headers include drivers.h which contains the struct definitions --- board/drivers/harness.h | 35 ++--------------------------------- board/drivers/uart.h | 29 ++--------------------------- 2 files changed, 4 insertions(+), 60 deletions(-) diff --git a/board/drivers/harness.h b/board/drivers/harness.h index c9a83ca39da..0fc172bfedc 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -1,38 +1,7 @@ -#ifndef DRIVERS_HARNESS_H -#define DRIVERS_HARNESS_H - -#include -#include - -#define HARNESS_STATUS_NC 0U -#define HARNESS_STATUS_NORMAL 1U -#define HARNESS_STATUS_FLIPPED 2U - -struct harness_t { - uint8_t status; - uint16_t sbu1_voltage_mV; - uint16_t sbu2_voltage_mV; - bool relay_driven; - bool sbu_adc_lock; -}; -extern struct harness_t harness; - -struct harness_configuration { - GPIO_TypeDef * const GPIO_SBU1; - GPIO_TypeDef * const GPIO_SBU2; - GPIO_TypeDef * const GPIO_relay_SBU1; - GPIO_TypeDef * const GPIO_relay_SBU2; - const uint8_t pin_SBU1; - const uint8_t pin_SBU2; - const uint8_t pin_relay_SBU1; - const uint8_t pin_relay_SBU2; - const adc_signal_t adc_signal_SBU1; - const adc_signal_t adc_signal_SBU2; -}; +#include "board/drivers/drivers.h" +// Function declarations void set_intercept_relay(bool intercept, bool ignition_relay); bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); - -#endif diff --git a/board/drivers/uart.h b/board/drivers/uart.h index e747dfac960..a7a35827a78 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -1,29 +1,6 @@ -#ifndef DRIVERS_UART_H -#define DRIVERS_UART_H - -#include -#include - -#define FIFO_SIZE_INT 0x400U - -typedef struct uart_ring { - volatile uint16_t w_ptr_tx; - volatile uint16_t r_ptr_tx; - uint8_t *elems_tx; - uint32_t tx_fifo_size; - volatile uint16_t w_ptr_rx; - volatile uint16_t r_ptr_rx; - uint8_t *elems_rx; - uint32_t rx_fifo_size; - USART_TypeDef *uart; - void (*callback)(struct uart_ring*); - bool overwrite; -} uart_ring; - -// UART ring buffers (defined in uart.c) -extern uart_ring uart_ring_debug; -extern uart_ring uart_ring_som_debug; +#include "board/drivers/drivers.h" +// Function declarations void debug_ring_callback(uart_ring *ring); void uart_tx_ring(uart_ring *q); uart_ring *get_ring_by_number(int a); @@ -43,5 +20,3 @@ void puth4(unsigned int i); #if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) void hexdump(const void *a, int l); #endif - -#endif From a104951cd2e6db018e28f4b92e729ba2d48f6479 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Thu, 5 Mar 2026 16:35:17 +0800 Subject: [PATCH 10/45] fix: remove duplicate fan_state_t definition from fan.h --- board/drivers/fan.h | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 78992d3ae4c..854c01bb958 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -1,21 +1,8 @@ -#ifndef DRIVERS_FAN_H -#define DRIVERS_FAN_H - -#include - -struct fan_state_t { - uint16_t tach_counter; - uint16_t rpm; - uint8_t power; - float error_integral; - uint8_t cooldown_counter; -}; -extern struct fan_state_t fan_state; +#include "board/drivers/drivers.h" +// Function declarations void fan_set_power(uint8_t percentage); void llfan_init(void); void fan_init(void); // Call this at FAN_TICK_FREQ void fan_tick(void); - -#endif From d029671411ed03928ebfce59c984d062bacb8964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agent=20=E6=B8=A1=E6=96=87?= Date: Sat, 7 Mar 2026 15:54:53 +0800 Subject: [PATCH 11/45] fix: resolve compilation errors in can_comms.h and sound.h - Add extern declaration for can_rx_q in can_comms.h - Add __attribute__((unused)) to sound_stop_dac to suppress warning --- board/can_comms.h | 3 +++ board/stm32h7/sound.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/board/can_comms.h b/board/can_comms.h index f79c8ba4e46..188a1bce21f 100644 --- a/board/can_comms.h +++ b/board/can_comms.h @@ -34,6 +34,9 @@ which is sent by the host on each start of a connection. */ +// External CAN RX queue defined in drivers/can_common.c +extern can_ring can_rx_q; + typedef struct { uint32_t ptr; uint32_t tail_size; diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h index 80c9317fda9..5569e7a9710 100644 --- a/board/stm32h7/sound.h +++ b/board/stm32h7/sound.h @@ -143,7 +143,7 @@ void sound_init_dac(void) { DMA1_Stream1->CR = DMA_SxCR_DBM | (0b11UL << DMA_SxCR_PL_Pos) | (0b01UL << DMA_SxCR_MSIZE_Pos) | (0b01UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | (1U << DMA_SxCR_DIR_Pos); } -static void sound_stop_dac(void) { +__attribute__((unused)) static void sound_stop_dac(void) { register_clear_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); BDMA->IFCR = 0xFFFFFFFFU; From f3f4c7a25c1795d481d7bc6dc7ddf2b45855bf73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?agent=20=E6=B8=A1=E6=96=87?= Date: Sat, 7 Mar 2026 15:57:46 +0800 Subject: [PATCH 12/45] fix: add forward declaration for refresh_can_tx_slots_available --- board/can_comms.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/board/can_comms.h b/board/can_comms.h index 188a1bce21f..9bc4244a3ab 100644 --- a/board/can_comms.h +++ b/board/can_comms.h @@ -37,6 +37,9 @@ // External CAN RX queue defined in drivers/can_common.c extern can_ring can_rx_q; +// Forward declaration +void refresh_can_tx_slots_available(void); + typedef struct { uint32_t ptr; uint32_t tail_size; From 3f3db7cc5d95b35f4ee3acba0f6a4c459d631984 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Sun, 8 Mar 2026 01:34:28 +0800 Subject: [PATCH 13/45] fix: include can_common.h for can_set_checksum declaration --- board/drivers/drivers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index dcdd13f9d95..4fc15f8bddc 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -3,6 +3,7 @@ #include "board/can.h" #include "board/health.h" #include "board/crc.h" +#include "board/drivers/can_common.h" #ifdef STM32H7 #include "board/stm32h7/lladc_declarations.h" #endif From 2a93ec358f611e430eb8cf78171698d0098e512e Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Sun, 8 Mar 2026 01:36:22 +0800 Subject: [PATCH 14/45] fix: correct include paths in driver source files --- board/drivers/bootkick.c | 2 +- board/drivers/can_common.c | 2 +- board/drivers/clock_source.c | 2 +- board/drivers/fake_siren.c | 2 +- board/drivers/fan.c | 2 +- board/drivers/fdcan.c | 2 +- board/drivers/gpio.c | 2 +- board/drivers/harness.c | 2 +- board/drivers/interrupts.c | 2 +- board/drivers/led.c | 6 +++--- board/drivers/pwm.c | 2 +- board/drivers/registers.c | 2 +- board/drivers/simple_watchdog.c | 2 +- board/drivers/spi.c | 2 +- board/drivers/timers.c | 2 +- board/drivers/uart.c | 2 +- board/drivers/usb.c | 2 +- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c index 4c4c64bea24..132a870e9a6 100644 --- a/board/drivers/bootkick.c +++ b/board/drivers/bootkick.c @@ -1,4 +1,4 @@ -#include "drivers/bootkick.h" +#include "board/drivers/bootkick.h" #include "board/drivers/drivers.h" bool bootkick_reset_triggered = false; diff --git a/board/drivers/can_common.c b/board/drivers/can_common.c index cf65b71411a..539212b6560 100644 --- a/board/drivers/can_common.c +++ b/board/drivers/can_common.c @@ -1,4 +1,4 @@ -#include "drivers/can_common.h" +#include "board/drivers/can_common.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" #include "board/drivers/registers.h" diff --git a/board/drivers/clock_source.c b/board/drivers/clock_source.c index 27954ae18ca..fcebbdb772b 100644 --- a/board/drivers/clock_source.c +++ b/board/drivers/clock_source.c @@ -1,4 +1,4 @@ -#include "drivers/clock_source.h" +#include "board/drivers/clock_source.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" #include "board/drivers/gpio.h" diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c index 599f6c874db..a8abe25a734 100644 --- a/board/drivers/fake_siren.c +++ b/board/drivers/fake_siren.c @@ -1,4 +1,4 @@ -#include "drivers/fake_siren.h" +#include "board/drivers/fake_siren.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" #include "board/stm32h7/lli2c.h" diff --git a/board/drivers/fan.c b/board/drivers/fan.c index 6e3b9f8be59..abd722a5112 100644 --- a/board/drivers/fan.c +++ b/board/drivers/fan.c @@ -1,4 +1,4 @@ -#include "drivers/fan.h" +#include "board/drivers/fan.h" #include "board/drivers/drivers.h" struct fan_state_t fan_state; diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index d5cc8fcfe95..72093f96ddc 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -1,4 +1,4 @@ -#include "drivers/fdcan.h" +#include "board/drivers/fdcan.h" #include "board/drivers/drivers.h" FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; diff --git a/board/drivers/gpio.c b/board/drivers/gpio.c index 3c2e2debd42..077b22c4a9a 100644 --- a/board/drivers/gpio.c +++ b/board/drivers/gpio.c @@ -1,4 +1,4 @@ -#include "drivers/gpio.h" +#include "board/drivers/gpio.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" #include "board/drivers/registers.h" diff --git a/board/drivers/harness.c b/board/drivers/harness.c index f7276ef41e3..85f97a60754 100644 --- a/board/drivers/harness.c +++ b/board/drivers/harness.c @@ -1,4 +1,4 @@ -#include "drivers/harness.h" +#include "board/drivers/harness.h" #include "board/drivers/drivers.h" struct harness_t harness; diff --git a/board/drivers/interrupts.c b/board/drivers/interrupts.c index 90a5229a5b4..328184b868e 100644 --- a/board/drivers/interrupts.c +++ b/board/drivers/interrupts.c @@ -1,4 +1,4 @@ -#include "drivers/interrupts.h" +#include "board/drivers/interrupts.h" #include "board/drivers/drivers.h" interrupt interrupts[NUM_INTERRUPTS]; diff --git a/board/drivers/led.c b/board/drivers/led.c index 8bc7f551674..50116acae2a 100644 --- a/board/drivers/led.c +++ b/board/drivers/led.c @@ -1,8 +1,8 @@ // LED driver implementation #include "board/drivers/drivers.h" -#include "drivers/led.h" -#include "drivers/pwm.h" -#include "drivers/gpio.h" +#include "board/drivers/led.h" +#include "board/drivers/pwm.h" +#include "board/drivers/gpio.h" void led_set(uint8_t color, bool enabled) { if (color < 3U) { diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c index 5994b2483fd..dcb8ff27c4c 100644 --- a/board/drivers/pwm.c +++ b/board/drivers/pwm.c @@ -1,6 +1,6 @@ // PWM driver implementation #include "board/drivers/drivers.h" -#include "drivers/pwm.h" +#include "board/drivers/pwm.h" #define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz diff --git a/board/drivers/registers.c b/board/drivers/registers.c index 36e325b4bab..1d04979b49d 100644 --- a/board/drivers/registers.c +++ b/board/drivers/registers.c @@ -1,4 +1,4 @@ -#include "drivers/registers.h" +#include "board/drivers/registers.h" #include "board/drivers/drivers.h" typedef struct reg { diff --git a/board/drivers/simple_watchdog.c b/board/drivers/simple_watchdog.c index 93f8eb7c509..2e7fb5e8df9 100644 --- a/board/drivers/simple_watchdog.c +++ b/board/drivers/simple_watchdog.c @@ -1,4 +1,4 @@ -#include "drivers/simple_watchdog.h" +#include "board/drivers/simple_watchdog.h" #include "board/drivers/drivers.h" static simple_watchdog_state_t wd_state; diff --git a/board/drivers/spi.c b/board/drivers/spi.c index a6375a5ba42..6babb1a7e45 100644 --- a/board/drivers/spi.c +++ b/board/drivers/spi.c @@ -1,4 +1,4 @@ -#include "drivers/spi.h" +#include "board/drivers/spi.h" #include "board/drivers/drivers.h" // H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 diff --git a/board/drivers/timers.c b/board/drivers/timers.c index 1fb46e33e67..21fef426d52 100644 --- a/board/drivers/timers.c +++ b/board/drivers/timers.c @@ -1,4 +1,4 @@ -#include "drivers/timers.h" +#include "board/drivers/timers.h" #include "board/drivers/drivers.h" static void timer_init(TIM_TypeDef *TIM, int psc) { diff --git a/board/drivers/uart.c b/board/drivers/uart.c index 85549524dda..dedae2d7bc3 100644 --- a/board/drivers/uart.c +++ b/board/drivers/uart.c @@ -1,4 +1,4 @@ -#include "drivers/uart.h" +#include "board/drivers/uart.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" diff --git a/board/drivers/usb.c b/board/drivers/usb.c index d9cfba4ad05..599712ee4a8 100644 --- a/board/drivers/usb.c +++ b/board/drivers/usb.c @@ -1,4 +1,4 @@ -#include "drivers/usb.h" +#include "board/drivers/usb.h" #include "board/drivers/drivers.h" typedef union { From d7b5b7853548af45dd2d6159f4e18703f84581fa Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Sun, 8 Mar 2026 01:38:15 +0800 Subject: [PATCH 15/45] fix: add stdint.h and stdbool.h includes to health.h --- board/health.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/board/health.h b/board/health.h index 60f12460656..d4c97e281f6 100644 --- a/board/health.h +++ b/board/health.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + // When changing these structs, python/__init__.py needs to be kept up to date! #define HEALTH_PACKET_VERSION 18 From 1ebc07a38fd8dde050135d1546170ceaa5a4f6ec Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 00:15:10 +0800 Subject: [PATCH 16/45] fix: include board/config.h in all driver source files The driver .c files need to include board/config.h first to get stm32h7xx.h (which defines ADC_TypeDef) before including drivers.h. This fixes the build error: lladc_declarations.h:29:3: error: unknown type name 'ADC_TypeDef' --- board/drivers/bootkick.c | 1 + board/drivers/can_common.c | 1 + board/drivers/clock_source.c | 1 + board/drivers/fake_siren.c | 1 + board/drivers/fan.c | 1 + board/drivers/fdcan.c | 1 + board/drivers/gpio.c | 1 + board/drivers/harness.c | 1 + board/drivers/interrupts.c | 1 + board/drivers/led.c | 1 + board/drivers/pwm.c | 1 + board/drivers/registers.c | 1 + board/drivers/simple_watchdog.c | 1 + board/drivers/spi.c | 1 + board/drivers/timers.c | 1 + board/drivers/uart.c | 1 + board/drivers/usb.c | 1 + 17 files changed, 17 insertions(+) diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c index 132a870e9a6..56b46a1dca7 100644 --- a/board/drivers/bootkick.c +++ b/board/drivers/bootkick.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/bootkick.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/can_common.c b/board/drivers/can_common.c index 539212b6560..fbb2f7671cc 100644 --- a/board/drivers/can_common.c +++ b/board/drivers/can_common.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/can_common.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" diff --git a/board/drivers/clock_source.c b/board/drivers/clock_source.c index fcebbdb772b..55f382e68d9 100644 --- a/board/drivers/clock_source.c +++ b/board/drivers/clock_source.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/clock_source.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c index a8abe25a734..fd973f03211 100644 --- a/board/drivers/fake_siren.c +++ b/board/drivers/fake_siren.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/fake_siren.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" diff --git a/board/drivers/fan.c b/board/drivers/fan.c index abd722a5112..4540f89a4ee 100644 --- a/board/drivers/fan.c +++ b/board/drivers/fan.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/fan.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index 72093f96ddc..c0366312751 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/fdcan.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/gpio.c b/board/drivers/gpio.c index 077b22c4a9a..f6f8b7452f6 100644 --- a/board/drivers/gpio.c +++ b/board/drivers/gpio.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/gpio.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" diff --git a/board/drivers/harness.c b/board/drivers/harness.c index 85f97a60754..016c3b6f301 100644 --- a/board/drivers/harness.c +++ b/board/drivers/harness.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/harness.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/interrupts.c b/board/drivers/interrupts.c index 328184b868e..b3d273dd121 100644 --- a/board/drivers/interrupts.c +++ b/board/drivers/interrupts.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/interrupts.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/led.c b/board/drivers/led.c index 50116acae2a..663c4269b14 100644 --- a/board/drivers/led.c +++ b/board/drivers/led.c @@ -1,3 +1,4 @@ +#include "board/config.h" // LED driver implementation #include "board/drivers/drivers.h" #include "board/drivers/led.h" diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c index dcb8ff27c4c..2a7f187ce53 100644 --- a/board/drivers/pwm.c +++ b/board/drivers/pwm.c @@ -1,3 +1,4 @@ +#include "board/config.h" // PWM driver implementation #include "board/drivers/drivers.h" #include "board/drivers/pwm.h" diff --git a/board/drivers/registers.c b/board/drivers/registers.c index 1d04979b49d..49cd6935b70 100644 --- a/board/drivers/registers.c +++ b/board/drivers/registers.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/registers.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/simple_watchdog.c b/board/drivers/simple_watchdog.c index 2e7fb5e8df9..a29403ad378 100644 --- a/board/drivers/simple_watchdog.c +++ b/board/drivers/simple_watchdog.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/simple_watchdog.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/spi.c b/board/drivers/spi.c index 6babb1a7e45..cfcdc746f29 100644 --- a/board/drivers/spi.c +++ b/board/drivers/spi.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/spi.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/timers.c b/board/drivers/timers.c index 21fef426d52..7ad1a4a4d34 100644 --- a/board/drivers/timers.c +++ b/board/drivers/timers.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/timers.h" #include "board/drivers/drivers.h" diff --git a/board/drivers/uart.c b/board/drivers/uart.c index dedae2d7bc3..2cf1fde6304 100644 --- a/board/drivers/uart.c +++ b/board/drivers/uart.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/uart.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" diff --git a/board/drivers/usb.c b/board/drivers/usb.c index 599712ee4a8..c45860d5a29 100644 --- a/board/drivers/usb.c +++ b/board/drivers/usb.c @@ -1,3 +1,4 @@ +#include "board/config.h" #include "board/drivers/usb.h" #include "board/drivers/drivers.h" From 5360c4fb7ff777b6b342a4ce91de0b224fc0d02d Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 00:26:34 +0800 Subject: [PATCH 17/45] chore: trigger CI rebuild From fec69c15f979ef464fbdb948a651abb3955e92d6 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:02:36 +0800 Subject: [PATCH 18/45] fix: add missing includes and declarations in drivers.h - Include board/boards/board_declarations.h for BootState type - Add refresh_can_tx_slots_available() declaration that was lost during refactoring This fixes build errors: - unknown type name 'BootState' - implicit declaration of function 'refresh_can_tx_slots_available' --- board/drivers/drivers.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index 4fc15f8bddc..48f1648e0de 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -1,6 +1,7 @@ #pragma once #include "board/can.h" +#include "board/boards/board_declarations.h" #include "board/health.h" #include "board/crc.h" #include "board/drivers/can_common.h" @@ -163,4 +164,7 @@ extern uart_ring uart_ring_som_debug; // Function declarations in drivers/uart.h // ******************** usb ******************** -// Function declarations moved to drivers/usb.h + +void usb_init(void); +void refresh_can_tx_slots_available(void); +void can_tx_comms_resume_usb(void); From ece42c2df64192285da785fa9eff6683af6526dc Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:05:56 +0800 Subject: [PATCH 19/45] fix: extract BootState to separate header and add missing declaration - Create board/boot_state.h with BootState enum definition - Include boot_state.h in drivers.h instead of board_declarations.h (avoiding struct board redefinition conflict with body/boards) - Add refresh_can_tx_slots_available() declaration to can_common.h This fixes: - 'unknown type name BootState' in bootkick.c - 'redefinition of struct board' when building for PANDA_BODY - 'implicit declaration of refresh_can_tx_slots_available' in can_common.c --- board/boot_state.h | 8 ++++++++ board/drivers/can_common.h | 1 + board/drivers/drivers.h | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 board/boot_state.h diff --git a/board/boot_state.h b/board/boot_state.h new file mode 100644 index 00000000000..84ba57fe45d --- /dev/null +++ b/board/boot_state.h @@ -0,0 +1,8 @@ +#pragma once + +// Boot state enum used by bootkick driver +typedef enum { + BOOT_STANDBY, + BOOT_BOOTKICK, + BOOT_RESET, +} BootState; diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 37ba1f97eb1..b5f7379da4b 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -69,4 +69,5 @@ bool can_check_checksum(CANPacket_t *packet); void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook); bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len); +void refresh_can_tx_slots_available(void); #endif diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index 48f1648e0de..d002c59ca7b 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -1,7 +1,7 @@ #pragma once +#include "board/boot_state.h" #include "board/can.h" -#include "board/boards/board_declarations.h" #include "board/health.h" #include "board/crc.h" #include "board/drivers/can_common.h" From 221bcb0f99719af6fcb3797d1d57242451bd3429 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:07:44 +0800 Subject: [PATCH 20/45] fix: extract BootState to separate header to avoid circular deps - Create board/drivers/boot_state.h with just the BootState enum - Include it from board_declarations.h and drivers.h - Fix include path (was board/boot_state.h, now board/drivers/boot_state.h) This breaks the circular dependency: - drivers.h needs BootState for bootkick_tick declaration - board_declarations.h defines BootState but also struct board - Different board types (body/jungle/regular) have different struct board Now BootState can be included early without pulling in struct board. --- board/boards/board_declarations.h | 6 +----- board/drivers/boot_state.h | 12 ++++++++++++ board/drivers/drivers.h | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 board/drivers/boot_state.h diff --git a/board/boards/board_declarations.h b/board/boards/board_declarations.h index ff5ca97a86b..14b34467c07 100644 --- a/board/boards/board_declarations.h +++ b/board/boards/board_declarations.h @@ -4,11 +4,7 @@ #include // ******************** Prototypes ******************** -typedef enum { - BOOT_STANDBY, - BOOT_BOOTKICK, - BOOT_RESET, -} BootState; +#include "board/drivers/boot_state.h" typedef void (*board_init)(void); typedef void (*board_init_bootloader)(void); diff --git a/board/drivers/boot_state.h b/board/drivers/boot_state.h new file mode 100644 index 00000000000..14897017763 --- /dev/null +++ b/board/drivers/boot_state.h @@ -0,0 +1,12 @@ +#ifndef DRIVERS_BOOT_STATE_H +#define DRIVERS_BOOT_STATE_H + +// Boot state enum used by bootkick driver +// This is a minimal header to avoid circular dependencies +typedef enum { + BOOT_STANDBY, + BOOT_BOOTKICK, + BOOT_RESET, +} BootState; + +#endif diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index d002c59ca7b..c1cad7f28ae 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -1,6 +1,6 @@ #pragma once -#include "board/boot_state.h" +#include "board/drivers/boot_state.h" #include "board/can.h" #include "board/health.h" #include "board/crc.h" From 1bfd271e784126d055b0b5200a44805382b994cb Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:09:04 +0800 Subject: [PATCH 21/45] fix: remove duplicate board/boot_state.h Use board/drivers/boot_state.h instead (which already exists in PR) --- board/boot_state.h | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 board/boot_state.h diff --git a/board/boot_state.h b/board/boot_state.h deleted file mode 100644 index 84ba57fe45d..00000000000 --- a/board/boot_state.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Boot state enum used by bootkick driver -typedef enum { - BOOT_STANDBY, - BOOT_BOOTKICK, - BOOT_RESET, -} BootState; From fe4bfb8d22a547b393371205c19cbfba21cfbaf0 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:11:39 +0800 Subject: [PATCH 22/45] fix: conditionally compile bootkick.c + add safety.h include - SConscript: Skip bootkick.c for PANDA_BODY/JUNGLE builds (no SOM support) - can_common.c: Include opendbc/safety/safety.h for safety_tx_hook declaration --- SConscript | 3 +++ board/drivers/can_common.c | 1 + 2 files changed, 4 insertions(+) diff --git a/SConscript b/SConscript index a76e0f1a3af..3339de2c0c7 100644 --- a/SConscript +++ b/SConscript @@ -121,6 +121,9 @@ def build_project(project_name, project, main, extra_flags): "./board/drivers/usb.c", ] + # bootkick is only for regular panda (has SOM support), not body/jungle + if "-DPANDA_BODY" in extra_flags or "-DPANDA_JUNGLE" in extra_flags: + driver_sources = [s for s in driver_sources if s != "./board/drivers/bootkick.c"] # Build bootstub bs_env = env.Clone() bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") diff --git a/board/drivers/can_common.c b/board/drivers/can_common.c index fbb2f7671cc..27efb9aaaf3 100644 --- a/board/drivers/can_common.c +++ b/board/drivers/can_common.c @@ -1,4 +1,5 @@ #include "board/config.h" +#include "opendbc/safety/safety.h" #include "board/drivers/can_common.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" From 792242182efd36e414e3e139ba19d67a524b74d5 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:13:11 +0800 Subject: [PATCH 23/45] fix: fake_siren.c needs sound.h for sound_stop_dac --- board/drivers/bootkick.c | 6 ++++++ board/drivers/fake_siren.c | 1 + 2 files changed, 7 insertions(+) diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c index 56b46a1dca7..f37889cb135 100644 --- a/board/drivers/bootkick.c +++ b/board/drivers/bootkick.c @@ -1,4 +1,8 @@ #include "board/config.h" + +// bootkick is only used on regular panda, not on BODY or JUNGLE variants +#if !defined(PANDA_BODY) && !defined(PANDA_JUNGLE) + #include "board/drivers/bootkick.h" #include "board/drivers/drivers.h" @@ -68,3 +72,5 @@ void bootkick_tick(bool ignition, bool recent_heartbeat) { } current_board->set_bootkick(boot_state); } + +#endif // !PANDA_BODY && !PANDA_JUNGLE diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c index fd973f03211..43c2f5dadbc 100644 --- a/board/drivers/fake_siren.c +++ b/board/drivers/fake_siren.c @@ -2,6 +2,7 @@ #include "board/drivers/fake_siren.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" +#include "board/stm32h7/sound.h" #include "board/stm32h7/lli2c.h" void siren_tim7_init(void) { From aae7e03854830f8d07bc6a9b946dcae41c5ea12d Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:14:31 +0800 Subject: [PATCH 24/45] fix: guard bootkick declarations for non-regular panda builds - Wrap bootkick_tick and bootkick_reset_triggered in #if !PANDA_BODY && !PANDA_JUNGLE - These symbols don't exist for BODY/JUNGLE builds since struct board is different --- board/drivers/drivers.h | 63 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index c1cad7f28ae..a422dd12544 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -10,11 +10,15 @@ #endif // ******************** bootkick ******************** +// bootkick is only available on regular panda, not on BODY or JUNGLE variants +#if !defined(PANDA_BODY) && !defined(PANDA_JUNGLE) extern bool bootkick_reset_triggered; void bootkick_tick(bool ignition, bool recent_heartbeat); +#endif // !PANDA_BODY && !PANDA_JUNGLE + // ******************** can_common ******************** // Type definitions and function declarations moved to drivers/can_common.h @@ -91,6 +95,38 @@ bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); +// ******************** interrupts ******************** + +typedef struct interrupt { + IRQn_Type irq_type; + void (*handler)(void); + uint32_t call_counter; + uint32_t call_rate; + uint32_t max_call_rate; // Call rate is defined as the amount of calls each second + uint32_t call_rate_fault; +} interrupt; + +void interrupt_timer_init(void); +uint32_t microsecond_timer_get(void); +void unused_interrupt_handler(void); + +extern interrupt interrupts[NUM_INTERRUPTS]; + +#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ + interrupts[irq_num].irq_type = (irq_num); \ + interrupts[irq_num].handler = (func_ptr); \ + interrupts[irq_num].call_counter = 0U; \ + interrupts[irq_num].call_rate = 0U; \ + interrupts[irq_num].max_call_rate = (call_rate_max); \ + interrupts[irq_num].call_rate_fault = (rate_fault); + +extern float interrupt_load; + +void handle_interrupt(IRQn_Type irq_type); +// Every second +void interrupt_timer_handler(void); +void init_interrupts(bool check_rate_limit); + #endif // STM32H7 // ******************** registers ******************** @@ -140,7 +176,9 @@ void spi_rx_done(void); void spi_tx_done(bool reset); // ******************** uart ******************** +#ifdef STM32H7 +// ***************************** Definitions ***************************** #define FIFO_SIZE_INT 0x400U typedef struct uart_ring { @@ -157,11 +195,28 @@ typedef struct uart_ring { bool overwrite; } uart_ring; -// UART ring buffers (defined in uart.c or stm32h7_config.h for BOOTSTUB) -extern uart_ring uart_ring_debug; -extern uart_ring uart_ring_som_debug; +// ***************************** Function prototypes ***************************** +void debug_ring_callback(uart_ring *ring); +void uart_tx_ring(uart_ring *q); +uart_ring *get_ring_by_number(int a); +// ************************* Low-level buffer functions ************************* +bool get_char(uart_ring *q, char *elem); +bool injectc(uart_ring *q, char elem); +bool put_char(uart_ring *q, char elem); +void clear_uart_buff(uart_ring *q); +// ************************ High-level debug functions ********************** +void putch(const char a); +void print(const char *a); +void puthx(uint32_t i, uint8_t len); +void puth(unsigned int i); +#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) +static void puth4(unsigned int i); +#endif +#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) +static void hexdump(const void *a, int l); +#endif -// Function declarations in drivers/uart.h +#endif // STM32H7 // ******************** usb ******************** From d132ce7178c7680aca70e46eca58fbf622b14f39 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:15:05 +0800 Subject: [PATCH 25/45] fix: exclude fan/fake_siren/harness from body/jungle builds --- SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConscript b/SConscript index 3339de2c0c7..df5abbc8f9d 100644 --- a/SConscript +++ b/SConscript @@ -123,7 +123,7 @@ def build_project(project_name, project, main, extra_flags): # bootkick is only for regular panda (has SOM support), not body/jungle if "-DPANDA_BODY" in extra_flags or "-DPANDA_JUNGLE" in extra_flags: - driver_sources = [s for s in driver_sources if s != "./board/drivers/bootkick.c"] + driver_sources = [s for s in driver_sources if s not in ["./board/drivers/bootkick.c", "./board/drivers/fan.c", "./board/drivers/fake_siren.c", "./board/drivers/harness.c"]] # Build bootstub bs_env = env.Clone() bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") From 2168c4574afd1a39045557c36d4bf48b7dd1c38e Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:17:18 +0800 Subject: [PATCH 26/45] fix: remove duplicate interrupt defs from drivers.h (already in interrupts.h) --- board/drivers/drivers.h | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index a422dd12544..318c6436ba0 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -95,40 +95,6 @@ bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); -// ******************** interrupts ******************** - -typedef struct interrupt { - IRQn_Type irq_type; - void (*handler)(void); - uint32_t call_counter; - uint32_t call_rate; - uint32_t max_call_rate; // Call rate is defined as the amount of calls each second - uint32_t call_rate_fault; -} interrupt; - -void interrupt_timer_init(void); -uint32_t microsecond_timer_get(void); -void unused_interrupt_handler(void); - -extern interrupt interrupts[NUM_INTERRUPTS]; - -#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ - interrupts[irq_num].irq_type = (irq_num); \ - interrupts[irq_num].handler = (func_ptr); \ - interrupts[irq_num].call_counter = 0U; \ - interrupts[irq_num].call_rate = 0U; \ - interrupts[irq_num].max_call_rate = (call_rate_max); \ - interrupts[irq_num].call_rate_fault = (rate_fault); - -extern float interrupt_load; - -void handle_interrupt(IRQn_Type irq_type); -// Every second -void interrupt_timer_handler(void); -void init_interrupts(bool check_rate_limit); - -#endif // STM32H7 - // ******************** registers ******************** // 10 bit hash with 23 as a prime From 17aa60ccffb99f85d1ddf3f94d53540f1ff21785 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:19:08 +0800 Subject: [PATCH 27/45] fix: add missing #endif for STM32H7 block in drivers.h --- board/drivers/drivers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index 318c6436ba0..aaf940f81b5 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -95,6 +95,7 @@ bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); +#endif // STM32H7 // ******************** registers ******************** // 10 bit hash with 23 as a prime From b42f6d8a6865193b682bc82ab03a4933793a25dd Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:19:58 +0800 Subject: [PATCH 28/45] fix: add missing struct interrupt and related declarations to drivers.h - struct interrupt was missing, causing redefinition errors - Also added REGISTER_INTERRUPT macro and related function declarations - These are needed by interrupts.h which includes drivers.h --- board/drivers/drivers.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index aaf940f81b5..a422dd12544 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -95,7 +95,40 @@ bool harness_check_ignition(void); void harness_tick(void); void harness_init(void); +// ******************** interrupts ******************** + +typedef struct interrupt { + IRQn_Type irq_type; + void (*handler)(void); + uint32_t call_counter; + uint32_t call_rate; + uint32_t max_call_rate; // Call rate is defined as the amount of calls each second + uint32_t call_rate_fault; +} interrupt; + +void interrupt_timer_init(void); +uint32_t microsecond_timer_get(void); +void unused_interrupt_handler(void); + +extern interrupt interrupts[NUM_INTERRUPTS]; + +#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ + interrupts[irq_num].irq_type = (irq_num); \ + interrupts[irq_num].handler = (func_ptr); \ + interrupts[irq_num].call_counter = 0U; \ + interrupts[irq_num].call_rate = 0U; \ + interrupts[irq_num].max_call_rate = (call_rate_max); \ + interrupts[irq_num].call_rate_fault = (rate_fault); + +extern float interrupt_load; + +void handle_interrupt(IRQn_Type irq_type); +// Every second +void interrupt_timer_handler(void); +void init_interrupts(bool check_rate_limit); + #endif // STM32H7 + // ******************** registers ******************** // 10 bit hash with 23 as a prime From 2acfc74148052c5943430ea2999825f43991b403 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:22:03 +0800 Subject: [PATCH 29/45] fix: remove duplicate struct interrupt from interrupts.h - struct interrupt is now defined in drivers.h - interrupts.h just includes drivers.h and provides implementation - This fixes the redefinition error --- board/drivers/interrupts.h | 107 +++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index dec4acc0107..58a272eb0d0 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -1,39 +1,78 @@ #ifndef DRIVERS_INTERRUPTS_H #define DRIVERS_INTERRUPTS_H -#include -#include - -#ifdef STM32H7 - -typedef struct interrupt { - IRQn_Type irq_type; - void (*handler)(void); - uint32_t call_counter; - uint32_t call_rate; - uint32_t max_call_rate; // Call rate is defined as the amount of calls each second - uint32_t call_rate_fault; -} interrupt; - -extern interrupt interrupts[NUM_INTERRUPTS]; - -#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ - interrupts[irq_num].irq_type = (irq_num); \ - interrupts[irq_num].handler = (func_ptr); \ - interrupts[irq_num].call_counter = 0U; \ - interrupts[irq_num].call_rate = 0U; \ - interrupts[irq_num].max_call_rate = (call_rate_max); \ - interrupts[irq_num].call_rate_fault = (rate_fault); - -extern float interrupt_load; - -void interrupt_timer_init(void); -uint32_t microsecond_timer_get(void); -void unused_interrupt_handler(void); -void handle_interrupt(IRQn_Type irq_type); -void interrupt_timer_handler(void); -void init_interrupts(bool check_rate_limit); - -#endif // STM32H7 +#include "board/drivers/drivers.h" + +// interrupt struct and related macros are now defined in drivers.h +// This file only contains the implementation + +void unused_interrupt_handler(void) { + // Something is wrong if this handler is called! + print("Unused interrupt handler called!\n"); + fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); +} + +// Global interrupt array - defined here, declared in drivers.h +interrupt interrupts[NUM_INTERRUPTS]; + +static bool check_interrupt_rate = false; + +static uint32_t idle_time = 0U; +static uint32_t busy_time = 0U; +// interrupt_load is declared in drivers.h + +void handle_interrupt(IRQn_Type irq_type){ + static uint8_t interrupt_depth = 0U; + static uint32_t last_time = 0U; + ENTER_CRITICAL(); + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + idle_time += get_ts_elapsed(time, last_time); + last_time = time; + } + interrupt_depth += 1U; + EXIT_CRITICAL(); + + interrupts[irq_type].call_counter++; + interrupts[irq_type].handler(); + + EXIT_CRITICAL(); + interrupt_depth -= 1U; + ENTER_CRITICAL(); + + if (interrupt_depth == 0U) { + last_time = microsecond_timer_get(); + busy_time += get_ts_elapsed(last_time, time); + } +} + +void interrupt_timer_handler(void) { + static uint32_t call_rate_counters[NUM_INTERRUPTS] = {0}; + uint32_t i; + + for (i = 0U; i < NUM_INTERRUPTS; i++) { + if (interrupts[i].handler != unused_interrupt_handler) { + call_rate_counters[i] += interrupts[i].call_counter; + if (check_interrupt_rate && (call_rate_counters[i] > interrupts[i].max_call_rate)) { + interrupts[i].call_rate_fault += 1U; + } + interrupts[i].call_counter = 0U; + } + } + interrupt_load = ((float)busy_time) / ((float)(busy_time + idle_time)); + busy_time = 0U; + idle_time = 0U; +} + +void init_interrupts(bool check_rate_limit) { + uint32_t i; + check_interrupt_rate = check_rate_limit; + for (i = 0U; i < NUM_INTERRUPTS; i++) { + interrupts[i].handler = unused_interrupt_handler; + interrupts[i].call_counter = 0U; + interrupts[i].call_rate = 0U; + interrupts[i].call_rate_fault = 0U; + } +} #endif From 911ba181557266a21803acf9a1b781a41b503f45 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 01:23:35 +0800 Subject: [PATCH 30/45] fix: declare 'time' variable in handle_interrupt - Added uint32_t time declaration - Fixed the time tracking logic in handle_interrupt --- board/drivers/interrupts.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index 58a272eb0d0..7a6f2f500eb 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -24,9 +24,11 @@ static uint32_t busy_time = 0U; void handle_interrupt(IRQn_Type irq_type){ static uint8_t interrupt_depth = 0U; static uint32_t last_time = 0U; + uint32_t time; + ENTER_CRITICAL(); if (interrupt_depth == 0U) { - uint32_t time = microsecond_timer_get(); + time = microsecond_timer_get(); idle_time += get_ts_elapsed(time, last_time); last_time = time; } @@ -41,8 +43,9 @@ void handle_interrupt(IRQn_Type irq_type){ ENTER_CRITICAL(); if (interrupt_depth == 0U) { - last_time = microsecond_timer_get(); - busy_time += get_ts_elapsed(last_time, time); + time = microsecond_timer_get(); + busy_time += get_ts_elapsed(time, last_time); + last_time = time; } } From bc51abcaa6a297dd4487b77067771e0f98abc0b6 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:29:40 +0800 Subject: [PATCH 31/45] fix: add extern declaration for uart_ring_som_debug in bootkick.c --- board/drivers/bootkick.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c index f37889cb135..dd09b9d2d6c 100644 --- a/board/drivers/bootkick.c +++ b/board/drivers/bootkick.c @@ -6,6 +6,9 @@ #include "board/drivers/bootkick.h" #include "board/drivers/drivers.h" +// uart_ring_som_debug is defined in stm32h7_config.h for BOOTSTUB, or uart.c for main +extern uart_ring uart_ring_som_debug; + bool bootkick_reset_triggered = false; void bootkick_tick(bool ignition, bool recent_heartbeat) { From 7b619139beb4816e5c407650f0ebe95deed1c276 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:34:11 +0800 Subject: [PATCH 32/45] fix: add extern declarations for uart_ring_debug and uart_ring_som_debug These are needed by lluart.h and other header files that reference them. --- board/drivers/drivers.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h index a422dd12544..d01d60a8211 100644 --- a/board/drivers/drivers.h +++ b/board/drivers/drivers.h @@ -198,6 +198,12 @@ typedef struct uart_ring { // ***************************** Function prototypes ***************************** void debug_ring_callback(uart_ring *ring); void uart_tx_ring(uart_ring *q); + +// ******************** UART rings ******************** +// These are defined in uart.c (main build) or stm32h7_config.h (BOOTSTUB) +extern uart_ring uart_ring_debug; +extern uart_ring uart_ring_som_debug; + uart_ring *get_ring_by_number(int a); // ************************* Low-level buffer functions ************************* bool get_char(uart_ring *q, char *elem); From 34b71f76230c93b0cbb763af50b0440efb5dc857 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:42:07 +0800 Subject: [PATCH 33/45] fix: add llfdcan includes to fdcan.c - Include llfdcan.h and llfdcan_declarations.h for llcan_set_speed - Wrap in #ifndef BOOTSTUB since FDCAN only used in main builds --- board/drivers/fdcan.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index c0366312751..00b01784791 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -1,4 +1,10 @@ #include "board/config.h" + +// FDCAN is only used in main builds, not BOOTSTUB +#ifndef BOOTSTUB + +#include "board/stm32h7/llfdcan.h" +#include "board/stm32h7/llfdcan_declarations.h" #include "board/drivers/fdcan.h" #include "board/drivers/drivers.h" @@ -7,7 +13,7 @@ FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; static bool can_set_speed(uint8_t can_number) { bool ret = true; FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_BUS_NUM(can_number); ret &= llcan_set_speed( FDCANx, @@ -269,3 +275,5 @@ bool can_init(uint8_t can_number) { } return ret; } + +#endif // BOOTSTUB From a602521b9660f63719877df06a2b48bfab30d2ab Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:46:39 +0800 Subject: [PATCH 34/45] fix: remove implementations from interrupts.h - keep only declarations interrupts.h should only have declarations, not implementations. Implementations are in interrupts.c. --- board/drivers/interrupts.h | 83 +++++--------------------------------- 1 file changed, 9 insertions(+), 74 deletions(-) diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index 7a6f2f500eb..42b1f0519be 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -3,79 +3,14 @@ #include "board/drivers/drivers.h" -// interrupt struct and related macros are now defined in drivers.h -// This file only contains the implementation - -void unused_interrupt_handler(void) { - // Something is wrong if this handler is called! - print("Unused interrupt handler called!\n"); - fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); -} - -// Global interrupt array - defined here, declared in drivers.h -interrupt interrupts[NUM_INTERRUPTS]; - -static bool check_interrupt_rate = false; - -static uint32_t idle_time = 0U; -static uint32_t busy_time = 0U; -// interrupt_load is declared in drivers.h - -void handle_interrupt(IRQn_Type irq_type){ - static uint8_t interrupt_depth = 0U; - static uint32_t last_time = 0U; - uint32_t time; - - ENTER_CRITICAL(); - if (interrupt_depth == 0U) { - time = microsecond_timer_get(); - idle_time += get_ts_elapsed(time, last_time); - last_time = time; - } - interrupt_depth += 1U; - EXIT_CRITICAL(); - - interrupts[irq_type].call_counter++; - interrupts[irq_type].handler(); - - EXIT_CRITICAL(); - interrupt_depth -= 1U; - ENTER_CRITICAL(); - - if (interrupt_depth == 0U) { - time = microsecond_timer_get(); - busy_time += get_ts_elapsed(time, last_time); - last_time = time; - } -} - -void interrupt_timer_handler(void) { - static uint32_t call_rate_counters[NUM_INTERRUPTS] = {0}; - uint32_t i; - - for (i = 0U; i < NUM_INTERRUPTS; i++) { - if (interrupts[i].handler != unused_interrupt_handler) { - call_rate_counters[i] += interrupts[i].call_counter; - if (check_interrupt_rate && (call_rate_counters[i] > interrupts[i].max_call_rate)) { - interrupts[i].call_rate_fault += 1U; - } - interrupts[i].call_counter = 0U; - } - } - interrupt_load = ((float)busy_time) / ((float)(busy_time + idle_time)); - busy_time = 0U; - idle_time = 0U; -} - -void init_interrupts(bool check_rate_limit) { - uint32_t i; - check_interrupt_rate = check_rate_limit; - for (i = 0U; i < NUM_INTERRUPTS; i++) { - interrupts[i].handler = unused_interrupt_handler; - interrupts[i].call_counter = 0U; - interrupts[i].call_rate = 0U; - interrupts[i].call_rate_fault = 0U; - } -} +// interrupt struct and related macros are defined in drivers.h +// This file only contains declarations + +void unused_interrupt_handler(void); +void handle_interrupt(IRQn_Type irq_type); +void interrupt_timer_handler(void); +void init_interrupts(bool check_rate_limit); +void interrupt_timer_init(void); +uint32_t microsecond_timer_get(void); #endif From 952e8de1d0ba98e3fcfc826685fd1eca855e5306 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:49:51 +0800 Subject: [PATCH 35/45] fix: wrap uart.c content in #ifndef BOOTSTUB BOOTSTUB builds use stub print functions from bootstub_declarations.h. Main builds use full UART implementation. --- board/drivers/uart.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/board/drivers/uart.c b/board/drivers/uart.c index 2cf1fde6304..ddb744f1fc7 100644 --- a/board/drivers/uart.c +++ b/board/drivers/uart.c @@ -1,4 +1,10 @@ #include "board/config.h" + +// UART is only fully functional in main builds, not BOOTSTUB +// BOOTSTUB uses stubs from bootstub_declarations.h + +#ifndef BOOTSTUB + #include "board/drivers/uart.h" #include "board/drivers/drivers.h" #include "board/drivers/interrupts.h" @@ -120,7 +126,7 @@ void puth(unsigned int i) { puthx(i, 8U); } -#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) +#if defined(DEBUG_SPI) || defined(DEBUG) void puth4(unsigned int i) { puthx(i, 4U); } @@ -138,3 +144,5 @@ void hexdump(const void *a, int l) { print("\n"); } #endif + +#endif // BOOTSTUB From da42085e8c08d60e5632357fc4e9a5b9ad593087 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:52:33 +0800 Subject: [PATCH 36/45] fix: remove direct llfdcan.h include from fdcan.c llfdcan.h is already included by stm32h7_config.h. Including it again causes redefinition of speeds/data_speeds arrays. --- board/drivers/fdcan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index 00b01784791..fc47e7af398 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -3,11 +3,13 @@ // FDCAN is only used in main builds, not BOOTSTUB #ifndef BOOTSTUB -#include "board/stm32h7/llfdcan.h" #include "board/stm32h7/llfdcan_declarations.h" #include "board/drivers/fdcan.h" #include "board/drivers/drivers.h" +// Note: llfdcan.h is included by stm32h7_config.h, don't include it here +// to avoid redefinition of speeds/data_speeds arrays + FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; static bool can_set_speed(uint8_t can_number) { From cac24287c4f8241a43e8a85e92bfb42fd37ff101 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:54:45 +0800 Subject: [PATCH 37/45] fix: correct BUS_NUM_FROM_CAN_NUM typo in fdcan.c --- board/drivers/fdcan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index fc47e7af398..bb1c7a09f6d 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -15,7 +15,7 @@ FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; static bool can_set_speed(uint8_t can_number) { bool ret = true; FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_BUS_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); ret &= llcan_set_speed( FDCANx, From 7996fd41f0ff99ad7b766ff1aa9c92b440801a47 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:57:24 +0800 Subject: [PATCH 38/45] fix: add extern declarations for CAN ring buffers fdcan.c needs access to can_rx_q, can_tx1_q, etc. --- board/drivers/can_common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index b5f7379da4b..ce933f5b112 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -45,6 +45,12 @@ extern bool can_silent; extern bool can_loopback; extern can_ring *can_queues[PANDA_CAN_CNT]; + +// CAN ring buffers - defined in can_common.c +extern can_ring can_rx_q; +extern can_ring can_tx1_q; +extern can_ring can_tx2_q; +extern can_ring can_tx3_q; extern bus_config_t bus_config[PANDA_CAN_CNT]; #define CANIF_FROM_CAN_NUM(num) (cans[num]) From f0ab87916e93a77f4a2b0c44198aa79a8ab36396 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 02:59:55 +0800 Subject: [PATCH 39/45] fix: include safety.h in fdcan.c for safety_fwd_hook --- board/drivers/fdcan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index bb1c7a09f6d..fea4ab8782a 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -5,6 +5,7 @@ #include "board/stm32h7/llfdcan_declarations.h" #include "board/drivers/fdcan.h" +#include "opendbc/safety/safety.h" #include "board/drivers/drivers.h" // Note: llfdcan.h is included by stm32h7_config.h, don't include it here From 790aa985706caf74e3deeb4e9c7ac67bff093b9f Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 03:02:13 +0800 Subject: [PATCH 40/45] fix: include led.h in fdcan.c for led_set --- board/drivers/fdcan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index fea4ab8782a..eac31a4082b 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -7,6 +7,7 @@ #include "board/drivers/fdcan.h" #include "opendbc/safety/safety.h" #include "board/drivers/drivers.h" +#include "board/drivers/led.h" // Note: llfdcan.h is included by stm32h7_config.h, don't include it here // to avoid redefinition of speeds/data_speeds arrays From 40fbd00b6e9ce5297de4555b214a41b7014fa7a1 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 03:04:37 +0800 Subject: [PATCH 41/45] fix: remove redundant sound.h include from fake_siren.c sound.h is already included via board.h -> stm32h7_config.h --- board/drivers/fake_siren.c | 1 - 1 file changed, 1 deletion(-) diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c index 43c2f5dadbc..fd973f03211 100644 --- a/board/drivers/fake_siren.c +++ b/board/drivers/fake_siren.c @@ -2,7 +2,6 @@ #include "board/drivers/fake_siren.h" #include "board/drivers/drivers.h" #include "board/drivers/registers.h" -#include "board/stm32h7/sound.h" #include "board/stm32h7/lli2c.h" void siren_tim7_init(void) { From 42f4e36e28daf60394e908d088b8fb48ed2e3afc Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 04:44:57 +0800 Subject: [PATCH 42/45] fix: resolve multiple definition errors when drivers compiled separately - Add static inline to functions in llspi.h, interrupt_handlers.h - Add static const to arrays in llfdcan.h - Add extern declarations and move global definitions to .c files: - USBx moved from llusb.h to usb.c - Global state moved from faults.h/critical.h/main_definitions.h to sys.c - early_gpio_float moved from stm32h7_config.h to gpio.c - Properly declare hw_type/current_board extern in bootstub_declarations.h This fixes linker errors when driver source files are compiled separately for bootstub and main builds. --- SConscript | 1 + board/body/boards/board_body.h | 9 +- board/body/main.c | 7 + board/bootstub.c | 21 +++ board/bootstub_declarations.h | 5 +- board/drivers/fdcan.c | 4 + board/drivers/gpio.c | 7 + board/drivers/sys.c | 9 + board/drivers/usb.c | 3 + board/main.c | 9 + board/main_definitions.h | 14 +- board/stm32h7/interrupt_handlers.h | 281 +++++++++++++++-------------- board/stm32h7/llfdcan.h | 4 +- board/stm32h7/llspi.h | 6 +- board/stm32h7/llusb.h | 2 +- board/stm32h7/stm32h7_config.h | 4 +- board/sys/critical.h | 6 +- board/sys/faults.h | 8 +- 18 files changed, 229 insertions(+), 171 deletions(-) create mode 100644 board/drivers/sys.c diff --git a/SConscript b/SConscript index df5abbc8f9d..8c48a52e747 100644 --- a/SConscript +++ b/SConscript @@ -116,6 +116,7 @@ def build_project(project_name, project, main, extra_flags): "./board/drivers/registers.c", "./board/drivers/simple_watchdog.c", "./board/drivers/spi.c", + "./board/drivers/sys.c", "./board/drivers/timers.c", "./board/drivers/uart.c", "./board/drivers/usb.c", diff --git a/board/body/boards/board_body.h b/board/body/boards/board_body.h index a1eebfdb8d2..c2ba652c609 100644 --- a/board/body/boards/board_body.h +++ b/board/body/boards/board_body.h @@ -1,6 +1,6 @@ #include "board/body/motor_control.h" -void board_body_init(void) { +static inline void board_body_init(void) { motor_init(); motor_encoder_init(); motor_speed_controller_init(); @@ -14,8 +14,5 @@ void board_body_init(void) { set_gpio_alternate(GPIOD, 1, GPIO_AF9_FDCAN1); } -board board_body = { - .led_GPIO = {GPIOC, GPIOC, GPIOC}, - .led_pin = {7, 7, 7}, - .init = board_body_init, -}; +// board_body is defined in board/body/main.c +extern board board_body; diff --git a/board/body/main.c b/board/body/main.c index 4a2d0b4c537..71200c2ae2e 100644 --- a/board/body/main.c +++ b/board/body/main.c @@ -16,6 +16,13 @@ extern int _app_start[0xc000]; +#include "board/body/boards/board_body.h" +board board_body = { + .led_GPIO = {GPIOC, GPIOC, GPIOC}, + .led_pin = {7, 7, 7}, + .init = board_body_init, +}; + #include "board/body/main_comms.h" void debug_ring_callback(uart_ring *ring) { diff --git a/board/bootstub.c b/board/bootstub.c index c09cb9fe1b7..fb7f58cb7a6 100644 --- a/board/bootstub.c +++ b/board/bootstub.c @@ -8,6 +8,27 @@ #include "board/drivers/pwm.h" #include "board/drivers/usb.h" +// Globals for BOOTSTUB mode +uint8_t hw_type = 0; +board *current_board; + +// UART ring buffers for BOOTSTUB mode (uart.c not compiled in BOOTSTUB) +static uint8_t elems_rx_som_debug[FIFO_SIZE_INT]; +static uint8_t elems_tx_som_debug[FIFO_SIZE_INT]; +uart_ring uart_ring_som_debug = { + .w_ptr_tx = 0, + .r_ptr_tx = 0, + .elems_tx = ((uint8_t *)&(elems_tx_som_debug)), + .tx_fifo_size = FIFO_SIZE_INT, + .w_ptr_rx = 0, + .r_ptr_rx = 0, + .elems_rx = ((uint8_t *)&(elems_rx_som_debug)), + .rx_fifo_size = FIFO_SIZE_INT, + .uart = UART7, + .callback = NULL, + .overwrite = true +}; + #include "board/early_init.h" #include "board/provision.h" diff --git a/board/bootstub_declarations.h b/board/bootstub_declarations.h index 1b9580857c7..670cd69742a 100644 --- a/board/bootstub_declarations.h +++ b/board/bootstub_declarations.h @@ -13,5 +13,6 @@ typedef struct uart_ring uart_ring; void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); } // ********************* Globals ********************** -uint8_t hw_type = 0; -board *current_board; +// hw_type and current_board are defined in bootstub.c +extern uint8_t hw_type; +extern board *current_board; diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c index eac31a4082b..0343c6155b3 100644 --- a/board/drivers/fdcan.c +++ b/board/drivers/fdcan.c @@ -12,6 +12,10 @@ // Note: llfdcan.h is included by stm32h7_config.h, don't include it here // to avoid redefinition of speeds/data_speeds arrays +// kbps multiplied by 10 - defined here, declared extern in llfdcan_declarations.h +const uint32_t speeds[SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; +const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U}; + FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; static bool can_set_speed(uint8_t can_number) { diff --git a/board/drivers/gpio.c b/board/drivers/gpio.c index f6f8b7452f6..8dc2833e511 100644 --- a/board/drivers/gpio.c +++ b/board/drivers/gpio.c @@ -4,6 +4,13 @@ #include "board/drivers/interrupts.h" #include "board/drivers/registers.h" +void early_gpio_float(void) { + RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN; + GPIOA->MODER = 0xAB000000U; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; + GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; GPIOD->ODR = 0; GPIOE->ODR = 0; GPIOF->ODR = 0; GPIOG->ODR = 0; GPIOH->ODR = 0; + GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; GPIOD->PUPDR = 0; GPIOE->PUPDR = 0; GPIOF->PUPDR = 0; GPIOG->PUPDR = 0; GPIOH->PUPDR = 0; +} + void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { ENTER_CRITICAL(); uint32_t tmp = GPIO->MODER; diff --git a/board/drivers/sys.c b/board/drivers/sys.c new file mode 100644 index 00000000000..108893f68f6 --- /dev/null +++ b/board/drivers/sys.c @@ -0,0 +1,9 @@ +#include "board/sys/sys.h" +#include "board/can.h" + +// Global critical section depth +uint8_t global_critical_depth = 0U; + +// Global fault state +uint8_t fault_status = FAULT_STATUS_NONE; +uint32_t faults = 0U; diff --git a/board/drivers/usb.c b/board/drivers/usb.c index c45860d5a29..b0c3c44577d 100644 --- a/board/drivers/usb.c +++ b/board/drivers/usb.c @@ -2,6 +2,9 @@ #include "board/drivers/usb.h" #include "board/drivers/drivers.h" +// USB peripheral pointer - defined here, declared extern in llusb.h +USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; + typedef union { uint16_t w; struct BW { diff --git a/board/main.c b/board/main.c index e3e98074798..2ea2d595e93 100644 --- a/board/main.c +++ b/board/main.c @@ -25,6 +25,15 @@ #include "board/can_comms.h" #include "board/main_comms.h" +// ********************* Globals (defined here, extern in main_definitions.h) ********************** +uint8_t hw_type = 0; +board *current_board = NULL; +uint32_t uptime_cnt = 0; +uint32_t heartbeat_counter = 0; +bool heartbeat_lost = false; +bool heartbeat_disabled = false; +bool siren_enabled = false; + // ********************* Serial debugging ********************* diff --git a/board/main_definitions.h b/board/main_definitions.h index 83c91090579..2052c0a427f 100644 --- a/board/main_definitions.h +++ b/board/main_definitions.h @@ -1,14 +1,14 @@ #include "main_declarations.h" // ********************* Globals ********************** -uint8_t hw_type = 0; -board *current_board; -uint32_t uptime_cnt = 0; +extern uint8_t hw_type; +extern board *current_board; +extern uint32_t uptime_cnt; // heartbeat state -uint32_t heartbeat_counter = 0; -bool heartbeat_lost = false; -bool heartbeat_disabled = false; // set over USB +extern uint32_t heartbeat_counter; +extern bool heartbeat_lost; +extern bool heartbeat_disabled; // siren state -bool siren_enabled = false; +extern bool siren_enabled; diff --git a/board/stm32h7/interrupt_handlers.h b/board/stm32h7/interrupt_handlers.h index 8148021d697..5ca70cf3ae5 100644 --- a/board/stm32h7/interrupt_handlers.h +++ b/board/stm32h7/interrupt_handlers.h @@ -1,143 +1,144 @@ // ********************* Bare interrupt handlers ********************* // Interrupts for STM32H7x5 +// These are static inline to avoid multiple definition errors when included in multiple compilation units -void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} -void PVD_AVD_IRQHandler(void) {handle_interrupt(PVD_AVD_IRQn);} -void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} -void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} -void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} -void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} -void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} -void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} -void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} -void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} -void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} -void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} -void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} -void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} -void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} -void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} -void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} -void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} -void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} -void FDCAN1_IT0_IRQHandler(void) {handle_interrupt(FDCAN1_IT0_IRQn);} -void FDCAN2_IT0_IRQHandler(void) {handle_interrupt(FDCAN2_IT0_IRQn);} -void FDCAN1_IT1_IRQHandler(void) {handle_interrupt(FDCAN1_IT1_IRQn);} -void FDCAN2_IT1_IRQHandler(void) {handle_interrupt(FDCAN2_IT1_IRQn);} -void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} -void TIM1_BRK_IRQHandler(void) {handle_interrupt(TIM1_BRK_IRQn);} -void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} -void TIM1_TRG_COM_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_IRQn);} -void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} -void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} -void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} -void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} -void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} -void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} -void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} -void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} -void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} -void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} -void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} -void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} -void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} -void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} -void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} -void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} -void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} -void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} -void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} -void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} -void FMC_IRQHandler(void) {handle_interrupt(FMC_IRQn);} -void SDMMC1_IRQHandler(void) {handle_interrupt(SDMMC1_IRQn);} -void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} -void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} -void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} -void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} -void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} -void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} -void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} -void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} -void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} -void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} -void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} -void ETH_IRQHandler(void) {handle_interrupt(ETH_IRQn);} -void ETH_WKUP_IRQHandler(void) {handle_interrupt(ETH_WKUP_IRQn);} -void FDCAN_CAL_IRQHandler(void) {handle_interrupt(FDCAN_CAL_IRQn);} -void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} -void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} -void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} -void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} -void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} -void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} -void OTG_HS_EP1_OUT_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_OUT_IRQn);} -void OTG_HS_EP1_IN_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_IN_IRQn);} -void OTG_HS_WKUP_IRQHandler(void) {handle_interrupt(OTG_HS_WKUP_IRQn);} -void OTG_HS_IRQHandler(void) {handle_interrupt(OTG_HS_IRQn);} -void DCMI_PSSI_IRQHandler(void) {handle_interrupt(DCMI_PSSI_IRQn);} -void CRYP_IRQHandler(void) {handle_interrupt(CRYP_IRQn);} -void HASH_RNG_IRQHandler(void) {handle_interrupt(HASH_RNG_IRQn);} -void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} -void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} -void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} -void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} -void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} -void SPI6_IRQHandler(void) {handle_interrupt(SPI6_IRQn);} -void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} -void LTDC_IRQHandler(void) {handle_interrupt(LTDC_IRQn);} -void LTDC_ER_IRQHandler(void) {handle_interrupt(LTDC_ER_IRQn);} -void DMA2D_IRQHandler(void) {handle_interrupt(DMA2D_IRQn);} -void OCTOSPI1_IRQHandler(void) {handle_interrupt(OCTOSPI1_IRQn);} -void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} -void CEC_IRQHandler(void) {handle_interrupt(CEC_IRQn);} -void I2C4_EV_IRQHandler(void) {handle_interrupt(I2C4_EV_IRQn);} -void I2C4_ER_IRQHandler(void) {handle_interrupt(I2C4_ER_IRQn);} -void SPDIF_RX_IRQHandler(void) {handle_interrupt(SPDIF_RX_IRQn);} -void DMAMUX1_OVR_IRQHandler(void) {handle_interrupt(DMAMUX1_OVR_IRQn);} -void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} -void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} -void DFSDM1_FLT2_IRQHandler(void) {handle_interrupt(DFSDM1_FLT2_IRQn);} -void DFSDM1_FLT3_IRQHandler(void) {handle_interrupt(DFSDM1_FLT3_IRQn);} -void SWPMI1_IRQHandler(void) {handle_interrupt(SWPMI1_IRQn);} -void TIM15_IRQHandler(void) {handle_interrupt(TIM15_IRQn);} -void TIM16_IRQHandler(void) {handle_interrupt(TIM16_IRQn);} -void TIM17_IRQHandler(void) {handle_interrupt(TIM17_IRQn);} -void MDIOS_WKUP_IRQHandler(void) {handle_interrupt(MDIOS_WKUP_IRQn);} -void MDIOS_IRQHandler(void) {handle_interrupt(MDIOS_IRQn);} -void MDMA_IRQHandler(void) {handle_interrupt(MDMA_IRQn);} -void SDMMC2_IRQHandler(void) {handle_interrupt(SDMMC2_IRQn);} -void HSEM1_IRQHandler(void) {handle_interrupt(HSEM1_IRQn);} -void ADC3_IRQHandler(void) {handle_interrupt(ADC3_IRQn);} -void DMAMUX2_OVR_IRQHandler(void) {handle_interrupt(DMAMUX2_OVR_IRQn);} -void BDMA_Channel0_IRQHandler(void) {handle_interrupt(BDMA_Channel0_IRQn);} -void BDMA_Channel1_IRQHandler(void) {handle_interrupt(BDMA_Channel1_IRQn);} -void BDMA_Channel2_IRQHandler(void) {handle_interrupt(BDMA_Channel2_IRQn);} -void BDMA_Channel3_IRQHandler(void) {handle_interrupt(BDMA_Channel3_IRQn);} -void BDMA_Channel4_IRQHandler(void) {handle_interrupt(BDMA_Channel4_IRQn);} -void BDMA_Channel5_IRQHandler(void) {handle_interrupt(BDMA_Channel5_IRQn);} -void BDMA_Channel6_IRQHandler(void) {handle_interrupt(BDMA_Channel6_IRQn);} -void BDMA_Channel7_IRQHandler(void) {handle_interrupt(BDMA_Channel7_IRQn);} -void COMP_IRQHandler(void) {handle_interrupt(COMP_IRQn);} -void LPTIM2_IRQHandler(void) {handle_interrupt(LPTIM2_IRQn);} -void LPTIM3_IRQHandler(void) {handle_interrupt(LPTIM3_IRQn);} -void LPTIM4_IRQHandler(void) {handle_interrupt(LPTIM4_IRQn);} -void LPTIM5_IRQHandler(void) {handle_interrupt(LPTIM5_IRQn);} -void LPUART1_IRQHandler(void) {handle_interrupt(LPUART1_IRQn);} -void CRS_IRQHandler(void) {handle_interrupt(CRS_IRQn);} -void ECC_IRQHandler(void) {handle_interrupt(ECC_IRQn);} -void SAI4_IRQHandler(void) {handle_interrupt(SAI4_IRQn);} -void DTS_IRQHandler(void) {handle_interrupt(DTS_IRQn);} -void WAKEUP_PIN_IRQHandler(void) {handle_interrupt(WAKEUP_PIN_IRQn);} -void OCTOSPI2_IRQHandler(void) {handle_interrupt(OCTOSPI2_IRQn);} -void OTFDEC1_IRQHandler(void) {handle_interrupt(OTFDEC1_IRQn);} -void OTFDEC2_IRQHandler(void) {handle_interrupt(OTFDEC2_IRQn);} -void FMAC_IRQHandler(void) {handle_interrupt(FMAC_IRQn);} -void CORDIC_IRQHandler(void) {handle_interrupt(CORDIC_IRQn);} -void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} -void USART10_IRQHandler(void) {handle_interrupt(USART10_IRQn);} -void I2C5_EV_IRQHandler(void) {handle_interrupt(I2C5_EV_IRQn);} -void I2C5_ER_IRQHandler(void) {handle_interrupt(I2C5_ER_IRQn);} -void FDCAN3_IT0_IRQHandler(void) {handle_interrupt(FDCAN3_IT0_IRQn);} -void FDCAN3_IT1_IRQHandler(void) {handle_interrupt(FDCAN3_IT1_IRQn);} -void TIM23_IRQHandler(void) {handle_interrupt(TIM23_IRQn);} -void TIM24_IRQHandler(void) {handle_interrupt(TIM24_IRQn);} +static inline void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} +static inline void PVD_AVD_IRQHandler(void) {handle_interrupt(PVD_AVD_IRQn);} +static inline void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} +static inline void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} +static inline void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} +static inline void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} +static inline void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} +static inline void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} +static inline void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} +static inline void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} +static inline void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} +static inline void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} +static inline void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} +static inline void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} +static inline void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} +static inline void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} +static inline void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} +static inline void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} +static inline void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} +static inline void FDCAN1_IT0_IRQHandler(void) {handle_interrupt(FDCAN1_IT0_IRQn);} +static inline void FDCAN2_IT0_IRQHandler(void) {handle_interrupt(FDCAN2_IT0_IRQn);} +static inline void FDCAN1_IT1_IRQHandler(void) {handle_interrupt(FDCAN1_IT1_IRQn);} +static inline void FDCAN2_IT1_IRQHandler(void) {handle_interrupt(FDCAN2_IT1_IRQn);} +static inline void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} +static inline void TIM1_BRK_IRQHandler(void) {handle_interrupt(TIM1_BRK_IRQn);} +static inline void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} +static inline void TIM1_TRG_COM_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_IRQn);} +static inline void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} +static inline void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} +static inline void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} +static inline void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} +static inline void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} +static inline void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} +static inline void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} +static inline void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} +static inline void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} +static inline void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} +static inline void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} +static inline void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} +static inline void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} +static inline void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} +static inline void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} +static inline void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} +static inline void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} +static inline void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} +static inline void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} +static inline void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} +static inline void FMC_IRQHandler(void) {handle_interrupt(FMC_IRQn);} +static inline void SDMMC1_IRQHandler(void) {handle_interrupt(SDMMC1_IRQn);} +static inline void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} +static inline void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} +static inline void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} +static inline void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} +static inline void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} +static inline void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} +static inline void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} +static inline void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} +static inline void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} +static inline void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} +static inline void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} +static inline void ETH_IRQHandler(void) {handle_interrupt(ETH_IRQn);} +static inline void ETH_WKUP_IRQHandler(void) {handle_interrupt(ETH_WKUP_IRQn);} +static inline void FDCAN_CAL_IRQHandler(void) {handle_interrupt(FDCAN_CAL_IRQn);} +static inline void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} +static inline void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} +static inline void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} +static inline void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} +static inline void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} +static inline void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} +static inline void OTG_HS_EP1_OUT_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_OUT_IRQn);} +static inline void OTG_HS_EP1_IN_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_IN_IRQn);} +static inline void OTG_HS_WKUP_IRQHandler(void) {handle_interrupt(OTG_HS_WKUP_IRQn);} +static inline void OTG_HS_IRQHandler(void) {handle_interrupt(OTG_HS_IRQn);} +static inline void DCMI_PSSI_IRQHandler(void) {handle_interrupt(DCMI_PSSI_IRQn);} +static inline void CRYP_IRQHandler(void) {handle_interrupt(CRYP_IRQn);} +static inline void HASH_RNG_IRQHandler(void) {handle_interrupt(HASH_RNG_IRQn);} +static inline void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} +static inline void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} +static inline void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} +static inline void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} +static inline void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} +static inline void SPI6_IRQHandler(void) {handle_interrupt(SPI6_IRQn);} +static inline void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} +static inline void LTDC_IRQHandler(void) {handle_interrupt(LTDC_IRQn);} +static inline void LTDC_ER_IRQHandler(void) {handle_interrupt(LTDC_ER_IRQn);} +static inline void DMA2D_IRQHandler(void) {handle_interrupt(DMA2D_IRQn);} +static inline void OCTOSPI1_IRQHandler(void) {handle_interrupt(OCTOSPI1_IRQn);} +static inline void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} +static inline void CEC_IRQHandler(void) {handle_interrupt(CEC_IRQn);} +static inline void I2C4_EV_IRQHandler(void) {handle_interrupt(I2C4_EV_IRQn);} +static inline void I2C4_ER_IRQHandler(void) {handle_interrupt(I2C4_ER_IRQn);} +static inline void SPDIF_RX_IRQHandler(void) {handle_interrupt(SPDIF_RX_IRQn);} +static inline void DMAMUX1_OVR_IRQHandler(void) {handle_interrupt(DMAMUX1_OVR_IRQn);} +static inline void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} +static inline void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} +static inline void DFSDM1_FLT2_IRQHandler(void) {handle_interrupt(DFSDM1_FLT2_IRQn);} +static inline void DFSDM1_FLT3_IRQHandler(void) {handle_interrupt(DFSDM1_FLT3_IRQn);} +static inline void SWPMI1_IRQHandler(void) {handle_interrupt(SWPMI1_IRQn);} +static inline void TIM15_IRQHandler(void) {handle_interrupt(TIM15_IRQn);} +static inline void TIM16_IRQHandler(void) {handle_interrupt(TIM16_IRQn);} +static inline void TIM17_IRQHandler(void) {handle_interrupt(TIM17_IRQn);} +static inline void MDIOS_WKUP_IRQHandler(void) {handle_interrupt(MDIOS_WKUP_IRQn);} +static inline void MDIOS_IRQHandler(void) {handle_interrupt(MDIOS_IRQn);} +static inline void MDMA_IRQHandler(void) {handle_interrupt(MDMA_IRQn);} +static inline void SDMMC2_IRQHandler(void) {handle_interrupt(SDMMC2_IRQn);} +static inline void HSEM1_IRQHandler(void) {handle_interrupt(HSEM1_IRQn);} +static inline void ADC3_IRQHandler(void) {handle_interrupt(ADC3_IRQn);} +static inline void DMAMUX2_OVR_IRQHandler(void) {handle_interrupt(DMAMUX2_OVR_IRQn);} +static inline void BDMA_Channel0_IRQHandler(void) {handle_interrupt(BDMA_Channel0_IRQn);} +static inline void BDMA_Channel1_IRQHandler(void) {handle_interrupt(BDMA_Channel1_IRQn);} +static inline void BDMA_Channel2_IRQHandler(void) {handle_interrupt(BDMA_Channel2_IRQn);} +static inline void BDMA_Channel3_IRQHandler(void) {handle_interrupt(BDMA_Channel3_IRQn);} +static inline void BDMA_Channel4_IRQHandler(void) {handle_interrupt(BDMA_Channel4_IRQn);} +static inline void BDMA_Channel5_IRQHandler(void) {handle_interrupt(BDMA_Channel5_IRQn);} +static inline void BDMA_Channel6_IRQHandler(void) {handle_interrupt(BDMA_Channel6_IRQn);} +static inline void BDMA_Channel7_IRQHandler(void) {handle_interrupt(BDMA_Channel7_IRQn);} +static inline void COMP_IRQHandler(void) {handle_interrupt(COMP_IRQn);} +static inline void LPTIM2_IRQHandler(void) {handle_interrupt(LPTIM2_IRQn);} +static inline void LPTIM3_IRQHandler(void) {handle_interrupt(LPTIM3_IRQn);} +static inline void LPTIM4_IRQHandler(void) {handle_interrupt(LPTIM4_IRQn);} +static inline void LPTIM5_IRQHandler(void) {handle_interrupt(LPTIM5_IRQn);} +static inline void LPUART1_IRQHandler(void) {handle_interrupt(LPUART1_IRQn);} +static inline void CRS_IRQHandler(void) {handle_interrupt(CRS_IRQn);} +static inline void ECC_IRQHandler(void) {handle_interrupt(ECC_IRQn);} +static inline void SAI4_IRQHandler(void) {handle_interrupt(SAI4_IRQn);} +static inline void DTS_IRQHandler(void) {handle_interrupt(DTS_IRQn);} +static inline void WAKEUP_PIN_IRQHandler(void) {handle_interrupt(WAKEUP_PIN_IRQn);} +static inline void OCTOSPI2_IRQHandler(void) {handle_interrupt(OCTOSPI2_IRQn);} +static inline void OTFDEC1_IRQHandler(void) {handle_interrupt(OTFDEC1_IRQn);} +static inline void OTFDEC2_IRQHandler(void) {handle_interrupt(OTFDEC2_IRQn);} +static inline void FMAC_IRQHandler(void) {handle_interrupt(FMAC_IRQn);} +static inline void CORDIC_IRQHandler(void) {handle_interrupt(CORDIC_IRQn);} +static inline void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} +static inline void USART10_IRQHandler(void) {handle_interrupt(USART10_IRQn);} +static inline void I2C5_EV_IRQHandler(void) {handle_interrupt(I2C5_EV_IRQn);} +static inline void I2C5_ER_IRQHandler(void) {handle_interrupt(I2C5_ER_IRQn);} +static inline void FDCAN3_IT0_IRQHandler(void) {handle_interrupt(FDCAN3_IT0_IRQn);} +static inline void FDCAN3_IT1_IRQHandler(void) {handle_interrupt(FDCAN3_IT1_IRQn);} +static inline void TIM23_IRQHandler(void) {handle_interrupt(TIM23_IRQn);} +static inline void TIM24_IRQHandler(void) {handle_interrupt(TIM24_IRQn);} diff --git a/board/stm32h7/llfdcan.h b/board/stm32h7/llfdcan.h index 64a40bd072e..e0adda526e4 100644 --- a/board/stm32h7/llfdcan.h +++ b/board/stm32h7/llfdcan.h @@ -1,8 +1,6 @@ #include "llfdcan_declarations.h" -// kbps multiplied by 10 -const uint32_t speeds[SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; -const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U}; +// speeds and data_speeds arrays are defined in fdcan.c static bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) { bool ret = true; diff --git a/board/stm32h7/llspi.h b/board/stm32h7/llspi.h index 05f8e22f9a8..6a7331497e6 100644 --- a/board/stm32h7/llspi.h +++ b/board/stm32h7/llspi.h @@ -1,5 +1,5 @@ // master -> panda DMA start -void llspi_mosi_dma(uint8_t *addr, int len) { +static inline void llspi_mosi_dma(uint8_t *addr, int len) { // disable DMA + SPI register_clear_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); DMA2_Stream2->CR &= ~DMA_SxCR_EN; @@ -26,7 +26,7 @@ void llspi_mosi_dma(uint8_t *addr, int len) { } // panda -> master DMA start -void llspi_miso_dma(uint8_t *addr, int len) { +static inline void llspi_miso_dma(uint8_t *addr, int len) { // disable DMA + SPI DMA2_Stream3->CR &= ~DMA_SxCR_EN; register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); @@ -79,7 +79,7 @@ static void SPI4_IRQ_Handler(void) { } -void llspi_init(void) { +static inline void llspi_init(void) { REGISTER_INTERRUPT(SPI4_IRQn, SPI4_IRQ_Handler, (SPI_IRQ_RATE * 2U), FAULT_INTERRUPT_RATE_SPI) REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) diff --git a/board/stm32h7/llusb.h b/board/stm32h7/llusb.h index f1bcf16ad5e..d87dcebef2c 100644 --- a/board/stm32h7/llusb.h +++ b/board/stm32h7/llusb.h @@ -1,6 +1,6 @@ #include "llusb_declarations.h" -USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; +extern USB_OTG_GlobalTypeDef *USBx; static void OTG_HS_IRQ_Handler(void) { NVIC_DisableIRQ(OTG_HS_IRQn); diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index f615c8b1926..e9eab5be357 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -62,7 +62,7 @@ separate IRQs for RX and TX. #include "board/drivers/drivers.h" #ifdef BOOTSTUB -uart_ring uart_ring_som_debug; +extern uart_ring uart_ring_som_debug; #endif #include "board/drivers/gpio.h" #include "board/stm32h7/peripherals.h" @@ -94,7 +94,7 @@ uart_ring uart_ring_som_debug; #include "board/drivers/spi.h" #include "board/stm32h7/llspi.h" -void early_gpio_float(void) { +static inline void early_gpio_float(void) { RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN; GPIOA->MODER = 0xAB000000U; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; GPIOD->ODR = 0; GPIOE->ODR = 0; GPIOF->ODR = 0; GPIOG->ODR = 0; GPIOH->ODR = 0; diff --git a/board/sys/critical.h b/board/sys/critical.h index 70c143f7012..81db41b71a7 100644 --- a/board/sys/critical.h +++ b/board/sys/critical.h @@ -1,16 +1,16 @@ #include "board/sys/sys.h" // ********************* Critical section helpers ********************* -uint8_t global_critical_depth = 0U; +// global_critical_depth is declared extern in sys.h static volatile bool interrupts_enabled = false; -void enable_interrupts(void) { +static inline void enable_interrupts(void) { interrupts_enabled = true; __enable_irq(); } -void disable_interrupts(void) { +static inline void disable_interrupts(void) { interrupts_enabled = false; __disable_irq(); } diff --git a/board/sys/faults.h b/board/sys/faults.h index 005457988b9..1b5fbe7289a 100644 --- a/board/sys/faults.h +++ b/board/sys/faults.h @@ -1,9 +1,9 @@ #include "board/sys/sys.h" -uint8_t fault_status = FAULT_STATUS_NONE; -uint32_t faults = 0U; +extern uint8_t fault_status; +extern uint32_t faults; -void fault_occurred(uint32_t fault) { +static inline void fault_occurred(uint32_t fault) { if ((faults & fault) == 0U) { if ((PERMANENT_FAULTS & fault) != 0U) { print("Permanent fault occurred: 0x"); puth(fault); print("\n"); @@ -16,7 +16,7 @@ void fault_occurred(uint32_t fault) { faults |= fault; } -void fault_recovered(uint32_t fault) { +static inline void fault_recovered(uint32_t fault) { if ((PERMANENT_FAULTS & fault) == 0U) { faults &= ~fault; } else { From d11e12f09724e53e48122a64bd72713a98e9363a Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 04:47:06 +0800 Subject: [PATCH 43/45] fix: use opendbc.DBC_PATH instead of opendbc.INCLUDE_PATH The opendbc module exports DBC_PATH, not INCLUDE_PATH. This fixes SCons build configuration errors. --- SConscript | 2 +- tests/libpanda/SConscript | 2 +- tests/misra/test_misra.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SConscript b/SConscript index 8c48a52e747..50723718933 100644 --- a/SConscript +++ b/SConscript @@ -90,7 +90,7 @@ def build_project(project_name, project, main, extra_flags): CFLAGS=flags, ASFLAGS=flags, LINKFLAGS=flags, - CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.INCLUDE_PATH], + CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.DBC_PATH], ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES", BUILDERS={ 'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf') diff --git a/tests/libpanda/SConscript b/tests/libpanda/SConscript index 1b82b0e450f..4fbbb0c6051 100644 --- a/tests/libpanda/SConscript +++ b/tests/libpanda/SConscript @@ -8,7 +8,7 @@ env = Environment( '-Wfatal-errors', '-Wno-pointer-to-int-cast', ], - CPPPATH=[".", "../../", "../../board/", opendbc.INCLUDE_PATH], + CPPPATH=[".", "../../", "../../board/", opendbc.DBC_PATH], ) panda = env.SharedObject("panda.os", "panda.c") diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh index ba38568075c..abef4ac39c8 100755 --- a/tests/misra/test_misra.sh +++ b/tests/misra/test_misra.sh @@ -3,7 +3,7 @@ set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" PANDA_DIR=$(realpath $DIR/../../) -OPENDBC_ROOT=$(python3 -c "import opendbc; print(opendbc.INCLUDE_PATH)") +OPENDBC_ROOT=$(python3 -c "import opendbc; print(opendbc.DBC_PATH)") GREEN="\e[1;32m" YELLOW="\e[1;33m" From 853083df1a201959a53a6eaf0a4e20856d04d65f Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 05:16:03 +0800 Subject: [PATCH 44/45] fix: resolve enable_interrupts static/non-static declaration conflict - Remove forward declarations from sys.h (now static inline in critical.h) - Declare interrupts_enabled as extern in sys.h - Define interrupts_enabled in sys.c --- board/drivers/sys.c | 3 +++ board/sys/critical.h | 2 -- board/sys/sys.h | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/board/drivers/sys.c b/board/drivers/sys.c index 108893f68f6..7edd8b2e114 100644 --- a/board/drivers/sys.c +++ b/board/drivers/sys.c @@ -4,6 +4,9 @@ // Global critical section depth uint8_t global_critical_depth = 0U; +// Interrupt enable state (used by EXIT_CRITICAL macro) +volatile bool interrupts_enabled = false; + // Global fault state uint8_t fault_status = FAULT_STATUS_NONE; uint32_t faults = 0U; diff --git a/board/sys/critical.h b/board/sys/critical.h index 81db41b71a7..a24661021bc 100644 --- a/board/sys/critical.h +++ b/board/sys/critical.h @@ -3,8 +3,6 @@ // ********************* Critical section helpers ********************* // global_critical_depth is declared extern in sys.h -static volatile bool interrupts_enabled = false; - static inline void enable_interrupts(void) { interrupts_enabled = true; __enable_irq(); diff --git a/board/sys/sys.h b/board/sys/sys.h index fe5527a0c72..23e1f27d122 100644 --- a/board/sys/sys.h +++ b/board/sys/sys.h @@ -1,11 +1,10 @@ #pragma once // ******************** critical ******************** - -void enable_interrupts(void); -void disable_interrupts(void); +// enable_interrupts/disable_interrupts are defined as static inline in critical.h extern uint8_t global_critical_depth; +extern volatile bool interrupts_enabled; #ifndef ENTER_CRITICAL #define ENTER_CRITICAL() \ From aad55bb384acb51b982168671b89395eb8324fb9 Mon Sep 17 00:00:00 2001 From: xunwen-art Date: Mon, 9 Mar 2026 05:19:25 +0800 Subject: [PATCH 45/45] fix: remove unnecessary can.h include from sys.c - sys.c doesn't need can.h definitions - This fixes the opendbc header not found error in CI --- board/drivers/sys.c | 1 - 1 file changed, 1 deletion(-) diff --git a/board/drivers/sys.c b/board/drivers/sys.c index 7edd8b2e114..624b6224076 100644 --- a/board/drivers/sys.c +++ b/board/drivers/sys.c @@ -1,5 +1,4 @@ #include "board/sys/sys.h" -#include "board/can.h" // Global critical section depth uint8_t global_critical_depth = 0U;