hsbp-manager: Add drive interface to fru-less drives

Some drives don't have frus, but should still show up
in redfish. For this reason we need to populate the drive
interface when it is not available in entity-manager.

Tested: All drives showed up in redfish

Change-Id: If3bfa1e3e9c03146a77c896a5c983fbdaff3f4f5
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/hsbp-manager/src/hsbp_manager.cpp b/hsbp-manager/src/hsbp_manager.cpp
index 56469dd..5b34872 100644
--- a/hsbp-manager/src/hsbp_manager.cpp
+++ b/hsbp-manager/src/hsbp_manager.cpp
@@ -33,6 +33,7 @@
 
 constexpr const char* configType =
     "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
+constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
 
 constexpr size_t scanRateSeconds = 5;
 constexpr size_t maxDrives = 8; // only 1 byte alloted
@@ -86,6 +87,7 @@
         objServer.remove_interface(operationalIface);
         objServer.remove_interface(rebuildingIface);
         objServer.remove_interface(associationIface);
+        objServer.remove_interface(driveIface);
     }
 
     void createAssociation(const std::string& path)
@@ -103,10 +105,35 @@
         associationIface->initialize();
     }
 
+    void removeDriveIface()
+    {
+        objServer.remove_interface(driveIface);
+    }
+
+    void createDriveIface()
+    {
+        if (associationIface != nullptr || driveIface != nullptr)
+        {
+            // this shouldn't be used if we found another provider of the drive
+            // interface, or if we already created one
+            return;
+        }
+        driveIface =
+            objServer.add_interface(itemIface->get_object_path(),
+                                    "xyz.openbmc_project.Inventory.Item.Drive");
+        driveIface->initialize();
+        createAssociation(itemIface->get_object_path());
+    }
+
     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> associationIface;
+
+    // if something else doesn't expose a driveInterface for this, we need to
+    // export it ourselves
+    std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
+
     bool isNvme;
 };
 
@@ -407,6 +434,21 @@
 
 std::unordered_map<std::string, Backplane> backplanes;
 
+void createDriveInterfaces(size_t referenceCount)
+{
+    if (referenceCount != 1)
+    {
+        return;
+    }
+    for (auto& [name, backplane] : backplanes)
+    {
+        for (Drive& drive : backplane.drives)
+        {
+            drive.createDriveIface();
+        }
+    }
+}
+
 void updateAssociations()
 {
     constexpr const char* driveType =
@@ -427,15 +469,23 @@
                 }
 
                 const std::string& owner = objDict.begin()->first;
+                // we export this interface too
+                if (owner == busName)
+                {
+                    continue;
+                }
+                std::shared_ptr<bool> referenceCount = std::make_shared<bool>();
                 conn->async_method_call(
-                    [path](const boost::system::error_code ec2,
-                           const boost::container::flat_map<
-                               std::string, std::variant<uint64_t>>& values) {
+                    [path, referenceCount](
+                        const boost::system::error_code ec2,
+                        const boost::container::flat_map<
+                            std::string, std::variant<uint64_t>>& values) {
                         if (ec2)
                         {
                             std::cerr << "Error Getting Config "
                                       << ec2.message() << " " << __FUNCTION__
                                       << "\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
                         auto findBus = values.find("Bus");
@@ -446,6 +496,7 @@
                         {
                             std::cerr << "Illegal interface at " << path
                                       << "\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
 
@@ -459,6 +510,7 @@
                         if (!std::filesystem::is_symlink(muxPath))
                         {
                             std::cerr << path << " mux does not exist\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
 
@@ -472,6 +524,7 @@
                             findDash + 1 >= fname.size())
                         {
                             std::cerr << path << " mux path invalid\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
 
@@ -497,6 +550,7 @@
                         {
                             std::cerr << "Failed to find mux at bus " << bus
                                       << ", addr " << addr << "\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
                         if (parent->drives.size() <= driveIndex)
@@ -504,10 +558,13 @@
 
                             std::cerr << "Illegal drive index at " << path
                                       << " " << driveIndex << "\n";
+                            createDriveInterfaces(referenceCount.use_count());
                             return;
                         }
                         Drive& drive = parent->drives[driveIndex];
+                        drive.removeDriveIface();
                         drive.createAssociation(path);
+                        createDriveInterfaces(referenceCount.use_count());
                     },
                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
                     "xyz.openbmc_project.Inventory.Item.Drive");
@@ -679,7 +736,7 @@
 {
     boost::asio::steady_timer callbackTimer(io);
 
-    conn->request_name("xyz.openbmc_project.HsbpManager");
+    conn->request_name(busName);
 
     sdbusplus::bus::match::match match(
         *conn,
@@ -706,8 +763,12 @@
         *conn,
         "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
         "Inventory.Item.Drive'",
-        [&callbackTimer](sdbusplus::message::message&) {
+        [&callbackTimer](sdbusplus::message::message& message) {
             callbackTimer.expires_after(std::chrono::seconds(2));
+            if (message.get_sender() == conn->get_unique_name())
+            {
+                return;
+            }
             callbackTimer.async_wait([](const boost::system::error_code ec) {
                 if (ec == boost::asio::error::operation_aborted)
                 {