1 #include "config.h" 2 3 #include "button_handler.hpp" 4 5 #include "power_button_profile_factory.hpp" 6 7 #include <phosphor-logging/lg2.hpp> 8 #include <xyz/openbmc_project/State/Chassis/server.hpp> 9 #include <xyz/openbmc_project/State/Host/server.hpp> 10 namespace phosphor 11 { 12 namespace button 13 { 14 15 namespace sdbusRule = sdbusplus::bus::match::rules; 16 using namespace sdbusplus::xyz::openbmc_project::State::server; 17 18 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis"; 19 constexpr auto hostIface = "xyz.openbmc_project.State.Host"; 20 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power"; 21 constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID"; 22 constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset"; 23 constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group"; 24 constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/"; 25 constexpr auto hostSelectorIface = 26 "xyz.openbmc_project.Chassis.Buttons.HostSelector"; 27 constexpr auto debugHostSelectorIface = 28 "xyz.openbmc_project.Chassis.Buttons.Button"; 29 30 constexpr auto propertyIface = "org.freedesktop.DBus.Properties"; 31 constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper"; 32 33 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper"; 34 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper"; 35 constexpr auto BMC_POSITION = 0; 36 37 Handler::Handler(sdbusplus::bus_t& bus) : bus(bus) 38 { 39 try 40 { 41 if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty()) 42 { 43 lg2::info("Starting power button handler"); 44 45 // Check for a custom handler 46 powerButtonProfile = 47 PowerButtonProfileFactory::instance().createProfile(bus); 48 49 if (!powerButtonProfile) 50 { 51 powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 52 bus, 53 sdbusRule::type::signal() + sdbusRule::member("Released") + 54 sdbusRule::path(POWER_DBUS_OBJECT_NAME) + 55 sdbusRule::interface(powerButtonIface), 56 std::bind(std::mem_fn(&Handler::powerReleased), this, 57 std::placeholders::_1)); 58 } 59 } 60 } 61 catch (const sdbusplus::exception_t& e) 62 { 63 // The button wasn't implemented 64 } 65 66 try 67 { 68 if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty()) 69 { 70 lg2::info("Registering ID button handler"); 71 idButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 72 bus, 73 sdbusRule::type::signal() + sdbusRule::member("Released") + 74 sdbusRule::path(ID_DBUS_OBJECT_NAME) + 75 sdbusRule::interface(idButtonIface), 76 std::bind(std::mem_fn(&Handler::idReleased), this, 77 std::placeholders::_1)); 78 } 79 } 80 catch (const sdbusplus::exception_t& e) 81 { 82 // The button wasn't implemented 83 } 84 85 try 86 { 87 if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty()) 88 { 89 lg2::info("Registering reset button handler"); 90 resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 91 bus, 92 sdbusRule::type::signal() + sdbusRule::member("Released") + 93 sdbusRule::path(RESET_DBUS_OBJECT_NAME) + 94 sdbusRule::interface(resetButtonIface), 95 std::bind(std::mem_fn(&Handler::resetReleased), this, 96 std::placeholders::_1)); 97 } 98 } 99 catch (const sdbusplus::exception_t& e) 100 { 101 // The button wasn't implemented 102 } 103 try 104 { 105 if (!getService(DBG_HS_DBUS_OBJECT_NAME, debugHostSelectorIface) 106 .empty()) 107 { 108 lg2::info("Registering debug host selector button handler"); 109 debugHSButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 110 bus, 111 sdbusRule::type::signal() + sdbusRule::member("Released") + 112 sdbusRule::path(DBG_HS_DBUS_OBJECT_NAME) + 113 sdbusRule::interface(debugHostSelectorIface), 114 std::bind(std::mem_fn(&Handler::debugHostSelectorReleased), 115 this, std::placeholders::_1)); 116 } 117 } 118 catch (const sdbusplus::exception_t& e) 119 { 120 // The button wasn't implemented 121 } 122 } 123 bool Handler::isMultiHost() 124 { 125 // return true in case host selector object is available 126 return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty()); 127 } 128 std::string Handler::getService(const std::string& path, 129 const std::string& interface) const 130 { 131 auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface, 132 "GetObject"); 133 method.append(path, std::vector{interface}); 134 try 135 { 136 auto result = bus.call(method); 137 std::map<std::string, std::vector<std::string>> objectData; 138 result.read(objectData); 139 return objectData.begin()->first; 140 } 141 catch (const sdbusplus::exception_t& e) 142 { 143 return std::string(); 144 } 145 } 146 size_t Handler::getHostSelectorValue() 147 { 148 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 149 150 if (HSService.empty()) 151 { 152 lg2::info("Host selector dbus object not available"); 153 throw std::invalid_argument("Host selector dbus object not available"); 154 } 155 156 try 157 { 158 auto method = bus.new_method_call( 159 HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get"); 160 method.append(hostSelectorIface, "Position"); 161 auto result = bus.call(method); 162 163 std::variant<size_t> HSPositionVariant; 164 result.read(HSPositionVariant); 165 166 auto position = std::get<size_t>(HSPositionVariant); 167 return position; 168 } 169 catch (const sdbusplus::exception_t& e) 170 { 171 lg2::error("Error reading host selector position: {ERROR}", "ERROR", e); 172 throw; 173 } 174 } 175 bool Handler::poweredOn(size_t hostNumber) const 176 { 177 auto hostObjectName = HOST_STATE_OBJECT_NAME + std::to_string(hostNumber); 178 auto service = getService(hostObjectName.c_str(), hostIface); 179 auto method = bus.new_method_call(service.c_str(), hostObjectName.c_str(), 180 propertyIface, "Get"); 181 method.append(hostIface, "CurrentHostState"); 182 auto result = bus.call(method); 183 184 std::variant<std::string> state; 185 result.read(state); 186 187 return Host::HostState::Off != 188 Host::convertHostStateFromString(std::get<std::string>(state)); 189 } 190 191 void Handler::handlePowerEvent(PowerEvent powerEventType, 192 std::chrono::microseconds duration) 193 { 194 std::string objPathName; 195 std::string dbusIfaceName; 196 std::string transitionName; 197 std::variant<Host::Transition, Chassis::Transition> transition; 198 uint64_t durationMs = 199 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); 200 201 size_t hostNumber = 0; 202 auto isMultiHostSystem = isMultiHost(); 203 if (isMultiHostSystem) 204 { 205 hostNumber = getHostSelectorValue(); 206 lg2::info("Multi-host system detected : {POSITION}", "POSITION", 207 hostNumber); 208 } 209 210 std::string hostNumStr = std::to_string(hostNumber); 211 212 // ignore power and reset button events if BMC is selected. 213 if (isMultiHostSystem && (hostNumber == BMC_POSITION) && 214 (powerEventType != PowerEvent::powerReleased) && 215 (durationMs <= LONG_PRESS_TIME_MS)) 216 { 217 lg2::info( 218 "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events..."); 219 return; 220 } 221 222 switch (powerEventType) 223 { 224 case PowerEvent::powerReleased: 225 { 226 if (durationMs <= LONG_PRESS_TIME_MS) 227 { 228 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 229 dbusIfaceName = hostIface; 230 transitionName = "RequestedHostTransition"; 231 232 transition = Host::Transition::On; 233 234 if (poweredOn(hostNumber)) 235 { 236 transition = Host::Transition::Off; 237 } 238 lg2::info("handlePowerEvent : Handle power button press "); 239 240 break; 241 } 242 else 243 { 244 dbusIfaceName = chassisIface; 245 transitionName = "RequestedPowerTransition"; 246 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr; 247 transition = Chassis::Transition::Off; 248 249 /* multi host system : 250 hosts (1 to N) - host shutdown 251 bmc (0) - sled cycle 252 single host system : 253 host(0) - host shutdown 254 */ 255 if (isMultiHostSystem && (hostNumber == BMC_POSITION)) 256 { 257 #if CHASSIS_SYSTEM_RESET_ENABLED 258 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr; 259 transition = Chassis::Transition::PowerCycle; 260 #else 261 return; 262 #endif 263 } 264 else if (!poweredOn(hostNumber)) 265 { 266 lg2::info( 267 "Power is off so ignoring long power button press"); 268 return; 269 } 270 lg2::info("handlePowerEvent : handle long power button press"); 271 break; 272 } 273 } 274 case PowerEvent::resetReleased: 275 { 276 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 277 dbusIfaceName = hostIface; 278 transitionName = "RequestedHostTransition"; 279 280 if (!poweredOn(hostNumber)) 281 { 282 lg2::info("Power is off so ignoring reset button press"); 283 return; 284 } 285 286 lg2::info("Handling reset button press"); 287 #ifdef ENABLE_RESET_BUTTON_DO_WARM_REBOOT 288 transition = Host::Transition::ForceWarmReboot; 289 #else 290 transition = Host::Transition::Reboot; 291 #endif 292 break; 293 } 294 default: 295 { 296 lg2::error("{EVENT} is invalid power event. skipping...", "EVENT", 297 static_cast<std::underlying_type_t<PowerEvent>>( 298 powerEventType)); 299 300 return; 301 } 302 } 303 auto service = getService(objPathName.c_str(), dbusIfaceName); 304 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(), 305 propertyIface, "Set"); 306 method.append(dbusIfaceName, transitionName, transition); 307 bus.call(method); 308 } 309 void Handler::powerReleased(sdbusplus::message_t& msg) 310 { 311 try 312 { 313 uint64_t time; 314 msg.read(time); 315 316 handlePowerEvent(PowerEvent::powerReleased, 317 std::chrono::microseconds(time)); 318 } 319 catch (const sdbusplus::exception_t& e) 320 { 321 lg2::error("Failed power state change on a power button press: {ERROR}", 322 "ERROR", e); 323 } 324 } 325 326 void Handler::resetReleased(sdbusplus::message_t& /* msg */) 327 { 328 try 329 { 330 // No need to calculate duration, set to 0. 331 handlePowerEvent(PowerEvent::resetReleased, 332 std::chrono::microseconds(0)); 333 } 334 catch (const sdbusplus::exception_t& e) 335 { 336 lg2::error("Failed power state change on a reset button press: {ERROR}", 337 "ERROR", e); 338 } 339 } 340 341 void Handler::idReleased(sdbusplus::message_t& /* msg */) 342 { 343 std::string groupPath{ledGroupBasePath}; 344 groupPath += ID_LED_GROUP; 345 346 auto service = getService(groupPath, ledGroupIface); 347 348 if (service.empty()) 349 { 350 lg2::info("No found {GROUP} during ID button press:", "GROUP", 351 groupPath); 352 return; 353 } 354 355 try 356 { 357 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(), 358 propertyIface, "Get"); 359 method.append(ledGroupIface, "Asserted"); 360 auto result = bus.call(method); 361 362 std::variant<bool> state; 363 result.read(state); 364 365 state = !std::get<bool>(state); 366 367 lg2::info( 368 "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}", 369 "GROUP", groupPath, "STATE", std::get<bool>(state)); 370 371 method = bus.new_method_call(service.c_str(), groupPath.c_str(), 372 propertyIface, "Set"); 373 374 method.append(ledGroupIface, "Asserted", state); 375 result = bus.call(method); 376 } 377 catch (const sdbusplus::exception_t& e) 378 { 379 lg2::error("Error toggling ID LED group on ID button press: {ERROR}", 380 "ERROR", e); 381 } 382 } 383 384 void Handler::increaseHostSelectorPosition() 385 { 386 try 387 { 388 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 389 390 if (HSService.empty()) 391 { 392 lg2::error("Host selector service not available"); 393 return; 394 } 395 396 auto method = 397 bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME, 398 phosphor::button::propertyIface, "GetAll"); 399 method.append(phosphor::button::hostSelectorIface); 400 auto result = bus.call(method); 401 std::unordered_map<std::string, std::variant<size_t>> properties; 402 result.read(properties); 403 404 auto maxPosition = std::get<size_t>(properties.at("MaxPosition")); 405 auto position = std::get<size_t>(properties.at("Position")); 406 407 std::variant<size_t> HSPositionVariant = 408 (position < maxPosition) ? (position + 1) : 0; 409 410 method = bus.new_method_call(HSService.c_str(), HS_DBUS_OBJECT_NAME, 411 phosphor::button::propertyIface, "Set"); 412 method.append(phosphor::button::hostSelectorIface, "Position"); 413 414 method.append(HSPositionVariant); 415 result = bus.call(method); 416 } 417 catch (const sdbusplus::exception_t& e) 418 { 419 lg2::error("Error modifying host selector position : {ERROR}", "ERROR", 420 e); 421 } 422 } 423 424 void Handler::debugHostSelectorReleased(sdbusplus::message_t& /* msg */) 425 { 426 try 427 { 428 increaseHostSelectorPosition(); 429 } 430 catch (const sdbusplus::exception_t& e) 431 { 432 lg2::error( 433 "Failed power process debug host selector button press : {ERROR}", 434 "ERROR", e); 435 } 436 } 437 438 } // namespace button 439 } // namespace phosphor 440