/** * Copyright © 2020 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config_file_parser.hpp" #include "config_file_parser_error.hpp" #include "i2c_interface.hpp" #include "pmbus_utils.hpp" #include #include #include #include using json = nlohmann::json; namespace phosphor::power::regulators::config_file_parser { std::tuple>, std::vector>> parse(const std::filesystem::path& pathName) { try { // Use standard JSON parser to create tree of JSON elements std::ifstream file{pathName}; json rootElement = json::parse(file); // Parse tree of JSON elements and return corresponding C++ objects return internal::parseRoot(rootElement); } catch (const std::exception& e) { throw ConfigFileParserError{pathName, e.what()}; } } namespace internal { std::unique_ptr parseAction(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required action type property; there must be exactly one specified std::unique_ptr action{}; if (element.contains("and")) { action = parseAnd(element["and"]); ++propertyCount; } else if (element.contains("compare_presence")) { action = parseComparePresence(element["compare_presence"]); ++propertyCount; } else if (element.contains("compare_vpd")) { // TODO: Not implemented yet // action = parseCompareVPD(element["compare_vpd"]); // ++propertyCount; } else if (element.contains("i2c_compare_bit")) { action = parseI2CCompareBit(element["i2c_compare_bit"]); ++propertyCount; } else if (element.contains("i2c_compare_byte")) { action = parseI2CCompareByte(element["i2c_compare_byte"]); ++propertyCount; } else if (element.contains("i2c_compare_bytes")) { action = parseI2CCompareBytes(element["i2c_compare_bytes"]); ++propertyCount; } else if (element.contains("i2c_write_bit")) { action = parseI2CWriteBit(element["i2c_write_bit"]); ++propertyCount; } else if (element.contains("i2c_write_byte")) { action = parseI2CWriteByte(element["i2c_write_byte"]); ++propertyCount; } else if (element.contains("i2c_write_bytes")) { action = parseI2CWriteBytes(element["i2c_write_bytes"]); ++propertyCount; } else if (element.contains("if")) { action = parseIf(element["if"]); ++propertyCount; } else if (element.contains("not")) { action = parseNot(element["not"]); ++propertyCount; } else if (element.contains("or")) { action = parseOr(element["or"]); ++propertyCount; } else if (element.contains("pmbus_read_sensor")) { // TODO: Not implemented yet // action = parsePMBusReadSensor(element["pmbus_read_sensor"]); // ++propertyCount; } else if (element.contains("pmbus_write_vout_command")) { action = parsePMBusWriteVoutCommand(element["pmbus_write_vout_command"]); ++propertyCount; } else if (element.contains("run_rule")) { action = parseRunRule(element["run_rule"]); ++propertyCount; } else if (element.contains("set_device")) { action = parseSetDevice(element["set_device"]); ++propertyCount; } else { throw std::invalid_argument{"Required action type property missing"}; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return action; } std::vector> parseActionArray(const json& element) { verifyIsArray(element); std::vector> actions; for (auto& actionElement : element) { actions.emplace_back(parseAction(actionElement)); } return actions; } std::unique_ptr parseAnd(const json& element) { verifyIsArray(element); // Verify if array size less than 2 if (element.size() < 2) { throw std::invalid_argument{"Array must contain two or more actions"}; } // Array of two or more actions std::vector> actions = parseActionArray(element); return std::make_unique(std::move(actions)); } std::unique_ptr parseChassis(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required number property const json& numberElement = getRequiredProperty(element, "number"); unsigned int number = parseUnsignedInteger(numberElement); if (number < 1) { throw std::invalid_argument{"Invalid chassis number: Must be > 0"}; } ++propertyCount; // Optional devices property std::vector> devices{}; auto devicesIt = element.find("devices"); if (devicesIt != element.end()) { devices = parseDeviceArray(*devicesIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(number, std::move(devices)); } std::vector> parseChassisArray(const json& element) { verifyIsArray(element); std::vector> chassis; for (auto& chassisElement : element) { chassis.emplace_back(parseChassis(chassisElement)); } return chassis; } std::unique_ptr parseComparePresence(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required fru property const json& fruElement = getRequiredProperty(element, "fru"); std::string fru = parseString(fruElement); ++propertyCount; // Required value property const json& valueElement = getRequiredProperty(element, "value"); bool value = parseBoolean(valueElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(fru, value); } std::unique_ptr parseConfiguration(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Optional volts property std::optional volts{}; auto voltsIt = element.find("volts"); if (voltsIt != element.end()) { volts = parseDouble(*voltsIt); ++propertyCount; } // Required rule_id or actions property std::vector> actions{}; actions = parseRuleIDOrActionsProperty(element); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(volts, std::move(actions)); } std::unique_ptr parseDevice(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required id property const json& idElement = getRequiredProperty(element, "id"); std::string id = parseString(idElement); ++propertyCount; // Required is_regulator property const json& isRegulatorElement = getRequiredProperty(element, "is_regulator"); bool isRegulator = parseBoolean(isRegulatorElement); ++propertyCount; // Required fru property const json& fruElement = getRequiredProperty(element, "fru"); std::string fru = parseString(fruElement); ++propertyCount; // Required i2c_interface property const json& i2cInterfaceElement = getRequiredProperty(element, "i2c_interface"); std::unique_ptr i2cInterface = parseI2CInterface(i2cInterfaceElement); ++propertyCount; // Optional presence_detection property std::unique_ptr presenceDetection{}; auto presenceDetectionIt = element.find("presence_detection"); if (presenceDetectionIt != element.end()) { presenceDetection = parsePresenceDetection(*presenceDetectionIt); ++propertyCount; } // Optional configuration property std::unique_ptr configuration{}; auto configurationIt = element.find("configuration"); if (configurationIt != element.end()) { configuration = parseConfiguration(*configurationIt); ++propertyCount; } // Optional rails property std::vector> rails{}; auto railsIt = element.find("rails"); if (railsIt != element.end()) { if (!isRegulator) { throw std::invalid_argument{ "Invalid rails property when is_regulator is false"}; } rails = parseRailArray(*railsIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(id, isRegulator, fru, std::move(i2cInterface), std::move(presenceDetection), std::move(configuration), std::move(rails)); } std::vector> parseDeviceArray(const json& element) { verifyIsArray(element); std::vector> devices; for (auto& deviceElement : element) { devices.emplace_back(parseDevice(deviceElement)); } return devices; } std::vector parseHexByteArray(const json& element) { verifyIsArray(element); std::vector values; for (auto& valueElement : element) { values.emplace_back(parseHexByte(valueElement)); } return values; } std::unique_ptr parseI2CCompareBit(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required position property const json& positionElement = getRequiredProperty(element, "position"); uint8_t position = parseBitPosition(positionElement); ++propertyCount; // Required value property const json& valueElement = getRequiredProperty(element, "value"); uint8_t value = parseBitValue(valueElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(reg, position, value); } std::unique_ptr parseI2CCompareByte(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required value property const json& valueElement = getRequiredProperty(element, "value"); uint8_t value = parseHexByte(valueElement); ++propertyCount; // Optional mask property uint8_t mask = 0xff; auto maskIt = element.find("mask"); if (maskIt != element.end()) { mask = parseHexByte(*maskIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(reg, value, mask); } std::unique_ptr parseI2CCompareBytes(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required values property const json& valueElement = getRequiredProperty(element, "values"); std::vector values = parseHexByteArray(valueElement); ++propertyCount; // Optional masks property std::vector masks{}; auto masksIt = element.find("masks"); if (masksIt != element.end()) { masks = parseHexByteArray(*masksIt); ++propertyCount; } // Verify masks array (if specified) was same size as values array if ((!masks.empty()) && (masks.size() != values.size())) { throw std::invalid_argument{"Invalid number of elements in masks"}; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); if (masks.empty()) { return std::make_unique(reg, values); } return std::make_unique(reg, values, masks); } std::unique_ptr parseI2CInterface(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required bus property const json& busElement = getRequiredProperty(element, "bus"); uint8_t bus = parseUint8(busElement); ++propertyCount; // Required address property const json& addressElement = getRequiredProperty(element, "address"); uint8_t address = parseHexByte(addressElement); ++propertyCount; verifyPropertyCount(element, propertyCount); return i2c::create(bus, address, i2c::I2CInterface::InitialState::CLOSED); } std::unique_ptr parseI2CWriteBit(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required position property const json& positionElement = getRequiredProperty(element, "position"); uint8_t position = parseBitPosition(positionElement); ++propertyCount; // Required value property const json& valueElement = getRequiredProperty(element, "value"); uint8_t value = parseBitValue(valueElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(reg, position, value); } std::unique_ptr parseI2CWriteByte(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required value property const json& valueElement = getRequiredProperty(element, "value"); uint8_t value = parseHexByte(valueElement); ++propertyCount; // Optional mask property uint8_t mask = 0xff; auto maskIt = element.find("mask"); if (maskIt != element.end()) { mask = parseHexByte(*maskIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(reg, value, mask); } std::unique_ptr parseI2CWriteBytes(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required register property const json& regElement = getRequiredProperty(element, "register"); uint8_t reg = parseHexByte(regElement); ++propertyCount; // Required values property const json& valueElement = getRequiredProperty(element, "values"); std::vector values = parseHexByteArray(valueElement); ++propertyCount; // Optional masks property std::vector masks{}; auto masksIt = element.find("masks"); if (masksIt != element.end()) { masks = parseHexByteArray(*masksIt); ++propertyCount; } // Verify masks array (if specified) was same size as values array if ((!masks.empty()) && (masks.size() != values.size())) { throw std::invalid_argument{"Invalid number of elements in masks"}; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); if (masks.empty()) { return std::make_unique(reg, values); } return std::make_unique(reg, values, masks); } std::unique_ptr parseIf(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required condition property const json& conditionElement = getRequiredProperty(element, "condition"); std::unique_ptr conditionAction = parseAction(conditionElement); ++propertyCount; // Required then property const json& thenElement = getRequiredProperty(element, "then"); std::vector> thenActions = parseActionArray(thenElement); ++propertyCount; // Optional else property std::vector> elseActions{}; auto elseIt = element.find("else"); if (elseIt != element.end()) { elseActions = parseActionArray(*elseIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(std::move(conditionAction), std::move(thenActions), std::move(elseActions)); } std::unique_ptr parseNot(const json& element) { // Required action to execute std::unique_ptr action = parseAction(element); return std::make_unique(std::move(action)); } std::unique_ptr parseOr(const json& element) { verifyIsArray(element); // Verify if array size less than 2 if (element.size() < 2) { throw std::invalid_argument{"Array must contain two or more actions"}; } // Array of two or more actions std::vector> actions = parseActionArray(element); return std::make_unique(std::move(actions)); } std::unique_ptr parsePMBusWriteVoutCommand(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional volts property std::optional volts{}; auto voltsIt = element.find("volts"); if (voltsIt != element.end()) { volts = parseDouble(*voltsIt); ++propertyCount; } // Required format property const json& formatElement = getRequiredProperty(element, "format"); std::string formatString = parseString(formatElement); if (formatString != "linear") { throw std::invalid_argument{"Invalid format value: " + formatString}; } pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear; ++propertyCount; // Optional exponent property std::optional exponent{}; auto exponentIt = element.find("exponent"); if (exponentIt != element.end()) { exponent = parseInt8(*exponentIt); ++propertyCount; } // Optional is_verified property bool isVerified = false; auto isVerifiedIt = element.find("is_verified"); if (isVerifiedIt != element.end()) { isVerified = parseBoolean(*isVerifiedIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(volts, format, exponent, isVerified); } std::unique_ptr parsePresenceDetection(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required rule_id or actions property std::vector> actions{}; actions = parseRuleIDOrActionsProperty(element); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(std::move(actions)); } std::unique_ptr parseRail(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required id property const json& idElement = getRequiredProperty(element, "id"); std::string id = parseString(idElement); ++propertyCount; // Optional configuration property std::unique_ptr configuration{}; auto configurationIt = element.find("configuration"); if (configurationIt != element.end()) { configuration = parseConfiguration(*configurationIt); ++propertyCount; } // Optional sensor_monitoring property std::unique_ptr sensorMonitoring{}; auto sensorMonitoringIt = element.find("sensor_monitoring"); if (sensorMonitoringIt != element.end()) { sensorMonitoring = parseSensorMonitoring(*sensorMonitoringIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(id, std::move(configuration), std::move(sensorMonitoring)); } std::vector> parseRailArray(const json& element) { verifyIsArray(element); std::vector> rails; for (auto& railElement : element) { rails.emplace_back(parseRail(railElement)); } return rails; } std::tuple>, std::vector>> parseRoot(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Optional rules property std::vector> rules{}; auto rulesIt = element.find("rules"); if (rulesIt != element.end()) { rules = parseRuleArray(*rulesIt); ++propertyCount; } // Required chassis property const json& chassisElement = getRequiredProperty(element, "chassis"); std::vector> chassis = parseChassisArray(chassisElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_tuple(std::move(rules), std::move(chassis)); } std::unique_ptr parseRule(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required id property const json& idElement = getRequiredProperty(element, "id"); std::string id = parseString(idElement); ++propertyCount; // Required actions property const json& actionsElement = getRequiredProperty(element, "actions"); std::vector> actions = parseActionArray(actionsElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(id, std::move(actions)); } std::vector> parseRuleArray(const json& element) { verifyIsArray(element); std::vector> rules; for (auto& ruleElement : element) { rules.emplace_back(parseRule(ruleElement)); } return rules; } std::vector> parseRuleIDOrActionsProperty(const json& element) { verifyIsObject(element); // Required rule_id or actions property std::vector> actions{}; auto ruleIDIt = element.find("rule_id"); auto actionsIt = element.find("actions"); if ((actionsIt == element.end()) && (ruleIDIt != element.end())) { std::string ruleID = parseString(*ruleIDIt); actions.emplace_back(std::make_unique(ruleID)); } else if ((actionsIt != element.end()) && (ruleIDIt == element.end())) { actions = parseActionArray(*actionsIt); } else { throw std::invalid_argument{"Invalid property combination: Must " "contain either rule_id or actions"}; } return actions; } std::unique_ptr parseRunRule(const json& element) { // String ruleID std::string ruleID = parseString(element); return std::make_unique(ruleID); } std::unique_ptr parseSensorMonitoring(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Optional comments property; value not stored if (element.contains("comments")) { ++propertyCount; } // Required rule_id or actions property std::vector> actions{}; actions = parseRuleIDOrActionsProperty(element); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(std::move(actions)); } std::unique_ptr parseSetDevice(const json& element) { // String deviceID std::string deviceID = parseString(element); return std::make_unique(deviceID); } } // namespace internal } // namespace phosphor::power::regulators::config_file_parser