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,