diff --git a/platformio.ini b/platformio.ini index ba66a38..db484aa 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,6 +23,7 @@ lib_deps = build_flags = -DSUNK_ENABLE -DSUNM_ENABLE + ; -DSUNK_SNIFFER_ENABLE ; sun keyboard sniffer ; -DWIPE_SETTINGS ; wipe settings on every boot -DUSB3SUN_HAL_ARDUINO_PICO -DUSE_TINYUSB diff --git a/run-build-tests.sh b/run-build-tests.sh index 5e58043..bf1e250 100755 --- a/run-build-tests.sh +++ b/run-build-tests.sh @@ -10,6 +10,7 @@ build() { export PLATFORMIO_BUILD_FLAGS=' -USUNK_ENABLE -USUNM_ENABLE + -USUNK_SNIFFER_ENABLE -UWIPE_SETTINGS -UDEBUG_TIMINGS -UUHID_LED_ENABLE @@ -27,6 +28,22 @@ build "$@" export PLATFORMIO_BUILD_FLAGS=' -DSUNK_ENABLE -DSUNM_ENABLE + -USUNK_SNIFFER_ENABLE + -DWIPE_SETTINGS + -DDEBUG_TIMINGS + -DUHID_LED_ENABLE + -DUHID_LED_TEST + -DWAIT_PIN + -DWAIT_SERIAL + -UBUZZER_VERBOSE + -USUNK_VERBOSE + -USUNM_VERBOSE + -UUHID_VERBOSE +'; build "$@" +export PLATFORMIO_BUILD_FLAGS=' + -USUNK_ENABLE + -USUNM_ENABLE + -DSUNK_SNIFFER_ENABLE -DWIPE_SETTINGS -DDEBUG_TIMINGS -DUHID_LED_ENABLE @@ -39,10 +56,11 @@ export PLATFORMIO_BUILD_FLAGS=' -UUHID_VERBOSE '; build "$@" -# build with all flags on. +# build with all flags on, where compatible. export PLATFORMIO_BUILD_FLAGS=' -DSUNK_ENABLE -DSUNM_ENABLE + -USUNK_SNIFFER_ENABLE -DWIPE_SETTINGS -DDEBUG_TIMINGS -DUHID_LED_ENABLE diff --git a/run-tests.sh b/run-tests.sh index 8da236b..9f8b3ea 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -7,3 +7,6 @@ for me in -DSUNM_ENABLE ''; do .pio/build/linux/program ${1-all} done done + +PLATFORMIO_BUILD_FLAGS="-DSUNK_SNIFFER_ENABLE" pio run -e linux +.pio/build/linux/program ${1-all} diff --git a/src/config.h b/src/config.h index 8f66cb7..7de699a 100644 --- a/src/config.h +++ b/src/config.h @@ -53,10 +53,20 @@ #define POWER_KEY 15 // ...any // pin 8: +5 Vdc (orange) +// the following should generally be set in platformio.ini only, but remember +// to update compile_commands.json (pio run -t compiledb) when changing these, +// so clangd knows what features are enabled. // -DSUNK_ENABLE in platformio.ini to enable sun keyboard interface // -DSUNM_ENABLE in platformio.ini to enable sun mouse interface +// -DSUNK_SNIFFER_ENABLE in platform.ini to enable sun keyboard sniffer // -DWIPE_SETTINGS in platformio.ini to wipe settings on every boot +#if defined(SUNK_ENABLE) || defined(SUNM_ENABLE) +#ifdef SUNK_SNIFFER_ENABLE +#error SUNK_ENABLE and SUNM_ENABLE are not allowed when SUNK_SNIFFER_ENABLE is set +#endif +#endif + #if defined(DEBUG_LOGGING) #define Sprint(...) do { pinout.debugPrint(__VA_ARGS__); } while (0) #define Sprintln(...) do { pinout.debugPrintln(__VA_ARGS__); } while (0) diff --git a/src/hal.cc b/src/hal.cc index 207300f..2ff59f3 100644 --- a/src/hal.cc +++ b/src/hal.cc @@ -145,9 +145,12 @@ extern "C" { static Adafruit_SSD1306 display{128, 32, &Wire, /* OLED_RESET */ -1}; static Adafruit_USBH_Host USBHost; +static SerialPIO sunkTxSnifferV1{SerialPIO::NOPIN, SUN_KTX_V1}; +static SerialPIO sunkTxSnifferV2{SerialPIO::NOPIN, SUN_KTX_V2}; static struct { size_t version = 1; HardwareSerial *sunk = &SUNK_UART_V1; + HardwareSerial *sunkTxSniffer = &sunkTxSnifferV1; HardwareSerial *sunm = &SUNM_UART_V1; Adafruit_USBD_CDC *debugCdc = nullptr; SerialUART *debugUart = nullptr; @@ -170,6 +173,7 @@ size_t usb3sun_pinout_version(void) { void usb3sun_pinout_v2(void) { pinout.version = 2; pinout.sunk = &SUNK_UART_V2; + pinout.sunkTxSniffer = &sunkTxSnifferV2; pinout.sunm = &pinout.sunmV2; pinout.sunkTx = SUN_KTX_V2; pinout.sunkRx = SUN_KRX_V2; @@ -194,6 +198,22 @@ size_t usb3sun_sunk_write(uint8_t *data, size_t len) { return pinout.sunk->write(data, len); } +void usb3sun_sunk_sniffer_init(void) { + pinout.sunk->end(); + // SerialUART api requires both tx and rx pins, but we can detach tx afterwards + pinout.sunkUart->setPinout(pinout.sunkTx, pinout.sunkRx); + pinout.sunk->begin(1200, SERIAL_8N1); + // SerialPIO has no such requirement; this call detaches tx from hardware uart + pinout.sunkTxSniffer->begin(1200, SERIAL_8N1); + // gpio invert must be set *after* setPinout/begin + usb3sun_gpio_set_as_inverted(pinout.sunkTx); + usb3sun_gpio_set_as_inverted(pinout.sunkRx); +} + +int usb3sun_sunk_sniff_tx(void) { + return pinout.sunkTxSniffer->read(); +} + void usb3sun_sunm_init(uint32_t baud) { pinout.sunm->end(); switch (pinout.version) { @@ -652,6 +672,15 @@ size_t usb3sun_sunm_write(uint8_t *data, size_t len) { return 0; } +void usb3sun_sunk_sniffer_init(void) { + push_history(SunkSnifferInitOp {}); +} + +int usb3sun_sunk_sniff_tx(void) { + push_history(SunkSniffTxOp {}); + return -1; +} + void usb3sun_usb_init(void) {} void usb3sun_usb_task(void) {} diff --git a/src/hal.h b/src/hal.h index ad48d81..0780565 100644 --- a/src/hal.h +++ b/src/hal.h @@ -46,6 +46,8 @@ typedef struct { struct FsWriteOp { static const uint64_t id = 1 << 11; std::string path; std::vector data; }; struct RebootOp { static const uint64_t id = 1 << 12; }; struct AlarmOp { static const uint64_t id = 1 << 13; uint32_t ms; }; + struct SunkSnifferInitOp { static const uint64_t id = 1 << 14; }; + struct SunkSniffTxOp { static const uint64_t id = 1 << 15; }; using Op = std::variant< PinoutV2Op, SunkInitOp, @@ -60,7 +62,9 @@ typedef struct { FsReadOp, FsWriteOp, RebootOp, - AlarmOp>; + AlarmOp, + SunkSnifferInitOp, + SunkSniffTxOp>; struct Entry { uint64_t micros; Op op; @@ -93,6 +97,8 @@ typedef struct { DERIVE_OP(FsWriteOp, p.path == q.path && p.data == q.data, "fs_write " << o.path << " " << o.data); DERIVE_OP(RebootOp, ((void) p, (void) q, true), ((void) o, "reboot")); DERIVE_OP(AlarmOp, p.ms == q.ms, "alarm " << o.ms); + DERIVE_OP(SunkSnifferInitOp, ((void) p, (void) q, true), ((void) o, "sunk_sniffer_init")); + DERIVE_OP(SunkSniffTxOp, ((void) p, (void) q, true), ((void) o, "sunk_sniff_tx")); void usb3sun_test_init(uint64_t history_filter_mask); void usb3sun_mock_gpio_read(usb3sun_pin pin, bool value); void usb3sun_mock_sunk_read(const char *data, size_t len); @@ -117,6 +123,9 @@ void usb3sun_sunk_init(void); int usb3sun_sunk_read(void); size_t usb3sun_sunk_write(uint8_t *data, size_t len); +void usb3sun_sunk_sniffer_init(void); +int usb3sun_sunk_sniff_tx(void); + void usb3sun_sunm_init(uint32_t baud); size_t usb3sun_sunm_write(uint8_t *data, size_t len); diff --git a/src/main.cc b/src/main.cc index d4b6a0f..29f7956 100644 --- a/src/main.cc +++ b/src/main.cc @@ -194,11 +194,18 @@ void loop() { while ((input = usb3sun_debug_uart_read()) != -1) { handleCliInput(input); } +#ifdef SUNK_SNIFFER_ENABLE + while ((input = usb3sun_sunk_sniff_tx()) != -1) { + uint8_t command = input; + Sprintf("sunk: sniff tx %02Xh (%d)\n", command, command); + } +#endif + usb3sun_sleep_micros(10'000); } -#ifdef SUNK_ENABLE +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) void sunkEvent() { int result; while ((result = usb3sun_sunk_read()) != -1) { @@ -206,11 +213,13 @@ void sunkEvent() { Sprintf("sunk: rx %02Xh\n", command); switch (command) { case SUNK_RESET: { +#ifndef SUNK_SNIFFER_ENABLE // self test fail: // usb3sun_sunk_write(0x7E); // usb3sun_sunk_write(0x01); uint8_t response[]{SUNK_RESET_RESPONSE, 0x04, 0x7F}; // TODO optional make code usb3sun_sunk_write(response, sizeof response); +#endif } break; case SUNK_BELL_ON: state.bell = true; @@ -240,9 +249,11 @@ void sunkEvent() { usb3sun_fifo_push((uint32_t)Message::UHID_LED_FROM_STATE); } break; case SUNK_LAYOUT: { +#ifndef SUNK_SNIFFER_ENABLE // UNITED STATES (TODO alternate layouts) uint8_t response[]{SUNK_LAYOUT_RESPONSE, 0b00000000}; usb3sun_sunk_write(response, sizeof response); +#endif } break; } } @@ -250,13 +261,13 @@ void sunkEvent() { #endif void serialEvent1() { -#if defined(SUNK_ENABLE) +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) sunkEvent(); #endif } void serialEvent2() { -#if defined(SUNK_ENABLE) +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) sunkEvent(); #endif } @@ -531,7 +542,7 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons #include #include -#define TEST_REQUIRES(expr) do { fprintf(stderr, ">>> skipping test (%s)\n", #expr); return true; } while (0) +#define TEST_REQUIRES(expr) do { fprintf(stderr, ">>> skipping test (requires %s)\n", #expr); return true; } while (0) #define TEST_ASSERT_EQ(actual, expected) do { if (actual != expected) { std::cerr << "\n" __FILE__ ":" << __LINE__ << ": assertion failed: " #actual "\n actual: " << actual << "\n expected: " << expected << "\n"; return false; } } while (0) static bool assert_then_clear_test_history(const char *file, size_t line, const std::vector &expected) { const std::vector &actual = usb3sun_test_get_history(); @@ -598,7 +609,7 @@ static std::vector bytes(size_t len, const char *data) { static bool run_test(const char *test_name) { if (!strcmp(test_name, "setup_pinout_v1")) { - usb3sun_test_init(PinoutV2Op::id | SunkInitOp::id | SunmInitOp::id | GpioWriteOp::id | GpioReadOp::id); + usb3sun_test_init(PinoutV2Op::id | SunkInitOp::id | SunmInitOp::id | GpioWriteOp::id | GpioReadOp::id | SunkSnifferInitOp::id); usb3sun_mock_gpio_read(PINOUT_V2_PIN, false); setup(); return assert_then_clear_test_history(std::vector { @@ -607,6 +618,9 @@ static bool run_test(const char *test_name) { #ifdef SUNK_ENABLE SunkInitOp {}, #endif +#ifdef SUNK_SNIFFER_ENABLE + SunkSnifferInitOp {}, +#endif #ifdef SUNM_ENABLE SunmInitOp {9600}, #endif @@ -615,7 +629,7 @@ static bool run_test(const char *test_name) { } if (!strcmp(test_name, "setup_pinout_v2")) { - usb3sun_test_init(PinoutV2Op::id | SunkInitOp::id | SunmInitOp::id | GpioWriteOp::id | GpioReadOp::id); + usb3sun_test_init(PinoutV2Op::id | SunkInitOp::id | SunmInitOp::id | GpioWriteOp::id | GpioReadOp::id | SunkSnifferInitOp::id); usb3sun_mock_gpio_read(PINOUT_V2_PIN, true); setup(); return assert_then_clear_test_history(std::vector { @@ -625,6 +639,11 @@ static bool run_test(const char *test_name) { GpioWriteOp {DISPLAY_ENABLE, true}, #ifdef SUNK_ENABLE SunkInitOp {}, +#endif +#ifdef SUNK_SNIFFER_ENABLE + SunkSnifferInitOp {}, +#endif +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) GpioWriteOp {KTX_ENABLE, false}, #endif #ifdef SUNM_ENABLE @@ -635,8 +654,8 @@ static bool run_test(const char *test_name) { } if (!strcmp(test_name, "sunk_reset")) { -#ifndef SUNK_ENABLE - TEST_REQUIRES(SUNK_ENABLE); +#if !defined(SUNK_ENABLE) && !defined(SUNK_SNIFFER_ENABLE) + TEST_REQUIRES(SUNK_ENABLE or SUNK_SNIFFER_ENABLE); #endif usb3sun_test_init(SunkReadOp::id | SunkWriteOp::id); usb3sun_mock_sunk_read("\x01", 1); // SUNK_RESET @@ -646,10 +665,14 @@ static bool run_test(const char *test_name) { serialEvent2(); } return assert_then_clear_test_history(std::vector { +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) SunkReadOp {}, +#ifndef SUNK_SNIFFER_ENABLE SunkWriteOp {{0xFF, 0x04, 0x7F}}, +#endif SunkReadOp {}, SunkReadOp {}, +#endif }); } @@ -681,8 +704,8 @@ static bool run_test(const char *test_name) { } if (!strcmp(test_name, "buzzer_bell")) { -#ifndef SUNK_ENABLE - TEST_REQUIRES(SUNK_ENABLE); +#if !defined(SUNK_ENABLE) && !defined(SUNK_SNIFFER_ENABLE) + TEST_REQUIRES(SUNK_ENABLE or SUNK_SNIFFER_ENABLE); #endif usb3sun_test_init(BuzzerStartOp::id | GpioWriteOp::id); usb3sun_mock_sunk_read("\x01\x02\x03", 3); // SUNK_RESET, SUNK_BELL_ON, SUNK_BELL_OFF @@ -702,8 +725,8 @@ static bool run_test(const char *test_name) { } if (!strcmp(test_name, "buzzer_click")) { -#ifndef SUNK_ENABLE - TEST_REQUIRES(SUNK_ENABLE); +#if !defined(SUNK_ENABLE) && !defined(SUNK_SNIFFER_ENABLE) + TEST_REQUIRES(SUNK_ENABLE or SUNK_SNIFFER_ENABLE); #endif const auto pumpSunkInput = []() { while (usb3sun_mock_sunk_read_has_input()) { diff --git a/src/pinout.cc b/src/pinout.cc index cf2abb7..7d85140 100644 --- a/src/pinout.cc +++ b/src/pinout.cc @@ -106,7 +106,11 @@ void Pinout::begin() { void Pinout::beginSun() { #if defined(SUNK_ENABLE) usb3sun_sunk_init(); - +#endif +#if defined(SUNK_SNIFFER_ENABLE) + usb3sun_sunk_sniffer_init(); +#endif +#if defined(SUNK_ENABLE) || defined(SUNK_SNIFFER_ENABLE) if (usb3sun_pinout_version() == 2) { // break preventer: set KTX_ENABLE# low to connect sun keyboard tx. // the pin is high on reset and boot, which pulls INT_KTX low, which keeps the