button-handler: Add power button actions
Create match handlers for the following power button signals:
1) 'Released'
* If the system is off, then power it on.
* If the system is on, then soft power it off.
2) 'PressedLong' (Really means long press released)
* If the system is on, do an immediate power off.
* If the system is off, do nothing.
It only installs the match objects for these if the power button
object exists on D-Bus. This is done so that systems that don't
implement a certain button won't still have watches on their
signals.
Tested: Pushed buttons and watched the magic happen.
Change-Id: I6d17529d0dafc237f90a0e6a121e4b5da1204f81
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5c3078..c4e8483 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,12 +12,17 @@
set(ID_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/ID")
set(GPIO_BASE_LABEL_NAME "1e780000.gpio")
set(LONG_PRESS_TIME_MS 3000)
+set(CHASSIS_STATE_OBJECT_NAME "xyz/openbmc_project/state/chassis")
+set(HOST_STATE_OBJECT_NAME "xyz/openbmc_project/state/host")
add_definitions(-DPOWER_DBUS_OBJECT_NAME="/${POWER_DBUS_OBJECT_NAME}0")
add_definitions(-DRESET_DBUS_OBJECT_NAME="/${RESET_DBUS_OBJECT_NAME}0")
add_definitions(-DID_DBUS_OBJECT_NAME="/${ID_DBUS_OBJECT_NAME}0")
add_definitions(-DGPIO_BASE_LABEL_NAME="${GPIO_BASE_LABEL_NAME}")
add_definitions(-DLONG_PRESS_TIME_MS=${LONG_PRESS_TIME_MS})
+add_definitions(-DHOST_STATE_OBJECT_NAME="/${HOST_STATE_OBJECT_NAME}0")
+add_definitions(-DCHASSIS_STATE_OBJECT_NAME="/${CHASSIS_STATE_OBJECT_NAME}0")
+
set(SRC_FILES src/power_button.cpp
src/reset_button.cpp
src/id_button.cpp
diff --git a/inc/button_handler.hpp b/inc/button_handler.hpp
index 4bc6310..790bbe9 100644
--- a/inc/button_handler.hpp
+++ b/inc/button_handler.hpp
@@ -38,9 +38,58 @@
private:
/**
+ * @brief The handler for a power button press
+ *
+ * It will power on the system if it's currently off,
+ * else it will soft power it off.
+ *
+ * @param[in] msg - sdbusplus message from signal
+ */
+ void powerPressed(sdbusplus::message::message& msg);
+
+ /**
+ * @brief The handler for a long power button press
+ *
+ * If the system is currently powered on, it will
+ * perform an immediate power off.
+ *
+ * @param[in] msg - sdbusplus message from signal
+ */
+ void longPowerPressed(sdbusplus::message::message& msg);
+
+ /**
+ * @brief Checks if system is powered on
+ *
+ * @return true if powered on, false else
+ */
+ bool poweredOn() const;
+
+ /**
+ * @brief Returns the service name for an object
+ *
+ * @param[in] path - the object path
+ * @param[in] interface - the interface name
+ *
+ * @return std::string - the D-Bus service name if found, else
+ * an empty string
+ */
+ std::string getService(const std::string& path,
+ const std::string& interface) const;
+
+ /**
* @brief sdbusplus connection object
*/
sdbusplus::bus::bus& bus;
+
+ /**
+ * @brief Matches on the power button released signal
+ */
+ std::unique_ptr<sdbusplus::bus::match_t> powerButtonReleased;
+
+ /**
+ * @brief Matches on the power button long press released signal
+ */
+ std::unique_ptr<sdbusplus::bus::match_t> powerButtonLongPressReleased;
};
} // namespace button
diff --git a/src/button_handler.cpp b/src/button_handler.cpp
index e8edc41..f4b43fc 100644
--- a/src/button_handler.cpp
+++ b/src/button_handler.cpp
@@ -1,11 +1,148 @@
#include "button_handler.hpp"
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/State/Chassis/server.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
+
namespace phosphor
{
namespace button
{
+
+namespace sdbusRule = sdbusplus::bus::match::rules;
+using namespace sdbusplus::xyz::openbmc_project::State::server;
+using namespace phosphor::logging;
+using sdbusplus::exception::SdBusError;
+
+constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
+constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
+constexpr auto hostIface = "xyz.openbmc_project.State.Host";
+constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
+constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
+
+constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
+
Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
{
+ try
+ {
+ if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
+ {
+ log<level::INFO>("Starting power button handler");
+ powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusRule::type::signal() + sdbusRule::member("Released") +
+ sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
+ sdbusRule::interface(powerButtonIface),
+ std::bind(std::mem_fn(&Handler::powerPressed), this,
+ std::placeholders::_1));
+
+ powerButtonLongPressReleased =
+ std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusRule::type::signal() +
+ sdbusRule::member("PressedLong") +
+ sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
+ sdbusRule::interface(powerButtonIface),
+ std::bind(std::mem_fn(&Handler::longPowerPressed), this,
+ std::placeholders::_1));
+ }
+ }
+ catch (SdBusError& e)
+ {
+ // The button wasn't implemented
+ }
+}
+
+std::string Handler::getService(const std::string& path,
+ const std::string& interface) const
+{
+ auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
+ "GetObject");
+ method.append(path, std::vector{interface});
+ auto result = bus.call(method);
+
+ std::map<std::string, std::vector<std::string>> objectData;
+ result.read(objectData);
+
+ return objectData.begin()->first;
+}
+
+bool Handler::poweredOn() const
+{
+ auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
+ auto method = bus.new_method_call(
+ service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
+ method.append(chassisIface, "CurrentPowerState");
+ auto result = bus.call(method);
+
+ sdbusplus::message::variant<std::string> state;
+ result.read(state);
+
+ return Chassis::PowerState::On ==
+ Chassis::convertPowerStateFromString(
+ sdbusplus::message::variant_ns::get<std::string>(state));
+}
+
+void Handler::powerPressed(sdbusplus::message::message& msg)
+{
+ auto transition = Host::Transition::On;
+
+ try
+ {
+ if (poweredOn())
+ {
+ transition = Host::Transition::Off;
+ }
+
+ log<level::INFO>("Handling power button press");
+
+ sdbusplus::message::variant<std::string> state =
+ convertForMessage(transition);
+
+ auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
+ auto method = bus.new_method_call(
+ service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
+ method.append(hostIface, "RequestedHostTransition", state);
+
+ bus.call(method);
+ }
+ catch (SdBusError& e)
+ {
+ log<level::ERR>("Failed power state change on a power button press",
+ entry("ERROR=%s", e.what()));
+ }
+}
+
+void Handler::longPowerPressed(sdbusplus::message::message& msg)
+{
+ try
+ {
+ if (!poweredOn())
+ {
+ log<level::INFO>(
+ "Power is off so ignoring long power button press");
+ return;
+ }
+
+ log<level::INFO>("Handling long power button press");
+
+ sdbusplus::message::variant<std::string> state =
+ convertForMessage(Chassis::Transition::Off);
+
+ auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
+ auto method = bus.new_method_call(
+ service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
+ method.append(chassisIface, "RequestedPowerTransition", state);
+
+ bus.call(method);
+ }
+ catch (SdBusError& e)
+ {
+ log<level::ERR>("Failed powering off on long power button press",
+ entry("ERROR=%s", e.what()));
+ }
}
} // namespace button
} // namespace phosphor