pc-hosted: Put serial handling in own files for unix and implement for windows.

- If host provided /dev/serial/by-id, device must not be given and
  multiple devices may be selected with -s <(partial) serial number>
- On windows, device must by specifies with -d
This commit is contained in:
Uwe Bonnes 2020-03-28 17:27:21 +01:00 committed by UweBonnes
parent 18ae2bb50d
commit b4ab9f328e
7 changed files with 380 additions and 169 deletions

View File

@ -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

View File

@ -25,77 +25,16 @@
#include "remote.h"
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#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 "
"<http://gnu.org/licenses/gpl.html>\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-data<maxsize));
fprintf(stderr,"Failed to read\n");
exit(-3);
return 0;
}
const char *platform_target_voltage(void)
{

View File

@ -41,10 +41,12 @@
#define SET_IDLE_STATE(state)
#define SET_ERROR_STATE(state)
/* Allow 100mS for responses to reach us */
#define RESP_TIMEOUT (100)
void platform_buffer_flush(void);
int platform_buffer_write(const uint8_t *data, int size);
int platform_buffer_read(uint8_t *data, int size);
static inline int platform_hwversion(void)
{
return 0;

View File

@ -53,6 +53,9 @@ struct mmap_data {
int fd;
#endif
};
int cl_debuglevel;
static struct mmap_data map; /* Portable way way to nullify the struct!*/
static int bmp_mmap(char *file, struct mmap_data *map)
{
@ -116,6 +119,7 @@ static void cl_help(char **argv, BMP_CL_OPTIONS_t *opt)
printf("Usage: %s [options]\n", argv[0]);
printf("\t-h\t\t: This help.\n");
printf("\t-v[1|2]\t\t: Increasing verbosity\n");
printf("\t-d \"path\"\t: Use serial device at \"path\"\n");
printf("\t-s \"string\"\t: Use dongle with (partial) "
"serial number \"string\"\n");
printf("\t-c \"string\"\t: Use ftdi dongle with type \"string\"\n");
@ -145,7 +149,7 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv)
opt->opt_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);

View File

@ -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

View File

@ -0,0 +1,215 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2019 Dave Marples <dave@marples.net>
* 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 <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include "general.h"
#include <windows.h>
#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;
}