Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support connecting to BMP devices via TCP #1941

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/platforms/hosted/serial_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
#include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "general.h"
#include "remote.h"
#include "bmp_hosted.h"
Expand All @@ -43,6 +47,73 @@ static uint8_t read_buffer[READ_BUFFER_LENGTH];
static size_t read_buffer_fullness = 0U;
static size_t read_buffer_offset = 0U;

/* Socket code taken from https://beej.us/guide/bgnet/ */
static bool try_opening_network_device(const char *const name)
{
// Maximum legal length of a hostname
char hostname[256];

// The service name or port number
char *service_name;

struct addrinfo addr_hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED,
};
struct addrinfo *server_info;

if (!name) {
return false;
}

// Copy the hostname to an internal array. We need to modify it
// to separate the hostname from the service name.
strncpy(hostname, name, sizeof(hostname) - 1U);

service_name = strstr(hostname, ":");
if (service_name == NULL) {
return false;
}

// Separate the service name / port number from the hostname
*service_name = '\0';
service_name += 1;

if (*service_name == '\0') {
return false;
}

if (getaddrinfo(hostname, service_name, &addr_hints, &server_info) != 0) {
return false;
}

// Loop through all the results and connect to the first we can.
struct addrinfo *p;
for (p = server_info; p != NULL; p = p->ai_next) {
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (fd == -1) {
continue;
}

if (connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
close(fd);
continue;
}

// If we get here, we must have connected successfully
break;
}
freeaddrinfo(server_info);

if (p == NULL) {
return false;
}

return true;
}

/* A nice routine grabbed from
* https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c
*/
Expand Down Expand Up @@ -97,6 +168,9 @@ bool serial_open(const bmda_cli_options_s *cl_opts, const char *serial)
}
fd = open(name, O_RDWR | O_SYNC | O_NOCTTY);
if (fd < 0) {
if (try_opening_network_device(name)) {
return true;
}
DEBUG_ERROR("Couldn't open serial port %s\n", name);
return false;
}
Expand Down Expand Up @@ -202,6 +276,9 @@ bool serial_open(const bmda_cli_options_s *const cl_opts, const char *const seri
read_buffer_offset = 0U;
fd = open(name, O_RDWR | O_SYNC | O_NOCTTY);
if (fd < 0) {
if (try_opening_network_device(name)) {
return true;
}
DEBUG_ERROR("Couldn't open serial port %s\n", name);
return false;
}
Expand Down
153 changes: 148 additions & 5 deletions src/platforms/hosted/serial_win.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <winsock2.h>
#include <ws2tcpip.h>
#include "general.h"
#include <windows.h>
#include "platform.h"
Expand Down Expand Up @@ -55,11 +57,92 @@ static char *format_string(const char *format, ...)

/* Windows handle for the connection to the remote BMP */
static HANDLE port_handle = INVALID_HANDLE_VALUE;
static SOCKET network_socket = INVALID_SOCKET;
/* Buffer for read request data + fullness and next read position values */
static uint8_t read_buffer[READ_BUFFER_LENGTH];
static size_t read_buffer_fullness = 0U;
static size_t read_buffer_offset = 0U;

/* Socket code taken from https://beej.us/guide/bgnet/ */
static bool try_opening_network_device(const char *const name)
{
DWORD last_error = GetLastError();
// Maximum legal length of a hostname
char hostname[256];

// The service name or port number
char *service_name;

struct addrinfo addr_hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED,
};
struct addrinfo *server_info;

if (!name) {
return false;
}

// Copy the hostname to an internal array. We need to modify it
// to separate the hostname from the service name.
strncpy(hostname, name, sizeof(hostname) - 1U);

service_name = strstr(hostname, ":");
if (service_name == NULL) {
return false;
}

// Separate the service name / port number from the hostname
*service_name = '\0';
service_name += 1;

// The service name is commonly empty on Windows where port names
// tend to be things like "COM4:" which can be mistaken for a hostname.
if (*service_name == '\0') {
return false;
}

WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
SetLastError(last_error);
return false;
}

if (getaddrinfo(hostname, service_name, &addr_hints, &server_info) != 0) {
SetLastError(last_error);
return false;
}

