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