pseq: Add setPowerSupplyError D-Bus method

Add a setPowerSupplyError method to the org.openbmc.control.Power
D-Bus interface.  When a power supply error is detected which is severe
enough to cause a power good failure, that error should be used in
preference to the power good error. Add a D-Bus method to allow this to
be communicated between the applications.  The parameter passed should
be the power supply error to log, for example
"xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault".

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: I8500ace4638236dda2d7ff4361b61efa30a50ac1
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index aa47598..9e8b1b4 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -44,21 +44,17 @@
 PowerControl::PowerControl(sdbusplus::bus::bus& bus,
                            const sdeventplus::Event& event) :
     PowerObject{bus, POWER_OBJ_PATH, true},
-    bus{bus}, timer{event, std::bind(&PowerControl::pollPgood, this),
-                    pollInterval}
+    bus{bus}, match{bus,
+                    sdbusplus::bus::match::rules::interfacesAdded() +
+                        sdbusplus::bus::match::rules::sender(
+                            "xyz.openbmc_project.EntityManager"),
+                    std::bind(&PowerControl::interfacesAddedHandler, this,
+                              std::placeholders::_1)},
+    timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
 {
     // Obtain dbus service name
     bus.request_name(POWER_IFACE);
 
-    // Subscribe to D-Bus interfacesAdded signal from Entity Manager.  This
-    // notifies us if the interface becomes available later.
-    match = std::make_unique<sdbusplus::bus::match_t>(
-        bus,
-        sdbusplus::bus::match::rules::interfacesAdded() +
-            sdbusplus::bus::match::rules::sender(
-                "xyz.openbmc_project.EntityManager"),
-        std::bind(&PowerControl::interfacesAddedHandler, this,
-                  std::placeholders::_1));
     setUpDevice();
     setUpGpio();
 }
@@ -100,6 +96,7 @@
                 .c_str());
         // Create device object
         device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
+        deviceFound = true;
     }
 }
 
@@ -120,8 +117,8 @@
 
 void PowerControl::interfacesAddedHandler(sdbusplus::message::message& msg)
 {
-    // Verify message is valid
-    if (!msg)
+    // Only continue if message is valid and device has not already been found
+    if (!msg || deviceFound)
     {
         return;
     }
@@ -236,6 +233,11 @@
     }
 }
 
+void PowerControl::setPowerSupplyError(const std::string& error)
+{
+    powerSupplyError = error;
+}
+
 void PowerControl::setState(int s)
 {
     if (state == s)
diff --git a/phosphor-power-sequencer/src/power_control.hpp b/phosphor-power-sequencer/src/power_control.hpp
index 62dfce2..1bac68c 100644
--- a/phosphor-power-sequencer/src/power_control.hpp
+++ b/phosphor-power-sequencer/src/power_control.hpp
@@ -63,6 +63,9 @@
     /** @copydoc PowerInterface::setState() */
     void setState(int state) override;
 
+    /** @copydoc PowerInterface::setPowerSupplyError() */
+    void setPowerSupplyError(const std::string& error) override;
+
   private:
     /**
      * The D-Bus bus object
@@ -72,7 +75,13 @@
     /**
      * The power sequencer device to monitor.
      */
-    std::unique_ptr<PowerSequencerMonitor> device;
+    std::unique_ptr<PowerSequencerMonitor> device{
+        std::make_unique<PowerSequencerMonitor>()};
+
+    /**
+     * Indicates if a specific power sequencer device has already been found.
+     */
+    bool deviceFound{false};
 
     /**
      * Indicates if a state transistion is taking place
@@ -82,7 +91,7 @@
     /**
      * The match to Entity Manager interfaces added.
      */
-    std::unique_ptr<sdbusplus::bus::match_t> match;
+    sdbusplus::bus::match_t match;
 
     /**
      * Power good
@@ -117,6 +126,11 @@
     gpiod::line powerControlLine;
 
     /**
+     * Power supply error
+     */
+    std::string powerSupplyError;
+
+    /**
      * Power state
      */
     int state{0};
diff --git a/phosphor-power-sequencer/src/power_interface.cpp b/phosphor-power-sequencer/src/power_interface.cpp
index 50f5911..d266ee1 100644
--- a/phosphor-power-sequencer/src/power_interface.cpp
+++ b/phosphor-power-sequencer/src/power_interface.cpp
@@ -151,10 +151,10 @@
 
             int timeout{};
             m.read(timeout);
-
-            auto pwrObj = static_cast<PowerInterface*>(context);
             log<level::INFO>(
                 fmt::format("callbackSetPgoodTimeout: {}", timeout).c_str());
+
+            auto pwrObj = static_cast<PowerInterface*>(context);
             pwrObj->setPgoodTimeout(timeout);
         }
         catch (const sdbusplus::exception_t& e)
