QUELLCODE / C
firmware_test.c
pico/tests/firmware_test.c
1/*2 * OpenAss: nur privater, experimenteller Gebrauch; auf eigene Verantwortung.3 * Niemals Unternehmensrechner, Produktivsysteme, Industrieanlagen oder4 * sicherheitskritische Systeme anschließen oder bedienen.5 * Haftungshinweis und gesetzliche Grenzen: ../../DISCLAIMER.txt.6 */7#include <assert.h>8#include <stdio.h>9#include "fake_sdk.h"1011// Include the real firmware so tests exercise its private control path rather12// than a parallel implementation. Only hardware/TinyUSB transport is simulated.13#define main firmware_entry_unused14#include "../main.c"15#undef main1617uart_inst_t fake_uart;18static uint64_t fake_now = 50000u;19static bool fake_pins[32];20static bool fake_mounted = true;21static bool fake_suspended;22static bool fake_queue_ready = true;23static bool fake_complete = true;24static bool fake_inflight[3];25static uint8_t fake_reports[3][8];26static uint16_t fake_lengths[3];27static unsigned fake_report_count[3];28static uint8_t fake_protocol[3] = {1, 1, 1};29static uint64_t inject_estop_at;30static char fake_tx[8192];31static size_t fake_tx_length;32static uint32_t fake_rx[512];33static size_t fake_rx_length;34static size_t fake_rx_offset;3536uint64_t time_us_64(void) { return fake_now++; }37bool gpio_get(uint pin) { return fake_pins[pin]; }38void gpio_init(uint pin) { (void)pin; }39void gpio_set_dir(uint pin, bool out) { (void)pin; (void)out; }40void gpio_pull_up(uint pin) { (void)pin; }41void gpio_put(uint pin, bool state) { (void)pin; (void)state; }42void gpio_set_function(uint pin, uint function) { (void)pin; (void)function; }43void gpio_set_irq_enabled_with_callback(uint pin, uint32_t events, bool enabled,44 void (*callback)(uint, uint32_t)) {45 (void)pin; (void)events; (void)enabled; (void)callback;46}47void uart_init(uart_inst_t *uart, uint baud) { (void)uart; (void)baud; }48void uart_set_hw_flow(uart_inst_t *uart, bool cts, bool rts) { (void)uart; (void)cts; (void)rts; }49void uart_set_format(uart_inst_t *uart, uint data, uint stop, uint parity) {50 (void)uart; (void)data; (void)stop; (void)parity;51}52void uart_set_fifo_enabled(uart_inst_t *uart, bool enabled) { (void)uart; (void)enabled; }53bool uart_is_writable(uart_inst_t *uart) { (void)uart; return true; }54bool uart_is_readable(uart_inst_t *uart) {55 if (fake_rx_offset == fake_rx_length) return false;56 uart->hw.dr = fake_rx[fake_rx_offset++];57 return true;58}59void uart_putc_raw(uart_inst_t *uart, char byte) {60 (void)uart;61 assert(fake_tx_length + 1u < sizeof(fake_tx));62 fake_tx[fake_tx_length++] = byte;63 fake_tx[fake_tx_length] = '\0';64}65uart_hw_t *uart_get_hw(uart_inst_t *uart) { return &uart->hw; }66void watchdog_update(void) {}67void watchdog_enable(uint32_t delay_ms, bool pause) { (void)delay_ms; (void)pause; }68void tight_loop_contents(void) {}69bool tud_init(uint8_t port) { (void)port; return true; }70bool tud_mounted(void) { return fake_mounted; }71bool tud_suspended(void) { return fake_suspended; }72uint8_t tud_hid_n_get_protocol(uint8_t instance) { return fake_protocol[instance]; }73bool tud_hid_n_ready(uint8_t instance) {74 return fake_mounted && !fake_suspended && fake_queue_ready && !fake_inflight[instance];75}76bool tud_hid_n_report(uint8_t instance, uint8_t report_id, const void *data, uint16_t length) {77 assert(report_id == 0 && length <= sizeof(fake_reports[instance]));78 if (!tud_hid_n_ready(instance)) return false;79 memcpy(fake_reports[instance], data, length);80 fake_lengths[instance] = length;81 fake_inflight[instance] = true;82 ++fake_report_count[instance];83 return true;84}85void tud_task(void) {86 fake_now += 1000u;87 if (inject_estop_at && fake_now >= inject_estop_at) {88 fake_pins[HB_INTERLOCK_PIN] = true;89 interlock_irq(HB_INTERLOCK_PIN, GPIO_IRQ_EDGE_RISE);90 inject_estop_at = 0;91 }92 if (fake_complete) {93 for (uint8_t i = 0; i < HB_HID_COUNT; ++i) {94 if (!fake_inflight[i]) continue;95 fake_inflight[i] = false;96 tud_hid_report_complete_cb(i, fake_reports[i], fake_lengths[i]);97 }98 }99}100101static hb_request_t request(const char *body) {102 char frame[HB_FRAME_MAX + 1u];103 hb_request_t result;104 assert(hb_response(frame, sizeof(frame), 1, body));105 assert(hb_parse(frame, strlen(frame) - 1u, &result) == HB_PARSE_OK);106 return result;107}108109static void expect(const char *body, const char *result) {110 hb_request_t parsed = request(body);111 assert(strcmp(execute(&parsed), result) == 0);112}113114static void command(uint32_t seq, const char *body) {115 char frame[HB_FRAME_MAX + 1u];116 assert(hb_response(frame, sizeof(frame), seq, body));117 process_line(frame, strlen(frame) - 1u);118 uart_tx_service();119}120121static void drain(void) {122 for (unsigned i = 0; i < 5; ++i) core_service();123 assert(release_pending == 0 && release_inflight == 0);124}125126static void grant_and_arm(void) {127 local_window_until_us = fake_now + HB_LOCAL_WINDOW_US;128 expect("ARM", "OK");129 assert(armed && local_window_until_us == 0);130}131132int main(void) {133 fake_pins[HB_LOCAL_ARM_PIN] = true;134 fake_pins[HB_INTERLOCK_PIN] = false;135 safety_service();136 unsigned before = fake_report_count[HB_HID_ABSOLUTE];137 expect("ABS 1 2 1", "ERR NOT_ARMED");138 assert(fake_report_count[HB_HID_ABSOLUTE] == before);139 expect("ARM", "ERR LOCAL_ARM_REQUIRED");140 expect("PING", "ERR NOT_ARMED");141 fake_now += HB_DEBOUNCE_US;142 safety_service();143 fake_pins[HB_LOCAL_ARM_PIN] = false;144 safety_service();145 fake_now += HB_DEBOUNCE_US - 1000u;146 safety_service();147 assert(local_window_until_us == 0);148 fake_now += 2000u;149 safety_service();150 assert(local_window_until_us > fake_now);151 request_releases();152 expect("ARM", "OK");153 expect("ARM", "ERR ALREADY_ARMED");154 expect("KEY 256 0 0 0 0 0 0", "ERR RANGE");155 expect("REL 128 0 0 0 0", "ERR RANGE");156157 command(10, "KEY 2 4 0 0 0 0 0");158 assert(keyboard_state[0] == 2 && keyboard_state[2] == 4);159 unsigned keys_sent = fake_report_count[HB_HID_KEYBOARD];160 command(10, "KEY 2 4 0 0 0 0 0");161 assert(fake_report_count[HB_HID_KEYBOARD] == keys_sent);162 command(10, "KEY 0 5 0 0 0 0 0");163 assert(strstr(fake_tx, "ERR SEQ_CONFLICT") && keyboard_state[2] == 4);164 command(9, "STOP");165 assert(armed && strstr(fake_tx, "ERR SEQ_OLD"));166 command(11, "PING");167 uint64_t heartbeat = last_ping_us;168 fake_now += 100000u;169 command(11, "PING");170 assert(last_ping_us == heartbeat);171 expect("RELEASE", "OK");172 assert(armed && keyboard_state[2] == 0);173 expect("ABS 32767 0 1", "OK");174 assert(absolute_known && absolute_state[0] == 1 && fake_lengths[HB_HID_ABSOLUTE] == 5);175 expect("REL -127 127 -1 1 2", "OK");176 assert(absolute_state[0] == 0 && relative_state[0] == 2 && relative_state[1] == 0);177 assert(fake_lengths[HB_HID_RELATIVE] == 5);178 uint8_t get_report[8];179 assert(tud_hid_get_report_cb(HB_HID_RELATIVE, 0, HID_REPORT_TYPE_INPUT, get_report, 8) == 5);180 assert(get_report[1] == 0 && get_report[2] == 0 && get_report[3] == 0 && get_report[4] == 0);181 fake_protocol[HB_HID_RELATIVE] = HID_PROTOCOL_BOOT;182 expect("REL 0 0 1 0 0", "ERR BOOT_SCROLL_UNAVAILABLE");183 expect("REL 127 -127 0 0 1", "OK");184 assert(fake_lengths[HB_HID_RELATIVE] == 3);185 assert(tud_hid_set_idle_cb(HB_HID_RELATIVE, 1));186 uint64_t idle_heartbeat = last_ping_us;187 fake_now += 5000u;188 core_service();189 assert(fake_reports[HB_HID_RELATIVE][0] == 1 && fake_reports[HB_HID_RELATIVE][1] == 0 && fake_reports[HB_HID_RELATIVE][2] == 0);190 assert(last_ping_us == idle_heartbeat);191 assert(tud_hid_set_idle_cb(HB_HID_RELATIVE, 0));192 expect("KEY 255 100 101 0 0 0 0", "OK");193 fake_now += HB_WATCHDOG_US;194 safety_service();195 assert(!armed && keyboard_state[0] == 0 && keyboard_state[2] == 0 && relative_state[0] == 0);196 drain();197 assert(fake_reports[HB_HID_KEYBOARD][0] == 0 && fake_reports[HB_HID_KEYBOARD][2] == 0);198 command(1, "HELLO");199 assert(sequence_state.seq == 1 && strstr(sequence_state.response, "OK 1"));200 grant_and_arm();201 fake_queue_ready = false;202 inject_estop_at = fake_now + 10000u;203 expect("KEY 0 4 0 0 0 0 0", "ERR ESTOP");204 assert(estop_latched && !armed && keyboard_state[2] == 0);205 fake_queue_ready = true;206 fake_pins[HB_INTERLOCK_PIN] = false;207 local_window_until_us = fake_now + HB_LOCAL_WINDOW_US;208 expect("ARM", "ERR ESTOP");209 expect("STOP", "OK");210 drain();211212 // Simulate a deliberate MCU reboot (closed interlock); normal commands have213 // no path that clears the latch. Then check hung target transport timeout.214 estop_irq_latched = false;215 estop_latched = false;216 grant_and_arm();217 fake_complete = false;218 uint64_t started = fake_now;219 expect("KEY 0 4 0 0 0 0 0", "ERR USB_TIMEOUT");220 assert(!armed && fake_now - started < 110000u && keyboard_state[2] == 0);221 fake_complete = true;222 drain();223 grant_and_arm();224 fake_queue_ready = false;225 started = fake_now;226 expect("REL 0 0 0 0 0", "ERR USB_TIMEOUT");227 assert(!armed && fake_now - started < 110000u);228 fake_queue_ready = true;229 drain();230 grant_and_arm();231 fake_complete = false;232 inject_estop_at = fake_now + 10000u;233 expect("KEY 0 4 0 0 0 0 0", "ERR ESTOP");234 assert(!armed && keyboard_state[2] == 0);235 fake_complete = true;236 fake_pins[HB_INTERLOCK_PIN] = false;237 estop_irq_latched = false;238 estop_latched = false;239 drain();240 fake_mounted = false;241 expect("ARM", "ERR LOCAL_ARM_REQUIRED");242 local_window_until_us = fake_now + HB_LOCAL_WINDOW_US;243 expect("ARM", "ERR USB_NOT_READY");244 expect("STOP", "OK");245 assert(release_pending != 0);246 fake_mounted = true;247 tud_mount_cb();248 drain();249 assert(!armed);250 grant_and_arm();251 tud_suspend_cb(false);252 assert(!armed);253 drain();254255 // Framing error and overflow drop the entire line without executing it.256 memset(&sequence_state, 0, sizeof(sequence_state));257 char rx_frame[HB_FRAME_MAX + 1u];258 assert(hb_response(rx_frame, sizeof(rx_frame), 5, "HELLO"));259 fake_rx_length = strlen(rx_frame);260 for (size_t i = 0; i < fake_rx_length; ++i) fake_rx[i] = (uint8_t)rx_frame[i];261 fake_rx[6] |= 0x0800u;262 fake_rx_offset = 0;263 uart_rx_service();264 assert(!sequence_state.valid);265 uart_tx_service();266 fake_rx_length = 201u;267 for (size_t i = 0; i < fake_rx_length - 1u; ++i) fake_rx[i] = 'X';268 fake_rx[fake_rx_length - 1u] = '\n';269 fake_rx_offset = 0;270 while (fake_rx_offset < fake_rx_length) { uart_rx_service(); uart_tx_service(); }271 assert(!sequence_state.valid && !rx_discard && rx_length == 0);272 fake_rx_length = 1u;273 fake_rx[0] = 'H';274 fake_rx_offset = 0;275 uart_rx_service();276 assert(rx_length == 1u);277 fake_now += HB_RX_TIMEOUT_US;278 uart_rx_service();279 assert(rx_discard && rx_length == 0);280 fake_rx_offset = 0;281 fake_rx[0] = '\n';282 uart_rx_service();283 assert(!rx_discard && !sequence_state.valid);284 puts("Simulated firmware safety tests passed (no physical USB/UART verification).");285 return 0;286}