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 { 201 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 202 dbusIfaceName = hostIface; 203 transitionName = "RequestedHostTransition"; 204 205 transition = Host::Transition::On; 206 207 if (poweredOn(hostNumber)) 208 { 209 transition = Host::Transition::Off; 210 } 211 log<level::INFO>("handlePowerEvent : handle power button press "); 212 213 break; 214 } 215 case PowerEvent::longPowerPressed: 216 { 217 dbusIfaceName = chassisIface; 218 transitionName = "RequestedPowerTransition"; 219 objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr; 220 transition = Chassis::Transition::Off; 221 222 /* multi host system : 223 hosts (1 to N) - host shutdown 224 bmc (0) - sled cycle 225 single host system : 226 host(0) - host shutdown 227 */ 228 if (isMultiHostSystem && (hostNumber == BMC_POSITION)) 229 { 230 #if CHASSIS_SYSTEM_RESET_ENABLED 231 objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr; 232 transition = Chassis::Transition::PowerCycle; 233 #else 234 return; 235 #endif 236 } 237 else if (!poweredOn(hostNumber)) 238 { 239 log<level::INFO>( 240 "Power is off so ignoring long power button press"); 241 return; 242 } 243 log<level::INFO>( 244 "handlePowerEvent : handle long power button press"); 245 246 break; 247 } 248 249 case PowerEvent::resetPressed: 250 { 251 objPathName = HOST_STATE_OBJECT_NAME + hostNumStr; 252 dbusIfaceName = hostIface; 253 transitionName = "RequestedHostTransition"; 254 255 if (!poweredOn(hostNumber)) 256 { 257 log<level::INFO>("Power is off so ignoring reset button press"); 258 return; 259 } 260 261 log<level::INFO>("Handling reset button press"); 262 transition = Host::Transition::Reboot; 263 break; 264 } 265 default: 266 { 267 log<level::ERR>( 268 "Invalid power event. skipping...", 269 entry("EVENT=%d", static_cast<int>(powerEventType))); 270 271 return; 272 } 273 } 274 auto service = getService(objPathName.c_str(), dbusIfaceName); 275 auto method = bus.new_method_call(service.c_str(), objPathName.c_str(), 276 propertyIface, "Set"); 277 method.append(dbusIfaceName, transitionName, transition); 278 bus.call(method); 279 } 280 void Handler::powerPressed(sdbusplus::message::message& /* msg */) 281 { 282 try 283 { 284 handlePowerEvent(PowerEvent::powerPressed); 285 } 286 catch (const sdbusplus::exception::exception& e) 287 { 288 log<level::ERR>("Failed power state change on a power button press", 289 entry("ERROR=%s", e.what())); 290 } 291 } 292 void Handler::longPowerPressed(sdbusplus::message::message& /* msg */) 293 { 294 try 295 { 296 handlePowerEvent(PowerEvent::longPowerPressed); 297 } 298 catch (const sdbusplus::exception::exception& e) 299 { 300 log<level::ERR>("Failed powering off on long power button press", 301 entry("ERROR=%s", e.what())); 302 } 303 } 304 305 void Handler::resetPressed(sdbusplus::message::message& /* msg */) 306 { 307 try 308 { 309 handlePowerEvent(PowerEvent::resetPressed); 310 } 311 catch (const sdbusplus::exception::exception& e) 312 { 313 log<level::ERR>("Failed power state change on a reset button press", 314 entry("ERROR=%s", e.what())); 315 } 316 } 317 318 void Handler::idPressed(sdbusplus::message::message& /* msg */) 319 { 320 std::string groupPath{ledGroupBasePath}; 321 groupPath += ID_LED_GROUP; 322 323 auto service = getService(groupPath, ledGroupIface); 324 325 if (service.empty()) 326 { 327 log<level::INFO>("No identify LED group found during ID button press", 328 entry("GROUP=%s", groupPath.c_str())); 329 return; 330 } 331 332 try 333 { 334 auto method = bus.new_method_call(service.c_str(), groupPath.c_str(), 335 propertyIface, "Get"); 336 method.append(ledGroupIface, "Asserted"); 337 auto result = bus.call(method); 338 339 std::variant<bool> state; 340 result.read(state); 341 342 state = !std::get<bool>(state); 343 344 log<level::INFO>("Changing ID LED group state on ID LED press", 345 entry("GROUP=%s", groupPath.c_str()), 346 entry("STATE=%d", std::get<bool>(state))); 347 348 method = bus.new_method_call(service.c_str(), groupPath.c_str(), 349 propertyIface, "Set"); 350 351 method.append(ledGroupIface, "Asserted", state); 352 result = bus.call(method); 353 } 354 catch (const sdbusplus::exception::exception& e) 355 { 356 log<level::ERR>("Error toggling ID LED group on ID button press", 357 entry("ERROR=%s", e.what())); 358 } 359 } 360 } // namespace button 361 } // namespace phosphor 362