xref: /openbmc/phosphor-buttons/src/button_handler.cpp (revision 9a529a690da8246196bd4a6336f1f9eb535385ea)
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.DebugHostSelector";
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 }
102 bool Handler::isMultiHost()
103 {
104     // return true in case host selector object is available
105     return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
106 }
107 std::string Handler::getService(const std::string& path,
108                                 const std::string& interface) const
109 {
110     auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
111                                       "GetObject");
112     method.append(path, std::vector{interface});
113     auto result = bus.call(method);
114 
115     std::map<std::string, std::vector<std::string>> objectData;
116     result.read(objectData);
117 
118     return objectData.begin()->first;
119 }
120 size_t Handler::getHostSelectorValue()
121 {
122     auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
123 
124     if (HSService.empty())
125     {
126         lg2::info("Host Selector dbus object not available");
127         throw std::invalid_argument("Host selector dbus object not available");
128     }
129 
130     try
131     {
132         auto method = bus.new_method_call(
133             HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get");
134         method.append(hostSelectorIface, "Position");
135         auto result = bus.call(method);
136 
137         std::variant<size_t> HSPositionVariant;
138         result.read(HSPositionVariant);
139 
140         auto position = std::get<size_t>(HSPositionVariant);
141         return position;
142     }
143     catch (const sdbusplus::exception_t& e)
144     {
145         lg2::error("Error reading Host selector Position: {ERROR}", "ERROR", e);
146         throw;
147     }
148 }
149 bool Handler::poweredOn(size_t hostNumber) const
150 {
151     auto chassisObjectName =
152         CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber);
153     auto service = getService(chassisObjectName.c_str(), chassisIface);
154     auto method = bus.new_method_call(
155         service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
156     method.append(chassisIface, "CurrentPowerState");
157     auto result = bus.call(method);
158 
159     std::variant<std::string> state;
160     result.read(state);
161 
162     return Chassis::PowerState::On ==
163            Chassis::convertPowerStateFromString(std::get<std::string>(state));
164 }
165 
166 void Handler::handlePowerEvent(PowerEvent powerEventType)
167 {
168     std::string objPathName;
169     std::string dbusIfaceName;
170     std::string transitionName;
171     std::variant<Host::Transition, Chassis::Transition> transition;
172 
173     size_t hostNumber = 0;
174     auto isMultiHostSystem = isMultiHost();
175     if (isMultiHostSystem)
176     {
177         hostNumber = getHostSelectorValue();
178         lg2::info("Multi host system detected : {POSITION}", "POSITION",
179                   hostNumber);
180     }
181 
182     std::string hostNumStr = std::to_string(hostNumber);
183 
184     // ignore  power and reset button events if BMC is selected.
185     if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
186         (powerEventType != PowerEvent::longPowerPressed))
187     {
188         lg2::info(
189             "handlePowerEvent : BMC selected on multihost system. ignoring power and reset button events...");
190         return;
191     }
192 
193     switch (powerEventType)
194     {
195         case PowerEvent::powerReleased:
196         {
197             objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
198             dbusIfaceName = hostIface;
199             transitionName = "RequestedHostTransition";
200 
201             transition = Host::Transition::On;
202 
203             if (poweredOn(hostNumber))
204             {
205                 transition = Host::Transition::Off;
206             }
207             lg2::info("handlePowerEvent : handle power button press ");
208 
209             break;
210         }
211         case PowerEvent::longPowerPressed:
212         {
213             dbusIfaceName = chassisIface;
214             transitionName = "RequestedPowerTransition";
215             objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
216             transition = Chassis::Transition::Off;
217 
218             /*  multi host system :
219                     hosts (1 to N) - host shutdown
220                     bmc (0) - sled cycle
221                 single host system :
222                     host(0) - host shutdown
223             */
224             if (isMultiHostSystem && (hostNumber == BMC_POSITION))
225             {
226 #if CHASSIS_SYSTEM_RESET_ENABLED
227                 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr;
228                 transition = Chassis::Transition::PowerCycle;
229 #else
230                 return;
231 #endif
232             }
233             else if (!poweredOn(hostNumber))
234             {
235                 lg2::info("Power is off so ignoring long power button press");
236                 return;
237             }
238             lg2::info("handlePowerEvent : handle long power button press");
239 
240             break;
241         }
242 
243         case PowerEvent::resetReleased:
244         {
245             objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
246             dbusIfaceName = hostIface;
247             transitionName = "RequestedHostTransition";
248 
249             if (!poweredOn(hostNumber))
250             {
251                 lg2::info("Power is off so ignoring reset button press");
252                 return;
253             }
254 
255             lg2::info("Handling reset button press");
256             transition = Host::Transition::Reboot;
257             break;
258         }
259         default:
260         {
261             lg2::error("{EVENT} is invalid power event. skipping...", "EVENT",
262                        static_cast<std::underlying_type_t<PowerEvent>>(
263                            powerEventType));
264 
265             return;
266         }
267     }
268     auto service = getService(objPathName.c_str(), dbusIfaceName);
269     auto method = bus.new_method_call(service.c_str(), objPathName.c_str(),
270                                       propertyIface, "Set");
271     method.append(dbusIfaceName, transitionName, transition);
272     bus.call(method);
273 }
274 void Handler::powerReleased(sdbusplus::message_t& /* msg */)
275 {
276     try
277     {
278         handlePowerEvent(PowerEvent::powerReleased);
279     }
280     catch (const sdbusplus::exception_t& e)
281     {
282         lg2::error("Failed power state change on a power button press: {ERROR}",
283                    "ERROR", e);
284     }
285 }
286 void Handler::longPowerPressed(sdbusplus::message_t& /* msg */)
287 {
288     try
289     {
290         handlePowerEvent(PowerEvent::longPowerPressed);
291     }
292     catch (const sdbusplus::exception_t& e)
293     {
294         lg2::error("Failed powering off on long power button press: {ERROR}",
295                    "ERROR", e);
296     }
297 }
298 
299 void Handler::resetReleased(sdbusplus::message_t& /* msg */)
300 {
301     try
302     {
303         handlePowerEvent(PowerEvent::resetReleased);
304     }
305     catch (const sdbusplus::exception_t& e)
306     {
307         lg2::error("Failed power state change on a reset button press: {ERROR}",
308                    "ERROR", e);
309     }
310 }
311 
312 void Handler::idReleased(sdbusplus::message_t& /* msg */)
313 {
314     std::string groupPath{ledGroupBasePath};
315     groupPath += ID_LED_GROUP;
316 
317     auto service = getService(groupPath, ledGroupIface);
318 
319     if (service.empty())
320     {
321         lg2::info("No found {GROUP} during ID button press:", "GROUP",
322                   groupPath);
323         return;
324     }
325 
326     try
327     {
328         auto method = bus.new_method_call(service.c_str(), groupPath.c_str(),
329                                           propertyIface, "Get");
330         method.append(ledGroupIface, "Asserted");
331         auto result = bus.call(method);
332 
333         std::variant<bool> state;
334         result.read(state);
335 
336         state = !std::get<bool>(state);
337 
338         lg2::info(
339             "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}",
340             "GROUP", groupPath, "STATE", std::get<bool>(state));
341 
342         method = bus.new_method_call(service.c_str(), groupPath.c_str(),
343                                      propertyIface, "Set");
344 
345         method.append(ledGroupIface, "Asserted", state);
346         result = bus.call(method);
347     }
348     catch (const sdbusplus::exception_t& e)
349     {
350         lg2::error("Error toggling ID LED group on ID button press: {ERROR}",
351                    "ERROR", e);
352     }
353 }
354 } // namespace button
355 } // namespace phosphor
356