xref: /aosp_15_r20/external/pigweed/pw_stream_uart_linux/stream.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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