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