libxr
Embedded C++ framework for bare-metal, RTOS and Linux
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 <poll.h>
10#include <sys/eventfd.h>
11#include <sys/ioctl.h>
12#include <sys/uio.h>
13#include <termios.h>
14#include <unistd.h>
15
16#include <algorithm>
17#include <array>
18#include <atomic>
19#include <cerrno>
20#include <cstddef>
21#include <cstdint>
22#include <filesystem>
23#include <string>
24#include <system_error>
25#include <vector>
26
27#include "libxr_def.hpp"
28#include "libxr_rw.hpp"
29#include "semaphore.hpp"
30#include "uart.hpp"
31
32namespace LibXR
33{
34class LinuxUART;
35
36namespace Detail
37{
42{
43 public:
45 LinuxUARTReadPort(size_t size, LinuxUART& owner) : ReadPort(size), owner_(owner) {}
46
48 void OnReadQueueSpaceAvailable(bool) override;
49
50 private:
51 LinuxUART& owner_;
52};
53} // namespace Detail
54
73class LinuxUART : public UART
74{
75 friend class Detail::LinuxUARTReadPort;
76
78 enum class ConfigState : uint8_t
79 {
80 EMPTY,
81 RESERVED,
82 PUBLISHED,
83 };
84
85 public:
101 LinuxUART(const char* dev_path, unsigned int baudrate = 115200,
102 Parity parity = Parity::NO_PARITY, uint8_t data_bits = 8,
103 uint8_t stop_bits = 1, uint32_t tx_queue_size = 5, size_t buffer_size = 512,
104 size_t thread_stack_size = 65536)
105 : UART(&_read_port, &_write_port),
106 rx_buff_(new uint8_t[buffer_size]),
107 buff_size_(buffer_size),
108 config_{baudrate, parity, data_bits, stop_bits},
109 _read_port(buffer_size, *this),
110 _write_port(tx_queue_size, buffer_size)
111 {
112 ASSERT(buff_size_ > 0);
113 ASSERT(tx_queue_size > 0);
114
115 while (!std::filesystem::exists(dev_path))
116 {
117 XR_LOG_WARN("Cannot find UART device: %s, retrying...", dev_path);
118 Thread::Sleep(100);
119 }
120
121 device_path_ = GetByPathForTTY(dev_path);
122
123 StartIoOwner(thread_stack_size);
124 }
125
139 LinuxUART(const std::string& vid, const std::string& pid,
140 unsigned int baudrate = 115200, Parity parity = Parity::NO_PARITY,
141 uint8_t data_bits = 8, uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
142 size_t buffer_size = 512, size_t thread_stack_size = 65536)
143 : LinuxUART(vid, pid, "", "", baudrate, parity, data_bits, stop_bits, tx_queue_size,
144 buffer_size, thread_stack_size)
145 {
146 }
147
163 LinuxUART(const std::string& vid, const std::string& pid,
164 const std::string& control_interface_name, unsigned int baudrate = 115200,
165 Parity parity = Parity::NO_PARITY, uint8_t data_bits = 8,
166 uint8_t stop_bits = 1, uint32_t tx_queue_size = 5, size_t buffer_size = 512,
167 size_t thread_stack_size = 65536)
168 : LinuxUART(vid, pid, control_interface_name, "", baudrate, parity, data_bits,
169 stop_bits, tx_queue_size, buffer_size, thread_stack_size)
170 {
171 }
172
192 LinuxUART(const std::string& vid, const std::string& pid,
193 const std::string& control_interface_name, const std::string& serial,
194 unsigned int baudrate = 115200, Parity parity = Parity::NO_PARITY,
195 uint8_t data_bits = 8, uint8_t stop_bits = 1, uint32_t tx_queue_size = 5,
196 size_t buffer_size = 512, size_t thread_stack_size = 65536)
197 : UART(&_read_port, &_write_port),
198 rx_buff_(new uint8_t[buffer_size]),
199 buff_size_(buffer_size),
200 config_{baudrate, parity, data_bits, stop_bits},
201 _read_port(buffer_size, *this),
202 _write_port(tx_queue_size, buffer_size)
203 {
204 ASSERT(tx_queue_size > 0);
205 ASSERT(buff_size_ > 0);
206 while (!FindUSBTTYByVidPid(vid, pid, control_interface_name, serial, device_path_))
207 {
208 XR_LOG_WARN(
209 "Cannot find USB TTY device with VID=%s PID=%s SERIAL=%s CONTROL_INTERFACE=%s, "
210 "retrying...",
211 vid.c_str(), pid.c_str(), serial.empty() ? "*" : serial.c_str(),
212 control_interface_name.empty() ? "*" : control_interface_name.c_str());
213 Thread::Sleep(100);
214 }
215
216 XR_LOG_PASS("Found USB TTY: %s", device_path_.c_str());
217
218 if (std::filesystem::exists(device_path_) == false)
219 {
220 XR_LOG_ERROR("Cannot find UART device: %s", device_path_.c_str());
221 REQUIRE(false);
222 return;
223 }
224
225 device_path_ = GetByPathForTTY(device_path_);
226
227 StartIoOwner(thread_stack_size);
228 }
229
236 std::string GetByPathForTTY(const std::string& tty_name)
237 {
238 const std::string BASE = "/dev/serial/by-path";
239 if (strncmp(tty_name.c_str(), BASE.c_str(), BASE.length()) == 0 ||
240 !std::filesystem::exists(BASE))
241 {
242 return tty_name;
243 }
244 for (const auto& entry : std::filesystem::directory_iterator(BASE))
245 {
246 std::error_code ec;
247 const auto full = std::filesystem::canonical(entry.path(), ec);
248 if (ec)
249 {
250 continue;
251 }
252 if (full == tty_name)
253 {
254 return entry.path().string(); // 返回稳定链接 / Return the stable link.
255 }
256 }
257 return tty_name; // 保留原路径 / Keep the original path.
258 }
259
264 static bool FindUSBTTYByVidPid(const std::string& target_vid,
265 const std::string& target_pid, std::string& tty_path)
266 {
267 return FindUSBTTYByVidPid(target_vid, target_pid, "", "", tty_path);
268 }
269
274 static bool FindUSBTTYByVidPid(const std::string& target_vid,
275 const std::string& target_pid,
276 const std::string& target_control_interface_name,
277 std::string& tty_path)
278 {
279 return FindUSBTTYByVidPid(target_vid, target_pid, target_control_interface_name, "",
280 tty_path);
281 }
282
287 static bool FindUSBTTYByVidPid(const std::string& target_vid,
288 const std::string& target_pid,
289 const std::string& target_control_interface_name,
290 const std::string& target_serial, std::string& tty_path)
291 {
292 struct udev* udev = udev_new();
293 if (!udev)
294 {
295 XR_LOG_ERROR("Cannot create udev context");
296 return false;
297 }
298
299 struct udev_enumerate* enumerate = udev_enumerate_new(udev);
300 udev_enumerate_add_match_subsystem(enumerate, "tty");
301 udev_enumerate_scan_devices(enumerate);
302
303 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
304 struct udev_list_entry* entry = nullptr;
305 std::vector<std::string> matches;
306
307 udev_list_entry_foreach(entry, devices)
308 {
309 const char* path = udev_list_entry_get_name(entry);
310 struct udev_device* tty_dev = udev_device_new_from_syspath(udev, path);
311 if (!tty_dev)
312 {
313 continue;
314 }
315
316 struct udev_device* usb_dev =
317 udev_device_get_parent_with_subsystem_devtype(tty_dev, "usb", "usb_device");
318 struct udev_device* usb_interface =
319 udev_device_get_parent_with_subsystem_devtype(tty_dev, "usb", "usb_interface");
320
321 if (usb_dev)
322 {
323 const char* vid = udev_device_get_sysattr_value(usb_dev, "idVendor");
324 const char* pid = udev_device_get_sysattr_value(usb_dev, "idProduct");
325 const char* serial = udev_device_get_sysattr_value(usb_dev, "serial");
326 const char* control_interface_name = nullptr;
327 if (usb_interface)
328 {
329 // Linux cdc_acm 的 tty 父节点对应 CDC 控制接口。
330 // The parent of a Linux cdc_acm TTY is the CDC control interface.
331 control_interface_name =
332 udev_device_get_sysattr_value(usb_interface, "interface");
333 }
334
335 if (vid && pid && target_vid == vid && target_pid == pid &&
336 (target_serial.empty() || (serial && target_serial == serial)) &&
337 (target_control_interface_name.empty() ||
338 (control_interface_name &&
339 target_control_interface_name == control_interface_name)))
340 {
341 const char* devnode = udev_device_get_devnode(tty_dev);
342 if (devnode)
343 {
344 matches.emplace_back(devnode);
345 }
346 }
347 }
348
349 udev_device_unref(tty_dev);
350 }
351
352 udev_enumerate_unref(enumerate);
353 udev_unref(udev);
354 if (matches.empty())
355 {
356 return false;
357 }
358
359 std::sort(matches.begin(), matches.end());
360 tty_path = matches.front();
361
362 if (matches.size() > 1)
363 {
364 XR_LOG_WARN(
365 "Multiple USB TTY devices found with VID=%s PID=%s SERIAL=%s "
366 "CONTROL_INTERFACE=%s, using %s. Specify serial or control interface name to "
367 "disambiguate.",
368 target_vid.c_str(), target_pid.c_str(),
369 target_serial.empty() ? "*" : target_serial.c_str(),
370 target_control_interface_name.empty() ? "*"
371 : target_control_interface_name.c_str(),
372 tty_path.c_str());
373 }
374
375 return true;
376 }
377
384 void SetLowLatency(int fd)
385 {
386 struct serial_struct serinfo{};
387 if (ioctl(fd, TIOCGSERIAL, &serinfo) != 0)
388 {
389 return;
390 }
391 serinfo.flags |= ASYNC_LOW_LATENCY;
392 ioctl(fd, TIOCSSERIAL, &serinfo);
393 }
394
406 ErrorCode SetConfig(UART::Configuration config, bool = false) override
407 {
408 if (ValidateConfig(config) != ErrorCode::OK)
409 {
410 return ErrorCode::ARG_ERR;
411 }
412
413 uint8_t expected = static_cast<uint8_t>(ConfigState::EMPTY);
414 if (!config_state_.compare_exchange_strong(
415 expected, static_cast<uint8_t>(ConfigState::RESERVED),
416 std::memory_order_acq_rel, std::memory_order_acquire))
417 {
418 return ErrorCode::BUSY;
419 }
420
421 requested_config_ = config;
422 config_state_.store(static_cast<uint8_t>(ConfigState::PUBLISHED),
423 std::memory_order_release);
424 NotifyIoOwner();
425 return ErrorCode::OK;
426 }
427
428 private:
430 static ErrorCode ValidateConfig(const UART::Configuration& config)
431 {
432 if (config.baudrate == 0U || (config.stop_bits != 1U && config.stop_bits != 2U) ||
433 config.data_bits < 5U || config.data_bits > 8U)
434 {
435 return ErrorCode::ARG_ERR;
436 }
437
438 switch (config.parity)
439 {
443 return ErrorCode::OK;
444 default:
445 return ErrorCode::ARG_ERR;
446 }
447 }
448
450 ErrorCode ApplyConfig(int fd, const UART::Configuration& config, bool flush)
451 {
452 struct termios2 tio{};
453 if (ioctl(fd, TCGETS2, &tio) != 0)
454 {
455 return ErrorCode::INIT_ERR;
456 }
457
458 tio.c_cflag &= ~CBAUD;
459 tio.c_cflag |= BOTHER;
460 tio.c_ispeed = config.baudrate;
461 tio.c_ospeed = config.baudrate;
462
463 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
464#ifdef IUCLC
465 | IUCLC
466#endif
467 );
468
469 tio.c_oflag &= ~(OPOST
470#ifdef ONLCR
471 | ONLCR
472#endif
473#ifdef OCRNL
474 | OCRNL
475#endif
476#ifdef ONOCR
477 | ONOCR
478#endif
479#ifdef ONLRET
480 | ONLRET
481#endif
482 );
483
484 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
485
486 tio.c_cflag &= ~CSIZE;
487 switch (config.data_bits)
488 {
489 case 5:
490 tio.c_cflag |= CS5;
491 break;
492 case 6:
493 tio.c_cflag |= CS6;
494 break;
495 case 7:
496 tio.c_cflag |= CS7;
497 break;
498 case 8:
499 tio.c_cflag |= CS8;
500 break;
501 default:
502 return ErrorCode::ARG_ERR;
503 }
504
505 tio.c_cflag &= ~CSTOPB;
506 if (config.stop_bits == 2U)
507 {
508 tio.c_cflag |= CSTOPB;
509 }
510
511 tio.c_cflag &= ~(PARENB | PARODD);
512 switch (config.parity)
513 {
515 break;
517 tio.c_cflag |= PARENB;
518 break;
520 tio.c_cflag |= PARENB | PARODD;
521 break;
522 }
523
524 tio.c_cflag &= ~CRTSCTS;
525 tio.c_cflag |= (CLOCAL | CREAD);
526 tio.c_cc[VTIME] = 0;
527 tio.c_cc[VMIN] = 1;
528
529 if (ioctl(fd, TCSETS2, &tio) != 0)
530 {
531 return ErrorCode::INIT_ERR;
532 }
533
534 SetLowLatency(fd);
535 if (flush && tcflush(fd, TCIOFLUSH) != 0)
536 {
537 return ErrorCode::INIT_ERR;
538 }
539 return ErrorCode::OK;
540 }
541
543 static void WriteFun(WritePort& port, bool)
544 {
545 auto* uart = LibXR::ContainerOf(&port, &LinuxUART::_write_port);
546 uart->NotifyIoOwner();
547 }
548
549 static constexpr int RECONNECT_DELAY_MS = 1000;
550 static constexpr int CONFIG_DRAIN_POLL_MS = 10;
551
554 void StartIoOwner(size_t thread_stack_size)
555 {
556 ASSERT(ValidateConfig(config_) == ErrorCode::OK);
557
558 wake_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
559 REQUIRE(wake_fd_ >= 0);
560
561 _write_port = WriteFun;
562 io_thread_.Create<LinuxUART*>(
563 this, [](LinuxUART* self) { self->IoLoop(); }, "io_uart", thread_stack_size,
565
566 ErrorCode wait_result = ErrorCode::TIMEOUT;
567 do
568 {
569 wait_result = startup_sem_.Wait();
570 } while (wait_result == ErrorCode::TIMEOUT);
571 REQUIRE(wait_result == ErrorCode::OK);
572 }
573
575 void NotifyIoOwner()
576 {
577 DEV_ASSERT(wake_fd_ >= 0);
578 const uint64_t value = 1U;
579 for (;;)
580 {
581 const ssize_t written = write(wake_fd_, &value, sizeof(value));
582 if (written == static_cast<ssize_t>(sizeof(value)))
583 {
584 return;
585 }
586 if (written < 0 && errno == EINTR)
587 {
588 continue;
589 }
590 if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
591 {
592 return;
593 }
594 REQUIRE(false);
595 }
596 }
597
599 void DrainWakeFd()
600 {
601 uint64_t value = 0U;
602 for (;;)
603 {
604 const ssize_t bytes = read(wake_fd_, &value, sizeof(value));
605 if (bytes == static_cast<ssize_t>(sizeof(value)))
606 {
607 continue;
608 }
609 if (bytes < 0 && errno == EINTR)
610 {
611 continue;
612 }
613 if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
614 {
615 return;
616 }
617 REQUIRE(false);
618 }
619 }
620
623 int OpenDevice(const Configuration& config, bool reconnect, ErrorCode& result)
624 {
625 const int fd = open(device_path_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
626 if (fd < 0)
627 {
628 result = ErrorCode::INIT_ERR;
629 XR_LOG_WARN("Cannot open UART device: %s", device_path_.c_str());
630 return -1;
631 }
632
633 result = ApplyConfig(fd, config, true);
634 if (result != ErrorCode::OK)
635 {
636 XR_LOG_WARN("Cannot configure UART device: %s", device_path_.c_str());
637 (void)close(fd);
638 return -1;
639 }
640
641 XR_LOG_PASS("%s UART device: %s", reconnect ? "Reopen" : "Open",
642 device_path_.c_str());
643 return fd;
644 }
645
648 void Disconnect(int& fd, bool fail_front)
649 {
650 if (fd >= 0)
651 {
652 (void)close(fd);
653 fd = -1;
654 }
655
656 if (fail_front)
657 {
658 auto queue = _write_port.GetWriteQueue(false);
659 if (!queue.Empty())
660 {
661 queue.FailFront(ErrorCode::FAILED);
662 }
663 tx_front_unreplayable_ = false;
664 }
665 }
666
669 size_t WriteSpans(int fd, const uint8_t* first, size_t first_size,
670 const uint8_t* second, size_t second_size, bool& fatal_error)
671 {
672 std::array<iovec, 2> spans{};
673 spans[0] = iovec{const_cast<uint8_t*>(first), first_size};
674 spans[1] = iovec{const_cast<uint8_t*>(second), second_size};
675 const int span_count = second_size == 0U ? 1 : 2;
676
677 for (;;)
678 {
679 const ssize_t written = writev(fd, spans.data(), span_count);
680 if (written > 0)
681 {
682 const size_t accepted = static_cast<size_t>(written);
683 DEV_ASSERT(accepted <= first_size + second_size);
684 return accepted;
685 }
686 if (written == 0)
687 {
688 return 0U;
689 }
690 if (errno == EINTR)
691 {
692 continue;
693 }
694 if (errno == EAGAIN || errno == EWOULDBLOCK)
695 {
696 return 0U;
697 }
698
699 fatal_error = true;
700 return 0U;
701 }
702 }
703
705 bool HasPendingTx()
706 {
707 auto queue = _write_port.GetWriteQueue(false);
708 return !queue.Empty();
709 }
710
713 bool PumpTx(int& fd)
714 {
715 bool fatal_error = false;
716 size_t offered = 0U;
717 size_t accepted = 0U;
718 {
719 auto queue = _write_port.GetWriteQueue(false);
720 offered = queue.AvailableSize();
721 if (offered != 0U)
722 {
723 accepted = queue.PopWithWriter(
724 offered,
725 [this, fd, &fatal_error](const uint8_t* first, size_t first_size,
726 const uint8_t* second, size_t second_size) -> size_t
727 {
728 return WriteSpans(fd, first, first_size, second, second_size, fatal_error);
729 });
730 }
731 }
732
733 if (offered == 0U)
734 {
735 return false;
736 }
737
738 if (fatal_error)
739 {
740 XR_LOG_WARN("Cannot write UART device: %s", device_path_.c_str());
741 Disconnect(fd, true);
742 return false;
743 }
744
745 if (accepted == 0U)
746 {
747 // 暂时无法发送时,保留此前部分发送的记录。
748 // Preserve prior partial progress while waiting for writability.
749 return true;
750 }
751 tx_front_unreplayable_ = accepted < offered;
752 return accepted < offered || HasPendingTx();
753 }
754
756 bool DrainRx(int& fd, bool terminal = false)
757 {
758 size_t budget = terminal ? static_cast<size_t>(-1) : buff_size_;
759 while (budget > 0U)
760 {
761 const size_t empty_size = _read_port.EmptySize();
762 if (empty_size == 0U)
763 {
764 return true;
765 }
766
767 const size_t read_size = std::min(budget, empty_size);
768 auto queue = _read_port.GetReadQueue(false);
769 const ssize_t bytes = read(fd, rx_buff_, read_size);
770 if (bytes > 0)
771 {
772 [[maybe_unused]] const auto push_batch_result =
773 queue.PushBatch(rx_buff_, static_cast<size_t>(bytes));
774 DEV_ASSERT(push_batch_result == ErrorCode::OK);
775 queue.Publish();
776 budget -= static_cast<size_t>(bytes);
777 continue;
778 }
779
780 queue.Publish();
781 if (bytes < 0 && errno == EINTR)
782 {
783 continue;
784 }
785 if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
786 {
787 return true;
788 }
789 if (bytes == 0)
790 {
791 XR_LOG_WARN("UART device closed: %s", device_path_.c_str());
792 }
793 else
794 {
795 XR_LOG_WARN("Cannot read UART device: %s", device_path_.c_str());
796 }
797 Disconnect(fd, tx_front_unreplayable_);
798 return false;
799 }
800 return true;
801 }
802
804 bool ConfigRequested() const
805 {
806 return config_state_.load(std::memory_order_acquire) ==
807 static_cast<uint8_t>(ConfigState::PUBLISHED);
808 }
809
812 void ServiceConfig(int& fd, Configuration& current_config)
813 {
814 if (!ConfigRequested() || tx_front_unreplayable_)
815 {
816 return;
817 }
818
819 int queued_bytes = 0;
820 if (ioctl(fd, TIOCOUTQ, &queued_bytes) != 0)
821 {
822 XR_LOG_WARN("Cannot inspect UART output queue: %s", device_path_.c_str());
823 Disconnect(fd, false);
824 return;
825 }
826 if (queued_bytes > 0)
827 {
828 return;
829 }
830
831 const Configuration requested = requested_config_;
832 const ErrorCode result = ApplyConfig(fd, requested, false);
833 if (result != ErrorCode::OK)
834 {
835 XR_LOG_WARN("Cannot apply UART configuration: %s", device_path_.c_str());
836 Disconnect(fd, false);
837 return;
838 }
839
840 current_config = requested;
841 config_state_.store(static_cast<uint8_t>(ConfigState::EMPTY),
842 std::memory_order_release);
843 }
844
846 void WaitForReconnectWake()
847 {
848 pollfd wake_poll{};
849 wake_poll.fd = wake_fd_;
850 wake_poll.events = POLLIN;
851
852 int result = 0;
853 do
854 {
855 result = poll(&wake_poll, 1U, RECONNECT_DELAY_MS);
856 } while (result < 0 && errno == EINTR);
857
858 REQUIRE(result >= 0);
859 if (result > 0)
860 {
861 DrainWakeFd();
862 }
863 }
864
866 void IoLoop()
867 {
868 Configuration current_config = config_;
869 ErrorCode open_result = ErrorCode::INIT_ERR;
870 int fd = OpenDevice(current_config, false, open_result);
871
872 startup_sem_.Post();
873
874 while (true)
875 {
876 if (fd < 0)
877 {
878 const bool config_pending = ConfigRequested();
879 const Configuration target_config =
880 config_pending ? requested_config_ : current_config;
881 fd = OpenDevice(target_config, true, open_result);
882 if (fd >= 0)
883 {
884 current_config = target_config;
885 if (config_pending)
886 {
887 config_state_.store(static_cast<uint8_t>(ConfigState::EMPTY),
888 std::memory_order_release);
889 }
890 continue;
891 }
892
893 WaitForReconnectWake();
894 continue;
895 }
896
897 ServiceConfig(fd, current_config);
898 if (fd < 0)
899 {
900 continue;
901 }
902
903 const bool tx_waiting =
904 (!ConfigRequested() || tx_front_unreplayable_) && PumpTx(fd);
905 if (fd < 0)
906 {
907 continue;
908 }
909 ServiceConfig(fd, current_config);
910 if (fd < 0)
911 {
912 continue;
913 }
914
915 std::array<pollfd, 2> poll_fds{};
916 poll_fds[0].fd = fd;
917 if (_read_port.EmptySize() != 0U)
918 {
919 poll_fds[0].events |= POLLIN;
920 }
921 if (tx_waiting)
922 {
923 poll_fds[0].events |= POLLOUT;
924 }
925 // 无收发需求时只等唤醒,避免满接收队列遇到 HUP 后空转。
926 // With no I/O demand, wait only for wakes; a full RX queue must not spin on HUP.
927 if (poll_fds[0].events == 0)
928 {
929 poll_fds[0].fd = -1;
930 }
931
932 poll_fds[1].fd = wake_fd_;
933 poll_fds[1].events = POLLIN;
934 const int timeout = ConfigRequested() && !tx_waiting ? CONFIG_DRAIN_POLL_MS : -1;
935
936 int poll_result = 0;
937 do
938 {
939 poll_result = poll(poll_fds.data(), poll_fds.size(), timeout);
940 } while (poll_result < 0 && errno == EINTR);
941
942 if (poll_result < 0)
943 {
944 XR_LOG_WARN("Cannot poll UART device: %s", device_path_.c_str());
945 Disconnect(fd, tx_front_unreplayable_);
946 continue;
947 }
948 if (poll_result == 0)
949 {
950 continue;
951 }
952
953 if ((poll_fds[1].revents & POLLIN) != 0)
954 {
955 DrainWakeFd();
956 }
957
958 const bool hung_up = (poll_fds[0].revents & POLLHUP) != 0;
959 if (((poll_fds[0].revents & POLLIN) != 0 || hung_up) && !DrainRx(fd, hung_up))
960 {
961 continue;
962 }
963 if (fd < 0)
964 {
965 continue;
966 }
967 if ((poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0)
968 {
969 XR_LOG_WARN("UART device disconnected: %s", device_path_.c_str());
970 Disconnect(fd, tx_front_unreplayable_);
971 continue;
972 }
973 }
974 }
975
977 std::string device_path_;
979 Thread io_thread_;
981 uint8_t* rx_buff_ = nullptr;
983 size_t buff_size_ = 0;
985 Configuration config_{};
987 Configuration requested_config_{};
989 int wake_fd_ = -1;
991 Semaphore startup_sem_;
993 std::atomic<uint8_t> config_state_{static_cast<uint8_t>(ConfigState::EMPTY)};
995 bool tx_front_unreplayable_ = false;
996
997 Detail::LinuxUARTReadPort _read_port; // NOLINT
998 WritePort _write_port; // NOLINT
999};
1000
1002{
1003 owner_.NotifyIoOwner();
1004}
1005
1006} // namespace LibXR
通知串口线程恢复接收的读端口 / Read port that wakes UART reception.
void OnReadQueueSpaceAvailable(bool) override
释放空间后唤醒串口线程 / Wake the UART thread after space is freed.
LinuxUARTReadPort(size_t size, LinuxUART &owner)
构造接收端口并关联串口 / Construct the RX port associated with its UART.
使用单个 I/O 线程的 Linux 串口 / Linux UART with one I/O thread.
void SetLowLatency(int fd)
尝试启用设备低延迟选项 / Try to enable the device low-latency option.
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)
通过设备路径构造串口 / Construct a UART by device path.
LinuxUART(const std::string &vid, const std::string &pid, const std::string &control_interface_name, 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 接口和序列号构造串口 / Construct a UART by USB interface and serial.
static bool FindUSBTTYByVidPid(const std::string &target_vid, const std::string &target_pid, std::string &tty_path)
查找匹配的 USB 串口设备 / Find a matching USB TTY device.
LinuxUART(const std::string &vid, const std::string &pid, const std::string &control_interface_name, 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 控制接口名称构造串口 / Construct a UART by USB control interface.
std::string GetByPathForTTY(const std::string &tty_name)
查找设备的稳定路径 / Find a stable path for a TTY.
ErrorCode SetConfig(UART::Configuration config, bool=false) override
提交串口配置请求 / Submit a UART configuration request.
static bool FindUSBTTYByVidPid(const std::string &target_vid, const std::string &target_pid, const std::string &target_control_interface_name, const std::string &target_serial, std::string &tty_path)
查找匹配的 USB 串口设备 / Find a matching USB TTY device.
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 构造串口 / Construct a UART by USB VID/PID.
static bool FindUSBTTYByVidPid(const std::string &target_vid, const std::string &target_pid, const std::string &target_control_interface_name, std::string &tty_path)
查找匹配的 USB 串口设备 / Find a matching USB TTY device.
基于 SPSC 字节队列的读端口 / Read endpoint backed by an SPSC byte queue.
Definition read_port.hpp:30
size_t EmptySize() const
获取当前空闲空间 / Get current free space.
Definition read_port.cpp:52
ReadQueue GetReadQueue(bool in_isr=false)
获取后端接收入队接口 / Obtain the backend RX producer interface.
Definition read_port.cpp:14
void Post()
释放(增加)信号量 Releases (increments) the semaphore
Definition semaphore.cpp:43
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:49
@ 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:74
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
Parity
奇偶校验模式 / Parity mode
Definition uart.hpp:29
@ NO_PARITY
无校验 / No parity
@ ODD
奇校验 / Odd parity
@ EVEN
偶校验 / Even parity
bool Empty() const
检查本接口是否还有数据 / Check whether this interface has remaining data.
size_t AvailableSize() const
获取当前队头的剩余长度 / Get the current front remainder.
WriteQueue GetWriteQueue(bool in_isr=false)
获取已提交队头的出队接口 / Obtain a consumer interface for the released front.
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ TIMEOUT
超时 / Timeout
@ INIT_ERR
初始化错误 / Initialization error
@ BUSY
忙碌 / Busy
@ FAILED
操作失败 / Operation failed
@ EMPTY
为空 / Empty
@ OK
操作成功 / Operation successful
@ ARG_ERR
参数错误 / Argument error
void(* WriteFun)(WritePort &port, bool in_isr)
写入进度通知函数类型 / Write progress notification function type.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针 Recover the owning object pointer from a member pointer
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:53
uint32_t baudrate
波特率 / Baud rate
Definition uart.hpp:45