1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_stream_uart_linux/stream.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <fcntl.h>
18*61c4878aSAndroid Build Coastguard Worker #include <termios.h>
19*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker #include <cerrno>
22*61c4878aSAndroid Build Coastguard Worker #include <cinttypes>
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
25*61c4878aSAndroid Build Coastguard Worker
26*61c4878aSAndroid Build Coastguard Worker namespace pw::stream {
27*61c4878aSAndroid Build Coastguard Worker
BaudRateToSpeed(uint32_t baud_rate)28*61c4878aSAndroid Build Coastguard Worker Result<speed_t> BaudRateToSpeed(uint32_t baud_rate) {
29*61c4878aSAndroid Build Coastguard Worker switch (baud_rate) {
30*61c4878aSAndroid Build Coastguard Worker case 9600:
31*61c4878aSAndroid Build Coastguard Worker return B9600;
32*61c4878aSAndroid Build Coastguard Worker case 19200:
33*61c4878aSAndroid Build Coastguard Worker return B19200;
34*61c4878aSAndroid Build Coastguard Worker case 38400:
35*61c4878aSAndroid Build Coastguard Worker return B38400;
36*61c4878aSAndroid Build Coastguard Worker case 57600:
37*61c4878aSAndroid Build Coastguard Worker return B57600;
38*61c4878aSAndroid Build Coastguard Worker case 115200:
39*61c4878aSAndroid Build Coastguard Worker return B115200;
40*61c4878aSAndroid Build Coastguard Worker case 230400:
41*61c4878aSAndroid Build Coastguard Worker return B230400;
42*61c4878aSAndroid Build Coastguard Worker case 460800:
43*61c4878aSAndroid Build Coastguard Worker return B460800;
44*61c4878aSAndroid Build Coastguard Worker case 500000:
45*61c4878aSAndroid Build Coastguard Worker return B500000;
46*61c4878aSAndroid Build Coastguard Worker case 576000:
47*61c4878aSAndroid Build Coastguard Worker return B576000;
48*61c4878aSAndroid Build Coastguard Worker case 921600:
49*61c4878aSAndroid Build Coastguard Worker return B921600;
50*61c4878aSAndroid Build Coastguard Worker case 1000000:
51*61c4878aSAndroid Build Coastguard Worker return B1000000;
52*61c4878aSAndroid Build Coastguard Worker case 1152000:
53*61c4878aSAndroid Build Coastguard Worker return B1152000;
54*61c4878aSAndroid Build Coastguard Worker case 1500000:
55*61c4878aSAndroid Build Coastguard Worker return B1500000;
56*61c4878aSAndroid Build Coastguard Worker case 2000000:
57*61c4878aSAndroid Build Coastguard Worker return B2000000;
58*61c4878aSAndroid Build Coastguard Worker case 2500000:
59*61c4878aSAndroid Build Coastguard Worker return B2500000;
60*61c4878aSAndroid Build Coastguard Worker case 3000000:
61*61c4878aSAndroid Build Coastguard Worker return B3000000;
62*61c4878aSAndroid Build Coastguard Worker case 3500000:
63*61c4878aSAndroid Build Coastguard Worker return B3500000;
64*61c4878aSAndroid Build Coastguard Worker case 4000000:
65*61c4878aSAndroid Build Coastguard Worker return B4000000;
66*61c4878aSAndroid Build Coastguard Worker default:
67*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
68*61c4878aSAndroid Build Coastguard Worker }
69*61c4878aSAndroid Build Coastguard Worker }
70*61c4878aSAndroid Build Coastguard Worker
Open(const char * path,Config config)71*61c4878aSAndroid Build Coastguard Worker Status UartStreamLinux::Open(const char* path, Config config) {
72*61c4878aSAndroid Build Coastguard Worker std::optional<speed_t> speed;
73*61c4878aSAndroid Build Coastguard Worker if (config.baud_rate.has_value()) {
74*61c4878aSAndroid Build Coastguard Worker const auto speed_result = BaudRateToSpeed(*config.baud_rate);
75*61c4878aSAndroid Build Coastguard Worker if (!speed_result.ok()) {
76*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Unsupported baud rate: %" PRIu32, *config.baud_rate);
77*61c4878aSAndroid Build Coastguard Worker return speed_result.status();
78*61c4878aSAndroid Build Coastguard Worker }
79*61c4878aSAndroid Build Coastguard Worker speed = speed_result.value();
80*61c4878aSAndroid Build Coastguard Worker }
81*61c4878aSAndroid Build Coastguard Worker
82*61c4878aSAndroid Build Coastguard Worker if (fd_ != kInvalidFd) {
83*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("UART device already open");
84*61c4878aSAndroid Build Coastguard Worker return Status::FailedPrecondition();
85*61c4878aSAndroid Build Coastguard Worker }
86*61c4878aSAndroid Build Coastguard Worker
87*61c4878aSAndroid Build Coastguard Worker fd_ = open(path, O_RDWR);
88*61c4878aSAndroid Build Coastguard Worker if (fd_ < 0) {
89*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR(
90*61c4878aSAndroid Build Coastguard Worker "Failed to open UART device '%s', %s", path, std::strerror(errno));
91*61c4878aSAndroid Build Coastguard Worker return Status::Unknown();
92*61c4878aSAndroid Build Coastguard Worker }
93*61c4878aSAndroid Build Coastguard Worker
94*61c4878aSAndroid Build Coastguard Worker struct termios tty;
95*61c4878aSAndroid Build Coastguard Worker int result = tcgetattr(fd_, &tty);
96*61c4878aSAndroid Build Coastguard Worker if (result < 0) {
97*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to get TTY attributes for '%s', %s",
98*61c4878aSAndroid Build Coastguard Worker path,
99*61c4878aSAndroid Build Coastguard Worker std::strerror(errno));
100*61c4878aSAndroid Build Coastguard Worker return Status::Unknown();
101*61c4878aSAndroid Build Coastguard Worker }
102*61c4878aSAndroid Build Coastguard Worker
103*61c4878aSAndroid Build Coastguard Worker cfmakeraw(&tty);
104*61c4878aSAndroid Build Coastguard Worker
105*61c4878aSAndroid Build Coastguard Worker if (speed.has_value()) {
106*61c4878aSAndroid Build Coastguard Worker result = cfsetspeed(&tty, *speed);
107*61c4878aSAndroid Build Coastguard Worker if (result < 0) {
108*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR(
109*61c4878aSAndroid Build Coastguard Worker "Failed to set TTY speed for '%s', %s", path, std::strerror(errno));
110*61c4878aSAndroid Build Coastguard Worker return Status::Unknown();
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker }
113*61c4878aSAndroid Build Coastguard Worker
114*61c4878aSAndroid Build Coastguard Worker if (config.flow_control.has_value()) {
115*61c4878aSAndroid Build Coastguard Worker if (*config.flow_control) {
116*61c4878aSAndroid Build Coastguard Worker // Enable hardware flow control.
117*61c4878aSAndroid Build Coastguard Worker tty.c_cflag |= (CRTSCTS);
118*61c4878aSAndroid Build Coastguard Worker } else {
119*61c4878aSAndroid Build Coastguard Worker // Disable hardware flow control.
120*61c4878aSAndroid Build Coastguard Worker tty.c_cflag &= ~(CRTSCTS);
121*61c4878aSAndroid Build Coastguard Worker }
122*61c4878aSAndroid Build Coastguard Worker }
123*61c4878aSAndroid Build Coastguard Worker
124*61c4878aSAndroid Build Coastguard Worker result = tcsetattr(fd_, TCSANOW, &tty);
125*61c4878aSAndroid Build Coastguard Worker if (result < 0) {
126*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to set TTY attributes for '%s', %s",
127*61c4878aSAndroid Build Coastguard Worker path,
128*61c4878aSAndroid Build Coastguard Worker std::strerror(errno));
129*61c4878aSAndroid Build Coastguard Worker return Status::Unknown();
130*61c4878aSAndroid Build Coastguard Worker }
131*61c4878aSAndroid Build Coastguard Worker
132*61c4878aSAndroid Build Coastguard Worker return OkStatus();
133*61c4878aSAndroid Build Coastguard Worker }
134*61c4878aSAndroid Build Coastguard Worker
Close()135*61c4878aSAndroid Build Coastguard Worker void UartStreamLinux::Close() {
136*61c4878aSAndroid Build Coastguard Worker if (fd_ != kInvalidFd) {
137*61c4878aSAndroid Build Coastguard Worker close(fd_);
138*61c4878aSAndroid Build Coastguard Worker fd_ = kInvalidFd;
139*61c4878aSAndroid Build Coastguard Worker }
140*61c4878aSAndroid Build Coastguard Worker }
141*61c4878aSAndroid Build Coastguard Worker
DoWrite(ConstByteSpan data)142*61c4878aSAndroid Build Coastguard Worker Status UartStreamLinux::DoWrite(ConstByteSpan data) {
143*61c4878aSAndroid Build Coastguard Worker const size_t size = data.size_bytes();
144*61c4878aSAndroid Build Coastguard Worker size_t written = 0;
145*61c4878aSAndroid Build Coastguard Worker while (written < size) {
146*61c4878aSAndroid Build Coastguard Worker int bytes = write(fd_, &data[written], size - written);
147*61c4878aSAndroid Build Coastguard Worker if (bytes < 0) {
148*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to write to UART, %s", std::strerror(errno));
149*61c4878aSAndroid Build Coastguard Worker return Status::Unknown();
150*61c4878aSAndroid Build Coastguard Worker }
151*61c4878aSAndroid Build Coastguard Worker written += bytes;
152*61c4878aSAndroid Build Coastguard Worker }
153*61c4878aSAndroid Build Coastguard Worker return OkStatus();
154*61c4878aSAndroid Build Coastguard Worker }
155*61c4878aSAndroid Build Coastguard Worker
DoRead(ByteSpan dest)156*61c4878aSAndroid Build Coastguard Worker StatusWithSize UartStreamLinux::DoRead(ByteSpan dest) {
157*61c4878aSAndroid Build Coastguard Worker int bytes = read(fd_, &dest[0], dest.size_bytes());
158*61c4878aSAndroid Build Coastguard Worker if (bytes < 0) {
159*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to read from UART, %s", std::strerror(errno));
160*61c4878aSAndroid Build Coastguard Worker return StatusWithSize::Unknown();
161*61c4878aSAndroid Build Coastguard Worker }
162*61c4878aSAndroid Build Coastguard Worker return StatusWithSize(bytes);
163*61c4878aSAndroid Build Coastguard Worker }
164*61c4878aSAndroid Build Coastguard Worker
165*61c4878aSAndroid Build Coastguard Worker } // namespace pw::stream
166