From 13767ee7df7b226ca4da2ed86f81448edb64c542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Th=C3=B6rnblad?= Date: Tue, 8 Jul 2025 16:52:02 +0200 Subject: [PATCH] Add loopbackapp for app debugging using physical TKey with QEMU This app takes data coming in on any of the normal USB endpoints like CDC and FIDO (or CCID) and outputs the data on the DEBUG endpoint. Using the scripts (tkey_to_udp_linux.py, tkey_to_udp_win.py) in QEMU (tk1 branch) under tools/tk1, the data from the DEBUG endpoint can be read and sent over the network to the script udp_to_qemu_linux.py that feeds it to QEMU. --- hw/application_fpga/apps/Makefile | 19 ++- hw/application_fpga/apps/loopbackapp/main.c | 143 ++++++++++++++++++++ 2 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 hw/application_fpga/apps/loopbackapp/main.c diff --git a/hw/application_fpga/apps/Makefile b/hw/application_fpga/apps/Makefile index b8ce38c..a43b6cf 100644 --- a/hw/application_fpga/apps/Makefile +++ b/hw/application_fpga/apps/Makefile @@ -41,7 +41,7 @@ LDFLAGS = \ -L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s .PHONY: all -all: defaultapp.bin reset_test.bin testapp.bin testloadapp.bin +all: defaultapp.bin loopbackapp.bin reset_test.bin testapp.bin testloadapp.bin # Turn elf into bin for device %.bin: %.elf @@ -66,6 +66,14 @@ DEFAULTAPP_OBJS = \ defaultapp.elf: tkey-libs $(OBJS) $(DEFAULTAPP_OBJS) $(CC) $(CFLAGS) $(OBJS) $(DEFAULTAPP_OBJS) $(LDFLAGS) -o $@ +# loopbackapp + +LOOPBACKAPP_OBJS = \ + $(P)/loopbackapp/main.o + +loopbackapp.elf: tkey-libs $(LOOPBACKAPP_OBJS) + $(CC) $(CFLAGS) $(LOOPBACKAPP_OBJS) $(LDFLAGS) -o $@ + # reset_test RESET_TEST_FMTFILES = *.[ch] @@ -97,6 +105,9 @@ fmt: clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch] clang-format --verbose -i defaultapp/*.[ch] + clang-format --dry-run --ferror-limit=0 loopbackapp/*.[ch] + clang-format --verbose -i loopbackapp/*.[ch] + clang-format --dry-run --ferror-limit=0 reset_test/*.[ch] clang-format --verbose -i reset_test/*.[ch] @@ -110,6 +121,8 @@ fmt: checkfmt: clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch] + clang-format --dry-run --ferror-limit=0 loopbackapp/*.[ch] + clang-format --dry-run --ferror-limit=0 reset_test/*.[ch] clang-format --dry-run --ferror-limit=0 testapp/*.[ch] @@ -118,6 +131,6 @@ checkfmt: .PHONY: clean clean: - rm -f *.elf *.bin $(OBJS) $(DEFAULTAPP_OBJS) $(RESET_TEST_OBJS) \ - $(TESTAPP_OBJS) $(TESTLOADAPP_OBJS) + rm -f *.elf *.bin $(OBJS) $(DEFAULTAPP_OBJS) $(LOOPBACKAPP_OBJS) \ + $(RESET_TEST_OBJS) $(TESTAPP_OBJS) $(TESTLOADAPP_OBJS) diff --git a/hw/application_fpga/apps/loopbackapp/main.c b/hw/application_fpga/apps/loopbackapp/main.c new file mode 100644 index 0000000..0121d0f --- /dev/null +++ b/hw/application_fpga/apps/loopbackapp/main.c @@ -0,0 +1,143 @@ +// Copyright (C) 2022, 2023 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 256 +#define HEADER_SIZE 2 +#define HID_PACKET_SIZE 64 +#define MAX_PAYLOAD_SIZE 64 +#define SLEEPTIME 100000 + +void sleep(uint32_t n) +{ + for (volatile int i = 0; i < n; i++) + ; +} + +int main(void) +{ + uint8_t available = 0; + uint8_t cmdbuf[BUFSIZE] = {0}; + enum ioend endpoint = IO_NONE; + bool waiting_for_more_data = false; + uint8_t dest_endpoint = IO_NONE; + uint8_t payload_length = 0; + uint8_t payload_left = 0; + uint8_t payloadbuf[MAX_PAYLOAD_SIZE] = {0}; + uint8_t first_packet_len = HID_PACKET_SIZE - HEADER_SIZE; + uint8_t endpoints = IO_DEBUG; + led_set(LED_BLUE); + + // Give time to CH552 to settle USB enumeration at power on + sleep(SLEEPTIME * 5); + + // Setup available endpoints + endpoints |= IO_CDC | IO_FIDO; + config_endpoints(endpoints); + + while (1) { + // Wait for data + if (readselect(endpoints, &endpoint, &available) != 0) { + assert(1 == 2); + } + + switch (endpoint) { + case IO_CDC: + cmdbuf[0] = IO_CDC; + cmdbuf[1] = available; + if (read(IO_CDC, cmdbuf + 2, BUFSIZE - 2, available) < + 0) { + assert(1 == 2); + } + write(IO_DEBUG, cmdbuf, available + 2); + memset(cmdbuf, 0, BUFSIZE); + break; + + case IO_FIDO: + cmdbuf[0] = IO_FIDO; + cmdbuf[1] = available; + if (read(IO_FIDO, cmdbuf + 2, BUFSIZE - 2, available) < + 0) { + assert(1 == 2); + } + write(IO_DEBUG, cmdbuf, available + 2); + memset(cmdbuf, 0, BUFSIZE); + break; + + case IO_CCID: + cmdbuf[0] = IO_CCID; + cmdbuf[1] = available; + if (read(IO_CCID, cmdbuf + 2, BUFSIZE - 2, available) < + 0) { + assert(1 == 2); + } + write(IO_DEBUG, cmdbuf, available + 2); + memset(cmdbuf, 0, BUFSIZE); + break; + + case IO_DEBUG: + if (available != HID_PACKET_SIZE) { + led_set(LED_RED); + while (1) + ; + } + + if (read(IO_DEBUG, cmdbuf, BUFSIZE, available) < 0) { + assert(1 == 2); + } + + if (!waiting_for_more_data) { + dest_endpoint = cmdbuf[0]; + payload_length = cmdbuf[1]; + + // Check that destination endpoint is ok + if (dest_endpoint != IO_CDC && + dest_endpoint != IO_FIDO && + dest_endpoint != IO_CCID) { + led_set(LED_RED); + while (1) + ; + } + + // Check payload size + if (payload_length > MAX_PAYLOAD_SIZE) { + led_set(LED_RED); + while (1) + ; + } + + // Complete payload fits in this packet + if (payload_length <= first_packet_len) { + write(dest_endpoint, cmdbuf + 2, + payload_length); + memset(cmdbuf, 0, BUFSIZE); + } else { // More payload will come in next + // packet + memcpy(payloadbuf, cmdbuf + 2, + first_packet_len); + payload_left = + payload_length - first_packet_len; + waiting_for_more_data = true; + } + } else { + memcpy(payloadbuf + first_packet_len, cmdbuf, + payload_left); + write(dest_endpoint, payloadbuf, + payload_length); + memset(payloadbuf, 0, MAX_PAYLOAD_SIZE); + memset(cmdbuf, 0, BUFSIZE); + waiting_for_more_data = false; + } + break; + + default: + break; + } + } +}