1 /*
2  * Copyright (C) 2020 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 <android-base/logging.h>
19 #include <google/protobuf/io/zero_copy_stream_impl.h>
20 #include <google/protobuf/text_format.h>
21 #include <hidl/HidlTransportSupport.h>
22 #include <libcanhaltools/libcanhaltools.h>
23 
24 #include <fstream>
25 
26 namespace android::hardware::automotive::can::config {
27 
28 using ICanController = V1_0::ICanController;
29 
30 /**
31  * Helper function for parseConfigFile. readString is used to get the fist n characters (n) from an
32  * istream object (s) and return it as a string object.
33  *
34  * \param s istream of the file you intend to read.
35  * \param n streamsize object of the number of characters you'd like.
36  * \return optional string containing up to n characters from the stream(s) you provided.
37  */
readString(std::istream & s,std::streamsize n)38 static std::optional<std::string> readString(std::istream& s, std::streamsize n) {
39     char buff[n];
40     auto got = s.read(buff, n).gcount();
41     if (!s.good() && !s.eof()) return std::nullopt;
42     return std::string(buff, 0, std::min(n, got));
43 }
44 
parseConfigFile(const std::string & filepath)45 std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
46     std::ifstream cfg_stream(filepath);
47 
48     // text headers that would be present in a plaintext proto config file.
49     static const std::array<std::string, 3> text_headers = {"buses", "#", "controller"};
50     auto cfg_file_snippet = readString(cfg_stream, 10);
51 
52     if (!cfg_file_snippet.has_value()) {
53         LOG(ERROR) << "Can't open " << filepath << " for reading";
54         return std::nullopt;
55     }
56     cfg_stream.seekg(0);
57 
58     // check if any of the textHeaders are at the start of the config file.
59     bool text_format = false;
60     for (auto const& header : text_headers) {
61         if (cfg_file_snippet->compare(0, header.length(), header) == 0) {
62             text_format = true;
63             break;
64         }
65     }
66 
67     CanBusConfig config;
68     if (text_format) {
69         google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream);
70         if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) {
71             LOG(ERROR) << "Failed to parse (text format) " << filepath;
72             return std::nullopt;
73         }
74     } else if (!config.ParseFromIstream(&cfg_stream)) {
75         LOG(ERROR) << "Failed to parse (binary format) " << filepath;
76         return std::nullopt;
77     }
78     return config;
79 }
80 
fromPbBus(const Bus & pb_bus)81 std::optional<ICanController::BusConfig> fromPbBus(const Bus& pb_bus) {
82     ICanController::BusConfig bus_cfg = {};
83     bus_cfg.name = pb_bus.name();
84 
85     switch (pb_bus.iface_type_case()) {
86         case Bus::kNative: {
87             const auto ifname = pb_bus.native().ifname();
88             const auto serialno = pb_bus.native().serialno();
89             if (ifname.empty() == serialno.empty()) {
90                 LOG(ERROR) << "Invalid config: native type bus must have an iface name xor a "
91                            << "serial number";
92                 return std::nullopt;
93             }
94             bus_cfg.bitrate = pb_bus.bitrate();
95             ICanController::BusConfig::InterfaceId::Socketcan socketcan = {};
96             if (!ifname.empty()) socketcan.ifname(ifname);
97             if (!serialno.empty()) socketcan.serialno({serialno.begin(), serialno.end()});
98             bus_cfg.interfaceId.socketcan(socketcan);
99             break;
100         }
101         case Bus::kSlcan: {
102             const auto ttyname = pb_bus.slcan().ttyname();
103             const auto serialno = pb_bus.slcan().serialno();
104             if (ttyname.empty() == serialno.empty()) {
105                 LOG(ERROR) << "Invalid config: slcan type bus must have a tty name";
106                 return std::nullopt;
107             }
108             bus_cfg.bitrate = pb_bus.bitrate();
109             ICanController::BusConfig::InterfaceId::Slcan slcan = {};
110             if (!ttyname.empty()) slcan.ttyname(ttyname);
111             if (!serialno.empty()) slcan.serialno({serialno.begin(), serialno.end()});
112             bus_cfg.interfaceId.slcan(slcan);
113             break;
114         }
115         case Bus::kVirtual: {
116             // Theoretically, we could just create the next available vcan iface.
117             const auto ifname = pb_bus.virtual_().ifname();
118             if (ifname.empty()) {
119                 LOG(ERROR) << "Invalid config: native type bus must have an iface name";
120                 return std::nullopt;
121             }
122             bus_cfg.interfaceId.virtualif({ifname});
123             break;
124         }
125         case Bus::kIndexed: {
126             const auto index = pb_bus.indexed().index();
127             if (index > UINT8_MAX) {
128                 LOG(ERROR) << "Interface index out of range: " << index;
129                 return std::nullopt;
130             }
131             bus_cfg.interfaceId.indexed({uint8_t(index)});
132             break;
133         }
134         default:
135             LOG(ERROR) << "Invalid config: bad interface type for " << bus_cfg.name;
136             return std::nullopt;
137     }
138     return bus_cfg;
139 }
140 
getHalIftype(const Bus & pb_bus)141 std::optional<ICanController::InterfaceType> getHalIftype(const Bus& pb_bus) {
142     switch (pb_bus.iface_type_case()) {
143         case Bus::kNative:
144             return ICanController::InterfaceType::SOCKETCAN;
145         case Bus::kSlcan:
146             return ICanController::InterfaceType::SLCAN;
147         case Bus::kVirtual:
148             return ICanController::InterfaceType::VIRTUAL;
149         case Bus::kIndexed:
150             return ICanController::InterfaceType::INDEXED;
151         default:
152             return std::nullopt;
153     }
154 }
155 
156 }  // namespace android::hardware::automotive::can::config
157