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.DebugHostSelector"; 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 } 102 bool Handler::isMultiHost() 103 { 104 // return true in case host selector object is available 105 return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty()); 106 } 107 std::string Handler::getService(const std::string& path, 108 const std::string& interface) const 109 { 110 auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface, 111 "GetObject"); 112 method.append(path, std::vector{interface}); 113 auto result = bus.call(method); 114 115 std::map<std::string, std::vector<std::string>> objectData; 116 result.read(objectData); 117 118 return objectData.begin()->first; 119 } 120 size_t Handler::getHostSelectorValue() 121 { 122 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 123 124 if (HSService.empty()) 125 { 126 lg2::info("Host Selector dbus object not available"); 127 throw std::invalid_argument("Host selector dbus object not available"); 128 } 129 130 try 131 { 132 auto method = bus.new_method_call( 133 HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get"); 134 method.append(hostSelectorIface, "Position"); 135 auto result = bus.call(method); 136 137 std::variant<size_t> HSPositionVariant; 138 result.read(HSPositionVariant); 139 140 auto position = std::get<size_t>(HSPositionVariant); 141 return position; 142 } 143 catch (const sdbusplus::exception_t& e) 144 { 145 lg2::error("Error reading Host selector Position: {ERROR}", "ERROR", e); 146 throw; 147 } 148 } 149 bool Handler::poweredOn(size_t hostNumber) const 150 { 151 auto chassisObjectName = 152 CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber); 153 auto service = getService(chassisObjectName.c_str(), chassisIface); 154 auto method = bus.new_method_call( 155 service.c_str(), chassisObjectName.c_str(), propertyIface, "Get"); 156 method.append(chassisIface, "CurrentPowerState"); 157 auto result = bus.call(method); 158 159 std::variant<std::string> state; 160 result.read(state); 161 162 return Chassis::PowerState::On == 163 Chassis::convertPowerStateFromString(std::get<std::string>(state)); 164 } 165 166 void Handler::handlePowerEvent(PowerEvent powerEventType) 167 { 168 std::string objPathName; 169 std::string dbusIfaceName; 170 std::string transitionName; 171 std::variant<Host::Transition, Chassis::Transition> transition; 172 173 size_t hostNumber = 0; 174 auto isMultiHostSystem = isMultiHost(); 175 if (isMultiHostSystem) 176 { 177 hostNumber = getHostSelectorValue(); 178 lg2::info("Multi host system detected : {POSITION}", "POSITION", 179 hostNumber); 180 } 181 182 std::string hostNumStr = std::to_string(hostNumber); 183 184 // ignore power and reset button events if BMC is selected. 185 if (isMultiHostSystem && (hostNumber == BMC_POSITION) && 186 (powerEventType != PowerEvent::longPowerPressed)) 187 { 188 lg2::info( 189 "handlePowerEvent : BMC selected on multihost system. ignoring power and reset button events..."); 190 return; 191 } 192 193 switch (powerEventType) 194 { 195 case PowerEvent::powerReleased: 196 { 197 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 198 dbusIfaceName = hostIface; 199 transitionName = "RequestedHostTransition"; 200 201 transition = Host::Transition::On; 202 203 if (poweredOn(hostNumber)) 204 { 205 transition = Host::Transition::Off; 206 } 207 lg2::info("handlePowerEvent : handle power button press "); 208 209 break; 210 } 211 case PowerEvent::longPowerPressed: 212 { 213 dbusIfaceName = chassisIface; 214 transitionName = "RequestedPowerTransition"; 215 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr; 216 transition = Chassis::Transition::Off; 217 218 /* multi host system : 219 hosts (1 to N) - host shutdown 220 bmc (0) - sled cycle 221 single host system : 222 host(0) - host shutdown 223 */ 224 if (isMultiHostSystem && (hostNumber == BMC_POSITION)) 225 { 226 #if CHASSIS_SYSTEM_RESET_ENABLED 227 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr; 228 transition = Chassis::Transition::PowerCycle; 229 #else 230 return; 231 #endif 232 } 233 else if (!poweredOn(hostNumber)) 234 { 235 lg2::info("Power is off so ignoring long power button press"); 236 return; 237 } 238 lg2::info("handlePowerEvent : handle long power button press"); 239 240 break; 241 } 242 243 case PowerEvent::resetReleased: 244 { 245 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 246 dbusIfaceName = hostIface; 247 transitionName = "RequestedHostTransition"; 248 249 if (!poweredOn(hostNumber)) 250 { 251 lg2::info("Power is off so ignoring reset button press"); 252 return; 253 } 254 255 lg2::info("Handling reset button press"); 256 transition = Host::Transition::Reboot; 257 break; 258 } 259 default: 260 { 261 lg2::error("{EVENT} is invalid power event. skipping...", "EVENT", 262 static_cast<std::underlying_type_t<PowerEvent>>( 263 powerEventType)); 264 265 return; 266 } 267 } 268 auto service = getService(objPathName.c_str(), dbusIfaceName); 269 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(), 270 propertyIface, "Set"); 271 method.append(dbusIfaceName, transitionName, transition); 272 bus.call(method); 273 } 274 void Handler::powerReleased(sdbusplus::message_t& /* msg */) 275 { 276 try 277 { 278 handlePowerEvent(PowerEvent::powerReleased); 279 } 280 catch (const sdbusplus::exception_t& e) 281 { 282 lg2::error("Failed power state change on a power button press: {ERROR}", 283 "ERROR", e); 284 } 285 } 286 void Handler::longPowerPressed(sdbusplus::message_t& /* msg */) 287 { 288 try 289 { 290 handlePowerEvent(PowerEvent::longPowerPressed); 291 } 292 catch (const sdbusplus::exception_t& e) 293 { 294 lg2::error("Failed powering off on long power button press: {ERROR}", 295 "ERROR", e); 296 } 297 } 298 299 void Handler::resetReleased(sdbusplus::message_t& /* msg */) 300 { 301 try 302 { 303 handlePowerEvent(PowerEvent::resetReleased); 304 } 305 catch (const sdbusplus::exception_t& e) 306 { 307 lg2::error("Failed power state change on a reset button press: {ERROR}", 308 "ERROR", e); 309 } 310 } 311 312 void Handler::idReleased(sdbusplus::message_t& /* msg */) 313 { 314 std::string groupPath{ledGroupBasePath}; 315 groupPath += ID_LED_GROUP; 316 317 auto service = getService(groupPath, ledGroupIface); 318 319 if (service.empty()) 320 { 321 lg2::info("No found {GROUP} during ID button press:", "GROUP", 322 groupPath); 323 return; 324 } 325 326 try 327 { 328 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(), 329 propertyIface, "Get"); 330 method.append(ledGroupIface, "Asserted"); 331 auto result = bus.call(method); 332 333 std::variant<bool> state; 334 result.read(state); 335 336 state = !std::get<bool>(state); 337 338 lg2::info( 339 "Changing ID LED group state on ID LED press, GROUP = {GROUP}, STATE = {STATE}", 340 "GROUP", groupPath, "STATE", std::get<bool>(state)); 341 342 method = bus.new_method_call(service.c_str(), groupPath.c_str(), 343 propertyIface, "Set"); 344 345 method.append(ledGroupIface, "Asserted", state); 346 result = bus.call(method); 347 } 348 catch (const sdbusplus::exception_t& e) 349 { 350 lg2::error("Error toggling ID LED group on ID button press: {ERROR}", 351 "ERROR", e); 352 } 353 } 354 } // namespace button 355 } // namespace phosphor 356