#include <sys/stat.h>
#include <sys/select.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include "general.h"
#include "remote.h"
#include "bmp_hosted.h"
#include "utils.h"
#include "cortexm.h"
static int fd;
static bool set_interface_attribs(void)
{
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
DEBUG_ERROR("error %d from tcgetattr", errno);
return false;
}
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; tty.c_iflag &= ~IGNBRK; tty.c_lflag = 0; tty.c_oflag = 0; tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 5;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD); tty.c_cflag &= ~CSTOPB;
#if defined(CRTSCTS)
tty.c_cflag &= ~CRTSCTS;
#endif
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
DEBUG_ERROR("error %d from tcsetattr", errno);
return false;
}
return true;
}
#ifdef __APPLE__
bool serial_open(const bmda_cli_options_s *cl_opts, const char *serial)
{
char name[4096];
if (!cl_opts->opt_device) {
if (!serial) {
DEBUG_WARN("No serial device found\n");
return false;
} else {
sprintf(name, "/dev/cu.usbmodem%s1", serial);
}
} else {
strncpy(name, cl_opts->opt_device, sizeof(name) - 1U);
}
fd = open(name, O_RDWR | O_SYNC | O_NOCTTY);
if (fd < 0) {
DEBUG_ERROR("Couldn't open serial port %s\n", name);
return false;
}
return set_interface_attribs();
}
#else
#define BMP_IDSTRING_BLACKSPHERE "usb-Black_Sphere_Technologies_Black_Magic_Probe"
#define BMP_IDSTRING_BLACKMAGIC "usb-Black_Magic_Debug_Black_Magic_Probe"
#define BMP_IDSTRING_1BITSQUARED "usb-1BitSquared_Black_Magic_Probe"
#define DEVICE_BY_ID "/dev/serial/by-id/"
typedef struct dirent dirent_s;
bool device_is_bmp_gdb_port(const char *const device)
{
const size_t length = strlen(device);
if (begins_with(device, length, BMP_IDSTRING_BLACKSPHERE) || begins_with(device, length, BMP_IDSTRING_BLACKMAGIC) ||
begins_with(device, length, BMP_IDSTRING_1BITSQUARED)) {
return ends_with(device, length, "-if00");
}
return false;
}
static bool match_serial(const char *const device, const char *const serial)
{
const char *const last_underscore = strrchr(device, '_');
if (!last_underscore)
return false;
const char *const begin = last_underscore + 1;
const char *const end = device + strlen(device) - 5U;
return contains_substring(begin, end - begin, serial);
}
bool serial_open(const bmda_cli_options_s *const cl_opts, const char *const serial)
{
char name[4096];
if (!cl_opts->opt_device) {
DIR *dir = opendir(DEVICE_BY_ID);
if (!dir) {
DEBUG_WARN("No serial devices found\n");
return false;
}
size_t matches = 0;
size_t total = 0;
while (true) {
const dirent_s *const entry = readdir(dir);
if (entry == NULL)
break;
if (device_is_bmp_gdb_port(entry->d_name)) {
++total;
if (serial && !match_serial(entry->d_name, serial))
continue;
++matches;
const size_t path_len = sizeof(DEVICE_BY_ID) - 1U;
memcpy(name, DEVICE_BY_ID, path_len);
const size_t name_len = strlen(entry->d_name);
const size_t truncated_len = MIN(name_len, sizeof(name) - path_len - 2U);
memcpy(name + path_len, entry->d_name, truncated_len);
name[path_len + truncated_len] = '\0';
}
}
closedir(dir);
if (total == 0) {
DEBUG_ERROR("No Black Magic Probes found\n");
return false;
}
if (matches != 1) {
DEBUG_INFO("Available Probes:\n");
dir = opendir(DEVICE_BY_ID);
if (dir) {
while (true) {
const dirent_s *const entry = readdir(dir);
if (entry == NULL)
break;
if (device_is_bmp_gdb_port(entry->d_name))
DEBUG_WARN("%s\n", entry->d_name);
}
closedir(dir);
if (serial)
DEBUG_ERROR("No match for (partial) serial number \"%s\"\n", serial);
else
DEBUG_WARN("Select probe with `-s <(Partial) Serial Number>`\n");
} else
DEBUG_ERROR("Could not scan %s: %s\n", name, strerror(errno));
return false;
}
} else {
const size_t path_len = strlen(cl_opts->opt_device);
const size_t truncated_len = MIN(path_len, sizeof(name) - 1U);
memcpy(name, cl_opts->opt_device, truncated_len);
name[truncated_len] = '\0';
}
fd = open(name, O_RDWR | O_SYNC | O_NOCTTY);
if (fd < 0) {
DEBUG_ERROR("Couldn't open serial port %s\n", name);
return false;
}
return set_interface_attribs();
}
#endif
void serial_close(void)
{
close(fd);
}
bool platform_buffer_write(const void *const data, const size_t length)
{
DEBUG_WIRE("%s\n", (const char *)data);
const ssize_t written = write(fd, data, length);
if (written < 0) {
const int error = errno;
DEBUG_ERROR("Failed to write (%d): %s\n", errno, strerror(error));
exit(-2);
}
return (size_t)written == length;
}
int platform_buffer_read(void *const data, size_t length)
{
char response = 0;
timeval_s timeout = {
.tv_sec = cortexm_wait_timeout / 1000U,
.tv_usec = 1000U * (cortexm_wait_timeout % 1000U),
};
while (response != REMOTE_RESP) {
fd_set select_set;
FD_ZERO(&select_set);
FD_SET(fd, &select_set);
const int result = select(FD_SETSIZE, &select_set, NULL, NULL, &timeout);
if (result < 0) {
DEBUG_ERROR("Failed on select\n");
return -3;
}
if (result == 0) {
DEBUG_ERROR("Timeout while waiting for BMP response\n");
return -4;
}
if (read(fd, &response, 1) != 1) {
const int error = errno;
DEBUG_ERROR("Failed to read response (%d): %s\n", error, strerror(error));
return -6;
}
}
for (size_t offset = 0; offset < length;) {
fd_set select_set;
FD_ZERO(&select_set);
FD_SET(fd, &select_set);
const int result = select(FD_SETSIZE, &select_set, NULL, NULL, &timeout);
if (result < 0) {
DEBUG_ERROR("Failed on select\n");
exit(-4);
}
if (result == 0) {
DEBUG_ERROR("Timeout on read\n");
return -5;
}
if (read(fd, data + offset, 1) != 1) {
const int error = errno;
DEBUG_ERROR("Failed to read response (%d): %s\n", error, strerror(error));
return -6;
}
char *const buffer = (char *)data;
if (buffer[offset] == REMOTE_EOM) {
buffer[offset] = 0;
DEBUG_WIRE(" %s\n", buffer);
return offset;
}
++offset;
}
DEBUG_ERROR("Failed to read\n");
return -6;
}