blob: 0f25eb5b86d74127fb0f72da22bf9ecab8ef02e7 [file] [log] [blame]
James Feistc95cb142018-02-26 10:41:42 -08001/*
2// Copyright (c) 2018 Intel Corporation
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 <string>
18#include <iostream>
19#include <regex>
20#include <boost/process/child.hpp>
James Feist053a6642018-10-15 13:17:09 -070021#include <boost/algorithm/string/predicate.hpp>
James Feistc95cb142018-02-26 10:41:42 -080022#include <experimental/filesystem>
23#include <boost/container/flat_map.hpp>
24#include <boost/container/flat_set.hpp>
25#include <nlohmann/json.hpp>
James Feist053a6642018-10-15 13:17:09 -070026#include <devices.hpp>
James Feistc95cb142018-02-26 10:41:42 -080027#include <Overlay.hpp>
28#include <Utils.hpp>
29
30constexpr const char *DT_OVERLAY = "/usr/bin/dtoverlay";
31constexpr const char *DTC = "/usr/bin/dtc";
32constexpr const char *OUTPUT_DIR = "/tmp/overlays";
James Feistce4367c2018-10-16 09:19:57 -070033constexpr const char *TEMPLATE_DIR = PACKAGE_DIR "overlay_templates";
James Feistc95cb142018-02-26 10:41:42 -080034constexpr const char *TEMPLATE_CHAR = "$";
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -070035constexpr const char *HEX_FORMAT_STR = "0x";
James Feistc95cb142018-02-26 10:41:42 -080036constexpr const char *PLATFORM = "aspeed,ast2500";
37constexpr const char *I2C_DEVS_DIR = "/sys/class/i2c-dev";
38
39// some drivers need to be unbind / bind to load device tree changes
40static const boost::container::flat_map<std::string, std::string> FORCE_PROBES =
41 {{"IntelFanConnector", "/sys/bus/platform/drivers/aspeed_pwm_tacho"}};
42
43static const boost::container::flat_set<std::string> MUX_TYPES = {
44 {"PCA9543Mux"}, {"PCA9545Mux"}};
45
46std::regex ILLEGAL_NAME_REGEX("[^A-Za-z0-9_]");
47
48void createOverlay(const std::string &templatePath,
49 const nlohmann::json &configuration);
50
51void unloadAllOverlays(void)
52{
53 boost::process::child c(DT_OVERLAY, "-d", OUTPUT_DIR, "-R");
54 c.wait();
55}
56
57// this is hopefully temporary, but some drivers can't detect changes
58// without an unbind and bind so this function must exist for now
59void forceProbe(const std::string &driver)
60{
61 std::ofstream driverUnbind(driver + "/unbind");
62 std::ofstream driverBind(driver + "/bind");
63 if (!driverUnbind.is_open())
64 {
65 std::cerr << "force probe error opening " << driver << "\n";
66 return;
67 }
68 if (!driverBind.is_open())
69 {
70 driverUnbind.close();
71 std::cerr << "force probe error opening " << driver << "\n";
72 return;
73 }
74
75 std::experimental::filesystem::path pathObj(driver);
76 for (auto &p : std::experimental::filesystem::directory_iterator(pathObj))
77 {
78 // symlinks are object names
79 if (is_symlink(p))
80 {
81 std::string driverName = p.path().filename();
82 driverUnbind << driverName;
83 driverBind << driverName;
84 break;
85 }
86 }
87 driverUnbind.close();
88 driverBind.close();
89}
90
91// helper function to make json types into string
92std::string jsonToString(const nlohmann::json &in)
93{
94 if (in.type() == nlohmann::json::value_t::string)
95 {
96 return in.get<std::string>();
97 }
98 else if (in.type() == nlohmann::json::value_t::array)
99 {
100 // remove brackets and comma from array
101 std::string array = in.dump();
102 array = array.substr(1, array.size() - 2);
103 boost::replace_all(array, ",", " ");
104 return array;
105 }
106 return in.dump();
107}
108
109// when muxes create new i2c devices, the symbols are not there to map the new
110// i2c devices to the mux address. this looks up the device tree path and adds
111// the new symbols so the new devices can be referenced via the phandle
112void fixupSymbols(
113 const std::vector<std::experimental::filesystem::path> &i2cDevsBefore)
114{
115 std::vector<std::experimental::filesystem::path> i2cDevsAfter;
James Feista3c180a2018-08-09 16:06:04 -0700116 findFiles(std::experimental::filesystem::path(I2C_DEVS_DIR),
117 R"(i2c-\d+)", i2cDevsAfter);
James Feistc95cb142018-02-26 10:41:42 -0800118
119 for (const auto &dev : i2cDevsAfter)
120 {
121 if (std::find(i2cDevsBefore.begin(), i2cDevsBefore.end(), dev) !=
122 i2cDevsBefore.end())
123 {
124 continue;
125 }
126 // removes everything before and including the '-' in /path/i2c-##
127 std::string bus =
128 std::regex_replace(dev.string(), std::regex("^.*-"), "");
129 std::string devtreeRef = dev.string() + "/device/of_node";
130 auto devtreePath = std::experimental::filesystem::path(devtreeRef);
131 std::string symbolPath =
132 std::experimental::filesystem::canonical(devtreePath);
133 symbolPath =
134 symbolPath.substr(sizeof("/sys/firmware/devicetree/base") - 1);
James Feist1e3e6982018-08-03 16:09:28 -0700135 nlohmann::json configuration = {{"Path", symbolPath},
James Feistd63d18a2018-07-19 15:23:45 -0700136 {"Type", "Symbol"},
James Feist1e3e6982018-08-03 16:09:28 -0700137 {"Bus", std::stoul(bus)},
James Feistd63d18a2018-07-19 15:23:45 -0700138 {"Name", "i2c" + bus}};
James Feistc95cb142018-02-26 10:41:42 -0800139 createOverlay(TEMPLATE_DIR + std::string("/Symbol.template"),
140 configuration);
141 }
142}
143
James Feist053a6642018-10-15 13:17:09 -0700144void exportDevice(const devices::ExportTemplate &exportTemplate,
145 const nlohmann::json &configuration)
146{
147
148 std::string parameters = exportTemplate.parameters;
149 std::string device = exportTemplate.device;
150 std::string name = "unknown";
151 const uint64_t *bus = nullptr;
152 const uint64_t *address = nullptr;
153
154 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
155 keyPair++)
156 {
157 std::string subsituteString;
158
159 if (keyPair.key() == "Name" &&
160 keyPair.value().type() == nlohmann::json::value_t::string)
161 {
162 subsituteString = std::regex_replace(
163 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
164 name = subsituteString;
165 }
166 else
167 {
168 subsituteString = jsonToString(keyPair.value());
169 }
170
171 if (keyPair.key() == "Bus")
172 {
173 bus = keyPair.value().get_ptr<const uint64_t *>();
174 }
175 else if (keyPair.key() == "Address")
176 {
177 address = keyPair.value().get_ptr<const uint64_t *>();
178 }
179 boost::replace_all(parameters, TEMPLATE_CHAR + keyPair.key(),
180 subsituteString);
181 boost::replace_all(device, TEMPLATE_CHAR + keyPair.key(),
182 subsituteString);
183 }
184
185 // if we found bus and address we can attempt to prevent errors
186 if (bus != nullptr && address != nullptr)
187 {
188 std::ostringstream hex;
189 hex << std::hex << *address;
190 const std::string &addressHex = hex.str();
191 std::string busStr = std::to_string(*bus);
192
193 std::experimental::filesystem::path devicePath(device);
194 const std::string &dir = devicePath.parent_path().string();
195 for (const auto &path :
196 std::experimental::filesystem::directory_iterator(dir))
197 {
198 if (!std::experimental::filesystem::is_directory(path))
199 {
200 continue;
201 }
202
203 const std::string &directoryName = path.path().filename();
204 if (boost::starts_with(directoryName, busStr) &&
205 boost::ends_with(directoryName, addressHex))
206 {
207 return; // already exported
208 }
209 }
210 }
211
212 std::ofstream deviceFile(device);
213 if (!deviceFile.good())
214 {
215 std::cerr << "Error writing " << device << "\n";
216 return;
217 }
218 deviceFile << parameters;
219 deviceFile.close();
220}
221
222// this is now deprecated
James Feistc95cb142018-02-26 10:41:42 -0800223void createOverlay(const std::string &templatePath,
224 const nlohmann::json &configuration)
225{
226 std::ifstream templateFile(templatePath);
227
228 if (!templateFile.is_open())
229 {
230 std::cerr << "createOverlay error opening " << templatePath << "\n";
231 return;
232 }
233 std::stringstream buff;
234 buff << templateFile.rdbuf();
235 std::string templateStr = buff.str();
236 std::string name = "unknown";
James Feistd63d18a2018-07-19 15:23:45 -0700237 std::string type = configuration["Type"];
James Feistc95cb142018-02-26 10:41:42 -0800238 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
239 keyPair++)
240 {
241 std::string subsituteString;
242
243 // device tree symbols are in decimal
James Feist1e3e6982018-08-03 16:09:28 -0700244 if (keyPair.key() == "Bus" &&
James Feistc95cb142018-02-26 10:41:42 -0800245 keyPair.value().type() == nlohmann::json::value_t::string)
246 {
247 unsigned int dec =
248 std::stoul(keyPair.value().get<std::string>(), nullptr, 16);
249 subsituteString = std::to_string(dec);
250 }
James Feistd63d18a2018-07-19 15:23:45 -0700251 else if (keyPair.key() == "Name" &&
James Feistc95cb142018-02-26 10:41:42 -0800252 keyPair.value().type() == nlohmann::json::value_t::string)
253 {
254 subsituteString = std::regex_replace(
255 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
256 name = subsituteString;
257 }
James Feist1e3e6982018-08-03 16:09:28 -0700258 else if (keyPair.key() == "Address")
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700259 {
260 if (keyPair.value().type() == nlohmann::json::value_t::string)
261 {
262 subsituteString = keyPair.value().get<std::string>();
263 subsituteString.erase(
264 0, subsituteString.find_first_not_of(HEX_FORMAT_STR));
265 }
266 else
267 {
268 std::ostringstream hex;
269 hex << std::hex << keyPair.value().get<unsigned int>();
270 subsituteString = hex.str();
271 }
272 }
James Feistc95cb142018-02-26 10:41:42 -0800273 else
274 {
275 subsituteString = jsonToString(keyPair.value());
276 }
277 boost::replace_all(templateStr, TEMPLATE_CHAR + keyPair.key(),
278 subsituteString);
279 }
280 // todo: this is a lame way to fill in platform, but we only
281 // care about ast2500 right now
James Feist1e3e6982018-08-03 16:09:28 -0700282 boost::replace_all(templateStr, TEMPLATE_CHAR + std::string("Platform"),
James Feistc95cb142018-02-26 10:41:42 -0800283 PLATFORM);
284 std::string dtsFilename =
285 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dts";
286 std::string dtboFilename =
287 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dtbo";
288
289 std::ofstream out(dtsFilename);
290 if (!out.is_open())
291 {
292 std::cerr << "createOverlay error opening " << dtsFilename << "\n";
293 return;
294 }
295 out << templateStr;
296 out.close();
297
James Feistc95cb142018-02-26 10:41:42 -0800298 // compile dtbo and load overlay
299 boost::process::child c1(DTC, "-@", "-q", "-I", "dts", "-O", "dtb", "-o",
300 dtboFilename, dtsFilename);
301 c1.wait();
302 if (c1.exit_code())
303 {
304 std::cerr << "DTC error with file " << dtsFilename << "\n";
305 return;
306 }
307 boost::process::child c2(DT_OVERLAY, "-d", OUTPUT_DIR, name + "_" + type);
308 c2.wait();
309 if (c2.exit_code())
310 {
311 std::cerr << "DTOverlay error with file " << dtboFilename << "\n";
312 return;
313 }
314 auto findForceProbe = FORCE_PROBES.find(type);
315 if (findForceProbe != FORCE_PROBES.end())
316 {
317 forceProbe(findForceProbe->second);
318 }
James Feistc95cb142018-02-26 10:41:42 -0800319}
320
321bool loadOverlays(const nlohmann::json &systemConfiguration)
322{
323
324 std::vector<std::experimental::filesystem::path> paths;
James Feista3c180a2018-08-09 16:06:04 -0700325 if (!findFiles(std::experimental::filesystem::path(TEMPLATE_DIR),
326 R"(.*\.template)", paths))
James Feistc95cb142018-02-26 10:41:42 -0800327 {
328 std::cerr << "Unable to find any tempate files in " << TEMPLATE_DIR
329 << "\n";
330 return false;
331 }
332
333 std::experimental::filesystem::create_directory(OUTPUT_DIR);
334 for (auto entity = systemConfiguration.begin();
335 entity != systemConfiguration.end(); entity++)
336 {
James Feist1e3e6982018-08-03 16:09:28 -0700337 auto findExposes = entity.value().find("Exposes");
James Feistc95cb142018-02-26 10:41:42 -0800338 if (findExposes == entity.value().end() ||
339 findExposes->type() != nlohmann::json::value_t::array)
340 {
341 continue;
342 }
343
344 for (auto &configuration : *findExposes)
345 {
James Feist1e3e6982018-08-03 16:09:28 -0700346 auto findStatus = configuration.find("Status");
James Feistc95cb142018-02-26 10:41:42 -0800347 // status missing is assumed to be 'okay'
348 if (findStatus != configuration.end() && *findStatus == "disabled")
349 {
350 continue;
351 }
James Feistd63d18a2018-07-19 15:23:45 -0700352 auto findType = configuration.find("Type");
James Feistc95cb142018-02-26 10:41:42 -0800353 if (findType == configuration.end() ||
354 findType->type() != nlohmann::json::value_t::string)
355 {
356 continue;
357 }
358 std::string type = findType.value().get<std::string>();
James Feistce4367c2018-10-16 09:19:57 -0700359#if OVERLAYS
360
James Feistc95cb142018-02-26 10:41:42 -0800361 std::string typeFile = type + std::string(".template");
James Feist053a6642018-10-15 13:17:09 -0700362 for (const auto &path : paths)
James Feistc95cb142018-02-26 10:41:42 -0800363 {
364 if (path.filename() != typeFile)
365 {
366 continue;
367 }
368 createOverlay(path.string(), configuration);
369 break;
370 }
James Feistce4367c2018-10-16 09:19:57 -0700371#endif
James Feist053a6642018-10-15 13:17:09 -0700372 auto device = devices::exportTemplates.find(type.c_str());
373 if (device != devices::exportTemplates.end())
374 {
375 exportDevice(device->second, configuration);
376 }
James Feistc95cb142018-02-26 10:41:42 -0800377 }
378 }
379
380 return true;
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700381}