diff --git a/minichlink/Makefile b/minichlink/Makefile index a60c9f9b4f4ff6706cbab48dfec14641a1495561..15d3c1a5a3f680519403ae26d9bdb8883b348351 100644 --- a/minichlink/Makefile +++ b/minichlink/Makefile @@ -1,7 +1,7 @@ TOOLS:=minichlink minichlink.so CFLAGS:=-O0 -g3 -Wall -C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c minichgdb.c +C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c ardulink.c serial_dev.c minichgdb.c # General Note: To use with GDB, gdb-multiarch # gdb-multilib {file} @@ -23,9 +23,6 @@ else LIBINCLUDES:=$(shell pkg-config --libs-only-L libusb-1.0) INCS:=$(INCLUDES) $(LIBINCLUDES) endif - - # Until it's supported on Windows, add the Ardulink file here. - C_S:=$(C_S) ardulink.c endif all : $(TOOLS) diff --git a/minichlink/ardulink.c b/minichlink/ardulink.c index 5da4d10640b31c44aaa2c8651182aa14aedb8a08..44fa1446f831715b03b56fde47cad800cbaa04c7 100644 --- a/minichlink/ardulink.c +++ b/minichlink/ardulink.c @@ -1,14 +1,7 @@ #include <stdint.h> #include <stdio.h> #include <stdlib.h> - -#include <termios.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include <errno.h> - +#include "serial_dev.h" #include "minichlink.h" void * TryInit_Ardulink(void); @@ -19,10 +12,9 @@ static int ArdulinkFlushLLCommands(void * dev); static int ArdulinkExit(void * dev); static int ArdulinkTargetReset(void * dev, int reset); - typedef struct { struct ProgrammerStructBase psb; - int fd; + serial_dev_t serial; } ardulink_ctx_t; int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command) @@ -38,10 +30,10 @@ int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command) buf[4] = (command >> 16) & 0xff; buf[5] = (command >> 24) & 0xff; - if (write(((ardulink_ctx_t*)dev)->fd, buf, 6) == -1) + if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 6) == -1) return -errno; - if (read(((ardulink_ctx_t*)dev)->fd, buf, 1) == -1) + if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 1) == -1) return -errno; return buf[0] == '+' ? 0 : -EPROTO; @@ -53,10 +45,10 @@ int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp) buf[0] = 'r'; buf[1] = reg_7_bit; - if (write(((ardulink_ctx_t*)dev)->fd, buf, 2) == -1) + if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 2) == -1) return -errno; - if (read(((ardulink_ctx_t*)dev)->fd, buf, 4) == -1) + if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 4) == -1) return -errno; *commandresp = (uint32_t)buf[0] | (uint32_t)buf[1] << 8 | \ @@ -74,7 +66,7 @@ int ArdulinkFlushLLCommands(void * dev) int ArdulinkExit(void * dev) { - close(((ardulink_ctx_t*)dev)->fd); + serial_dev_close(&((ardulink_ctx_t*)dev)->serial); free(dev); return 0; } @@ -86,10 +78,10 @@ int ArdulinkTargetReset(void * dev, int reset) { // Assert reset. c = reset ? 'a' : 'A'; - if (write(((ardulink_ctx_t*)dev)->fd, &c, 1) == -1) + if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1) return -errno; - if (read(((ardulink_ctx_t*)dev)->fd, &c, 1) == -1) + if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1) return -errno; if (c != '+') @@ -102,64 +94,39 @@ int ArdulinkTargetReset(void * dev, int reset) { void * TryInit_Ardulink(void) { ardulink_ctx_t *ctx; - struct termios attr; char first; - int argp = TIOCM_DTR; if (!(ctx = calloc(sizeof(ardulink_ctx_t), 1))) { perror("calloc"); return NULL; } - if ((ctx->fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY)) == -1) { - perror("open"); - return NULL; - } - - if (tcgetattr(ctx->fd, &attr) == -1) { - perror("tcgetattr"); + if (serial_dev_create(&ctx->serial, DEFAULT_SERIAL_NAME, 115200) == -1) { + perror("create"); return NULL; } - cfmakeraw(&attr); - cfsetspeed(&attr, 115200); - - if (tcsetattr(ctx->fd, TCSANOW, &attr) == -1) { - perror("tcsetattr"); + if (serial_dev_open(&ctx->serial) == -1) { + perror("open"); return NULL; } // Arduino DTR reset. - if (ioctl(ctx->fd, TIOCMBIC, &argp) == -1) { - perror("ioctl"); - return NULL; - } - - if (tcdrain(ctx->fd) == -1) { - perror("tcdrain"); - return NULL; - } - - if (ioctl(ctx->fd, TIOCMBIS, &argp) == -1) { - perror("ioctl"); - return NULL; - } - - if (tcdrain(ctx->fd) == -1) { - perror("tcdrain"); + if (serial_dev_do_dtr_reset(&ctx->serial) == -1) { + perror("dtr reset"); return NULL; } // Flush anything that might be in the RX buffer, we need the sync char. - if (tcflush(ctx->fd, TCIFLUSH) == -1) { - perror("tcflush"); + if (serial_dev_flush_rx(&ctx->serial) == -1) { + perror("flush rx"); return NULL; } // Let the bootloader do its thing. - sleep(3); + usleep(3UL*1000UL*1000UL); - if (read(ctx->fd, &first, 1) == -1) { + if (serial_dev_read(&ctx->serial, &first, 1) == -1) { perror("read"); return NULL; } diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index 905fd6c035462e549705a43b71b19c62e3ea6a9b..0513750e3eabf1ca302d370c1941fe092d597341 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -12,7 +12,6 @@ #include "../ch32v003fun/ch32v003fun.h" #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) -#define DISABLE_ARDULINK void Sleep(uint32_t dwMilliseconds); #else #include <unistd.h> diff --git a/minichlink/minichlink.exe b/minichlink/minichlink.exe index be95741c4b2eb1f14e5805055bedb7a41cab47d1..f366cfcf2e1fb0fd2451a2c4217a62650e72829a 100644 Binary files a/minichlink/minichlink.exe and b/minichlink/minichlink.exe differ diff --git a/minichlink/serial_dev.c b/minichlink/serial_dev.c new file mode 100644 index 0000000000000000000000000000000000000000..7afe36d394ddbfea36e5f763e523da1a823b17ac --- /dev/null +++ b/minichlink/serial_dev.c @@ -0,0 +1,161 @@ +#include "serial_dev.h" + +int serial_dev_create(serial_dev_t *dev, const char* port, unsigned baud) { + if (!dev) + return -1; + dev->port = port; + dev->baud = baud; + #ifdef IS_WINDOWS + dev->handle = INVALID_HANDLE_VALUE; + #else + dev->fd = -1; + #endif + return 0; +} + +int serial_dev_open(serial_dev_t *dev) { + fprintf(stderr, "Opening serial port %s at %u baud.\n", dev->port, dev->baud); +#ifdef IS_WINDOWS + dev->handle = CreateFileA(dev->port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0); + if (dev->handle == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + fprintf(stderr, "Serial port %s not found.\n", dev->port); + return -1; // Device not found + } + // Error while opening the device + return -2; + } + DCB dcbSerialParams; + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + if (!GetCommState(dev->handle, &dcbSerialParams)) { + return -3; + } + // set baud and 8N1 serial formatting + dcbSerialParams.BaudRate = dev->baud; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.Parity = NOPARITY; + // write back + if (!SetCommState(dev->handle, &dcbSerialParams)){ + return -5; + } + // Set the timeout parameters to "no timeout" (blocking). + // see https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts + COMMTIMEOUTS timeouts; + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutConstant = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = MAXDWORD; + timeouts.WriteTotalTimeoutMultiplier = 0; + // Write the parameters + if (!SetCommTimeouts(dev->handle, &timeouts)) { + return -6; + } +#else + struct termios attr; + if ((dev->fd = open(dev->port, O_RDWR | O_NOCTTY)) == -1) { + perror("open"); + return -1; + } + + if (tcgetattr(ctx->fd, &attr) == -1) { + perror("tcgetattr"); + return -2; + } + + cfmakeraw(&attr); + cfsetspeed(&attr, dev->baud); + + if (tcsetattr(ctx->fd, TCSANOW, &attr) == -1) { + perror("tcsetattr"); + return -3; + } +#endif + // all okay if we get here + return 0; +} + +int serial_dev_write(serial_dev_t *dev, const void* data, size_t len) { +#ifdef IS_WINDOWS + DWORD dwBytesWritten; + if (!WriteFile(dev->handle, data, len, &dwBytesWritten,NULL)) { + return -1; + } + return (int) dwBytesWritten; +#else + return write(dev->fd, data, len); +#endif +} + +int serial_dev_read(serial_dev_t *dev, void* data, size_t len) { +#ifdef IS_WINDOWS + DWORD dwBytesRead = 0; + if (!ReadFile(dev->handle, data, len, &dwBytesRead, NULL)) { + return -1; + } + return (int) dwBytesRead; +#else + return read(dev->fd, data, len); +#endif +} + +int serial_dev_do_dtr_reset(serial_dev_t *dev) { +#ifdef IS_WINDOWS + // EscapeCommFunction returns 0 on fail + if(EscapeCommFunction(dev->handle, SETDTR) == 0) { + return -1; + } + if(EscapeCommFunction(dev->handle, CLRDTR) == 0) { + return -1; + } +#else + int argp = TIOCM_DTR; + // Arduino DTR reset. + if (ioctl(dev->fd, TIOCMBIC, &argp) == -1) { + perror("ioctl"); + return -1; + } + + if (tcdrain(dev->fd) == -1) { + perror("tcdrain"); + return -2; + } + + if (ioctl(dev->fd, TIOCMBIS, &argp) == -1) { + perror("ioctl"); + return -3; + } +#endif + return 0; +} + +int serial_dev_flush_rx(serial_dev_t *dev) { +#ifdef IS_WINDOWS + // PurgeComm returns 0 on fail + if (PurgeComm(dev->handle, PURGE_RXCLEAR) == 0) { + return -1; + } +#else + if (tcflush(dev->fd, TCIFLUSH) == -1) { + perror("tcflush"); + return -1; + } +#endif + return 0; +} + +int serial_dev_close(serial_dev_t *dev) { +#ifdef IS_WINDOWS + if(!CloseHandle(dev->handle)) { + return -1; + } + dev->handle = INVALID_HANDLE_VALUE; +#else + int ret = 0; + if((ret = close(dev->fd)) != 0) { + return ret; + } + dev->fd = -1; +#endif + return 0; +} \ No newline at end of file diff --git a/minichlink/serial_dev.h b/minichlink/serial_dev.h new file mode 100644 index 0000000000000000000000000000000000000000..a75733f41870308408ce0b2f5b82d20ef7ccfe54 --- /dev/null +++ b/minichlink/serial_dev.h @@ -0,0 +1,48 @@ +#ifndef _SERIAL_DEV_H +#define _SERIAL_DEV_H + +#include <stddef.h> + +#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#define IS_WINDOWS +#define DEFAULT_SERIAL_NAME "\\\\.\\COM3" +#else +#include <termios.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#define IS_POSIX +#define DEFAULT_SERIAL_NAME "/dev/ttyACM0" +#endif +/* these are available on all platforms */ +#include <errno.h> +#include <unistd.h> +#include <stdio.h> + +typedef struct { + const char* port; + unsigned baud; +#ifdef IS_WINDOWS + HANDLE handle; +#else + int fd; +#endif +} serial_dev_t; + +/* returns 0 if OK */ +int serial_dev_create(serial_dev_t *dev, const char* port, unsigned baud); +/* returns 0 if OK */ +int serial_dev_open(serial_dev_t *dev); +/* returns -1 on write error */ +int serial_dev_write(serial_dev_t *dev, const void* data, size_t len); +/* returns -1 on read error */ +int serial_dev_read(serial_dev_t *dev, void* data, size_t len); +/* returns -1 on reset error */ +int serial_dev_do_dtr_reset(serial_dev_t *dev); +/* returns -1 on flush error */ +int serial_dev_flush_rx(serial_dev_t *dev); +/* returns -1 on close error */ +int serial_dev_close(serial_dev_t *dev); + +#endif