#pragma once #include "sdbusplus.hpp" #include #include #include #include using HostState = sdbusplus::xyz::openbmc_project::State::server::Host::HostState; namespace phosphor::fan { /** * @class PowerState * * This class provides an interface to check the current power state, * and to register a function that gets called when there is a power * state change. A callback can be passed in using the constructor, * or can be added later using addCallback(). * * Different architectures may have different ways of considering * power to be on, such as a pgood property on the * org.openbmc.Control.Power interface, or the CurrentPowerState * property on the State.Chassis interface, so those details will * be in a derived class. */ class PowerState { public: using StateChangeFunc = std::function; virtual ~PowerState() = default; PowerState(const PowerState&) = delete; PowerState& operator=(const PowerState&) = delete; PowerState(PowerState&&) = delete; PowerState& operator=(PowerState&&) = delete; /** * @brief Constructor * * @param[in] bus - The D-Bus bus connection object * @param[in] callback - The function that should be run when * the power state changes */ PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus) { _callbacks.emplace("default", std::move(callback)); } /** * @brief Constructor * * Callbacks can be added with addCallback(). */ PowerState() : _bus(util::SDBusPlus::getBus()) {} /** * @brief Adds a function to call when the power state changes * * @param[in] - Any unique name, so the callback can be removed later * if desired. * @param[in] callback - The function that should be run when * the power state changes */ void addCallback(const std::string& name, StateChangeFunc callback) { _callbacks.emplace(name, std::move(callback)); } /** * @brief Remove the callback so it is no longer called * * @param[in] name - The name used when it was added. */ void deleteCallback(const std::string& name) { _callbacks.erase(name); } /** * @brief Says if power is on * * @return bool - The power state */ bool isPowerOn() const { return _powerState; } protected: /** * @brief Called by derived classes to set the power state value * * Will call the callback functions if the state changed. * * @param[in] state - The new power state */ void setPowerState(bool state) { if (state != _powerState) { _powerState = state; for (const auto& [name, callback] : _callbacks) { callback(_powerState); } } } /** * @brief Reference to the D-Bus connection object. */ sdbusplus::bus_t& _bus; /** * @brief The power state value */ bool _powerState = false; private: /** * @brief The callback functions to run when the power state changes */ std::map _callbacks; }; /** * @class PGoodState * * This class implements the PowerState API by looking at the 'pgood' * property on the org.openbmc.Control.Power interface. */ class PGoodState : public PowerState { public: virtual ~PGoodState() = default; PGoodState(const PGoodState&) = delete; PGoodState& operator=(const PGoodState&) = delete; PGoodState(PGoodState&&) = delete; PGoodState& operator=(PGoodState&&) = delete; PGoodState() : PowerState(), _match(_bus, sdbusplus::bus::match::rules::propertiesChanged( _pgoodPath, _pgoodInterface), [this](auto& msg) { this->pgoodChanged(msg); }) { readPGood(); } /** * @brief Constructor * * @param[in] bus - The D-Bus bus connection object * @param[in] callback - The function that should be run when * the power state changes */ PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) : PowerState(bus, func), _match(_bus, sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath, _pgoodInterface), [this](auto& msg) { this->pgoodChanged(msg); }) { readPGood(); } /** * @brief PropertiesChanged callback for the PGOOD property. * * Will call the registered callback function if necessary. * * @param[in] msg - The payload of the propertiesChanged signal */ void pgoodChanged(sdbusplus::message_t& msg) { std::string interface; std::map> properties; msg.read(interface, properties); auto pgoodProp = properties.find(_pgoodProperty); if (pgoodProp != properties.end()) { auto pgood = std::get(pgoodProp->second); setPowerState(pgood); } } private: /** * @brief Reads the PGOOD property from D-Bus and saves it. */ void readPGood() { try { auto pgood = util::SDBusPlus::getProperty( _bus, _pgoodPath, _pgoodInterface, _pgoodProperty); _powerState = static_cast(pgood); } catch (const util::DBusServiceError& e) { // Wait for propertiesChanged signal when service starts } } /** @brief D-Bus path constant */ const std::string _pgoodPath{"/org/openbmc/control/power0"}; /** @brief D-Bus interface constant */ const std::string _pgoodInterface{"org.openbmc.control.Power"}; /** @brief D-Bus property constant */ const std::string _pgoodProperty{"pgood"}; /** @brief The propertiesChanged match */ sdbusplus::bus::match_t _match; }; /** * @class HostPowerState * * This class implements the PowerState API by looking at the 'powerState' * property on the phosphor virtual sensor interface. */ class HostPowerState : public PowerState { public: virtual ~HostPowerState() = default; HostPowerState(const HostPowerState&) = delete; HostPowerState& operator=(const HostPowerState&) = delete; HostPowerState(HostPowerState&&) = delete; HostPowerState& operator=(HostPowerState&&) = delete; HostPowerState() : PowerState(), _match(_bus, sdbusplus::bus::match::rules::propertiesChangedNamespace( _hostStatePath, _hostStateInterface), [this](auto& msg) { this->hostStateChanged(msg); }) { readHostState(); } /** * @brief Constructor * * @param[in] bus - The D-Bus bus connection object * @param[in] callback - The function that should be run when * the power state changes */ HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) : PowerState(bus, func), _match(_bus, sdbusplus::bus::match::rules::propertiesChangedNamespace( _hostStatePath, _hostStateInterface), [this](auto& msg) { this->hostStateChanged(msg); }) { readHostState(); } /** * @brief PropertiesChanged callback for the CurrentHostState property. * * Will call the registered callback function if necessary. * * @param[in] msg - The payload of the propertiesChanged signal */ void hostStateChanged(sdbusplus::message_t& msg) { std::string interface; std::map> properties; std::vector hostPowerStates; msg.read(interface, properties); auto hostStateProp = properties.find(_hostStateProperty); if (hostStateProp != properties.end()) { auto currentHostState = sdbusplus::message::convert_from_string( std::get(hostStateProp->second)); if (!currentHostState) { throw sdbusplus::exception::InvalidEnumString(); } HostState hostState = *currentHostState; hostPowerStates.emplace_back(hostState); setHostPowerState(hostPowerStates); } } private: void setHostPowerState(std::vector& hostPowerStates) { bool powerStateflag = false; for (const auto& powerState : hostPowerStates) { if (powerState == HostState::Standby || powerState == HostState::Running || powerState == HostState::TransitioningToRunning || powerState == HostState::Quiesced || powerState == HostState::DiagnosticMode) { powerStateflag = true; break; } } setPowerState(powerStateflag); } /** * @brief Reads the CurrentHostState property from D-Bus and saves it. */ void readHostState() { std::string hostStatePath; std::string hostStateService; std::string hostService = "xyz.openbmc_project.State.Host"; std::vector hostPowerStates; int32_t depth = 0; const std::string path = "/"; auto mapperResponse = util::SDBusPlus::getSubTreeRaw( _bus, path, _hostStateInterface, depth); for (const auto& path : mapperResponse) { for (const auto& service : path.second) { hostStateService = service.first; if (hostStateService.find(hostService) != std::string::npos) { hostStatePath = path.first; auto currentHostState = util::SDBusPlus::getProperty( hostStateService, hostStatePath, _hostStateInterface, _hostStateProperty); hostPowerStates.emplace_back(currentHostState); } } } setHostPowerState(hostPowerStates); } const std::string _hostStatePath{"/xyz/openbmc_project/state"}; /** @brief D-Bus interface constant */ const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"}; /** @brief D-Bus property constant */ const std::string _hostStateProperty{"CurrentHostState"}; /** @brief The propertiesChanged match */ sdbusplus::bus::match_t _match; }; } // namespace phosphor::fan