libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
linux_uart.hpp
1#pragma once
2
3#define termios asmtermios
4#include <asm/termbits.h>
5#undef termios
6#include <fcntl.h>
7#include <libudev.h>
8#include <linux/serial.h>
9#include <sys/ioctl.h>
10#include <sys/select.h>
11#include <termios.h>
12#include <unistd.h>
13
14#include <cstddef>
15#include <cstdint>
16#include <algorithm>
17#include <filesystem>
18#include <system_error>
19#include <vector>
20
21#include "libxr_def.hpp"
22#include "libxr_rw.hpp"
23#include "semaphore.hpp"
24#include "uart.hpp"
25
26namespace LibXR
27{
34class LinuxUART : public UART
35{
36 public:
40 LinuxUART(const char* dev_path, unsigned int baudrate = 115200,
41 Parity parity = Parity::NO_PARITY, uint8_t data_bits = 8,
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)
50 {
51 ASSERT(buff_size_ > 0);
52
53 while (!std::filesystem::exists(dev_path))
54 {
55 XR_LOG_WARN("Cannot find UART device: %s, retrying...", dev_path);
56 Thread::Sleep(100);
57 }
58
59 device_path_ = GetByPathForTTY(dev_path);
60
61 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
62 if (fd_ < 0)
63 {
64 XR_LOG_ERROR("Cannot open UART device: %s", device_path_.c_str());
65 ASSERT(false);
66 }
67 else
68 {
69 XR_LOG_PASS("Open UART device: %s", device_path_.c_str());
70 }
71
72 config_ = {};
73 config_.baudrate = baudrate;
74 config_.parity = parity;
75 config_.data_bits = data_bits;
76 config_.stop_bits = stop_bits;
77
78 SetConfig(config_);
79
80 _read_port = ReadFun;
81 _write_port = WriteFun;
82
83 rx_thread_.Create<LinuxUART*>(
84 this, [](LinuxUART* self) { self->RxLoop(); }, "rx_uart", thread_stack_size,
86
87 tx_thread_.Create<LinuxUART*>(
88 this, [](LinuxUART* self) { self->TxLoop(); }, "tx_uart", thread_stack_size,
90 }
91
95 LinuxUART(const std::string& vid, const std::string& pid,
96 unsigned int baudrate = 115200, Parity parity = Parity::NO_PARITY,
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)
101 {
102 }
103
107 LinuxUART(const std::string& vid, const std::string& pid, const std::string& serial,
108 unsigned int baudrate = 115200, Parity parity = Parity::NO_PARITY,
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)
117 {
118 while (!FindUSBTTYByVidPid(vid, pid, serial, device_path_))
119 {
120 if (serial.empty())
121 {
122 XR_LOG_WARN("Cannot find USB TTY device with VID=%s PID=%s, retrying...",
123 vid.c_str(), pid.c_str());
124 }
125 else
126 {
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());
129 }
130 Thread::Sleep(100);
131 }
132
133 XR_LOG_PASS("Found USB TTY: %s", device_path_.c_str());
134
135 if (std::filesystem::exists(device_path_) == false)
136 {
137 XR_LOG_ERROR("Cannot find UART device: %s", device_path_.c_str());
138 ASSERT(false);
139 return;
140 }
141
142 device_path_ = GetByPathForTTY(device_path_);
143
144 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
145 if (fd_ < 0)
146 {
147 XR_LOG_ERROR("Cannot open UART device: %s", device_path_.c_str());
148 ASSERT(false);
149 }
150 else
151 {
152 XR_LOG_PASS("Open UART device: %s", device_path_.c_str());
153 }
154
155 config_ = {};
156 config_.baudrate = baudrate;
157 config_.parity = parity;
158 config_.data_bits = data_bits;
159 config_.stop_bits = stop_bits;
160
161 SetConfig(config_);
162
163 _read_port = ReadFun;
164 _write_port = WriteFun;
165
166 rx_thread_.Create<LinuxUART*>(
167 this, [](LinuxUART* self) { self->RxLoop(); }, "rx_uart", thread_stack_size,
169
170 tx_thread_.Create<LinuxUART*>(
171 this, [](LinuxUART* self) { self->TxLoop(); }, "tx_uart", thread_stack_size,
173 }
174
175 std::string GetByPathForTTY(const std::string& tty_name)
176 {
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))
180 {
181 return tty_name;
182 }
183 for (const auto& entry : std::filesystem::directory_iterator(BASE))
184 {
185 std::error_code ec;
186 const auto full = std::filesystem::canonical(entry.path(), ec);
187 if (ec)
188 {
189 continue;
190 }
191 if (full == tty_name)
192 {
193 return entry.path().string(); // 返回符号链接路径
194 }
195 }
196 return tty_name; // 未命中 by-path 时保留原始 tty 路径
197 }
198
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)
202 {
203 struct udev* udev = udev_new();
204 if (!udev)
205 {
206 XR_LOG_ERROR("Cannot create udev context");
207 return false;
208 }
209
210 struct udev_enumerate* enumerate = udev_enumerate_new(udev);
211 udev_enumerate_add_match_subsystem(enumerate, "tty");
212 udev_enumerate_scan_devices(enumerate);
213
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;
217
218 udev_list_entry_foreach(entry, devices)
219 {
220 const char* path = udev_list_entry_get_name(entry);
221 struct udev_device* tty_dev = udev_device_new_from_syspath(udev, path);
222 if (!tty_dev)
223 {
224 continue;
225 }
226
227 struct udev_device* usb_dev =
228 udev_device_get_parent_with_subsystem_devtype(tty_dev, "usb", "usb_device");
229
230 if (usb_dev)
231 {
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");
235
236 if (vid && pid && target_vid == vid && target_pid == pid &&
237 (target_serial.empty() || (serial && target_serial == serial)))
238 {
239 const char* devnode = udev_device_get_devnode(tty_dev);
240 if (devnode)
241 {
242 matches.emplace_back(devnode);
243 }
244 }
245 }
246
247 udev_device_unref(tty_dev);
248 }
249
250 udev_enumerate_unref(enumerate);
251 udev_unref(udev);
252 if (matches.empty())
253 {
254 return false;
255 }
256
257 std::sort(matches.begin(), matches.end());
258 tty_path = matches.front();
259
260 if (matches.size() > 1 && target_serial.empty())
261 {
262 XR_LOG_WARN(
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());
265 }
266
267 return true;
268 }
269
270 void SetLowLatency(int fd)
271 {
272 struct serial_struct serinfo;
273 ioctl(fd, TIOCGSERIAL, &serinfo);
274 serinfo.flags |= ASYNC_LOW_LATENCY;
275 ioctl(fd, TIOCSSERIAL, &serinfo);
276 }
277
279 {
280 if (&config != &config_)
281 {
282 config_ = config;
283 }
284
285 struct termios2 tio{};
286 if (ioctl(fd_, TCGETS2, &tio) != 0)
287 {
288 return ErrorCode::INIT_ERR;
289 }
290
291 // 设置自定义波特率
292 tio.c_cflag &= ~CBAUD;
293 tio.c_cflag |= BOTHER;
294 tio.c_ispeed = config.baudrate;
295 tio.c_ospeed = config.baudrate;
296
297 // 输入模式:关闭软件流控、特殊字符处理
298 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
299#ifdef IUCLC
300 | IUCLC
301#endif
302 );
303
304 // 输出模式:关闭所有加工
305 tio.c_oflag &= ~(OPOST
306#ifdef ONLCR
307 | ONLCR
308#endif
309#ifdef OCRNL
310 | OCRNL
311#endif
312#ifdef ONOCR
313 | ONOCR
314#endif
315#ifdef ONLRET
316 | ONLRET
317#endif
318 );
319
320 // 本地模式:禁用行缓冲、回显、信号中断
321 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
322
323 // 控制模式:设置数据位、校验、停止位、流控
324 tio.c_cflag &= ~CSIZE;
325 switch (config.data_bits)
326 {
327 case 5:
328 tio.c_cflag |= CS5;
329 break;
330 case 6:
331 tio.c_cflag |= CS6;
332 break;
333 case 7:
334 tio.c_cflag |= CS7;
335 break;
336 case 8:
337 tio.c_cflag |= CS8;
338 break;
339 default:
340 return ErrorCode::ARG_ERR;
341 }
342
343 // 停止位
344 tio.c_cflag &= ~CSTOPB;
345 if (config.stop_bits == 2)
346 {
347 tio.c_cflag |= CSTOPB;
348 }
349
350 // 奇偶校验
351 switch (config.parity)
352 {
354 tio.c_cflag &= ~PARENB;
355 break;
357 tio.c_cflag |= PARENB;
358 tio.c_cflag &= ~PARODD;
359 break;
361 tio.c_cflag |= PARENB;
362 tio.c_cflag |= PARODD;
363 break;
364 }
365
366 // 禁用硬件流控
367 tio.c_cflag &= ~CRTSCTS;
368
369 // 启用本地模式、读功能
370 tio.c_cflag |= (CLOCAL | CREAD);
371
372 // 控制字符配置:阻塞直到读到 1 字节
373 // for (int i = 0; i < NCCS; ++i) tio.c_cc[i] = 0;
374 tio.c_cc[VTIME] = 0;
375 tio.c_cc[VMIN] = 1;
376
377 if (ioctl(fd_, TCSETS2, &tio) != 0)
378 {
379 return ErrorCode::INIT_ERR;
380 }
381
382 SetLowLatency(fd_);
383
384 tcflush(fd_, TCIOFLUSH);
385
386 return ErrorCode::OK;
387 }
388
389 static ErrorCode ReadFun(ReadPort&, bool) { return ErrorCode::EMPTY; }
390
391 static ErrorCode WriteFun(WritePort& port, bool)
392 {
393 auto* uart = LibXR::ContainerOf(&port, &LinuxUART::_write_port);
394 uart->write_sem_.Post();
395 return ErrorCode::OK;
396 }
397
398 private:
399 void RxLoop()
400 {
401 while (true)
402 {
403 if (!connected_)
404 {
405 close(fd_);
406 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
407
408 if (fd_ < 0)
409 {
410 XR_LOG_WARN("Cannot open UART device: %s", device_path_.c_str());
411 Thread::Sleep(1000);
412 }
413 else
414 {
415 SetConfig(config_);
416 XR_LOG_PASS("Reopen UART device: %s", device_path_.c_str());
417 connected_ = true;
418 }
419 }
420 auto n = read(fd_, rx_buff_, buff_size_);
421 if (n > 0)
422 {
423 read_port_->queue_data_->PushBatch(rx_buff_, n);
425 }
426 else
427 {
428 XR_LOG_WARN("Cannot read UART device: %s", device_path_.c_str());
429 connected_ = false;
430 }
431 }
432 }
433
434 void TxLoop()
435 {
436 WriteInfoBlock info;
437 while (true)
438 {
439 if (!connected_)
440 {
441 Thread::Sleep(1);
442 continue;
443 }
444
445 if (write_sem_.Wait() != ErrorCode::OK)
446 {
447 continue;
448 }
449
450 if (write_port_->queue_info_->Pop(info) == ErrorCode::OK)
451 {
452 if (write_port_->queue_data_->PopBatch(tx_buff_, info.data.size_) ==
454 {
455 auto written = write(fd_, tx_buff_, info.data.size_);
456 if (written < 0)
457 {
458 XR_LOG_WARN("Cannot write UART device: %s", device_path_.c_str());
459 connected_ = false;
460 }
461 write_port_->Finish(false,
462 (written == static_cast<int>(info.data.size_))
465 info);
466 }
467 else
468 {
469 info.op.UpdateStatus(false, ErrorCode::FAILED);
470 }
471 }
472 }
473 }
474
475 int fd_ = -1;
476 bool connected_ = true;
477 Configuration config_;
478 std::string device_path_;
479 Thread rx_thread_;
480 Thread tx_thread_;
481 uint8_t* rx_buff_ = nullptr;
482 uint8_t* tx_buff_ = nullptr;
483 size_t buff_size_ = 0;
484 Semaphore write_sem_;
485 Mutex read_mutex_;
486
487 ReadPort _read_port; // NOLINT
488 WritePort _write_port; // NOLINT
489};
490
491} // namespace LibXR
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.
Definition libxr_rw.hpp:379
void ProcessPendingReads(bool in_isr)
Processes pending reads.
Definition libxr_rw.cpp:193
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:53
@ 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
Definition thread.hpp:68
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
Definition thread.cpp:15
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
ReadPort * read_port_
读取端口 / Read port
Definition uart.hpp:53
Parity
奇偶校验模式 / Parity mode
Definition uart.hpp:29
@ NO_PARITY
无校验 / No parity
@ ODD
奇校验 / Odd parity
@ EVEN
偶校验 / Even parity
WritePort * write_port_
写入端口 / Write port
Definition uart.hpp:54
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
Definition libxr_rw.cpp:294
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ FAILED
操作失败 | Operation failed
@ EMPTY
为空 | Empty
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
Definition libxr_rw.hpp:356
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
Definition libxr_rw.hpp:352
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针
Definition libxr_def.hpp:79
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:50
Parity parity
校验模式 / Parity mode
Definition uart.hpp:47
uint8_t data_bits
数据位长度 / Number of data bits
Definition uart.hpp:48
uint32_t baudrate
波特率 / Baud rate
Definition uart.hpp:45