common: add entity manager interface

Define an EntityManager interface to fetch the serial port related
config or any device config from Entity Manager.

Change-Id: Ieeff4422903e3b4bcc3ce28cd493eecab5618f13
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/common/entity_manager_interface.cpp b/common/entity_manager_interface.cpp
new file mode 100644
index 0000000..49b767a
--- /dev/null
+++ b/common/entity_manager_interface.cpp
@@ -0,0 +1,133 @@
+#include "entity_manager_interface.hpp"
+
+#include <boost/container/flat_map.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/message/native_types.hpp>
+#include <xyz/openbmc_project/Inventory/Item/client.hpp>
+
+#include <algorithm>
+#include <utility>
+
+namespace entity_manager
+{
+
+PHOSPHOR_LOG2_USING;
+
+namespace rules_intf = sdbusplus::bus::match::rules;
+
+using BasicVariantType =
+    std::variant<std::vector<std::string>, std::vector<uint8_t>, std::string,
+                 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
+                 uint16_t, uint8_t, bool>;
+using BaseConfigMap = boost::container::flat_map<std::string, BasicVariantType>;
+using ConfigData = boost::container::flat_map<std::string, BaseConfigMap>;
+using ManagedObjectType =
+    boost::container::flat_map<sdbusplus::message::object_path, ConfigData>;
+
+EntityManagerInterface::EntityManagerInterface(
+    sdbusplus::async::context& ctx, const interface_list_t& interfaceNames,
+    Callback_t addedCallback, Callback_t removedCallback) :
+    ctx(ctx), interfaceNames(interfaceNames),
+    addedCallback(std::move(addedCallback)),
+    removedCallback(std::move(removedCallback))
+{
+    ctx.spawn(handleInventoryAdded());
+    ctx.spawn(handleInventoryRemoved());
+}
+
+auto EntityManagerInterface::handleInventoryGet() -> sdbusplus::async::task<>
+{
+    if (!addedCallback)
+    {
+        error("addedCallback is not set");
+        co_return;
+    }
+
+    using InventoryIntf =
+        sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
+
+    constexpr auto entityManager =
+        sdbusplus::async::proxy()
+            .service(serviceName)
+            .path(InventoryIntf::namespace_path)
+            .interface("org.freedesktop.DBus.ObjectManager");
+
+    for (const auto& [objectPath, interfaceConfig] :
+         co_await entityManager.call<ManagedObjectType>(ctx,
+                                                        "GetManagedObjects"))
+    {
+        for (const auto& interfaceName : interfaceNames)
+        {
+            if (interfaceConfig.contains(interfaceName))
+            {
+                co_await addedCallback(objectPath, interfaceName);
+            }
+        }
+    }
+
+    co_return;
+}
+
+auto EntityManagerInterface::handleInventoryAdded() -> sdbusplus::async::task<>
+{
+    if (!addedCallback)
+    {
+        error("addedCallback is not set");
+        co_return;
+    }
+
+    auto addedMatch = sdbusplus::async::match(
+        ctx, rules_intf::interfacesAdded() + rules_intf::sender(serviceName));
+
+    while (!ctx.stop_requested())
+    {
+        auto result = co_await addedMatch
+                          .next<sdbusplus::message::object_path, ConfigData>();
+        auto& [objectPath, inventoryData] = result;
+
+        for (const auto& interfaceName : interfaceNames)
+        {
+            if (inventoryData.contains(interfaceName))
+            {
+                co_await addedCallback(objectPath, interfaceName);
+            }
+        }
+    }
+
+    co_return;
+}
+
+auto EntityManagerInterface::handleInventoryRemoved()
+    -> sdbusplus::async::task<>
+{
+    if (!removedCallback)
+    {
+        error("removedCallback is not set");
+        co_return;
+    }
+
+    auto removedMatch = sdbusplus::async::match(
+        ctx, rules_intf::interfacesRemoved() + rules_intf::sender(serviceName));
+
+    while (!ctx.stop_requested())
+    {
+        auto result =
+            co_await removedMatch
+                .next<sdbusplus::message::object_path, interface_list_t>();
+        auto& [objectPath, interfaces] = result;
+
+        for (const auto& interfaceName : interfaceNames)
+        {
+            if (std::ranges::find(interfaces, interfaceName) !=
+                interfaces.end())
+            {
+                co_await removedCallback(objectPath, interfaceName);
+            }
+        }
+    }
+
+    co_return;
+}
+
+} // namespace entity_manager
diff --git a/common/entity_manager_interface.hpp b/common/entity_manager_interface.hpp
new file mode 100644
index 0000000..86e1ba9
--- /dev/null
+++ b/common/entity_manager_interface.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/message/native_types.hpp>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace entity_manager
+{
+
+using interface_list_t = std::vector<std::string>;
+
+class EntityManagerInterface
+{
+  public:
+    using Callback_t = std::function<sdbusplus::async::task<>(
+        const sdbusplus::message::object_path&, const std::string&)>;
+    static constexpr auto serviceName = "xyz.openbmc_project.EntityManager";
+
+    EntityManagerInterface() = delete;
+
+    explicit EntityManagerInterface(
+        sdbusplus::async::context& ctx, const interface_list_t& interfaceNames,
+        Callback_t addedCallback, Callback_t removedCallback);
+
+    /** Get the inventory info from Entity Manager */
+    auto handleInventoryGet() -> sdbusplus::async::task<>;
+
+  private:
+    /** @brief Handle async inventory add from Entity Manager */
+    auto handleInventoryAdded() -> sdbusplus::async::task<>;
+
+    /** @brief Handle async inventory remove from Entity Manager */
+    auto handleInventoryRemoved() -> sdbusplus::async::task<>;
+
+    sdbusplus::async::context& ctx;
+    interface_list_t interfaceNames;
+    Callback_t addedCallback;
+    Callback_t removedCallback;
+};
+
+} // namespace entity_manager
diff --git a/common/meson.build b/common/meson.build
index 8b13789..cd17e78 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1 +1,6 @@
-
+modbus_common_lib = static_library(
+    'modbus_common_lib',
+    'entity_manager_interface.cpp',
+    include_directories: ['.'],
+    dependencies: [default_deps],
+)
diff --git a/meson.build b/meson.build
index cbc46df..643b7ec 100644
--- a/meson.build
+++ b/meson.build
@@ -17,6 +17,8 @@
     sdbusplus_dep,
 ]
 
+common_include = include_directories('.')
+
 subdir('common')
 
 if get_option('modbus-rtu').allowed()