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}