| #pragma once |
| |
| #include "sdbusplus.hpp" |
| |
| #include <phosphor-logging/log.hpp> |
| #include <xyz/openbmc_project/State/Host/server.hpp> |
| |
| #include <functional> |
| |
| 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<void(bool)>; |
| |
| 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<std::string, StateChangeFunc> _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<std::string, std::variant<int32_t>> properties; |
| |
| msg.read(interface, properties); |
| |
| auto pgoodProp = properties.find(_pgoodProperty); |
| if (pgoodProp != properties.end()) |
| { |
| auto pgood = std::get<int32_t>(pgoodProp->second); |
| setPowerState(pgood); |
| } |
| } |
| |
| private: |
| /** |
| * @brief Reads the PGOOD property from D-Bus and saves it. |
| */ |
| void readPGood() |
| { |
| try |
| { |
| auto pgood = util::SDBusPlus::getProperty<int32_t>( |
| _bus, _pgoodPath, _pgoodInterface, _pgoodProperty); |
| |
| _powerState = static_cast<bool>(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<std::string, std::variant<std::string>> properties; |
| std::vector<HostState> hostPowerStates; |
| |
| msg.read(interface, properties); |
| |
| auto hostStateProp = properties.find(_hostStateProperty); |
| if (hostStateProp != properties.end()) |
| { |
| auto currentHostState = |
| sdbusplus::message::convert_from_string<HostState>( |
| std::get<std::string>(hostStateProp->second)); |
| |
| if (!currentHostState) |
| { |
| throw sdbusplus::exception::InvalidEnumString(); |
| } |
| HostState hostState = *currentHostState; |
| |
| hostPowerStates.emplace_back(hostState); |
| setHostPowerState(hostPowerStates, true); |
| } |
| } |
| |
| private: |
| void setHostPowerState(std::vector<HostState>& hostPowerStates, |
| bool callFromStateChange) |
| { |
| 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; |
| } |
| } |
| if (callFromStateChange) |
| { |
| setPowerState(powerStateflag); |
| } |
| else |
| { |
| // This won't call callbacks (if exists) during the constructor of |
| // the class when the first read for this flag is true which is |
| // different from the init value of _powerState. |
| // Daemon that wants to do anything when the initial power state |
| // is true can check isPowerOn() and do it. |
| _powerState = 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<HostState> 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<HostState>( |
| hostStateService, hostStatePath, |
| _hostStateInterface, _hostStateProperty); |
| |
| hostPowerStates.emplace_back(currentHostState); |
| } |
| } |
| } |
| setHostPowerState(hostPowerStates, false); |
| } |
| |
| 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 |