pseq: Add power control dbus server interface

Add the PowerInterface class which provides a server interface for the
"org.openbmc.control.Power" dbus service.

Update meson.build file to support.

The "org.openbmc.control.Power" interface is currently used by the
Chassis component to control power on and off as well as by a number of
other OBMC applications which use the pgood property and associated
signals to determine the power state of the system.  Some desire to
move to a "xyz.openbmc_project.control.Power" interface has been
expressed.  The following is a proposed plan to accomplish this:
1. Create a compatible "org.openbmc.control.Power" application (this
   commit).
2. Add "xyz..." interface to this application (future commit).
3. Begin transition of existing applications to new interface (future
   commits).

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ia9a6bbb58b5e439623d3931426cb91cb8948da7a
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index 6b42151..f3e0d26 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -7,6 +7,7 @@
     'phosphor-power-control',
     'power_control_main.cpp',
     'power_control.cpp',
+    'power_interface.cpp',
     dependencies: [
         phosphor_logging,
         sdbusplus,
diff --git a/phosphor-power-sequencer/src/power_interface.cpp b/phosphor-power-sequencer/src/power_interface.cpp
new file mode 100644
index 0000000..ed537db
--- /dev/null
+++ b/phosphor-power-sequencer/src/power_interface.cpp
@@ -0,0 +1,292 @@
+/**
+ * Copyright © 2021 IBM 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 "power_interface.hpp"
+
+#include "types.hpp"
+
+#include <fmt/format.h>
+
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/exception.hpp>
+#include <sdbusplus/sdbus.hpp>
+#include <sdbusplus/server.hpp>
+
+#include <string>
+#include <tuple>
+
+using namespace phosphor::logging;
+
+namespace phosphor::power::sequencer
+{
+
+PowerInterface::PowerInterface(sdbusplus::bus::bus& bus, const char* path) :
+    _serverInterface(bus, path, POWER_IFACE, _vtable, this)
+{
+}
+
+int PowerInterface::callbackGetPgood(sd_bus* /*bus*/, const char* /*path*/,
+                                     const char* /*interface*/,
+                                     const char* /*property*/,
+                                     sd_bus_message* msg, void* context,
+                                     sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            int pgood = pwrObj->getPgood();
+            log<level::INFO>(
+                fmt::format("callbackGetPgood: {}", pgood).c_str());
+
+            sdbusplus::message::message(msg).append(pgood);
+        }
+        catch (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 get pgood property callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+int PowerInterface::callbackGetPgoodTimeout(sd_bus* /*bus*/,
+                                            const char* /*path*/,
+                                            const char* /*interface*/,
+                                            const char* /*property*/,
+                                            sd_bus_message* msg, void* context,
+                                            sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            int timeout = pwrObj->getPgoodTimeout();
+            log<level::INFO>(
+                fmt::format("callbackGetPgoodTimeout: {}", timeout).c_str());
+
+            sdbusplus::message::message(msg).append(timeout);
+        }
+        catch (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 get pgood timeout property callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+int PowerInterface::callbackGetPowerState(sd_bus_message* msg, void* context,
+                                          sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            // Return the current power state of the GPIO, rather than the last
+            // requested power state change
+            int pgood = pwrObj->getPgood();
+            log<level::INFO>(
+                fmt::format("callbackGetPowerState: {}", pgood).c_str());
+
+            auto reply = sdbusplus::message::message(msg).new_method_return();
+            reply.append(pgood);
+            reply.method_return();
+        }
+        catch (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 getPowerState method callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+int PowerInterface::callbackSetPgoodTimeout(sd_bus* /*bus*/,
+                                            const char* /*path*/,
+                                            const char* /*interface*/,
+                                            const char* /*property*/,
+                                            sd_bus_message* msg, void* context,
+                                            sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto m = sdbusplus::message::message(msg);
+
+            int timeout{};
+            m.read(timeout);
+
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            log<level::INFO>(
+                fmt::format("callbackSetPgoodTimeout: {}", timeout).c_str());
+            pwrObj->setPgoodTimeout(timeout);
+        }
+        catch (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 set pgood timeout property callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+int PowerInterface::callbackGetState(sd_bus* /*bus*/, const char* /*path*/,
+                                     const char* /*interface*/,
+                                     const char* /*property*/,
+                                     sd_bus_message* msg, void* context,
+                                     sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            int state = pwrObj->getState();
+            log<level::INFO>(
+                fmt::format("callbackGetState: {}", state).c_str());
+
+            sdbusplus::message::message(msg).append(state);
+        }
+        catch (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 get state property callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+int PowerInterface::callbackSetPowerState(sd_bus_message* msg, void* context,
+                                          sd_bus_error* error)
+{
+    if (msg != nullptr && context != nullptr)
+    {
+        try
+        {
+            auto m = sdbusplus::message::message(msg);
+
+            int state{};
+            m.read(state);
+
+            if (state != 1 && state != 0)
+            {
+                return sd_bus_error_set(error,
+                                        "org.openbmc.ControlPower.Error.Failed",
+                                        "Invalid power state");
+            }
+
+            auto pwrObj = static_cast<PowerInterface*>(context);
+            log<level::INFO>(
+                fmt::format("callbackSetPowerState: {}", state).c_str());
+            pwrObj->setState(state);
+
+            m.new_method_return().method_return();
+        }
+        catch (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 setPowerState method callback");
+        return -1;
+    }
+
+    return 1;
+}
+
+void PowerInterface::emitPowerGoodSignal()
+{
+    log<level::INFO>("emitPowerGoodSignal");
+    _serverInterface.new_signal("PowerGood").signal_send();
+}
+
+void PowerInterface::emitPowerLostSignal()
+{
+    log<level::INFO>("emitPowerLostSignal");
+    _serverInterface.new_signal("PowerLost").signal_send();
+}
+
+void PowerInterface::emitPropertyChangedSignal(const char* property)
+{
+    log<level::INFO>(
+        fmt::format("emitPropertyChangedSignal: {}", property).c_str());
+    _serverInterface.property_changed(property);
+}
+
+const sdbusplus::vtable::vtable_t PowerInterface::_vtable[] = {
+    sdbusplus::vtable::start(),
+    // Method setPowerState takes an int parameter and returns void
+    sdbusplus::vtable::method("setPowerState", "i", "", callbackSetPowerState),
+    // Method getPowerState takes no parameters and returns int
+    sdbusplus::vtable::method("getPowerState", "", "i", callbackGetPowerState),
+    // Signal PowerGood
+    sdbusplus::vtable::signal("PowerGood", ""),
+    // Signal PowerLost
+    sdbusplus::vtable::signal("PowerLost", ""),
+    // Property pgood is type int, read only, and uses the emits_change flag
+    sdbusplus::vtable::property("pgood", "i", callbackGetPgood,
+                                sdbusplus::vtable::property_::emits_change),
+    // Property state is type int, read only, and uses the emits_change flag
+    sdbusplus::vtable::property("state", "i", callbackGetState,
+                                sdbusplus::vtable::property_::emits_change),
+    // Property pgood_timeout is type int, read write, and uses the emits_change
+    // flag
+    sdbusplus::vtable::property("pgood_timeout", "i", callbackGetPgoodTimeout,
+                                callbackSetPgoodTimeout,
+                                sdbusplus::vtable::property_::emits_change),
+    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
new file mode 100644
index 0000000..76608ff
--- /dev/null
+++ b/phosphor-power-sequencer/src/power_interface.hpp
@@ -0,0 +1,142 @@
+#pragma once
+
+#include <systemd/sd-bus.h>
+
+#include <sdbusplus/sdbus.hpp>
+#include <sdbusplus/server/interface.hpp>
+#include <sdbusplus/vtable.hpp>
+
+#include <string>
+
+namespace phosphor::power::sequencer
+{
+
+/**
+ * @class PowerControl
+ * This class provides the org.openbmc.control.Power D-Bus interface.
+ */
+class PowerInterface
+{
+  public:
+    PowerInterface() = delete;
+    PowerInterface(const PowerInterface&) = delete;
+    PowerInterface& operator=(const PowerInterface&) = delete;
+    PowerInterface(PowerInterface&&) = delete;
+    PowerInterface& operator=(PowerInterface&&) = delete;
+    virtual ~PowerInterface() = default;
+
+    /**
+     * @brief Constructor to put object onto bus at a dbus path.
+     * @param[in] bus D-Bus bus object
+     * @param[in] path D-Bus object path
+     */
+    PowerInterface(sdbusplus::bus::bus& bus, const char* path);
+
+    /**
+     * Emit the power good signal
+     */
+    void emitPowerGoodSignal();
+
+    /**
+     * Emit the power lost signal
+     */
+    void emitPowerLostSignal();
+
+    /**
+     * Emit the property changed signal
+     * @param[in] property the property that changed
+     */
+    void emitPropertyChangedSignal(const char* property);
+
+    /**
+     * Implementation for the getPgood method
+     * @return power good
+     */
+    virtual int getPgood() = 0;
+
+    /**
+     * Implementation for the getPgoodTimeout method
+     * @return power good timeout
+     */
+    virtual int getPgoodTimeout() = 0;
+
+    /**
+     * Implementation for the getState method
+     * @return power state. A power on request is value 1. Power off is 0.
+     */
+    virtual int getState() = 0;
+
+    /**
+     * Implementation for the setPgoodTimeout method
+     * @param[in] timeout power good timeout
+     */
+    virtual void setPgoodTimeout(int timeout) = 0;
+
+    /**
+     * Implementation for the setState method
+     * @param[in] state power state. Request power on with a value of 1. Request
+     * power off with a value of 0. Other values will be rejected.
+     */
+    virtual void setState(int state) = 0;
+
+  private:
+    /**
+     * Holder for the instance of this interface to be on dbus
+     */
+    sdbusplus::server::interface::interface _serverInterface;
+
+    /**
+     * Systemd vtable structure that contains all the
+     * methods, signals, and properties of this interface with their
+     * respective systemd attributes
+     */
+    static const sdbusplus::vtable::vtable_t _vtable[];
+
+    /**
+     * Systemd bus callback for getting the pgood property
+     */
+    static int callbackGetPgood(sd_bus* bus, const char* path,
+                                const char* interface, const char* property,
+                                sd_bus_message* msg, void* context,
+                                sd_bus_error* ret_error);
+
+    /**
+     * Systemd bus callback for getting the pgood_timeout property
+     */
+    static int callbackGetPgoodTimeout(sd_bus* bus, const char* path,
+                                       const char* interface,
+                                       const char* property,
+                                       sd_bus_message* msg, void* context,
+                                       sd_bus_error* error);
+
+    /**
+     * Systemd bus callback for the getPowerState method
+     */
+    static int callbackGetPowerState(sd_bus_message* msg, void* context,
+                                     sd_bus_error* error);
+
+    /**
+     * Systemd bus callback for getting the state property
+     */
+    static int callbackGetState(sd_bus* bus, const char* path,
+                                const char* interface, const char* property,
+                                sd_bus_message* msg, void* context,
+                                sd_bus_error* error);
+
+    /**
+     * Systemd bus callback for setting the pgood_timeout property
+     */
+    static int callbackSetPgoodTimeout(sd_bus* bus, const char* path,
+                                       const char* interface,
+                                       const char* property,
+                                       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,
+                                     sd_bus_error* error);
+};
+
+} // namespace phosphor::power::sequencer