#include "general.h"
#include <windows.h>
#include "remote.h"
#include "cli.h"
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#define NT_DEV_SUFFIX "\\\\.\\"
#define NT_DEV_SUFFIX_LEN ARRAY_LENGTH(NT_DEV_SUFFIX)
static char *format_string(const char *format, ...) __attribute__((format(printf, 1, 2)));
static char *format_string(const char *format, ...)
{
va_list args;
va_start(args, format);
const int len = vsnprintf(NULL, 0, format, args);
va_end(args);
if (len <= 0)
return NULL;
char *const ret = (char *)malloc(len + 1);
if (!ret)
return NULL;
va_start(args, format);
vsprintf(ret, format, args);
va_end(args);
return ret;
}
static HANDLE port_handle = INVALID_HANDLE_VALUE;
static void display_error(const LSTATUS error, const char *const operation, const char *const path)
{
char *message = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&message, 0, NULL);
DEBUG_ERROR("Error %s %s, got error %08lx: %s\n", operation, path, error, message);
LocalFree(message);
}
static void handle_dev_error(HANDLE device, const char *const operation)
{
const DWORD error = GetLastError();
char *message = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&message, 0, NULL);
DEBUG_ERROR("Error %s (%08lx): %s\n", operation, error, message);
LocalFree(message);
if (device != INVALID_HANDLE_VALUE)
CloseHandle(device);
}
static HKEY open_hklm_registry_path(const char *const path, const REGSAM permissions)
{
HKEY handle = INVALID_HANDLE_VALUE;
const LSTATUS result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, permissions, &handle);
if (result != ERROR_SUCCESS) {
display_error(result, "opening registry key", path);
return INVALID_HANDLE_VALUE;
}
return handle;
}
static char *read_key_from_path(const char *const subpath, const char *const key_name)
{
char *key_path = format_string(
"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04X&PID_%04X%s", VENDOR_ID_BMP, PRODUCT_ID_BMP, subpath);
if (!key_path)
return NULL;
HKEY key_path_handle = open_hklm_registry_path(key_path, KEY_READ);
free(key_path);
if (key_path_handle == INVALID_HANDLE_VALUE)
return NULL;
DWORD value_len = 0;
const LSTATUS result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, NULL, &value_len);
if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA) {
display_error(result, "retrieving value for key", key_name);
RegCloseKey(key_path_handle);
return NULL;
}
char *value = calloc(1, value_len);
if (!value) {
DEBUG_ERROR("Could not allocate sufficient memory for key value\n");
RegCloseKey(key_path_handle);
return NULL;
}
assert(RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, value, &value_len) == ERROR_SUCCESS);
RegCloseKey(key_path_handle);
return value;
}
static char *find_bmp_by_serial(const char *serial)
{
char *serial_path = format_string("\\%s", serial);
if (!serial_path)
return NULL;
char *prefix = read_key_from_path(serial_path, "ParentIdPrefix");
free(serial_path);
if (!prefix)
return NULL;
DEBUG_INFO("prefix: %s\n", prefix);
char *parameter_path = format_string("&MI_00\\%s&0000\\Device Parameters", prefix);
if (!parameter_path) {
free(prefix);
return NULL;
}
char *port_name = read_key_from_path(parameter_path, "PortName");
free(prefix);
if (!port_name)
return NULL;
DEBUG_WARN("Using BMP at %s\n", port_name);
return port_name;
}
static char *device_to_path(const char *const device)
{
if (memcmp(device, NT_DEV_SUFFIX, NT_DEV_SUFFIX_LEN - 1U) == 0)
return strdup(device);
const size_t path_len = strlen(device) + NT_DEV_SUFFIX_LEN;
char *const path = malloc(path_len);
memcpy(path, NT_DEV_SUFFIX, NT_DEV_SUFFIX_LEN);
memcpy(path + NT_DEV_SUFFIX_LEN - 1U, device, path_len - NT_DEV_SUFFIX_LEN);
path[path_len - 1U] = '\0';
return path;
}
static char *find_bmp_device(const bmda_cli_options_s *const cl_opts, const char *const serial)
{
if (cl_opts->opt_device)
return device_to_path(cl_opts->opt_device);
char *const device = find_bmp_by_serial(serial);
if (!device)
return NULL;
char *const result = device_to_path(device);
free(device);
return result;
}
bool serial_open(const bmda_cli_options_s *const cl_opts, const char *const serial)
{
char *const device = find_bmp_device(cl_opts, serial);
if (!device) {
DEBUG_ERROR("Unexpected problems finding the device!\n");
return false;
}
port_handle = CreateFile(device,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
NULL);
free(device);
if (port_handle == INVALID_HANDLE_VALUE) {
handle_dev_error(port_handle, "opening device");
return false;
}
DCB serial_params = {0};
serial_params.DCBlength = sizeof(serial_params);
if (!GetCommState(port_handle, &serial_params)) {
handle_dev_error(port_handle, "getting communication state from device");
return false;
}
serial_params.fParity = FALSE;
serial_params.fOutxCtsFlow = FALSE;
serial_params.fOutxDsrFlow = FALSE;
serial_params.fDtrControl = DTR_CONTROL_ENABLE;
serial_params.fDsrSensitivity = FALSE;
serial_params.fOutX = FALSE;
serial_params.fInX = FALSE;
serial_params.fRtsControl = RTS_CONTROL_DISABLE;
serial_params.ByteSize = 8;
serial_params.Parity = NOPARITY;
if (!SetCommState(port_handle, &serial_params)) {
handle_dev_error(port_handle, "setting communication state on device");
return false;
}
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 10;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 10;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(port_handle, &timeouts)) {
handle_dev_error(port_handle, "setting communication timeouts for device");
return false;
}
return true;
}
void serial_close(void)
{
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
}
bool platform_buffer_write(const void *const data, const size_t length)
{
const char *const buffer = (const char *)data;
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;
}
offset += written;
}
return true;
}
int platform_buffer_read(void *const data, const size_t length)
{
DWORD read = 0;
char response = 0;
const uint32_t start_time = platform_time_ms();
const uint32_t end_time = start_time + cortexm_wait_timeout;
while (response != REMOTE_RESP) {
if (!ReadFile(port_handle, &response, 1, &read, NULL)) {
DEBUG_ERROR("error occurred while reading response: %lu\n", GetLastError());
exit(-3);
}
if (platform_time_ms() > end_time) {
DEBUG_ERROR("Timeout while waiting for BMP response\n");
exit(-4);
}
}
char *const buffer = (char *)data;
for (size_t offset = 0; offset < length && platform_time_ms() < end_time;) {
if (!ReadFile(port_handle, buffer + offset, 1, &read, NULL)) {
DEBUG_ERROR("Error on read\n");
exit(-3);
}
if (read > 0) {
DEBUG_WIRE("%c", buffer[offset]);
if (buffer[offset] == REMOTE_EOM) {
buffer[offset] = 0;
DEBUG_WIRE("\n");
return offset;
}
++offset;
}
}
DEBUG_ERROR("Failed to read response after %ums\n", platform_time_ms() - start_time);
exit(-3);
return 0;
}