xref: /openbmc/phosphor-buttons/src/button_handler.cpp (revision 963c65f3e7636a8f402eb7c940ab37e5eb903e92)
1 #include "button_handler.hpp"
2 
3 #include <phosphor-logging/log.hpp>
4 #include <xyz/openbmc_project/State/Chassis/server.hpp>
5 #include <xyz/openbmc_project/State/Host/server.hpp>
6 
7 namespace phosphor
8 {
9 namespace button
10 {
11 
12 namespace sdbusRule = sdbusplus::bus::match::rules;
13 using namespace sdbusplus::xyz::openbmc_project::State::server;
14 using namespace phosphor::logging;
15 using sdbusplus::exception::SdBusError;
16 
17 constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
18 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
19 constexpr auto hostIface = "xyz.openbmc_project.State.Host";
20 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
21 constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
22 
23 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
24 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
25 
26 Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
27 {
28     try
29     {
30         if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
31         {
32             log<level::INFO>("Starting power button handler");
33             powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
34                 bus,
35                 sdbusRule::type::signal() + sdbusRule::member("Released") +
36                     sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
37                     sdbusRule::interface(powerButtonIface),
38                 std::bind(std::mem_fn(&Handler::powerPressed), this,
39                           std::placeholders::_1));
40 
41             powerButtonLongPressReleased =
42                 std::make_unique<sdbusplus::bus::match_t>(
43                     bus,
44                     sdbusRule::type::signal() +
45                         sdbusRule::member("PressedLong") +
46                         sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
47                         sdbusRule::interface(powerButtonIface),
48                     std::bind(std::mem_fn(&Handler::longPowerPressed), this,
49                               std::placeholders::_1));
50         }
51     }
52     catch (SdBusError& e)
53     {
54         // The button wasn't implemented
55     }
56 }
57 
58 std::string Handler::getService(const std::string& path,
59                                 const std::string& interface) const
60 {
61     auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
62                                       "GetObject");
63     method.append(path, std::vector{interface});
64     auto result = bus.call(method);
65 
66     std::map<std::string, std::vector<std::string>> objectData;
67     result.read(objectData);
68 
69     return objectData.begin()->first;
70 }
71 
72 bool Handler::poweredOn() const
73 {
74     auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
75     auto method = bus.new_method_call(
76         service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
77     method.append(chassisIface, "CurrentPowerState");
78     auto result = bus.call(method);
79 
80     sdbusplus::message::variant<std::string> state;
81     result.read(state);
82 
83     return Chassis::PowerState::On ==
84            Chassis::convertPowerStateFromString(
85                sdbusplus::message::variant_ns::get<std::string>(state));
86 }
87 
88 void Handler::powerPressed(sdbusplus::message::message& msg)
89 {
90     auto transition = Host::Transition::On;
91 
92     try
93     {
94         if (poweredOn())
95         {
96             transition = Host::Transition::Off;
97         }
98 
99         log<level::INFO>("Handling power button press");
100 
101         sdbusplus::message::variant<std::string> state =
102             convertForMessage(transition);
103 
104         auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
105         auto method = bus.new_method_call(
106             service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
107         method.append(hostIface, "RequestedHostTransition", state);
108 
109         bus.call(method);
110     }
111     catch (SdBusError& e)
112     {
113         log<level::ERR>("Failed power state change on a power button press",
114                         entry("ERROR=%s", e.what()));
115     }
116 }
117 
118 void Handler::longPowerPressed(sdbusplus::message::message& msg)
119 {
120     try
121     {
122         if (!poweredOn())
123         {
124             log<level::INFO>(
125                 "Power is off so ignoring long power button press");
126             return;
127         }
128 
129         log<level::INFO>("Handling long power button press");
130 
131         sdbusplus::message::variant<std::string> state =
132             convertForMessage(Chassis::Transition::Off);
133 
134         auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
135         auto method = bus.new_method_call(
136             service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
137         method.append(chassisIface, "RequestedPowerTransition", state);
138 
139         bus.call(method);
140     }
141     catch (SdBusError& e)
142     {
143         log<level::ERR>("Failed powering off on long power button press",
144                         entry("ERROR=%s", e.what()));
145     }
146 }
147 } // namespace button
148 } // namespace phosphor
149