psu-ng: Fixed PSU monitor app to bind/unbind device driver appropriately.

Modified bindOrUnbind function:
      Bind the  device driver when the PSU present and i2cbus-i2caddr
      does not exist. Unbind the device driver when i2cbus-i2caddr exists
      and the PSU not present.

      bindOrUnbind does not do anything when:
      * PSU device driver bind exist and the device present
      * PSU device driver bind does not exist and device not present

Added new function populateDriverName:
      Search for device driver name in all PSUs, then assign the device
      driver name to all PSUs including missing PSUs.

Testing:
    Verified in the following test, there is no bind/unbind error log.
        1 - Removed 1 PSU and powered on the BMC with 3 PSUs
        2 - Use step 1, after power on plugged in the 4th PSU and verified
            /sys/bus/i2c/drivers/-ibm-cffps/3-006b exist
        3 - Removed 2 PSUs and powered on the BMC with 2 PSUs.
        4 - Use step 3, after power on plugged in the missing PSUs, verified
            bind/Unbind /sys/bus/i2c/drivers/-ibm-cffps/3-006x for appropriate PSU.
        5 - Powered on system with 4 PSUs then removed 2 PSUs. Verified
            unbind done for appropriate PSU.

Change-Id: I325fc0dbb16f9c3b582b739e8440a74e34aae753
Signed-off-by: Faisal Awada <faisal@us.ibm.com>
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index fa4b8e3..6ec8115 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -96,9 +96,36 @@
 
 void PowerSupply::bindOrUnbindDriver(bool present)
 {
+    // Symbolic link to the device will exist if the driver is bound.
+    // So exit no action required if both the link and PSU are present
+    // or neither is present.
+    namespace fs = std::filesystem;
+    fs::path path;
     auto action = (present) ? "bind" : "unbind";
-    auto path = bindPath / action;
 
+    // This case should not happen, if no device driver name return.
+    if (driverName.empty())
+    {
+        log<level::INFO>("No device driver name found");
+        return;
+    }
+    if (bindPath.string().find(driverName) != std::string::npos)
+    {
+        // bindPath has driver name
+        path = bindPath / action;
+    }
+    else
+    {
+        // Add driver name to bindPath
+        path = bindPath / driverName / action;
+        bindPath = bindPath / driverName;
+    }
+
+    if ((std::filesystem::exists(bindPath / bindDevice) && present) ||
+        (!std::filesystem::exists(bindPath / bindDevice) && !present))
+    {
+        return;
+    }
     if (present)
     {
         std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index da76434..3c6408b 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -547,6 +547,23 @@
      */
     static double linearToInteger(uint16_t data);
 
+    /**
+     * @brief Retrieve device driver name
+     */
+    const std::string& getDriverName() const
+    {
+        return driverName;
+    }
+
+    /**
+     * @brief Set device driver name
+     * @param[in] newDriver - device driver name.
+     */
+    void setDriverName(const std::string& newDriver)
+    {
+        driverName = newDriver;
+    }
+
   private:
     /**
      * @brief Examine STATUS_WORD for CML (communication, memory, logic fault).
@@ -656,6 +673,10 @@
      * driver for the power supply when it is installed, or unbind the device
      * driver when the power supply is removed.
      *
+     * Note:
+     *    Bind device when device present and i2cbus-i2caddr does not exist
+     *    UnBind device when device not present and i2cbus-i2caddr  exist
+
      * Writes <device> to <path>/bind (or unbind)
      *
      * @param present - when true, will bind the device driver
@@ -784,7 +805,7 @@
     /**
      * @brief The file system path used for binding the device driver.
      */
-    const std::filesystem::path bindPath;
+    std::filesystem::path bindPath;
 
     /**
      * @brief Get the power on status of the psu manager class.
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 277497c..3ef0bb6 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -272,6 +272,10 @@
     {
         log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
     }
+    else
+    {
+        populateDriverName();
+    }
 }
 
 void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
@@ -1313,4 +1317,19 @@
                             .c_str());
     }
 }
+
+void PSUManager::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(),
+                  [=](auto& psu) { psu->setDriverName(driverName); });
+}
 } // namespace phosphor::power::manager
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 0533a95..79caa96 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -411,6 +411,12 @@
     void buildDriverName(uint64_t i2cbus, uint64_t i2caddr);
 
     /**
+     * @brief Find PSU with device driver name, then populate the device
+     *        driver name to all PSUs (including missing PSUs).
+     */
+    void populateDriverName();
+
+    /**
      * @brief The device driver name for all power supplies.
      */
     std::string driverName;