Support LED control for phosphor-nvme

- Add logic control of the LED
- Set inventory of NVMe drive to D-bus inventory manager

Change-Id: Ie1ef6b1a4523fcc80c3c2e9d274edd6fd02b2990
Signed-off-by: Tony Lee <tony.lee@quantatw.com>
diff --git a/meson.build b/meson.build
index 93a1537..19506df 100644
--- a/meson.build
+++ b/meson.build
@@ -32,6 +32,16 @@
 conf_data.set('NVME_OBJ_PATH_ROOT', '"/xyz/openbmc_project/sensors/temperature"')
 conf_data.set('NVME_OBJ_PATH', '"/xyz/openbmc_project/sensors/temperature/nvme"')
 conf_data.set('DBUS_PROPERTY_IFACE', '"org.freedesktop.DBus.Properties"')
+conf_data.set('LED_GROUP_BUSNAME', '"xyz.openbmc_project.LED.GroupManager"')
+conf_data.set('LED_GROUP_IFACE', '"xyz.openbmc_project.Led.Group"')
+conf_data.set('LED_CONTROLLER_IFACE', '"xyz.openbmc_project.Led.Physical"')
+conf_data.set('ITEM_IFACE', '"xyz.openbmc_project.Inventory.Item"')
+conf_data.set('NVME_STATUS_IFACE', '"xyz.openbmc_project.Nvme.Status"')
+conf_data.set('ASSET_IFACE', '"xyz.openbmc_project.Inventory.Decorator.Asset"')
+conf_data.set('INVENTORY_BUSNAME', '"xyz.openbmc_project.Inventory.Manager"')
+conf_data.set('NVME_INVENTORY_PATH', '"/xyz/openbmc_project/inventory/system/chassis/motherboard/nvme"')
+conf_data.set('INVENTORY_NAMESPACE', '"/xyz/openbmc_project/inventory"')
+conf_data.set('INVENTORY_MANAGER_IFACE', '"xyz.openbmc_project.Inventory.Manager"')
 
 configure_file(output : 'config.h',
                configuration : conf_data)
diff --git a/nvme_config.json b/nvme_config.json
index 2f6423d..324ebd2 100644
--- a/nvme_config.json
+++ b/nvme_config.json
@@ -3,48 +3,80 @@
         {
             "NVMeDriveIndex": 0,
             "NVMeDriveBusID": 16,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_0_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_0_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_0_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_0_locate",
             "NVMeDrivePresentPin": 148,
             "NVMeDrivePwrGoodPin": 161
         },
         {
             "NVMeDriveIndex": 1,
             "NVMeDriveBusID": 17,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_1_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_1_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_1_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_1_locate",
             "NVMeDrivePresentPin": 149,
             "NVMeDrivePwrGoodPin": 162
         },
         {
             "NVMeDriveIndex": 2,
             "NVMeDriveBusID": 18,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_2_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_2_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_2_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_2_locate",
             "NVMeDrivePresentPin": 150,
             "NVMeDrivePwrGoodPin": 163
         },
         {
             "NVMeDriveIndex": 3,
             "NVMeDriveBusID": 19,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_3_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_3_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_3_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_3_locate",
             "NVMeDrivePresentPin": 151,
             "NVMeDrivePwrGoodPin": 164
         },
         {
             "NVMeDriveIndex": 4,
             "NVMeDriveBusID": 20,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_4_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_4_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_4_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_4_locate",
             "NVMeDrivePresentPin": 152,
             "NVMeDrivePwrGoodPin": 165
         },
         {
             "NVMeDriveIndex": 5,
             "NVMeDriveBusID": 21,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_5_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_5_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_5_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_5_locate",
             "NVMeDrivePresentPin": 153,
             "NVMeDrivePwrGoodPin": 166
         },
         {
             "NVMeDriveIndex": 6,
             "NVMeDriveBusID": 22,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_6_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_6_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_6_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_6_locate",
             "NVMeDrivePresentPin": 154,
             "NVMeDrivePwrGoodPin": 167
         },
         {
             "NVMeDriveIndex": 7,
             "NVMeDriveBusID": 23,
+            "NVMeDriveFaultLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_7_fault",
+            "NVMeDriveLocateLEDGroupPath": "/xyz/openbmc_project/led/groups/led_u2_7_locate",
+            "NVMeDriveLocateLEDControllerBusName": "xyz.openbmc_project.LED.Controller.led_u2_7_locate",
+            "NVMeDriveLocateLEDControllerPath": "/xyz/openbmc_project/led/physical/led_u2_7_locate",
             "NVMeDrivePresentPin": 155,
             "NVMeDrivePwrGoodPin": 168
         }
