Move to subproject to enable both projects

Yocto builds need each component to build individually, but meson wants
them to build as a single project. This follows google-misc and moves
to subprojects.

It also enables callback-manager in the build and fixes some build
errors and warnings.

Change-Id: Ie56141bf86b6d9c6b27eb697944fbc392e374c22
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/subprojects/hsbp-manager/.gitignore b/subprojects/hsbp-manager/.gitignore
new file mode 100644
index 0000000..a007fea
--- /dev/null
+++ b/subprojects/hsbp-manager/.gitignore
@@ -0,0 +1 @@
+build/*
diff --git a/subprojects/hsbp-manager/LICENCE b/subprojects/hsbp-manager/LICENCE
new file mode 100644
index 0000000..729f4d4
--- /dev/null
+++ b/subprojects/hsbp-manager/LICENCE
@@ -0,0 +1,13 @@
+Copyright 2019 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/subprojects/hsbp-manager/include/utils.hpp b/subprojects/hsbp-manager/include/utils.hpp
new file mode 100644
index 0000000..304ea5b
--- /dev/null
+++ b/subprojects/hsbp-manager/include/utils.hpp
@@ -0,0 +1,219 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <systemd/sd-journal.h>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <cstdint>
+#include <iostream>
+#include <string>
+#include <variant>
+#include <vector>
+
+using GetSubTreeType = std::vector<
+    std::pair<std::string,
+              std::vector<std::pair<std::string, std::vector<std::string>>>>>;
+using BasicVariantType =
+    std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
+                 double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
+using Association = std::tuple<std::string, std::string, std::string>;
+
+constexpr const char* assetTag =
+    "xyz.openbmc_project.Inventory.Decorator.Asset";
+
+namespace mapper
+{
+constexpr const char* busName = "xyz.openbmc_project.ObjectMapper";
+constexpr const char* path = "/xyz/openbmc_project/object_mapper";
+constexpr const char* interface = "xyz.openbmc_project.ObjectMapper";
+constexpr const char* subtree = "GetSubTree";
+} // namespace mapper
+
+namespace entityManager
+{
+constexpr const char* busName = "xyz.openbmc_project.EntityManager";
+} // namespace entityManager
+
+namespace inventory
+{
+constexpr const char* interface = "xyz.openbmc_project.Inventory.Item";
+} // namespace inventory
+
+namespace ledGroup
+{
+constexpr const char* interface = "xyz.openbmc_project.Led.Group";
+constexpr const char* asserted = "Asserted";
+} // namespace ledGroup
+
+namespace properties
+{
+constexpr const char* interface = "org.freedesktop.DBus.Properties";
+constexpr const char* get = "Get";
+} // namespace properties
+
+namespace power
+{
+const static constexpr char* busname = "xyz.openbmc_project.State.Host";
+const static constexpr char* interface = "xyz.openbmc_project.State.Host";
+const static constexpr char* path = "/xyz/openbmc_project/state/host0";
+const static constexpr char* property = "CurrentHostState";
+} // namespace power
+
+namespace association
+{
+const static constexpr char* interface =
+    "xyz.openbmc_project.Association.Definitions";
+} // namespace association
+
+namespace hsbp
+{
+enum class registers : uint8_t
+{
+    fpgaIdH = 0x0,
+    fpgaIdL = 0x1,
+    typeId = 0x2,
+    bootVer = 0x3,
+    fpgaVer = 0x4,
+    securityRev = 0x5,
+    funSupported = 0x6,
+    numDisks = 0x7,
+    presence = 0x8,
+    ssdIFDET = 0x9,
+    ifdetPart = 0xA,
+    statusLocate = 0xB,
+    statusFail = 0xC,
+    statusRebuild = 0xD,
+    ledOverride = 0xE,
+    ledStatus = 0xF,
+    ledPattern0 = 0x10,
+    ledPattern1 = 0x11,
+    ledPattern2 = 0x12,
+    ledPattern3 = 0x13,
+    ledPattern4 = 0x14,
+    ledPattern5 = 0x15,
+    ledPattern6 = 0x16,
+    ledPattern7 = 0x17,
+};
+
+} // namespace hsbp
+
+static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
+static bool powerStatusOn = false;
+
+bool isPowerOn(void)
+{
+    if (!powerMatch)
+    {
+        throw std::runtime_error("Power Match Not Created");
+    }
+    return powerStatusOn;
+}
+
+void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
+{
+    static boost::asio::steady_timer timer(conn->get_io_context());
+    // create a match for powergood changes, first time do a method call to
+    // cache the correct value
+    if (powerMatch)
+    {
+        return;
+    }
+
+    powerMatch = std::make_unique<sdbusplus::bus::match_t>(
+        static_cast<sdbusplus::bus_t&>(*conn),
+        "type='signal',interface='" + std::string(properties::interface) +
+            "',path='" + std::string(power::path) + "',arg0='" +
+            std::string(power::interface) + "'",
+        [](sdbusplus::message_t& message) {
+            std::string objectName;
+            boost::container::flat_map<std::string, std::variant<std::string>>
+                values;
+            message.read(objectName, values);
+            auto findState = values.find(power::property);
+            if (findState != values.end())
+            {
+                bool on = boost::ends_with(
+                    std::get<std::string>(findState->second), "Running");
+                if (!on)
+                {
+                    timer.cancel();
+                    powerStatusOn = false;
+                    return;
+                }
+                // on comes too quickly
+                timer.expires_after(std::chrono::seconds(10));
+                timer.async_wait([](boost::system::error_code ec) {
+                    if (ec == boost::asio::error::operation_aborted)
+                    {
+                        return;
+                    }
+                    else if (ec)
+                    {
+                        std::cerr << "Timer error " << ec.message() << "\n";
+                        return;
+                    }
+                    powerStatusOn = true;
+                });
+            }
+        });
+
+    conn->async_method_call(
+        [](boost::system::error_code ec,
+           const std::variant<std::string>& state) {
+            if (ec)
+            {
+                // we commonly come up before power control, we'll capture the
+                // property change later
+                return;
+            }
+            powerStatusOn =
+                boost::ends_with(std::get<std::string>(state), "Running");
+        },
+        power::busname, power::path, properties::interface, properties::get,
+        power::interface, power::property);
+}
+
+inline void logDeviceAdded(const std::string& model, const std::string& type,
+                           const std::string& sn)
+{
+    sd_journal_send("MESSAGE=%s", "Inventory Added", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.InventoryAdded",
+                    "REDFISH_MESSAGE_ARGS=%s,%s,%s", model.c_str(),
+                    type.c_str(), sn.c_str(), NULL);
+}
+
+inline void logDeviceRemoved(const std::string& model, const std::string& type,
+                             const std::string& sn)
+{
+    sd_journal_send("MESSAGE=%s", "Inventory Removed", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.InventoryRemoved",
+                    "REDFISH_MESSAGE_ARGS=%s,%s,%s", model.c_str(),
+                    type.c_str(), sn.c_str(), NULL);
+}
+
+inline void logDriveError(const std::string& name)
+{
+    sd_journal_send("MESSAGE=%s", "Drive Error", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.DriveError",
+                    "REDFISH_MESSAGE_ARGS=%s", name.c_str(), NULL);
+}
diff --git a/subprojects/hsbp-manager/meson.build b/subprojects/hsbp-manager/meson.build
new file mode 100644
index 0000000..c757d26
--- /dev/null
+++ b/subprojects/hsbp-manager/meson.build
@@ -0,0 +1,70 @@
+project(
+    'hsbp-manager',
+    'cpp',
+    version: '1.1.1',
+    meson_version: '>=1.1.1',
+    default_options: ['cpp_std=c++23'],
+)
+
+# Compiler flags
+cpp_args = [
+    '-lstdc++fs',
+    '-Werror',
+    '-Wall',
+    '-Wextra',
+    '-Wshadow',
+    '-Wnon-virtual-dtor',
+    '-Wold-style-cast',
+    '-Wcast-align',
+    '-Wunused',
+    '-Woverloaded-virtual',
+    '-Wpedantic',
+    '-Wconversion',
+    '-Wmisleading-indentation',
+    '-Wduplicated-cond',
+    '-Wduplicated-branches',
+    '-Wlogical-op',
+    '-Wnull-dereference',
+    '-Wuseless-cast',
+    '-Wdouble-promotion',
+    '-Wformat=2',
+    '-fno-rtti',
+]
+
+# Definitions
+add_project_arguments(
+    '-DBOOST_ERROR_CODE_HEADER_ONLY',
+    '-DBOOST_SYSTEM_NO_DEPRECATED',
+    '-DBOOST_ALL_NO_LIB',
+    '-DBOOST_NO_RTTI',
+    '-DBOOST_NO_TYPEID',
+    '-DBOOST_ASIO_DISABLE_THREADS',
+    language: 'cpp',
+)
+
+# Include directories
+inc = include_directories('include')
+
+cpp = meson.get_compiler('cpp')
+boost = dependency('boost', version: '1.86.0', required: false)
+sdbusplus = dependency('sdbusplus', required: true)
+i2c_dep = cpp.find_library('i2c')
+gpiodcxx = dependency('libgpiodcxx', default_options: ['bindings=cxx'])
+
+incdir = include_directories('include')
+
+executable(
+    'hsbp-manager',
+    'src/hsbp_manager.cpp',
+    include_directories: incdir,
+    dependencies: [boost, i2c_dep, sdbusplus, gpiodcxx],
+)
+# Systemd service files
+systemd_system_unit_dir = dependency('systemd').get_variable(
+    'systemdsystemunitdir',
+)
+
+install_data(
+    'service_files/hsbp-manager.service',
+    install_dir: systemd_system_unit_dir,
+)
diff --git a/subprojects/hsbp-manager/service_files/hsbp-manager.service b/subprojects/hsbp-manager/service_files/hsbp-manager.service
new file mode 100644
index 0000000..b9637d0
--- /dev/null
+++ b/subprojects/hsbp-manager/service_files/hsbp-manager.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=HSBP Manager
+
+[Service]
+Type=dbus
+BusName=xyz.openbmc_project.HsbpManager
+Restart=always
+RestartSec=5
+ExecStart=/usr/bin/hsbp-manager
+
+[Install]
+WantedBy=multi-user.target
diff --git a/subprojects/hsbp-manager/src/hsbp_manager.cpp b/subprojects/hsbp-manager/src/hsbp_manager.cpp
new file mode 100644
index 0000000..6e22d2d
--- /dev/null
+++ b/subprojects/hsbp-manager/src/hsbp_manager.cpp
@@ -0,0 +1,2914 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "utils.hpp"
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_set.hpp>
+#include <gpiod.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <algorithm>
+#include <bitset>
+#include <filesystem>
+#include <forward_list>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <string>
+#include <utility>
+
+extern "C"
+{
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+}
+
+/****************************************************************************/
+/******************** Global Constants/Type Declarations ********************/
+/****************************************************************************/
+constexpr const char* hsbpCpldInft =
+    "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
+constexpr const char* hsbpConfigIntf =
+    "xyz.openbmc_project.Configuration.HSBPConfiguration";
+constexpr const char* nvmeIntf = "xyz.openbmc_project.Inventory.Item.NVMe";
+constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
+
+constexpr size_t scanRateSeconds = 5;
+constexpr size_t maxDrives = 8; // only 1 byte alloted
+
+using NvmeMapping = std::vector<std::string>;
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/**************************** Enums Definitions *****************************/
+/****************************************************************************/
+enum class AppState : uint8_t
+{
+    idle,
+    loadingHsbpConfig,
+    hsbpConfigLoaded,
+    loadingComponents,
+    componentsLoaded,
+    loadingBackplanes,
+    backplanesLoaded,
+    loadingDrives,
+    drivesLoaded
+};
+
+enum class BlinkPattern : uint8_t
+{
+    off = 0x0,
+    error = 0x2,
+    terminate = 0x3
+};
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/************ HSBP Configuration related struct/class Definitions ***********/
+/****************************************************************************/
+struct HsbpConfig
+{
+    size_t rootBus;
+    std::vector<std::string> supportedHsbps;
+    std::unordered_map<std::string, NvmeMapping> hsbpNvmeMap;
+    std::vector<std::string> clockBufferTypes;
+    std::vector<std::string> ioExpanderTypes;
+
+    void clearConfig()
+    {
+        rootBus = -1;
+        supportedHsbps.clear();
+        hsbpNvmeMap.clear();
+        clockBufferTypes.clear();
+        ioExpanderTypes.clear();
+    }
+};
+
+class ClockBuffer
+{
+    size_t bus;
+    size_t address;
+    std::string modeOfOperation;
+    size_t outCtrlBaseAddr;
+    size_t outCtrlByteCount;
+    std::unordered_map<std::string, std::vector<std::string>> byteMap;
+    std::string name;
+    std::string type;
+
+    int file = -1;
+    bool initialized = false;
+
+    void initialize()
+    {
+        /* Execute below operation only when mode of operation is SMBus. By
+         * default the clock buffer is configured to follow OE pin output, so we
+         * need to set the output value to 0 to disable the clock outputs and 1
+         * to enable clock output. If mode of operation is IO, then the IO value
+         * will determine the disable/enable of clock output */
+        if (modeOfOperation == "SMBus")
+        {
+            if (file < 0)
+            {
+                file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
+                            O_RDWR | O_CLOEXEC);
+                if (file < 0)
+                {
+                    std::cerr << "ClockBuffer : \"" << name
+                              << "\" - Unable to open bus : " << bus << "\n";
+                    return;
+                }
+            }
+
+            if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
+            {
+                std::cerr << "ClockBuffer : \"" << name
+                          << "\" - Unable to set address to " << address
+                          << "\n";
+                return;
+            }
+
+            for (uint8_t i = 0; i < outCtrlByteCount; i++)
+            {
+                std::string byteName = "Byte" + std::to_string(i);
+
+                auto byte = byteMap.find(byteName);
+                if (byte == byteMap.end())
+                {
+                    std::cerr << "ClockBuffer : \"" << name
+                              << "\" - Byte map error ! Unable to find "
+                              << byteName << "\n";
+                    return;
+                }
+
+                /* Get current value of output control register */
+                int read = i2c_smbus_read_byte_data(
+                    file, static_cast<uint8_t>(outCtrlBaseAddr + i));
+                if (read < 0)
+                {
+                    std::cerr << "ClockBuffer : \"" << name
+                              << "\" - Error: Unable to read data from clock "
+                                 "buffer register\n";
+                    return;
+                }
+
+                std::bitset<8> currByte(read);
+                bool writeRequired = false;
+
+                /* Set 0/1 only at bit position that we have a NVMe drive (i.e.
+                 * ignore where byteMap is "-"). We do not want to touch other
+                 * bits */
+                for (uint8_t bit = 0; bit < 8; bit++)
+                {
+                    if (byte->second.at(bit) != "-")
+                    {
+                        writeRequired = true;
+                        /* Default to enabling the clock output and once the
+                         * HSBP's are detected the clocks will be
+                         * enabled/disabled depending on the drive status */
+                        /* TODO: This code might require a re-visit in case of
+                         * any signal integrity issues in the future */
+                        currByte.set(bit);
+                    }
+                }
+
+                if (writeRequired)
+                {
+                    int ret = i2c_smbus_write_byte_data(
+                        file, static_cast<uint8_t>(outCtrlBaseAddr + i),
+                        static_cast<uint8_t>(currByte.to_ulong()));
+
+                    if (ret < 0)
+                    {
+                        std::cerr
+                            << "ClockBuffer : \"" << name
+                            << "\" - Error: Unable to write data to clock "
+                               "buffer register\n";
+                        return;
+                    }
+                }
+            }
+        }
+        initialized = true;
+        std::cerr << "ClockBuffer : \"" << name << "\" initialized\n";
+    }
+
+  public:
+    ClockBuffer(
+        size_t busIn, size_t addressIn, std::string& modeOfOperationIn,
+        size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
+        std::unordered_map<std::string, std::vector<std::string>>& byteMapIn,
+        std::string& nameIn, std::string& typeIn) :
+        bus(busIn), address(addressIn),
+        modeOfOperation(std::move(modeOfOperationIn)),
+        outCtrlBaseAddr(outCtrlBaseAddrIn),
+        outCtrlByteCount(outCtrlByteCountIn), byteMap(std::move(byteMapIn)),
+        name(std::move(nameIn)), type(std::move(typeIn))
+    {
+        initialize();
+    }
+
+    bool isInitialized()
+    {
+        if (!initialized)
+        {
+            /* There was an issue with the initialization of this component. Try
+             * to invoke initialization again */
+            initialize();
+        }
+        return initialized;
+    }
+
+    std::string getName()
+    {
+        return name;
+    }
+
+    bool enableDisableClock(std::forward_list<std::string>& nvmeDrivesInserted,
+                            std::forward_list<std::string>& nvmeDrivesRemoved)
+    {
+        if (modeOfOperation != "SMBus")
+        {
+            /* The clock is enabled using IO expander. No action needed from
+             * here */
+            return true;
+        }
+
+        if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
+        {
+            /* There are no drives to update */
+            return true;
+        }
+
+        for (uint8_t i = 0; i < outCtrlByteCount; i++)
+        {
+            std::string byteName = "Byte" + std::to_string(i);
+
+            auto byte = byteMap.find(byteName);
+            if (byte == byteMap.end())
+            {
+                std::cerr << "ClockBuffer : \"" << name
+                          << "\" - Byte map error ! Unable to find " << byteName
+                          << "\n";
+                return false;
+            }
+
+            /* Get current value of output control register */
+            int read = i2c_smbus_read_byte_data(
+                file, static_cast<uint8_t>(outCtrlBaseAddr + i));
+            if (read < 0)
+            {
+                std::cerr << "ClockBuffer : \"" << name
+                          << "\" - Error: Unable to read data from clock "
+                             "buffer register\n";
+                return false;
+            }
+
+            std::bitset<8> currByte(read);
+            bool writeRequired = false;
+
+            /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
+             * reset the bit if found in nvmeDrivesRemoved */
+            for (uint8_t bit = 0; bit < 8; bit++)
+            {
+                /* The remove function returns number of elements removed from
+                 * list indicating the presence of the drive and also removing
+                 * it form the list */
+                if (nvmeDrivesInserted.remove(byte->second.at(bit)))
+                {
+                    writeRequired = true;
+                    currByte.set(bit);
+                    continue;
+                }
+
+                if (nvmeDrivesRemoved.remove(byte->second.at(bit)))
+                {
+                    writeRequired = true;
+                    currByte.reset(bit);
+                }
+            }
+
+            if (!writeRequired)
+            {
+                /* No Write is required as there are no changes */
+                continue;
+            }
+
+            int ret = i2c_smbus_write_byte_data(
+                file, static_cast<uint8_t>(outCtrlBaseAddr + i),
+                static_cast<uint8_t>(currByte.to_ulong()));
+            if (ret < 0)
+            {
+                std::cerr << "ClockBuffer : \"" << name
+                          << "\" - Error: Unable to write data to clock "
+                             "buffer register\n";
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    ~ClockBuffer()
+    {
+        if (file >= 0)
+        {
+            close(file);
+        }
+    }
+};
+
+class IoExpander
+{
+    size_t bus;
+    size_t address;
+    size_t confIORegAddr;
+    size_t outCtrlBaseAddr;
+    size_t outCtrlByteCount;
+    std::unordered_map<std::string, std::vector<std::string>> ioMap;
+    std::string name;
+    std::string type;
+
+    int file = -1;
+    bool initialized = false;
+
+    void initialize()
+    {
+        /* Initialize the IO expander Control register to configure the IO ports
+         * as outputs and set the output*/
+        if (file < 0)
+        {
+            file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
+                        O_RDWR | O_CLOEXEC);
+            if (file < 0)
+            {
+                std::cerr << "IoExpander : " << name
+                          << " - Unable to open bus : " << bus << "\n";
+                return;
+            }
+        }
+
+        if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
+        {
+            std::cerr << "IoExpander : \"" << name
+                      << "\" - Unable to set address to " << address << "\n";
+            return;
+        }
+
+        for (uint8_t i = 0; i < outCtrlByteCount; i++)
+        {
+            std::string ioName = "IO" + std::to_string(i);
+
+            auto io = ioMap.find(ioName);
+            if (io == ioMap.end())
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - IO map error ! Unable to find " << ioName
+                          << "\n";
+                return;
+            }
+
+            /* Get current value of IO configuration register */
+            int read1 = i2c_smbus_read_byte_data(
+                file, static_cast<uint8_t>(confIORegAddr + i));
+            if (read1 < 0)
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - Error: Unable to read data from io expander "
+                             "IO control register\n";
+                return;
+            }
+
+            /* Get current value of IO Ouput register */
+            int read2 = i2c_smbus_read_byte_data(
+                file, static_cast<uint8_t>(confIORegAddr + i));
+            if (read2 < 0)
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - Error: Unable to read data from io expander "
+                             "IO output register\n";
+                return;
+            }
+
+            bool writeRequired = false;
+            std::bitset<8> currCtrlVal(read1);
+            std::bitset<8> currOutVal(read2);
+
+            /* Set 0/1 only at bit position that we have a NVMe drive (i.e.
+             * ignore where ioMap is "-"). We do not want to touch other
+             * bits */
+            for (uint8_t bit = 0; bit < 8; bit++)
+            {
+                if (io->second.at(bit) != "-")
+                {
+                    writeRequired = true;
+                    currCtrlVal.reset(bit);
+                    /* Set the output register to drive the OE pin high thereby
+                     * enabling the clock. Default to enabling the clock output
+                     * and once the HSBP's are detected the clocks will be
+                     * enabled/disabled depending on the drive status */
+                    currOutVal.set(bit);
+                }
+            }
+
+            if (writeRequired)
+            {
+                int ret1 = i2c_smbus_write_byte_data(
+                    file, static_cast<uint8_t>(confIORegAddr + i),
+                    static_cast<uint8_t>(currCtrlVal.to_ulong()));
+                if (ret1 < 0)
+                {
+                    std::cerr
+                        << "IoExpander : \"" << name
+                        << "\" - Error: Unable to write data to IO expander "
+                           "IO control register\n";
+                    return;
+                }
+
+                int ret2 = i2c_smbus_write_byte_data(
+                    file, static_cast<uint8_t>(outCtrlBaseAddr + i),
+                    static_cast<uint8_t>(currOutVal.to_ulong()));
+                if (ret2 < 0)
+                {
+                    std::cerr
+                        << "IoExpander : \"" << name
+                        << "\" - Error: Unable to write data to IO expander "
+                           "IO output register\n";
+                    return;
+                }
+            }
+        }
+        initialized = true;
+        std::cerr << "IoExpander : \"" << name << "\" initialized\n";
+    }
+
+  public:
+    IoExpander(
+        size_t busIn, size_t addressIn, size_t confIORegAddrIn,
+        size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
+        std::unordered_map<std::string, std::vector<std::string>>& ioMapIn,
+        std::string& nameIn, std::string& typeIn) :
+        bus(busIn), address(addressIn), confIORegAddr(confIORegAddrIn),
+        outCtrlBaseAddr(outCtrlBaseAddrIn),
+        outCtrlByteCount(outCtrlByteCountIn), ioMap(std::move(ioMapIn)),
+        name(std::move(nameIn)), type(std::move(typeIn))
+    {
+        initialize();
+    }
+
+    bool isInitialized()
+    {
+        if (!initialized)
+        {
+            /* There was an issue with the initialization of this component. Try
+             * to invoke initialization again */
+            initialize();
+        }
+        return initialized;
+    }
+
+    std::string getName()
+    {
+        return name;
+    }
+
+    bool enableDisableOuput(std::forward_list<std::string>& nvmeDrivesInserted,
+                            std::forward_list<std::string>& nvmeDrivesRemoved)
+    {
+        if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
+        {
+            /* There are no drives to update */
+            return true;
+        }
+
+        for (uint8_t i = 0; i < outCtrlByteCount; i++)
+        {
+            std::string ioName = "IO" + std::to_string(i);
+
+            auto io = ioMap.find(ioName);
+            if (io == ioMap.end())
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - IO map error ! Unable to find " << ioName
+                          << "\n";
+                return false;
+            }
+
+            /* Get current value of IO output register */
+            int read = i2c_smbus_read_byte_data(
+                file, static_cast<uint8_t>(outCtrlBaseAddr + i));
+            if (read < 0)
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - Error: Unable to read data from io expander "
+                             "register\n";
+                return false;
+            }
+
+            std::bitset<8> currVal(read);
+            bool writeRequired = false;
+
+            /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
+             * reset the bit if found in nvmeDrivesRemoved */
+            for (uint8_t bit = 0; bit < 8; bit++)
+            {
+                /* The remove function returns number of elements removed from
+                 * list indicating the presence of the drive and also removing
+                 * it form the list */
+                if (nvmeDrivesInserted.remove(io->second.at(bit)))
+                {
+                    writeRequired = true;
+                    currVal.set(bit);
+                    continue;
+                }
+
+                if (nvmeDrivesRemoved.remove(io->second.at(bit)))
+                {
+                    writeRequired = true;
+                    currVal.reset(bit);
+                }
+            }
+
+            if (!writeRequired)
+            {
+                /* No Write is required as there are no changes */
+                continue;
+            }
+
+            int ret = i2c_smbus_write_byte_data(
+                file, static_cast<uint8_t>(outCtrlBaseAddr + i),
+                static_cast<uint8_t>(currVal.to_ulong()));
+            if (ret < 0)
+            {
+                std::cerr << "IoExpander : \"" << name
+                          << "\" - Error: Unable to write data to IO expander "
+                             "register\n";
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    ~IoExpander()
+    {
+        if (file >= 0)
+        {
+            close(file);
+        }
+    }
+};
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/*********************** Global Variables Declarations **********************/
+/****************************************************************************/
+/* State os Application */
+static AppState appState = AppState::idle;
+
+/* Configuration and Components */
+static HsbpConfig hsbpConfig;
+std::forward_list<ClockBuffer> clockBuffers;
+std::forward_list<IoExpander> ioExpanders;
+
+/* Boost IO context and Dbus variables */
+boost::asio::io_context io;
+auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+sdbusplus::asio::object_server objServer(conn);
+
+/* GPIO Lines and GPIO Event Descriptors */
+static gpiod::line nvmeLvc3AlertLine;
+static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/********** HSBP Backplane related struct and Global definitions ************/
+/****************************************************************************/
+struct Mux
+{
+    Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
+        bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
+    {}
+    size_t bus;
+    size_t address;
+    size_t channels;
+    size_t index;
+
+    // to sort in the flat set
+    bool operator<(const Mux& rhs) const
+    {
+        return index < rhs.index;
+    }
+};
+
+struct Led : std::enable_shared_from_this<Led>
+{
+    // led pattern addresses start at 0x10
+    Led(const std::string& path, size_t index, int fd) :
+        address(static_cast<uint8_t>(index + 0x10)), file(fd),
+        ledInterface(objServer.add_interface(path, ledGroup::interface))
+    {
+        if (index >= maxDrives)
+        {
+            throw std::runtime_error("Invalid drive index");
+        }
+
+        if (!set(BlinkPattern::off))
+        {
+            std::cerr << "Cannot initialize LED " << path << "\n";
+        }
+    }
+
+    // this has to be called outside the constructor for shared_from_this to
+    // work
+    void createInterface(void)
+    {
+        ledInterface->register_property(
+            ledGroup::asserted, false,
+            [weakRef{weak_from_this()}](const bool req, bool& val) {
+                auto self = weakRef.lock();
+                if (!self)
+                {
+                    return 0;
+                }
+                if (req == val)
+                {
+                    return 1;
+                }
+
+                if (!isPowerOn())
+                {
+                    std::cerr << "Can't change blink state when power is off\n";
+                    throw std::runtime_error(
+                        "Can't change blink state when power is off");
+                }
+                BlinkPattern pattern =
+                    req ? BlinkPattern::error : BlinkPattern::terminate;
+                if (!self->set(pattern))
+                {
+                    std::cerr << "Can't change blink pattern\n";
+                    throw std::runtime_error("Cannot set blink pattern");
+                }
+                val = req;
+                return 1;
+            });
+        ledInterface->initialize();
+    }
+
+    virtual ~Led()
+    {
+        objServer.remove_interface(ledInterface);
+    }
+
+    bool set(BlinkPattern pattern)
+    {
+        int ret = i2c_smbus_write_byte_data(file, address,
+                                            static_cast<uint8_t>(pattern));
+        return ret >= 0;
+    }
+
+    uint8_t address;
+    int file;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
+};
+
+struct Drive
+{
+    Drive(std::string driveName, bool present, bool isOperational, bool nvme,
+          bool rebuilding) : isNvme(nvme), isPresent(present), name(driveName)
+    {
+        constexpr const char* basePath =
+            "/xyz/openbmc_project/inventory/item/drive/";
+        itemIface =
+            objServer.add_interface(basePath + driveName, inventory::interface);
+        itemIface->register_property("Present", isPresent);
+        itemIface->register_property("PrettyName", driveName);
+        itemIface->initialize();
+        operationalIface = objServer.add_interface(
+            itemIface->get_object_path(),
+            "xyz.openbmc_project.State.Decorator.OperationalStatus");
+
+        operationalIface->register_property(
+            "Functional", isOperational,
+            [this](const bool req, bool& property) {
+                if (!isPresent)
+                {
+                    return 0;
+                }
+                if (property == req)
+                {
+                    return 1;
+                }
+                property = req;
+                if (req)
+                {
+                    clearFailed();
+                    return 1;
+                }
+                markFailed();
+                return 1;
+            });
+
+        operationalIface->initialize();
+        rebuildingIface = objServer.add_interface(
+            itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
+        rebuildingIface->register_property("Rebuilding", rebuilding);
+        rebuildingIface->initialize();
+        driveIface =
+            objServer.add_interface(itemIface->get_object_path(),
+                                    "xyz.openbmc_project.Inventory.Item.Drive");
+        driveIface->initialize();
+        associations = objServer.add_interface(itemIface->get_object_path(),
+                                               association::interface);
+        associations->register_property("Associations",
+                                        std::vector<Association>{});
+        associations->initialize();
+
+        if (isPresent && (!isOperational || rebuilding))
+        {
+            markFailed();
+        }
+    }
+    virtual ~Drive()
+    {
+        objServer.remove_interface(itemIface);
+        objServer.remove_interface(operationalIface);
+        objServer.remove_interface(rebuildingIface);
+        objServer.remove_interface(assetIface);
+        objServer.remove_interface(driveIface);
+        objServer.remove_interface(associations);
+    }
+
+    void removeAsset()
+    {
+        objServer.remove_interface(assetIface);
+        assetIface = nullptr;
+    }
+
+    void createAsset(
+        const boost::container::flat_map<std::string, std::string>& data)
+    {
+        if (assetIface != nullptr)
+        {
+            return;
+        }
+        assetIface = objServer.add_interface(
+            itemIface->get_object_path(),
+            "xyz.openbmc_project.Inventory.Decorator.Asset");
+        for (const auto& [key, value] : data)
+        {
+            assetIface->register_property(key, value);
+            if (key == "SerialNumber")
+            {
+                serialNumber = value;
+                serialNumberInitialized = true;
+            }
+        }
+        assetIface->initialize();
+    }
+
+    void markFailed(void)
+    {
+        // todo: maybe look this up via mapper
+        constexpr const char* globalInventoryPath =
+            "/xyz/openbmc_project/CallbackManager";
+
+        if (!isPresent)
+        {
+            return;
+        }
+
+        operationalIface->set_property("Functional", false);
+        std::vector<Association> warning = {
+            {"", "warning", globalInventoryPath}};
+        associations->set_property("Associations", warning);
+        logDriveError("Drive " + name);
+    }
+
+    void clearFailed(void)
+    {
+        operationalIface->set_property("Functional", true);
+        associations->set_property("Associations", std::vector<Association>{});
+    }
+
+    void setPresent(bool set)
+    {
+        // nvme drives get detected by their fru
+        if (set == isPresent)
+        {
+            return;
+        }
+        itemIface->set_property("Present", set);
+        isPresent = set;
+    }
+
+    void logPresent()
+    {
+        if (isNvme && !serialNumberInitialized)
+        {
+            // wait until NVMe asset is updated to include the serial number
+            // from the NVMe drive
+            return;
+        }
+
+        if (!isPresent && loggedPresent)
+        {
+            loggedPresent = false;
+            logDeviceRemoved("Drive", name, serialNumber);
+            serialNumber = "N/A";
+            serialNumberInitialized = false;
+            removeAsset();
+        }
+        else if (isPresent && !loggedPresent)
+        {
+            loggedPresent = true;
+            logDeviceAdded("Drive", name, serialNumber);
+        }
+    }
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
+
+    bool isNvme;
+    bool isPresent;
+    std::string name;
+    std::string serialNumber = "N/A";
+    bool serialNumberInitialized = false;
+    bool loggedPresent = false;
+};
+
+struct Backplane : std::enable_shared_from_this<Backplane>
+{
+    Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
+              const std::string& nameIn) :
+        bus(busIn), address(addressIn), backplaneIndex(backplaneIndexIn - 1),
+        name(nameIn), timer(boost::asio::steady_timer(io)),
+        muxes(std::make_shared<boost::container::flat_set<Mux>>())
+    {}
+    void populateAsset(const std::string& rootPath, const std::string& busname)
+    {
+        conn->async_method_call(
+            [assetIface{assetInterface}](
+                const boost::system::error_code ec,
+                const boost::container::flat_map<
+                    std::string, std::variant<std::string>>& values) mutable {
+                if (ec)
+                {
+                    std::cerr
+                        << "Error getting asset tag from HSBP configuration\n";
+
+                    return;
+                }
+                for (const auto& [key, value] : values)
+                {
+                    const std::string* ptr = std::get_if<std::string>(&value);
+                    if (ptr == nullptr)
+                    {
+                        std::cerr << key << " Invalid type!\n";
+                        continue;
+                    }
+                    assetIface->register_property(key, *ptr);
+                }
+                assetIface->initialize();
+            },
+            busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
+            assetTag);
+    }
+
+    static std::string zeroPad(const uint8_t val)
+    {
+        std::ostringstream version;
+        version << std::setw(2) << std::setfill('0')
+                << static_cast<size_t>(val);
+        return version.str();
+    }
+
+    void run(const std::string& rootPath, const std::string& busname)
+    {
+        file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
+                    O_RDWR | O_CLOEXEC);
+        if (file < 0)
+        {
+            std::cerr << "unable to open bus " << bus << "\n";
+            return;
+        }
+
+        if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
+        {
+            std::cerr << "unable to set address to " << address << "\n";
+            return;
+        }
+
+        if (!getPresent())
+        {
+            std::cerr << "Cannot detect CPLD\n";
+            return;
+        }
+
+        getBootVer(bootVer);
+        getFPGAVer(fpgaVer);
+        getSecurityRev(securityRev);
+        std::string dbusName = boost::replace_all_copy(name, " ", "_");
+        hsbpItemIface = objServer.add_interface(
+            "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
+            inventory::interface);
+        hsbpItemIface->register_property("Present", true);
+        hsbpItemIface->register_property("PrettyName", name);
+        hsbpItemIface->initialize();
+
+        storageInterface = objServer.add_interface(
+            hsbpItemIface->get_object_path(),
+            "xyz.openbmc_project.Inventory.Item.StorageController");
+        storageInterface->initialize();
+
+        assetInterface =
+            objServer.add_interface(hsbpItemIface->get_object_path(), assetTag);
+
+        versionIface =
+            objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
+                                    "xyz.openbmc_project.Software.Version");
+        versionIface->register_property(
+            "Version", zeroPad(bootVer) + "." + zeroPad(fpgaVer) + "." +
+                           zeroPad(securityRev));
+        versionIface->register_property(
+            "Purpose",
+            std::string(
+                "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
+        versionIface->initialize();
+
+        activationIface =
+            objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
+                                    "xyz.openbmc_project.Software.Activation");
+        activationIface->register_property(
+            "Activation",
+            std::string(
+                "xyz.openbmc_project.Software.Activation.Activations.Active"));
+        activationIface->register_property(
+            "RequestedActivation",
+            std::string("xyz.openbmc_project.Software.Activation."
+                        "RequestedActivations.None"));
+        activationIface->initialize();
+
+        getPresence(presence);
+        getIFDET(ifdet);
+
+        populateAsset(rootPath, busname);
+
+        createDrives();
+
+        runTimer();
+    }
+
+    void runTimer()
+    {
+        timer.expires_after(std::chrono::seconds(scanRateSeconds));
+        timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
+                             boost::system::error_code ec) {
+            auto self = weak.lock();
+            if (!self)
+            {
+                return;
+            }
+            if (ec == boost::asio::error::operation_aborted)
+            {
+                // we're being destroyed
+                return;
+            }
+            else if (ec)
+            {
+                std::cerr << "timer error " << ec.message() << "\n";
+                return;
+            }
+
+            if (!isPowerOn())
+            {
+                // can't access hsbp when power is off
+                self->runTimer();
+                return;
+            }
+
+            self->getPresence(self->presence);
+            self->getIFDET(self->ifdet);
+            self->getFailed(self->failed);
+            self->getRebuild(self->rebuilding);
+
+            self->updateDrives();
+            self->runTimer();
+        });
+    }
+
+    void createDrives()
+    {
+        for (size_t ii = 0; ii < maxDrives; ii++)
+        {
+            uint8_t driveSlot = (1 << ii);
+            bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
+            bool isPresent = isNvme || (presence & driveSlot);
+            bool isFailed = !isPresent || failed & driveSlot;
+            bool isRebuilding = !isPresent && (rebuilding & driveSlot);
+
+            // +1 to convert from 0 based to 1 based
+            std::string driveName = boost::replace_all_copy(name, " ", "_") +
+                                    "_Drive_" + std::to_string(ii + 1);
+            Drive& drive = drives.emplace_back(driveName, isPresent, !isFailed,
+                                               isNvme, isRebuilding);
+            std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
+                drive.itemIface->get_object_path(), ii, file));
+            led->createInterface();
+        }
+    }
+
+    void updateDrives()
+    {
+        size_t ii = 0;
+
+        for (auto it = drives.begin(); it != drives.end(); it++, ii++)
+        {
+            uint8_t driveSlot = (1 << ii);
+            bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
+            bool isPresent = isNvme || (presence & driveSlot);
+            bool isFailed = !isPresent || (failed & driveSlot);
+            bool isRebuilding = isPresent && (rebuilding & driveSlot);
+
+            it->isNvme = isNvme;
+            it->setPresent(isPresent);
+            it->logPresent();
+
+            it->rebuildingIface->set_property("Rebuilding", isRebuilding);
+            if (isFailed || isRebuilding)
+            {
+                it->markFailed();
+            }
+            else
+            {
+                it->clearFailed();
+            }
+        }
+    }
+
+    bool getPresent()
+    {
+        present = i2c_smbus_read_byte(file) >= 0;
+        return present;
+    }
+
+    bool getTypeID(uint8_t& val)
+    {
+        constexpr uint8_t addr = 2;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getBootVer(uint8_t& val)
+    {
+        constexpr uint8_t addr = 3;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getFPGAVer(uint8_t& val)
+    {
+        constexpr uint8_t addr = 4;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getSecurityRev(uint8_t& val)
+    {
+        constexpr uint8_t addr = 5;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getPresence(uint8_t& val)
+    {
+        // NVMe drives do not assert PRSNTn, and as such do not get reported as
+        // PRESENT in this register
+
+        constexpr uint8_t addr = 8;
+
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        // presence is inverted
+        val = static_cast<uint8_t>(~ret);
+        return true;
+    }
+
+    bool getIFDET(uint8_t& val)
+    {
+        // This register is a bitmap of parallel GPIO pins connected to the
+        // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
+        // IFDETn low when they are inserted into the HSBP.This register, in
+        // combination with the PRESENCE register, are used by the BMC to detect
+        // the presence of NVMe drives.
+
+        constexpr uint8_t addr = 9;
+
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        // ifdet is inverted
+        val = static_cast<uint8_t>(~ret);
+        return true;
+    }
+
+    bool getFailed(uint8_t& val)
+    {
+        constexpr uint8_t addr = 0xC;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getRebuild(uint8_t& val)
+    {
+        constexpr uint8_t addr = 0xD;
+        int ret = i2c_smbus_read_byte_data(file, addr);
+        if (ret < 0)
+        {
+            std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
+                      << "\n";
+            return false;
+        }
+        val = static_cast<uint8_t>(ret);
+        return true;
+    }
+
+    bool getInsertedAndRemovedNvmeDrives(
+        std::forward_list<std::string>& nvmeDrivesInserted,
+        std::forward_list<std::string>& nvmeDrivesRemoved)
+    {
+        /* Get the current drives status */
+        std::bitset<8> currDriveStatus;
+        uint8_t nPresence;
+        uint8_t nIfdet;
+
+        if (!getPresence(nPresence) || !getIFDET(nIfdet))
+        {
+            /* Error getting value. Return */
+            std::cerr << "Backplane " << name
+                      << " failed to get drive status\n";
+            return false;
+        }
+
+        std::string dbusHsbpName = boost::replace_all_copy(name, " ", "_");
+        auto nvmeMap = hsbpConfig.hsbpNvmeMap.find(dbusHsbpName);
+        if (nvmeMap == hsbpConfig.hsbpNvmeMap.end())
+        {
+            std::cerr << "Couldn't get the NVMe Map for the backplane : "
+                      << name << "\n";
+            return false;
+        }
+
+        /* NVMe drives do not assert PRSNTn, and as such do not get reported in
+         * "presence" register, but assert ifdet low. This implies for a NVMe
+         * drive to be present, corresponding precense bit has to be 0 and idfet
+         * has to be 1 (as the values of these regosters are negated: check
+         * getPresence() and getIfdet() functions) */
+        for (uint8_t bit = 0; bit < 8; bit++)
+        {
+            if ((nPresence & (1U << bit)) == 0)
+            {
+                if (nIfdet & (1U << bit))
+                {
+                    currDriveStatus.set(bit);
+                }
+            }
+        }
+
+        /* Determine Inserted and Removed Drives
+         * Prev Bit | Curr Bit | Status
+         *    0     |    0     | No Change
+         *    0     |    1     | Inserted
+         *    1     |    0     | Removed
+         *    1     |    1     | No Change
+         */
+        for (uint8_t index = 0; index < 8; index++)
+        {
+            /* Inserted */
+            if (!prevDriveStatus.test(index) && currDriveStatus.test(index))
+            {
+                nvmeDrivesInserted.emplace_front(nvmeMap->second.at(index));
+                std::cerr << name << " : " << nvmeDrivesInserted.front()
+                          << " Inserted !\n";
+            }
+
+            /* Removed */
+            else if (prevDriveStatus.test(index) &&
+                     !currDriveStatus.test(index))
+            {
+                nvmeDrivesRemoved.emplace_front(nvmeMap->second.at(index));
+                std::cerr << name << " : " << nvmeDrivesRemoved.front()
+                          << " Removed !\n";
+            }
+        }
+
+        prevDriveStatus = currDriveStatus;
+        return true;
+    }
+
+    virtual ~Backplane()
+    {
+        timer.cancel();
+        objServer.remove_interface(hsbpItemIface);
+        objServer.remove_interface(versionIface);
+        objServer.remove_interface(storageInterface);
+        objServer.remove_interface(assetInterface);
+        objServer.remove_interface(activationIface);
+        if (file >= 0)
+        {
+            close(file);
+        }
+    }
+
+    size_t bus;
+    size_t address;
+    size_t backplaneIndex;
+    std::string name;
+    boost::asio::steady_timer timer;
+    bool present = false;
+    uint8_t typeId = 0;
+    uint8_t bootVer = 0;
+    uint8_t fpgaVer = 0;
+    uint8_t securityRev = 0;
+    uint8_t funSupported = 0;
+    uint8_t presence = 0;
+    uint8_t ifdet = 0;
+    uint8_t failed = 0;
+    uint8_t rebuilding = 0;
+    std::bitset<8> prevDriveStatus;
+
+    int file = -1;
+
+    std::string type;
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> activationIface;
+    std::list<Drive> drives;
+    std::vector<std::shared_ptr<Led>> leds;
+    std::shared_ptr<boost::container::flat_set<Mux>> muxes;
+};
+
+/* Global HSBP backplanes and NVMe drives collection */
+std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
+std::list<Drive> ownerlessDrives; // drives without a backplane
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/***************** Miscellaneous Class/Function Definitions *****************/
+/****************************************************************************/
+/* The purpose of this class is to sync the code flow. Often times there could
+ * be multiple dbus calls which are async, and upon completely finishing all
+ * Dbus calls, we need to call next function, or handle the error.
+ * When an object of this class goes out of scope, the respective handlers
+ * will be called */
+class AsyncCallbackHandler
+{
+    bool errorOccurred = false;
+    std::function<void()> onSuccess = nullptr;
+    std::function<void()> onError = nullptr;
+
+  public:
+    explicit AsyncCallbackHandler(std::function<void()> onSuccessIn,
+                                  std::function<void()> onErrorIn) :
+        onSuccess(std::move(onSuccessIn)), onError(std::move(onErrorIn))
+    {}
+
+    void setError()
+    {
+        errorOccurred = true;
+    }
+
+    ~AsyncCallbackHandler()
+    {
+        /* If error occurred flag was set, execute the error handler */
+        if (errorOccurred)
+        {
+            /* Check if Error Handler is defined */
+            if (onError)
+            {
+                onError();
+            }
+
+            return;
+        }
+
+        /* If Success Handler is present, execute Success Handler */
+        if (onSuccess)
+        {
+            onSuccess();
+        }
+    }
+};
+
+void stopHsbpManager()
+{
+    std::cerr << __FUNCTION__ << ": Stopping hsbp-manager\n";
+    appState = AppState::idle;
+    hsbpConfig.clearConfig();
+    clockBuffers.clear();
+    ioExpanders.clear();
+    backplanes.clear();
+
+    io.stop();
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/********* HSBP clock enable/disable related Function Definitions ***********/
+/****************************************************************************/
+void updateHsbpClocks(std::forward_list<std::string>& nvmeDrivesInserted,
+                      std::forward_list<std::string>& nvmeDrivesRemoved)
+{
+    if (appState < AppState::backplanesLoaded)
+    {
+        std::cerr << "HSBP not initialized ! Cancelling Clock Update ! \n";
+        return;
+    }
+
+    std::cerr << "Updating HSBP drive clocks ...\n";
+
+    /* Loop through all clock buffers and try to update the clocks (this will be
+     * done if the mode of operation of the clock buffer is SMBus) */
+    for (auto& clockBuffer : clockBuffers)
+    {
+        if (!clockBuffer.enableDisableClock(nvmeDrivesInserted,
+                                            nvmeDrivesRemoved))
+        {
+            std::cerr << "Error Occurred while setting the clock in \""
+                      << clockBuffer.getName() << "\"\n";
+        }
+    }
+
+    /* If there are drives yet to be updated, check all the IO Expanders in case
+     * they are mapped to the drives and enable the respective IO */
+    if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
+    {
+        for (auto& ioExpander : ioExpanders)
+        {
+            if (!ioExpander.enableDisableOuput(nvmeDrivesInserted,
+                                               nvmeDrivesRemoved))
+            {
+                std::cerr << "Error Occurred while setting the IO in \""
+                          << ioExpander.getName() << "\"\n";
+            }
+        }
+    }
+
+    /* If there are drives still left, then one or more drives clock
+     * enable/diable failed. There is a possibility of improper mapping or
+     * current communication with the device failed */
+    if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
+    {
+        std::cerr << "Critical Error !!!\nMapping issue detected !\n";
+
+        if (!nvmeDrivesInserted.empty())
+        {
+            std::cerr << "The clock enable failed for : ";
+            for (auto& nvme1 : nvmeDrivesInserted)
+            {
+                std::cerr << nvme1 << ", ";
+            }
+            std::cerr << "\n";
+        }
+
+        if (!nvmeDrivesRemoved.empty())
+        {
+            std::cerr << "The clock disable failed for : ";
+            for (auto& nvme1 : nvmeDrivesRemoved)
+            {
+                std::cerr << nvme1 << ", ";
+            }
+            std::cerr << "\n";
+        }
+    }
+}
+
+void scanHsbpDrives(bool& hsbpDriveScanInProgress)
+{
+    std::cerr << __FUNCTION__ << ": Scanning HSBP drives status ...\n";
+
+    /* List variables to store the drives Inserted/Removed */
+    std::forward_list<std::string> nvmeDrivesInserted;
+    std::forward_list<std::string> nvmeDrivesRemoved;
+
+    /* Loop through each backplane present and get the list of inserted/removed
+     * drives */
+    for (auto& [name, backplane] : backplanes)
+    {
+        backplane->getInsertedAndRemovedNvmeDrives(nvmeDrivesInserted,
+                                                   nvmeDrivesRemoved);
+    }
+
+    if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
+    {
+        updateHsbpClocks(nvmeDrivesInserted, nvmeDrivesRemoved);
+    }
+
+    std::cerr << __FUNCTION__ << ": Scanning HSBP drives Completed\n";
+
+    hsbpDriveScanInProgress = false;
+}
+
+void checkHsbpDrivesStatus()
+{
+    static bool hsbpDriveScanInProgress = false;
+    static bool hsbpDriveRescanInQueue = false;
+
+    if (appState < AppState::backplanesLoaded)
+    {
+        std::cerr << __FUNCTION__
+                  << ": HSBP not initialized ! Cancelling scan of HSBP drives "
+                     "status ! \n";
+        return;
+    }
+
+    if (hsbpDriveScanInProgress)
+    {
+        /* Scan and Clock Update already in progress. Try again after sometime.
+         * This event can occur due to the GPIO interrupt */
+        std::cerr << __FUNCTION__
+                  << ": HSBP Drives Scan is already in progress\n";
+        if (hsbpDriveRescanInQueue)
+        {
+            /* There is already a Re-Scan in queue. No need to create multiple
+             * rescans */
+            return;
+        }
+
+        hsbpDriveRescanInQueue = true;
+
+        std::cerr << __FUNCTION__ << ": Queuing the Scan \n";
+
+        auto driveScanTimer = std::make_shared<boost::asio::steady_timer>(io);
+        driveScanTimer->expires_after(std::chrono::seconds(1));
+        driveScanTimer->async_wait(
+            [driveScanTimer](const boost::system::error_code ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    // Timer was Aborted
+                    return;
+                }
+                else if (ec)
+                {
+                    std::cerr << "driveScanTimer: Timer error" << ec.message()
+                              << "\n";
+                    return;
+                }
+                hsbpDriveRescanInQueue = false;
+                checkHsbpDrivesStatus();
+            });
+
+        return;
+    }
+
+    hsbpDriveScanInProgress = true;
+
+    /* Post the scan to IO queue and return from here. This enables capturing
+     * next GPIO event if any */
+    boost::asio::post(io, []() { scanHsbpDrives(hsbpDriveScanInProgress); });
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/********** Backplanes and NVMe drives related Function Definitions *********/
+/****************************************************************************/
+static size_t getDriveCount()
+{
+    size_t count = 0;
+    for (const auto& [key, backplane] : backplanes)
+    {
+        count += backplane->drives.size();
+    }
+    return count + ownerlessDrives.size();
+}
+
+void updateAssets()
+{
+    appState = AppState::loadingDrives;
+
+    /* Setup a callback to be called once the assets are populated completely or
+     * fallback to error handler */
+    auto drivesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
+        []() {
+            appState = AppState::drivesLoaded;
+            std::cerr << "Drives Updated !\n";
+        },
+        []() {
+            // TODO: Handle this error if needed
+            appState = AppState::backplanesLoaded;
+            std::cerr << "An error occured ! Drives load failed \n";
+        });
+
+    conn->async_method_call(
+        [drivesLoadedCallback](const boost::system::error_code ec,
+                               const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                drivesLoadedCallback->setError();
+                return;
+            }
+
+            // drives may get an owner during this, or we might disover more
+            // drives
+            ownerlessDrives.clear();
+            for (const auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty())
+                {
+                    continue;
+                }
+
+                const std::string& owner = objDict.begin()->first;
+                // we export this interface too
+                if (owner == busName)
+                {
+                    continue;
+                }
+                if (std::find(objDict.begin()->second.begin(),
+                              objDict.begin()->second.end(), assetTag) ==
+                    objDict.begin()->second.end())
+                {
+                    // no asset tag to associate to
+                    continue;
+                }
+
+                conn->async_method_call(
+                    [path, drivesLoadedCallback](
+                        const boost::system::error_code ec2,
+                        const boost::container::flat_map<
+                            std::string, std::variant<uint64_t, std::string>>&
+                            values) {
+                        if (ec2)
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Error Getting Config "
+                                << ec2.message() << " "
+                                << "\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+                        auto findBus = values.find("Bus");
+
+                        if (findBus == values.end())
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Illegal interface at "
+                                << path << "\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+
+                        // find the mux bus and addr
+                        size_t muxBus = static_cast<size_t>(
+                            std::get<uint64_t>(findBus->second));
+                        std::filesystem::path muxPath =
+                            "/sys/bus/i2c/devices/i2c-" +
+                            std::to_string(muxBus) + "/mux_device";
+                        if (!std::filesystem::is_symlink(muxPath))
+                        {
+                            std::cerr << path << " mux does not exist\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+
+                        // we should be getting something of the form 7-0052
+                        // for bus 7 addr 52
+                        std::string fname =
+                            std::filesystem::read_symlink(muxPath).filename();
+                        auto findDash = fname.find('-');
+
+                        if (findDash == std::string::npos ||
+                            findDash + 1 >= fname.size())
+                        {
+                            std::cerr << path << " mux path invalid\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+
+                        std::string busStr = fname.substr(0, findDash);
+                        std::string muxStr = fname.substr(findDash + 1);
+
+                        size_t bus = static_cast<size_t>(std::stoi(busStr));
+                        size_t addr =
+                            static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
+                        size_t muxIndex = 0;
+
+                        // find the channel of the mux the drive is on
+                        std::ifstream nameFile(
+                            "/sys/bus/i2c/devices/i2c-" +
+                            std::to_string(muxBus) + "/name");
+                        if (!nameFile)
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Unable to open name file of bus "
+                                      << muxBus << "\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+
+                        std::string nameStr;
+                        std::getline(nameFile, nameStr);
+
+                        // file is of the form "i2c-4-mux (chan_id 1)", get chan
+                        // assume single digit chan
+                        const std::string prefix = "chan_id ";
+                        size_t findId = nameStr.find(prefix);
+                        if (findId == std::string::npos ||
+                            findId + 1 >= nameStr.size())
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Illegal name file on bus "
+                                << muxBus << "\n";
+                        }
+
+                        std::string indexStr =
+                            nameStr.substr(findId + prefix.size(), 1);
+
+                        size_t driveIndex = std::stoi(indexStr);
+
+                        boost::container::flat_map<std::string, std::string>
+                            assetInventory;
+                        const std::array<const char*, 4> assetKeys = {
+                            "PartNumber", "SerialNumber", "Manufacturer",
+                            "Model"};
+                        for (const auto& [key, value] : values)
+                        {
+                            if (std::find(assetKeys.begin(), assetKeys.end(),
+                                          key) == assetKeys.end())
+                            {
+                                continue;
+                            }
+                            if (std::holds_alternative<std::string>(value))
+                            {
+                                assetInventory[key] =
+                                    std::get<std::string>(value);
+                            }
+                        }
+
+                        Backplane* parent = nullptr;
+                        for (auto& [name, backplane] : backplanes)
+                        {
+                            muxIndex = 0;
+                            for (const Mux& mux : *(backplane->muxes))
+                            {
+                                if (bus == mux.bus && addr == mux.address)
+                                {
+                                    parent = backplane.get();
+                                    break;
+                                }
+                                muxIndex += mux.channels;
+                            }
+                            if (parent)
+                            {
+                                /* Found the backplane. No need to proceed
+                                 * further */
+                                break;
+                            }
+                        }
+
+                        // assume its a M.2 or something without a hsbp
+                        if (parent == nullptr)
+                        {
+                            std::string driveName =
+                                "Drive_" + std::to_string(getDriveCount() + 1);
+                            auto& drive = ownerlessDrives.emplace_back(
+                                driveName, true, true, true, false);
+                            drive.createAsset(assetInventory);
+                            return;
+                        }
+
+                        driveIndex += muxIndex;
+
+                        if (parent->drives.size() <= driveIndex)
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Illegal drive index at "
+                                << path << " " << driveIndex << "\n";
+                            drivesLoadedCallback->setError();
+                            return;
+                        }
+                        auto it = parent->drives.begin();
+                        std::advance(it, driveIndex);
+
+                        it->createAsset(assetInventory);
+                    },
+                    owner, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    "" /*all interface items*/);
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, std::array<const char*, 1>{nvmeIntf});
+}
+
+void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
+                   std::string& rootPath)
+{
+    const static std::array<const std::string, 4> muxTypes = {
+        "xyz.openbmc_project.Configuration.PCA9543Mux",
+        "xyz.openbmc_project.Configuration.PCA9544Mux",
+        "xyz.openbmc_project.Configuration.PCA9545Mux",
+        "xyz.openbmc_project.Configuration.PCA9546Mux"};
+
+    conn->async_method_call(
+        [muxes](const boost::system::error_code ec,
+                const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                return;
+            }
+            size_t index = 0; // as we use a flat map, these are sorted
+            for (const auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty() || objDict.begin()->second.empty())
+                {
+                    continue;
+                }
+
+                const std::string& owner = objDict.begin()->first;
+                const std::vector<std::string>& interfaces =
+                    objDict.begin()->second;
+
+                const std::string* interface = nullptr;
+                for (const std::string& iface : interfaces)
+                {
+                    if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
+                        muxTypes.end())
+                    {
+                        interface = &iface;
+                        break;
+                    }
+                }
+
+                if (interface == nullptr)
+                {
+                    std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
+                    continue;
+                }
+
+                conn->async_method_call(
+                    [path, muxes, index](
+                        const boost::system::error_code ec2,
+                        const boost::container::flat_map<
+                            std::string,
+                            std::variant<uint64_t, std::vector<std::string>>>&
+                            values) {
+                        if (ec2)
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Error Getting Config "
+                                << ec2.message() << "\n";
+                            return;
+                        }
+                        auto findBus = values.find("Bus");
+                        auto findAddress = values.find("Address");
+                        auto findChannelNames = values.find("ChannelNames");
+                        if (findBus == values.end() ||
+                            findAddress == values.end())
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Illegal configuration at "
+                                << path << "\n";
+                            return;
+                        }
+                        size_t bus = static_cast<size_t>(
+                            std::get<uint64_t>(findBus->second));
+                        size_t address = static_cast<size_t>(
+                            std::get<uint64_t>(findAddress->second));
+                        std::vector<std::string> channels =
+                            std::get<std::vector<std::string>>(
+                                findChannelNames->second);
+                        muxes->emplace(bus, address, channels.size(), index);
+                    },
+                    owner, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    *interface);
+                index++;
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree,
+        rootPath, 1, muxTypes);
+}
+
+void populateHsbpBackplanes(
+    const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
+{
+    std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
+    appState = AppState::loadingBackplanes;
+    backplanes.clear();
+
+    conn->async_method_call(
+        [backplanesLoadedCallback](const boost::system::error_code ec,
+                                   const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                backplanesLoadedCallback->setError();
+                return;
+            }
+
+            if (subtree.empty())
+            {
+                /* There wer no HSBP's detected. set teh state back to
+                 * componentsLoaded so that on backplane match event, the
+                 * process can start again */
+                appState = AppState::componentsLoaded;
+                std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
+                return;
+            }
+
+            for (const auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty())
+                {
+                    std::cerr << __FUNCTION__
+                              << ": Subtree data "
+                                 "corrupted !\n";
+                    backplanesLoadedCallback->setError();
+                    return;
+                }
+
+                const std::string& owner = objDict.begin()->first;
+                conn->async_method_call(
+                    [backplanesLoadedCallback, path,
+                     owner](const boost::system::error_code ec2,
+                            const boost::container::flat_map<
+                                std::string, BasicVariantType>& resp) {
+                        if (ec2)
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Error Getting Config "
+                                << ec2.message() << "\n";
+                            backplanesLoadedCallback->setError();
+                            return;
+                        }
+                        std::optional<size_t> bus;
+                        std::optional<size_t> address;
+                        std::optional<size_t> backplaneIndex;
+                        std::optional<std::string> name;
+                        for (const auto& [key, value] : resp)
+                        {
+                            if (key == "Bus")
+                            {
+                                bus = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Address")
+                            {
+                                address = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Index")
+                            {
+                                backplaneIndex = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Name")
+                            {
+                                name = std::get<std::string>(value);
+                            }
+                        }
+                        if (!bus || !address || !name || !backplaneIndex)
+                        {
+                            std::cerr
+                                << __FUNCTION__ << ": Illegal configuration at "
+                                << path << "\n";
+                            backplanesLoadedCallback->setError();
+                            return;
+                        }
+                        std::string parentPath =
+                            std::filesystem::path(path).parent_path();
+                        const auto& [backplane, status] = backplanes.emplace(
+                            *name, std::make_shared<Backplane>(
+                                       *bus, *address, *backplaneIndex, *name));
+                        backplane->second->run(parentPath, owner);
+                        populateMuxes(backplane->second->muxes, parentPath);
+                    },
+                    owner, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    hsbpCpldInft);
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, std::array<const char*, 1>{hsbpCpldInft});
+}
+
+void setUpBackplanesAndDrives()
+{
+    static bool backplanesScanInProgress = false;
+    static bool backplanesRescanInQueue = false;
+
+    if (appState < AppState::componentsLoaded)
+    {
+        std::cerr << __FUNCTION__
+                  << ": Components are not initialized ! Cancelling scan of "
+                     "Backplanes ! \n";
+        return;
+    }
+
+    if (backplanesScanInProgress)
+    {
+        std::cerr << __FUNCTION__
+                  << ": Backplanes Scan is already in progress\n";
+        if (backplanesRescanInQueue)
+        {
+            /* There is already a Re-Scan in queue. No need to create multiple
+             * rescans */
+            return;
+        }
+
+        backplanesRescanInQueue = true;
+
+        std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
+
+        auto backplaneScanTimer =
+            std::make_shared<boost::asio::steady_timer>(io);
+        backplaneScanTimer->expires_after(std::chrono::seconds(1));
+        backplaneScanTimer->async_wait(
+            [backplaneScanTimer](const boost::system::error_code ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    // Timer was Aborted
+                    return;
+                }
+                else if (ec)
+                {
+                    std::cerr << "backplaneScanTimer: Timer error"
+                              << ec.message() << "\n";
+                    return;
+                }
+                backplanesRescanInQueue = false;
+                setUpBackplanesAndDrives();
+            });
+
+        return;
+    }
+
+    backplanesScanInProgress = true;
+
+    /* Set Callback to be called once backplanes are populated to call
+     * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
+    auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
+        []() {
+            /* If no HSBP's were detected, the state changes to
+             * componentsLoaded. Proceed further only if state was
+             * loadingBackplanes */
+            if (appState != AppState::loadingBackplanes)
+            {
+                backplanesScanInProgress = false;
+                return;
+            }
+
+            /* If there is a ReScan in the Queue, dont proceed further. Load the
+             * Backplanes again and then proceed further */
+            if (backplanesRescanInQueue)
+            {
+                backplanesScanInProgress = false;
+                return;
+            }
+
+            appState = AppState::backplanesLoaded;
+            std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
+
+            checkHsbpDrivesStatus();
+            updateAssets();
+            backplanesScanInProgress = false;
+        },
+        []() {
+            /* Loading Backplanes is an important step. If the load failed due
+             * to an error, stop the app so that restart cant be triggerred */
+            std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
+            appState = AppState::idle;
+            backplanesScanInProgress = false;
+            stopHsbpManager();
+        });
+
+    populateHsbpBackplanes(backplanesLoadedCallback);
+}
+
+void setupBackplanesAndDrivesMatch()
+{
+    static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
+        *conn,
+        "sender='xyz.openbmc_project.EntityManager', type='signal', "
+        "member='PropertiesChanged', "
+        "interface='org.freedesktop.DBus.Properties', "
+        "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
+            std::string(hsbpCpldInft) + "'",
+        [](sdbusplus::message_t& msg) {
+            std::string intfName;
+            boost::container::flat_map<std::string, BasicVariantType> values;
+            msg.read(intfName, values);
+
+            /* This match will be triggered for each of the property being set
+             * under the hsbpCpldInft interface. Call the loader only on one
+             * property say "name". This will avoid multiple calls to populate
+             * function
+             */
+            for (const auto& [key, value] : values)
+            {
+                if (key == "Name")
+                {
+                    /* This match will be triggered when ever there is a
+                     * addition/removal of HSBP backplane. At this stage, all
+                     * the HSBP's need to be populated again and also assets
+                     * have to be re-discovered. So, setting state to
+                     * componentsLoaded and calling setUpBackplanesAndDrives()
+                     * only if configuration and components loading was
+                     * completed */
+                    if (appState < AppState::componentsLoaded)
+                    {
+                        /* Configuration is not loaded yet. Backplanes will be
+                         * loaded
+                         * once configuration and components are loaded. */
+                        std::cerr
+                            << __FUNCTION__ << ": Discarding Backplane match\n";
+                        return;
+                    }
+
+                    appState = AppState::componentsLoaded;
+
+                    /* We will call the function after a small delay to let all
+                     * the properties to be intialized */
+                    auto backplaneTimer =
+                        std::make_shared<boost::asio::steady_timer>(io);
+                    backplaneTimer->expires_after(std::chrono::seconds(2));
+                    backplaneTimer->async_wait(
+                        [backplaneTimer](const boost::system::error_code ec) {
+                            if (ec == boost::asio::error::operation_aborted)
+                            {
+                                return;
+                            }
+                            else if (ec)
+                            {
+                                std::cerr << "backplaneTimer: Timer error"
+                                          << ec.message() << "\n";
+                                return;
+                            }
+                            setUpBackplanesAndDrives();
+                        });
+                }
+            }
+        });
+
+    static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
+        *conn,
+        "sender='xyz.openbmc_project.EntityManager', type='signal', "
+        "member='PropertiesChanged', "
+        "interface='org.freedesktop.DBus.Properties', arg0='" +
+            std::string(nvmeIntf) + "'",
+        [](sdbusplus::message_t& msg) {
+            std::string intfName;
+            boost::container::flat_map<std::string, BasicVariantType> values;
+            msg.read(intfName, values);
+
+            /* This match will be triggered for each of the property being set
+             * under the nvmeIntf interface. Call the loader only on one
+             * property say "name". This will avoid multiple calls to populate
+             * function
+             */
+            for (const auto& [key, value] : values)
+            {
+                if (key == "Name")
+                {
+                    /* This match will be triggered when ever there is a
+                     * addition/removal of drives. At this stage only assets
+                     * have to be re-discovered. So, setting state to
+                     * backplanesLoaded and calling updateAssets() only if all
+                     * previous states are completed */
+                    if (appState < AppState::backplanesLoaded)
+                    {
+                        /* Configuration is not loaded yet. Drives will be
+                         * loaded once
+                         * configuration, components and backplanes are loaded.
+                         */
+                        std::cerr
+                            << __FUNCTION__ << ": Discarding Drive match\n";
+                        return;
+                    }
+
+                    appState = AppState::backplanesLoaded;
+
+                    /* We will call the function after a small delay to let all
+                     * the properties to be intialized */
+                    auto driveTimer =
+                        std::make_shared<boost::asio::steady_timer>(io);
+                    driveTimer->expires_after(std::chrono::seconds(2));
+                    driveTimer->async_wait(
+                        [driveTimer](const boost::system::error_code ec) {
+                            if (ec == boost::asio::error::operation_aborted)
+                            {
+                                return;
+                            }
+                            else if (ec)
+                            {
+                                std::cerr << "driveTimer: Timer error"
+                                          << ec.message() << "\n";
+                                return;
+                            }
+                            updateAssets();
+                        });
+                }
+            }
+        });
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/******************* Components related Function Definitions ****************/
+/****************************************************************************/
+bool verifyComponentsLoaded()
+{
+    std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
+
+    /* Loop through all clock buffers */
+    for (auto& clockBuffer : clockBuffers)
+    {
+        if (!clockBuffer.isInitialized())
+        {
+            std::cerr << "Critical Error: Initializing \""
+                      << clockBuffer.getName() << "\" failed\n";
+            return false;
+        }
+    }
+
+    /* Loop through all IO Expanders */
+    for (auto& ioExpander : ioExpanders)
+    {
+        if (!ioExpander.isInitialized())
+        {
+            std::cerr << "Critical Error: Initializing \""
+                      << ioExpander.getName() << "\" failed\n";
+            return false;
+        }
+    }
+
+    std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
+
+    return true;
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/****************** IO expander related Function Definitions ****************/
+/****************************************************************************/
+void loadIoExpanderInfo(
+    const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
+{
+    appState = AppState::loadingComponents;
+
+    /* Clear global ioExpanders to start off */
+    ioExpanders.clear();
+
+    conn->async_method_call(
+        [componentsLoadedCallback](const boost::system::error_code ec,
+                                   const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                componentsLoadedCallback->setError();
+                return;
+            }
+
+            for (auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty())
+                {
+                    std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
+                    componentsLoadedCallback->setError();
+                    return;
+                }
+
+                /* Ideally there would be only one element in objDict as only
+                 * one service exposes it and there would be only one interface
+                 * so it is safe to directly read them without loop */
+                const std::string& service = objDict.begin()->first;
+                const std::string& intf = objDict.begin()->second.front();
+
+                conn->async_method_call(
+                    [componentsLoadedCallback](
+                        const boost::system::error_code er,
+                        const boost::container::flat_map<
+                            std::string, BasicVariantType>& resp) {
+                        if (er)
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Error Getting "
+                                         "Config "
+                                      << er.message() << "\n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        std::optional<uint64_t> bus;
+                        std::optional<uint64_t> address;
+                        std::optional<uint64_t> confIORegAddr;
+                        std::optional<uint64_t> outCtrlBaseAddr;
+                        std::optional<uint64_t> outCtrlByteCount;
+                        std::unordered_map<std::string,
+                                           std::vector<std::string>>
+                            ioMap;
+                        std::optional<std::string> name;
+                        std::optional<std::string> type;
+
+                        /* Loop through to get all IO Expander properties */
+                        for (const auto& [key, value] : resp)
+                        {
+                            if (key == "Bus")
+                            {
+                                bus = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Address")
+                            {
+                                address = std::get<uint64_t>(value);
+                            }
+                            else if (key == "ConfIORegAddr")
+                            {
+                                confIORegAddr = std::get<uint64_t>(value);
+                            }
+                            else if (key == "OutCtrlBaseAddr")
+                            {
+                                outCtrlBaseAddr = std::get<uint64_t>(value);
+                            }
+                            else if (key == "OutCtrlByteCount")
+                            {
+                                outCtrlByteCount = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Name")
+                            {
+                                name = std::get<std::string>(value);
+                            }
+                            else if (key == "Type")
+                            {
+                                type = std::get<std::string>(value);
+                            }
+                            else if (key.starts_with("IO"))
+                            {
+                                std::optional<std::vector<std::string>> outList;
+                                outList = std::get<NvmeMapping>(value);
+                                if (!outList)
+                                {
+                                    break;
+                                }
+                                ioMap.try_emplace(key, *outList);
+                            }
+                        }
+
+                        /* Verify if all properties were defined */
+                        if (!bus || !address || !confIORegAddr ||
+                            !outCtrlBaseAddr || !outCtrlByteCount || !name)
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Incomplete "
+                                         "Clock Buffer definition !! \n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        /* Check if we were able to get byteMap correctly */
+                        if ((*outCtrlByteCount) != ioMap.size())
+                        {
+                            std::cerr << "loadIoExpanderInfo(): Incomplete "
+                                         "IO Map !! \n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        /* Create IO expander object and add it to global
+                         * ioExpanders vector */
+                        ioExpanders.emplace_front(
+                            *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
+                            *outCtrlByteCount, ioMap, *name, *type);
+                    },
+                    service, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    intf);
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, hsbpConfig.ioExpanderTypes);
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/***************** Clock buffer related Function Definitions ****************/
+/****************************************************************************/
+void loadClockBufferInfo(
+    const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
+{
+    appState = AppState::loadingComponents;
+
+    /* Clear global clockBuffers to start off */
+    clockBuffers.clear();
+
+    conn->async_method_call(
+        [componentsLoadedCallback](const boost::system::error_code ec,
+                                   const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                componentsLoadedCallback->setError();
+                return;
+            }
+
+            for (auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty())
+                {
+                    std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
+                    componentsLoadedCallback->setError();
+                    return;
+                }
+
+                /* Ideally there would be only one element in objDict as only
+                 * one service exposes it and there would be only one interface
+                 * so it is safe to directly read them without loop */
+                const std::string& service = objDict.begin()->first;
+                const std::string& intf = objDict.begin()->second.front();
+
+                conn->async_method_call(
+                    [componentsLoadedCallback](
+                        const boost::system::error_code er,
+                        const boost::container::flat_map<
+                            std::string, BasicVariantType>& resp) {
+                        if (er)
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Error Getting "
+                                         "Config "
+                                      << er.message() << "\n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        std::optional<uint64_t> bus;
+                        std::optional<uint64_t> address;
+                        std::optional<std::string> mode;
+                        std::optional<uint64_t> outCtrlBaseAddr;
+                        std::optional<uint64_t> outCtrlByteCount;
+                        std::unordered_map<std::string,
+                                           std::vector<std::string>>
+                            byteMap;
+                        std::optional<std::string> name;
+                        std::optional<std::string> type;
+
+                        /* Loop through to get all Clock Buffer properties */
+                        for (const auto& [key, value] : resp)
+                        {
+                            if (key == "Bus")
+                            {
+                                bus = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Address")
+                            {
+                                address = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Mode")
+                            {
+                                mode = std::get<std::string>(value);
+                            }
+                            else if (key == "OutCtrlBaseAddr")
+                            {
+                                outCtrlBaseAddr = std::get<uint64_t>(value);
+                            }
+                            else if (key == "OutCtrlByteCount")
+                            {
+                                outCtrlByteCount = std::get<uint64_t>(value);
+                            }
+                            else if (key == "Name")
+                            {
+                                name = std::get<std::string>(value);
+                            }
+                            else if (key == "Type")
+                            {
+                                type = std::get<std::string>(value);
+                            }
+                            else if (key.starts_with("Byte"))
+                            {
+                                std::optional<std::vector<std::string>>
+                                    byteList;
+                                byteList = std::get<NvmeMapping>(value);
+                                if (!byteList)
+                                {
+                                    break;
+                                }
+                                byteMap.try_emplace(key, *byteList);
+                            }
+                        }
+
+                        /* Verify if all properties were defined */
+                        if (!bus || !address || !mode || !outCtrlBaseAddr ||
+                            !outCtrlByteCount || !name)
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Incomplete "
+                                         "Clock Buffer definition !! \n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        /* Check if we were able to get byteMap correctly */
+                        if ((*outCtrlByteCount) != byteMap.size())
+                        {
+                            std::cerr << __FUNCTION__
+                                      << ": Incomplete "
+                                         "Byte Map !! \n";
+                            componentsLoadedCallback->setError();
+                            return;
+                        }
+
+                        /* Create clock buffer object and add it to global
+                         * clockBuffers vector */
+                        clockBuffers.emplace_front(
+                            *bus, *address, *mode, *outCtrlBaseAddr,
+                            *outCtrlByteCount, byteMap, *name, *type);
+                    },
+                    service, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    intf);
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, hsbpConfig.clockBufferTypes);
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/***************** HSBP Config related Function Definitions *****************/
+/****************************************************************************/
+void loadHsbpConfig()
+{
+    appState = AppState::loadingHsbpConfig;
+
+    conn->async_method_call(
+        [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": Error contacting mapper "
+                          << ec.message() << "\n";
+                return;
+            }
+
+            if (subtree.empty())
+            {
+                /* Entity manager is either still loading the configuration or
+                 * failed to load. In either way, return from here as the dbus
+                 * match will take care */
+                std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
+                return;
+            }
+
+            /* There should be only one HSBP Configureation exposed */
+            if (subtree.size() != 1)
+            {
+                std::cerr << __FUNCTION__
+                          << ": Multiple configurations "
+                             "detected !!\n";
+                /* Critical Error. Stop Application */
+                stopHsbpManager();
+                return;
+            }
+
+            auto& path = subtree.begin()->first;
+            auto& objDict = subtree.begin()->second;
+
+            if (objDict.empty())
+            {
+                /* Critical Error. Stop Application */
+                std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
+                stopHsbpManager();
+                return;
+            }
+
+            const std::string& service = objDict.begin()->first;
+
+            conn->async_method_call(
+                [](const boost::system::error_code er,
+                   const boost::container::flat_map<std::string,
+                                                    BasicVariantType>& resp) {
+                    if (er)
+                    {
+                        std::cerr << __FUNCTION__ << ": Error Getting Config "
+                                  << er.message() << "\n";
+                        /* Critical Error. Stop Application */
+                        stopHsbpManager();
+                        return;
+                    }
+
+                    std::optional<uint64_t> rootI2cBus;
+                    std::optional<std::vector<std::string>> supportedHsbps;
+                    std::optional<std::vector<std::string>> clockBufferTypes;
+                    std::optional<std::vector<std::string>> ioExpanderTypes;
+
+                    /* Loop through to get root i2c bus and list of supported
+                     * HSBPs */
+                    for (const auto& [key, value] : resp)
+                    {
+                        if (key == "HsbpSupported")
+                        {
+                            supportedHsbps =
+                                std::get<std::vector<std::string>>(value);
+                        }
+                        else if (key == "RootI2cBus")
+                        {
+                            rootI2cBus = std::get<uint64_t>(value);
+                        }
+                        else if (key == "ClockBuffer")
+                        {
+                            clockBufferTypes =
+                                std::get<std::vector<std::string>>(value);
+                        }
+                        else if (key == "IoExpander")
+                        {
+                            ioExpanderTypes =
+                                std::get<std::vector<std::string>>(value);
+                        }
+                    }
+
+                    /* Verify if i2c bus, supported HSBP's and clock buffers
+                     * were defined (IO Expanders are optional) */
+                    if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
+                    {
+                        std::cerr << __FUNCTION__
+                                  << ": Incomplete HSBP "
+                                     "configuration !! \n";
+                        /* Critical Error. Stop Application */
+                        stopHsbpManager();
+                        return;
+                    }
+
+                    /* Clear and Load all details to global hsbp configuration
+                     * variable */
+                    hsbpConfig.clearConfig();
+                    hsbpConfig.rootBus = *rootI2cBus;
+                    hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
+
+                    for (auto& clkBuffType : *clockBufferTypes)
+                    {
+                        hsbpConfig.clockBufferTypes.emplace_back(
+                            "xyz.openbmc_project.Configuration." + clkBuffType);
+                    }
+
+                    if (ioExpanderTypes)
+                    {
+                        for (auto& ioCntrType : *ioExpanderTypes)
+                        {
+                            hsbpConfig.ioExpanderTypes.emplace_back(
+                                "xyz.openbmc_project.Configuration." +
+                                ioCntrType);
+                        }
+                    }
+
+                    /* Loop through to get HSBP-NVME map and Components map
+                     * details */
+                    uint8_t hsbpMapCount = 0;
+                    for (const auto& [key, value] : resp)
+                    {
+                        if (std::find(hsbpConfig.supportedHsbps.begin(),
+                                      hsbpConfig.supportedHsbps.end(), key) !=
+                            hsbpConfig.supportedHsbps.end())
+                        {
+                            std::optional<std::vector<std::string>> hsbpMap;
+                            hsbpMap = std::get<NvmeMapping>(value);
+                            if (!hsbpMap)
+                            {
+                                break;
+                            }
+                            hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
+                            hsbpMapCount++;
+                        }
+                    }
+
+                    /* Check if we were able to get all the HSBP-NVMe maps */
+                    if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
+                    {
+                        std::cerr << __FUNCTION__
+                                  << ": Incomplete HSBP Map "
+                                     "details !! \n";
+                        /* Critical Error. Stop Application */
+                        stopHsbpManager();
+                        return;
+                    }
+
+                    /* HSBP configuration is loaded */
+                    appState = AppState::hsbpConfigLoaded;
+                    std::cerr << "HSBP Config loaded !\n";
+
+                    /* Get Clock buffers and IO expander details. Create shared
+                     * object of AsyncCallbackHandler with success and error
+                     * callback */
+                    auto componentsLoadedCallback = std::make_shared<
+                        AsyncCallbackHandler>(
+                        []() {
+                            /* Verify if all components were initialized without
+                             * errors */
+                            if (!verifyComponentsLoaded())
+                            {
+                                /* The application cannot proceed further as
+                                 * components initialization failed. App needs
+                                 * Restart */
+                                appState = AppState::idle;
+                                std::cerr
+                                    << "One or more Componenets initialization "
+                                       "failed !! Restart Required !\n";
+                                stopHsbpManager();
+                            }
+
+                            appState = AppState::componentsLoaded;
+                            setUpBackplanesAndDrives();
+                        },
+                        []() {
+                            /* The application cannot proceed further as
+                             * components load failed. App needs Restart */
+                            appState = AppState::idle;
+                            std::cerr << "Loading Componenets failed !! "
+                                         "Restart Required !\n";
+                            stopHsbpManager();
+                        });
+
+                    loadClockBufferInfo(componentsLoadedCallback);
+
+                    if (ioExpanderTypes)
+                    {
+                        loadIoExpanderInfo(componentsLoadedCallback);
+                    }
+                },
+                service, path, "org.freedesktop.DBus.Properties", "GetAll",
+                hsbpConfigIntf);
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, std::array<const char*, 1>{hsbpConfigIntf});
+}
+
+void setupHsbpConfigMatch()
+{
+    static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
+        *conn,
+        "sender='xyz.openbmc_project.EntityManager', type='signal', "
+        "member='PropertiesChanged', "
+        "interface='org.freedesktop.DBus.Properties', "
+        "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
+            std::string(hsbpConfigIntf) + "'",
+        [](sdbusplus::message_t& msg) {
+            std::string intfName;
+            boost::container::flat_map<std::string, BasicVariantType> values;
+            msg.read(intfName, values);
+
+            /* This match will be triggered for each of the property being set
+             * under the hsbpConfig interface. "HsbpSupported" is one of the
+             * important property which will enable us to read other properties.
+             * So, when the match event occurs for "HsbpSupported" property
+             * being set, we will call "loadHsbpConfig()" If the control has
+             * come here, its either the first initialization or entity-manager
+             * reload. So, we will reset the state to uninitialized
+             */
+            for (const auto& [key, value] : values)
+            {
+                if (key == "HsbpSupported")
+                {
+                    /* Configuration change detected, change the state to stop
+                     * other processing */
+                    appState = AppState::idle;
+
+                    /* We will call the function after a small delay to let all
+                     * the properties to be intialized */
+                    auto loadTimer =
+                        std::make_shared<boost::asio::steady_timer>(io);
+                    loadTimer->expires_after(std::chrono::seconds(1));
+                    loadTimer->async_wait(
+                        [loadTimer](const boost::system::error_code ec) {
+                            if (ec == boost::asio::error::operation_aborted)
+                            {
+                                return;
+                            }
+                            else if (ec)
+                            {
+                                std::cerr << __FUNCTION__ << ": Timer error"
+                                          << ec.message() << "\n";
+                                if (hsbpConfig.supportedHsbps.empty())
+                                {
+                                    /* Critical Error as none of the
+                                     * configuration was loaded and timer
+                                     * failed. Stop the application */
+                                    stopHsbpManager();
+                                }
+                                return;
+                            }
+                            loadHsbpConfig();
+                        });
+                }
+            }
+        });
+}
+/***************************** End of Section *******************************/
+
+/****************************************************************************/
+/***************** GPIO Events related Function Definitions *****************/
+/****************************************************************************/
+static void nvmeLvc3AlertHandler()
+{
+    /* If the state is not backplanesLoaded, we ignore the GPIO event as we
+     * cannot communicate to the backplanes yet */
+    if (appState < AppState::backplanesLoaded)
+    {
+        std::cerr << __FUNCTION__
+                  << ": HSBP not initialized ! Dropping the interrupt ! \n";
+        return;
+    }
+
+    /* This GPIO event only indicates the addition or removal of drive to either
+     * of CPU. The backplanes detected need to be scanned and detect which drive
+     * has been added/removed and enable/diable clock accordingly */
+    gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
+
+    if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
+    {
+        /* Check for HSBP Drives status to determine if any new drive has been
+         * added/removed and update clocks accordingly */
+        checkHsbpDrivesStatus();
+    }
+
+    nvmeLvc3AlertEvent.async_wait(
+        boost::asio::posix::stream_descriptor::wait_read,
+        [](const boost::system::error_code ec) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": nvmealert event error: "
+                          << ec.message() << "\n";
+            }
+            nvmeLvc3AlertHandler();
+        });
+}
+
+static bool hsbpRequestAlertGpioEvents(
+    const std::string& name, const std::function<void()>& handler,
+    gpiod::line& gpioLine,
+    boost::asio::posix::stream_descriptor& gpioEventDescriptor)
+{
+    // Find the GPIO line
+    gpioLine = gpiod::find_line(name);
+    if (!gpioLine)
+    {
+        std::cerr << __FUNCTION__ << ": Failed to find the " << name
+                  << " line\n";
+        return false;
+    }
+
+    try
+    {
+        gpioLine.request(
+            {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
+    }
+    catch (std::exception&)
+    {
+        std::cerr << __FUNCTION__ << ": Failed to request events for " << name
+                  << "\n";
+        return false;
+    }
+
+    int gpioLineFd = gpioLine.event_get_fd();
+    if (gpioLineFd < 0)
+    {
+        std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
+        return false;
+    }
+
+    gpioEventDescriptor.assign(gpioLineFd);
+
+    gpioEventDescriptor.async_wait(
+        boost::asio::posix::stream_descriptor::wait_read,
+        [&name, handler](const boost::system::error_code ec) {
+            if (ec)
+            {
+                std::cerr << __FUNCTION__ << ": " << name
+                          << " fd handler error: " << ec.message() << "\n";
+                return;
+            }
+            handler();
+        });
+    return true;
+}
+/***************************** End of Section *******************************/
+
+int main()
+{
+    std::cerr << "******* Starting hsbp-manager *******\n";
+
+    /* Set the Dbus name */
+    conn->request_name(busName);
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> storageIface;
+
+    /* Add interface for storage inventory */
+    storageIface = objServer.add_interface(
+        "/xyz/openbmc_project/inventory/item/storage/hsbp/1",
+        "xyz.openbmc_project.Inventory.Item.Storage");
+
+    storageIface->initialize();
+
+    /* HSBP initializtion flow:
+     * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
+     * 2. Set up Dbus match for power - determine if host is up and running
+     *    or powered off
+     * 3. Set up Dbus match for HSBP backplanes and Drives
+     * 4. Load HSBP config exposed by entity manager
+     *    - Also setup a match to capture HSBP configuation in case
+     *      entity-manager restarts
+     * 5. Load Clock buffer and IO expander (and other peripherals if any
+     *    related to HSBP functionality)
+     *    - Reload the info each time HSBP configuration is changed
+     * 6. Populate all Backpanes (HSBP's)
+     * 7. Load all NVMe drive's and associate with HSBP Backpane
+     */
+
+    /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
+    if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
+                                    nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
+                                    nvmeLvc3AlertEvent))
+    {
+        std::cerr << __FUNCTION__
+                  << ": error: Unable to monitor events on HSBP "
+                     "Alert line\n";
+        return -1;
+    }
+
+    /* Setup Dbus-match for power */
+    setupPowerMatch(conn);
+
+    /* Setup Dbus-match for HSBP backplanes and Drives */
+    setupBackplanesAndDrivesMatch();
+
+    /* Setup HSBP Config match and load config
+     * In the event of entity-manager reboot, the match will help catch new
+     * configuration.
+     * In the event of hsbp-manager reboot, loadHsbpConfig will get all
+     * config details and will take care of remaining config's to be
+     * loaded
+     */
+    setupHsbpConfigMatch();
+    loadHsbpConfig();
+
+    io.run();
+    std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
+    return -1;
+}