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