blob: 697456d33831ca27b794e7d640fdadc556f2ab59 [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>
21#include <experimental/filesystem>
22#include <boost/container/flat_map.hpp>
23#include <boost/container/flat_set.hpp>
24#include <nlohmann/json.hpp>
25#include <Overlay.hpp>
26#include <Utils.hpp>
27
28constexpr const char *DT_OVERLAY = "/usr/bin/dtoverlay";
29constexpr const char *DTC = "/usr/bin/dtc";
30constexpr const char *OUTPUT_DIR = "/tmp/overlays";
31constexpr const char *TEMPLATE_DIR = "/usr/share/overlay_templates";
32constexpr const char *TEMPLATE_CHAR = "$";
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -070033constexpr const char *HEX_FORMAT_STR = "0x";
James Feistc95cb142018-02-26 10:41:42 -080034constexpr const char *PLATFORM = "aspeed,ast2500";
35constexpr const char *I2C_DEVS_DIR = "/sys/class/i2c-dev";
36
37// some drivers need to be unbind / bind to load device tree changes
38static const boost::container::flat_map<std::string, std::string> FORCE_PROBES =
39 {{"IntelFanConnector", "/sys/bus/platform/drivers/aspeed_pwm_tacho"}};
40
41static const boost::container::flat_set<std::string> MUX_TYPES = {
42 {"PCA9543Mux"}, {"PCA9545Mux"}};
43
44std::regex ILLEGAL_NAME_REGEX("[^A-Za-z0-9_]");
45
46void createOverlay(const std::string &templatePath,
47 const nlohmann::json &configuration);
48
49void unloadAllOverlays(void)
50{
51 boost::process::child c(DT_OVERLAY, "-d", OUTPUT_DIR, "-R");
52 c.wait();
53}
54
55// this is hopefully temporary, but some drivers can't detect changes
56// without an unbind and bind so this function must exist for now
57void forceProbe(const std::string &driver)
58{
59 std::ofstream driverUnbind(driver + "/unbind");
60 std::ofstream driverBind(driver + "/bind");
61 if (!driverUnbind.is_open())
62 {
63 std::cerr << "force probe error opening " << driver << "\n";
64 return;
65 }
66 if (!driverBind.is_open())
67 {
68 driverUnbind.close();
69 std::cerr << "force probe error opening " << driver << "\n";
70 return;
71 }
72
73 std::experimental::filesystem::path pathObj(driver);
74 for (auto &p : std::experimental::filesystem::directory_iterator(pathObj))
75 {
76 // symlinks are object names
77 if (is_symlink(p))
78 {
79 std::string driverName = p.path().filename();
80 driverUnbind << driverName;
81 driverBind << driverName;
82 break;
83 }
84 }
85 driverUnbind.close();
86 driverBind.close();
87}
88
89// helper function to make json types into string
90std::string jsonToString(const nlohmann::json &in)
91{
92 if (in.type() == nlohmann::json::value_t::string)
93 {
94 return in.get<std::string>();
95 }
96 else if (in.type() == nlohmann::json::value_t::array)
97 {
98 // remove brackets and comma from array
99 std::string array = in.dump();
100 array = array.substr(1, array.size() - 2);
101 boost::replace_all(array, ",", " ");
102 return array;
103 }
104 return in.dump();
105}
106
107// when muxes create new i2c devices, the symbols are not there to map the new
108// i2c devices to the mux address. this looks up the device tree path and adds
109// the new symbols so the new devices can be referenced via the phandle
110void fixupSymbols(
111 const std::vector<std::experimental::filesystem::path> &i2cDevsBefore)
112{
113 std::vector<std::experimental::filesystem::path> i2cDevsAfter;
114 find_files(std::experimental::filesystem::path(I2C_DEVS_DIR),
115 R"(i2c-\d+)", i2cDevsAfter, 0);
116
117 for (const auto &dev : i2cDevsAfter)
118 {
119 if (std::find(i2cDevsBefore.begin(), i2cDevsBefore.end(), dev) !=
120 i2cDevsBefore.end())
121 {
122 continue;
123 }
124 // removes everything before and including the '-' in /path/i2c-##
125 std::string bus =
126 std::regex_replace(dev.string(), std::regex("^.*-"), "");
127 std::string devtreeRef = dev.string() + "/device/of_node";
128 auto devtreePath = std::experimental::filesystem::path(devtreeRef);
129 std::string symbolPath =
130 std::experimental::filesystem::canonical(devtreePath);
131 symbolPath =
132 symbolPath.substr(sizeof("/sys/firmware/devicetree/base") - 1);
133 nlohmann::json configuration = {{"path", symbolPath},
James Feistd63d18a2018-07-19 15:23:45 -0700134 {"Type", "Symbol"},
James Feistc95cb142018-02-26 10:41:42 -0800135 {"bus", std::stoul(bus)},
James Feistd63d18a2018-07-19 15:23:45 -0700136 {"Name", "i2c" + bus}};
James Feistc95cb142018-02-26 10:41:42 -0800137 createOverlay(TEMPLATE_DIR + std::string("/Symbol.template"),
138 configuration);
139 }
140}
141
142void createOverlay(const std::string &templatePath,
143 const nlohmann::json &configuration)
144{
145 std::ifstream templateFile(templatePath);
146
147 if (!templateFile.is_open())
148 {
149 std::cerr << "createOverlay error opening " << templatePath << "\n";
150 return;
151 }
152 std::stringstream buff;
153 buff << templateFile.rdbuf();
154 std::string templateStr = buff.str();
155 std::string name = "unknown";
James Feistd63d18a2018-07-19 15:23:45 -0700156 std::string type = configuration["Type"];
James Feistc95cb142018-02-26 10:41:42 -0800157 for (auto keyPair = configuration.begin(); keyPair != configuration.end();
158 keyPair++)
159 {
160 std::string subsituteString;
161
162 // device tree symbols are in decimal
163 if (keyPair.key() == "bus" &&
164 keyPair.value().type() == nlohmann::json::value_t::string)
165 {
166 unsigned int dec =
167 std::stoul(keyPair.value().get<std::string>(), nullptr, 16);
168 subsituteString = std::to_string(dec);
169 }
James Feistd63d18a2018-07-19 15:23:45 -0700170 else if (keyPair.key() == "Name" &&
James Feistc95cb142018-02-26 10:41:42 -0800171 keyPair.value().type() == nlohmann::json::value_t::string)
172 {
173 subsituteString = std::regex_replace(
174 keyPair.value().get<std::string>(), ILLEGAL_NAME_REGEX, "_");
175 name = subsituteString;
176 }
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700177 else if (keyPair.key() == "address")
178 {
179 if (keyPair.value().type() == nlohmann::json::value_t::string)
180 {
181 subsituteString = keyPair.value().get<std::string>();
182 subsituteString.erase(
183 0, subsituteString.find_first_not_of(HEX_FORMAT_STR));
184 }
185 else
186 {
187 std::ostringstream hex;
188 hex << std::hex << keyPair.value().get<unsigned int>();
189 subsituteString = hex.str();
190 }
191 }
James Feistc95cb142018-02-26 10:41:42 -0800192 else
193 {
194 subsituteString = jsonToString(keyPair.value());
195 }
196 boost::replace_all(templateStr, TEMPLATE_CHAR + keyPair.key(),
197 subsituteString);
198 }
199 // todo: this is a lame way to fill in platform, but we only
200 // care about ast2500 right now
201 boost::replace_all(templateStr, TEMPLATE_CHAR + std::string("platform"),
202 PLATFORM);
203 std::string dtsFilename =
204 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dts";
205 std::string dtboFilename =
206 std::string(OUTPUT_DIR) + "/" + name + "_" + type + ".dtbo";
207
208 std::ofstream out(dtsFilename);
209 if (!out.is_open())
210 {
211 std::cerr << "createOverlay error opening " << dtsFilename << "\n";
212 return;
213 }
214 out << templateStr;
215 out.close();
216
217 // this is for muxes, we need to store the diff of i2c devices before
218 // and after scanning to load new symbols into device tree so that if we
219 // later add devices to the "virtual" i2c device, we can match the phandle
220 // to the correct mux
221 std::vector<std::experimental::filesystem::path> i2cDevsBefore;
222 auto findMux = MUX_TYPES.find(type);
223 if (findMux != MUX_TYPES.end())
224 {
225 find_files(std::experimental::filesystem::path(I2C_DEVS_DIR),
226 R"(i2c-\d+)", i2cDevsBefore, 0);
227 }
228
229 // compile dtbo and load overlay
230 boost::process::child c1(DTC, "-@", "-q", "-I", "dts", "-O", "dtb", "-o",
231 dtboFilename, dtsFilename);
232 c1.wait();
233 if (c1.exit_code())
234 {
235 std::cerr << "DTC error with file " << dtsFilename << "\n";
236 return;
237 }
238 boost::process::child c2(DT_OVERLAY, "-d", OUTPUT_DIR, name + "_" + type);
239 c2.wait();
240 if (c2.exit_code())
241 {
242 std::cerr << "DTOverlay error with file " << dtboFilename << "\n";
243 return;
244 }
245 auto findForceProbe = FORCE_PROBES.find(type);
246 if (findForceProbe != FORCE_PROBES.end())
247 {
248 forceProbe(findForceProbe->second);
249 }
250 if (findMux != MUX_TYPES.end())
251 {
252 fixupSymbols(i2cDevsBefore);
253 }
254}
255
256bool loadOverlays(const nlohmann::json &systemConfiguration)
257{
258
259 std::vector<std::experimental::filesystem::path> paths;
260 if (!find_files(std::experimental::filesystem::path(TEMPLATE_DIR),
261 R"(.*\.template)", paths, 0))
262 {
263 std::cerr << "Unable to find any tempate files in " << TEMPLATE_DIR
264 << "\n";
265 return false;
266 }
267
268 std::experimental::filesystem::create_directory(OUTPUT_DIR);
269 for (auto entity = systemConfiguration.begin();
270 entity != systemConfiguration.end(); entity++)
271 {
272 auto findExposes = entity.value().find("exposes");
273 if (findExposes == entity.value().end() ||
274 findExposes->type() != nlohmann::json::value_t::array)
275 {
276 continue;
277 }
278
279 for (auto &configuration : *findExposes)
280 {
281 auto findStatus = configuration.find("status");
282 // status missing is assumed to be 'okay'
283 if (findStatus != configuration.end() && *findStatus == "disabled")
284 {
285 continue;
286 }
James Feistd63d18a2018-07-19 15:23:45 -0700287 auto findType = configuration.find("Type");
James Feistc95cb142018-02-26 10:41:42 -0800288 if (findType == configuration.end() ||
289 findType->type() != nlohmann::json::value_t::string)
290 {
291 continue;
292 }
293 std::string type = findType.value().get<std::string>();
294 std::string typeFile = type + std::string(".template");
295 for (auto path : paths)
296 {
297 if (path.filename() != typeFile)
298 {
299 continue;
300 }
301 createOverlay(path.string(), configuration);
302 break;
303 }
304 }
305 }
306
307 return true;
Jae Hyun Yoo6d1d0142018-07-25 10:07:43 -0700308}