blob: 3fdb554a5d7d37b854c06b54c74b265f48485955 [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
James Feistc95cb142018-02-26 10:41:42 -080017#include <Overlay.hpp>
18#include <Utils.hpp>
James Feista465ccc2019-02-08 12:51:01 -080019#include <boost/algorithm/string/predicate.hpp>
20#include <boost/container/flat_map.hpp>
21#include <boost/container/flat_set.hpp>
22#include <boost/process/child.hpp>
23#include <devices.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070024#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080025#include <iomanip>
26#include <iostream>
27#include <nlohmann/json.hpp>
28#include <regex>
29#include <string>
James Feistc95cb142018-02-26 10:41:42 -080030
James Feista465ccc2019-02-08 12:51:01 -080031constexpr const char* DT_OVERLAY = "/usr/bin/dtoverlay";
32constexpr const char* DTC = "/usr/bin/dtc";
33constexpr const char* OUTPUT_DIR = "/tmp/overlays";
34constexpr const char* TEMPLATE_DIR = PACKAGE_DIR "overlay_templates";
35constexpr const char* TEMPLATE_CHAR = "$";
36constexpr const char* HEX_FORMAT_STR = "0x";
37constexpr const char* PLATFORM = "aspeed,ast2500";
38constexpr const char* I2C_DEVS_DIR = "/sys/bus/i2c/devices";
39constexpr const char* MUX_SYMLINK_DIR = "/dev/i2c-mux";
James Feistc95cb142018-02-26 10:41:42 -080040
41// some drivers need to be unbind / bind to load device tree changes
42static const boost::container::flat_map<std::string, std::string> FORCE_PROBES =
43 {{"IntelFanConnector", "/sys/bus/platform/drivers/aspeed_pwm_tacho"}};
44
James Feistc95cb142018-02-26 10:41:42 -080045std::regex ILLEGAL_NAME_REGEX("[^A-Za-z0-9_]");
46
James Feista465ccc2019-02-08 12:51:01 -080047void createOverlay(const std::string& templatePath,
48 const nlohmann::json& configuration);
James Feistc95cb142018-02-26 10:41:42 -080049
50void unloadAllOverlays(void)
51{
52 boost::process::child c(DT_OVERLAY, "-d", OUTPUT_DIR, "-R");
53 c.wait();
54}
55
56// this is hopefully temporary, but some drivers can't detect changes
57// without an unbind and bind so this function must exist for now
James Feista465ccc2019-02-08 12:51:01 -080058void forceProbe(const std::string& driver)
James Feistc95cb142018-02-26 10:41:42 -080059{
60 std::ofstream driverUnbind(driver + "/unbind");
61 std::ofstream driverBind(driver + "/bind");
62 if (!driverUnbind.is_open())
63 {
64 std::cerr << "force probe error opening " << driver << "\n";
65 return;
66 }
67 if (!driverBind.is_open())
68 {
69 driverUnbind.close();
70 std::cerr << "force probe error opening " << driver << "\n";
71 return;
72 }
73
Ed Tanous072e25d2018-12-16 21:45:20 -080074 std::filesystem::path pathObj(driver);
James Feista465ccc2019-02-08 12:51:01 -080075 for (auto& p : std::filesystem::directory_iterator(pathObj))
James Feistc95cb142018-02-26 10:41:42 -080076 {
77 // symlinks are object names
78 if (is_symlink(p))
79 {
80 std::string driverName = p.path().filename();
81 driverUnbind << driverName;
82 driverBind << driverName;
83 break;
84 }
85 }
86 driverUnbind.close();
87 driverBind.close();
88}
89
90// helper function to make json types into string
James Feista465ccc2019-02-08 12:51:01 -080091std::string jsonToString(const nlohmann::json& in)
James Feistc95cb142018-02-26 10:41:42 -080092{
93 if (in.type() == nlohmann::json::value_t::string)
94 {
95 return in.get<std::string>();
96 }
97 else if (in.type() == nlohmann::json::value_t::array)
98 {
99 // remove brackets and comma from array
100 std::string array = in.dump();
101 array = array.substr(1, array.size() - 2);
102 boost::replace_all(array, ",", " ");
103 return array;
104 }
105 return in.dump();
106}
107
Ed Tanousee3357a2019-02-26 12:44:14 -0800108void linkMux(const std::string& muxName, size_t busIndex, size_t address,
James Feista465ccc2019-02-08 12:51:01 -0800109 const nlohmann::json::array_t& channelNames)
James Feistc95cb142018-02-26 10:41:42 -0800110{
James Feist286babc2019-02-07 16:48:28 -0800111 std::error_code ec;
Ed Tanousee3357a2019-02-26 12:44:14 -0800112 std::filesystem::path muxSymlinkDir(MUX_SYMLINK_DIR);
113 std::filesystem::create_directory(muxSymlinkDir, ec);
114 // ignore error codes here if the directory already exists
115 ec.clear();
116 std::filesystem::path linkDir = muxSymlinkDir / muxName;
117 std::filesystem::create_directory(linkDir, ec);
118
119 std::ostringstream hexAddress;
120 hexAddress << std::hex << std::setfill('0') << std::setw(4) << address;
James Feist286babc2019-02-07 16:48:28 -0800121
122 std::filesystem::path devDir(I2C_DEVS_DIR);
Ed Tanousee3357a2019-02-26 12:44:14 -0800123 devDir /= std::to_string(busIndex) + "-" + hexAddress.str();
James Feist286babc2019-02-07 16:48:28 -0800124
James Feist286babc2019-02-07 16:48:28 -0800125 std::string channelName;
James Feist9d746262019-02-13 15:08:10 -0800126
Ed Tanousee3357a2019-02-26 12:44:14 -0800127 for (std::size_t channelIndex = 0; channelIndex < channelNames.size();
128 channelIndex++)
James Feist286babc2019-02-07 16:48:28 -0800129 {
Ed Tanousee3357a2019-02-26 12:44:14 -0800130 const std::string* channelName =
131 channelNames[channelIndex].get_ptr<const std::string*>();
132 if (channelName == nullptr)
133 {
134 continue;
135 }
136 if (channelName->empty())
137 {
138 continue;
139 }
140
141 std::filesystem::path channelPath =
142 devDir / ("channel-" + std::to_string(channelIndex));
143 if (!is_symlink(channelPath))
144 {
145 std::cerr << channelPath << "for mux channel " << *channelName
146 << " doesn't exist!\n";
147 continue;
148 }
149 std::filesystem::path bus = std::filesystem::read_symlink(channelPath);
150
151 std::filesystem::path fp("/dev" / bus.filename());
152 std::filesystem::path link(linkDir / *channelName);
153
154 std::filesystem::create_symlink(fp, link, ec);
155 if (ec)
156 {
157 std::cerr << "Failure creating symlink for " << fp << " to " << link
158 << "\n";
159 }
James Feistc95cb142018-02-26 10:41:42 -0800160 }
161}
162
James Feista465ccc2019-02-08 12:51:01 -0800163void exportDevice(const std::string& type,
164 const devices::ExportTemplate& exportTemplate,
165 const nlohmann::json& configuration)
James Feist053a6642018-10-15 13:17:09 -0700166{
167
168 std::string parameters = exportTemplate.parameters;
169 std::string device = exportTemplate.device;
170 std::string name = "unknown";
James Feista465ccc2019-02-08 12:51:01 -0800171 const uint64_t* bus = nullptr;
172 const uint64_t* address = nullptr;
173 const nlohmann::json::array_t* channels = nullptr;
James Feist053a6642018-10-15 13:17:09 -0700174
175 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
176 keyPair++)
177 {
178 std::string subsituteString;
179
180 if (keyPair.key() == "Name" &&
181 keyPair.value().type() == nlohmann::json::value_t::string)
182 {
183 subsituteString = std::regex_replace(
184 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
185 name = subsituteString;
186 }
187 else
188 {
189 subsituteString = jsonToString(keyPair.value());
190 }
191
192 if (keyPair.key() == "Bus")
193 {
James Feista465ccc2019-02-08 12:51:01 -0800194 bus = keyPair.value().get_ptr<const uint64_t*>();
James Feist053a6642018-10-15 13:17:09 -0700195 }
196 else if (keyPair.key() == "Address")
197 {
James Feista465ccc2019-02-08 12:51:01 -0800198 address = keyPair.value().get_ptr<const uint64_t*>();
James Feist053a6642018-10-15 13:17:09 -0700199 }
James Feist286babc2019-02-07 16:48:28 -0800200 else if (keyPair.key() == "ChannelNames")
201 {
202 channels =
James Feista465ccc2019-02-08 12:51:01 -0800203 keyPair.value().get_ptr<const nlohmann::json::array_t*>();
James Feist286babc2019-02-07 16:48:28 -0800204 }
James Feist053a6642018-10-15 13:17:09 -0700205 boost::replace_all(parameters, TEMPLATE_CHAR + keyPair.key(),
206 subsituteString);
207 boost::replace_all(device, TEMPLATE_CHAR + keyPair.key(),
208 subsituteString);
209 }
210
211 // if we found bus and address we can attempt to prevent errors
212 if (bus != nullptr && address != nullptr)
213 {
214 std::ostringstream hex;
215 hex << std::hex << *address;
James Feista465ccc2019-02-08 12:51:01 -0800216 const std::string& addressHex = hex.str();
James Feist053a6642018-10-15 13:17:09 -0700217 std::string busStr = std::to_string(*bus);
218
Ed Tanous072e25d2018-12-16 21:45:20 -0800219 std::filesystem::path devicePath(device);
James Feist58b9c262019-02-12 13:38:45 -0800220 std::filesystem::path parentPath = devicePath.parent_path();
221 if (std::filesystem::is_directory(parentPath))
James Feist053a6642018-10-15 13:17:09 -0700222 {
James Feist58b9c262019-02-12 13:38:45 -0800223 for (const auto& path :
224 std::filesystem::directory_iterator(parentPath))
James Feist053a6642018-10-15 13:17:09 -0700225 {
James Feist58b9c262019-02-12 13:38:45 -0800226 if (!std::filesystem::is_directory(path))
227 {
228 continue;
229 }
James Feist053a6642018-10-15 13:17:09 -0700230
James Feist58b9c262019-02-12 13:38:45 -0800231 const std::string& directoryName = path.path().filename();
232 if (boost::starts_with(directoryName, busStr) &&
233 boost::ends_with(directoryName, addressHex))
234 {
235 return; // already exported
236 }
James Feist053a6642018-10-15 13:17:09 -0700237 }
238 }
239 }
240
241 std::ofstream deviceFile(device);
242 if (!deviceFile.good())
243 {
244 std::cerr << "Error writing " << device << "\n";
245 return;
246 }
247 deviceFile << parameters;
248 deviceFile.close();
James Feist286babc2019-02-07 16:48:28 -0800249 if (boost::ends_with(type, "Mux") && bus && address && channels)
250 {
251 linkMux(name, *bus, *address, *channels);
252 }
James Feist053a6642018-10-15 13:17:09 -0700253}
254
255// this is now deprecated
James Feista465ccc2019-02-08 12:51:01 -0800256void createOverlay(const std::string& templatePath,
257 const nlohmann::json& configuration)
James Feistc95cb142018-02-26 10:41:42 -0800258{
259 std::ifstream templateFile(templatePath);
260
261 if (!templateFile.is_open())
262 {
263 std::cerr << "createOverlay error opening " << templatePath << "\n";
264 return;
265 }
266 std::stringstream buff;
267 buff << templateFile.rdbuf();
268 std::string templateStr = buff.str();
269 std::string name = "unknown";
James Feistd63d18a2018-07-19 15:23:45 -0700270 std::string type = configuration["Type"];
James Feistc95cb142018-02-26 10:41:42 -0800271 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
272 keyPair++)
273 {
274 std::string subsituteString;
275
276 // device tree symbols are in decimal
James Feist1e3e6982018-08-03 16:09:28 -0700277 if (keyPair.key() == "Bus" &&
James Feistc95cb142018-02-26 10:41:42 -0800278 keyPair.value().type() == nlohmann::json::value_t::string)
279 {
280 unsigned int dec =
281 std::stoul(keyPair.value().get<std::string>(), nullptr, 16);
282 subsituteString = std::to_string(dec);
283 }
James Feistd63d18a2018-07-19 15:23:45 -0700284 else if (keyPair.key() == "Name" &&
James Feistc95cb142018-02-26 10:41:42 -0800285 keyPair.value().type() == nlohmann::json::value_t::string)
286 {
287 subsituteString = std::regex_replace(
288 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
289 name = subsituteString;
290 }
James Feist1e3e6982018-08-03 16:09:28 -0700291 else if (keyPair.key() == "Address")
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700292 {
293 if (keyPair.value().type() == nlohmann::json::value_t::string)
294 {
295 subsituteString = keyPair.value().get<std::string>();
296 subsituteString.erase(
297 0, subsituteString.find_first_not_of(HEX_FORMAT_STR));
298 }
299 else
300 {
301 std::ostringstream hex;
302 hex << std::hex << keyPair.value().get<unsigned int>();
303 subsituteString = hex.str();
304 }
305 }
James Feistc95cb142018-02-26 10:41:42 -0800306 else
307 {
308 subsituteString = jsonToString(keyPair.value());
309 }
310 boost::replace_all(templateStr, TEMPLATE_CHAR + keyPair.key(),
311 subsituteString);
312 }
313 // todo: this is a lame way to fill in platform, but we only
314 // care about ast2500 right now
James Feist1e3e6982018-08-03 16:09:28 -0700315 boost::replace_all(templateStr, TEMPLATE_CHAR + std::string("Platform"),
James Feistc95cb142018-02-26 10:41:42 -0800316 PLATFORM);
317 std::string dtsFilename =
318 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dts";
319 std::string dtboFilename =
320 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dtbo";
321
322 std::ofstream out(dtsFilename);
323 if (!out.is_open())
324 {
325 std::cerr << "createOverlay error opening " << dtsFilename << "\n";
326 return;
327 }
328 out << templateStr;
329 out.close();
330
James Feistc95cb142018-02-26 10:41:42 -0800331 // compile dtbo and load overlay
332 boost::process::child c1(DTC, "-@", "-q", "-I", "dts", "-O", "dtb", "-o",
333 dtboFilename, dtsFilename);
334 c1.wait();
335 if (c1.exit_code())
336 {
337 std::cerr << "DTC error with file " << dtsFilename << "\n";
338 return;
339 }
340 boost::process::child c2(DT_OVERLAY, "-d", OUTPUT_DIR, name + "_" + type);
341 c2.wait();
342 if (c2.exit_code())
343 {
344 std::cerr << "DTOverlay error with file " << dtboFilename << "\n";
345 return;
346 }
347 auto findForceProbe = FORCE_PROBES.find(type);
348 if (findForceProbe != FORCE_PROBES.end())
349 {
350 forceProbe(findForceProbe->second);
351 }
James Feistc95cb142018-02-26 10:41:42 -0800352}
353
James Feista465ccc2019-02-08 12:51:01 -0800354bool loadOverlays(const nlohmann::json& systemConfiguration)
James Feistc95cb142018-02-26 10:41:42 -0800355{
356
Ed Tanous072e25d2018-12-16 21:45:20 -0800357 std::vector<std::filesystem::path> paths;
358 if (!findFiles(std::filesystem::path(TEMPLATE_DIR),
James Feista3c180a2018-08-09 16:06:04 -0700359 R"(.*\.template)", paths))
James Feistc95cb142018-02-26 10:41:42 -0800360 {
361 std::cerr << "Unable to find any tempate files in " << TEMPLATE_DIR
362 << "\n";
363 return false;
364 }
365
Ed Tanous072e25d2018-12-16 21:45:20 -0800366 std::filesystem::create_directory(OUTPUT_DIR);
James Feistc95cb142018-02-26 10:41:42 -0800367 for (auto entity = systemConfiguration.begin();
368 entity != systemConfiguration.end(); entity++)
369 {
James Feist1e3e6982018-08-03 16:09:28 -0700370 auto findExposes = entity.value().find("Exposes");
James Feistc95cb142018-02-26 10:41:42 -0800371 if (findExposes == entity.value().end() ||
372 findExposes->type() != nlohmann::json::value_t::array)
373 {
374 continue;
375 }
376
James Feista465ccc2019-02-08 12:51:01 -0800377 for (auto& configuration : *findExposes)
James Feistc95cb142018-02-26 10:41:42 -0800378 {
James Feist1e3e6982018-08-03 16:09:28 -0700379 auto findStatus = configuration.find("Status");
James Feistc95cb142018-02-26 10:41:42 -0800380 // status missing is assumed to be 'okay'
381 if (findStatus != configuration.end() && *findStatus == "disabled")
382 {
383 continue;
384 }
James Feistd63d18a2018-07-19 15:23:45 -0700385 auto findType = configuration.find("Type");
James Feistc95cb142018-02-26 10:41:42 -0800386 if (findType == configuration.end() ||
387 findType->type() != nlohmann::json::value_t::string)
388 {
389 continue;
390 }
391 std::string type = findType.value().get<std::string>();
James Feistce4367c2018-10-16 09:19:57 -0700392#if OVERLAYS
393
James Feistc95cb142018-02-26 10:41:42 -0800394 std::string typeFile = type + std::string(".template");
James Feista465ccc2019-02-08 12:51:01 -0800395 for (const auto& path : paths)
James Feistc95cb142018-02-26 10:41:42 -0800396 {
397 if (path.filename() != typeFile)
398 {
399 continue;
400 }
401 createOverlay(path.string(), configuration);
402 break;
403 }
James Feistce4367c2018-10-16 09:19:57 -0700404#endif
James Feist053a6642018-10-15 13:17:09 -0700405 auto device = devices::exportTemplates.find(type.c_str());
406 if (device != devices::exportTemplates.end())
407 {
James Feist286babc2019-02-07 16:48:28 -0800408 exportDevice(type, device->second, configuration);
James Feist053a6642018-10-15 13:17:09 -0700409 }
James Feistc95cb142018-02-26 10:41:42 -0800410 }
411 }
412
413 return true;
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700414}