1 #include "button_handler.hpp"
2 
3 #include "settings.hpp"
4 
5 #include <phosphor-logging/log.hpp>
6 #include <xyz/openbmc_project/State/Chassis/server.hpp>
7 #include <xyz/openbmc_project/State/Host/server.hpp>
8 
9 namespace phosphor
10 {
11 namespace button
12 {
13 
14 namespace sdbusRule = sdbusplus::bus::match::rules;
15 using namespace sdbusplus::xyz::openbmc_project::State::server;
16 using namespace phosphor::logging;
17 
18 constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
19 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
20 constexpr auto hostIface = "xyz.openbmc_project.State.Host";
21 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
22 constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
23 constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
24 constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
25 constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
26 
27 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
28 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
29 constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
30 
31 Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
32 {
33     try
34     {
35         if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
36         {
37             log<level::INFO>("Starting power button handler");
38             powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
39                 bus,
40                 sdbusRule::type::signal() + sdbusRule::member("Released") +
41                     sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
42                     sdbusRule::interface(powerButtonIface),
43                 std::bind(std::mem_fn(&Handler::powerPressed), this,
44                           std::placeholders::_1));
45 
46             powerButtonLongPressReleased =
47                 std::make_unique<sdbusplus::bus::match_t>(
48                     bus,
49                     sdbusRule::type::signal() +
50                         sdbusRule::member("PressedLong") +
51                         sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
52                         sdbusRule::interface(powerButtonIface),
53                     std::bind(std::mem_fn(&Handler::longPowerPressed), this,
54                               std::placeholders::_1));
55         }
56     }
57     catch (sdbusplus::exception::exception& e)
58     {
59         // The button wasn't implemented
60     }
61 
62     try
63     {
64         if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty())
65         {
66             log<level::INFO>("Registering ID button handler");
67             idButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
68                 bus,
69                 sdbusRule::type::signal() + sdbusRule::member("Released") +
70                     sdbusRule::path(ID_DBUS_OBJECT_NAME) +
71                     sdbusRule::interface(idButtonIface),
72                 std::bind(std::mem_fn(&Handler::idPressed), this,
73                           std::placeholders::_1));
74         }
75     }
76     catch (sdbusplus::exception::exception& e)
77     {
78         // The button wasn't implemented
79     }
80 
81     try
82     {
83         if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty())
84         {
85             log<level::INFO>("Registering reset button handler");
86             resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
87                 bus,
88                 sdbusRule::type::signal() + sdbusRule::member("Released") +
89                     sdbusRule::path(RESET_DBUS_OBJECT_NAME) +
90                     sdbusRule::interface(resetButtonIface),
91                 std::bind(std::mem_fn(&Handler::resetPressed), this,
92                           std::placeholders::_1));
93         }
94     }
95     catch (sdbusplus::exception::exception& e)
96     {
97         // The button wasn't implemented
98     }
99 }
100 
101 std::string Handler::getService(const std::string& path,
102                                 const std::string& interface) const
103 {
104     auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
105                                       "GetObject");
106     method.append(path, std::vector{interface});
107     auto result = bus.call(method);
108 
109     std::map<std::string, std::vector<std::string>> objectData;
110     result.read(objectData);
111 
112     return objectData.begin()->first;
113 }
114 
115 bool Handler::poweredOn() const
116 {
117     auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
118     auto method = bus.new_method_call(
119         service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
120     method.append(chassisIface, "CurrentPowerState");
121     auto result = bus.call(method);
122 
123     std::variant<std::string> state;
124     result.read(state);
125 
126     return Chassis::PowerState::On ==
127            Chassis::convertPowerStateFromString(std::get<std::string>(state));
128 }
129 
130 void Handler::powerPressed(sdbusplus::message::message& msg)
131 {
132     auto transition = Host::Transition::On;
133 
134     try
135     {
136         if (poweredOn())
137         {
138             transition = Host::Transition::Off;
139         }
140 
141         log<level::INFO>("Handling power button press");
142 
143         std::variant<std::string> state = convertForMessage(transition);
144 
145         auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
146         auto method = bus.new_method_call(
147             service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
148         method.append(hostIface, "RequestedHostTransition", state);
149 
150         bus.call(method);
151     }
152     catch (sdbusplus::exception::exception& e)
153     {
154         log<level::ERR>("Failed power state change on a power button press",
155                         entry("ERROR=%s", e.what()));
156     }
157 }
158 
159 void Handler::longPowerPressed(sdbusplus::message::message& msg)
160 {
161     try
162     {
163         if (!poweredOn())
164         {
165             log<level::INFO>(
166                 "Power is off so ignoring long power button press");
167             return;
168         }
169 
170         log<level::INFO>("Handling long power button press");
171 
172         std::variant<std::string> state =
173             convertForMessage(Chassis::Transition::Off);
174 
175         auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
176         auto method = bus.new_method_call(
177             service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
178         method.append(chassisIface, "RequestedPowerTransition", state);
179 
180         bus.call(method);
181     }
182     catch (sdbusplus::exception::exception& e)
183     {
184         log<level::ERR>("Failed powering off on long power button press",
185                         entry("ERROR=%s", e.what()));
186     }
187 }
188 
189 void Handler::resetPressed(sdbusplus::message::message& msg)
190 {
191     try
192     {
193         if (!poweredOn())
194         {
195             log<level::INFO>("Power is off so ignoring reset button press");
196             return;
197         }
198 
199         log<level::INFO>("Handling reset button press");
200 
201         std::variant<std::string> state =
202             convertForMessage(Host::Transition::Reboot);
203 
204         auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
205         auto method = bus.new_method_call(
206             service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
207 
208         method.append(hostIface, "RequestedHostTransition", state);
209 
210         bus.call(method);
211     }
212     catch (sdbusplus::exception::exception& e)
213     {
214         log<level::ERR>("Failed power state change on a reset button press",
215                         entry("ERROR=%s", e.what()));
216     }
217 }
218 
219 void Handler::idPressed(sdbusplus::message::message& msg)
220 {
221     std::string groupPath{ledGroupBasePath};
222     groupPath += ID_LED_GROUP;
223 
224     auto service = getService(groupPath, ledGroupIface);
225 
226     if (service.empty())
227     {
228         log<level::INFO>("No identify LED group found during ID button press",
229                          entry("GROUP=%s", groupPath.c_str()));
230         return;
231     }
232 
233     try
234     {
235         auto method = bus.new_method_call(service.c_str(), groupPath.c_str(),
236                                           propertyIface, "Get");
237         method.append(ledGroupIface, "Asserted");
238         auto result = bus.call(method);
239 
240         std::variant<bool> state;
241         result.read(state);
242 
243         state = !std::get<bool>(state);
244 
245         log<level::INFO>("Changing ID LED group state on ID LED press",
246                          entry("GROUP=%s", groupPath.c_str()),
247                          entry("STATE=%d", std::get<bool>(state)));
248 
249         method = bus.new_method_call(service.c_str(), groupPath.c_str(),
250                                      propertyIface, "Set");
251 
252         method.append(ledGroupIface, "Asserted", state);
253         result = bus.call(method);
254     }
255     catch (sdbusplus::exception::exception& e)
256     {
257         log<level::ERR>("Error toggling ID LED group on ID button press",
258                         entry("ERROR=%s", e.what()));
259     }
260 }
261 } // namespace button
262 } // namespace phosphor
263