diff --git a/src/platforms/pc-hosted/Makefile.inc b/src/platforms/pc-hosted/Makefile.inc index 3867e805..9493b0cf 100644 --- a/src/platforms/pc-hosted/Makefile.inc +++ b/src/platforms/pc-hosted/Makefile.inc @@ -3,9 +3,13 @@ SYS = $(shell $(CC) -dumpmachine) CFLAGS += -DPC_HOSTED -DNO_LIBOPENCM3 -DENABLE_DEBUG CFLAGS +=-I ./target -I./platforms/pc ifneq (, $(findstring mingw, $(SYS))) +SRC += serial_win.c LDFLAGS += -lws2_32 else ifneq (, $(findstring cygwin, $(SYS))) +SRC += serial_win.c LDFLAGS += -lws2_32 +else +SRC += serial_unix.c endif VPATH += platforms/pc SRC += cl_utils.c timing.c utils.c diff --git a/src/platforms/pc-hosted/platform.c b/src/platforms/pc-hosted/platform.c index f95859e8..361574d5 100644 --- a/src/platforms/pc-hosted/platform.c +++ b/src/platforms/pc-hosted/platform.c @@ -25,77 +25,16 @@ #include "remote.h" #include -#include #include -#include #include -#include -#include #include -#include #include -#include -#include #include "cl_utils.h" - -/* Allow 100mS for responses to reach us */ -#define RESP_TIMEOUT (100) - -/* Define this to see the transactions across the link */ -//#define DUMP_TRANSACTIONS - -static int f; /* File descriptor for connection to GDB remote */ - -int set_interface_attribs (int fd, int speed, int parity) - -/* A nice routine grabbed from - * https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c - */ - -{ - struct termios tty; - memset (&tty, 0, sizeof tty); - if (tcgetattr (fd, &tty) != 0) - { - fprintf(stderr,"error %d from tcgetattr", errno); - return -1; - } - - cfsetospeed (&tty, speed); - cfsetispeed (&tty, speed); - - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars - // disable IGNBRK for mismatched speed tests; otherwise receive break - // as \000 chars - tty.c_iflag &= ~IGNBRK; // disable break processing - tty.c_lflag = 0; // no signaling chars, no echo, - // no canonical processing - tty.c_oflag = 0; // no remapping, no delays - tty.c_cc[VMIN] = 0; // read doesn't block - tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout - - tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl - - tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, - // enable reading - tty.c_cflag &= ~(PARENB | PARODD); // shut off parity - tty.c_cflag |= parity; - tty.c_cflag &= ~CSTOPB; - tty.c_cflag &= ~CRTSCTS; - - if (tcsetattr (fd, TCSANOW, &tty) != 0) - { - fprintf(stderr,"error %d from tcsetattr", errno); - return -1; - } - return 0; -} - +static BMP_CL_OPTIONS_t cl_opts; /* Portable way to nullify the struct*/ void platform_init(int argc, char **argv) { - BMP_CL_OPTIONS_t cl_opts = {0}; cl_opts.opt_idstring = "Blackmagic Debug Probe Remote"; cl_init(&cl_opts, argc, argv); char construct[PLATFORM_MAX_MSG_SIZE]; @@ -105,18 +44,8 @@ void platform_init(int argc, char **argv) printf("License GPLv3+: GNU GPL version 3 or later " "\n\n"); - f=open(cl_opts.opt_serial,O_RDWR|O_SYNC|O_NOCTTY); - if (f<0) - { - fprintf(stderr,"Couldn't open serial port %s\n", cl_opts.opt_serial); - exit(-1); - } - - if (set_interface_attribs (f, 115000, 0)<0) - { - exit(-1); - } - + if (serial_open(&cl_opts)) + exit(-1); int c=snprintf(construct,PLATFORM_MAX_MSG_SIZE,"%s",REMOTE_START_STR); platform_buffer_write((uint8_t *)construct,c); c=platform_buffer_read((uint8_t *)construct, PLATFORM_MAX_MSG_SIZE); @@ -132,7 +61,7 @@ void platform_init(int argc, char **argv) int ret = cl_execute(&cl_opts); if (cl_opts.opt_tpwr) platform_target_set_power(0); - close(f); + serial_close(); exit(ret); } else { assert(gdb_if_init() == 0); @@ -217,96 +146,6 @@ void platform_buffer_flush(void) } -int platform_buffer_write(const uint8_t *data, int size) -{ - int s; - -#ifdef DUMP_TRANSACTIONS - printf("%s\n",data); -#endif - s=write(f,data,size); - if (s<0) - { - fprintf(stderr,"Failed to write\n"); - exit(-2); - } - - return size; -} - -int platform_buffer_read(uint8_t *data, int maxsize) - -{ - uint8_t *c; - int s; - int ret; - uint32_t endTime; - fd_set rset; - struct timeval tv; - - c=data; - tv.tv_sec=0; - - endTime=platform_time_ms()+RESP_TIMEOUT; - tv.tv_usec=1000*(endTime-platform_time_ms()); - - /* Look for start of response */ - do - { - FD_ZERO(&rset); - FD_SET(f, &rset); - - ret = select(f + 1, &rset, NULL, NULL, &tv); - if (ret < 0) - { - fprintf(stderr,"Failed on select\n"); - exit(-4); - } - if(ret == 0) - { - fprintf(stderr,"Timeout on read\n"); - exit(-3); - } - - s=read(f,c,1); - } - while ((s>0) && (*c!=REMOTE_RESP)); - - /* Now collect the response */ - do - { - FD_ZERO(&rset); - FD_SET(f, &rset); - ret = select(f + 1, &rset, NULL, NULL, &tv); - if (ret < 0) - { - fprintf(stderr,"Failed on select\n"); - exit(-4); - } - if(ret == 0) - { - fprintf(stderr,"Timeout on read\n"); - exit(-3); - } - s=read(f,c,1); - if (*c==REMOTE_EOM) - { - *c=0; -#ifdef DUMP_TRANSACTIONS - printf(" %s\n",data); -#endif - return (c-data); - } - else - c++; - } - while ((s>=0) && (c-dataopt_target_dev = 1; opt->opt_flash_start = 0x08000000; opt->opt_flash_size = 16 * 1024 *1024; - while((c = getopt(argc, argv, "Ehv::s:c:CnN:tVta:S:jprR")) != -1) { + while((c = getopt(argc, argv, "Ehv::d:s:c:CnN:tVta:S:jprR")) != -1) { switch(c) { case 'c': if (optarg) @@ -156,7 +160,9 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) break; case 'v': if (optarg) - opt->opt_debuglevel = strtol(optarg, NULL, 0); + cl_debuglevel = strtol(optarg, NULL, 0); + else + cl_debuglevel = -1; break; case 'j': opt->opt_usejtag = true; @@ -167,6 +173,10 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) case 'n': opt->opt_no_wait = true; break; + case 'd': + if (optarg) + opt->opt_device = optarg; + break; case 's': if (optarg) opt->opt_serial = optarg; @@ -277,7 +287,6 @@ int cl_execute(BMP_CL_OPTIONS_t *opt) goto target_detach; } int read_file = -1; - struct mmap_data map = {0}; if ((opt->opt_mode == BMP_MODE_FLASH_WRITE) || (opt->opt_mode == BMP_MODE_FLASH_VERIFY)) { int mmap_res = bmp_mmap(opt->opt_flash_file, &map); diff --git a/src/platforms/pc/cl_utils.h b/src/platforms/pc/cl_utils.h index 850112b5..6e89656d 100644 --- a/src/platforms/pc/cl_utils.h +++ b/src/platforms/pc/cl_utils.h @@ -41,6 +41,7 @@ typedef struct BMP_CL_OPTIONS_s { bool opt_tpwr; bool opt_connect_under_reset; char *opt_flash_file; + char *opt_device; char *opt_serial; char *opt_cable; int opt_debuglevel; @@ -52,4 +53,6 @@ typedef struct BMP_CL_OPTIONS_s { void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv); int cl_execute(BMP_CL_OPTIONS_t *opt); +int serial_open(BMP_CL_OPTIONS_t *opt); +void serial_close(void); #endif diff --git a/src/platforms/pc/serial_unix.c b/src/platforms/pc/serial_unix.c new file mode 100644 index 00000000..c4688b08 --- /dev/null +++ b/src/platforms/pc/serial_unix.c @@ -0,0 +1,215 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2019 Dave Marples + * with additions from Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "general.h" +#include "remote.h" +#include "cl_utils.h" + +static int fd; /* File descriptor for connection to GDB remote */ +extern int cl_debuglevel; +/* A nice routine grabbed from + * https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c + */ +static int set_interface_attribs(void) +{ + struct termios tty; + memset (&tty, 0, sizeof tty); + if (tcgetattr (fd, &tty) != 0) { + fprintf(stderr,"error %d from tcgetattr", errno); + return -1; + } + + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars + // disable IGNBRK for mismatched speed tests; otherwise receive break + // as \000 chars + tty.c_iflag &= ~IGNBRK; // disable break processing + tty.c_lflag = 0; // no signaling chars, no echo, + // no canonical processing + tty.c_oflag = 0; // no remapping, no delays + tty.c_cc[VMIN] = 0; // read doesn't block + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl + + tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, + // enable reading + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CRTSCTS; + + if (tcsetattr (fd, TCSANOW, &tty) != 0) { + fprintf(stderr,"error %d from tcsetattr", errno); + return -1; + } + return 0; +} +#define BMP_IDSTRING "usb-Black_Sphere_Technologies_Black_Magic_Probe" +#define DEVICE_BY_ID "/dev/serial/by-id/" +int serial_open(BMP_CL_OPTIONS_t *opt) +{ + char name[256]; + if (!opt->opt_device) { + /* Try to find some BMP if0*/ + struct dirent *dp; + DIR *dir = opendir(DEVICE_BY_ID); + if (!dir) { + fprintf(stderr, "No serial device found\n"); + return -1; + } + int num_devices = 0; + int num_total = 0; + while ((dp = readdir(dir)) != NULL) { + if ((strstr(dp->d_name, BMP_IDSTRING)) && + (strstr(dp->d_name, "-if00"))) { + num_total++; + if (((opt->opt_serial) && + (!strstr(dp->d_name, opt->opt_serial)))) + continue; + num_devices++; + strcpy(name, DEVICE_BY_ID); + strncat(name, dp->d_name, sizeof(name) - strlen(name) - 1); + } + } + closedir(dir); + if ((num_devices == 0) && (num_total == 0)){ + fprintf(stderr, "No BMP probe found\n"); + return -1; + } else if (num_devices != 1) { + fprintf(stderr, "Available Probes:\n"); + dir = opendir(DEVICE_BY_ID); + if (dir) { + while ((dp = readdir(dir)) != NULL) { + if ((strstr(dp->d_name, BMP_IDSTRING)) && + (strstr(dp->d_name, "-if00"))) + fprintf(stderr, "%s\n", dp->d_name); + } + closedir(dir); + if (opt->opt_serial) + fprintf(stderr, "Do no match given serial \"%s\"\n", opt->opt_serial); + else + fprintf(stderr, "Select Probe with -s <(Partial) Serial Number\n"); + } else { + fprintf(stderr, "Could not opendir %s: %s\n", name, strerror(errno)); + } + return -1; + } + } else { + strncpy(name, opt->opt_device, sizeof(name) - 1); + } + fd = open(name, O_RDWR | O_SYNC | O_NOCTTY); + if (fd < 0) { + fprintf(stderr,"Couldn't open serial port %s\n", name); + return -1; + } + /* BMP only offers an USB-Serial connection with no real serial + * line in between. No need for baudrate or parity.! + */ + return set_interface_attribs(); +} + +void serial_close(void) +{ + close(fd); +} + +int platform_buffer_write(const uint8_t *data, int size) +{ + int s; + + if (cl_debuglevel) + printf("%s\n",data); + s = write(fd, data, size); + if (s < 0) { + fprintf(stderr, "Failed to write\n"); + exit(-2); + } + + return size; +} + +int platform_buffer_read(uint8_t *data, int maxsize) +{ + uint8_t *c; + int s; + int ret; + uint32_t endTime; + fd_set rset; + struct timeval tv; + + c = data; + tv.tv_sec = 0; + + endTime = platform_time_ms() + RESP_TIMEOUT; + tv.tv_usec = 1000 * (endTime - platform_time_ms()); + + /* Look for start of response */ + do { + FD_ZERO(&rset); + FD_SET(fd, &rset); + + ret = select(fd + 1, &rset, NULL, NULL, &tv); + if (ret < 0) { + fprintf(stderr,"Failed on select\n"); + exit(-4); + } + if(ret == 0) { + fprintf(stderr,"Timeout on read RESP\n"); + exit(-3); + } + + s = read(fd, c, 1); + } + while ((s > 0) && (*c != REMOTE_RESP)); + /* Now collect the response */ + do { + FD_ZERO(&rset); + FD_SET(fd, &rset); + ret = select(fd + 1, &rset, NULL, NULL, &tv); + if (ret < 0) { + fprintf(stderr,"Failed on select\n"); + exit(-4); + } + if(ret == 0) { + fprintf(stderr,"Timeout on read\n"); + exit(-3); + } + s = read(fd, c, 1); + if (*c==REMOTE_EOM) { + *c = 0; + if (cl_debuglevel) + printf(" %s\n",data); + return (c - data); + } else { + c++; + } + }while ((s >= 0) && ((c - data) < maxsize)); + + fprintf(stderr,"Failed to read\n"); + exit(-3); + return 0; +} + diff --git a/src/platforms/pc/serial_win.c b/src/platforms/pc/serial_win.c new file mode 100644 index 00000000..a39d1a1a --- /dev/null +++ b/src/platforms/pc/serial_win.c @@ -0,0 +1,139 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2020 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "general.h" +#include +#include "remote.h" +#include "cl_utils.h" + +HANDLE hComm; +extern int cl_debuglevel; + +int serial_open(BMP_CL_OPTIONS_t *opt) +{ + if (!opt->opt_device) { + fprintf(stderr,"Specify the serial device to use!\n"); + return -1; + } + char device[256]; + if (strstr(opt->opt_device, "\\\\.\\")) { + strncpy(device, opt->opt_device, sizeof(device) - 1); + } else { + strcpy(device, "\\\\.\\"); + strncat(device, opt->opt_device, sizeof(device) - strlen(device) - 1); + } + hComm = CreateFile(device, //port name + GENERIC_READ | GENERIC_WRITE, //Read/Write + 0, // No Sharing + NULL, // No Security + OPEN_EXISTING,// Open existing port only + 0, // Non Overlapped I/O + NULL); // Null for Comm Devices} + if (hComm == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Could not open %s: %ld\n", device, + GetLastError()); + return -1; + } + DCB dcbSerialParams; + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + if (!GetCommState(hComm, &dcbSerialParams)) { + fprintf(stderr, "GetCommState failed %ld\n", GetLastError()); + return -1; + } + dcbSerialParams.ByteSize = 8; + if (!SetCommState(hComm, &dcbSerialParams)) { + fprintf(stderr, "SetCommState failed %ld\n", GetLastError()); + return -1; + } + COMMTIMEOUTS timeouts = {0}; + timeouts.ReadIntervalTimeout = 10; + timeouts.ReadTotalTimeoutConstant = 10; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.WriteTotalTimeoutConstant = 10; + timeouts.WriteTotalTimeoutMultiplier = 10; + if (!SetCommTimeouts(hComm, &timeouts)) { + fprintf(stderr, "SetCommTimeouts failed %ld\n", GetLastError()); + return -1; + } + return 0; +} + +void serial_close(void) +{ + CloseHandle(hComm); +} + +int platform_buffer_write(const uint8_t *data, int size) +{ + if (cl_debuglevel) + printf("%s\n",data); + int s = 0; + + do { + DWORD written; + if (!WriteFile(hComm, data + s, size - s, &written, NULL)) { + fprintf(stderr, "Serial write failed %ld, written %d\n", + GetLastError(), s); + return -1; + } + s += written; + } while (s < size); + return 0; +} +int platform_buffer_read(uint8_t *data, int maxsize) +{ + DWORD s; + uint8_t response = 0; + uint32_t startTime = platform_time_ms(); + uint32_t endTime = platform_time_ms() + RESP_TIMEOUT; + do { + if (!ReadFile(hComm, &response, 1, &s, NULL)) { + fprintf(stderr,"ERROR on read RESP\n"); + exit(-3); + } + if (platform_time_ms() > endTime) { + fprintf(stderr,"Timeout on read RESP\n"); + exit(-4); + } + } while (response != REMOTE_RESP); + uint8_t *c = data; + do { + if (!ReadFile(hComm, c, 1, &s, NULL)) { + fprintf(stderr,"Error on read\n"); + exit(-3); + } + if (s > 0 ) { + if (cl_debuglevel) + printf("%c", *c); + if (*c == REMOTE_EOM) { + *c = 0; + if (cl_debuglevel) + printf("\n"); + return (c - data); + } else { + c++; + } + } + } while (((c - data) < maxsize) && (platform_time_ms() < endTime)); + fprintf(stderr,"Failed to read EOM at %d\n", + platform_time_ms() - startTime); + exit(-3); + return 0; +}