xref: /aosp_15_r20/hardware/interfaces/automotive/can/aidl/default/CanBusSlcan.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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