blob: 85e2a88d3809ea88d8c280e9e3f1daecbc2c5d66 [file] [log] [blame]
#include "config.h"
#include "chassis.hpp"
#include <filesystem>
#include <iostream>
using namespace phosphor::logging;
using namespace phosphor::power::util;
namespace phosphor::power::chassis
{
constexpr auto IBMCFFPSInterface =
"xyz.openbmc_project.Configuration.IBMCFFPSConnector";
constexpr auto chassisIdProp = "SlotNumber";
constexpr auto i2cBusProp = "I2CBus";
constexpr auto i2cAddressProp = "I2CAddress";
constexpr auto psuNameProp = "Name";
constexpr auto presLineName = "NamedPresenceGpio";
constexpr auto supportedConfIntf =
"xyz.openbmc_project.Configuration.SupportedConfiguration";
const auto deviceDirPath = "/sys/bus/i2c/devices/";
const auto driverDirName = "/driver";
const auto entityMgrService = "xyz.openbmc_project.EntityManager";
const auto decoratorChassisId = "xyz.openbmc_project.Inventory.Decorator.Slot";
Chassis::Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath,
const sdeventplus::Event& e) :
bus(bus), chassisPath(chassisPath),
chassisPathUniqueId(getChassisPathUniqueId(chassisPath)), eventLoop(e)
{
getPSUConfiguration();
}
void Chassis::getPSUConfiguration()
{
namespace fs = std::filesystem;
auto depth = 0;
try
{
if (chassisPathUniqueId == invalidObjectPathUniqueId)
{
lg2::error("Chassis does not have chassis ID: {CHASSISPATH}",
"CHASSISPATH", chassisPath);
return;
}
auto connectorsSubTree = getSubTree(bus, "/", IBMCFFPSInterface, depth);
for (const auto& [path, services] : connectorsSubTree)
{
uint64_t id;
fs::path fspath(path);
getProperty(decoratorChassisId, chassisIdProp, fspath.parent_path(),
entityMgrService, bus, id);
if (id == static_cast<uint64_t>(chassisPathUniqueId))
{
// For each object in the array of objects, I want
// to get properties from the service, path, and
// interface.
auto properties = getAllProperties(bus, path, IBMCFFPSInterface,
entityMgrService);
getPSUProperties(properties);
}
}
}
catch (const sdbusplus::exception_t& e)
{
lg2::error("Failed while getting configuration - exception: {ERROR}",
"ERROR", e);
}
if (psus.empty())
{
// Interface or properties not found. Let the Interfaces Added callback
// process the information once the interfaces are added to D-Bus.
lg2::info("No power supplies to monitor");
}
}
void Chassis::getPSUProperties(util::DbusPropertyMap& properties)
{
std::string basePSUInvPath = chassisPath + "/motherboard/powersupply";
// From passed in properties, I want to get: I2CBus, I2CAddress,
// and Name. Create a power supply object, using Name to build the inventory
// path.
uint64_t* i2cbus = nullptr;
uint64_t* i2caddr = nullptr;
std::string* psuname = nullptr;
std::string* preslineptr = nullptr;
for (const auto& property : properties)
{
try
{
if (property.first == i2cBusProp)
{
i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
}
else if (property.first == i2cAddressProp)
{
i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
}
else if (property.first == psuNameProp)
{
psuname = std::get_if<std::string>(&properties[psuNameProp]);
}
else if (property.first == presLineName)
{
preslineptr =
std::get_if<std::string>(&properties[presLineName]);
}
}
catch (const std::exception& e)
{}
}
if (i2cbus && i2caddr && psuname && !psuname->empty())
{
std::string invpath = basePSUInvPath;
invpath.push_back(psuname->back());
std::string presline = "";
lg2::debug("Inventory Path: {INVPATH}", "INVPATH", invpath);
if (nullptr != preslineptr)
{
presline = *preslineptr;
}
auto invMatch =
std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) {
return psu->getInventoryPath() == invpath;
});
if (invMatch != psus.end())
{
// This power supply has the same inventory path as the one with
// information just added to D-Bus.
// Changes to GPIO line name unlikely, so skip checking.
// Changes to the I2C bus and address unlikely, as that would
// require corresponding device tree updates.
// Return out to avoid duplicate object creation.
return;
}
buildDriverName(*i2cbus, *i2caddr);
lg2::debug(
"make PowerSupply bus: {I2CBUS} addr: {I2CADDR} presline: {PRESLINE}",
"I2CBUS", *i2cbus, "I2CADDR", *i2caddr, "PRESLINE", presline);
// auto psu = std::make_unique<PowerSupply>(
// bus, invpath, *i2cbus, *i2caddr, driverName, presline,
// std::bind(&Chassis::isPowerOn, this), chassisPath);
auto psu = std::make_unique<PowerSupply>(
bus, invpath, *i2cbus, *i2caddr, driverName, presline,
std::bind(&Chassis::isPowerOn, this));
psus.emplace_back(std::move(psu));
// Subscribe to power supply presence changes
auto presenceMatch = std::make_unique<sdbusplus::bus::match_t>(
bus,
sdbusplus::bus::match::rules::propertiesChanged(invpath,
INVENTORY_IFACE),
[this](auto& msg) { this->psuPresenceChanged(msg); });
presenceMatches.emplace_back(std::move(presenceMatch));
}
if (psus.empty())
{
lg2::info("No power supplies to monitor");
}
else
{
populateDriverName();
}
}
void Chassis::getSupportedConfiguration()
{
// TBD
}
void Chassis::populateSupportedConfiguration(
const util::DbusPropertyMap& properties)
{
try
{
auto propIt = properties.find("SupportedType");
if (propIt == properties.end())
{
return;
}
const std::string* type = std::get_if<std::string>(&(propIt->second));
if ((type == nullptr) || (*type != "PowerSupply"))
{
return;
}
propIt = properties.find("SupportedModel");
if (propIt == properties.end())
{
return;
}
const std::string* model = std::get_if<std::string>(&(propIt->second));
if (model == nullptr)
{
return;
}
SupportedPsuConfiguration supportedPsuConfig;
propIt = properties.find("RedundantCount");
if (propIt != properties.end())
{
const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
if (count != nullptr)
{
supportedPsuConfig.powerSupplyCount = *count;
}
}
propIt = properties.find("InputVoltage");
if (propIt != properties.end())
{
const std::vector<uint64_t>* voltage =
std::get_if<std::vector<uint64_t>>(&(propIt->second));
if (voltage != nullptr)
{
supportedPsuConfig.inputVoltage = *voltage;
}
}
// The PowerConfigFullLoad is an optional property, default it to false
// since that's the default value of the power-config-full-load GPIO.
supportedPsuConfig.powerConfigFullLoad = false;
propIt = properties.find("PowerConfigFullLoad");
if (propIt != properties.end())
{
const bool* fullLoad = std::get_if<bool>(&(propIt->second));
if (fullLoad != nullptr)
{
supportedPsuConfig.powerConfigFullLoad = *fullLoad;
}
}
supportedConfigs.emplace(*model, supportedPsuConfig);
}
catch (const std::exception& e)
{
lg2::info("populateSupportedConfiguration error {ERR}", "ERR", e);
}
}
void Chassis::psuPresenceChanged(sdbusplus::message_t& msg)
{
std::string msgSensor;
std::map<std::string, std::variant<uint32_t, bool>> msgData;
msg.read(msgSensor, msgData);
// Check if it was the Present property that changed.
auto valPropMap = msgData.find(PRESENT_PROP);
if (valPropMap != msgData.end())
{
if (std::get<bool>(valPropMap->second))
{
// A PSU became present, force the PSU validation to run.
runValidateConfig = true;
validationTimer->restartOnce(validationTimeout);
}
}
}
void Chassis::buildDriverName(uint64_t i2cbus, uint64_t i2caddr)
{
namespace fs = std::filesystem;
std::stringstream ss;
ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
std::string symLinkPath =
deviceDirPath + std::to_string(i2cbus) + "-" + ss.str() + driverDirName;
try
{
fs::path linkStrPath = fs::read_symlink(symLinkPath);
driverName = linkStrPath.filename();
}
catch (const std::exception& e)
{
lg2::error(
"Failed to find device driver {SYM_LINK_PATH}, error {ERROR_STR}",
"SYM_LINK_PATH", symLinkPath, "ERROR_STR", e);
}
}
void Chassis::populateDriverName()
{
std::string driverName;
// Search in PSUs for driver name
std::for_each(psus.begin(), psus.end(), [&driverName](auto& psu) {
if (!psu->getDriverName().empty())
{
driverName = psu->getDriverName();
}
});
// Assign driver name to all PSUs
std::for_each(psus.begin(), psus.end(),
[&driverName](auto& psu) { psu->setDriverName(driverName); });
}
uint64_t Chassis::getChassisPathUniqueId(const std::string& path)
{
try
{
return getChassisInventoryUniqueId(bus, path);
}
catch (const sdbusplus::exception_t& e)
{
lg2::error(
"Failed to find chassis path {CHASSIS_PATH} ID - exception: {ERROR}",
"CHASSIS_PATH", path, "ERROR", e);
}
return invalidObjectPathUniqueId;
}
void Chassis::analyze() {}
} // namespace phosphor::power::chassis