// Loop through all the results and connect to the first we can.
struct addrinfo *p;
for (p = server_info; p != NULL; p = p->ai_next) {
network_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (network_socket == INVALID_SOCKET) {
continue;
}

if (connect(network_socket, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) {
closesocket(network_socket);
continue;
}

// If we get here, we must have connected successfully
break;
}
freeaddrinfo(server_info);

if (p == NULL) {
network_socket = INVALID_SOCKET;
SetLastError(last_error);
return false;
}

return true;
}

static void display_error(const LSTATUS error, const char *const operation, const char *const path)
{
char *message = NULL;
Expand Down Expand Up @@ -216,6 +299,9 @@ bool serial_open(const bmda_cli_options_s *const cl_opts, const char *const seri

/* If opening the device node failed for any reason, error out early */
if (port_handle == INVALID_HANDLE_VALUE) {
if (try_opening_network_device(cl_opts->opt_device)) {
return true;
}
handle_dev_error(port_handle, "opening device");
return false;
}
Expand Down Expand Up @@ -270,8 +356,15 @@ bool serial_open(const bmda_cli_options_s *const cl_opts, const char *const seri

void serial_close(void)
{
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
if (port_handle != INVALID_HANDLE_VALUE) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
WSACleanup();
}
if (network_socket != INVALID_SOCKET) {
closesocket(network_socket);
network_socket = INVALID_SOCKET;
}
}

bool platform_buffer_write(const void *const data, const size_t length)
Expand All @@ -280,16 +373,66 @@ bool platform_buffer_write(const void *const data, const size_t length)
DEBUG_WIRE("%s\n", buffer);
DWORD written = 0;
for (size_t offset = 0; offset < length; offset += written) {
if (!WriteFile(port_handle, buffer + offset, length - offset, &written, NULL)) {
DEBUG_ERROR("Serial write failed %lu, written %zu\n", GetLastError(), offset);
return false;
if (port_handle != INVALID_HANDLE_VALUE) {
if (!WriteFile(port_handle, buffer + offset, length - offset, &written, NULL)) {
DEBUG_ERROR("Serial write failed %lu, written %zu\n", GetLastError(), offset);
return false;
}
} else if (network_socket != INVALID_SOCKET) {
int network_written = send(network_socket, buffer + offset, length - offset, 0);
if (network_written == SOCKET_ERROR) {
DEBUG_ERROR("Network write failed %d, written %zu\n", WSAGetLastError(), offset);
return false;
}
written = network_written;
}
}
return true;
}

static ssize_t bmda_read_more_socket_data()
{
struct timeval timeout = {
.tv_sec = cortexm_wait_timeout / 1000U,
.tv_usec = 1000U * (cortexm_wait_timeout % 1000U),
};

fd_set select_set;
FD_ZERO(&select_set);
FD_SET(network_socket, &select_set);

/* Set up to wait for more data from the probe */
const int result = select(FD_SETSIZE, &select_set, NULL, NULL, &timeout);
/* If select() fails, bail */
if (result < 0) {
DEBUG_ERROR("Failed on select\n");
return -3;
}
/* If we timed out, bail differently */
if (result == 0) {
DEBUG_ERROR("Timeout while waiting for BMP response\n");
return -4;
}
/* Now we know there's data, try to fill the read buffer */
const ssize_t bytes_received = recv(network_socket, (char *)read_buffer, READ_BUFFER_LENGTH, 0);
/* If that failed, bail */
if (bytes_received < 0) {
const int error = errno;
DEBUG_ERROR("Failed to read response (%d): %s\n", error, strerror(error));
return -6;
}
/* We now have more data, so update the read buffer counters */
read_buffer_fullness = (size_t)bytes_received;
read_buffer_offset = 0U;
return 0;
}

static ssize_t bmda_read_more_data(const uint32_t end_time)
{
if (network_socket != INVALID_SOCKET) {
return bmda_read_more_socket_data();
}

// Try to wait for up to 100ms for data to become available
if (WaitForSingleObject(port_handle, 100) != WAIT_OBJECT_0) {
DEBUG_ERROR("Timeout while waiting for BMP response: %lu\n", GetLastError());
Expand Down
Loading