psu-ng: Add method to get PSU conf from D-Bus.
Add a getPSUProperties method that will get the power supply unit
configuration information from the D-Bus properties populated by
entity-manager. The IBM common form factor power supplies will have
these properties under the interface called
xyz.openbmc_project.Configuration.IBMCFFPSConnector. See
I45b724238cffe6fb7f1f8f9947fa1c2c9e9e8015 for changes to add the
necessary updates to entity-manager.
The D-Bus property for I2CAddress is a uint64_t type, while the JSON
configuration file had a string. Move the PowerSupply constructor to the
cpp file, updated to take in uint16_t for the i2caddr.
Update PSUManager::getJSONProperties() to convert Address from string to
uint16_t.
Add in a PSUManager constructor to use the getPSUProperties() function.
Add code to handle interfacesAdded from entity-manager prior to
getPSUProperties() or getSystemProperties().
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
Change-Id: I553e8982cf008828823cea2c0f017ff23c9070ab
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 63697f4..cedc184 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -19,6 +19,39 @@
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
+PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
+ std::uint8_t i2cbus, std::uint16_t i2caddr) :
+ bus(bus),
+ inventoryPath(invpath)
+{
+ if (inventoryPath.empty())
+ {
+ throw std::invalid_argument{"Invalid empty inventoryPath"};
+ }
+
+ // Setup the functions to call when the D-Bus inventory path for the
+ // Present property changes.
+ presentMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
+ INVENTORY_IFACE),
+ [this](auto& msg) { this->inventoryChanged(msg); });
+
+ presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
+ [this](auto& msg) { this->inventoryAdded(msg); });
+
+ std::ostringstream ss;
+ ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
+ std::string addrStr = ss.str();
+ pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
+
+ // Get the current state of the Present property.
+ updatePresence();
+}
+
void PowerSupply::updatePresence()
{
try
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 24d1df8..fc41635 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -52,32 +52,7 @@
* @param[in] i2caddr - The 16-bit I2C address of the power supply
*/
PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
- std::uint8_t i2cbus, const std::string& i2caddr) :
- bus(bus),
- inventoryPath(invpath),
- pmbusIntf(phosphor::pmbus::createPMBus(i2cbus, i2caddr))
- {
- if (inventoryPath.empty())
- {
- throw std::invalid_argument{"Invalid empty inventoryPath"};
- }
-
- // Setup the functions to call when the D-Bus inventory path for the
- // Present property changes.
- presentMatch = std::make_unique<sdbusplus::bus::match_t>(
- bus,
- sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
- INVENTORY_IFACE),
- [this](auto& msg) { this->inventoryChanged(msg); });
- presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
- bus,
- sdbusplus::bus::match::rules::interfacesAdded() +
- sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
- std::bind(&PowerSupply::inventoryAdded, this,
- std::placeholders::_1));
- // Get the current state of the Present property.
- updatePresence();
- }
+ std::uint8_t i2cbus, const std::uint16_t i2caddr);
phosphor::pmbus::PMBusBase& getPMBus()
{
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index fa1294a..5bb0807 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -11,6 +11,12 @@
namespace phosphor::power::manager
{
+constexpr auto IBMCFFPSInterface =
+ "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
+constexpr auto i2cBusProp = "I2CBus";
+constexpr auto i2cAddressProp = "I2CAddress";
+constexpr auto psuNameProp = "Name";
+
constexpr auto supportedConfIntf =
"xyz.openbmc_project.Configuration.SupportedConfiguration";
constexpr auto maxCountProp = "MaxCount";
@@ -19,8 +25,8 @@
const std::string& configfile) :
bus(bus)
{
- // Parse out the JSON properties
sysProperties = {0};
+ // Parse out the JSON properties
getJSONProperties(configfile);
// Subscribe to InterfacesAdded before doing a property read, otherwise
// the interface could be created after the read attempt but before the
@@ -50,6 +56,39 @@
initialize();
}
+PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
+ bus(bus)
+{
+ sysProperties = {0};
+ // Subscribe to InterfacesAdded before doing a property read, otherwise
+ // the interface could be created after the read attempt but before the
+ // match is created.
+ entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::sender(
+ "xyz.openbmc_project.EntityManager"),
+ std::bind(&PSUManager::entityManagerIfaceAdded, this,
+ std::placeholders::_1));
+ getPSUConfiguration();
+ getSystemProperties();
+
+ using namespace sdeventplus;
+ auto interval = std::chrono::milliseconds(1000);
+ timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
+ e, std::bind(&PSUManager::analyze, this), interval);
+
+ // Subscribe to power state changes
+ powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
+ powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
+ POWER_IFACE),
+ [this](auto& msg) { this->powerStateChanged(msg); });
+
+ initialize();
+}
+
void PSUManager::getJSONProperties(const std::string& path)
{
nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
@@ -71,7 +110,7 @@
{
std::string invpath = psuJSON["Inventory"];
std::uint8_t i2cbus = psuJSON["Bus"];
- std::string i2caddr = psuJSON["Address"];
+ std::uint16_t i2caddr = static_cast<uint16_t>(psuJSON["Address"]);
auto psu =
std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
psus.emplace_back(std::move(psu));
@@ -88,6 +127,93 @@
}
}
+void PSUManager::getPSUConfiguration()
+{
+ using namespace phosphor::power::util;
+ auto depth = 0;
+ auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
+
+ psus.clear();
+
+ // I should get a map of objects back.
+ // Each object will have a path, a service, and an interface.
+ // The interface should match the one passed into this function.
+ for (const auto& [path, services] : objects)
+ {
+ auto service = services.begin()->first;
+
+ if (path.empty() || service.empty())
+ {
+ continue;
+ }
+
+ // 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, service);
+
+ getPSUProperties(properties);
+ }
+
+ if (psus.empty())
+ {
+ // Interface or properties not found. Let the Interfaces Added callback
+ // process the information once the interfaces are added to D-Bus.
+ log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
+ }
+}
+
+void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
+{
+ // From passed in properties, I want to get: I2CBus, I2CAddress,
+ // and Name. Create a power supply object, using Name to build the inventory
+ // path.
+ const auto basePSUInvPath =
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
+ uint64_t* i2cbus = nullptr;
+ uint64_t* i2caddr = nullptr;
+ std::string* psuname = 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]);
+ }
+ }
+ catch (std::exception& e)
+ {
+ }
+ }
+
+ if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
+ {
+ std::string invpath = basePSUInvPath;
+ invpath.push_back(psuname->back());
+
+ log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
+
+ auto psu =
+ std::make_unique<PowerSupply>(bus, invpath, *i2cbus, *i2caddr);
+ psus.emplace_back(std::move(psu));
+ }
+
+ if (psus.empty())
+ {
+ log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
+ }
+}
+
void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
{
try
@@ -148,12 +274,19 @@
msg.read(objPath, interfaces);
auto itIntf = interfaces.find(supportedConfIntf);
- if (itIntf == interfaces.cend())
+ if (itIntf != interfaces.cend())
{
- return;
+ populateSysProperties(itIntf->second);
}
- populateSysProperties(itIntf->second);
+ itIntf = interfaces.find(IBMCFFPSInterface);
+ if (itIntf != interfaces.cend())
+ {
+ log<level::INFO>(
+ fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
+ .c_str());
+ getPSUProperties(itIntf->second);
+ }
}
catch (std::exception& e)
{
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 0f93279..b950869 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -37,7 +37,7 @@
PSUManager& operator=(PSUManager&&) = delete;
/**
- * Constructor
+ * Constructor to read configuration from JSON file.
*
* @param[in] bus - D-Bus bus object
* @param[in] e - event object
@@ -47,12 +47,34 @@
const std::string& configfile);
/**
+ * Constructor to read configuration from D-Bus.
+ *
+ * @param[in] bus - D-Bus bus object
+ * @param[in] e - event object
+ */
+ PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e);
+
+ /**
* @brief Initialize the PowerSupply objects from the JSON config file.
* @param[in] path - Path to the JSON config file
*/
void getJSONProperties(const std::string& path);
/**
+ * Get PSU properties from D-Bus, use that to build a power supply
+ * object.
+ *
+ * @param[in] properties - A map of property names and values
+ *
+ */
+ void getPSUProperties(util::DbusPropertyMap& properties);
+
+ /**
+ * Get PSU configuration from D-Bus
+ */
+ void getPSUConfiguration();
+
+ /**
* @brief Initialize the system properties from the Supported Configuration
* D-Bus object provided by Entity Manager.
*/
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 07b4b9e..b028d56 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -50,7 +50,7 @@
// Try where inventory path is empty, constructor should fail.
try
{
- auto psu = std::make_unique<PowerSupply>(bus, "", 3, "0068");
+ auto psu = std::make_unique<PowerSupply>(bus, "", 3, 0x68);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -68,7 +68,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1);
auto psu =
- std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, "0068");
+ std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68);
EXPECT_EQ(psu->isPresent(), false);
EXPECT_EQ(psu->isFaulted(), false);
@@ -87,7 +87,7 @@
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
- PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
+ PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
psu.analyze();
// By default, nothing should change.
EXPECT_EQ(psu.isPresent(), false);
@@ -101,7 +101,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(true)); // present
- PowerSupply psu2{bus, PSUInventoryPath, 5, "006a"};
+ PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a};
EXPECT_EQ(psu2.isPresent(), true);
// STATUS_WORD 0x0000 is powered on, no faults.
@@ -193,7 +193,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(false));
- PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
+ PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
// If it is not present, I should not be trying to write to it.
EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
@@ -209,7 +209,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 5, "006a"};
+ PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
// TODO: ???should I check the filename?
EXPECT_CALL(mockPMBus,
@@ -228,7 +228,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 13, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 13, 0x68};
EXPECT_EQ(psu.isPresent(), true);
EXPECT_EQ(psu.isFaulted(), false);
EXPECT_EQ(psu.hasInputFault(), false);
@@ -265,7 +265,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(false)); // missing
- PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
// If it is not present, I should not be trying to read a string
EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
@@ -281,7 +281,7 @@
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
.Times(1)
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 13, "0069"};
+ PowerSupply psu{bus, PSUInventoryPath, 13, 0x69};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
psu.updateInventory();
@@ -306,12 +306,12 @@
{
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
- PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
EXPECT_EQ(psu.isPresent(), false);
EXPECT_CALL(mockedUtil, getPresence(_, _))
.WillOnce(Return(true)); // present
- PowerSupply psu2{bus, PSUInventoryPath, 10, "006b"};
+ PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b};
EXPECT_EQ(psu2.isPresent(), true);
}
@@ -320,7 +320,7 @@
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, _))
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 11, "006f"};
+ PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f};
EXPECT_EQ(psu.isFaulted(), false);
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
EXPECT_CALL(mockPMBus, read(_, _))
@@ -336,7 +336,7 @@
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, _))
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
EXPECT_EQ(psu.hasInputFault(), false);
EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -358,7 +358,7 @@
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, _))
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
EXPECT_EQ(psu.hasMFRFault(), false);
EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -380,7 +380,7 @@
auto bus = sdbusplus::bus::new_default();
EXPECT_CALL(mockedUtil, getPresence(_, _))
.WillOnce(Return(true)); // present
- PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
+ PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
EXPECT_EQ(psu.hasVINUVFault(), false);
EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));