1 /*
2 * Copyright 2022, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "CanBusSlcan.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <libnetdevice/libnetdevice.h>
22
23 #include <linux/serial.h>
24 #include <linux/tty.h>
25 #include <net/if.h>
26 #include <termios.h>
27
28 #include <map>
29
30 namespace aidl::android::hardware::automotive::can {
31
32 using namespace std::string_view_literals;
33 using namespace ::android::base;
34
35 namespace slcanprotocol {
36 static constexpr std::string_view kOpenCommand = "O\r"sv;
37 static constexpr std::string_view kCloseCommand = "C\r"sv;
38 static constexpr int kSlcanDiscipline = N_SLCAN;
39 static constexpr int kDefaultDiscipline = N_TTY;
40
41 static const std::map<uint32_t, std::string_view> kBitrateCommands = {
42 {10000, "C\rS0\r"sv}, {20000, "C\rS1\r"sv}, {50000, "C\rS2\r"sv},
43 {100000, "C\rS3\r"sv}, {125000, "C\rS4\r"sv}, {250000, "C\rS5\r"sv},
44 {500000, "C\rS6\r"sv}, {800000, "C\rS7\r"sv}, {1000000, "C\rS8\r"sv}};
45 } // namespace slcanprotocol
46
47 /**
48 * Serial Line CAN constructor
49 * \param string uartName - name of slcan device (e.x. /dev/ttyUSB0)
50 * \param uint32_t bitrate - speed of the CAN bus (125k = MSCAN, 500k = HSCAN)
51 */
CanBusSlcan(const std::string & uartName,uint32_t bitrate)52 CanBusSlcan::CanBusSlcan(const std::string& uartName, uint32_t bitrate)
53 : CanBus(), mTtyPath(uartName), kBitrate(bitrate) {}
54
55 /** helper function to update CanBusSlcan object's iface name */
updateIfaceName(unique_fd & uartFd)56 Result CanBusSlcan::updateIfaceName(unique_fd& uartFd) {
57 struct ifreq ifrequest = {};
58 /*
59 * Fetching the iface name with an ioctl won't interfere with an open socketCAN iface attached
60 * to this tty. This is important in the event we are trying to register a SLCAN based iface
61 * that has already been configured and brought up.
62 */
63 if (ioctl(uartFd.get(), SIOCGIFNAME, ifrequest.ifr_name) < 0) {
64 PLOG(ERROR) << "Failed to get the name of the created device";
65 return Result::UNKNOWN_ERROR;
66 }
67
68 // Update the CanBus object with name that was assigned to it
69 mIfname = ifrequest.ifr_name;
70 return Result::OK;
71 }
72
preUp()73 Result CanBusSlcan::preUp() {
74 // verify valid bitrate and translate to serial command format
75 std::optional<std::string_view> canBitrateCommand = std::nullopt;
76 if (kBitrate != 0) {
77 const auto lookupIt = slcanprotocol::kBitrateCommands.find(kBitrate);
78 if (lookupIt == slcanprotocol::kBitrateCommands.end()) {
79 return Result::BAD_BITRATE;
80 }
81 canBitrateCommand = lookupIt->second;
82 }
83
84 /* Attempt to open the uart in r/w without blocking or becoming the
85 * controlling terminal */
86 mFd = unique_fd(open(mTtyPath.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC));
87 if (!mFd.ok()) {
88 PLOG(ERROR) << "SLCAN Failed to open " << mTtyPath;
89 return Result::BAD_INTERFACE_ID;
90 }
91
92 // If the device is already up, update the iface name in our CanBusSlcan object
93 if (kBitrate == 0) {
94 return updateIfaceName(mFd);
95 }
96
97 // blank terminal settings and pull them from the device
98 struct termios terminalSettings = {};
99 if (tcgetattr(mFd.get(), &terminalSettings) < 0) {
100 PLOG(ERROR) << "Failed to read attrs of" << mTtyPath;
101 return Result::UNKNOWN_ERROR;
102 }
103
104 // change settings to raw mode
105 cfmakeraw(&terminalSettings);
106
107 // disable software flow control
108 terminalSettings.c_iflag &= ~IXOFF;
109 // enable hardware flow control
110 terminalSettings.c_cflag |= CRTSCTS;
111
112 struct serial_struct serialSettings;
113 // get serial settings
114 if (ioctl(mFd.get(), TIOCGSERIAL, &serialSettings) < 0) {
115 PLOG(ERROR) << "Failed to read serial settings from " << mTtyPath;
116 return Result::UNKNOWN_ERROR;
117 }
118 // set low latency mode
119 serialSettings.flags |= ASYNC_LOW_LATENCY;
120 // apply serial settings
121 if (ioctl(mFd.get(), TIOCSSERIAL, &serialSettings) < 0) {
122 PLOG(ERROR) << "Failed to set low latency mode on " << mTtyPath;
123 return Result::UNKNOWN_ERROR;
124 }
125
126 /* TCSADRAIN applies settings after we finish writing the rest of our
127 * changes (as opposed to TCSANOW, which changes immediately) */
128 if (tcsetattr(mFd.get(), TCSADRAIN, &terminalSettings) < 0) {
129 PLOG(ERROR) << "Failed to apply terminal settings to " << mTtyPath;
130 return Result::UNKNOWN_ERROR;
131 }
132
133 // apply speed setting for CAN
134 if (!WriteStringToFd(*canBitrateCommand, mFd)) {
135 PLOG(ERROR) << "Failed to apply CAN bitrate";
136 return Result::UNKNOWN_ERROR;
137 }
138
139 // TODO(b/144775286): set open flag & support listen only
140 if (!WriteStringToFd(slcanprotocol::kOpenCommand, mFd)) {
141 PLOG(ERROR) << "Failed to set open flag";
142 return Result::UNKNOWN_ERROR;
143 }
144
145 // set line discipline to slcan
146 if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kSlcanDiscipline) < 0) {
147 PLOG(ERROR) << "Failed to set line discipline to slcan";
148 return Result::UNKNOWN_ERROR;
149 }
150
151 // Update the CanBus object with name that was assigned to it
152 return updateIfaceName(mFd);
153 }
154
postDown()155 bool CanBusSlcan::postDown() {
156 // reset the line discipline to TTY mode
157 if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kDefaultDiscipline) < 0) {
158 LOG(ERROR) << "Failed to reset line discipline!";
159 return false;
160 }
161
162 // issue the close command
163 if (!WriteStringToFd(slcanprotocol::kCloseCommand, mFd)) {
164 LOG(ERROR) << "Failed to close tty!";
165 return false;
166 }
167
168 // close our unique_fd
169 mFd.reset();
170
171 return true;
172 }
173
174 } // namespace aidl::android::hardware::automotive::can
175