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