Added serial support
This commit is contained in:
@@ -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/
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
@@ -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/
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user