blob: c428a39b8333a421418cdf68da3d7692f8133975 [file] [log] [blame]
#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