Ken Van Hoeylandt b85ef7a2e7
UART refactored (#236)
`Uart` is now an abstract class with a `UartEsp` and a `UartPosix` implementation.
2025-02-26 17:13:37 +01:00

194 lines
5.1 KiB
C++

#ifndef ESP_PLATFORM
#include "Tactility/hal/uart/UartPosix.h"
#include "Tactility/hal/uart/Uart.h"
#include <Tactility/Log.h>
#include <cstring>
#include <sstream>
#include <sys/ioctl.h>
#include <unistd.h>
#define TAG "uart"
namespace tt::hal::uart {
bool UartPosix::start() {
auto lock = mutex.asScopedLock();
lock.lock();
if (device != nullptr) {
TT_LOG_E(TAG, "(%s) Starting: Already started", configuration.name.c_str());
return false;
}
auto file = fopen(configuration.name.c_str(), "w");
if (file == nullptr) {
TT_LOG_E(TAG, "(%s) failed to open", configuration.name.c_str());
return false;
}
auto new_device = std::unique_ptr<FILE, AutoCloseFileDeleter>(file);
struct termios tty;
if (tcgetattr(fileno(file), &tty) < 0) {
printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno));
return false;
}
if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) {
TT_LOG_E(TAG, "(%s) failed to set output speed", configuration.name.c_str());
return false;
}
if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) {
TT_LOG_E(TAG, "(%s) failed to set input speed", configuration.name.c_str());
return false;
}
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fileno(file), TCSANOW, &tty) != 0) {
printf("(%s) tcsetattr failed: %s\n", configuration.name.c_str(), strerror(errno));
return false;
}
device = std::move(new_device);
TT_LOG_I(TAG, "(%s) Started", configuration.name.c_str());
return true;
}
bool UartPosix::stop() {
auto lock = mutex.asScopedLock();
lock.lock();
if (device == nullptr) {
TT_LOG_E(TAG, "(%s) Stopping: Not started", configuration.name.c_str());
return false;
}
device = nullptr;
TT_LOG_I(TAG, "(%s) Stopped", configuration.name.c_str());
return true;
}
bool UartPosix::isStarted() const {
auto lock = mutex.asScopedLock();
lock.lock();
return device != nullptr;
}
size_t UartPosix::readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
if (awaitAvailable(timeout)) {
return read(fileno(device.get()), buffer, bufferSize);
} else {
return 0;
}
}
bool UartPosix::readByte(std::byte* output, TickType_t timeout) {
if (awaitAvailable(timeout)) {
return read(fileno(device.get()), output, 1) == 1;
} else {
return false;
}
}
size_t UartPosix::writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) {
if (!mutex.lock(timeout)) {
return false;
}
return write(fileno(device.get()), buffer, bufferSize);
}
size_t UartPosix::available(TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
uint32_t bytes_available = 0;
ioctl(fileno(device.get()), FIONREAD, bytes_available);
return bytes_available;
}
void UartPosix::flushInput() {
// TODO
}
uint32_t UartPosix::getBaudRate() {
struct termios tty;
if (tcgetattr(fileno(device.get()), &tty) < 0) {
printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno));
return false;
} else {
return (uint32_t)cfgetispeed(&tty);
}
}
bool UartPosix::setBaudRate(uint32_t baudRate, TickType_t timeout) {
auto lock = mutex.asScopedLock();
if (!lock.lock(timeout)) {
return false;
}
struct termios tty;
if (tcgetattr(fileno(device.get()), &tty) < 0) {
printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno));
return false;
}
if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) {
TT_LOG_E(TAG, "(%s) failed to set output speed", configuration.name.c_str());
return false;
}
if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) {
TT_LOG_E(TAG, "(%s) failed to set input speed", configuration.name.c_str());
return false;
}
return true;
}
bool UartPosix::awaitAvailable(TickType_t timeout) {
auto start_time = kernel::getTicks();
do {
if (available(timeout) > 0) {
return true;
}
kernel::delayTicks(timeout / 10);
} while ((kernel::getTicks() - start()) < timeout);
return false;
}
std::unique_ptr<Uart> create(const Configuration& configuration) {
return std::make_unique<UartPosix>(configuration);
}
} // namespace tt::hal::uart
#endif