diff --git a/makefile b/makefile index bb59865..eb3fb31 100644 --- a/makefile +++ b/makefile @@ -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/ #--------------------------------------------------------------------------------- diff --git a/makelib.mak b/makelib.mak index dbbe1c3..6aab398 100644 --- a/makelib.mak +++ b/makelib.mak @@ -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/ #--------------------------------------------------------------------------------- diff --git a/src/desktoplib/os/common_serial.hpp b/src/desktoplib/os/common_serial.hpp new file mode 100644 index 0000000..797f342 --- /dev/null +++ b/src/desktoplib/os/common_serial.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +namespace ckitty { + + namespace serial { + + namespace os { + + struct SerialInfo; + + std::vector 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); + + } + + } + +} \ No newline at end of file diff --git a/src/desktoplib/os/unix_serial.cpp b/src/desktoplib/os/unix_serial.cpp new file mode 100644 index 0000000..bdfba1e --- /dev/null +++ b/src/desktoplib/os/unix_serial.cpp @@ -0,0 +1,165 @@ +#if defined(__unix__) || defined(__APPLE__) || defined(__linux__) + +#include "common_serial.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ckitty { + namespace serial { + namespace os { + + struct SerialInfo { + int fd = -1; + std::string portName; + }; + + std::vector listPorts() { + std::vector 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 \ No newline at end of file diff --git a/src/desktoplib/os/windows_serial.cpp b/src/desktoplib/os/windows_serial.cpp new file mode 100644 index 0000000..c374c6b --- /dev/null +++ b/src/desktoplib/os/windows_serial.cpp @@ -0,0 +1,175 @@ +#ifdef _WIN32 + +#define INITGUID +#include +#include +#include + +#include "common_serial.hpp" + +#include +#include +#include + +namespace ckitty { + namespace serial { + namespace os { + + struct SerialInfo { + HANDLE handle = INVALID_HANDLE_VALUE; + std::string portName; + }; + + std::vector listPorts() { + std::vector 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(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(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(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 \ No newline at end of file diff --git a/src/desktoplib/serial/Serial.cpp b/src/desktoplib/serial/Serial.cpp new file mode 100644 index 0000000..999eeee --- /dev/null +++ b/src/desktoplib/serial/Serial.cpp @@ -0,0 +1,85 @@ +#include "Serial.hpp" + +namespace ckitty { + + Serial::~Serial() { + if (info) { + ckitty::serial::os::closePort(info); + info = nullptr; + } + } + + std::vector 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(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(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); + } + +} \ No newline at end of file diff --git a/src/desktoplib/serial/Serial.hpp b/src/desktoplib/serial/Serial.hpp new file mode 100644 index 0000000..9c2cbcd --- /dev/null +++ b/src/desktoplib/serial/Serial.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace ckitty { + + class Serial { + private: + ckitty::serial::os::SerialInfo* info = nullptr; + + public: + Serial() = default; + ~Serial(); + + static std::vector 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; + }; + +} \ No newline at end of file