blob: a2643afccf67970a55d78df379f6c954664f128b [file] [log] [blame]
Tomasz Wasilczyk87329672019-07-12 11:43:00 -07001/*
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 "CanController.h"
18
19#include "CanBusNative.h"
chrisweircf36cea2019-11-08 16:41:02 -080020#include "CanBusSlcan.h"
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070021#include "CanBusVirtual.h"
22
23#include <android-base/logging.h>
24#include <android/hidl/manager/1.2/IServiceManager.h>
25
chrisweira9d0e902020-02-27 14:33:51 -080026#include <filesystem>
27#include <fstream>
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070028#include <regex>
29
Tomasz Wasilczyk55f21932019-12-20 09:20:24 -080030namespace android::hardware::automotive::can::V1_0::implementation {
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070031
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -080032using IfId = ICanController::BusConfig::InterfaceId;
33using IfIdDisc = ICanController::BusConfig::InterfaceId::hidl_discriminator;
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070034
chrisweira9d0e902020-02-27 14:33:51 -080035namespace fsErrors {
36static const std::error_code ok;
37static const std::error_code eperm(EPERM, std::generic_category());
38static const std::error_code enoent(ENOENT, std::generic_category());
39static const std::error_code eacces(EACCES, std::generic_category());
40} // namespace fsErrors
41
42/* In the /sys/devices tree, there are files called "serial", which contain the serial numbers
43 * for various devices. The exact location inside of this directory is dependent upon the
44 * hardware we are running on, so we have to start from /sys/devices and work our way down. */
45static const std::filesystem::path kDevPath("/sys/devices/");
46static const std::regex kTtyRe("^tty[A-Z]+[0-9]+$");
47static constexpr auto kOpts = ~(std::filesystem::directory_options::follow_directory_symlink |
48 std::filesystem::directory_options::skip_permission_denied);
49
50/**
51 * A helper object to associate the interface name and type of a USB to CAN adapter.
52 */
53struct UsbCanIface {
54 ICanController::InterfaceType iftype;
55 std::string ifaceName;
56};
57
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070058Return<void> CanController::getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) {
chrisweircf36cea2019-11-08 16:41:02 -080059 _hidl_cb({ICanController::InterfaceType::VIRTUAL, ICanController::InterfaceType::SOCKETCAN,
60 ICanController::InterfaceType::SLCAN});
Tomasz Wasilczyk87329672019-07-12 11:43:00 -070061 return {};
62}
63
64static bool isValidName(const std::string& name) {
65 static const std::regex nameRE("^[a-zA-Z0-9_]{1,32}$");
66 return std::regex_match(name, nameRE);
67}
68
chrisweira9d0e902020-02-27 14:33:51 -080069/**
70 * Given a UsbCanIface object, get the ifaceName given the serialPath.
71 *
72 * \param serialPath - Absolute path to a "serial" file for a given device in /sys.
73 * \return A populated UsbCanIface. On failure, nullopt is returned.
74 */
75static std::optional<UsbCanIface> getIfaceName(std::filesystem::path serialPath) {
76 std::error_code fsStatus;
77 // Since the path is to a file called "serial", we need to search its parent directory.
78 std::filesystem::recursive_directory_iterator fsItr(serialPath.parent_path(), kOpts, fsStatus);
79 if (fsStatus != fsErrors::ok) {
80 LOG(ERROR) << "Failed to open " << serialPath.parent_path();
81 return std::nullopt;
82 }
83
84 for (; fsStatus == fsErrors::ok && fsItr != std::filesystem::recursive_directory_iterator();
85 fsItr.increment(fsStatus)) {
86 /* We want either a directory called "net" or a directory that looks like tty<something>, so
87 * skip files. */
88 bool isDir = fsItr->is_directory(fsStatus);
89 if (fsStatus != fsErrors::ok || !isDir) continue;
90
91 /* path() returns an iterator that steps through directories from / to the leaf.
92 * end() returns one past the leaf of the path, but we want the leaf. Decrementing the
93 * path gives us a pointer to the leaf, which we then dereference.*/
94 std::string currentDir = *(--(fsItr->path().end()));
95 if (currentDir == "net") {
96 /* This device is a SocketCAN device. The iface name is the only directory under
97 * net/. Multiple directories under net/ is an error.*/
98 std::filesystem::directory_iterator netItr(fsItr->path(), kOpts, fsStatus);
99 if (fsStatus != fsErrors::ok) {
100 LOG(ERROR) << "Failed to open " << fsItr->path() << " to get net name!";
101 return std::nullopt;
102 }
103
104 // Get the leaf of the path. This is the interface name, assuming it's the only leaf.
105 std::string netName = *(--(netItr->path().end()));
106
107 // Check if there is more than one item in net/
108 netItr.increment(fsStatus);
109 if (fsStatus != fsErrors::ok) {
110 // It's possible we have a valid net name, but this is most likely an error.
111 LOG(ERROR) << "Failed to verify " << fsItr->path() << " has valid net name!";
112 return std::nullopt;
113 }
114 if (netItr != std::filesystem::directory_iterator()) {
115 // There should never be more than one name under net/
116 LOG(ERROR) << "Found more than one net name in " << fsItr->path() << "!";
117 return std::nullopt;
118 }
119 return {{ICanController::InterfaceType::SOCKETCAN, netName}};
120 } else if (std::regex_match(currentDir, kTtyRe)) {
121 // This device is a USB serial device, and currentDir is the tty name.
122 return {{ICanController::InterfaceType::SLCAN, "/dev/" + currentDir}};
123 }
124 }
125
126 // check if the loop above exited due to a c++fs error.
127 if (fsStatus != fsErrors::ok) {
128 LOG(ERROR) << "Failed search filesystem: " << fsStatus;
129 }
130 return std::nullopt;
131}
132
133/**
134 * A helper function to read the serial number from a "serial" file in /sys/devices/
135 *
136 * \param serialnoPath - path to the file to read.
137 * \return the serial number, or nullopt on failure.
138 */
139static std::optional<std::string> readSerialNo(const std::string& serialnoPath) {
140 std::ifstream serialnoStream(serialnoPath);
141 std::string serialno;
142 if (!serialnoStream.good()) {
143 LOG(ERROR) << "Failed to read serial number from " << serialnoPath;
144 return std::nullopt;
145 }
146 std::getline(serialnoStream, serialno);
147 return serialno;
148}
149
150/**
151 * Searches for USB devices found in /sys/devices/, and attempts to find a device matching the
152 * provided list of serial numbers.
153 *
154 * \param configSerialnos - a list of serial number (suffixes) from the HAL config.
155 * \param iftype - the type of the interface to be located.
156 * \return a matching USB device. On failure, std::nullopt is returned.
157 */
158static std::optional<UsbCanIface> findUsbDevice(const hidl_vec<hidl_string>& configSerialnos) {
159 std::error_code fsStatus;
160 std::filesystem::recursive_directory_iterator fsItr(kDevPath, kOpts, fsStatus);
161 if (fsStatus != fsErrors::ok) {
162 LOG(ERROR) << "Failed to open " << kDevPath;
163 return std::nullopt;
164 }
165
166 for (; fsStatus == fsErrors::ok && fsItr != std::filesystem::recursive_directory_iterator();
167 fsItr.increment(fsStatus)) {
168 // We want to find a file called "serial", which is in a directory somewhere. Skip files.
169 bool isDir = fsItr->is_directory(fsStatus);
170 if (fsStatus != fsErrors::ok) {
171 LOG(ERROR) << "Failed check if " << fsStatus;
172 return std::nullopt;
173 }
174 if (!isDir) continue;
175
176 auto serialnoPath = fsItr->path() / "serial";
177 bool isReg = std::filesystem::is_regular_file(serialnoPath, fsStatus);
178
179 /* Make sure we have permissions to this directory, ignore enoent, since the file
180 * "serial" may not exist, which is ok. */
181 if (fsStatus == fsErrors::eperm || fsStatus == fsErrors::eacces) {
182 /* This means we don't have access to this directory. If we recurse into it, this
183 * will cause the iterator to loose its state and we'll crash. */
184 fsItr.disable_recursion_pending();
185 continue;
186 }
187 if (fsStatus == fsErrors::enoent) continue;
188 if (fsStatus != fsErrors::ok) {
189 LOG(WARNING) << "An unexpected error occurred while checking for serialno: "
190 << fsStatus;
191 continue;
192 }
193 if (!isReg) continue;
194
195 // we found a serial number
196 auto serialno = readSerialNo(serialnoPath);
197 if (!serialno.has_value()) continue;
198
199 // see if the serial number exists in the config
200 for (auto&& cfgSn : configSerialnos) {
201 if (serialno->ends_with(std::string(cfgSn))) {
202 auto ifaceInfo = getIfaceName(serialnoPath);
203 if (!ifaceInfo.has_value()) break;
204 return ifaceInfo;
205 }
206 }
207 }
208 if (fsStatus != fsErrors::ok) {
209 LOG(ERROR) << "Error searching filesystem: " << fsStatus;
210 return std::nullopt;
211 }
212 return std::nullopt;
213}
214
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -0800215Return<ICanController::Result> CanController::upInterface(const ICanController::BusConfig& config) {
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700216 LOG(VERBOSE) << "Attempting to bring interface up: " << toString(config);
217
218 std::lock_guard<std::mutex> lck(mCanBusesGuard);
219
220 if (!isValidName(config.name)) {
221 LOG(ERROR) << "Bus name " << config.name << " is invalid";
Tomasz Wasilczyk793fab02020-02-18 15:19:35 -0800222 return ICanController::Result::BAD_SERVICE_NAME;
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700223 }
224
225 if (mCanBuses.find(config.name) != mCanBuses.end()) {
226 LOG(ERROR) << "Bus " << config.name << " is already up";
227 return ICanController::Result::INVALID_STATE;
228 }
229
230 sp<CanBus> busService;
231
chrisweira9d0e902020-02-27 14:33:51 -0800232 // SocketCAN native type interface.
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -0800233 if (config.interfaceId.getDiscriminator() == IfIdDisc::socketcan) {
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -0800234 auto& socketcan = config.interfaceId.socketcan();
chrisweira9d0e902020-02-27 14:33:51 -0800235 std::string ifaceName;
236 if (socketcan.getDiscriminator() == IfId::Socketcan::hidl_discriminator::serialno) {
237 // Configure by serial number.
238 auto selectedDevice = findUsbDevice(socketcan.serialno());
239 // verify the returned device is the correct one
240 if (!selectedDevice.has_value() ||
241 selectedDevice->iftype != ICanController::InterfaceType::SOCKETCAN) {
242 return ICanController::Result::BAD_INTERFACE_ID;
243 }
244 ifaceName = selectedDevice->ifaceName;
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700245 } else {
chrisweira9d0e902020-02-27 14:33:51 -0800246 // configure by iface name.
247 ifaceName = socketcan.ifname();
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700248 }
chrisweira9d0e902020-02-27 14:33:51 -0800249 busService = new CanBusNative(ifaceName, config.bitrate);
250 }
251 // Virtual interface.
252 else if (config.interfaceId.getDiscriminator() == IfIdDisc::virtualif) {
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -0800253 busService = new CanBusVirtual(config.interfaceId.virtualif().ifname);
chrisweira9d0e902020-02-27 14:33:51 -0800254 }
255 // SLCAN interface.
256 else if (config.interfaceId.getDiscriminator() == IfIdDisc::slcan) {
Tomasz Wasilczykf3da9b62020-02-14 10:54:28 -0800257 auto& slcan = config.interfaceId.slcan();
chrisweira9d0e902020-02-27 14:33:51 -0800258 std::string ttyName;
259 if (slcan.getDiscriminator() == IfId::Slcan::hidl_discriminator::serialno) {
260 // Configure by serial number.
261 auto selectedDevice = findUsbDevice(slcan.serialno());
262 if (!selectedDevice.has_value() ||
263 selectedDevice->iftype != ICanController::InterfaceType::SLCAN) {
264 return ICanController::Result::BAD_INTERFACE_ID;
265 }
266 ttyName = selectedDevice->ifaceName;
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700267 } else {
chrisweira9d0e902020-02-27 14:33:51 -0800268 // Configure by tty name.
269 ttyName = slcan.ttyname();
chrisweircf36cea2019-11-08 16:41:02 -0800270 }
chrisweira9d0e902020-02-27 14:33:51 -0800271 busService = new CanBusSlcan(ttyName, config.bitrate);
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700272 } else {
273 return ICanController::Result::NOT_SUPPORTED;
274 }
275
Tomasz Wasilczyka9061962019-11-04 12:53:09 -0800276 busService->setErrorCallback([this, name = config.name]() { downInterface(name); });
277
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700278 const auto result = busService->up();
279 if (result != ICanController::Result::OK) return result;
280
281 if (busService->registerAsService(config.name) != OK) {
282 LOG(ERROR) << "Failed to register ICanBus/" << config.name;
283 if (!busService->down()) {
284 LOG(WARNING) << "Failed to bring down CAN bus that failed to register";
285 }
Tomasz Wasilczyk793fab02020-02-18 15:19:35 -0800286 return ICanController::Result::BAD_SERVICE_NAME;
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700287 }
288
289 mCanBuses[config.name] = busService;
290
291 return ICanController::Result::OK;
292}
293
Tomasz Wasilczyka9061962019-11-04 12:53:09 -0800294static bool unregisterCanBusService(const hidl_string& name, sp<CanBus> busService) {
295 auto manager = hidl::manager::V1_2::IServiceManager::getService();
296 if (!manager) return false;
297 const auto res = manager->tryUnregister(ICanBus::descriptor, name, busService);
298 if (!res.isOk()) return false;
299 return res;
300}
301
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700302Return<bool> CanController::downInterface(const hidl_string& name) {
303 LOG(VERBOSE) << "Attempting to bring interface down: " << name;
304
305 std::lock_guard<std::mutex> lck(mCanBusesGuard);
306
307 auto busEntry = mCanBuses.extract(name);
308 if (!busEntry) {
309 LOG(WARNING) << "Interface " << name << " is not up";
310 return false;
311 }
312
313 auto success = true;
314
Tomasz Wasilczyka9061962019-11-04 12:53:09 -0800315 if (!unregisterCanBusService(name, busEntry.mapped())) {
Tomasz Wasilczyk87329672019-07-12 11:43:00 -0700316 LOG(ERROR) << "Couldn't unregister " << name;
317 // don't return yet, let's try to do best-effort cleanup
318 success = false;
319 }
320
321 if (!busEntry.mapped()->down()) {
322 LOG(ERROR) << "Couldn't bring " << name << " down";
323 success = false;
324 }
325
326 return success;
327}
328
Tomasz Wasilczyk55f21932019-12-20 09:20:24 -0800329} // namespace android::hardware::automotive::can::V1_0::implementation