Matt Spinler | fb35a32 | 2018-11-26 14:30:30 -0600 | [diff] [blame] | 1 | #include "button_handler.hpp" |
| 2 | |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 3 | #include "settings.hpp" |
| 4 | |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 5 | #include <phosphor-logging/log.hpp> |
| 6 | #include <xyz/openbmc_project/State/Chassis/server.hpp> |
| 7 | #include <xyz/openbmc_project/State/Host/server.hpp> |
Matt Spinler | fb35a32 | 2018-11-26 14:30:30 -0600 | [diff] [blame] | 8 | namespace phosphor |
| 9 | { |
| 10 | namespace button |
| 11 | { |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 12 | |
| 13 | namespace sdbusRule = sdbusplus::bus::match::rules; |
| 14 | using namespace sdbusplus::xyz::openbmc_project::State::server; |
| 15 | using namespace phosphor::logging; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 16 | |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 17 | constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis"; |
| 18 | constexpr auto hostIface = "xyz.openbmc_project.State.Host"; |
| 19 | constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power"; |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 20 | constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID"; |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 21 | constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset"; |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 22 | constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group"; |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 23 | constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/"; |
| 24 | constexpr auto hostSelectorIface = |
| 25 | "xyz.openbmc_project.Chassis.Buttons.HostSelector"; |
| 26 | constexpr auto debugHostSelectorIface = |
| 27 | "xyz.openbmc_project.Chassis.Buttons.DebugHostSelector"; |
| 28 | |
| 29 | constexpr auto propertyIface = "org.freedesktop.DBus.Properties"; |
| 30 | constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper"; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 31 | |
| 32 | constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper"; |
| 33 | constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper"; |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 34 | constexpr auto BMC_POSITION = 0; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 35 | |
Matt Spinler | fb35a32 | 2018-11-26 14:30:30 -0600 | [diff] [blame] | 36 | Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus) |
| 37 | { |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 38 | 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 Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 62 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 63 | { |
| 64 | // The button wasn't implemented |
| 65 | } |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 66 | |
| 67 | try |
| 68 | { |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 69 | 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 Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 81 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 82 | { |
| 83 | // The button wasn't implemented |
| 84 | } |
| 85 | |
| 86 | try |
| 87 | { |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 88 | 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 Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 100 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 101 | { |
| 102 | // The button wasn't implemented |
| 103 | } |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 104 | } |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 105 | bool Handler::isMultiHost() |
| 106 | { |
| 107 | // return true in case host selector object is available |
| 108 | return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty()); |
| 109 | } |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 110 | std::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 Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 123 | size_t Handler::getHostSelectorValue() |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 124 | { |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 125 | 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 | } |
| 153 | bool 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 Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 158 | auto method = bus.new_method_call( |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 159 | service.c_str(), chassisObjectName.c_str(), propertyIface, "Get"); |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 160 | method.append(chassisIface, "CurrentPowerState"); |
| 161 | auto result = bus.call(method); |
| 162 | |
Patrick Williams | 5ed4cc0 | 2020-05-13 17:52:15 -0500 | [diff] [blame] | 163 | std::variant<std::string> state; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 164 | result.read(state); |
| 165 | |
| 166 | return Chassis::PowerState::On == |
Patrick Williams | 51f5e8b | 2020-05-13 11:33:44 -0500 | [diff] [blame] | 167 | Chassis::convertPowerStateFromString(std::get<std::string>(state)); |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 168 | } |
| 169 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 170 | void Handler::handlePowerEvent(PowerEvent powerEventType) |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 171 | { |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 172 | std::string objPathName; |
| 173 | std::string dbusIfaceName; |
| 174 | std::string transitionName; |
| 175 | std::variant<Host::Transition, Chassis::Transition> transition; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 176 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 177 | size_t hostNumber = 0; |
| 178 | auto isMultiHostSystem = isMultiHost(); |
| 179 | if (isMultiHostSystem) |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 180 | { |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 181 | 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 Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 245 | } |
| 246 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 247 | case PowerEvent::resetPressed: { |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 248 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 249 | objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; |
| 250 | dbusIfaceName = hostIface; |
| 251 | transitionName = "RequestedHostTransition"; |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 252 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 253 | if (!poweredOn(hostNumber)) |
| 254 | { |
| 255 | log<level::INFO>("Power is off so ignoring reset button press"); |
| 256 | return; |
| 257 | } |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 258 | |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 259 | 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 | } |
| 277 | void Handler::powerPressed(sdbusplus::message::message& msg) |
| 278 | { |
| 279 | try |
| 280 | { |
| 281 | handlePowerEvent(PowerEvent::powerPressed); |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 282 | } |
Patrick Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 283 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 284 | { |
| 285 | log<level::ERR>("Failed power state change on a power button press", |
| 286 | entry("ERROR=%s", e.what())); |
| 287 | } |
| 288 | } |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 289 | void Handler::longPowerPressed(sdbusplus::message::message& msg) |
| 290 | { |
| 291 | try |
| 292 | { |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 293 | handlePowerEvent(PowerEvent::longPowerPressed); |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 294 | } |
Patrick Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 295 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 963c65f | 2018-11-26 14:46:41 -0600 | [diff] [blame] | 296 | { |
| 297 | log<level::ERR>("Failed powering off on long power button press", |
| 298 | entry("ERROR=%s", e.what())); |
| 299 | } |
Matt Spinler | fb35a32 | 2018-11-26 14:30:30 -0600 | [diff] [blame] | 300 | } |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 301 | |
| 302 | void Handler::resetPressed(sdbusplus::message::message& msg) |
| 303 | { |
| 304 | try |
| 305 | { |
Naveen Moses | 3bd1cfc | 2022-02-14 18:04:20 +0530 | [diff] [blame^] | 306 | handlePowerEvent(PowerEvent::resetPressed); |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 307 | } |
Patrick Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 308 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 06a5bdd | 2018-11-26 14:50:48 -0600 | [diff] [blame] | 309 | { |
| 310 | log<level::ERR>("Failed power state change on a reset button press", |
| 311 | entry("ERROR=%s", e.what())); |
| 312 | } |
| 313 | } |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 314 | |
| 315 | void 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 Williams | 5ed4cc0 | 2020-05-13 17:52:15 -0500 | [diff] [blame] | 336 | std::variant<bool> state; |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 337 | result.read(state); |
| 338 | |
Patrick Williams | 51f5e8b | 2020-05-13 11:33:44 -0500 | [diff] [blame] | 339 | state = !std::get<bool>(state); |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 340 | |
Patrick Williams | 51f5e8b | 2020-05-13 11:33:44 -0500 | [diff] [blame] | 341 | 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 Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 344 | |
| 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 Williams | 6d724ce | 2021-10-06 12:40:26 -0500 | [diff] [blame] | 351 | catch (const sdbusplus::exception::exception& e) |
Matt Spinler | 69f9351 | 2018-11-26 14:55:58 -0600 | [diff] [blame] | 352 | { |
| 353 | log<level::ERR>("Error toggling ID LED group on ID button press", |
| 354 | entry("ERROR=%s", e.what())); |
| 355 | } |
| 356 | } |
Matt Spinler | fb35a32 | 2018-11-26 14:30:30 -0600 | [diff] [blame] | 357 | } // namespace button |
| 358 | } // namespace phosphor |