Add multi-host postcode support

These changes add the multiple host postcode implementation.
In addition to the LPC snoop the postcode can be read via D-Bus
interface.The same is displayed in seven segment display thorough
GPIO lines.

The design can be refered at
   https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/35065

Tested:

 Verified in Facebook Tiogapass & Yosemitev2 platforms.

1.The postcode displayed in the seven segment corresponding to the
  host position selected in the frontpanel of Facebook
  yosemitev2(4 host).

2.The postcode history is verified in the respective host directory
     "/var/lib/phosphor-post-code-manager/hostX(1,2,3,4)".

Signed-off-by: Kumar Thangavel <thangavel.k@hcl.com>
Change-Id: Id79ad00652bfedd319d3b8ccf1aafbdc60b33d86
diff --git a/ipmisnoop/ipmisnoop.hpp b/ipmisnoop/ipmisnoop.hpp
new file mode 100644
index 0000000..efc5378
--- /dev/null
+++ b/ipmisnoop/ipmisnoop.hpp
@@ -0,0 +1,190 @@
+#pragma once
+
+#include "lpcsnoop/snoop.hpp"
+
+#include <boost/asio.hpp>
+#include <filesystem>
+#include <gpiod.hpp>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/property.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp>
+#include <xyz/openbmc_project/State/Boot/Raw/server.hpp>
+
+const std::string ipmiSnoopObject = "/xyz/openbmc_project/state/boot/raw";
+
+const int hostParseIdx = 3;
+const int maxPostcode = 255;
+const int maxPosition = 4;
+
+std::vector<gpiod::line> led_lines;
+
+using Selector =
+    sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::HostSelector;
+
+std::unique_ptr<sdbusplus::bus::match_t> matchSignal;
+
+const std::string selectorService = "xyz.openbmc_project.Chassis.Buttons";
+const std::string selectorObject =
+    "/xyz/openbmc_project/Chassis/Buttons/HostSelector";
+const std::string selectorIface =
+    "xyz.openbmc_project.Chassis.Buttons.HostSelector";
+
+const std::string rawObject = "/xyz/openbmc_project/state/boot";
+const std::string rawIface = "xyz.openbmc_project.State.Boot.Raw";
+const std::string rawService = "xyz.openbmc_project.State.Boot.Raw";
+
+uint32_t getSelectorPosition(sdbusplus::bus::bus& bus)
+{
+    const std::string propertyName = "Position";
+
+    auto method =
+        bus.new_method_call(selectorService.c_str(), selectorObject.c_str(),
+                            "org.freedesktop.DBus.Properties", "Get");
+    method.append(selectorIface.c_str(), propertyName);
+
+    try
+    {
+        std::variant<uint32_t> value{};
+        auto reply = bus.call(method);
+        reply.read(value);
+        return std::get<uint32_t>(value);
+    }
+    catch (const sdbusplus::exception::exception& ex)
+    {
+        std::cerr << "GetProperty call failed ";
+        throw std::runtime_error("GetProperty call failed");
+    }
+}
+
+struct IpmiPostReporter : PostObject
+{
+    IpmiPostReporter(sdbusplus::bus::bus& bus, const char* objPath) :
+        PostObject(bus, objPath), bus(bus),
+        propertiesChangedSignalRaw(
+            bus,
+            sdbusplus::bus::match::rules::propertiesChanged(objPath, rawIface),
+
+            [this, &bus](sdbusplus::message::message& msg) {
+                using primarycode_t = uint64_t;
+                using secondarycode_t = std::vector<uint8_t>;
+                using postcode_t = std::tuple<primarycode_t, secondarycode_t>;
+
+                std::string objectName;
+                std::string InterfaceName;
+                std::map<std::string, std::variant<postcode_t>> msgData;
+                msg.read(InterfaceName, msgData);
+
+                std::filesystem::path name(msg.get_path());
+                objectName = name.filename();
+
+                std::string hostNumStr = objectName.substr(hostParseIdx);
+                size_t hostNum = std::stoi(hostNumStr);
+
+                size_t position = getSelectorPosition(bus);
+
+                if (position > maxPosition)
+                {
+                    std::cerr << "Invalid position. Position should be 1 to 4 "
+                                 "for all hosts "
+                              << std::endl;
+                }
+
+                // Check if it was the Value property that changed.
+                auto valPropMap = msgData.find("Value");
+                if (valPropMap == msgData.end())
+                {
+                    std::cerr << "Value property is not found " << std::endl;
+                    return;
+                }
+                uint64_t postcode =
+                    std::get<0>(std::get<postcode_t>(valPropMap->second));
+
+                if (postcode <= maxPostcode)
+                {
+                    if (position == hostNum)
+                    {
+                        uint8_t postcode_8bit =
+                            static_cast<uint8_t>(postcode & 0x0000FF);
+
+                        // write postcode into seven segment display
+                        if (postCodeDisplay(postcode_8bit) < 0)
+                        {
+                            fprintf(stderr, "Error in display the postcode\n");
+                        }
+                    }
+                    else
+                    {
+                        fprintf(stderr, "Host Selector Position and host "
+                                        "number is not matched..\n");
+                    }
+                }
+                else
+                {
+                    fprintf(stderr, "invalid postcode value \n");
+                }
+            })
+    {
+    }
+
+    sdbusplus::bus::bus& bus;
+    sdbusplus::bus::match_t propertiesChangedSignalRaw;
+    int postCodeDisplay(uint8_t);
+    void getSelectorPositionSignal(sdbusplus::bus::bus& bus);
+};
+
+// Configure the seven segment display connected GPIOs direction
+int configGPIODirOutput()
+{
+    std::string gpioStr;
+    // Need to define gpio names LED_POST_CODE_0 to 8 in dts file
+    std::string gpioName = "LED_POST_CODE_";
+    const int value = 0;
+
+    for (int iteration = 0; iteration < 8; iteration++)
+    {
+        gpioStr = gpioName + std::to_string(iteration);
+        gpiod::line gpioLine = gpiod::find_line(gpioStr);
+
+        if (!gpioLine)
+        {
+            std::string errMsg = "Failed to find the " + gpioStr + " line";
+            std::cerr << errMsg.c_str() << std::endl;
+            return -1;
+        }
+
+        led_lines.push_back(gpioLine);
+        // Request GPIO output to specified value
+        try
+        {
+            gpioLine.request({__FUNCTION__,
+                              gpiod::line_request::DIRECTION_OUTPUT,
+                              gpiod::line_request::FLAG_ACTIVE_LOW},
+                             value);
+        }
+        catch (std::exception&)
+        {
+            std::string errMsg = "Failed to request " + gpioStr + " output";
+            std::cerr << errMsg.c_str() << std::endl;
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+// Display the received postcode into seven segment display
+int IpmiPostReporter::postCodeDisplay(uint8_t status)
+{
+    for (int iteration = 0; iteration < 8; iteration++)
+    {
+        // split byte to write into GPIOs
+        int value = !((status >> iteration) & 0x01);
+
+        led_lines[iteration].set_value(value);
+    }
+    return 0;
+}