1 #include "button_handler.hpp" 2 3 #include "settings.hpp" 4 5 #include <phosphor-logging/log.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 using namespace phosphor::logging; 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.DebugHostSelector"; 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::bus& bus) : bus(bus) 37 { 38 try 39 { 40 if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty()) 41 { 42 log<level::INFO>("Starting power button handler"); 43 powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 44 bus, 45 sdbusRule::type::signal() + sdbusRule::member("Released") + 46 sdbusRule::path(POWER_DBUS_OBJECT_NAME) + 47 sdbusRule::interface(powerButtonIface), 48 std::bind(std::mem_fn(&Handler::powerPressed), this, 49 std::placeholders::_1)); 50 51 powerButtonLongPressReleased = 52 std::make_unique<sdbusplus::bus::match_t>( 53 bus, 54 sdbusRule::type::signal() + 55 sdbusRule::member("PressedLong") + 56 sdbusRule::path(POWER_DBUS_OBJECT_NAME) + 57 sdbusRule::interface(powerButtonIface), 58 std::bind(std::mem_fn(&Handler::longPowerPressed), this, 59 std::placeholders::_1)); 60 } 61 } 62 catch (const sdbusplus::exception::exception& e) 63 { 64 // The button wasn't implemented 65 } 66 67 try 68 { 69 if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty()) 70 { 71 log<level::INFO>("Registering ID button handler"); 72 idButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 73 bus, 74 sdbusRule::type::signal() + sdbusRule::member("Released") + 75 sdbusRule::path(ID_DBUS_OBJECT_NAME) + 76 sdbusRule::interface(idButtonIface), 77 std::bind(std::mem_fn(&Handler::idPressed), this, 78 std::placeholders::_1)); 79 } 80 } 81 catch (const sdbusplus::exception::exception& e) 82 { 83 // The button wasn't implemented 84 } 85 86 try 87 { 88 if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty()) 89 { 90 log<level::INFO>("Registering reset button handler"); 91 resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>( 92 bus, 93 sdbusRule::type::signal() + sdbusRule::member("Released") + 94 sdbusRule::path(RESET_DBUS_OBJECT_NAME) + 95 sdbusRule::interface(resetButtonIface), 96 std::bind(std::mem_fn(&Handler::resetPressed), this, 97 std::placeholders::_1)); 98 } 99 } 100 catch (const sdbusplus::exception::exception& e) 101 { 102 // The button wasn't implemented 103 } 104 } 105 bool Handler::isMultiHost() 106 { 107 // return true in case host selector object is available 108 return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty()); 109 } 110 std::string Handler::getService(const std::string& path, 111 const std::string& interface) const 112 { 113 auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface, 114 "GetObject"); 115 method.append(path, std::vector{interface}); 116 auto result = bus.call(method); 117 118 std::map<std::string, std::vector<std::string>> objectData; 119 result.read(objectData); 120 121 return objectData.begin()->first; 122 } 123 size_t Handler::getHostSelectorValue() 124 { 125 auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface); 126 127 if (HSService.empty()) 128 { 129 log<level::INFO>("Host Selector dbus object not available"); 130 throw std::invalid_argument("Host selector dbus object not available"); 131 } 132 133 try 134 { 135 auto method = bus.new_method_call( 136 HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get"); 137 method.append(hostSelectorIface, "Position"); 138 auto result = bus.call(method); 139 140 std::variant<size_t> HSPositionVariant; 141 result.read(HSPositionVariant); 142 143 auto position = std::get<size_t>(HSPositionVariant); 144 return position; 145 } 146 catch (const sdbusplus::exception::exception& e) 147 { 148 log<level::ERR>("Error reading Host selector Position", 149 entry("ERROR=%s", e.what())); 150 throw; 151 } 152 } 153 bool Handler::poweredOn(size_t hostNumber) const 154 { 155 auto chassisObjectName = 156 CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber); 157 auto service = getService(chassisObjectName.c_str(), chassisIface); 158 auto method = bus.new_method_call( 159 service.c_str(), chassisObjectName.c_str(), propertyIface, "Get"); 160 method.append(chassisIface, "CurrentPowerState"); 161 auto result = bus.call(method); 162 163 std::variant<std::string> state; 164 result.read(state); 165 166 return Chassis::PowerState::On == 167 Chassis::convertPowerStateFromString(std::get<std::string>(state)); 168 } 169 170 void Handler::handlePowerEvent(PowerEvent powerEventType) 171 { 172 std::string objPathName; 173 std::string dbusIfaceName; 174 std::string transitionName; 175 std::variant<Host::Transition, Chassis::Transition> transition; 176 177 size_t hostNumber = 0; 178 auto isMultiHostSystem = isMultiHost(); 179 if (isMultiHostSystem) 180 { 181 hostNumber = getHostSelectorValue(); 182 log<level::INFO>("Multi host system detected : ", 183 entry("POSITION=%d", hostNumber)); 184 } 185 186 std::string hostNumStr = std::to_string(hostNumber); 187 188 // ignore power and reset button events if BMC is selected. 189 if (isMultiHostSystem && (hostNumber == BMC_POSITION) && 190 (powerEventType != PowerEvent::longPowerPressed)) 191 { 192 log<level::INFO>("handlePowerEvent : BMC selected on multihost system." 193 "ignoring power and reset button events..."); 194 return; 195 } 196 197 switch (powerEventType) 198 { 199 case PowerEvent::powerPressed: { 200 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 201 dbusIfaceName = hostIface; 202 transitionName = "RequestedHostTransition"; 203 204 transition = Host::Transition::On; 205 206 if (poweredOn(hostNumber)) 207 { 208 transition = Host::Transition::Off; 209 } 210 log<level::INFO>("handlePowerEvent : handle power button press "); 211 212 break; 213 } 214 case PowerEvent::longPowerPressed: { 215 dbusIfaceName = chassisIface; 216 transitionName = "RequestedPowerTransition"; 217 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr; 218 transition = Chassis::Transition::Off; 219 220 /* multi host system : 221 hosts (1 to N) - host shutdown 222 bmc (0) - sled cycle 223 single host system : 224 host(0) - host shutdown 225 */ 226 if (isMultiHostSystem && (hostNumber == BMC_POSITION)) 227 { 228 #if CHASSIS_SYSTEM_RESET_ENABLED 229 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr; 230 transition = Chassis::Transition::PowerCycle; 231 #else 232 return; 233 #endif 234 } 235 else if (!poweredOn(hostNumber)) 236 { 237 log<level::INFO>( 238 "Power is off so ignoring long power button press"); 239 return; 240 } 241 log<level::INFO>( 242 "handlePowerEvent : handle long power button press"); 243 244 break; 245 } 246 247 case PowerEvent::resetPressed: { 248 249 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 250 dbusIfaceName = hostIface; 251 transitionName = "RequestedHostTransition"; 252 253 if (!poweredOn(hostNumber)) 254 { 255 log<level::INFO>("Power is off so ignoring reset button press"); 256 return; 257 } 258 259 log<level::INFO>("Handling reset button press"); 260 transition = Host::Transition::Reboot; 261 break; 262 } 263 default: { 264 log<level::ERR>( 265 "Invalid power event. skipping...", 266 entry("EVENT=%d", static_cast<int>(powerEventType))); 267 268 return; 269 } 270 } 271 auto service = getService(objPathName.c_str(), dbusIfaceName); 272 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(), 273 propertyIface, "Set"); 274 method.append(dbusIfaceName, transitionName, transition); 275 bus.call(method); 276 } 277 void Handler::powerPressed(sdbusplus::message::message& msg) 278 { 279 try 280 { 281 handlePowerEvent(PowerEvent::powerPressed); 282 } 283 catch (const sdbusplus::exception::exception& e) 284 { 285 log<level::ERR>("Failed power state change on a power button press", 286 entry("ERROR=%s", e.what())); 287 } 288 } 289 void Handler::longPowerPressed(sdbusplus::message::message& msg) 290 { 291 try 292 { 293 handlePowerEvent(PowerEvent::longPowerPressed); 294 } 295 catch (const sdbusplus::exception::exception& e) 296 { 297 log<level::ERR>("Failed powering off on long power button press", 298 entry("ERROR=%s", e.what())); 299 } 300 } 301 302 void Handler::resetPressed(sdbusplus::message::message& msg) 303 { 304 try 305 { 306 handlePowerEvent(PowerEvent::resetPressed); 307 } 308 catch (const sdbusplus::exception::exception& e) 309 { 310 log<level::ERR>("Failed power state change on a reset button press", 311 entry("ERROR=%s", e.what())); 312 } 313 } 314 315 void Handler::idPressed(sdbusplus::message::message& msg) 316 { 317 std::string groupPath{ledGroupBasePath}; 318 groupPath += ID_LED_GROUP; 319 320 auto service = getService(groupPath, ledGroupIface); 321 322 if (service.empty()) 323 { 324 log<level::INFO>("No identify LED group found during ID button press", 325 entry("GROUP=%s", groupPath.c_str())); 326 return; 327 } 328 329 try 330 { 331 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(), 332 propertyIface, "Get"); 333 method.append(ledGroupIface, "Asserted"); 334 auto result = bus.call(method); 335 336 std::variant<bool> state; 337 result.read(state); 338 339 state = !std::get<bool>(state); 340 341 log<level::INFO>("Changing ID LED group state on ID LED press", 342 entry("GROUP=%s", groupPath.c_str()), 343 entry("STATE=%d", std::get<bool>(state))); 344 345 method = bus.new_method_call(service.c_str(), groupPath.c_str(), 346 propertyIface, "Set"); 347 348 method.append(ledGroupIface, "Asserted", state); 349 result = bus.call(method); 350 } 351 catch (const sdbusplus::exception::exception& e) 352 { 353 log<level::ERR>("Error toggling ID LED group on ID button press", 354 entry("ERROR=%s", e.what())); 355 } 356 } 357 } // namespace button 358 } // namespace phosphor 359