blob: 6d9b1691761a8d5cf436f76d934c51b7720b4659 [file] [log] [blame]
Shawn McCarney6a957f62024-01-10 16:15:19 -06001/**
2 * Copyright © 2024 IBM 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 "config_file_parser.hpp"
18
19#include "config_file_parser_error.hpp"
Shawn McCarney38f85002025-10-31 17:59:36 -050020#include "json_parser_utils.hpp"
Shawn McCarney4840b252025-11-06 16:06:40 -060021#include "ucd90160_device.hpp"
22#include "ucd90320_device.hpp"
Shawn McCarney6a957f62024-01-10 16:15:19 -060023
Shawn McCarney38f85002025-10-31 17:59:36 -050024#include <cstdint>
Shawn McCarney6a957f62024-01-10 16:15:19 -060025#include <exception>
26#include <fstream>
27#include <optional>
Shawn McCarney38f85002025-10-31 17:59:36 -050028#include <stdexcept>
Shawn McCarney6a957f62024-01-10 16:15:19 -060029
Shawn McCarney38f85002025-10-31 17:59:36 -050030using namespace phosphor::power::json_parser_utils;
31using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
Shawn McCarneye9144ab2024-05-22 12:34:18 -050032namespace fs = std::filesystem;
Shawn McCarney6a957f62024-01-10 16:15:19 -060033
34namespace phosphor::power::sequencer::config_file_parser
35{
36
Shawn McCarneye9144ab2024-05-22 12:34:18 -050037const std::filesystem::path standardConfigFileDirectory{
38 "/usr/share/phosphor-power-sequencer"};
39
Patrick Williams92261f82025-02-01 08:22:34 -050040std::filesystem::path find(
41 const std::vector<std::string>& compatibleSystemTypes,
42 const std::filesystem::path& configFileDir)
Shawn McCarneye9144ab2024-05-22 12:34:18 -050043{
44 fs::path pathName, possiblePath;
45 std::string fileName;
46
47 for (const std::string& systemType : compatibleSystemTypes)
48 {
49 // Look for file name that is entire system type + ".json"
50 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json
51 fileName = systemType + ".json";
52 possiblePath = configFileDir / fileName;
53 if (fs::is_regular_file(possiblePath))
54 {
55 pathName = possiblePath;
56 break;
57 }
58
59 // Look for file name that is last node of system type + ".json"
60 // Example: MegaServer.json
61 std::string::size_type pos = systemType.rfind('.');
62 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
63 {
64 fileName = systemType.substr(pos + 1) + ".json";
65 possiblePath = configFileDir / fileName;
66 if (fs::is_regular_file(possiblePath))
67 {
68 pathName = possiblePath;
69 break;
70 }
71 }
72 }
73
74 return pathName;
75}
76
Shawn McCarney6a957f62024-01-10 16:15:19 -060077std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName)
78{
79 try
80 {
81 // Use standard JSON parser to create tree of JSON elements
82 std::ifstream file{pathName};
83 json rootElement = json::parse(file);
84
85 // Parse tree of JSON elements and return corresponding C++ objects
86 return internal::parseRoot(rootElement);
87 }
88 catch (const std::exception& e)
89 {
90 throw ConfigFileParserError{pathName, e.what()};
91 }
92}
93
94namespace internal
95{
96
Shawn McCarneyfb7e0932025-11-10 10:23:05 -060097std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
98 const json& element)
99{
100 verifyIsObject(element);
101 unsigned int propertyCount{0};
102
103 // Optional comments property; value not stored
104 if (element.contains("comments"))
105 {
106 ++propertyCount;
107 }
108
109 // Required id property
110 const json& idElement = getRequiredProperty(element, "id");
111 std::string id = parseString(idElement);
112 ++propertyCount;
113
114 // Required number property
115 // Just verify it exists; cannot be parsed without variable values
116 getRequiredProperty(element, "number");
117 ++propertyCount;
118
119 // Required inventory_path property
120 // Just verify it exists; cannot be parsed without variable values
121 getRequiredProperty(element, "inventory_path");
122 ++propertyCount;
123
124 // Required power_sequencers property
125 // Just verify it exists; cannot be parsed without variable values
126 getRequiredProperty(element, "power_sequencers");
127 ++propertyCount;
128
129 // Verify no invalid properties exist
130 verifyPropertyCount(element, propertyCount);
131
132 return {id, JSONRefWrapper{element}};
133}
134
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600135GPIO parseGPIO(const json& element,
136 const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600137{
138 verifyIsObject(element);
139 unsigned int propertyCount{0};
140
141 // Required line property
142 const json& lineElement = getRequiredProperty(element, "line");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600143 unsigned int line = parseUnsignedInteger(lineElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600144 ++propertyCount;
145
146 // Optional active_low property
147 bool activeLow{false};
148 auto activeLowIt = element.find("active_low");
149 if (activeLowIt != element.end())
150 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600151 activeLow = parseBoolean(*activeLowIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600152 ++propertyCount;
153 }
154
155 // Verify no invalid properties exist
156 verifyPropertyCount(element, propertyCount);
157
158 return GPIO(line, activeLow);
159}
160
Shawn McCarneye140ed92025-11-06 11:20:38 -0600161std::tuple<uint8_t, uint16_t> parseI2CInterface(
162 const nlohmann::json& element,
163 const std::map<std::string, std::string>& variables)
164{
165 verifyIsObject(element);
166 unsigned int propertyCount{0};
167
168 // Required bus property
169 const json& busElement = getRequiredProperty(element, "bus");
170 uint8_t bus = parseUint8(busElement, variables);
171 ++propertyCount;
172
173 // Required address property
174 const json& addressElement = getRequiredProperty(element, "address");
175 uint16_t address = parseHexByte(addressElement, variables);
176 ++propertyCount;
177
178 // Verify no invalid properties exist
179 verifyPropertyCount(element, propertyCount);
180
181 return {bus, address};
182}
183
Shawn McCarney4840b252025-11-06 16:06:40 -0600184std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
185 const nlohmann::json& element,
186 const std::map<std::string, std::string>& variables, Services& services)
187{
188 verifyIsObject(element);
189 unsigned int propertyCount{0};
190
191 // Optional comments property; value not stored
192 if (element.contains("comments"))
193 {
194 ++propertyCount;
195 }
196
197 // Required type property
198 const json& typeElement = getRequiredProperty(element, "type");
199 std::string type = parseString(typeElement, false, variables);
200 ++propertyCount;
201
202 // Required i2c_interface property
203 const json& i2cInterfaceElement =
204 getRequiredProperty(element, "i2c_interface");
205 auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
206 ++propertyCount;
207
208 // Required power_control_gpio_name property
209 const json& powerControlGPIONameElement =
210 getRequiredProperty(element, "power_control_gpio_name");
211 std::string powerControlGPIOName =
212 parseString(powerControlGPIONameElement, false, variables);
213 ++propertyCount;
214
215 // Required power_good_gpio_name property
216 const json& powerGoodGPIONameElement =
217 getRequiredProperty(element, "power_good_gpio_name");
218 std::string powerGoodGPIOName =
219 parseString(powerGoodGPIONameElement, false, variables);
220 ++propertyCount;
221
222 // Required rails property
223 const json& railsElement = getRequiredProperty(element, "rails");
224 std::vector<std::unique_ptr<Rail>> rails =
225 parseRailArray(railsElement, variables);
226 ++propertyCount;
227
228 // Verify no invalid properties exist
229 verifyPropertyCount(element, propertyCount);
230
231 if (type == UCD90160Device::deviceName)
232 {
233 return std::make_unique<UCD90160Device>(
234 bus, address, powerControlGPIOName, powerGoodGPIOName,
235 std::move(rails), services);
236 }
237 else if (type == UCD90320Device::deviceName)
238 {
239 return std::make_unique<UCD90320Device>(
240 bus, address, powerControlGPIOName, powerGoodGPIOName,
241 std::move(rails), services);
242 }
243 throw std::invalid_argument{"Invalid power sequencer type: " + type};
244}
245
246std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
247 const nlohmann::json& element,
248 const std::map<std::string, std::string>& variables, Services& services)
249{
250 verifyIsArray(element);
251 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
252 for (auto& powerSequencerElement : element)
253 {
254 powerSequencers.emplace_back(
255 parsePowerSequencer(powerSequencerElement, variables, services));
256 }
257 return powerSequencers;
258}
259
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600260std::unique_ptr<Rail> parseRail(
261 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600262{
263 verifyIsObject(element);
264 unsigned int propertyCount{0};
265
266 // Required name property
267 const json& nameElement = getRequiredProperty(element, "name");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600268 std::string name = parseString(nameElement, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600269 ++propertyCount;
270
271 // Optional presence property
272 std::optional<std::string> presence{};
273 auto presenceIt = element.find("presence");
274 if (presenceIt != element.end())
275 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600276 presence = parseString(*presenceIt, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600277 ++propertyCount;
278 }
279
280 // Optional page property
281 std::optional<uint8_t> page{};
282 auto pageIt = element.find("page");
283 if (pageIt != element.end())
284 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600285 page = parseUint8(*pageIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600286 ++propertyCount;
287 }
288
Shawn McCarney16e493a2024-01-29 14:20:32 -0600289 // Optional is_power_supply_rail property
290 bool isPowerSupplyRail{false};
291 auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
292 if (isPowerSupplyRailIt != element.end())
293 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600294 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
Shawn McCarney16e493a2024-01-29 14:20:32 -0600295 ++propertyCount;
296 }
297
Shawn McCarney6a957f62024-01-10 16:15:19 -0600298 // Optional check_status_vout property
299 bool checkStatusVout{false};
300 auto checkStatusVoutIt = element.find("check_status_vout");
301 if (checkStatusVoutIt != element.end())
302 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600303 checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600304 ++propertyCount;
305 }
306
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600307 // Optional compare_voltage_to_limit property
308 bool compareVoltageToLimit{false};
309 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
310 if (compareVoltageToLimitIt != element.end())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600311 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600312 compareVoltageToLimit =
313 parseBoolean(*compareVoltageToLimitIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600314 ++propertyCount;
315 }
316
317 // Optional gpio property
318 std::optional<GPIO> gpio{};
319 auto gpioIt = element.find("gpio");
320 if (gpioIt != element.end())
321 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600322 gpio = parseGPIO(*gpioIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600323 ++propertyCount;
324 }
325
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600326 // If check_status_vout or compare_voltage_to_limit property is true, the
327 // page property is required; verify page was specified
328 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600329 {
330 throw std::invalid_argument{"Required property missing: page"};
331 }
332
333 // Verify no invalid properties exist
334 verifyPropertyCount(element, propertyCount);
335
Shawn McCarney16e493a2024-01-29 14:20:32 -0600336 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600337 checkStatusVout, compareVoltageToLimit, gpio);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600338}
339
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600340std::vector<std::unique_ptr<Rail>> parseRailArray(
341 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600342{
343 verifyIsArray(element);
344 std::vector<std::unique_ptr<Rail>> rails;
345 for (auto& railElement : element)
346 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600347 rails.emplace_back(parseRail(railElement, variables));
Shawn McCarney6a957f62024-01-10 16:15:19 -0600348 }
349 return rails;
350}
351
352std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
353{
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600354 std::map<std::string, std::string> variables{};
355
Shawn McCarney6a957f62024-01-10 16:15:19 -0600356 verifyIsObject(element);
357 unsigned int propertyCount{0};
358
359 // Required rails property
360 const json& railsElement = getRequiredProperty(element, "rails");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600361 std::vector<std::unique_ptr<Rail>> rails =
362 parseRailArray(railsElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600363 ++propertyCount;
364
365 // Verify no invalid properties exist
366 verifyPropertyCount(element, propertyCount);
367
368 return rails;
369}
370
371} // namespace internal
372
373} // namespace phosphor::power::sequencer::config_file_parser