blob: 054f6422cf0a16cde58874391d9e6caa36f3320a [file] [log] [blame]
George Liud6a1bae2022-06-20 13:47:31 +08001#include "config.h"
Matt Spinlerfb35a322018-11-26 14:30:30 -06002
George Liud6a1bae2022-06-20 13:47:31 +08003#include "button_handler.hpp"
Matt Spinler69f93512018-11-26 14:55:58 -06004
George Liu9fb15972022-06-20 14:54:38 +08005#include <phosphor-logging/lg2.hpp>
Matt Spinler963c65f2018-11-26 14:46:41 -06006#include <xyz/openbmc_project/State/Chassis/server.hpp>
7#include <xyz/openbmc_project/State/Host/server.hpp>
Matt Spinlerfb35a322018-11-26 14:30:30 -06008namespace phosphor
9{
10namespace button
11{
Matt Spinler963c65f2018-11-26 14:46:41 -060012
13namespace sdbusRule = sdbusplus::bus::match::rules;
14using namespace sdbusplus::xyz::openbmc_project::State::server;
Matt Spinler963c65f2018-11-26 14:46:41 -060015
Matt Spinler963c65f2018-11-26 14:46:41 -060016constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
17constexpr auto hostIface = "xyz.openbmc_project.State.Host";
18constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
Matt Spinler69f93512018-11-26 14:55:58 -060019constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
Matt Spinler06a5bdd2018-11-26 14:50:48 -060020constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
Matt Spinler69f93512018-11-26 14:55:58 -060021constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
Naveen Moses3bd1cfc2022-02-14 18:04:20 +053022constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
23constexpr auto hostSelectorIface =
24 "xyz.openbmc_project.Chassis.Buttons.HostSelector";
25constexpr auto debugHostSelectorIface =
Naveen Mosesa6d4e652022-04-13 19:27:25 +053026 "xyz.openbmc_project.Chassis.Buttons.Button";
Naveen Moses3bd1cfc2022-02-14 18:04:20 +053027
28constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
29constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
Matt Spinler963c65f2018-11-26 14:46:41 -060030
31constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
32constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
Naveen Moses3bd1cfc2022-02-14 18:04:20 +053033constexpr auto BMC_POSITION = 0;
Matt Spinler963c65f2018-11-26 14:46:41 -060034
Patrick Williams9a529a62022-07-22 19:26:54 -050035Handler::Handler(sdbusplus::bus_t& bus) : bus(bus)
Matt Spinlerfb35a322018-11-26 14:30:30 -060036{
Matt Spinler963c65f2018-11-26 14:46:41 -060037 try
38 {
39 if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
40 {
George Liu9fb15972022-06-20 14:54:38 +080041 lg2::info("Starting power button handler");
Matt Spinler963c65f2018-11-26 14:46:41 -060042 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),
Naveen Mosesab8dac52022-07-15 19:32:27 +053047 std::bind(std::mem_fn(&Handler::powerReleased), this,
Matt Spinler963c65f2018-11-26 14:46:41 -060048 std::placeholders::_1));
49
Naveen Mosesab8dac52022-07-15 19:32:27 +053050 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));
Matt Spinler963c65f2018-11-26 14:46:41 -060057 }
58 }
Patrick Williams9a529a62022-07-22 19:26:54 -050059 catch (const sdbusplus::exception_t& e)
Matt Spinler963c65f2018-11-26 14:46:41 -060060 {
61 // The button wasn't implemented
62 }
Matt Spinler06a5bdd2018-11-26 14:50:48 -060063
64 try
65 {
Matt Spinler69f93512018-11-26 14:55:58 -060066 if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty())
67 {
George Liu9fb15972022-06-20 14:54:38 +080068 lg2::info("Registering ID button handler");
Matt Spinler69f93512018-11-26 14:55:58 -060069 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),
Naveen Mosesab8dac52022-07-15 19:32:27 +053074 std::bind(std::mem_fn(&Handler::idReleased), this,
Matt Spinler69f93512018-11-26 14:55:58 -060075 std::placeholders::_1));
76 }
77 }
Patrick Williams9a529a62022-07-22 19:26:54 -050078 catch (const sdbusplus::exception_t& e)
Matt Spinler69f93512018-11-26 14:55:58 -060079 {
80 // The button wasn't implemented
81 }
82
83 try
84 {
Matt Spinler06a5bdd2018-11-26 14:50:48 -060085 if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty())
86 {
George Liu9fb15972022-06-20 14:54:38 +080087 lg2::info("Registering reset button handler");
Matt Spinler06a5bdd2018-11-26 14:50:48 -060088 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),
Naveen Mosesab8dac52022-07-15 19:32:27 +053093 std::bind(std::mem_fn(&Handler::resetReleased), this,
Matt Spinler06a5bdd2018-11-26 14:50:48 -060094 std::placeholders::_1));
95 }
96 }
Patrick Williams9a529a62022-07-22 19:26:54 -050097 catch (const sdbusplus::exception_t& e)
Matt Spinler06a5bdd2018-11-26 14:50:48 -060098 {
99 // The button wasn't implemented
100 }
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530101 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 }
Matt Spinler963c65f2018-11-26 14:46:41 -0600120}
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530121bool Handler::isMultiHost()
122{
123 // return true in case host selector object is available
124 return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
125}
Matt Spinler963c65f2018-11-26 14:46:41 -0600126std::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}
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530139size_t Handler::getHostSelectorValue()
Matt Spinler963c65f2018-11-26 14:46:41 -0600140{
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530141 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
142
143 if (HSService.empty())
144 {
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530145 lg2::info("Host selector dbus object not available");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530146 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 }
Patrick Williams9a529a62022-07-22 19:26:54 -0500162 catch (const sdbusplus::exception_t& e)
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530163 {
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530164 lg2::error("Error reading host selector position: {ERROR}", "ERROR", e);
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530165 throw;
166 }
167}
168bool 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);
Matt Spinler963c65f2018-11-26 14:46:41 -0600173 auto method = bus.new_method_call(
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530174 service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
Matt Spinler963c65f2018-11-26 14:46:41 -0600175 method.append(chassisIface, "CurrentPowerState");
176 auto result = bus.call(method);
177
Patrick Williams5ed4cc02020-05-13 17:52:15 -0500178 std::variant<std::string> state;
Matt Spinler963c65f2018-11-26 14:46:41 -0600179 result.read(state);
180
181 return Chassis::PowerState::On ==
Patrick Williams51f5e8b2020-05-13 11:33:44 -0500182 Chassis::convertPowerStateFromString(std::get<std::string>(state));
Matt Spinler963c65f2018-11-26 14:46:41 -0600183}
184
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530185void Handler::handlePowerEvent(PowerEvent powerEventType)
Matt Spinler963c65f2018-11-26 14:46:41 -0600186{
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530187 std::string objPathName;
188 std::string dbusIfaceName;
189 std::string transitionName;
190 std::variant<Host::Transition, Chassis::Transition> transition;
Matt Spinler963c65f2018-11-26 14:46:41 -0600191
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530192 size_t hostNumber = 0;
193 auto isMultiHostSystem = isMultiHost();
194 if (isMultiHostSystem)
Matt Spinler963c65f2018-11-26 14:46:41 -0600195 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530196 hostNumber = getHostSelectorValue();
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530197 lg2::info("Multi-host system detected : {POSITION}", "POSITION",
George Liu9fb15972022-06-20 14:54:38 +0800198 hostNumber);
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530199 }
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) &&
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530205 (powerEventType != PowerEvent::longPowerReleased))
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530206 {
George Liu9fb15972022-06-20 14:54:38 +0800207 lg2::info(
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530208 "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events...");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530209 return;
210 }
211
212 switch (powerEventType)
213 {
Naveen Mosesab8dac52022-07-15 19:32:27 +0530214 case PowerEvent::powerReleased:
George Liu5b98f4d2022-06-20 13:31:14 +0800215 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530216 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 }
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530226 lg2::info("handlePowerEvent : Handle power button press ");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530227
228 break;
229 }
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530230 case PowerEvent::longPowerReleased:
George Liu5b98f4d2022-06-20 13:31:14 +0800231 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530232 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 {
George Liu9fb15972022-06-20 14:54:38 +0800254 lg2::info("Power is off so ignoring long power button press");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530255 return;
256 }
George Liu9fb15972022-06-20 14:54:38 +0800257 lg2::info("handlePowerEvent : handle long power button press");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530258
259 break;
Matt Spinler963c65f2018-11-26 14:46:41 -0600260 }
261
Naveen Mosesab8dac52022-07-15 19:32:27 +0530262 case PowerEvent::resetReleased:
George Liu5b98f4d2022-06-20 13:31:14 +0800263 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530264 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
265 dbusIfaceName = hostIface;
266 transitionName = "RequestedHostTransition";
Matt Spinler963c65f2018-11-26 14:46:41 -0600267
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530268 if (!poweredOn(hostNumber))
269 {
George Liu9fb15972022-06-20 14:54:38 +0800270 lg2::info("Power is off so ignoring reset button press");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530271 return;
272 }
Matt Spinler963c65f2018-11-26 14:46:41 -0600273
George Liu9fb15972022-06-20 14:54:38 +0800274 lg2::info("Handling reset button press");
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530275 transition = Host::Transition::Reboot;
276 break;
277 }
George Liu5b98f4d2022-06-20 13:31:14 +0800278 default:
279 {
George Liu9fb15972022-06-20 14:54:38 +0800280 lg2::error("{EVENT} is invalid power event. skipping...", "EVENT",
281 static_cast<std::underlying_type_t<PowerEvent>>(
282 powerEventType));
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530283
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}
Patrick Williams9a529a62022-07-22 19:26:54 -0500293void Handler::powerReleased(sdbusplus::message_t& /* msg */)
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530294{
295 try
296 {
Naveen Mosesab8dac52022-07-15 19:32:27 +0530297 handlePowerEvent(PowerEvent::powerReleased);
Matt Spinler963c65f2018-11-26 14:46:41 -0600298 }
Patrick Williams9a529a62022-07-22 19:26:54 -0500299 catch (const sdbusplus::exception_t& e)
Matt Spinler963c65f2018-11-26 14:46:41 -0600300 {
George Liu9fb15972022-06-20 14:54:38 +0800301 lg2::error("Failed power state change on a power button press: {ERROR}",
302 "ERROR", e);
Matt Spinler963c65f2018-11-26 14:46:41 -0600303 }
304}
Patrick Williams9a529a62022-07-22 19:26:54 -0500305void Handler::longPowerPressed(sdbusplus::message_t& /* msg */)
Matt Spinler963c65f2018-11-26 14:46:41 -0600306{
307 try
308 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530309 handlePowerEvent(PowerEvent::longPowerPressed);
Matt Spinler963c65f2018-11-26 14:46:41 -0600310 }
Patrick Williams9a529a62022-07-22 19:26:54 -0500311 catch (const sdbusplus::exception_t& e)
Matt Spinler963c65f2018-11-26 14:46:41 -0600312 {
George Liu9fb15972022-06-20 14:54:38 +0800313 lg2::error("Failed powering off on long power button press: {ERROR}",
314 "ERROR", e);
Matt Spinler963c65f2018-11-26 14:46:41 -0600315 }
Matt Spinlerfb35a322018-11-26 14:30:30 -0600316}
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600317
Patrick Williams9a529a62022-07-22 19:26:54 -0500318void Handler::resetReleased(sdbusplus::message_t& /* msg */)
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600319{
320 try
321 {
Naveen Mosesab8dac52022-07-15 19:32:27 +0530322 handlePowerEvent(PowerEvent::resetReleased);
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600323 }
Patrick Williams9a529a62022-07-22 19:26:54 -0500324 catch (const sdbusplus::exception_t& e)
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600325 {
George Liu9fb15972022-06-20 14:54:38 +0800326 lg2::error("Failed power state change on a reset button press: {ERROR}",
327 "ERROR", e);
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600328 }
329}
Matt Spinler69f93512018-11-26 14:55:58 -0600330
Patrick Williams9a529a62022-07-22 19:26:54 -0500331void Handler::idReleased(sdbusplus::message_t& /* msg */)
Matt Spinler69f93512018-11-26 14:55:58 -0600332{
333 std::string groupPath{ledGroupBasePath};
334 groupPath += ID_LED_GROUP;
335
336 auto service = getService(groupPath, ledGroupIface);
337
338 if (service.empty())
339 {
George Liu9fb15972022-06-20 14:54:38 +0800340 lg2::info("No found {GROUP} during ID button press:", "GROUP",
341 groupPath);
Matt Spinler69f93512018-11-26 14:55:58 -0600342 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
Patrick Williams5ed4cc02020-05-13 17:52:15 -0500352 std::variant<bool> state;
Matt Spinler69f93512018-11-26 14:55:58 -0600353 result.read(state);
354
Patrick Williams51f5e8b2020-05-13 11:33:44 -0500355 state = !std::get<bool>(state);
Matt Spinler69f93512018-11-26 14:55:58 -0600356
George Liu9fb15972022-06-20 14:54:38 +0800357 lg2::info(
358 "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}",
359 "GROUP", groupPath, "STATE", std::get<bool>(state));
Matt Spinler69f93512018-11-26 14:55:58 -0600360
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 }
Patrick Williams9a529a62022-07-22 19:26:54 -0500367 catch (const sdbusplus::exception_t& e)
Matt Spinler69f93512018-11-26 14:55:58 -0600368 {
George Liu9fb15972022-06-20 14:54:38 +0800369 lg2::error("Error toggling ID LED group on ID button press: {ERROR}",
370 "ERROR", e);
Matt Spinler69f93512018-11-26 14:55:58 -0600371 }
372}
Naveen Mosesa6d4e652022-04-13 19:27:25 +0530373
374void 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
414void 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
Matt Spinlerfb35a322018-11-26 14:30:30 -0600428} // namespace button
429} // namespace phosphor