blob: 7dce838ff139efca199d8fa45fefb441264d8f53 [file] [log] [blame]
chrisweircf36cea2019-11-08 16:41:02 -08001/*
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
28namespace android {
29namespace hardware {
30namespace automotive {
31namespace can {
32namespace V1_0 {
33namespace implementation {
34
35namespace slcanprotocol {
36static const std::string kOpenCommand = "O\r";
37static const std::string kCloseCommand = "C\r";
38static constexpr int kSlcanDiscipline = N_SLCAN;
39static constexpr int kDefaultDiscipline = N_TTY;
40
41static const std::map<uint32_t, std::string> kBitrateCommands = {
42 {10000, "C\rS0\r"}, {20000, "C\rS1\r"}, {50000, "C\rS2\r"},
43 {100000, "C\rS3\r"}, {125000, "C\rS4\r"}, {250000, "C\rS5\r"},
44 {500000, "C\rS6\r"}, {800000, "C\rS7\r"}, {1000000, "C\rS8\r"}};
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 */
52CanBusSlcan::CanBusSlcan(const std::string& uartName, uint32_t bitrate)
53 : CanBus(), mUartName(uartName), kBitrate(bitrate) {}
54
55ICanController::Result CanBusSlcan::preUp() {
56 // verify valid bitrate and translate to serial command format
57 const auto lookupIt = slcanprotocol::kBitrateCommands.find(kBitrate);
58 if (lookupIt == slcanprotocol::kBitrateCommands.end()) {
59 return ICanController::Result::BAD_BAUDRATE;
60 }
61 const auto canBitrateCommand = lookupIt->second;
62
63 /* Attempt to open the uart in r/w without blocking or becoming the
64 * controlling terminal */
65 mFd = base::unique_fd(open(mUartName.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY));
66 if (!mFd.ok()) {
67 LOG(ERROR) << "SLCAN Failed to open " << mUartName << ": " << strerror(errno);
68 return ICanController::Result::BAD_ADDRESS;
69 }
70
71 // blank terminal settings and pull them from the device
72 struct termios terminalSettings = {};
73 if (tcgetattr(mFd.get(), &terminalSettings) < 0) {
74 LOG(ERROR) << "Failed to read attrs of" << mUartName << ": " << strerror(errno);
75 return ICanController::Result::UNKNOWN_ERROR;
76 }
77
78 // change settings to raw mode
79 cfmakeraw(&terminalSettings);
80
81 // disable software flow control
82 terminalSettings.c_iflag &= ~IXOFF;
83 // enable hardware flow control
84 terminalSettings.c_cflag |= CRTSCTS;
85
86 struct serial_struct serialSettings;
87 // get serial settings
88 if (ioctl(mFd.get(), TIOCGSERIAL, &serialSettings) < 0) {
89 LOG(ERROR) << "Failed to read serial settings from " << mUartName << ": "
90 << strerror(errno);
91 return ICanController::Result::UNKNOWN_ERROR;
92 }
93 // set low latency mode
94 serialSettings.flags |= ASYNC_LOW_LATENCY;
95 // apply serial settings
96 if (ioctl(mFd.get(), TIOCSSERIAL, &serialSettings) < 0) {
97 LOG(ERROR) << "Failed to set low latency mode on " << mUartName << ": " << strerror(errno);
98 return ICanController::Result::UNKNOWN_ERROR;
99 }
100
101 /* TCSADRAIN applies settings after we finish writing the rest of our
102 * changes (as opposed to TCSANOW, which changes immediately) */
103 if (tcsetattr(mFd.get(), TCSADRAIN, &terminalSettings) < 0) {
104 LOG(ERROR) << "Failed to apply terminal settings to " << mUartName << ": "
105 << strerror(errno);
106 return ICanController::Result::UNKNOWN_ERROR;
107 }
108
109 // apply speed setting for CAN
110 if (write(mFd.get(), canBitrateCommand.c_str(), canBitrateCommand.length()) <= 0) {
111 LOG(ERROR) << "Failed to apply CAN bitrate: " << strerror(errno);
112 return ICanController::Result::UNKNOWN_ERROR;
113 }
114
115 // set open flag TODO: also support listen only
116 if (write(mFd.get(), slcanprotocol::kOpenCommand.c_str(),
117 slcanprotocol::kOpenCommand.length()) <= 0) {
118 LOG(ERROR) << "Failed to set open flag: " << strerror(errno);
119 return ICanController::Result::UNKNOWN_ERROR;
120 }
121
122 // set line discipline to slcan
123 if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kSlcanDiscipline) < 0) {
124 LOG(ERROR) << "Failed to set line discipline to slcan: " << strerror(errno);
125 return ICanController::Result::UNKNOWN_ERROR;
126 }
127
128 // get the name of the device we created
129 struct ifreq ifrequest = {};
130 if (ioctl(mFd.get(), SIOCGIFNAME, ifrequest.ifr_name) < 0) {
131 LOG(ERROR) << "Failed to get the name of the created device: " << strerror(errno);
132 return ICanController::Result::UNKNOWN_ERROR;
133 }
134
135 // Update the CanBus object with name that was assigned to it
136 mIfname = ifrequest.ifr_name;
137
138 return ICanController::Result::OK;
139}
140
141bool CanBusSlcan::postDown() {
142 // reset the line discipline to TTY mode
143 if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kDefaultDiscipline) < 0) {
144 LOG(ERROR) << "Failed to reset line discipline!";
145 return false;
146 }
147
148 // issue the close command
149 if (write(mFd.get(), slcanprotocol::kCloseCommand.c_str(),
150 slcanprotocol::kCloseCommand.length()) <= 0) {
151 LOG(ERROR) << "Failed to close tty!";
152 return false;
153 }
154
155 // close our unique_fd
156 mFd.reset();
157
158 return true;
159}
160
161} // namespace implementation
162} // namespace V1_0
163} // namespace can
164} // namespace automotive
165} // namespace hardware
166} // namespace android