Enable dynamic presence detect of FRUs
This commit enables presence detect of FRUs at runtime.
Anytime any FRU can get attached or de-attached,
this code will detect it and will enable/disable the corresponding
output I2C pin respectively.
Right now we have only one FRU- op-panel, which is attachable or
de-attachable at runtime.
Test- Tested on Simics:
>> 2timers keep running, as part of vpd-manager-
./vpd-manager
keep checking for event occurance...
hasEventOccurred ?
keep checking for event occurance...
hasEventOccurred ?
>> changed signal at presence-pin of FRU2
keep checking for event occurance...
hasEventOccurred ?
keep checking for event occurance...
hasEventOccurred ?
Yes, togggle the gpio <---------------------event on 2nd timer
>> Again, changed signal at presence-pin of FRU2
keep checking for event occurance...
hasEventOccurred ?
keep checking for event occurance...
hasEventOccurred ?
Yes, togggle the gpio <---------------------event on 2nd timer
>> changed signal at presence-pin of FRU1
keep checking for event occurance...
hasEventOccurred ?
Yes, togggle the gpio <---------------------event on 1st timer
keep checking for event occurance...
hasEventOccurred ?
>> Again changed signal at presence-pin of FRU1
keep checking for event occurance...
hasEventOccurred ?
Yes, togggle the gpio <---------------------event on 1st timer
keep checking for event occurance...
hasEventOccurred ?
>> Noticed change on output gpio after every signal change.
As of now for testing, output gpio is same for both of these FRUs-
>> Effects on i2c-
i2cdetect -y 7
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: UU UU UU -- -- -- -- -- -- -- 5a -- -- -- -- --
60: UU UU UU -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- UU --
i2cdetect -y 7
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: UU 51 UU -- -- -- -- -- -- -- 5a -- -- -- -- --
60: UU UU UU -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- UU --
Also tested for write functionality, shouldn't be affected due to this change
It looks fine.
root@p10bmc:~# busctl introspect xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard |grep PN
.PN property ay 7 48 50 87 71 54 55 56 emits-change writable
root@p10bmc:~# busctl call com.ibm.VPD.Manager /com/ibm/VPD/Manager com.ibm.VPD.Manager WriteKeyword ossay "/system/chassis/motherboard" "VINI" "PN" 1 80
root@p10bmc:~# busctl introspect xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard |grep PN
.PN property ay 7 80 50 87 71 54 55 56 emits-change writable
root@p10bmc:~# busctl call com.ibm.VPD.Manager /com/ibm/VPD/Manager com.ibm.VPD.Manager WriteKeyword ossay "/system/chassis/motherboard" "VINI" "PN" 1 48
root@p10bmc:~# busctl introspect xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard |grep PN
.PN property ay 7 48 50 87 71 54 55 56 emits-change writable
Change-Id: If7d311d36bf56ece751afe393a9ba2d83be5df11
Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 8cedafb..f067b9b 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -224,5 +224,22 @@
const string getKwVal(const Parsed& vpdMap, const string& rec,
const string& kwd);
+/** @brief This creates a complete command using all it's input parameters,
+ * to bind or unbind the driver.
+ * @param[in] devNameAddr - device address on that bus
+ * @param[in] busType - i2c, spi
+ * @param[in] driverType - type of driver like at24
+ * @param[in] bindOrUnbind - either bind or unbind
+ * @returns Command to bind or unbind the driver.
+ */
+inline string createBindUnbindDriverCmnd(const string& devNameAddr,
+ const string& busType,
+ const string& driverType,
+ const string& bindOrUnbind)
+{
+ return ("echo " + devNameAddr + " > /sys/bus/" + busType + "/drivers/" +
+ driverType + "/" + bindOrUnbind);
+}
+
} // namespace vpd
} // namespace openpower
\ No newline at end of file
diff --git a/vpd-manager/gpioMonitor.cpp b/vpd-manager/gpioMonitor.cpp
new file mode 100644
index 0000000..b57d52c
--- /dev/null
+++ b/vpd-manager/gpioMonitor.cpp
@@ -0,0 +1,173 @@
+#include "gpioMonitor.hpp"
+
+#include "common_utility.hpp"
+#include "ibm_vpd_utils.hpp"
+
+#include <systemd/sd-event.h>
+
+#include <chrono>
+#include <gpiod.hpp>
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/utility/timer.hpp>
+
+using namespace std;
+using namespace openpower::vpd::constants;
+using sdeventplus::ClockId;
+using sdeventplus::Event;
+using Timer = sdeventplus::utility::Timer<ClockId::Monotonic>;
+using namespace std::chrono_literals;
+
+namespace openpower
+{
+namespace vpd
+{
+namespace manager
+{
+
+bool GpioEventHandler::getPresencePinValue()
+{
+ Byte gpioData = 1;
+ gpiod::line presenceLine = gpiod::find_line(presencePin);
+ if (!presenceLine)
+ {
+ cerr << "Error getPresencePinValue: couldn't find presence line:"
+ << presencePin << " on GPIO \n";
+ // return previous state as we couldn't read current state
+ return prevPresPinValue;
+ }
+
+ presenceLine.request(
+ {"Op-panel presence line", gpiod::line_request::DIRECTION_INPUT, 0});
+
+ gpioData = presenceLine.get_value();
+
+ return gpioData;
+}
+
+void GpioMonitor::initGpioInfos(Event& event)
+{
+ Byte outputValue = 0;
+ Byte presenceValue = 0;
+ string presencePinName{}, outputPinName{};
+ string devNameAddr{}, driverType{}, busType{}, objectPath{};
+
+ for (const auto& eachFRU : jsonFile["frus"].items())
+ {
+ for (const auto& eachInventory : eachFRU.value())
+ {
+ objectPath = eachInventory["inventoryPath"];
+
+ if ((eachInventory.find("presence") != eachInventory.end()) &&
+ (eachInventory.find("preAction") != eachInventory.end()))
+ {
+ for (const auto& presStatus : eachInventory["presence"].items())
+ {
+ if (presStatus.key() == "pin")
+ {
+ presencePinName = presStatus.value();
+ }
+ else if (presStatus.key() == "value")
+ {
+ presenceValue = presStatus.value();
+ }
+ }
+
+ // Based on presence pin value, preAction pin will be set/reset
+ // This action will be taken before vpd collection.
+ for (const auto& preAction : eachInventory["preAction"].items())
+ {
+ if (preAction.key() == "pin")
+ {
+ outputPinName = preAction.value();
+ }
+ else if (preAction.key() == "value")
+ {
+ outputValue = preAction.value();
+ }
+ }
+
+ devNameAddr = eachInventory["devAddress"];
+ driverType = eachInventory["driverType"];
+ busType = eachInventory["busType"];
+
+ // Init all Gpio info variables
+ std::shared_ptr<GpioEventHandler> gpioObj =
+ make_shared<GpioEventHandler>(
+ presencePinName, presenceValue, outputPinName,
+ outputValue, devNameAddr, driverType, busType,
+ objectPath, event);
+
+ gpioObjects.push_back(gpioObj);
+ }
+ }
+ }
+}
+
+void GpioEventHandler::toggleGpio()
+{
+ bool presPinVal = getPresencePinValue();
+ bool isPresent = false;
+
+ // preserve the new value
+ prevPresPinValue = presPinVal;
+
+ if (presPinVal == presenceValue)
+ {
+ isPresent = true;
+ }
+
+ // if FRU went away set the present property to false
+ if (!isPresent)
+ {
+ inventory::ObjectMap objects;
+ inventory::InterfaceMap interfaces;
+ inventory::PropertyMap presProp;
+
+ presProp.emplace("Present", false);
+ interfaces.emplace("xyz.openbmc_project.Inventory.Item", presProp);
+ objects.emplace(objectPath, move(interfaces));
+
+ common::utility::callPIM(move(objects));
+ }
+
+ gpiod::line outputLine = gpiod::find_line(outputPin);
+ if (!outputLine)
+ {
+ cerr << "Error: toggleGpio: couldn't find output line:" << outputPin
+ << ". Skipping update\n";
+
+ return;
+ }
+
+ outputLine.request({"FRU presence: update the output GPIO pin",
+ gpiod::line_request::DIRECTION_OUTPUT, 0},
+ isPresent ? outputValue : (!outputValue));
+
+ string cmnd = createBindUnbindDriverCmnd(devNameAddr, busType, driverType,
+ isPresent ? "bind" : "unbind");
+
+ cout << cmnd << endl;
+ executeCmd(cmnd);
+}
+
+void GpioEventHandler::doEventAndTimerSetup(sdeventplus::Event& event)
+{
+ prevPresPinValue = getPresencePinValue();
+
+ static vector<shared_ptr<Timer>> timers;
+ shared_ptr<Timer> timer = make_shared<Timer>(
+ event,
+ [this](Timer&) {
+ if (hasEventOccurred())
+ {
+ toggleGpio();
+ }
+ },
+ std::chrono::seconds{5s});
+
+ timers.push_back(timer);
+}
+
+} // namespace manager
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-manager/gpioMonitor.hpp b/vpd-manager/gpioMonitor.hpp
new file mode 100644
index 0000000..66c049d
--- /dev/null
+++ b/vpd-manager/gpioMonitor.hpp
@@ -0,0 +1,137 @@
+#pragma once
+
+#include "manager.hpp"
+
+#include <sdeventplus/event.hpp>
+
+namespace openpower
+{
+namespace vpd
+{
+namespace manager
+{
+/** @class GpioEventHandler
+ * @brief Responsible for catching the event and handle it.
+ * This keeps checking for the FRU's presence.
+ * If any attachment or de-attachment found, it enables/disables that
+ * fru's output gpio and bind/unbind the driver, respectively.
+ */
+class GpioEventHandler
+{
+ public:
+ GpioEventHandler() = default;
+ ~GpioEventHandler() = default;
+ GpioEventHandler(const GpioEventHandler&) = default;
+ GpioEventHandler& operator=(const GpioEventHandler&) = delete;
+ GpioEventHandler(GpioEventHandler&&) = delete;
+ GpioEventHandler& operator=(GpioEventHandler&&) = delete;
+
+ GpioEventHandler(std::string& presPin, Byte& presValue, std::string& outPin,
+ Byte& outValue, std::string& devAddr, std::string& driver,
+ std::string& bus, std::string& objPath,
+ sdeventplus::Event& event) :
+ presencePin(presPin),
+ presenceValue(presValue), outputPin(outPin), outputValue(outValue),
+ devNameAddr(devAddr), driverType(driver), busType(bus),
+ objectPath(objPath)
+ {
+ doEventAndTimerSetup(event);
+ }
+
+ private:
+ /** @brief GPIO informations to get parsed from vpd json*/
+
+ // gpio pin indicates presence/absence of fru
+ const std::string presencePin;
+ // value which means fru is present
+ const Byte presenceValue;
+ // gpio pin to enable If fru is present
+ const std::string outputPin;
+ // Value to set, to enable the output pin
+ const Byte outputValue;
+
+ // FRU address on bus
+ const std::string devNameAddr;
+ // Driver type
+ const std::string driverType;
+ // Bus type
+ const std::string busType;
+ // object path of FRU
+ const std::string objectPath;
+
+ /** Preserves the GPIO pin value to compare it next time. Default init by
+ * false*/
+ bool prevPresPinValue = false;
+
+ /** @brief This is a helper function to read the
+ * current value of Presence GPIO
+ *
+ * @returns The GPIO value
+ */
+ bool getPresencePinValue();
+
+ /** @brief This function will toggle the output gpio as per the presence
+ * state of fru.
+ */
+ void toggleGpio();
+
+ /** @brief This function checks for fru's presence pin and detects change of
+ * value on that pin, (in case of fru gets attached or de-attached).
+ *
+ * @returns true if presence pin value changed
+ * false otherwise
+ */
+ inline bool hasEventOccurred()
+ {
+ return getPresencePinValue() != prevPresPinValue;
+ }
+
+ /** @brief This function runs a timer , which keeps checking for if an event
+ * happened, if event occured then takes action.
+ * @param[in] timer- Shared pointer of Timer to do event setup for each
+ * object.
+ * @param[in] event- Event which needs to be tagged with the timer.
+ */
+ void doEventAndTimerSetup(sdeventplus::Event& event);
+};
+
+/** @class GpioMonitor
+ * @brief Responsible for initialising the private variables containing gpio
+ * infos. These informations will be fetched from vpd json.
+ */
+class GpioMonitor
+{
+ public:
+ GpioMonitor() = delete;
+ ~GpioMonitor() = default;
+ GpioMonitor(const GpioMonitor&) = delete;
+ GpioMonitor& operator=(const GpioMonitor&) = delete;
+ GpioMonitor(GpioMonitor&&) = delete;
+ GpioMonitor& operator=(GpioMonitor&&) = delete;
+
+ GpioMonitor(nlohmann::json& js, sdeventplus::Event& event) : jsonFile(js)
+ {
+ initGpioInfos(event);
+ }
+
+ private:
+ // Json file to get the datas
+ nlohmann::json& jsonFile;
+ // Array of event handlers for all the attachable FRUs
+ std::vector<std::shared_ptr<GpioEventHandler>> gpioObjects;
+
+ /** @brief This function will extract the gpio informations from vpd json
+ * and store it in GpioEventHandler's private variables
+ * @param[in] gpioObj - shared object to initialise it's data and it's
+ * Timer setup
+ * @param[in] requestedGpioPin - Which GPIO's informations need to be
+ * stored
+ * @param[in] timer - shared object of timer to do the event setup
+ * @param[in] event - event to be tagged with timer.
+ */
+ void initGpioInfos(sdeventplus::Event& event);
+};
+
+} // namespace manager
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index 829dce8..edef56b 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -3,6 +3,7 @@
#include "manager.hpp"
#include "editor_impl.hpp"
+#include "gpioMonitor.hpp"
#include "ibm_vpd_utils.hpp"
#include "ipz_parser.hpp"
#include "reader_impl.hpp"
@@ -10,6 +11,7 @@
#include <phosphor-logging/elog-errors.hpp>
+using namespace openpower::vpd::manager;
using namespace openpower::vpd::constants;
using namespace openpower::vpd::inventory;
using namespace openpower::vpd::manager::editor;
@@ -38,19 +40,18 @@
try
{
processJSON();
+
+ auto event = sdeventplus::Event::get_default();
+ GpioMonitor gpioMon1(jsonFile, event);
+
+ _bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
+ cout << "VPD manager event loop started\n";
+ event.loop();
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
-
- while (true)
- {
- _bus.process_discard();
-
- // wait for event
- _bus.wait();
- }
}
void Manager::processJSON()
@@ -233,15 +234,14 @@
}
else
{
- string cmd = str + deviceAddress + " > /sys/bus/" + busType +
- "/drivers/" + driverType;
-
- executeCmd(cmd + "/unbind");
- executeCmd(cmd + "/bind");
+ executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
+ driverType, "/unbind"));
+ executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
+ driverType, "/bind"));
}
}
}
} // namespace manager
} // namespace vpd
-} // namespace openpower
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-manager/meson.build b/vpd-manager/meson.build
index 66e283f..b6ffa60 100644
--- a/vpd-manager/meson.build
+++ b/vpd-manager/meson.build
@@ -1,5 +1,6 @@
systemd = dependency('libsystemd', version: '>= 221')
phosphor_dbus_interfaces = dependency('phosphor-dbus-interfaces')
+sdeventplus = dependency('sdeventplus')
configuration_inc = include_directories('.', '../', '../vpd-parser/')
@@ -9,6 +10,7 @@
'error.cpp',
'editor_impl.cpp',
'reader_impl.cpp',
+ 'gpioMonitor.cpp',
'../impl.cpp',
'../vpd-parser/ipz_parser.cpp',
'../ibm_vpd_utils.cpp',
@@ -24,11 +26,13 @@
phosphor_logging,
systemd,
phosphor_dbus_interfaces,
+ libgpiodcxx,
+ sdeventplus,
]
vpd_manager_exe = executable(
'vpd-manager',
- vpd_manager_SOURCES,
+ vpd_manager_SOURCES,
include_directories : configuration_inc,
dependencies : [
vpd_manager_dependencies,