blob: 55f5db52a146e06f9161560e67daf7bfa1ddd74c [file] [log] [blame]
Matt Spinlerfb35a322018-11-26 14:30:30 -06001#include "button_handler.hpp"
2
Matt Spinler69f93512018-11-26 14:55:58 -06003#include "settings.hpp"
4
Matt Spinler963c65f2018-11-26 14:46:41 -06005#include <phosphor-logging/log.hpp>
6#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;
15using namespace phosphor::logging;
Matt Spinler963c65f2018-11-26 14:46:41 -060016
Matt Spinler963c65f2018-11-26 14:46:41 -060017constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
18constexpr auto hostIface = "xyz.openbmc_project.State.Host";
19constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
Matt Spinler69f93512018-11-26 14:55:58 -060020constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
Matt Spinler06a5bdd2018-11-26 14:50:48 -060021constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
Matt Spinler69f93512018-11-26 14:55:58 -060022constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
Naveen Moses3bd1cfc2022-02-14 18:04:20 +053023constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
24constexpr auto hostSelectorIface =
25 "xyz.openbmc_project.Chassis.Buttons.HostSelector";
26constexpr auto debugHostSelectorIface =
27 "xyz.openbmc_project.Chassis.Buttons.DebugHostSelector";
28
29constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
30constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
Matt Spinler963c65f2018-11-26 14:46:41 -060031
32constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
33constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
Naveen Moses3bd1cfc2022-02-14 18:04:20 +053034constexpr auto BMC_POSITION = 0;
Matt Spinler963c65f2018-11-26 14:46:41 -060035
Matt Spinlerfb35a322018-11-26 14:30:30 -060036Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
37{
Matt Spinler963c65f2018-11-26 14:46:41 -060038 try
39 {
40 if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
41 {
42 log<level::INFO>("Starting power button handler");
43 powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
44 bus,
45 sdbusRule::type::signal() + sdbusRule::member("Released") +
46 sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
47 sdbusRule::interface(powerButtonIface),
48 std::bind(std::mem_fn(&Handler::powerPressed), this,
49 std::placeholders::_1));
50
51 powerButtonLongPressReleased =
52 std::make_unique<sdbusplus::bus::match_t>(
53 bus,
54 sdbusRule::type::signal() +
55 sdbusRule::member("PressedLong") +
56 sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
57 sdbusRule::interface(powerButtonIface),
58 std::bind(std::mem_fn(&Handler::longPowerPressed), this,
59 std::placeholders::_1));
60 }
61 }
Patrick Williams6d724ce2021-10-06 12:40:26 -050062 catch (const sdbusplus::exception::exception& e)
Matt Spinler963c65f2018-11-26 14:46:41 -060063 {
64 // The button wasn't implemented
65 }
Matt Spinler06a5bdd2018-11-26 14:50:48 -060066
67 try
68 {
Matt Spinler69f93512018-11-26 14:55:58 -060069 if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty())
70 {
71 log<level::INFO>("Registering ID button handler");
72 idButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
73 bus,
74 sdbusRule::type::signal() + sdbusRule::member("Released") +
75 sdbusRule::path(ID_DBUS_OBJECT_NAME) +
76 sdbusRule::interface(idButtonIface),
77 std::bind(std::mem_fn(&Handler::idPressed), this,
78 std::placeholders::_1));
79 }
80 }
Patrick Williams6d724ce2021-10-06 12:40:26 -050081 catch (const sdbusplus::exception::exception& e)
Matt Spinler69f93512018-11-26 14:55:58 -060082 {
83 // The button wasn't implemented
84 }
85
86 try
87 {
Matt Spinler06a5bdd2018-11-26 14:50:48 -060088 if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty())
89 {
90 log<level::INFO>("Registering reset button handler");
91 resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
92 bus,
93 sdbusRule::type::signal() + sdbusRule::member("Released") +
94 sdbusRule::path(RESET_DBUS_OBJECT_NAME) +
95 sdbusRule::interface(resetButtonIface),
96 std::bind(std::mem_fn(&Handler::resetPressed), this,
97 std::placeholders::_1));
98 }
99 }
Patrick Williams6d724ce2021-10-06 12:40:26 -0500100 catch (const sdbusplus::exception::exception& e)
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600101 {
102 // The button wasn't implemented
103 }
Matt Spinler963c65f2018-11-26 14:46:41 -0600104}
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530105bool Handler::isMultiHost()
106{
107 // return true in case host selector object is available
108 return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
109}
Matt Spinler963c65f2018-11-26 14:46:41 -0600110std::string Handler::getService(const std::string& path,
111 const std::string& interface) const
112{
113 auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
114 "GetObject");
115 method.append(path, std::vector{interface});
116 auto result = bus.call(method);
117
118 std::map<std::string, std::vector<std::string>> objectData;
119 result.read(objectData);
120
121 return objectData.begin()->first;
122}
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530123size_t Handler::getHostSelectorValue()
Matt Spinler963c65f2018-11-26 14:46:41 -0600124{
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530125 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
126
127 if (HSService.empty())
128 {
129 log<level::INFO>("Host Selector dbus object not available");
130 throw std::invalid_argument("Host selector dbus object not available");
131 }
132
133 try
134 {
135 auto method = bus.new_method_call(
136 HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get");
137 method.append(hostSelectorIface, "Position");
138 auto result = bus.call(method);
139
140 std::variant<size_t> HSPositionVariant;
141 result.read(HSPositionVariant);
142
143 auto position = std::get<size_t>(HSPositionVariant);
144 return position;
145 }
146 catch (const sdbusplus::exception::exception& e)
147 {
148 log<level::ERR>("Error reading Host selector Position",
149 entry("ERROR=%s", e.what()));
150 throw;
151 }
152}
153bool Handler::poweredOn(size_t hostNumber) const
154{
155 auto chassisObjectName =
156 CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber);
157 auto service = getService(chassisObjectName.c_str(), chassisIface);
Matt Spinler963c65f2018-11-26 14:46:41 -0600158 auto method = bus.new_method_call(
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530159 service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
Matt Spinler963c65f2018-11-26 14:46:41 -0600160 method.append(chassisIface, "CurrentPowerState");
161 auto result = bus.call(method);
162
Patrick Williams5ed4cc02020-05-13 17:52:15 -0500163 std::variant<std::string> state;
Matt Spinler963c65f2018-11-26 14:46:41 -0600164 result.read(state);
165
166 return Chassis::PowerState::On ==
Patrick Williams51f5e8b2020-05-13 11:33:44 -0500167 Chassis::convertPowerStateFromString(std::get<std::string>(state));
Matt Spinler963c65f2018-11-26 14:46:41 -0600168}
169
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530170void Handler::handlePowerEvent(PowerEvent powerEventType)
Matt Spinler963c65f2018-11-26 14:46:41 -0600171{
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530172 std::string objPathName;
173 std::string dbusIfaceName;
174 std::string transitionName;
175 std::variant<Host::Transition, Chassis::Transition> transition;
Matt Spinler963c65f2018-11-26 14:46:41 -0600176
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530177 size_t hostNumber = 0;
178 auto isMultiHostSystem = isMultiHost();
179 if (isMultiHostSystem)
Matt Spinler963c65f2018-11-26 14:46:41 -0600180 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530181 hostNumber = getHostSelectorValue();
182 log<level::INFO>("Multi host system detected : ",
183 entry("POSITION=%d", hostNumber));
184 }
185
186 std::string hostNumStr = std::to_string(hostNumber);
187
188 // ignore power and reset button events if BMC is selected.
189 if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
190 (powerEventType != PowerEvent::longPowerPressed))
191 {
192 log<level::INFO>("handlePowerEvent : BMC selected on multihost system."
193 "ignoring power and reset button events...");
194 return;
195 }
196
197 switch (powerEventType)
198 {
199 case PowerEvent::powerPressed: {
200 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
201 dbusIfaceName = hostIface;
202 transitionName = "RequestedHostTransition";
203
204 transition = Host::Transition::On;
205
206 if (poweredOn(hostNumber))
207 {
208 transition = Host::Transition::Off;
209 }
210 log<level::INFO>("handlePowerEvent : handle power button press ");
211
212 break;
213 }
214 case PowerEvent::longPowerPressed: {
215 dbusIfaceName = chassisIface;
216 transitionName = "RequestedPowerTransition";
217 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
218 transition = Chassis::Transition::Off;
219
220 /* multi host system :
221 hosts (1 to N) - host shutdown
222 bmc (0) - sled cycle
223 single host system :
224 host(0) - host shutdown
225 */
226 if (isMultiHostSystem && (hostNumber == BMC_POSITION))
227 {
228#if CHASSIS_SYSTEM_RESET_ENABLED
229 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr;
230 transition = Chassis::Transition::PowerCycle;
231#else
232 return;
233#endif
234 }
235 else if (!poweredOn(hostNumber))
236 {
237 log<level::INFO>(
238 "Power is off so ignoring long power button press");
239 return;
240 }
241 log<level::INFO>(
242 "handlePowerEvent : handle long power button press");
243
244 break;
Matt Spinler963c65f2018-11-26 14:46:41 -0600245 }
246
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530247 case PowerEvent::resetPressed: {
Matt Spinler963c65f2018-11-26 14:46:41 -0600248
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530249 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
250 dbusIfaceName = hostIface;
251 transitionName = "RequestedHostTransition";
Matt Spinler963c65f2018-11-26 14:46:41 -0600252
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530253 if (!poweredOn(hostNumber))
254 {
255 log<level::INFO>("Power is off so ignoring reset button press");
256 return;
257 }
Matt Spinler963c65f2018-11-26 14:46:41 -0600258
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530259 log<level::INFO>("Handling reset button press");
260 transition = Host::Transition::Reboot;
261 break;
262 }
263 default: {
264 log<level::ERR>(
265 "Invalid power event. skipping...",
266 entry("EVENT=%d", static_cast<int>(powerEventType)));
267
268 return;
269 }
270 }
271 auto service = getService(objPathName.c_str(), dbusIfaceName);
272 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(),
273 propertyIface, "Set");
274 method.append(dbusIfaceName, transitionName, transition);
275 bus.call(method);
276}
277void Handler::powerPressed(sdbusplus::message::message& msg)
278{
279 try
280 {
281 handlePowerEvent(PowerEvent::powerPressed);
Matt Spinler963c65f2018-11-26 14:46:41 -0600282 }
Patrick Williams6d724ce2021-10-06 12:40:26 -0500283 catch (const sdbusplus::exception::exception& e)
Matt Spinler963c65f2018-11-26 14:46:41 -0600284 {
285 log<level::ERR>("Failed power state change on a power button press",
286 entry("ERROR=%s", e.what()));
287 }
288}
Matt Spinler963c65f2018-11-26 14:46:41 -0600289void Handler::longPowerPressed(sdbusplus::message::message& msg)
290{
291 try
292 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530293 handlePowerEvent(PowerEvent::longPowerPressed);
Matt Spinler963c65f2018-11-26 14:46:41 -0600294 }
Patrick Williams6d724ce2021-10-06 12:40:26 -0500295 catch (const sdbusplus::exception::exception& e)
Matt Spinler963c65f2018-11-26 14:46:41 -0600296 {
297 log<level::ERR>("Failed powering off on long power button press",
298 entry("ERROR=%s", e.what()));
299 }
Matt Spinlerfb35a322018-11-26 14:30:30 -0600300}
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600301
302void Handler::resetPressed(sdbusplus::message::message& msg)
303{
304 try
305 {
Naveen Moses3bd1cfc2022-02-14 18:04:20 +0530306 handlePowerEvent(PowerEvent::resetPressed);
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600307 }
Patrick Williams6d724ce2021-10-06 12:40:26 -0500308 catch (const sdbusplus::exception::exception& e)
Matt Spinler06a5bdd2018-11-26 14:50:48 -0600309 {
310 log<level::ERR>("Failed power state change on a reset button press",
311 entry("ERROR=%s", e.what()));
312 }
313}
Matt Spinler69f93512018-11-26 14:55:58 -0600314
315void Handler::idPressed(sdbusplus::message::message& msg)
316{
317 std::string groupPath{ledGroupBasePath};
318 groupPath += ID_LED_GROUP;
319
320 auto service = getService(groupPath, ledGroupIface);
321
322 if (service.empty())
323 {
324 log<level::INFO>("No identify LED group found during ID button press",
325 entry("GROUP=%s", groupPath.c_str()));
326 return;
327 }
328
329 try
330 {
331 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(),
332 propertyIface, "Get");
333 method.append(ledGroupIface, "Asserted");
334 auto result = bus.call(method);
335
Patrick Williams5ed4cc02020-05-13 17:52:15 -0500336 std::variant<bool> state;
Matt Spinler69f93512018-11-26 14:55:58 -0600337 result.read(state);
338
Patrick Williams51f5e8b2020-05-13 11:33:44 -0500339 state = !std::get<bool>(state);
Matt Spinler69f93512018-11-26 14:55:58 -0600340
Patrick Williams51f5e8b2020-05-13 11:33:44 -0500341 log<level::INFO>("Changing ID LED group state on ID LED press",
342 entry("GROUP=%s", groupPath.c_str()),
343 entry("STATE=%d", std::get<bool>(state)));
Matt Spinler69f93512018-11-26 14:55:58 -0600344
345 method = bus.new_method_call(service.c_str(), groupPath.c_str(),
346 propertyIface, "Set");
347
348 method.append(ledGroupIface, "Asserted", state);
349 result = bus.call(method);
350 }
Patrick Williams6d724ce2021-10-06 12:40:26 -0500351 catch (const sdbusplus::exception::exception& e)
Matt Spinler69f93512018-11-26 14:55:58 -0600352 {
353 log<level::ERR>("Error toggling ID LED group on ID button press",
354 entry("ERROR=%s", e.what()));
355 }
356}
Matt Spinlerfb35a322018-11-26 14:30:30 -0600357} // namespace button
358} // namespace phosphor