Add HSBP Manager Daemon

This is the initial commit for the hsbp-manager
daemon. Currently it reads a json configuration
and detects whether the cpld is available. More
feaures will be added in small patches.

Tested:

busctl introspect --no-pager xyz.openbmc_project.HsbpManager /xyz/openbmc_project/inventory/item/hsbp/J85894_HSBP_1
NAME                                TYPE      SIGNATURE RESULT/VALUE    FLAGS
org.freedesktop.DBus.Introspectable interface -         -               -
.Introspect                         method    -         s               -
org.freedesktop.DBus.Peer           interface -         -               -
.GetMachineId                       method    -         s               -
.Ping                               method    -         -               -
org.freedesktop.DBus.Properties     interface -         -               -
.Get                                method    ss        v               -
.GetAll                             method    s         a{sv}           -
.Set                                method    ssv       -               -
.PropertiesChanged                  signal    sa{sv}as  -               -
xyz.openbmc_project.Inventory.Item  interface -         -               -
.Present                            property  b         true            emits-change
.PrettyName                         property  s         "J85894 HSBP 1" emits-change

Change-Id: Ibbce272e045f8643af74b778f273168442623919
Signed-off-by: Feist, James <james.feist@linux.intel.com>
diff --git a/hsbp-manager/src/hsbp_manager.cpp b/hsbp-manager/src/hsbp_manager.cpp
new file mode 100644
index 0000000..110d32d
--- /dev/null
+++ b/hsbp-manager/src/hsbp_manager.cpp
@@ -0,0 +1,201 @@
+/*
+// 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/steady_timer.hpp>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <string>
+
+extern "C" {
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+}
+
+constexpr const char* configType =
+    "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
+
+boost::asio::io_context io;
+auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+sdbusplus::asio::object_server objServer(conn);
+
+struct Backplane
+{
+
+    Backplane(size_t busIn, size_t addressIn, const std::string& nameIn) :
+        bus(busIn), address(addressIn), name(nameIn)
+    {
+    }
+
+    void run()
+    {
+        file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), O_RDWR);
+        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;
+        }
+
+        hsbpItemIface = objServer.add_interface(
+            "/xyz/openbmc_project/inventory/item/hsbp/" +
+                boost::replace_all_copy(name, " ", "_"),
+            inventory::interface);
+        hsbpItemIface->register_property("Present", present(true));
+        hsbpItemIface->register_property("PrettyName", name);
+        hsbpItemIface->initialize();
+
+        if (!present())
+        {
+            // backplane isn't there
+            return;
+        }
+    }
+
+    bool present(bool update = false)
+    {
+        static bool present = false;
+        if (update)
+        {
+            present = i2c_smbus_read_byte(file) >= 0;
+        }
+        return present;
+    }
+    ~Backplane()
+    {
+        objServer.remove_interface(hsbpItemIface);
+        if (file >= 0)
+        {
+            close(file);
+        }
+    }
+
+    size_t bus;
+    size_t address;
+    int file = -1;
+    std::string name;
+    std::string type;
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
+    std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>
+        driveItemIfaces;
+};
+
+std::unordered_map<std::string, Backplane> backplanes;
+
+void populate()
+{
+    conn->async_method_call(
+        [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
+            if (ec)
+            {
+                std::cerr << "Error contacting mapper " << ec.message() << "\n";
+                return;
+            }
+            for (const auto& [path, objDict] : subtree)
+            {
+                if (objDict.empty())
+                {
+                    continue;
+                }
+
+                const std::string& owner = objDict.begin()->first;
+                conn->async_method_call(
+                    [path](const boost::system::error_code ec2,
+                           const boost::container::flat_map<
+                               std::string, BasicVariantType>& resp) {
+                        if (ec2)
+                        {
+                            std::cerr << "Error Getting Config "
+                                      << ec2.message() << "\n";
+                            return;
+                        }
+                        backplanes.clear();
+                        std::optional<size_t> bus;
+                        std::optional<size_t> address;
+                        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 == "Name")
+                            {
+                                name = std::get<std::string>(value);
+                            }
+                        }
+                        if (!bus || !address || !name)
+                        {
+                            std::cerr << "Illegal configuration at " << path
+                                      << "\n";
+                            return;
+                        }
+                        const auto& [backplane, status] = backplanes.emplace(
+                            *name, Backplane(*bus, *address, *name));
+                        backplane->second.run();
+                    },
+                    owner, path, "org.freedesktop.DBus.Properties", "GetAll",
+                    configType);
+            }
+        },
+        mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
+        0, std::array<const char*, 1>{configType});
+}
+
+int main()
+{
+    boost::asio::steady_timer callbackTimer(io);
+
+    conn->request_name("xyz.openbmc_project.HsbpManager");
+
+    sdbusplus::bus::match::match match(
+        *conn,
+        "type='signal',member='PropertiesChanged',arg0='" +
+            std::string(configType) + "'",
+        [&callbackTimer](sdbusplus::message::message&) {
+            callbackTimer.expires_after(std::chrono::seconds(2));
+            callbackTimer.async_wait([](const boost::system::error_code ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    // timer was restarted
+                    return;
+                }
+                else if (ec)
+                {
+                    std::cerr << "Timer error" << ec.message() << "\n";
+                    return;
+                }
+                populate();
+            });
+        });
+
+    io.post([]() { populate(); });
+    io.run();
+}