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 #include "canprototools.h"
17
18 #include <aidl/android/hardware/automotive/can/IndexedInterface.h>
19 #include <aidl/android/hardware/automotive/can/NativeInterface.h>
20 #include <aidl/android/hardware/automotive/can/SlcanInterface.h>
21 #include <aidl/android/hardware/automotive/can/VirtualInterface.h>
22
23 #include <android-base/logging.h>
24 #include <google/protobuf/io/zero_copy_stream_impl.h>
25 #include <google/protobuf/text_format.h>
26
27 #include <fstream>
28
29 namespace android::hardware::automotive::can::config {
30
31 using ::aidl::android::hardware::automotive::can::BusConfig;
32 using ::aidl::android::hardware::automotive::can::IndexedInterface;
33 using ::aidl::android::hardware::automotive::can::InterfaceType;
34 using ::aidl::android::hardware::automotive::can::NativeInterface;
35 using ::aidl::android::hardware::automotive::can::Result;
36 using ::aidl::android::hardware::automotive::can::SlcanInterface;
37 using ::aidl::android::hardware::automotive::can::VirtualInterface;
38
39 /**
40 * Helper function for parseConfigFile. readString is used to get the fist n characters (n) from an
41 * istream object (s) and return it as a string object.
42 *
43 * \param s istream of the file you intend to read.
44 * \param n streamsize object of the number of characters you'd like.
45 * \return optional string containing up to n characters from the stream(s) you provided.
46 */
readString(std::istream & s,std::streamsize n)47 static std::optional<std::string> readString(std::istream& s, std::streamsize n) {
48 char buff[n];
49 auto got = s.read(buff, n).gcount();
50 if (!s.good() && !s.eof()) return std::nullopt;
51 return std::string(buff, got);
52 }
53
parseConfigFile(const std::string & filepath)54 std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
55 std::ifstream cfg_stream(filepath);
56
57 // text headers that would be present in a plaintext proto config file.
58 static const std::array<std::string, 3> text_headers = {"buses", "#", "controller"};
59 auto cfg_file_snippet = readString(cfg_stream, 10);
60
61 if (!cfg_file_snippet.has_value()) {
62 LOG(ERROR) << "Can't open " << filepath << " for reading";
63 return std::nullopt;
64 }
65 cfg_stream.seekg(0);
66
67 // check if any of the textHeaders are at the start of the config file.
68 bool text_format = false;
69 for (auto const& header : text_headers) {
70 if (cfg_file_snippet->compare(0, header.length(), header) == 0) {
71 text_format = true;
72 break;
73 }
74 }
75
76 CanBusConfig config;
77 if (text_format) {
78 google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream);
79 if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) {
80 LOG(ERROR) << "Failed to parse (text format) " << filepath;
81 return std::nullopt;
82 }
83 } else if (!config.ParseFromIstream(&cfg_stream)) {
84 LOG(ERROR) << "Failed to parse (binary format) " << filepath;
85 return std::nullopt;
86 }
87 return config;
88 }
89
fromPbBus(const Bus & pb_bus)90 std::optional<BusConfig> fromPbBus(const Bus& pb_bus) {
91 BusConfig bus_cfg = {};
92 bus_cfg.name = pb_bus.name();
93
94 switch (pb_bus.iface_type_case()) {
95 case Bus::kNative: {
96 const std::string ifname = pb_bus.native().ifname();
97 const std::vector<std::string> serials = {pb_bus.native().serialno().begin(),
98 pb_bus.native().serialno().end()};
99 if (ifname.empty() == serials.empty()) {
100 LOG(ERROR) << "Invalid config: native type bus must have an iface name xor a "
101 << "serial number";
102 return std::nullopt;
103 }
104 bus_cfg.bitrate = pb_bus.bitrate();
105 NativeInterface nativeif = {};
106 if (!ifname.empty())
107 nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::ifname>(ifname);
108 if (!serials.empty())
109 nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::serialno>(serials);
110 bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::nativeif>(nativeif);
111 break;
112 }
113 case Bus::kSlcan: {
114 const std::string ttyname = pb_bus.slcan().ttyname();
115 const std::vector<std::string> serials = {pb_bus.slcan().serialno().begin(),
116 pb_bus.slcan().serialno().end()};
117 if (ttyname.empty() == serials.empty()) {
118 LOG(ERROR) << "Invalid config: slcan type bus must have a tty name xor a serial "
119 << "number";
120 return std::nullopt;
121 }
122 bus_cfg.bitrate = pb_bus.bitrate();
123 SlcanInterface slcan = {};
124 if (!ttyname.empty())
125 slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::ttyname>(ttyname);
126 if (!serials.empty())
127 slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::serialno>(serials);
128 bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::slcan>(slcan);
129 break;
130 }
131 case Bus::kVirtual: {
132 // Theoretically, we could just create the next available vcan iface.
133 const std::string ifname = pb_bus.virtual_().ifname();
134 if (ifname.empty()) {
135 LOG(ERROR) << "Invalid config: native type bus must have an iface name";
136 return std::nullopt;
137 }
138 VirtualInterface virtualif = {};
139 virtualif.ifname = ifname;
140 bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::virtualif>(virtualif);
141 break;
142 }
143 case Bus::kIndexed: {
144 const uint8_t index = pb_bus.indexed().index();
145 if (index > UINT8_MAX) {
146 LOG(ERROR) << "Interface index out of range: " << index;
147 return std::nullopt;
148 }
149 IndexedInterface indexedif = {};
150 indexedif.index = index;
151 bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::indexed>(indexedif);
152 break;
153 }
154 default:
155 LOG(ERROR) << "Invalid config: bad interface type for " << bus_cfg.name;
156 return std::nullopt;
157 }
158 return bus_cfg;
159 }
160
getHalIftype(const Bus & pb_bus)161 std::optional<InterfaceType> getHalIftype(const Bus& pb_bus) {
162 switch (pb_bus.iface_type_case()) {
163 case Bus::kNative:
164 return InterfaceType::NATIVE;
165 case Bus::kSlcan:
166 return InterfaceType::SLCAN;
167 case Bus::kVirtual:
168 return InterfaceType::VIRTUAL;
169 case Bus::kIndexed:
170 return InterfaceType::INDEXED;
171 default:
172 return std::nullopt;
173 }
174 }
175
resultStringFromStatus(const ndk::ScopedAStatus & status)176 std::string resultStringFromStatus(const ndk::ScopedAStatus& status) {
177 const auto res = static_cast<Result>(status.getServiceSpecificError());
178 switch (res) {
179 case Result::OK:
180 return "OK";
181 case Result::UNKNOWN_ERROR:
182 return "UNKNOWN_ERROR";
183 case Result::INVALID_STATE:
184 return "INVALID_STATE";
185 case Result::NOT_SUPPORTED:
186 return "NOT_SUPPORTED";
187 case Result::BAD_INTERFACE_ID:
188 return "BAD_INTERFACE_ID";
189 case Result::BAD_BITRATE:
190 return "BAD_BITRATE";
191 case Result::BAD_BUS_NAME:
192 return "BAD_BUS_NAME";
193 case Result::INTERFACE_DOWN:
194 return "INTERFACE_DOWN";
195 default:
196 return "Invalid Result!";
197 }
198 }
199
200 } // namespace android::hardware::automotive::can::config
201