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 <filesystem>
17
18#include "libxr_def.hpp"
19#include "libxr_rw.hpp"
20#include "semaphore.hpp"
21#include "uart.hpp"
22
23namespace LibXR
24{
25
26class LinuxUART : public UART
27{
28 public:
29 LinuxUART(const char *dev_path, unsigned int baudrate = 115200,
30 Parity parity = Parity::NO_PARITY, uint8_t data_bits = 8,
31 uint8_t stop_bits = 1, uint32_t tx_queue_size = 5, size_t buffer_size = 512)
32 : UART(&_read_port, &_write_port),
33 rx_buff_(new uint8_t[buffer_size]),
34 tx_buff_(new uint8_t[buffer_size]),
35 buff_size_(buffer_size),
36 _read_port(buffer_size),
37 _write_port(tx_queue_size, buffer_size)
38 {
39 ASSERT(buff_size_ > 0);
40
41 if (std::filesystem::exists(dev_path) == false)
42 {
43 XR_LOG_ERROR("Cannot find UART device: %s", dev_path);
44 ASSERT(false);
45 }
46
47 device_path_ = GetByPathForTTY(dev_path);
48
49 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
50 if (fd_ < 0)
51 {
52 XR_LOG_ERROR("Cannot open UART device: %s", device_path_.c_str());
53 ASSERT(false);
54 }
55 else
56 {
57 XR_LOG_PASS("Open UART device: %s", device_path_.c_str());
58 }
59
60 config_ = {.baudrate = baudrate,
61 .parity = parity,
62 .data_bits = data_bits,
63 .stop_bits = stop_bits};
64
65 SetConfig(config_);
66
67 _read_port = ReadFun;
68 _write_port = WriteFun;
69
70 rx_thread_.Create<LinuxUART *>(
71 this, [](LinuxUART *self) { self->RxLoop(); }, "rx_uart", 8192,
73
74 tx_thread_.Create<LinuxUART *>(
75 this, [](LinuxUART *self) { self->TxLoop(); }, "tx_uart", 8192,
77 }
78
79 LinuxUART(const std::string &vid, const std::string &pid,
80 unsigned int baudrate = 115200, Parity parity = Parity::NO_PARITY,
81 uint8_t data_bits = 8, uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
82 size_t buffer_size = 512)
83 : UART(&_read_port, &_write_port),
84 rx_buff_(new uint8_t[buffer_size]),
85 tx_buff_(new uint8_t[buffer_size]),
86 buff_size_(buffer_size),
87 _read_port(buffer_size),
88 _write_port(tx_queue_size, buffer_size)
89 {
90 while (!FindUSBTTYByVidPid(vid, pid, device_path_))
91 {
92 XR_LOG_WARN("Cannot find USB TTY device with VID=%s PID=%s, retrying...",
93 vid.c_str(), pid.c_str());
94 Thread::Sleep(100);
95 }
96
97 XR_LOG_PASS("Found USB TTY: %s", device_path_.c_str());
98
99 if (std::filesystem::exists(device_path_) == false)
100 {
101 XR_LOG_ERROR("Cannot find UART device: %s", device_path_.c_str());
102 ASSERT(false);
103 return;
104 }
105
106 device_path_ = GetByPathForTTY(device_path_);
107
108 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
109 if (fd_ < 0)
110 {
111 XR_LOG_ERROR("Cannot open UART device: %s", device_path_.c_str());
112 ASSERT(false);
113 }
114 else
115 {
116 XR_LOG_PASS("Open UART device: %s", device_path_.c_str());
117 }
118
119 config_ = {.baudrate = baudrate,
120 .parity = parity,
121 .data_bits = data_bits,
122 .stop_bits = stop_bits};
123
124 SetConfig(config_);
125
126 _read_port = ReadFun;
127 _write_port = WriteFun;
128
129 rx_thread_.Create<LinuxUART *>(
130 this, [](LinuxUART *self) { self->RxLoop(); }, "rx_uart", 8192,
132
133 tx_thread_.Create<LinuxUART *>(
134 this, [](LinuxUART *self) { self->TxLoop(); }, "tx_uart", 8192,
136 }
137
138 std::string GetByPathForTTY(const std::string &tty_name)
139 {
140 const std::string BASE = "/dev/serial/by-path";
141 if (strncmp(tty_name.c_str(), BASE.c_str(), BASE.length()) == 0 ||
142 !std::filesystem::exists(BASE))
143 {
144 return tty_name;
145 }
146 for (const auto &entry : std::filesystem::directory_iterator(BASE))
147 {
148 std::string full = std::filesystem::canonical(entry.path());
149 if (full == tty_name)
150 {
151 return entry.path(); // 返回符号链接路径
152 }
153 }
154 return ""; // 没找到
155 }
156
157 static bool FindUSBTTYByVidPid(const std::string &target_vid,
158 const std::string &target_pid, std::string &tty_path)
159 {
160 struct udev *udev = udev_new();
161 if (!udev)
162 {
163 XR_LOG_ERROR("Cannot create udev context");
164 return false;
165 }
166
167 struct udev_enumerate *enumerate = udev_enumerate_new(udev);
168 udev_enumerate_add_match_subsystem(enumerate, "tty");
169 udev_enumerate_scan_devices(enumerate);
170
171 struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
172 struct udev_list_entry *entry;
173 bool found = false;
174
175 udev_list_entry_foreach(entry, devices)
176 {
177 const char *path = udev_list_entry_get_name(entry);
178 struct udev_device *tty_dev = udev_device_new_from_syspath(udev, path);
179 if (!tty_dev) continue;
180
181 struct udev_device *usb_dev =
182 udev_device_get_parent_with_subsystem_devtype(tty_dev, "usb", "usb_device");
183
184 if (usb_dev)
185 {
186 const char *vid = udev_device_get_sysattr_value(usb_dev, "idVendor");
187 const char *pid = udev_device_get_sysattr_value(usb_dev, "idProduct");
188
189 if (vid && pid && target_vid == vid && target_pid == pid)
190 {
191 const char *devnode = udev_device_get_devnode(tty_dev);
192 if (devnode)
193 {
194 tty_path = devnode;
195 found = true;
196 udev_device_unref(tty_dev);
197 break;
198 }
199 }
200 }
201
202 udev_device_unref(tty_dev);
203 }
204
205 udev_enumerate_unref(enumerate);
206 udev_unref(udev);
207 return found;
208 }
209
210 void SetLowLatency(int fd)
211 {
212 struct serial_struct serinfo;
213 ioctl(fd, TIOCGSERIAL, &serinfo);
214 serinfo.flags |= ASYNC_LOW_LATENCY;
215 ioctl(fd, TIOCSSERIAL, &serinfo);
216 }
217
218 ErrorCode SetConfig(UART::Configuration config) override
219 {
220 if (&config != &config_)
221 {
222 config_ = config;
223 }
224
225 struct termios2 tio
226 {
227 };
228 if (ioctl(fd_, TCGETS2, &tio) != 0)
229 {
230 return ErrorCode::INIT_ERR;
231 }
232
233 // 设置自定义波特率
234 tio.c_cflag &= ~CBAUD;
235 tio.c_cflag |= BOTHER;
236 tio.c_ispeed = config.baudrate;
237 tio.c_ospeed = config.baudrate;
238
239 // 输入模式:关闭软件流控、特殊字符处理
240 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
241#ifdef IUCLC
242 | IUCLC
243#endif
244 );
245
246 // 输出模式:关闭所有加工
247 tio.c_oflag &= ~(OPOST
248#ifdef ONLCR
249 | ONLCR
250#endif
251#ifdef OCRNL
252 | OCRNL
253#endif
254#ifdef ONOCR
255 | ONOCR
256#endif
257#ifdef ONLRET
258 | ONLRET
259#endif
260 );
261
262 // 本地模式:禁用行缓冲、回显、信号中断
263 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
264
265 // 控制模式:设置数据位、校验、停止位、流控
266 tio.c_cflag &= ~CSIZE;
267 switch (config.data_bits)
268 {
269 case 5:
270 tio.c_cflag |= CS5;
271 break;
272 case 6:
273 tio.c_cflag |= CS6;
274 break;
275 case 7:
276 tio.c_cflag |= CS7;
277 break;
278 case 8:
279 tio.c_cflag |= CS8;
280 break;
281 default:
282 return ErrorCode::ARG_ERR;
283 }
284
285 // 停止位
286 tio.c_cflag &= ~CSTOPB;
287 if (config.stop_bits == 2)
288 {
289 tio.c_cflag |= CSTOPB;
290 }
291
292 // 奇偶校验
293 switch (config.parity)
294 {
296 tio.c_cflag &= ~PARENB;
297 break;
299 tio.c_cflag |= PARENB;
300 tio.c_cflag &= ~PARODD;
301 break;
303 tio.c_cflag |= PARENB;
304 tio.c_cflag |= PARODD;
305 break;
306 }
307
308 // 禁用硬件流控
309 tio.c_cflag &= ~CRTSCTS;
310
311 // 启用本地模式、读功能
312 tio.c_cflag |= (CLOCAL | CREAD);
313
314 // 控制字符配置:阻塞直到读到 1 字节
315 // for (int i = 0; i < NCCS; ++i) tio.c_cc[i] = 0;
316 tio.c_cc[VTIME] = 0;
317 tio.c_cc[VMIN] = 1;
318
319 if (ioctl(fd_, TCSETS2, &tio) != 0)
320 {
321 return ErrorCode::INIT_ERR;
322 }
323
324 SetLowLatency(fd_);
325
326 tcflush(fd_, TCIOFLUSH);
327
328 return ErrorCode::OK;
329 }
330
331 static ErrorCode ReadFun(ReadPort &port) { return ErrorCode::EMPTY; }
332
333 static ErrorCode WriteFun(WritePort &port)
334 {
335 auto uart = CONTAINER_OF(&port, LinuxUART, _write_port);
336 uart->write_sem_.Post();
337 return ErrorCode::OK;
338 }
339
340 private:
341 void RxLoop()
342 {
343 while (true)
344 {
345 if (!connected_)
346 {
347 close(fd_);
348 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
349
350 if (fd_ < 0)
351 {
352 XR_LOG_WARN("Cannot open UART device: %s", device_path_.c_str());
353 Thread::Sleep(1000);
354 }
355 else
356 {
357 SetConfig(config_);
358 XR_LOG_PASS("Reopen UART device: %s", device_path_.c_str());
359 connected_ = true;
360 }
361 }
362 auto n = read(fd_, rx_buff_, buff_size_);
363 if (n > 0)
364 {
365 read_port_->queue_data_->PushBatch(rx_buff_, n);
367 }
368 else
369 {
370 XR_LOG_WARN("Cannot read UART device: %s", device_path_.c_str());
371 connected_ = false;
372 }
373 }
374 }
375
376 void TxLoop()
377 {
378 WriteInfoBlock info;
379 while (true)
380 {
381 if (!connected_)
382 {
383 Thread::Sleep(1);
384 continue;
385 }
386
387 if (write_sem_.Wait() != ErrorCode::OK)
388 {
389 continue;
390 }
391
392 if (write_port_->queue_info_->Pop(info) == ErrorCode::OK)
393 {
394 if (write_port_->queue_data_->PopBatch(tx_buff_, info.data.size_) ==
395 ErrorCode::OK)
396 {
397 auto written = write(fd_, tx_buff_, info.data.size_);
398 if (written < 0)
399 {
400 XR_LOG_WARN("Cannot write UART device: %s", device_path_.c_str());
401 connected_ = false;
402 }
403 write_port_->Finish(false,
404 (written == static_cast<int>(info.data.size_))
405 ? ErrorCode::OK
406 : ErrorCode::FAILED,
407 info, written);
408 }
409 else
410 {
411 info.op.UpdateStatus(false, ErrorCode::FAILED);
412 }
413 }
414 }
415 }
416
417 int fd_ = -1;
418 bool connected_ = true;
419 Configuration config_;
420 std::string device_path_;
421 Thread rx_thread_;
422 Thread tx_thread_;
423 uint8_t *rx_buff_ = nullptr;
424 uint8_t *tx_buff_ = nullptr;
425 size_t buff_size_ = 0;
426 Semaphore write_sem_;
427 Mutex read_mutex_;
428
429 ReadPort _read_port;
430 WritePort _write_port;
431};
432
433} // namespace LibXR
ErrorCode SetConfig(UART::Configuration config) override
设置 UART 配置 / Sets the UART configuration
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:269
virtual void ProcessPendingReads(bool in_isr)
Processes pending reads.
Definition libxr_rw.cpp:126
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:25
@ 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:64
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
Definition thread.cpp:16
通用异步收发传输(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
UART(ReadPortType *read_port, WritePortType *write_port)
UART 构造函数 / UART constructor.
Definition uart.hpp:66
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info, uint32_t size)
更新写入操作的状态。 Updates the status of the write operation.
Definition libxr_rw.cpp:207
LibXR 命名空间
ErrorCode(* ReadFun)(ReadPort &port)
Function pointer type for read operations.
Definition libxr_rw.hpp:246
ErrorCode(* WriteFun)(WritePort &port)
Function pointer type for write operations.
Definition libxr_rw.hpp:242
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