78 enum class ConfigState : uint8_t
101 LinuxUART(
const char* dev_path,
unsigned int baudrate = 115200,
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)
112 ASSERT(buff_size_ > 0);
113 ASSERT(tx_queue_size > 0);
115 while (!std::filesystem::exists(dev_path))
117 XR_LOG_WARN(
"Cannot find UART device: %s, retrying...", dev_path);
123 StartIoOwner(thread_stack_size);
139 LinuxUART(
const std::string& vid,
const std::string& pid,
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)
163 LinuxUART(
const std::string& vid,
const std::string& pid,
164 const std::string& control_interface_name,
unsigned int baudrate = 115200,
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)
192 LinuxUART(
const std::string& vid,
const std::string& pid,
193 const std::string& control_interface_name,
const std::string& serial,
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)
204 ASSERT(tx_queue_size > 0);
205 ASSERT(buff_size_ > 0);
209 "Cannot find USB TTY device with VID=%s PID=%s SERIAL=%s CONTROL_INTERFACE=%s, "
211 vid.c_str(), pid.c_str(), serial.empty() ?
"*" : serial.c_str(),
212 control_interface_name.empty() ?
"*" : control_interface_name.c_str());
216 XR_LOG_PASS(
"Found USB TTY: %s", device_path_.c_str());
218 if (std::filesystem::exists(device_path_) ==
false)
220 XR_LOG_ERROR(
"Cannot find UART device: %s", device_path_.c_str());
227 StartIoOwner(thread_stack_size);
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))
244 for (
const auto& entry : std::filesystem::directory_iterator(BASE))
247 const auto full = std::filesystem::canonical(entry.path(), ec);
252 if (full == tty_name)
254 return entry.path().string();
265 const std::string& target_pid, std::string& tty_path)
275 const std::string& target_pid,
276 const std::string& target_control_interface_name,
277 std::string& tty_path)
288 const std::string& target_pid,
289 const std::string& target_control_interface_name,
290 const std::string& target_serial, std::string& tty_path)
292 struct udev* udev = udev_new();
295 XR_LOG_ERROR(
"Cannot create udev context");
299 struct udev_enumerate* enumerate = udev_enumerate_new(udev);
300 udev_enumerate_add_match_subsystem(enumerate,
"tty");
301 udev_enumerate_scan_devices(enumerate);
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;
307 udev_list_entry_foreach(entry, devices)
309 const char* path = udev_list_entry_get_name(entry);
310 struct udev_device* tty_dev = udev_device_new_from_syspath(udev, path);
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");
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;
331 control_interface_name =
332 udev_device_get_sysattr_value(usb_interface,
"interface");
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)))
341 const char* devnode = udev_device_get_devnode(tty_dev);
344 matches.emplace_back(devnode);
349 udev_device_unref(tty_dev);
352 udev_enumerate_unref(enumerate);
359 std::sort(matches.begin(), matches.end());
360 tty_path = matches.front();
362 if (matches.size() > 1)
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 "
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(),
386 struct serial_struct serinfo{};
387 if (ioctl(fd, TIOCGSERIAL, &serinfo) != 0)
391 serinfo.flags |= ASYNC_LOW_LATENCY;
392 ioctl(fd, TIOCSSERIAL, &serinfo);
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))
421 requested_config_ = config;
422 config_state_.store(
static_cast<uint8_t
>(ConfigState::PUBLISHED),
423 std::memory_order_release);
450 ErrorCode ApplyConfig(
int fd,
const UART::Configuration& config,
bool flush)
452 struct termios2 tio{};
453 if (ioctl(fd, TCGETS2, &tio) != 0)
458 tio.c_cflag &= ~CBAUD;
459 tio.c_cflag |= BOTHER;
460 tio.c_ispeed = config.baudrate;
461 tio.c_ospeed = config.baudrate;
463 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
469 tio.c_oflag &= ~(OPOST
484 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
486 tio.c_cflag &= ~CSIZE;
487 switch (config.data_bits)
505 tio.c_cflag &= ~CSTOPB;
506 if (config.stop_bits == 2U)
508 tio.c_cflag |= CSTOPB;
511 tio.c_cflag &= ~(PARENB | PARODD);
512 switch (config.parity)
517 tio.c_cflag |= PARENB;
520 tio.c_cflag |= PARENB | PARODD;
524 tio.c_cflag &= ~CRTSCTS;
525 tio.c_cflag |= (CLOCAL | CREAD);
529 if (ioctl(fd, TCSETS2, &tio) != 0)
535 if (flush && tcflush(fd, TCIOFLUSH) != 0)
543 static void WriteFun(WritePort& port,
bool)
546 uart->NotifyIoOwner();
549 static constexpr int RECONNECT_DELAY_MS = 1000;
550 static constexpr int CONFIG_DRAIN_POLL_MS = 10;
554 void StartIoOwner(
size_t thread_stack_size)
558 wake_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
559 REQUIRE(wake_fd_ >= 0);
563 this, [](
LinuxUART* self) { self->IoLoop(); },
"io_uart", thread_stack_size,
569 wait_result = startup_sem_.
Wait();
577 DEV_ASSERT(wake_fd_ >= 0);
578 const uint64_t value = 1U;
581 const ssize_t written = write(wake_fd_, &value,
sizeof(value));
582 if (written ==
static_cast<ssize_t
>(
sizeof(value)))
586 if (written < 0 && errno == EINTR)
590 if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
604 const ssize_t bytes = read(wake_fd_, &value,
sizeof(value));
605 if (bytes ==
static_cast<ssize_t
>(
sizeof(value)))
609 if (bytes < 0 && errno == EINTR)
613 if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
623 int OpenDevice(
const Configuration& config,
bool reconnect,
ErrorCode& result)
625 const int fd = open(device_path_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
629 XR_LOG_WARN(
"Cannot open UART device: %s", device_path_.c_str());
633 result = ApplyConfig(fd, config,
true);
636 XR_LOG_WARN(
"Cannot configure UART device: %s", device_path_.c_str());
641 XR_LOG_PASS(
"%s UART device: %s", reconnect ?
"Reopen" :
"Open",
642 device_path_.c_str());
648 void Disconnect(
int& fd,
bool fail_front)
663 tx_front_unreplayable_ =
false;
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)
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;
679 const ssize_t written = writev(fd, spans.data(), span_count);
682 const size_t accepted =
static_cast<size_t>(written);
683 DEV_ASSERT(accepted <= first_size + second_size);
694 if (errno == EAGAIN || errno == EWOULDBLOCK)
708 return !queue.
Empty();
715 bool fatal_error =
false;
717 size_t accepted = 0U;
723 accepted = queue.PopWithWriter(
725 [
this, fd, &fatal_error](
const uint8_t* first,
size_t first_size,
726 const uint8_t* second,
size_t second_size) ->
size_t
728 return WriteSpans(fd, first, first_size, second, second_size, fatal_error);
740 XR_LOG_WARN(
"Cannot write UART device: %s", device_path_.c_str());
741 Disconnect(fd,
true);
751 tx_front_unreplayable_ = accepted < offered;
752 return accepted < offered || HasPendingTx();
756 bool DrainRx(
int& fd,
bool terminal =
false)
758 size_t budget = terminal ?
static_cast<size_t>(-1) : buff_size_;
761 const size_t empty_size = _read_port.
EmptySize();
762 if (empty_size == 0U)
767 const size_t read_size = std::min(budget, empty_size);
769 const ssize_t bytes = read(fd, rx_buff_, read_size);
772 [[maybe_unused]]
const auto push_batch_result =
773 queue.PushBatch(rx_buff_,
static_cast<size_t>(bytes));
776 budget -=
static_cast<size_t>(bytes);
781 if (bytes < 0 && errno == EINTR)
785 if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
791 XR_LOG_WARN(
"UART device closed: %s", device_path_.c_str());
795 XR_LOG_WARN(
"Cannot read UART device: %s", device_path_.c_str());
797 Disconnect(fd, tx_front_unreplayable_);
804 bool ConfigRequested()
const
806 return config_state_.load(std::memory_order_acquire) ==
807 static_cast<uint8_t
>(ConfigState::PUBLISHED);
812 void ServiceConfig(
int& fd, Configuration& current_config)
814 if (!ConfigRequested() || tx_front_unreplayable_)
819 int queued_bytes = 0;
820 if (ioctl(fd, TIOCOUTQ, &queued_bytes) != 0)
822 XR_LOG_WARN(
"Cannot inspect UART output queue: %s", device_path_.c_str());
823 Disconnect(fd,
false);
826 if (queued_bytes > 0)
831 const Configuration requested = requested_config_;
832 const ErrorCode result = ApplyConfig(fd, requested,
false);
835 XR_LOG_WARN(
"Cannot apply UART configuration: %s", device_path_.c_str());
836 Disconnect(fd,
false);
840 current_config = requested;
841 config_state_.store(
static_cast<uint8_t
>(ConfigState::EMPTY),
842 std::memory_order_release);
846 void WaitForReconnectWake()
849 wake_poll.fd = wake_fd_;
850 wake_poll.events = POLLIN;
855 result = poll(&wake_poll, 1U, RECONNECT_DELAY_MS);
856 }
while (result < 0 && errno == EINTR);
858 REQUIRE(result >= 0);
868 Configuration current_config = config_;
870 int fd = OpenDevice(current_config,
false, open_result);
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);
884 current_config = target_config;
887 config_state_.store(
static_cast<uint8_t
>(ConfigState::EMPTY),
888 std::memory_order_release);
893 WaitForReconnectWake();
897 ServiceConfig(fd, current_config);
903 const bool tx_waiting =
904 (!ConfigRequested() || tx_front_unreplayable_) && PumpTx(fd);
909 ServiceConfig(fd, current_config);
915 std::array<pollfd, 2> poll_fds{};
919 poll_fds[0].events |= POLLIN;
923 poll_fds[0].events |= POLLOUT;
927 if (poll_fds[0].events == 0)
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;
939 poll_result = poll(poll_fds.data(), poll_fds.size(), timeout);
940 }
while (poll_result < 0 && errno == EINTR);
944 XR_LOG_WARN(
"Cannot poll UART device: %s", device_path_.c_str());
945 Disconnect(fd, tx_front_unreplayable_);
948 if (poll_result == 0)
953 if ((poll_fds[1].revents & POLLIN) != 0)
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))
967 if ((poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0)
969 XR_LOG_WARN(
"UART device disconnected: %s", device_path_.c_str());
970 Disconnect(fd, tx_front_unreplayable_);
977 std::string device_path_;
981 uint8_t* rx_buff_ =
nullptr;
983 size_t buff_size_ = 0;
985 Configuration config_{};
987 Configuration requested_config_{};
991 Semaphore startup_sem_;
993 std::atomic<uint8_t> config_state_{
static_cast<uint8_t
>(ConfigState::EMPTY)};
995 bool tx_front_unreplayable_ =
false;
997 Detail::LinuxUARTReadPort _read_port;
998 WritePort _write_port;