1 /*
2 * Copyright (C) 2022 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 "canbus_config.pb.h"
18 #include "canprototools.h"
19
20 #include <aidl/android/hardware/automotive/can/ICanController.h>
21 #include <android-base/logging.h>
22 #include <android/binder_manager.h>
23
24 #include <chrono>
25 #include <thread>
26
27 namespace android::hardware::automotive::can {
28
29 using namespace std::string_literals;
30 using ::aidl::android::hardware::automotive::can::ICanController;
31
32 static constexpr std::string_view kDefaultConfigPath = "/etc/canbus_config.pb";
33
34 /**
35 * Takes output from parsed protobuf config and uses it to configure the CAN HAL.
36 *
37 * \param pb_cfg is an instance of the autogenerated protobuf object for our configuration.
38 * \return boolean status, true on success, false on failure.
39 */
processPbCfg(const config::CanBusConfig & pb_cfg)40 static bool processPbCfg(const config::CanBusConfig& pb_cfg) {
41 for (auto const& bus : pb_cfg.buses()) {
42 if (bus.name().empty()) {
43 LOG(ERROR) << "Invalid config: Bus config must have a valid name field";
44 return false;
45 }
46
47 auto busCfgMaybe = config::fromPbBus(bus);
48 if (!busCfgMaybe.has_value()) {
49 return false;
50 }
51 auto busCfg = *busCfgMaybe;
52
53 const auto instance = ICanController::descriptor + "/default"s;
54 const auto service = ICanController::fromBinder(
55 ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
56 if (service == nullptr) {
57 LOG(FATAL) << "Can't find CAN HAL! (has it started yet?)";
58 return false;
59 }
60
61 LOG(VERBOSE) << "Bringing up a " << busCfg.name << " @ " << busCfg.bitrate;
62
63 std::string ifaceName;
64 const auto status = service->upBus(busCfg, &ifaceName);
65 if (!status.isOk() && status.getExceptionCode() != EX_SERVICE_SPECIFIC) {
66 LOG(FATAL) << "Binder transaction failed!" << status.getStatus();
67 return false;
68 } else if (!status.isOk()) {
69 LOG(ERROR) << "upBus failed: " << config::resultStringFromStatus(status) << ": "
70 << status.getMessage();
71 continue;
72 }
73
74 LOG(INFO) << bus.name() << " has been successfully configured on " << ifaceName;
75 }
76 return true;
77 }
78
79 /**
80 * This kicks off the CAN HAL configuration process. This starts the following:
81 * 1. Reading the config file
82 * 2. Setting up CAN buses
83 * 3. Handling services
84 * \param filepath is a string specifying the absolute path of the config file
85 * \return boolean status, true on success, false on failure
86 */
configuratorStart(const std::string & filepath)87 static bool configuratorStart(const std::string& filepath) {
88 base::SetDefaultTag("CanConfigurator");
89 auto pbCfg = config::parseConfigFile(filepath);
90 if (!pbCfg.has_value()) {
91 return false;
92 }
93 // process the rest of the config file data and configure the CAN buses.
94 if (!processPbCfg(*pbCfg)) {
95 return false;
96 }
97 LOG(INFO) << "CAN HAL has been configured!";
98 return true;
99 }
100
main(int argc,char * argv[])101 extern "C" int main(int argc, char* argv[]) {
102 std::string configFilepath = static_cast<std::string>(kDefaultConfigPath);
103
104 // allow for CLI specification of a config file.
105 if (argc == 2) {
106 configFilepath = argv[1];
107 } else if (argc > 2) {
108 std::cerr << "usage: " << argv[0] << " [optional config filepath]";
109 return 1;
110 }
111
112 if (!configuratorStart(configFilepath)) {
113 return 1;
114 }
115 return 0;
116 }
117
118 } // namespace android::hardware::automotive::can
119