Skip to content

Commit

Permalink
hosted: support network connections on windows
Browse files Browse the repository at this point in the history
This adds Windows support for remote BMDA targets.

Signed-off-by: Sean Cross <[email protected]>
  • Loading branch information
xobs committed Sep 27, 2024
1 parent 5db787b commit cebab7f
Showing 1 changed file with 147 additions and 5 deletions.
152 changes: 147 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,91 @@ 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) {
if ((network_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == 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 +298,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 +355,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 +372,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

0 comments on commit cebab7f

Please sign in to comment.