diff --git a/nvme_manager.cpp b/nvme_manager.cpp
index 639f85d..0e0b7b7 100644
--- a/nvme_manager.cpp
+++ b/nvme_manager.cpp
@@ -7,8 +7,10 @@
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
+#include <sdbusplus/message.hpp>
 #include <sstream>
 #include <string>
+#include <xyz/openbmc_project/Led/Physical/server.hpp>
 
 #include "i2c.h"
 #define MONITOR_INTERVAL_SECONDS 1
@@ -16,6 +18,7 @@
 #define GPIO_BASE_PATH "/sys/class/gpio/gpio"
 #define IS_PRESENT "0"
 #define POWERGD "1"
+#define NOWARNING_STRING "ff"
 
 static constexpr auto configFile = "/etc/nvme/nvme_config.json";
 static constexpr auto delay = std::chrono::milliseconds{100};
@@ -24,6 +27,13 @@
 static constexpr const uint8_t COMMAND_CODE_0 = 0;
 static constexpr const uint8_t COMMAND_CODE_8 = 8;
 
+static constexpr int CapacityFaultMask = 1;
+static constexpr int temperatureFaultMask = 1 << 1;
+static constexpr int DegradesFaultMask = 1 << 2;
+static constexpr int MediaFaultMask = 1 << 3;
+static constexpr int BackupDeviceFaultMask = 1 << 4;
+static constexpr int NOWARNING = 255;
+
 static constexpr int SERIALNUMBER_START_INDEX = 3;
 static constexpr int SERIALNUMBER_END_INDEX = 23;
 
@@ -39,6 +49,143 @@
 using namespace std;
 using namespace phosphor::logging;
 
