794ee9989a
Add cel_serial_platform_open/close/read/write/flush for Windows using CreateFileA, DCB for baud/8N1, and SetCommTimeouts for non-blocking reads. serial.c now delegates all operations to the platform backend via a cel_serial_platform_handle, and test_serial.c mocks that backend with CMock.
100 lines
3.3 KiB
C
100 lines
3.3 KiB
C
#include "celrs/platform/serial_internal.h"
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
cel_serial_platform_handle cel_serial_platform_open(char const* path, int baud_rate) {
|
|
char full_path[300];
|
|
snprintf(full_path, sizeof(full_path), "\\\\.\\%s", path);
|
|
|
|
HANDLE h = CreateFileA(full_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (h == INVALID_HANDLE_VALUE) return CEL_SERIAL_PLATFORM_INVALID_HANDLE;
|
|
|
|
DCB dcb = {0};
|
|
dcb.DCBlength = sizeof(dcb);
|
|
if (!GetCommState(h, &dcb)) {
|
|
CloseHandle(h);
|
|
return CEL_SERIAL_PLATFORM_INVALID_HANDLE;
|
|
}
|
|
|
|
dcb.BaudRate = (DWORD)baud_rate;
|
|
dcb.ByteSize = 8;
|
|
dcb.Parity = NOPARITY;
|
|
dcb.StopBits = ONESTOPBIT;
|
|
|
|
if (!SetCommState(h, &dcb)) {
|
|
CloseHandle(h);
|
|
return CEL_SERIAL_PLATFORM_INVALID_HANDLE;
|
|
}
|
|
|
|
/* Non-blocking reads: ReadFile returns immediately with whatever
|
|
* bytes are already buffered (possibly zero). */
|
|
COMMTIMEOUTS timeouts = {0};
|
|
timeouts.ReadIntervalTimeout = MAXDWORD;
|
|
if (!SetCommTimeouts(h, &timeouts)) {
|
|
CloseHandle(h);
|
|
return CEL_SERIAL_PLATFORM_INVALID_HANDLE;
|
|
}
|
|
|
|
return (cel_serial_platform_handle)(intptr_t)h;
|
|
}
|
|
|
|
void cel_serial_platform_close(cel_serial_platform_handle handle) {
|
|
CloseHandle((HANDLE)(intptr_t)handle);
|
|
}
|
|
|
|
size_t cel_serial_platform_read(cel_serial_platform_handle handle, uint8_t* buf, size_t len) {
|
|
DWORD bytes_read = 0;
|
|
if (!ReadFile((HANDLE)(intptr_t)handle, buf, (DWORD)len, &bytes_read, NULL)) return 0;
|
|
return (size_t)bytes_read;
|
|
}
|
|
|
|
size_t cel_serial_platform_write(cel_serial_platform_handle handle, uint8_t const* buf, size_t len) {
|
|
DWORD bytes_written = 0;
|
|
if (!WriteFile((HANDLE)(intptr_t)handle, buf, (DWORD)len, &bytes_written, NULL)) return 0;
|
|
return (size_t)bytes_written;
|
|
}
|
|
|
|
void cel_serial_platform_flush(cel_serial_platform_handle handle) {
|
|
FlushFileBuffers((HANDLE)(intptr_t)handle);
|
|
}
|
|
|
|
/* Enumerate active COM ports via HKLM\HARDWARE\DEVICEMAP\SERIALCOMM.
|
|
* Windows keeps one value per active port in this key, so this is a
|
|
* single registry read instead of probing COM1..COM255 with CreateFile. */
|
|
int cel_serial_platform_list_ports(char*** out_ports, int max_ports) {
|
|
char** ports = (char**)calloc(max_ports, sizeof(char*));
|
|
if (ports == NULL) return -1;
|
|
|
|
int count = 0;
|
|
|
|
HKEY key;
|
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM",
|
|
0, KEY_READ, &key) == ERROR_SUCCESS) {
|
|
for (DWORD index = 0; count < max_ports; index++) {
|
|
char value_name[256];
|
|
char port_name[256];
|
|
DWORD value_name_len = sizeof(value_name);
|
|
DWORD port_name_len = sizeof(port_name);
|
|
DWORD type;
|
|
|
|
LONG result = RegEnumValueA(key, index, value_name, &value_name_len,
|
|
NULL, &type, (BYTE*)port_name, &port_name_len);
|
|
if (result != ERROR_SUCCESS) break;
|
|
|
|
if (type == REG_SZ) {
|
|
ports[count] = _strdup(port_name);
|
|
if (ports[count] != NULL) count++;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
}
|
|
|
|
*out_ports = ports;
|
|
return count;
|
|
}
|