// Copyright 2023 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_stream_uart_linux/stream.h" #include #include #include #include #include #include "pw_log/log.h" namespace pw::stream { Result BaudRateToSpeed(uint32_t baud_rate) { switch (baud_rate) { case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return Status::InvalidArgument(); } } Status UartStreamLinux::Open(const char* path, Config config) { std::optional speed; if (config.baud_rate.has_value()) { const auto speed_result = BaudRateToSpeed(*config.baud_rate); if (!speed_result.ok()) { PW_LOG_ERROR("Unsupported baud rate: %" PRIu32, *config.baud_rate); return speed_result.status(); } speed = speed_result.value(); } if (fd_ != kInvalidFd) { PW_LOG_ERROR("UART device already open"); return Status::FailedPrecondition(); } fd_ = open(path, O_RDWR); if (fd_ < 0) { PW_LOG_ERROR( "Failed to open UART device '%s', %s", path, std::strerror(errno)); return Status::Unknown(); } struct termios tty; int result = tcgetattr(fd_, &tty); if (result < 0) { PW_LOG_ERROR("Failed to get TTY attributes for '%s', %s", path, std::strerror(errno)); return Status::Unknown(); } cfmakeraw(&tty); if (speed.has_value()) { result = cfsetspeed(&tty, *speed); if (result < 0) { PW_LOG_ERROR( "Failed to set TTY speed for '%s', %s", path, std::strerror(errno)); return Status::Unknown(); } } if (config.flow_control.has_value()) { if (*config.flow_control) { // Enable hardware flow control. tty.c_cflag |= (CRTSCTS); } else { // Disable hardware flow control. tty.c_cflag &= ~(CRTSCTS); } } result = tcsetattr(fd_, TCSANOW, &tty); if (result < 0) { PW_LOG_ERROR("Failed to set TTY attributes for '%s', %s", path, std::strerror(errno)); return Status::Unknown(); } return OkStatus(); } void UartStreamLinux::Close() { if (fd_ != kInvalidFd) { close(fd_); fd_ = kInvalidFd; } } Status UartStreamLinux::DoWrite(ConstByteSpan data) { const size_t size = data.size_bytes(); size_t written = 0; while (written < size) { int bytes = write(fd_, &data[written], size - written); if (bytes < 0) { PW_LOG_ERROR("Failed to write to UART, %s", std::strerror(errno)); return Status::Unknown(); } written += bytes; } return OkStatus(); } StatusWithSize UartStreamLinux::DoRead(ByteSpan dest) { int bytes = read(fd_, &dest[0], dest.size_bytes()); if (bytes < 0) { PW_LOG_ERROR("Failed to read from UART, %s", std::strerror(errno)); return StatusWithSize::Unknown(); } return StatusWithSize(bytes); } } // namespace pw::stream