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