3#define termios asmtermios
4#include <asm/termbits.h>
8#include <linux/serial.h>
10#include <sys/select.h>
18#include <system_error>
21#include "libxr_def.hpp"
22#include "libxr_rw.hpp"
23#include "semaphore.hpp"
40 LinuxUART(
const char* dev_path,
unsigned int baudrate = 115200,
42 uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
size_t buffer_size = 512,
43 size_t thread_stack_size = 65536)
44 :
UART(&_read_port, &_write_port),
45 rx_buff_(new uint8_t[buffer_size]),
46 tx_buff_(new uint8_t[buffer_size]),
47 buff_size_(buffer_size),
48 _read_port(buffer_size),
49 _write_port(tx_queue_size, buffer_size)
51 ASSERT(buff_size_ > 0);
53 while (!std::filesystem::exists(dev_path))
55 XR_LOG_WARN(
"Cannot find UART device: %s, retrying...", dev_path);
59 device_path_ = GetByPathForTTY(dev_path);
61 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
64 XR_LOG_ERROR(
"Cannot open UART device: %s", device_path_.c_str());
69 XR_LOG_PASS(
"Open UART device: %s", device_path_.c_str());
84 this, [](
LinuxUART* self) { self->RxLoop(); },
"rx_uart", thread_stack_size,
88 this, [](
LinuxUART* self) { self->TxLoop(); },
"tx_uart", thread_stack_size,
95 LinuxUART(
const std::string& vid,
const std::string& pid,
97 uint8_t data_bits = 8, uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
98 size_t buffer_size = 512,
size_t thread_stack_size = 65536)
99 :
LinuxUART(vid, pid,
"", baudrate, parity, data_bits, stop_bits, tx_queue_size,
100 buffer_size, thread_stack_size)
107 LinuxUART(
const std::string& vid,
const std::string& pid,
const std::string& serial,
109 uint8_t data_bits = 8, uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
110 size_t buffer_size = 512,
size_t thread_stack_size = 65536)
111 :
UART(&_read_port, &_write_port),
112 rx_buff_(new uint8_t[buffer_size]),
113 tx_buff_(new uint8_t[buffer_size]),
114 buff_size_(buffer_size),
115 _read_port(buffer_size),
116 _write_port(tx_queue_size, buffer_size)
118 while (!FindUSBTTYByVidPid(vid, pid, serial, device_path_))
122 XR_LOG_WARN(
"Cannot find USB TTY device with VID=%s PID=%s, retrying...",
123 vid.c_str(), pid.c_str());
127 XR_LOG_WARN(
"Cannot find USB TTY device with VID=%s PID=%s SERIAL=%s, retrying...",
128 vid.c_str(), pid.c_str(), serial.c_str());
133 XR_LOG_PASS(
"Found USB TTY: %s", device_path_.c_str());
135 if (std::filesystem::exists(device_path_) ==
false)
137 XR_LOG_ERROR(
"Cannot find UART device: %s", device_path_.c_str());
142 device_path_ = GetByPathForTTY(device_path_);
144 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
147 XR_LOG_ERROR(
"Cannot open UART device: %s", device_path_.c_str());
152 XR_LOG_PASS(
"Open UART device: %s", device_path_.c_str());
167 this, [](
LinuxUART* self) { self->RxLoop(); },
"rx_uart", thread_stack_size,
171 this, [](
LinuxUART* self) { self->TxLoop(); },
"tx_uart", thread_stack_size,
175 std::string GetByPathForTTY(
const std::string& tty_name)
177 const std::string BASE =
"/dev/serial/by-path";
178 if (strncmp(tty_name.c_str(), BASE.c_str(), BASE.length()) == 0 ||
179 !std::filesystem::exists(BASE))
183 for (
const auto& entry : std::filesystem::directory_iterator(BASE))
186 const auto full = std::filesystem::canonical(entry.path(), ec);
191 if (full == tty_name)
193 return entry.path().string();
199 static bool FindUSBTTYByVidPid(
const std::string& target_vid,
200 const std::string& target_pid,
201 const std::string& target_serial, std::string& tty_path)
203 struct udev* udev = udev_new();
206 XR_LOG_ERROR(
"Cannot create udev context");
210 struct udev_enumerate* enumerate = udev_enumerate_new(udev);
211 udev_enumerate_add_match_subsystem(enumerate,
"tty");
212 udev_enumerate_scan_devices(enumerate);
214 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
215 struct udev_list_entry* entry =
nullptr;
216 std::vector<std::string> matches;
218 udev_list_entry_foreach(entry, devices)
220 const char* path = udev_list_entry_get_name(entry);
221 struct udev_device* tty_dev = udev_device_new_from_syspath(udev, path);
227 struct udev_device* usb_dev =
228 udev_device_get_parent_with_subsystem_devtype(tty_dev,
"usb",
"usb_device");
232 const char* vid = udev_device_get_sysattr_value(usb_dev,
"idVendor");
233 const char* pid = udev_device_get_sysattr_value(usb_dev,
"idProduct");
234 const char* serial = udev_device_get_sysattr_value(usb_dev,
"serial");
236 if (vid && pid && target_vid == vid && target_pid == pid &&
237 (target_serial.empty() || (serial && target_serial == serial)))
239 const char* devnode = udev_device_get_devnode(tty_dev);
242 matches.emplace_back(devnode);
247 udev_device_unref(tty_dev);
250 udev_enumerate_unref(enumerate);
257 std::sort(matches.begin(), matches.end());
258 tty_path = matches.front();
260 if (matches.size() > 1 && target_serial.empty())
263 "Multiple USB TTY devices found with VID=%s PID=%s, using %s. Specify serial to disambiguate.",
264 target_vid.c_str(), target_pid.c_str(), tty_path.c_str());
270 void SetLowLatency(
int fd)
272 struct serial_struct serinfo;
273 ioctl(fd, TIOCGSERIAL, &serinfo);
274 serinfo.flags |= ASYNC_LOW_LATENCY;
275 ioctl(fd, TIOCSSERIAL, &serinfo);
280 if (&config != &config_)
285 struct termios2 tio{};
286 if (ioctl(fd_, TCGETS2, &tio) != 0)
292 tio.c_cflag &= ~CBAUD;
293 tio.c_cflag |= BOTHER;
298 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
305 tio.c_oflag &= ~(OPOST
321 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
324 tio.c_cflag &= ~CSIZE;
344 tio.c_cflag &= ~CSTOPB;
347 tio.c_cflag |= CSTOPB;
354 tio.c_cflag &= ~PARENB;
357 tio.c_cflag |= PARENB;
358 tio.c_cflag &= ~PARODD;
361 tio.c_cflag |= PARENB;
362 tio.c_cflag |= PARODD;
367 tio.c_cflag &= ~CRTSCTS;
370 tio.c_cflag |= (CLOCAL | CREAD);
377 if (ioctl(fd_, TCSETS2, &tio) != 0)
384 tcflush(fd_, TCIOFLUSH);
394 uart->write_sem_.Post();
406 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
410 XR_LOG_WARN(
"Cannot open UART device: %s", device_path_.c_str());
416 XR_LOG_PASS(
"Reopen UART device: %s", device_path_.c_str());
420 auto n = read(fd_, rx_buff_, buff_size_);
428 XR_LOG_WARN(
"Cannot read UART device: %s", device_path_.c_str());
455 auto written = write(fd_, tx_buff_, info.data.size_);
458 XR_LOG_WARN(
"Cannot write UART device: %s", device_path_.c_str());
462 (written ==
static_cast<int>(info.data.size_))
476 bool connected_ =
true;
477 Configuration config_;
478 std::string device_path_;
481 uint8_t* rx_buff_ =
nullptr;
482 uint8_t* tx_buff_ =
nullptr;
483 size_t buff_size_ = 0;
484 Semaphore write_sem_;
488 WritePort _write_port;
Linux UART 串口驱动实现 / Linux UART driver implementation.
ErrorCode SetConfig(UART::Configuration config) override
设置 UART 配置 / Sets the UART configuration
LinuxUART(const char *dev_path, unsigned int baudrate=115200, Parity parity=Parity::NO_PARITY, uint8_t data_bits=8, uint8_t stop_bits=1, uint32_t tx_queue_size=5, size_t buffer_size=512, size_t thread_stack_size=65536)
通过设备路径构造 UART / Construct UART by device path
LinuxUART(const std::string &vid, const std::string &pid, const std::string &serial, unsigned int baudrate=115200, Parity parity=Parity::NO_PARITY, uint8_t data_bits=8, uint8_t stop_bits=1, uint32_t tx_queue_size=5, size_t buffer_size=512, size_t thread_stack_size=65536)
通过 USB VID/PID/序列号构造 UART / Construct UART by USB VID/PID/serial
LinuxUART(const std::string &vid, const std::string &pid, unsigned int baudrate=115200, Parity parity=Parity::NO_PARITY, uint8_t data_bits=8, uint8_t stop_bits=1, uint32_t tx_queue_size=5, size_t buffer_size=512, size_t thread_stack_size=65536)
通过 USB VID/PID 构造 UART / Construct UART by USB VID/PID
ErrorCode PushBatch(const Data *data, size_t size)
批量推入数据 / Pushes multiple elements into the queue
ErrorCode PopBatch(Data *data, size_t size)
批量弹出数据 / Pops multiple elements from the queue
ReadPort class for handling read operations.
void ProcessPendingReads(bool in_isr)
Processes pending reads.
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
@ REALTIME
实时优先级 Realtime priority
void Create(ArgType arg, void(*function)(ArgType arg), const char *name, size_t stack_depth, Thread::Priority priority)
创建新线程 Creates a new thread
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
ReadPort * read_port_
读取端口 / Read port
Parity
奇偶校验模式 / Parity mode
@ NO_PARITY
无校验 / No parity
WritePort * write_port_
写入端口 / Write port
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
@ INIT_ERR
初始化错误 | Initialization error
@ FAILED
操作失败 | Operation failed
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针
UART 配置结构体 / UART configuration structure.
uint8_t stop_bits
停止位长度 / Number of stop bits
Parity parity
校验模式 / Parity mode
uint8_t data_bits
数据位长度 / Number of data bits
uint32_t baudrate
波特率 / Baud rate