Added serial support

This commit is contained in:
2026-09-07 20:19:13 -06:00
parent 28f4c1ac94
commit aea76b9d3e
7 changed files with 512 additions and 1 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ CFLAGS := -std=c++20 -O2 $(CDEFS) \
-Wlogical-op -Wuseless-cast
LFLAGS := -std=c++20 -static-libstdc++ -static-libgcc \
-Wl,--fatal-warnings -Wl,--warn-common
LIB :=
LIB := -lsetupapi -ldxguid
INC := -I./src/
#---------------------------------------------------------------------------------
+1
View File
@@ -25,6 +25,7 @@ CFLAGS := -std=c++20 -O2 \
-Wformat=2 -Wimplicit-fallthrough -Wsuggest-override \
-Wextra-semi -Wduplicated-cond -Wduplicated-branches \
-Wlogical-op -Wuseless-cast
LIB := -lsetupapi -ldxguid
INC := -I./src/
#---------------------------------------------------------------------------------
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>
namespace ckitty {
namespace serial {
namespace os {
struct SerialInfo;
std::vector<std::string> listPorts();
SerialInfo* openPort(const std::string& port, uint32_t baudRate);
void closePort(SerialInfo* info);
bool isOpen(SerialInfo* info);
size_t write(SerialInfo* info, const uint8_t* data, size_t size);
size_t read(SerialInfo* info, uint8_t* buffer, size_t maxSize);
void setDTR(SerialInfo* info, bool level);
void setRTS(SerialInfo* info, bool level);
bool getCTS(SerialInfo* info);
bool getDSR(SerialInfo* info);
bool getRI(SerialInfo* info);
bool getCD(SerialInfo* info);
}
}
}
+165
View File
@@ -0,0 +1,165 @@
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
#include "common_serial.hpp"
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <vector>
#include <string>
#include <cstring>
namespace ckitty {
namespace serial {
namespace os {
struct SerialInfo {
int fd = -1;
std::string portName;
};
std::vector<std::string> listPorts() {
std::vector<std::string> ports;
DIR* dir = opendir("/dev");
if (!dir) return ports;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name;
if (name.rfind("ttyS", 0) == 0 ||
name.rfind("ttyUSB", 0) == 0 ||
name.rfind("ttyACM", 0) == 0 ||
name.rfind("rfcomm", 0) == 0) {
ports.push_back("/dev/" + name);
}
}
closedir(dir);
return ports;
}
SerialInfo* openPort(const std::string& port, uint32_t baudRate) {
int fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) return nullptr;
struct termios tty = {};
if (tcgetattr(fd, &tty) != 0) {
close(fd);
return nullptr;
}
cfsetospeed(&tty, baudRate);
cfsetispeed(&tty, baudRate);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS;
tty.c_cflag |= CREAD | CLOCAL;
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO;
tty.c_lflag &= ~ECHOE;
tty.c_lflag &= ~ECHONL;
tty.c_lflag &= ~ISIG;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_oflag &= ~OPOST;
tty.c_oflag &= ~ONLCR;
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 0;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
close(fd);
return nullptr;
}
auto* info = new SerialInfo();
info->fd = fd;
info->portName = port;
return info;
}
void closePort(SerialInfo* info) {
if (!info) return;
if (info->fd != -1) {
close(info->fd);
info->fd = -1;
}
delete info;
}
bool isOpen(SerialInfo* info) {
return info && info->fd != -1;
}
size_t write(SerialInfo* info, const uint8_t* data, size_t size) {
if (!info || !isOpen(info)) return 0;
ssize_t written = ::write(info->fd, data, size);
return written > 0 ? written : 0;
}
size_t read(SerialInfo* info, uint8_t* buffer, size_t maxSize) {
if (!info || !isOpen(info)) return 0;
ssize_t n = ::read(info->fd, buffer, maxSize);
return n > 0 ? n : 0;
}
void setDTR(SerialInfo* info, bool level) {
if (!info || !isOpen(info)) return;
int status;
ioctl(info->fd, TIOCMGET, &status);
if (level) status |= TIOCM_DTR;
else status &= ~TIOCM_DTR;
ioctl(info->fd, TIOCMSET, &status);
}
void setRTS(SerialInfo* info, bool level) {
if (!info || !isOpen(info)) return;
int status;
ioctl(info->fd, TIOCMGET, &status);
if (level) status |= TIOCM_RTS;
else status &= ~TIOCM_RTS;
ioctl(info->fd, TIOCMSET, &status);
}
bool getCTS(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
int status;
ioctl(info->fd, TIOCMGET, &status);
return (status & TIOCM_CTS) != 0;
}
bool getDSR(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
int status;
ioctl(info->fd, TIOCMGET, &status);
return (status & TIOCM_DSR) != 0;
}
bool getRI(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
int status;
ioctl(info->fd, TIOCMGET, &status);
return (status & TIOCM_RI) != 0;
}
bool getCD(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
int status;
ioctl(info->fd, TIOCMGET, &status);
return (status & TIOCM_CD) != 0;
}
}
}
}
#endif
+175
View File
@@ -0,0 +1,175 @@
#ifdef _WIN32
#define INITGUID
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include "common_serial.hpp"
#include <vector>
#include <string>
#include <memory>
namespace ckitty {
namespace serial {
namespace os {
struct SerialInfo {
HANDLE handle = INVALID_HANDLE_VALUE;
std::string portName;
};
std::vector<std::string> listPorts() {
std::vector<std::string> ports;
HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, nullptr, nullptr, DIGCF_PRESENT);
if (devInfo == INVALID_HANDLE_VALUE) return ports;
SP_DEVINFO_DATA devInfoData;
ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (DWORD i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devInfoData); ++i) {
char buffer[256];
DWORD requiredSize = 0;
if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr,
reinterpret_cast<PBYTE>(buffer), sizeof(buffer), &requiredSize)) {
std::string name(buffer);
size_t comPos = name.find("COM");
if (comPos != std::string::npos) {
size_t endPos = name.find_first_of(" )", comPos);
if (endPos == std::string::npos) endPos = name.size();
std::string port = name.substr(comPos, endPos - comPos);
if (!port.empty() && port[0] == 'C') {
ports.push_back(port);
}
}
}
}
SetupDiDestroyDeviceInfoList(devInfo);
return ports;
}
SerialInfo* openPort(const std::string& port, uint32_t baudRate) {
std::string fullPort = "\\\\.\\" + port;
HANDLE h = CreateFileA(fullPort.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE) return nullptr;
DCB dcb;
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(h, &dcb)) {
CloseHandle(h);
return nullptr;
}
dcb.BaudRate = baudRate;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fBinary = TRUE;
dcb.fParity = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
if (!SetCommState(h, &dcb)) {
CloseHandle(h);
return nullptr;
}
COMMTIMEOUTS timeouts = {};
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
SetCommTimeouts(h, &timeouts);
auto* info = new SerialInfo();
info->handle = h;
info->portName = port;
return info;
}
void closePort(SerialInfo* info) {
if (!info) return;
if (info->handle != INVALID_HANDLE_VALUE) {
CloseHandle(info->handle);
info->handle = INVALID_HANDLE_VALUE;
}
delete info;
}
bool isOpen(SerialInfo* info) {
return info && info->handle != INVALID_HANDLE_VALUE;
}
size_t write(SerialInfo* info, const uint8_t* data, size_t size) {
if (!info || !isOpen(info)) return 0;
DWORD written = 0;
WriteFile(info->handle, data, static_cast<DWORD>(size), &written, nullptr);
return written;
}
size_t read(SerialInfo* info, uint8_t* buffer, size_t maxSize) {
if (!info || !isOpen(info)) return 0;
DWORD read = 0;
ReadFile(info->handle, buffer, static_cast<DWORD>(maxSize), &read, nullptr);
return read;
}
void setDTR(SerialInfo* info, bool level) {
if (!info || !isOpen(info)) return;
EscapeCommFunction(info->handle, level ? SETDTR : CLRDTR);
}
void setRTS(SerialInfo* info, bool level) {
if (!info || !isOpen(info)) return;
EscapeCommFunction(info->handle, level ? SETRTS : CLRRTS);
}
bool getCTS(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
DWORD modemStatus = 0;
GetCommModemStatus(info->handle, &modemStatus);
return (modemStatus & MS_CTS_ON) != 0;
}
bool getDSR(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
DWORD modemStatus = 0;
GetCommModemStatus(info->handle, &modemStatus);
return (modemStatus & MS_DSR_ON) != 0;
}
bool getRI(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
DWORD modemStatus = 0;
GetCommModemStatus(info->handle, &modemStatus);
return (modemStatus & MS_RING_ON) != 0;
}
bool getCD(SerialInfo* info) {
if (!info || !isOpen(info)) return false;
DWORD modemStatus = 0;
GetCommModemStatus(info->handle, &modemStatus);
return (modemStatus & MS_RLSD_ON) != 0;
}
}
}
}
#endif
+85
View File
@@ -0,0 +1,85 @@
#include "Serial.hpp"
namespace ckitty {
Serial::~Serial() {
if (info) {
ckitty::serial::os::closePort(info);
info = nullptr;
}
}
std::vector<std::string> Serial::listPorts() {
return ckitty::serial::os::listPorts();
}
bool Serial::open(std::string_view port, uint32_t baudRate) {
if (info) return false;
info = ckitty::serial::os::openPort(std::string(port), baudRate);
return info != nullptr;
}
void Serial::close() {
if (info) {
ckitty::serial::os::closePort(info);
info = nullptr;
}
}
bool Serial::isOpen() const {
return info && ckitty::serial::os::isOpen(info);
}
size_t Serial::write(const uint8_t* data, size_t size) {
if (!info || !isOpen()) return 0;
return ckitty::serial::os::write(info, data, size);
}
size_t Serial::write(std::string_view data) {
return write(reinterpret_cast<const uint8_t*>(data.data()), data.size());
}
size_t Serial::read(uint8_t* buffer, size_t maxSize) {
if (!info || !isOpen()) return 0;
return ckitty::serial::os::read(info, buffer, maxSize);
}
std::string Serial::read(size_t maxSize) {
std::string result;
result.resize(maxSize);
size_t n = read(reinterpret_cast<uint8_t*>(result.data()), maxSize);
result.resize(n);
return result;
}
void Serial::setDTR(bool level) {
if (!info || !isOpen()) return;
ckitty::serial::os::setDTR(info, level);
}
void Serial::setRTS(bool level) {
if (!info || !isOpen()) return;
ckitty::serial::os::setRTS(info, level);
}
bool Serial::getCTS() const {
if (!info || !isOpen()) return false;
return ckitty::serial::os::getCTS(info);
}
bool Serial::getDSR() const {
if (!info || !isOpen()) return false;
return ckitty::serial::os::getDSR(info);
}
bool Serial::getRI() const {
if (!info || !isOpen()) return false;
return ckitty::serial::os::getRI(info);
}
bool Serial::getCD() const {
if (!info || !isOpen()) return false;
return ckitty::serial::os::getCD(info);
}
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>
#include <desktoplib/os/common_serial.hpp>
namespace ckitty {
class Serial {
private:
ckitty::serial::os::SerialInfo* info = nullptr;
public:
Serial() = default;
~Serial();
static std::vector<std::string> listPorts();
bool open(std::string_view port, uint32_t baudRate);
void close();
bool isOpen() const;
size_t write(const uint8_t* data, size_t size);
size_t write(std::string_view data);
size_t read(uint8_t* buffer, size_t maxSize);
std::string read(size_t maxSize = 1024);
void setDTR(bool level);
void setRTS(bool level);
bool getCTS() const;
bool getDSR() const;
bool getRI() const;
bool getCD() const;
};
}