@@ -223,10 +223,10 @@
                                         "org.openbmc.ControlPower.Error.Failed",
                                         "Invalid power state");
             }
-
-            auto pwrObj = static_cast<PowerInterface*>(context);
             log<level::INFO>(
                 fmt::format("callbackSetPowerState: {}", state).c_str());
+
+            auto pwrObj = static_cast<PowerInterface*>(context);
             pwrObj->setState(state);
 
             m.new_method_return().method_return();
@@ -246,6 +246,43 @@
     return 1;
 }
 
+int PowerInterface::callbackSetPowerSupplyError(sd_bus_message* msg,
+                                                void* context,
+                                                sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto m = sdbusplus::message::message(msg);
+
+            std::string psError{};
+            m.read(psError);
+            log<level::INFO>(
+                fmt::format("callbackSetPowerSupplyError: {}", psError)
+                    .c_str());
+
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            pwrObj->setPowerSupplyError(psError);
+
+            m.new_method_return().method_return();
+        }
+        catch (const sdbusplus::exception_t& e)
+        {
+            return sd_bus_error_set(error, e.name(), e.description());
+        }
+    }
+    else
+    {
+        // The message or context were null
+        log<level::ERR>(
+            "Unable to service setPowerSupplyError method callback");
+        return -1;
+    }
+
+    return 1;
+}
+
 void PowerInterface::emitPowerGoodSignal()
 {
     log<level::INFO>("emitPowerGoodSignal");
@@ -286,6 +323,9 @@
     sdbusplus::vtable::property("pgood_timeout", "i", callbackGetPgoodTimeout,
                                 callbackSetPgoodTimeout,
                                 sdbusplus::vtable::property_::emits_change),
+    // Method setPowerSupplyError takes a string parameter and returns void
+    sdbusplus::vtable::method("setPowerSupplyError", "s", "",
+                              callbackSetPowerSupplyError),
     sdbusplus::vtable::end()};
 
 } // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/power_interface.hpp b/phosphor-power-sequencer/src/power_interface.hpp
index d189120..0a3dae0 100644
--- a/phosphor-power-sequencer/src/power_interface.hpp
+++ b/phosphor-power-sequencer/src/power_interface.hpp
@@ -79,6 +79,16 @@
      */
     virtual void setState(int state) = 0;
 
+    /**
+     * Sets the power supply error. The error should be of great enough severity
+     * that a power good failure may occur and will be issued in preference to
+     * the power good error.
+     * @param[in] error power supply error. The value should be a message
+     * argument for a phosphor-logging Create call, e.g.
+     * "xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault"
+     */
+    virtual void setPowerSupplyError(const std::string& error) = 0;
+
   private:
     /**
      * Holder for the instance of this interface to be on dbus
@@ -133,6 +143,12 @@
                                        sd_bus_error* error);
 
     /**
+     * Systemd bus callback for the setPowerSupplyError method
+     */
+    static int callbackSetPowerSupplyError(sd_bus_message* msg, void* context,
+                                           sd_bus_error* error);
+
+    /**
      * Systemd bus callback for the setPowerState method
      */
     static int callbackSetPowerState(sd_bus_message* msg, void* context,