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