xref: /openbmc/phosphor-buttons/src/button_handler.cpp (revision a6d4e65d5c4c78f86d690fff31b79db9fa8c3b4c)
1 #include "config.h"
2 
3 #include "button_handler.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 #include <xyz/openbmc_project/State/Chassis/server.hpp>
7 #include <xyz/openbmc_project/State/Host/server.hpp>
8 namespace phosphor
9 {
10 namespace button
11 {
12 
13 namespace sdbusRule = sdbusplus::bus::match::rules;
14 using namespace sdbusplus::xyz::openbmc_project::State::server;
15 
16 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
17 constexpr auto hostIface = "xyz.openbmc_project.State.Host";
18 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
19 constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
20 constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
21 constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
22 constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
23 constexpr auto hostSelectorIface =
24     "xyz.openbmc_project.Chassis.Buttons.HostSelector";
25 constexpr auto debugHostSelectorIface =
26     "xyz.openbmc_project.Chassis.Buttons.Button";
27 
28 constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
29 constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
30 
31 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
32 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
33 constexpr auto BMC_POSITION = 0;
34 
35 Handler::Handler(sdbusplus::bus_t& bus) : bus(bus)
36 {
37     try
38     {
39         if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
40         {
41             lg2::info("Starting power button handler");
42             powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
43                 bus,
44                 sdbusRule::type::signal() + sdbusRule::member("Released") +
45                     sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
46                     sdbusRule::interface(powerButtonIface),
47                 std::bind(std::mem_fn(&Handler::powerReleased), this,
48                           std::placeholders::_1));
49 
50             powerButtonLongPressed = std::make_unique<sdbusplus::bus::match_t>(
51                 bus,
52                 sdbusRule::type::signal() + sdbusRule::member("PressedLong") +
53                     sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
54                     sdbusRule::interface(powerButtonIface),
55                 std::bind(std::mem_fn(&Handler::longPowerPressed), this,
56                           std::placeholders::_1));
57         }
58     }
59     catch (const sdbusplus::exception_t& e)
60     {
61         // The button wasn't implemented
62     }
63 
64     try
65     {
66         if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty())
67         {
68             lg2::info("Registering ID button handler");
69             idButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
70                 bus,
71                 sdbusRule::type::signal() + sdbusRule::member("Released") +
72                     sdbusRule::path(ID_DBUS_OBJECT_NAME) +
73                     sdbusRule::interface(idButtonIface),
74                 std::bind(std::mem_fn(&Handler::idReleased), this,
75                           std::placeholders::_1));
76         }
77     }
78     catch (const sdbusplus::exception_t& e)
79     {
80         // The button wasn't implemented
81     }
82 
83     try
84     {
85         if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty())
86         {
87             lg2::info("Registering reset button handler");
88             resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
89                 bus,
90                 sdbusRule::type::signal() + sdbusRule::member("Released") +
91                     sdbusRule::path(RESET_DBUS_OBJECT_NAME) +
92                     sdbusRule::interface(resetButtonIface),
93                 std::bind(std::mem_fn(&Handler::resetReleased), this,
94                           std::placeholders::_1));
95         }
96     }
97     catch (const sdbusplus::exception_t& e)
98     {
99         // The button wasn't implemented
100     }
101     try
102     {
103         if (!getService(DBG_HS_DBUS_OBJECT_NAME, debugHostSelectorIface)
104                  .empty())
105         {
106             lg2::info("Registering debug host selector button handler");
107             debugHSButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
108                 bus,
109                 sdbusRule::type::signal() + sdbusRule::member("Released") +
110                     sdbusRule::path(DBG_HS_DBUS_OBJECT_NAME) +
111                     sdbusRule::interface(debugHostSelectorIface),
112                 std::bind(std::mem_fn(&Handler::debugHostSelectorReleased),
113                           this, std::placeholders::_1));
114         }
115     }
116     catch (const sdbusplus::exception::exception& e)
117     {
118         // The button wasn't implemented
119     }
120 }
121 bool Handler::isMultiHost()
122 {
123     // return true in case host selector object is available
124     return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
125 }
126 std::string Handler::getService(const std::string& path,
127                                 const std::string& interface) const
128 {
129     auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
130                                       "GetObject");
131     method.append(path, std::vector{interface});
132     auto result = bus.call(method);
133 
134     std::map<std::string, std::vector<std::string>> objectData;
135     result.read(objectData);
136 
137     return objectData.begin()->first;
138 }
139 size_t Handler::getHostSelectorValue()
140 {
141     auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
142 
143     if (HSService.empty())
144     {
145         lg2::info("Host selector dbus object not available");
146         throw std::invalid_argument("Host selector dbus object not available");
147     }
148 
149     try
150     {
151         auto method = bus.new_method_call(
152             HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get");
153         method.append(hostSelectorIface, "Position");
154         auto result = bus.call(method);
155 
156         std::variant<size_t> HSPositionVariant;
157         result.read(HSPositionVariant);
158 
159         auto position = std::get<size_t>(HSPositionVariant);
160         return position;
161     }
162     catch (const sdbusplus::exception_t& e)
163     {
164         lg2::error("Error reading host selector position: {ERROR}", "ERROR", e);
165         throw;
166     }
167 }
168 bool Handler::poweredOn(size_t hostNumber) const
169 {
170     auto chassisObjectName =
171         CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber);
172     auto service = getService(chassisObjectName.c_str(), chassisIface);
173     auto method = bus.new_method_call(
174         service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
175     method.append(chassisIface, "CurrentPowerState");
176     auto result = bus.call(method);
177 
178     std::variant<std::string> state;
179     result.read(state);
180 
181     return Chassis::PowerState::On ==
182            Chassis::convertPowerStateFromString(std::get<std::string>(state));
183 }
184 
185 void Handler::handlePowerEvent(PowerEvent powerEventType)
186 {
187     std::string objPathName;
188     std::string dbusIfaceName;
189     std::string transitionName;
190     std::variant<Host::Transition, Chassis::Transition> transition;
191 
192     size_t hostNumber = 0;
193     auto isMultiHostSystem = isMultiHost();
194     if (isMultiHostSystem)
195     {
196         hostNumber = getHostSelectorValue();
197         lg2::info("Multi-host system detected : {POSITION}", "POSITION",
198                   hostNumber);
199     }
200 
201     std::string hostNumStr = std::to_string(hostNumber);
202 
203     // ignore  power and reset button events if BMC is selected.
204     if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
205         (powerEventType != PowerEvent::longPowerReleased))
206     {
207         lg2::info(
208             "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events...");
209         return;
210     }
211 
212     switch (powerEventType)
213     {
214         case PowerEvent::powerReleased:
215         {
216             objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
217             dbusIfaceName = hostIface;
218             transitionName = "RequestedHostTransition";
219 
220             transition = Host::Transition::On;
221 
222             if (poweredOn(hostNumber))
223             {
224                 transition = Host::Transition::Off;
225             }
226             lg2::info("handlePowerEvent : Handle power button press ");
227 
228             break;
229         }
230         case PowerEvent::longPowerReleased:
231         {
232             dbusIfaceName = chassisIface;
233             transitionName = "RequestedPowerTransition";
234             objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
235             transition = Chassis::Transition::Off;
236 
237             /*  multi host system :
238                     hosts (1 to N) - host shutdown
239                     bmc (0) - sled cycle
240                 single host system :
241                     host(0) - host shutdown
242             */
243             if (isMultiHostSystem && (hostNumber == BMC_POSITION))
244             {
245 #if CHASSIS_SYSTEM_RESET_ENABLED
246                 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr;
247                 transition = Chassis::Transition::PowerCycle;
248 #else
249                 return;
250 #endif
251             }
252             else if (!poweredOn(hostNumber))
253             {
254                 lg2::info("Power is off so ignoring long power button press");
255                 return;
256             }
257             lg2::info("handlePowerEvent : handle long power button press");
258 
259             break;
260         }
261 
262         case PowerEvent::resetReleased:
263         {
264             objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
265             dbusIfaceName = hostIface;
266             transitionName = "RequestedHostTransition";
267 
268             if (!poweredOn(hostNumber))
269             {
270                 lg2::info("Power is off so ignoring reset button press");
271                 return;
272             }
273 
274             lg2::info("Handling reset button press");
275             transition = Host::Transition::Reboot;
276             break;
277         }
278         default:
279         {
280             lg2::error("{EVENT} is invalid power event. skipping...", "EVENT",
281                        static_cast<std::underlying_type_t<PowerEvent>>(
282                            powerEventType));
283 
284             return;
285         }
286     }
287     auto service = getService(objPathName.c_str(), dbusIfaceName);
288     auto method = bus.new_method_call(service.c_str(), objPathName.c_str(),
289                                       propertyIface, "Set");
290     method.append(dbusIfaceName, transitionName, transition);
291     bus.call(method);
292 }
293 void Handler::powerReleased(sdbusplus::message_t& /* msg */)
294 {
295     try
296     {
297         handlePowerEvent(PowerEvent::powerReleased);
298     }
299     catch (const sdbusplus::exception_t& e)
300     {
301         lg2::error("Failed power state change on a power button press: {ERROR}",
302                    "ERROR", e);
303     }
304 }
305 void Handler::longPowerPressed(sdbusplus::message_t& /* msg */)
306 {
307     try
308     {
309         handlePowerEvent(PowerEvent::longPowerPressed);
310     }
311     catch (const sdbusplus::exception_t& e)
312     {
313         lg2::error("Failed powering off on long power button press: {ERROR}",
314                    "ERROR", e);
315     }
316 }
317 
318 void Handler::resetReleased(sdbusplus::message_t& /* msg */)
319 {
320     try
321     {
322         handlePowerEvent(PowerEvent::resetReleased);
323     }
324     catch (const sdbusplus::exception_t& e)
325     {
326         lg2::error("Failed power state change on a reset button press: {ERROR}",
327                    "ERROR", e);
328     }
329 }
330 
331 void Handler::idReleased(sdbusplus::message_t& /* msg */)
332 {
333     std::string groupPath{ledGroupBasePath};
334     groupPath += ID_LED_GROUP;
335 
336     auto service = getService(groupPath, ledGroupIface);
337 
338     if (service.empty())
339     {
340         lg2::info("No found {GROUP} during ID button press:", "GROUP",
341                   groupPath);
342         return;
343     }
344 
345     try
346     {
347         auto method = bus.new_method_call(service.c_str(), groupPath.c_str(),
348                                           propertyIface, "Get");
349         method.append(ledGroupIface, "Asserted");
350         auto result = bus.call(method);
351 
352         std::variant<bool> state;
353         result.read(state);
354 
355         state = !std::get<bool>(state);
356 
357         lg2::info(
358             "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}",
359             "GROUP", groupPath, "STATE", std::get<bool>(state));
360 
361         method = bus.new_method_call(service.c_str(), groupPath.c_str(),
362                                      propertyIface, "Set");
363 
364         method.append(ledGroupIface, "Asserted", state);
365         result = bus.call(method);
366     }
367     catch (const sdbusplus::exception_t& e)
368     {
369         lg2::error("Error toggling ID LED group on ID button press: {ERROR}",
370                    "ERROR", e);
371     }
372 }
373 
374 void Handler::increaseHostSelectorPosition()
375 {
376     try
377     {
378         auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
379 
380         if (HSService.empty())
381         {
382             lg2::error("Host selector service not available");
383             return;
384         }
385 
386         auto method =
387             bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME,
388                                 phosphor::button::propertyIface, "GetAll");
389         method.append(phosphor::button::hostSelectorIface);
390         auto result = bus.call(method);
391         std::unordered_map<std::string, std::variant<size_t>> properties;
392         result.read(properties);
393 
394         auto maxPosition = std::get<size_t>(properties.at("MaxPosition"));
395         auto position = std::get<size_t>(properties.at("Position"));
396 
397         std::variant<size_t> HSPositionVariant =
398             (position < maxPosition) ? (position + 1) : 0;
399 
400         method = bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME,
401                                      phosphor::button::propertyIface, "Set");
402         method.append(phosphor::button::hostSelectorIface, "Position");
403 
404         method.append(HSPositionVariant);
405         result = bus.call(method);
406     }
407     catch (const sdbusplus::exception::exception& e)
408     {
409         lg2::error("Error modifying host selector position : {ERROR}", "ERROR",
410                    e);
411     }
412 }
413 
414 void Handler::debugHostSelectorReleased(sdbusplus::message::message& /* msg */)
415 {
416     try
417     {
418         increaseHostSelectorPosition();
419     }
420     catch (const sdbusplus::exception::exception& e)
421     {
422         lg2::error(
423             "Failed power process debug host selector button press : {ERROR}",
424             "ERROR", e);
425     }
426 }
427 
428 } // namespace button
429 } // namespace phosphor
430