+void Nvme::setNvmeInventoryProperties(
+    bool present, const phosphor::nvme::Nvme::NVMeData& nvmeData,
+    const std::string& inventoryPath)
+{
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 ITEM_IFACE, "Present", present);
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 ASSET_IFACE, "Manufacturer", nvmeData.vendor);
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 ASSET_IFACE, "SerialNumber",
+                                 nvmeData.serialNumber);
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "SmartWarnings",
+                                 nvmeData.smartWarnings);
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "StatusFlags",
+                                 nvmeData.statusFlags);
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "DriveLifeUsed",
+                                 nvmeData.driveLifeUsed);
+
+    auto smartWarning = (!nvmeData.smartWarnings.empty())
+                            ? std::stoi(nvmeData.smartWarnings, 0, 16)
+                            : NOWARNING;
+
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "CapacityFault",
+                                 !(smartWarning & CapacityFaultMask));
+
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "TemperatureFault",
+                                 !(smartWarning & temperatureFaultMask));
+
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "DegradesFault",
+                                 !(smartWarning & DegradesFaultMask));
+
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "MediaFault",
+                                 !(smartWarning & MediaFaultMask));
+
+    util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
+                                 NVME_STATUS_IFACE, "BackupDeviceFault",
+                                 !(smartWarning & BackupDeviceFaultMask));
+}
+
+void Nvme::setFaultLED(const std::string& locateLedGroupPath,
+                       const std::string& faultLedGroupPath, bool request)
+{
+    if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
+    {
+        return;
+    }
+
+    // Before toggle LED, check whether is Identify or not.
+    if (!getLEDGroupState(locateLedGroupPath))
+    {
+        util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME, faultLedGroupPath,
+                                     LED_GROUP_IFACE, "Asserted", request);
+    }
+}
+
+void Nvme::setLocateLED(const std::string& locateLedGroupPath,
+                        const std::string& locateLedBusName,
+                        const std::string& locateLedPath, bool isPresent)
+{
+    if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
+        locateLedPath.empty())
+    {
+        return;
+    }
+
+    namespace server = sdbusplus::xyz::openbmc_project::Led::server;
+
+    if (!getLEDGroupState(locateLedGroupPath))
+    {
+        if (isPresent)
+            util::SDBusPlus::setProperty(
+                bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
+                "State",
+                server::convertForMessage(server::Physical::Action::On));
+        else
+            util::SDBusPlus::setProperty(
+                bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
+                "State",
+                server::convertForMessage(server::Physical::Action::Off));
+    }
+}
+
+bool Nvme::getLEDGroupState(const std::string& ledPath)
+{
+    auto asserted = util::SDBusPlus::getProperty<bool>(
+        bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
+
+    return asserted;
+}
+
+void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
+                         bool success,
+                         const phosphor::nvme::Nvme::NVMeData& nvmeData)
+{
+    static std::unordered_map<std::string, bool> isError;
+
+    if (success)
+    {
+        if (!nvmeData.smartWarnings.empty())
+        {
+            auto request =
+                (strcmp(nvmeData.smartWarnings.c_str(), NOWARNING_STRING) == 0)
+                    ? false
+                    : true;
+
+            setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
+                        request);
+            setLocateLED(config.locateLedGroupPath,
+                         config.locateLedControllerBusName,
+                         config.locateLedControllerPath, !request);
+        }
+        isError[config.index] = false;
+    }
+    else
+    {
+        if (isError[config.index] != true)
+        {
+            // Drive is present but can not get data, turn on fault LED.
+            log<level::ERR>("Drive status is good but can not get data.",
+                            entry("objPath = %s", config.index.c_str()));
+            isError[config.index] = true;
+        }
+
+        setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath, true);
+        setLocateLED(config.locateLedGroupPath,
+                     config.locateLedControllerBusName,
+                     config.locateLedControllerPath, false);
+    }
+}
+
 std::string intToHex(int input)
 {
     std::stringstream tmp;
@@ -141,6 +288,8 @@
 
 void Nvme::run()
 {
+    init();
+
     std::function<void()> callback(std::bind(&Nvme::read, this));
     try
     {
@@ -215,13 +364,26 @@
             {
                 uint8_t index = instance.value("NVMeDriveIndex", 0);
                 uint8_t busID = instance.value("NVMeDriveBusID", 0);
+                std::string faultLedGroupPath =
+                    instance.value("NVMeDriveFaultLEDGroupPath", "");
+                std::string locateLedGroupPath =
+                    instance.value("NVMeDriveLocateLEDGroupPath", "");
                 uint8_t presentPin = instance.value("NVMeDrivePresentPin", 0);
                 uint8_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
+                std::string locateLedControllerBusName =
+                    instance.value("NVMeDriveLocateLEDControllerBusName", "");
+                std::string locateLedControllerPath =
+                    instance.value("NVMeDriveLocateLEDControllerPath", "");
 
                 nvmeConfig.index = std::to_string(index);
                 nvmeConfig.busID = busID;
+                nvmeConfig.faultLedGroupPath = faultLedGroupPath;
                 nvmeConfig.presentPin = presentPin;
                 nvmeConfig.pwrGoodPin = pwrGoodPin;
+                nvmeConfig.locateLedControllerBusName =
+                    locateLedControllerBusName;
+                nvmeConfig.locateLedControllerPath = locateLedControllerPath;
+                nvmeConfig.locateLedGroupPath = locateLedGroupPath;
                 nvmeConfig.criticalHigh = criticalHigh;
                 nvmeConfig.criticalLow = criticalLow;
                 nvmeConfig.warningHigh = warningHigh;
@@ -275,11 +437,39 @@
     return val;
 }
 
+void Nvme::createNVMeInventory()
+{
+    using Properties =
+        std::map<std::string, sdbusplus::message::variant<std::string, bool>>;
+    using Interfaces = std::map<std::string, Properties>;
+
+    std::string inventoryPath;
+    std::map<sdbusplus::message::object_path, Interfaces> obj;
+
+    for (const auto config : configs)
+    {
+        inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
+
+        obj = {{
+            inventoryPath,
+            {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
+        }};
+        util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
+                                    INVENTORY_MANAGER_IFACE, "Notify", obj);
+    }
+}
+
+void Nvme::init()
+{
+    createNVMeInventory();
+}
+
 /** @brief Monitor NVMe drives every one second  */
 void Nvme::read()
 {
     std::string devPresentPath;
     std::string devPwrGoodPath;
+    std::string inventoryPath;
 
     static std::unordered_map<std::string, bool> isErrorPower;
 
@@ -292,6 +482,8 @@
         devPwrGoodPath =
             GPIO_BASE_PATH + std::to_string(config.pwrGoodPin) + "/value";
 
+        inventoryPath = NVME_INVENTORY_PATH + config.index;
+
         auto iter = nvmes.find(config.index);
 
         if (getGPIOValueOfNvme(devPresentPath) == IS_PRESENT)
@@ -301,7 +493,7 @@
             if (getGPIOValueOfNvme(devPwrGoodPath) == POWERGD)
             {
                 // get NVMe information through i2c by busID.
-                getNVMeInfobyBusID(config.busID, nvmeData);
+                auto success = getNVMeInfobyBusID(config.busID, nvmeData);
                 // can not find. create dbus
                 if (iter == nvmes.end())
                 {
@@ -313,6 +505,7 @@
                         bus, objPath.c_str());
                     nvmes.emplace(config.index, nvmeSSD);
 
+                    setNvmeInventoryProperties(true, nvmeData, inventoryPath);
                     nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
                     nvmeSSD->setSensorThreshold(
                         config.criticalHigh, config.criticalLow,
@@ -320,19 +513,32 @@
                         config.warningLow);
 
                     nvmeSSD->checkSensorThreshold();
+                    setLEDsStatus(config, success, nvmeData);
                 }
                 else
                 {
+                    setNvmeInventoryProperties(true, nvmeData, inventoryPath);
                     iter->second->setSensorValueToDbus(nvmeData.sensorValue);
                     iter->second->checkSensorThreshold();
+                    setLEDsStatus(config, success, nvmeData);
                 }
+
                 isErrorPower[config.index] = false;
             }
             else
             {
                 // Present pin is true but power good pin is false
-                // remove nvme d-bus path
+                // remove nvme d-bus path, clean all properties in inventory
+                // and turn on fault LED
+
+                setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
+                            true);
+                setLocateLED(config.locateLedGroupPath,
+                             config.locateLedControllerBusName,
+                             config.locateLedControllerPath, false);
+
                 nvmeData = NVMeData();
+                setNvmeInventoryProperties(false, nvmeData, inventoryPath);
                 nvmes.erase(config.index);
 
                 if (isErrorPower[config.index] != true)
@@ -349,8 +555,18 @@
         }
         else
         {
-            // Drive not present, remove nvme d-bus path
+            // Drive not present, remove nvme d-bus path ,
+            // clean all properties in inventory
+            // and turn off fault and locate LED
+
+            setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
+                        false);
+            setLocateLED(config.locateLedGroupPath,
+                         config.locateLedControllerBusName,
+                         config.locateLedControllerPath, false);
+
             nvmeData = NVMeData();
+            setNvmeInventoryProperties(false, nvmeData, inventoryPath);
             nvmes.erase(config.index);
         }
     }
