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