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/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