From 07e3c9eb06cbec68711d73d47fe878fdcb132964 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 23 Oct 2024 08:07:38 -0500 Subject: [PATCH] tools: add ftdi_transfer --- tools/Makefile | 27 +++- tools/ftdi_transfer.c | 312 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 tools/ftdi_transfer.c diff --git a/tools/Makefile b/tools/Makefile index 216e912..0a3c6c7 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,16 +1,25 @@ CFLAGS = -Og -g -gdwarf-4 -Wall -Wextra -Werror -Wfatal-errors -ggdb -Wno-error=unused-parameter -Wno-error=unused-variable -fstack-protector-strong CXXFLAGS = -std=c++23 -CFLAGS += $(shell pkg-config --cflags freetype2) -LDFLAGS = $(shell pkg-config --libs freetype2) +FREETYPE_CFLAGS = $(shell pkg-config --cflags freetype2) +FREETYPE_LDFLAGS = $(shell pkg-config --libs freetype2) + +FTDI_CFLAGS = $(shell pkg-config --cflags libftdi1) +FTDI_LDFLAGS = $(shell pkg-config --libs libftdi1) all: ttf_outline -%.o: %.cpp - $(CXX) $(CFLAGS) $(CXXFLAGS) -c $< -o $@ +ttf_%.o: ttf_%.cpp + $(CXX) $(CFLAGS) $(FREETYPE_CFLAGS) $(CXXFLAGS) -c $< -o $@ -%: %.o - $(CXX) $(LDFLAGS) $^ -o $@ +ttf_%: ttf_%.o + $(CXX) $(LDFLAGS) $(FREETYPE_LDFLAGS) $^ -o $@ + +ftdi_%.o: ftdi_%.c + $(CC) -std=gnu2x $(CFLAGS) $(FTDI_CFLAGS) -c $< -o $@ + +ftdi_%: ftdi_%.o + $(CXX) $(LDFLAGS) $(FTDI_LDFLAGS) $^ -o $@ ttf_outline: ttf_outline.o 2d_pack.o @@ -21,3 +30,9 @@ clean: .INTERMEDIATE: .SECONDARY: .PHONY: all clean + +%: RCS/%,v +%: RCS/% +%: %,v +%: s.% +%: SCCS/s.% diff --git a/tools/ftdi_transfer.c b/tools/ftdi_transfer.c new file mode 100644 index 0000000..9c82e51 --- /dev/null +++ b/tools/ftdi_transfer.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi, + unsigned short *value, unsigned short *index); + +int dreamcast_rates[] = { + 1562500, + 781250, + 520833, + 390625, + 312500, + 260416, + 223214, + 195312, + 173611, + 156250, + 142045, + 130208, + 120192 +}; + +int init_ftdi_context(struct ftdi_context * ftdi) +{ + ftdi_set_interface(ftdi, INTERFACE_ANY); + struct ftdi_device_list * devlist; + int res; + if ((res = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0) { + fprintf(stderr, "ftdi_usb_find_all\n"); + return -1; + } + + if (res == 0) { + fprintf(stderr, "no device\n"); + return -1; + } + + struct libusb_device_descriptor desc; + struct ftdi_device_list * devlist_item = devlist; + for (int i = 0; i < res; i++) { + res = libusb_get_device_descriptor(devlist_item->dev, &desc); + if (res < 0) { + fprintf(stderr, "libusb_get_device_descriptor\n"); + return -1; + } + fprintf(stdout, "idVendor: %04x; idProduct: %04x;\n", desc.idVendor, desc.idProduct); + fprintf(stdout, "bNumConfigurations: %d;\n", desc.bNumConfigurations); + devlist_item = devlist_item->next; + } + + res = ftdi_usb_open_dev(ftdi, devlist->dev); + if (res < 0) { + fprintf(stderr, "ftdi_usb_open_dev\n"); + return -1; + } + ftdi_list_free(&devlist); + + unsigned short value; + unsigned short index; + + for (unsigned int i = 0; i < (sizeof (dreamcast_rates)) / (sizeof (dreamcast_rates[0])); i++) { + int baud = convert_baudrate_UT_export(dreamcast_rates[i], ftdi, &value, &index); + float baudf = baud; + float ratef = dreamcast_rates[i]; + float error = (baudf > ratef) ? ratef / baudf : baudf / ratef; + fprintf(stdout, "%d: best: %d, error: %f\n", dreamcast_rates[i], baud, (1.f - error) * 100.f); + } + + res = ftdi_set_baudrate(ftdi, 1562500); + //res = ftdi_set_baudrate(ftdi, 312500); + if (res < 0) { + fprintf(stderr, "ftdi_set_baudrate\n"); + return -1; + } + + res = ftdi_set_line_property(ftdi, 8, STOP_BIT_1, NONE); + if (res < 0) { + fprintf(stderr, "ftdi_set_line_property\n"); + return -1; + } + + res = ftdi_set_latency_timer(ftdi, 1); + if (res < 0) { + fprintf(stderr, "ftdi_set_latency_timer\n"); + return -1; + } + + res = ftdi_tciflush(ftdi); + if (res < 0) { + fprintf(stderr, "ftdi_tciflush\n"); + return -1; + } + + res = ftdi_tcoflush(ftdi); + if (res < 0) { + fprintf(stderr, "ftdi_tcoflush\n"); + return -1; + } + + + return 0; +} + +union data_command { + struct { + uint8_t command[4]; + uint32_t size; + uint32_t dest; + }; + uint8_t data[4 * 3]; +}; +static_assert((sizeof (union data_command)) == 4 * 3); + +uint32_t bswap(const uint32_t n) +{ + if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return n; + else + return __builtin_bswap32(n); +} + +long read_with_timeout(struct ftdi_context * ftdi, uint8_t * read_buf, const long expect_length) +{ + int res; + struct timespec tp0; + res = clock_gettime(CLOCK_MONOTONIC, &tp0); + assert(res >= 0); + + long read_length = 0; + while (true) { + res = ftdi_read_data(ftdi, read_buf, expect_length - read_length); + assert(res >= 0); + + read_length += res; + if (read_length >= expect_length) + break; + + struct timespec tp1; + res = clock_gettime(CLOCK_MONOTONIC, &tp1); + assert(res >= 0); + + if (tp1.tv_sec - tp0.tv_sec > 1) { + fprintf(stderr, "read timeout: %ld expect: %ld\n", read_length, expect_length); + break; + } + } + return read_length; +} + +const int chunk_size = 1024; + +long min(long a, long b) +{ + return a > b ? b : a; +} + +long max(long a, long b) +{ + return a > b ? a : b; +} + +void symmetric(struct ftdi_context * ftdi, const uint8_t * tx_buf, const long size) +{ + int res; + uint8_t rx_buf[size]; + long tx_offset = 0; + long rx_offset = 0; + + while (tx_offset < size) { + long txrx_diff = tx_offset - rx_offset; + long tx_length = max(min(min(chunk_size, size - tx_offset), chunk_size - txrx_diff), 0); + + if (tx_length > 0) { + res = ftdi_write_data(ftdi, &tx_buf[tx_offset], tx_length); + assert(res >= 0); + tx_offset += res; + } + + res = ftdi_read_data(ftdi, &rx_buf[rx_offset], size - rx_offset); + assert(res >= 0); + rx_offset += res; + } + + for (int i = 0; i < size; i++) { + if (tx_buf[i] != rx_buf[i]) { + fprintf(stderr, "mismatch at %d\n", i); + return; + } + } + fprintf(stderr, "equal\n"); +} + +double timespec_difference(struct timespec const * const a, struct timespec const * const b) +{ + return (double)(a->tv_sec - b->tv_sec) + (double)(a->tv_nsec - b->tv_nsec) / 1'000'000'000.0; +} + +int transfer(struct ftdi_context * ftdi, const uint8_t * buf, const long size) +{ + int res; + + union data_command command = { + .command = {'D', 'A', 'T', 'A'}, + .size = bswap(size), + .dest = bswap(0xac010000), + }; + + res = ftdi_write_data(ftdi, command.data, (sizeof (union data_command))); + assert(res >= 0); + + const char * expect = "data\n"; + const long expect_length = 5; + uint8_t read_buf[expect_length + 1]; + read_buf[expect_length] = 0; + long read_length = read_with_timeout(ftdi, read_buf, expect_length); + if (read_length != expect_length) { + fprintf(stderr, "want %ld bytes; received: %ld\n", expect_length, read_length); + return -1; + } + res = memcmp(read_buf, expect, expect_length); + if (res != 0) { + fprintf(stderr, "expect `%s`; received: `%s`\n", expect, read_buf); + return -1; + } + + fprintf(stderr, "OK\n"); + + struct timespec start; + struct timespec end; + res = clock_gettime(CLOCK_MONOTONIC, &start); + symmetric(ftdi, buf, size); + res = clock_gettime(CLOCK_MONOTONIC, &end); + fprintf(stderr, "symmetric time: %.03f\n", timespec_difference(&end, &start)); + + + return 0; +} + +int main(int argc, char * argv[]) +{ + if (argc < 2) { + fprintf(stderr, "argc\n"); + return EXIT_FAILURE; + } + + FILE * file = fopen(argv[1], "r"); + if (file == NULL) { + fprintf(stderr, "fopen\n"); + return EXIT_FAILURE; + } + + int ret; + ret = fseek(file, 0L, SEEK_END); + if (ret < 0) { + fprintf(stderr, "seek(SEEK_END)"); + return EXIT_FAILURE; + } + + long off = ftell(file); + + ret = fseek(file, 0L, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "seek(SEEK_SET)"); + return EXIT_FAILURE; + } + + fprintf(stderr, "%s off %ld\n", argv[1], off); + uint8_t buf[off]; + ssize_t size = fread(buf, 1, off, file); + if (size < 0) { + fprintf(stderr, "read"); + return EXIT_FAILURE; + } + + ret = fclose(file); + if (ret < 0) { + fprintf(stderr, "close"); + return EXIT_FAILURE; + } + + struct ftdi_context * ftdi; + + ftdi = ftdi_new(); + if (ftdi == 0) { + fprintf(stderr, "ftdi_new\n"); + return EXIT_FAILURE; + } + + int res; + res = init_ftdi_context(ftdi); + if (res < 0) { + return EXIT_FAILURE; + } + + struct timespec start; + struct timespec end; + res = clock_gettime(CLOCK_MONOTONIC, &start); + int transfer_ret = transfer(ftdi, buf, off); + res = clock_gettime(CLOCK_MONOTONIC, &end); + + fprintf(stderr, "time: %.03f\n", timespec_difference(&end, &start)); + + return transfer_ret; +}