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 transition = Host::Transition::Reboot; 288 break; 289 } 290 default: 291 { 292 lg2::error("{EVENT} is invalid power event. skipping...", "EVENT", 293 static_cast<std::underlying_type_t<PowerEvent>>( 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