blob: 3877ff2b2c313e2b8720559ae1e2ad6031eed413 [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
Ed Tanousee3357a2019-02-26 12:44:14 -0800125 for (std::size_t channelIndex = 0; channelIndex < channelNames.size();
126 channelIndex++)
James Feist286babc2019-02-07 16:48:28 -0800127 {
Ed Tanousee3357a2019-02-26 12:44:14 -0800128 const std::string* channelName =
129 channelNames[channelIndex].get_ptr<const std::string*>();
130 if (channelName == nullptr)
131 {
132 continue;
133 }
134 if (channelName->empty())
135 {
136 continue;
137 }
138
139 std::filesystem::path channelPath =
140 devDir / ("channel-" + std::to_string(channelIndex));
141 if (!is_symlink(channelPath))
142 {
143 std::cerr << channelPath << "for mux channel " << *channelName
144 << " doesn't exist!\n";
145 continue;
146 }
147 std::filesystem::path bus = std::filesystem::read_symlink(channelPath);
148
149 std::filesystem::path fp("/dev" / bus.filename());
150 std::filesystem::path link(linkDir / *channelName);
151
152 std::filesystem::create_symlink(fp, link, ec);
153 if (ec)
154 {
155 std::cerr << "Failure creating symlink for " << fp << " to " << link
156 << "\n";
157 }
James Feistc95cb142018-02-26 10:41:42 -0800158 }
159}
160
James Feista465ccc2019-02-08 12:51:01 -0800161void exportDevice(const std::string& type,
162 const devices::ExportTemplate& exportTemplate,
163 const nlohmann::json& configuration)
James Feist053a6642018-10-15 13:17:09 -0700164{
165
166 std::string parameters = exportTemplate.parameters;
167 std::string device = exportTemplate.device;
168 std::string name = "unknown";
James Feista465ccc2019-02-08 12:51:01 -0800169 const uint64_t* bus = nullptr;
170 const uint64_t* address = nullptr;
171 const nlohmann::json::array_t* channels = nullptr;
James Feist053a6642018-10-15 13:17:09 -0700172
173 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
174 keyPair++)
175 {
176 std::string subsituteString;
177
178 if (keyPair.key() == "Name" &&
179 keyPair.value().type() == nlohmann::json::value_t::string)
180 {
181 subsituteString = std::regex_replace(
182 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
183 name = subsituteString;
184 }
185 else
186 {
187 subsituteString = jsonToString(keyPair.value());
188 }
189
190 if (keyPair.key() == "Bus")
191 {
James Feista465ccc2019-02-08 12:51:01 -0800192 bus = keyPair.value().get_ptr<const uint64_t*>();
James Feist053a6642018-10-15 13:17:09 -0700193 }
194 else if (keyPair.key() == "Address")
195 {
James Feista465ccc2019-02-08 12:51:01 -0800196 address = keyPair.value().get_ptr<const uint64_t*>();
James Feist053a6642018-10-15 13:17:09 -0700197 }
James Feist286babc2019-02-07 16:48:28 -0800198 else if (keyPair.key() == "ChannelNames")
199 {
200 channels =
James Feista465ccc2019-02-08 12:51:01 -0800201 keyPair.value().get_ptr<const nlohmann::json::array_t*>();
James Feist286babc2019-02-07 16:48:28 -0800202 }
James Feist053a6642018-10-15 13:17:09 -0700203 boost::replace_all(parameters, TEMPLATE_CHAR + keyPair.key(),
204 subsituteString);
205 boost::replace_all(device, TEMPLATE_CHAR + keyPair.key(),
206 subsituteString);
207 }
208
209 // if we found bus and address we can attempt to prevent errors
210 if (bus != nullptr && address != nullptr)
211 {
212 std::ostringstream hex;
213 hex << std::hex << *address;
James Feista465ccc2019-02-08 12:51:01 -0800214 const std::string& addressHex = hex.str();
James Feist053a6642018-10-15 13:17:09 -0700215 std::string busStr = std::to_string(*bus);
216
Ed Tanous072e25d2018-12-16 21:45:20 -0800217 std::filesystem::path devicePath(device);
James Feist58b9c262019-02-12 13:38:45 -0800218 std::filesystem::path parentPath = devicePath.parent_path();
219 if (std::filesystem::is_directory(parentPath))
James Feist053a6642018-10-15 13:17:09 -0700220 {
James Feist58b9c262019-02-12 13:38:45 -0800221 for (const auto& path :
222 std::filesystem::directory_iterator(parentPath))
James Feist053a6642018-10-15 13:17:09 -0700223 {
James Feist58b9c262019-02-12 13:38:45 -0800224 if (!std::filesystem::is_directory(path))
225 {
226 continue;
227 }
James Feist053a6642018-10-15 13:17:09 -0700228
James Feist58b9c262019-02-12 13:38:45 -0800229 const std::string& directoryName = path.path().filename();
230 if (boost::starts_with(directoryName, busStr) &&
231 boost::ends_with(directoryName, addressHex))
232 {
233 return; // already exported
234 }
James Feist053a6642018-10-15 13:17:09 -0700235 }
236 }
237 }
238
239 std::ofstream deviceFile(device);
240 if (!deviceFile.good())
241 {
242 std::cerr << "Error writing " << device << "\n";
243 return;
244 }
245 deviceFile << parameters;
246 deviceFile.close();
James Feist286babc2019-02-07 16:48:28 -0800247 if (boost::ends_with(type, "Mux") && bus && address && channels)
248 {
James Feist98132792019-07-09 13:29:09 -0700249 linkMux(name, static_cast<size_t>(*bus), static_cast<size_t>(*address),
250 *channels);
James Feist286babc2019-02-07 16:48:28 -0800251 }
James Feist053a6642018-10-15 13:17:09 -0700252}
253
254// this is now deprecated
James Feista465ccc2019-02-08 12:51:01 -0800255void createOverlay(const std::string& templatePath,
256 const nlohmann::json& configuration)
James Feistc95cb142018-02-26 10:41:42 -0800257{
258 std::ifstream templateFile(templatePath);
259
260 if (!templateFile.is_open())
261 {
262 std::cerr << "createOverlay error opening " << templatePath << "\n";
263 return;
264 }
265 std::stringstream buff;
266 buff << templateFile.rdbuf();
267 std::string templateStr = buff.str();
268 std::string name = "unknown";
James Feistd63d18a2018-07-19 15:23:45 -0700269 std::string type = configuration["Type"];
James Feistc95cb142018-02-26 10:41:42 -0800270 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
271 keyPair++)
272 {
273 std::string subsituteString;
274
275 // device tree symbols are in decimal
James Feist1e3e6982018-08-03 16:09:28 -0700276 if (keyPair.key() == "Bus" &&
James Feistc95cb142018-02-26 10:41:42 -0800277 keyPair.value().type() == nlohmann::json::value_t::string)
278 {
James Feist98132792019-07-09 13:29:09 -0700279 long unsigned int dec =
James Feistc95cb142018-02-26 10:41:42 -0800280 std::stoul(keyPair.value().get<std::string>(), nullptr, 16);
281 subsituteString = std::to_string(dec);
282 }
James Feistd63d18a2018-07-19 15:23:45 -0700283 else if (keyPair.key() == "Name" &&
James Feistc95cb142018-02-26 10:41:42 -0800284 keyPair.value().type() == nlohmann::json::value_t::string)
285 {
286 subsituteString = std::regex_replace(
287 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
288 name = subsituteString;
289 }
James Feist1e3e6982018-08-03 16:09:28 -0700290 else if (keyPair.key() == "Address")
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700291 {
292 if (keyPair.value().type() == nlohmann::json::value_t::string)
293 {
294 subsituteString = keyPair.value().get<std::string>();
295 subsituteString.erase(
296 0, subsituteString.find_first_not_of(HEX_FORMAT_STR));
297 }
298 else
299 {
300 std::ostringstream hex;
301 hex << std::hex << keyPair.value().get<unsigned int>();
302 subsituteString = hex.str();
303 }
304 }
James Feistc95cb142018-02-26 10:41:42 -0800305 else
306 {
307 subsituteString = jsonToString(keyPair.value());
308 }
309 boost::replace_all(templateStr, TEMPLATE_CHAR + keyPair.key(),
310 subsituteString);
311 }
312 // todo: this is a lame way to fill in platform, but we only
313 // care about ast2500 right now
James Feist1e3e6982018-08-03 16:09:28 -0700314 boost::replace_all(templateStr, TEMPLATE_CHAR + std::string("Platform"),
James Feistc95cb142018-02-26 10:41:42 -0800315 PLATFORM);
316 std::string dtsFilename =
317 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dts";
318 std::string dtboFilename =
319 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dtbo";
320
321 std::ofstream out(dtsFilename);
322 if (!out.is_open())
323 {
324 std::cerr << "createOverlay error opening " << dtsFilename << "\n";
325 return;
326 }
327 out << templateStr;
328 out.close();
329
James Feistc95cb142018-02-26 10:41:42 -0800330 // compile dtbo and load overlay
331 boost::process::child c1(DTC, "-@", "-q", "-I", "dts", "-O", "dtb", "-o",
332 dtboFilename, dtsFilename);
333 c1.wait();
334 if (c1.exit_code())
335 {
336 std::cerr << "DTC error with file " << dtsFilename << "\n";
337 return;
338 }
339 boost::process::child c2(DT_OVERLAY, "-d", OUTPUT_DIR, name + "_" + type);
340 c2.wait();
341 if (c2.exit_code())
342 {
343 std::cerr << "DTOverlay error with file " << dtboFilename << "\n";
344 return;
345 }
346 auto findForceProbe = FORCE_PROBES.find(type);
347 if (findForceProbe != FORCE_PROBES.end())
348 {
349 forceProbe(findForceProbe->second);
350 }
James Feistc95cb142018-02-26 10:41:42 -0800351}
352
James Feista465ccc2019-02-08 12:51:01 -0800353bool loadOverlays(const nlohmann::json& systemConfiguration)
James Feistc95cb142018-02-26 10:41:42 -0800354{
355
Ed Tanous072e25d2018-12-16 21:45:20 -0800356 std::vector<std::filesystem::path> paths;
James Feistf861da82019-06-27 12:05:16 -0700357 if (!findFiles(std::filesystem::path(TEMPLATE_DIR), R"(.*\.template)",
358 paths))
James Feistc95cb142018-02-26 10:41:42 -0800359 {
360 std::cerr << "Unable to find any tempate files in " << TEMPLATE_DIR
361 << "\n";
362 return false;
363 }
364
Ed Tanous072e25d2018-12-16 21:45:20 -0800365 std::filesystem::create_directory(OUTPUT_DIR);
James Feistc95cb142018-02-26 10:41:42 -0800366 for (auto entity = systemConfiguration.begin();
367 entity != systemConfiguration.end(); entity++)
368 {
James Feist1e3e6982018-08-03 16:09:28 -0700369 auto findExposes = entity.value().find("Exposes");
James Feistc95cb142018-02-26 10:41:42 -0800370 if (findExposes == entity.value().end() ||
371 findExposes->type() != nlohmann::json::value_t::array)
372 {
373 continue;
374 }
375
James Feista465ccc2019-02-08 12:51:01 -0800376 for (auto& configuration : *findExposes)
James Feistc95cb142018-02-26 10:41:42 -0800377 {
James Feist1e3e6982018-08-03 16:09:28 -0700378 auto findStatus = configuration.find("Status");
James Feistc95cb142018-02-26 10:41:42 -0800379 // status missing is assumed to be 'okay'
380 if (findStatus != configuration.end() && *findStatus == "disabled")
381 {
382 continue;
383 }
James Feistd63d18a2018-07-19 15:23:45 -0700384 auto findType = configuration.find("Type");
James Feistc95cb142018-02-26 10:41:42 -0800385 if (findType == configuration.end() ||
386 findType->type() != nlohmann::json::value_t::string)
387 {
388 continue;
389 }
390 std::string type = findType.value().get<std::string>();
James Feistce4367c2018-10-16 09:19:57 -0700391#if OVERLAYS
392
James Feistc95cb142018-02-26 10:41:42 -0800393 std::string typeFile = type + std::string(".template");
James Feista465ccc2019-02-08 12:51:01 -0800394 for (const auto& path : paths)
James Feistc95cb142018-02-26 10:41:42 -0800395 {
396 if (path.filename() != typeFile)
397 {
398 continue;
399 }
400 createOverlay(path.string(), configuration);
401 break;
402 }
James Feistce4367c2018-10-16 09:19:57 -0700403#endif
James Feist053a6642018-10-15 13:17:09 -0700404 auto device = devices::exportTemplates.find(type.c_str());
405 if (device != devices::exportTemplates.end())
406 {
James Feist286babc2019-02-07 16:48:28 -0800407 exportDevice(type, device->second, configuration);
James Feist053a6642018-10-15 13:17:09 -0700408 }
James Feistc95cb142018-02-26 10:41:42 -0800409 }
410 }
411
412 return true;
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700413}