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 try 133 { 134 auto result = bus.call(method); 135 std::map<std::string, std::vector<std::string>> objectData; 136 result.read(objectData); 137 return objectData.begin()->first; 138 } 139 catch (const sdbusplus::exception::exception& e) 140 { 141 return std::string(); 142 } 143 } 144 size_t Handler::getHostSelectorValue() 145 { 146 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 147 148 if (HSService.empty()) 149 { 150 lg2::info("Host selector dbus object not available"); 151 throw std::invalid_argument("Host selector dbus object not available"); 152 } 153 154 try 155 { 156 auto method = bus.new_method_call( 157 HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get"); 158 method.append(hostSelectorIface, "Position"); 159 auto result = bus.call(method); 160 161 std::variant<size_t> HSPositionVariant; 162 result.read(HSPositionVariant); 163 164 auto position = std::get<size_t>(HSPositionVariant); 165 return position; 166 } 167 catch (const sdbusplus::exception_t& e) 168 { 169 lg2::error("Error reading host selector position: {ERROR}", "ERROR", e); 170 throw; 171 } 172 } 173 bool Handler::poweredOn(size_t hostNumber) const 174 { 175 auto chassisObjectName = 176 CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber); 177 auto service = getService(chassisObjectName.c_str(), chassisIface); 178 auto method = bus.new_method_call( 179 service.c_str(), chassisObjectName.c_str(), propertyIface, "Get"); 180 method.append(chassisIface, "CurrentPowerState"); 181 auto result = bus.call(method); 182 183 std::variant<std::string> state; 184 result.read(state); 185 186 return Chassis::PowerState::On == 187 Chassis::convertPowerStateFromString(std::get<std::string>(state)); 188 } 189 190 void Handler::handlePowerEvent(PowerEvent powerEventType) 191 { 192 std::string objPathName; 193 std::string dbusIfaceName; 194 std::string transitionName; 195 std::variant<Host::Transition, Chassis::Transition> transition; 196 197 size_t hostNumber = 0; 198 auto isMultiHostSystem = isMultiHost(); 199 if (isMultiHostSystem) 200 { 201 hostNumber = getHostSelectorValue(); 202 lg2::info("Multi-host system detected : {POSITION}", "POSITION", 203 hostNumber); 204 } 205 206 std::string hostNumStr = std::to_string(hostNumber); 207 208 // ignore power and reset button events if BMC is selected. 209 if (isMultiHostSystem && (hostNumber == BMC_POSITION) && 210 (powerEventType != PowerEvent::longPowerReleased)) 211 { 212 lg2::info( 213 "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events..."); 214 return; 215 } 216 217 switch (powerEventType) 218 { 219 case PowerEvent::powerReleased: 220 { 221 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 222 dbusIfaceName = hostIface; 223 transitionName = "RequestedHostTransition"; 224 225 transition = Host::Transition::On; 226 227 if (poweredOn(hostNumber)) 228 { 229 transition = Host::Transition::Off; 230 } 231 lg2::info("handlePowerEvent : Handle power button press "); 232 233 break; 234 } 235 case PowerEvent::longPowerReleased: 236 { 237 dbusIfaceName = chassisIface; 238 transitionName = "RequestedPowerTransition"; 239 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr; 240 transition = Chassis::Transition::Off; 241 242 /* multi host system : 243 hosts (1 to N) - host shutdown 244 bmc (0) - sled cycle 245 single host system : 246 host(0) - host shutdown 247 */ 248 if (isMultiHostSystem && (hostNumber == BMC_POSITION)) 249 { 250 #if CHASSIS_SYSTEM_RESET_ENABLED 251 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr; 252 transition = Chassis::Transition::PowerCycle; 253 #else 254 return; 255 #endif 256 } 257 else if (!poweredOn(hostNumber)) 258 { 259 lg2::info("Power is off so ignoring long power button press"); 260 return; 261 } 262 lg2::info("handlePowerEvent : handle long power button press"); 263 264 break; 265 } 266 267 case PowerEvent::resetReleased: 268 { 269 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 270 dbusIfaceName = hostIface; 271 transitionName = "RequestedHostTransition"; 272 273 if (!poweredOn(hostNumber)) 274 { 275 lg2::info("Power is off so ignoring reset button press"); 276 return; 277 } 278 279 lg2::info("Handling reset button press"); 280 transition = Host::Transition::Reboot; 281 break; 282 } 283 default: 284 { 285 lg2::error("{EVENT} is invalid power event. skipping...", "EVENT", 286 static_cast<std::underlying_type_t<PowerEvent>>( 287 powerEventType)); 288 289 return; 290 } 291 } 292 auto service = getService(objPathName.c_str(), dbusIfaceName); 293 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(), 294 propertyIface, "Set"); 295 method.append(dbusIfaceName, transitionName, transition); 296 bus.call(method); 297 } 298 void Handler::powerReleased(sdbusplus::message_t& /* msg */) 299 { 300 try 301 { 302 handlePowerEvent(PowerEvent::powerReleased); 303 } 304 catch (const sdbusplus::exception_t& e) 305 { 306 lg2::error("Failed power state change on a power button press: {ERROR}", 307 "ERROR", e); 308 } 309 } 310 void Handler::longPowerPressed(sdbusplus::message_t& /* msg */) 311 { 312 try 313 { 314 handlePowerEvent(PowerEvent::longPowerPressed); 315 } 316 catch (const sdbusplus::exception_t& e) 317 { 318 lg2::error("Failed powering off on long power button press: {ERROR}", 319 "ERROR", e); 320 } 321 } 322 323 void Handler::resetReleased(sdbusplus::message_t& /* msg */) 324 { 325 try 326 { 327 handlePowerEvent(PowerEvent::resetReleased); 328 } 329 catch (const sdbusplus::exception_t& e) 330 { 331 lg2::error("Failed power state change on a reset button press: {ERROR}", 332 "ERROR", e); 333 } 334 } 335 336 void Handler::idReleased(sdbusplus::message_t& /* msg */) 337 { 338 std::string groupPath{ledGroupBasePath}; 339 groupPath += ID_LED_GROUP; 340 341 auto service = getService(groupPath, ledGroupIface); 342 343 if (service.empty()) 344 { 345 lg2::info("No found {GROUP} during ID button press:", "GROUP", 346 groupPath); 347 return; 348 } 349 350 try 351 { 352 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(), 353 propertyIface, "Get"); 354 method.append(ledGroupIface, "Asserted"); 355 auto result = bus.call(method); 356 357 std::variant<bool> state; 358 result.read(state); 359 360 state = !std::get<bool>(state); 361 362 lg2::info( 363 "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}", 364 "GROUP", groupPath, "STATE", std::get<bool>(state)); 365 366 method = bus.new_method_call(service.c_str(), groupPath.c_str(), 367 propertyIface, "Set"); 368 369 method.append(ledGroupIface, "Asserted", state); 370 result = bus.call(method); 371 } 372 catch (const sdbusplus::exception_t& e) 373 { 374 lg2::error("Error toggling ID LED group on ID button press: {ERROR}", 375 "ERROR", e); 376 } 377 } 378 379 void Handler::increaseHostSelectorPosition() 380 { 381 try 382 { 383 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 384 385 if (HSService.empty()) 386 { 387 lg2::error("Host selector service not available"); 388 return; 389 } 390 391 auto method = 392 bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME, 393 phosphor::button::propertyIface, "GetAll"); 394 method.append(phosphor::button::hostSelectorIface); 395 auto result = bus.call(method); 396 std::unordered_map<std::string, std::variant<size_t>> properties; 397 result.read(properties); 398 399 auto maxPosition = std::get<size_t>(properties.at("MaxPosition")); 400 auto position = std::get<size_t>(properties.at("Position")); 401 402 std::variant<size_t> HSPositionVariant = 403 (position < maxPosition) ? (position + 1) : 0; 404 405 method = bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME, 406 phosphor::button::propertyIface, "Set"); 407 method.append(phosphor::button::hostSelectorIface, "Position"); 408 409 method.append(HSPositionVariant); 410 result = bus.call(method); 411 } 412 catch (const sdbusplus::exception::exception& e) 413 { 414 lg2::error("Error modifying host selector position : {ERROR}", "ERROR", 415 e); 416 } 417 } 418 419 void Handler::debugHostSelectorReleased(sdbusplus::message::message& /* msg */) 420 { 421 try 422 { 423 increaseHostSelectorPosition(); 424 } 425 catch (const sdbusplus::exception::exception& e) 426 { 427 lg2::error( 428 "Failed power process debug host selector button press : {ERROR}", 429 "ERROR", e); 430 } 431 } 432 433 } // namespace button 434 } // namespace phosphor 435