diff --git a/nvme_manager.hpp b/nvme_manager.hpp
index 2b0e27d..85389e4 100644
--- a/nvme_manager.hpp
+++ b/nvme_manager.hpp
@@ -3,6 +3,7 @@
 #include "config.h"
 
 #include "nvmes.hpp"
+#include "sdbusplus.hpp"
 
 #include <fstream>
 #include <sdbusplus/bus.hpp>
@@ -32,7 +33,7 @@
     /** @brief Constructs Nvme
      *
      * @param[in] bus     - Handle to system dbus
-     * @param[in] objPath - The Dbus path of nvme
+     * @param[in] objPath - The dbus path of nvme
      */
     Nvme(sdbusplus::bus::bus& bus) :
         bus(bus), _event(sdeventplus::Event::get_default()),
@@ -49,8 +50,12 @@
     {
         std::string index;
         uint8_t busID;
+        std::string faultLedGroupPath;
         uint8_t presentPin;
         uint8_t pwrGoodPin;
+        std::string locateLedControllerBusName;
+        std::string locateLedControllerPath;
+        std::string locateLedGroupPath;
         int8_t criticalHigh;
         int8_t criticalLow;
         int8_t maxValue;
@@ -87,6 +92,33 @@
     std::unordered_map<std::string, std::shared_ptr<phosphor::nvme::NvmeSSD>>
         nvmes;
 
+    /** @brief Set locate and fault LED status of SSD
+     *
+     * @param[in] config - Nvme configure data
+     * @param[in] success - Success or not that get NVMe Info by SMbus
+     * @param[in] nvmeData - Nvme information
+     */
+    void setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
+                       bool success,
+                       const phosphor::nvme::Nvme::NVMeData& nvmeData);
+
+    /** @brief Set SSD fault LED status */
+    void setFaultLED(const std::string& locateLedGroupPath,
+                     const std::string& ledPath, bool request);
+    /** @brief Set SSD locate LED status */
+    void setLocateLED(const std::string& ledPath,
+                      const std::string& locateLedBusName,
+                      const std::string& locateLedPath, bool ispresent);
+    /** @brief Get Identify State*/
+    bool getLEDGroupState(const std::string& ledPath);
+
+    /** @brief Set inventory properties of nvme */
+    void setNvmeInventoryProperties(
+        bool present, const phosphor::nvme::Nvme::NVMeData& nvmeData,
+        const std::string& inventoryPath);
+
+    void createNVMeInventory();
+
   private:
     /** @brief sdbusplus bus client connection. */
     sdbusplus::bus::bus& bus;
