| #pragma once |
| #include "util_base.hpp" |
| #include "utility.hpp" |
| #include "xyz/openbmc_project/Common/error.hpp" |
| |
| #include <fmt/format.h> |
| |
| #include <gpiod.hpp> |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <phosphor-logging/elog.hpp> |
| #include <phosphor-logging/log.hpp> |
| |
| #include <bitset> |
| #include <chrono> |
| |
| namespace phosphor::power::psu |
| { |
| |
| using Property = std::string; |
| using Value = std::variant<bool, std::string>; |
| using PropertyMap = std::map<Property, Value>; |
| using Interface = std::string; |
| using InterfaceMap = std::map<Interface, PropertyMap>; |
| using Object = sdbusplus::message::object_path; |
| using ObjectMap = std::map<Object, InterfaceMap>; |
| |
| class Util : public UtilBase |
| { |
| public: |
| bool getPresence(sdbusplus::bus_t& bus, |
| const std::string& invpath) const override |
| { |
| bool present = false; |
| |
| // Use getProperty utility function to get presence status. |
| util::getProperty(INVENTORY_IFACE, PRESENT_PROP, invpath, |
| INVENTORY_MGR_IFACE, bus, present); |
| |
| return present; |
| } |
| |
| void setPresence(sdbusplus::bus_t& bus, const std::string& invpath, |
| bool present, const std::string& name) const override |
| { |
| using InternalFailure = |
| sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; |
| PropertyMap invProp; |
| |
| invProp.emplace("Present", present); |
| invProp.emplace("PrettyName", name); |
| |
| InterfaceMap invIntf; |
| invIntf.emplace("xyz.openbmc_project.Inventory.Item", |
| std::move(invProp)); |
| |
| Interface extraIface = "xyz.openbmc_project.Inventory.Item.PowerSupply"; |
| |
| invIntf.emplace(extraIface, PropertyMap()); |
| |
| ObjectMap invObj; |
| invObj.emplace(std::move(invpath), std::move(invIntf)); |
| |
| using namespace phosphor::logging; |
| log<level::INFO>(fmt::format("Updating inventory present property. " |
| "present:{} invpath:{} name:{}", |
| present, invpath, name) |
| .c_str()); |
| |
| try |
| { |
| auto invService = phosphor::power::util::getService( |
| INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); |
| |
| // Update inventory |
| auto invMsg = bus.new_method_call(invService.c_str(), |
| INVENTORY_OBJ_PATH, |
| INVENTORY_MGR_IFACE, "Notify"); |
| invMsg.append(std::move(invObj)); |
| auto invMgrResponseMsg = bus.call(invMsg); |
| } |
| catch (const std::exception& e) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "Error in inventory manager call to update inventory: {}", |
| e.what()) |
| .c_str()); |
| elog<InternalFailure>(); |
| } |
| } |
| |
| void setAvailable(sdbusplus::bus_t& bus, const std::string& invpath, |
| bool available) const override |
| { |
| PropertyMap invProp; |
| InterfaceMap invIntf; |
| ObjectMap invObj; |
| |
| invProp.emplace(AVAILABLE_PROP, available); |
| invIntf.emplace(AVAILABILITY_IFACE, std::move(invProp)); |
| |
| invObj.emplace(std::move(invpath), std::move(invIntf)); |
| |
| try |
| { |
| auto invService = phosphor::power::util::getService( |
| INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); |
| |
| auto invMsg = bus.new_method_call(invService.c_str(), |
| INVENTORY_OBJ_PATH, |
| INVENTORY_MGR_IFACE, "Notify"); |
| invMsg.append(std::move(invObj)); |
| auto invMgrResponseMsg = bus.call(invMsg); |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| using namespace phosphor::logging; |
| log<level::ERR>( |
| fmt::format("Error in inventory manager call to update " |
| "availability interface: {}", |
| e.what()) |
| .c_str()); |
| throw; |
| } |
| } |
| |
| void handleChassisHealthRollup(sdbusplus::bus_t& bus, |
| const std::string& invpath, |
| bool addRollup) const override |
| { |
| using AssociationTuple = |
| std::tuple<std::string, std::string, std::string>; |
| using AssociationsProperty = std::vector<AssociationTuple>; |
| try |
| { |
| auto chassisPath = getChassis(bus, invpath); |
| |
| auto service = phosphor::power::util::getService( |
| invpath, ASSOC_DEF_IFACE, bus); |
| |
| AssociationsProperty associations; |
| phosphor::power::util::getProperty<AssociationsProperty>( |
| ASSOC_DEF_IFACE, ASSOC_PROP, invpath, service, bus, |
| associations); |
| |
| AssociationTuple critAssociation{"health_rollup", "critical", |
| chassisPath}; |
| |
| auto assocIt = std::find(associations.begin(), associations.end(), |
| critAssociation); |
| if (addRollup) |
| { |
| if (assocIt != associations.end()) |
| { |
| // It's already there |
| return; |
| } |
| |
| associations.push_back(critAssociation); |
| } |
| else |
| { |
| if (assocIt == associations.end()) |
| { |
| // It's already been removed. |
| return; |
| } |
| |
| // If the object still isn't functional, then don't clear |
| // the association. |
| bool functional = false; |
| phosphor::power::util::getProperty<bool>( |
| OPERATIONAL_STATE_IFACE, FUNCTIONAL_PROP, invpath, service, |
| bus, functional); |
| |
| if (!functional) |
| { |
| return; |
| } |
| |
| associations.erase(assocIt); |
| } |
| |
| phosphor::power::util::setProperty(ASSOC_DEF_IFACE, ASSOC_PROP, |
| invpath, service, bus, |
| associations); |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| using namespace phosphor::logging; |
| log<level::INFO>(fmt::format("Error trying to handle health rollup " |
| "associations for {}: {}", |
| invpath, e.what()) |
| .c_str()); |
| } |
| } |
| |
| std::string getChassis(sdbusplus::bus_t& bus, |
| const std::string& invpath) const |
| { |
| sdbusplus::message::object_path assocPath = invpath + "/powering"; |
| sdbusplus::message::object_path basePath{"/"}; |
| std::vector<std::string> interfaces{CHASSIS_IFACE}; |
| |
| // Find the object path that implements the chassis interface |
| // and also shows up in the endpoints list of the powering assoc. |
| auto chassisPaths = phosphor::power::util::getAssociatedSubTreePaths( |
| bus, assocPath, basePath, interfaces, 0); |
| |
| if (chassisPaths.empty()) |
| { |
| throw std::runtime_error(fmt::format( |
| "No association to a chassis found for {}", invpath)); |
| } |
| |
| return chassisPaths[0]; |
| } |
| }; |
| |
| std::unique_ptr<GPIOInterfaceBase> createGPIO(const std::string& namedGpio); |
| |
| class GPIOInterface : public GPIOInterfaceBase |
| { |
| public: |
| GPIOInterface() = delete; |
| virtual ~GPIOInterface() = default; |
| GPIOInterface(const GPIOInterface&) = default; |
| GPIOInterface& operator=(const GPIOInterface&) = default; |
| GPIOInterface(GPIOInterface&&) = default; |
| GPIOInterface& operator=(GPIOInterface&&) = default; |
| |
| /** |
| * Constructor |
| * |
| * @param[in] namedGpio - The string for the gpio-line-name |
| */ |
| GPIOInterface(const std::string& namedGpio); |
| |
| static std::unique_ptr<GPIOInterfaceBase> |
| createGPIO(const std::string& namedGpio); |
| |
| /** |
| * @brief Attempts to read the state of the GPIO line. |
| * |
| * Throws an exception if line not found, request line fails, or get_value |
| * from line fails. |
| * |
| * @return 1 for active (low/present), 0 for not active (high/not present). |
| */ |
| int read() override; |
| |
| /** |
| * @brief Attempts to set the state of the GPIO line to the specified value. |
| * |
| * Throws an exception if line not found, request line fails, or set_value |
| * to line fails. |
| * |
| * @param[in] value - The value to set the state of the GPIO line, 1 or 0. |
| * @param[in] flags - Additional line request flags as defined in gpiod.hpp. |
| */ |
| void write(int value, std::bitset<32> flags) override; |
| |
| /** |
| * @brief Attempts to toggle (write) a GPIO low then high. |
| * |
| * Relies on write, so throws exception if line not found, etc. |
| * |
| * @param[in] delay - Milliseconds to delay betwen low/high toggle. |
| */ |
| void toggleLowHigh(const std::chrono::milliseconds& delay) override; |
| |
| /** |
| * @brief Returns the name of the GPIO, if not empty. |
| */ |
| std::string getName() const override; |
| |
| private: |
| gpiod::line line; |
| }; |
| |
| } // namespace phosphor::power::psu |