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