diff --git a/sdbusplus.hpp b/sdbusplus.hpp
new file mode 100644
index 0000000..9e26b3b
--- /dev/null
+++ b/sdbusplus.hpp
@@ -0,0 +1,104 @@
+#include <iostream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace nvme
+{
+namespace util
+{
+
+using namespace phosphor::logging;
+
+class SDBusPlus
+{
+  public:
+    template <typename T>
+    static auto
+        setProperty(sdbusplus::bus::bus& bus, const std::string& busName,
+                    const std::string& objPath, const std::string& interface,
+                    const std::string& property, const T& value)
+    {
+        sdbusplus::message::variant<T> data = value;
+
+        try
+        {
+            auto methodCall = bus.new_method_call(
+                busName.c_str(), objPath.c_str(), DBUS_PROPERTY_IFACE, "Set");
+
+            methodCall.append(interface.c_str());
+            methodCall.append(property);
+            methodCall.append(data);
+
+            auto reply = bus.call(methodCall);
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("Set properties fail.",
+                            entry("ERROR = %s", e.what()),
+                            entry("Object path = %s", objPath.c_str()));
+            return;
+        }
+    }
+
+    template <typename Property>
+    static auto
+        getProperty(sdbusplus::bus::bus& bus, const std::string& busName,
+                    const std::string& objPath, const std::string& interface,
+                    const std::string& property)
+    {
+        auto methodCall = bus.new_method_call(busName.c_str(), objPath.c_str(),
+                                              DBUS_PROPERTY_IFACE, "Get");
+
+        methodCall.append(interface.c_str());
+        methodCall.append(property);
+
+        sdbusplus::message::variant<Property> value;
+
+        try
+        {
+            auto reply = bus.call(methodCall);
+            reply.read(value);
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("Get properties fail.",
+                            entry("ERROR = %s", e.what()),
+                            entry("Object path = %s", objPath.c_str()));
+            return false;
+        }
+
+        return sdbusplus::message::variant_ns::get<Property>(value);
+    }
+
+    template <typename... Args>
+    static auto CallMethod(sdbusplus::bus::bus& bus, const std::string& busName,
+                           const std::string& objPath,
+                           const std::string& interface,
+                           const std::string& method, Args&&... args)
+    {
+        auto reqMsg = bus.new_method_call(busName.c_str(), objPath.c_str(),
+                                          interface.c_str(), method.c_str());
+        reqMsg.append(std::forward<Args>(args)...);
+        try
+        {
+            auto respMsg = bus.call(reqMsg);
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("Call method fail.", entry("ERROR = %s", e.what()),
+                            entry("Object path = %s", objPath.c_str()));
+            return;
+        }
+    }
+};
+
+} // namespace util
+} // namespace nvme
+} // namespace phosphor