psu-ng: Add in ability to get presence via GPIO

The device tree and entity-manager (D-Bus) properties have the power
supply GPIO presence lines named, so we can use those GPIO line names to
find the GPIO device to read for determining power supply presence.

Some systems have the power supply presence lines in a gpio-keys section
(notably IBM Rainier). To facilitate continued function while running
with a device tree that has not removed those device tree entries, allow
for a fallback to the old inventory D-Bus propertiesChanged or
interfaceAdded matches for presence.

Change-Id: I5002aa62e5b460463cc26328c889a3786b467a3c
Signed-off-by: B. J. Wyman <bjwyman@gmail.com>
diff --git a/phosphor-power-supply/util.hpp b/phosphor-power-supply/util.hpp
index 2e4afe2..2203b92 100644
--- a/phosphor-power-supply/util.hpp
+++ b/phosphor-power-supply/util.hpp
@@ -1,7 +1,14 @@
 #pragma once
-
 #include "util_base.hpp"
 #include "utility.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <fmt/format.h>
+
+#include <gpiod.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
 
 namespace phosphor::power::psu
 {
@@ -9,7 +16,6 @@
 class Util : public UtilBase
 {
   public:
-    //~Util(){};
     bool getPresence(sdbusplus::bus::bus& bus,
                      const std::string& invpath) const override
     {
@@ -21,6 +27,102 @@
 
         return present;
     }
+
+    void setPresence(sdbusplus::bus::bus& bus, const std::string& invpath,
+                     bool present, const std::string& name) const override
+    {
+        using InternalFailure =
+            sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+        using Property = std::string;
+        using Value = std::variant<bool, std::string>;
+        // Association between property and its value
+        using PropertyMap = std::map<Property, Value>;
+        PropertyMap invProp;
+
+        invProp.emplace("Present", present);
+        invProp.emplace("PrettyName", name);
+
+        using Interface = std::string;
+        // Association between interface and the D-Bus property map
+        using InterfaceMap = std::map<Interface, PropertyMap>;
+        InterfaceMap invIntf;
+        invIntf.emplace("xyz.openbmc_project.Inventory.Item",
+                        std::move(invProp));
+
+        Interface extraIface = "xyz.openbmc_project.Inventory.Item.PowerSupply";
+
+        invIntf.emplace(extraIface, PropertyMap());
+
+        using Object = sdbusplus::message::object_path;
+        // Association between object and the interface map
+        using ObjectMap = std::map<Object, InterfaceMap>;
+        ObjectMap invObj;
+        invObj.emplace(std::move(invpath), std::move(invIntf));
+
+        using namespace phosphor::logging;
+        log<level::INFO>(fmt::format("Updating inventory present property. "
+                                     "present:{} invpath:{} name:{}",
+                                     present, invpath, name)
+                             .c_str());
+
+        try
+        {
+            auto invService = phosphor::power::util::getService(
+                INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
+
+            // Update inventory
+            auto invMsg =
+                bus.new_method_call(invService.c_str(), INVENTORY_OBJ_PATH,
+                                    INVENTORY_MGR_IFACE, "Notify");
+            invMsg.append(std::move(invObj));
+            auto invMgrResponseMsg = bus.call(invMsg);
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>(
+                fmt::format(
+                    "Error in inventory manager call to update inventory: {}",
+                    e.what())
+                    .c_str());
+            elog<InternalFailure>();
+        }
+    }
+};
+
+std::unique_ptr<GPIOInterface> createGPIO(const std::string& namedGpio);
+
+class GPIOReader : public GPIOInterface
+{
+  public:
+    GPIOReader() = delete;
+    virtual ~GPIOReader() = default;
+    GPIOReader(const GPIOReader&) = default;
+    GPIOReader& operator=(const GPIOReader&) = default;
+    GPIOReader(GPIOReader&&) = default;
+    GPIOReader& operator=(GPIOReader&&) = default;
+
+    /**
+     * Constructor
+     *
+     * @param[in] namedGpio - The string for the gpio-line-name
+     */
+    GPIOReader(const std::string& namedGpio);
+
+    static std::unique_ptr<GPIOInterface>
+        createGPIO(const std::string& namedGpio);
+
+    /**
+     * @brief Attempts to read the state of the GPIO line.
+     *
+     * Throws an exception if line not found, request line fails, or get_value
+     * from line fails.
+     *
+     * @return 1 for active (low/present), 0 for not active (high/not present).
+     */
+    int read() override;
+
+  private:
+    gpiod::line line;
 };
 
 } // namespace phosphor::power::psu