xref: /openbmc/phosphor-buttons/src/host_then_chassis_poweroff.cpp (revision d36b6b1d3887f8545b1558f8f0d946a9c9785a72)
11a309f77SMatt Spinler #include "host_then_chassis_poweroff.hpp"
21a309f77SMatt Spinler 
315c60e2fSDelphine CC Chiu #include "config.hpp"
41a309f77SMatt Spinler #include "power_button_profile_factory.hpp"
51a309f77SMatt Spinler 
61a309f77SMatt Spinler #include <phosphor-logging/lg2.hpp>
71a309f77SMatt Spinler #include <xyz/openbmc_project/State/BMC/server.hpp>
81a309f77SMatt Spinler #include <xyz/openbmc_project/State/Chassis/server.hpp>
91a309f77SMatt Spinler 
101a309f77SMatt Spinler namespace phosphor::button
111a309f77SMatt Spinler {
121a309f77SMatt Spinler 
131a309f77SMatt Spinler // Register the profile with the factory.
141a309f77SMatt Spinler static PowerButtonProfileRegister<HostThenChassisPowerOff> profileRegister;
151a309f77SMatt Spinler 
161a309f77SMatt Spinler namespace service
171a309f77SMatt Spinler {
181a309f77SMatt Spinler constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
199c276560SPatrick Williams constexpr auto chassisState = "xyz.openbmc_project.State.Chassis0";
201a309f77SMatt Spinler constexpr auto hostState = "xyz.openbmc_project.State.Host";
211a309f77SMatt Spinler } // namespace service
221a309f77SMatt Spinler 
231a309f77SMatt Spinler namespace object_path
241a309f77SMatt Spinler {
251a309f77SMatt Spinler constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
261a309f77SMatt Spinler constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
271a309f77SMatt Spinler constexpr auto hostState = "/xyz/openbmc_project/state/host0";
281a309f77SMatt Spinler } // namespace object_path
291a309f77SMatt Spinler 
301a309f77SMatt Spinler namespace interface
311a309f77SMatt Spinler {
321a309f77SMatt Spinler constexpr auto property = "org.freedesktop.DBus.Properties";
331a309f77SMatt Spinler constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
341a309f77SMatt Spinler constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
351a309f77SMatt Spinler constexpr auto hostState = "xyz.openbmc_project.State.Host";
361a309f77SMatt Spinler } // namespace interface
371a309f77SMatt Spinler 
381a309f77SMatt Spinler using namespace sdbusplus::xyz::openbmc_project::State::server;
391a309f77SMatt Spinler 
pressed()401a309f77SMatt Spinler void HostThenChassisPowerOff::pressed()
411a309f77SMatt Spinler {
421a309f77SMatt Spinler     lg2::info("Power button pressed");
431a309f77SMatt Spinler 
441a309f77SMatt Spinler     try
451a309f77SMatt Spinler     {
461a309f77SMatt Spinler         // If power not on - power on
471a309f77SMatt Spinler         if (!isPoweredOn())
481a309f77SMatt Spinler         {
491a309f77SMatt Spinler             if (!isBmcReady())
501a309f77SMatt Spinler             {
511a309f77SMatt Spinler                 lg2::error("BMC not at ready state yet, cannot power on");
521a309f77SMatt Spinler                 return;
531a309f77SMatt Spinler             }
541a309f77SMatt Spinler 
551a309f77SMatt Spinler             state = PowerOpState::powerOnPress;
561a309f77SMatt Spinler             powerOn();
571a309f77SMatt Spinler             return;
581a309f77SMatt Spinler         }
591a309f77SMatt Spinler     }
601a309f77SMatt Spinler     catch (const sdbusplus::exception_t& e)
611a309f77SMatt Spinler     {
621a309f77SMatt Spinler         lg2::error(
631a309f77SMatt Spinler             "Exception while processing power button press. Cannot continue");
641a309f77SMatt Spinler         return;
651a309f77SMatt Spinler     }
661a309f77SMatt Spinler 
671a309f77SMatt Spinler     // Power is on ...
681a309f77SMatt Spinler 
691a309f77SMatt Spinler     if (state == PowerOpState::buttonNotPressed)
701a309f77SMatt Spinler     {
711a309f77SMatt Spinler         lg2::info("Starting countdown to power off");
721a309f77SMatt Spinler         state = PowerOpState::buttonPressed;
731a309f77SMatt Spinler         setHostOffTime();
741a309f77SMatt Spinler         timer.restart(pollInterval);
751a309f77SMatt Spinler     }
761a309f77SMatt Spinler 
771a309f77SMatt Spinler     // Button press during host off to chassis off window.
781a309f77SMatt Spinler     // Causes a chassis power off.
791a309f77SMatt Spinler     else if (state == PowerOpState::buttonReleasedHostToChassisOffWindow)
801a309f77SMatt Spinler     {
811a309f77SMatt Spinler         lg2::info("Starting chassis power off due to button press");
821a309f77SMatt Spinler         state = PowerOpState::chassisOffStarted;
831a309f77SMatt Spinler         timer.setEnabled(false);
841a309f77SMatt Spinler         chassisPowerOff();
851a309f77SMatt Spinler     }
861a309f77SMatt Spinler }
871a309f77SMatt Spinler 
released(uint64_t)881a309f77SMatt Spinler void HostThenChassisPowerOff::released(uint64_t /*pressTimeMS*/)
891a309f77SMatt Spinler {
901a309f77SMatt Spinler     lg2::info("Power button released");
911a309f77SMatt Spinler 
921a309f77SMatt Spinler     // Button released in the host to chassis off window.
931a309f77SMatt Spinler     // Timer continues to run in case button is pressed again
941a309f77SMatt Spinler     // in the window.
951a309f77SMatt Spinler     if (state == PowerOpState::buttonPressedHostOffStarted)
961a309f77SMatt Spinler     {
971a309f77SMatt Spinler         state = PowerOpState::buttonReleasedHostToChassisOffWindow;
981a309f77SMatt Spinler         return;
991a309f77SMatt Spinler     }
1001a309f77SMatt Spinler 
1011a309f77SMatt Spinler     state = PowerOpState::buttonNotPressed;
1021a309f77SMatt Spinler     timer.setEnabled(false);
1031a309f77SMatt Spinler }
1041a309f77SMatt Spinler 
timerHandler()1051a309f77SMatt Spinler void HostThenChassisPowerOff::timerHandler()
1061a309f77SMatt Spinler {
1071a309f77SMatt Spinler     const auto now = std::chrono::steady_clock::now();
1081a309f77SMatt Spinler 
1091a309f77SMatt Spinler     if ((state == PowerOpState::buttonPressed) && (now >= hostOffTime))
1101a309f77SMatt Spinler     {
1111a309f77SMatt Spinler         // Start the host power off and start the chassis
1121a309f77SMatt Spinler         // power off countdown.
1131a309f77SMatt Spinler         state = PowerOpState::buttonPressedHostOffStarted;
1141a309f77SMatt Spinler         setChassisOffTime();
1151a309f77SMatt Spinler         hostPowerOff();
1161a309f77SMatt Spinler     }
1171a309f77SMatt Spinler     else if ((state == PowerOpState::buttonPressedHostOffStarted) &&
1181a309f77SMatt Spinler              (now >= chassisOffTime))
1191a309f77SMatt Spinler     {
1201a309f77SMatt Spinler         // Button still pressed and it passed the chassis off time.
1211a309f77SMatt Spinler         // Issue the chassis power off.
1221a309f77SMatt Spinler         state = PowerOpState::chassisOffStarted;
1231a309f77SMatt Spinler         timer.setEnabled(false);
1241a309f77SMatt Spinler         chassisPowerOff();
1251a309f77SMatt Spinler     }
1261a309f77SMatt Spinler }
1271a309f77SMatt Spinler 
hostTransition(Host::Transition transition)1281a309f77SMatt Spinler void HostThenChassisPowerOff::hostTransition(Host::Transition transition)
1291a309f77SMatt Spinler {
1301a309f77SMatt Spinler     try
1311a309f77SMatt Spinler     {
1321a309f77SMatt Spinler         std::variant<std::string> state = convertForMessage(transition);
1331a309f77SMatt Spinler 
1341a309f77SMatt Spinler         lg2::info("Power button action requesting host transition of {TRANS}",
1351a309f77SMatt Spinler                   "TRANS", std::get<std::string>(state));
1361a309f77SMatt Spinler 
137*d36b6b1dSPatrick Williams         auto method =
138*d36b6b1dSPatrick Williams             bus.new_method_call(service::hostState, object_path::hostState,
1391a309f77SMatt Spinler                                 interface::property, "Set");
1401a309f77SMatt Spinler         method.append(interface::hostState, "RequestedHostTransition", state);
1411a309f77SMatt Spinler 
1421a309f77SMatt Spinler         bus.call(method);
1431a309f77SMatt Spinler     }
1441a309f77SMatt Spinler     catch (const sdbusplus::exception_t& e)
1451a309f77SMatt Spinler     {
1461a309f77SMatt Spinler         lg2::error("Failed requesting host transition {TRANS}: {ERROR}",
1471a309f77SMatt Spinler                    "TRANS", convertForMessage(transition), "ERROR", e);
1481a309f77SMatt Spinler     }
1491a309f77SMatt Spinler }
1501a309f77SMatt Spinler 
powerOn()1511a309f77SMatt Spinler void HostThenChassisPowerOff::powerOn()
1521a309f77SMatt Spinler {
1531a309f77SMatt Spinler     hostTransition(Host::Transition::On);
1541a309f77SMatt Spinler }
1551a309f77SMatt Spinler 
hostPowerOff()1561a309f77SMatt Spinler void HostThenChassisPowerOff::hostPowerOff()
1571a309f77SMatt Spinler {
1581a309f77SMatt Spinler     hostTransition(Host::Transition::Off);
1591a309f77SMatt Spinler }
1601a309f77SMatt Spinler 
chassisPowerOff()1611a309f77SMatt Spinler void HostThenChassisPowerOff::chassisPowerOff()
1621a309f77SMatt Spinler {
1631a309f77SMatt Spinler     lg2::info("Power button action requesting chassis power off");
1641a309f77SMatt Spinler 
1651a309f77SMatt Spinler     try
1661a309f77SMatt Spinler     {
1671a309f77SMatt Spinler         std::variant<std::string> state =
1681a309f77SMatt Spinler             convertForMessage(Chassis::Transition::Off);
1691a309f77SMatt Spinler 
1701a309f77SMatt Spinler         auto method = bus.new_method_call(service::chassisState,
1711a309f77SMatt Spinler                                           object_path::chassisState,
1721a309f77SMatt Spinler                                           interface::property, "Set");
1731a309f77SMatt Spinler         method.append(interface::chassisState, "RequestedPowerTransition",
1741a309f77SMatt Spinler                       state);
1751a309f77SMatt Spinler 
1761a309f77SMatt Spinler         bus.call(method);
1771a309f77SMatt Spinler     }
1781a309f77SMatt Spinler     catch (const sdbusplus::exception_t& e)
1791a309f77SMatt Spinler     {
1801a309f77SMatt Spinler         lg2::error("Failed requesting chassis off: {ERROR}", "ERROR", e);
1811a309f77SMatt Spinler     }
1821a309f77SMatt Spinler }
1831a309f77SMatt Spinler 
isPoweredOn() const1841a309f77SMatt Spinler bool HostThenChassisPowerOff::isPoweredOn() const
1851a309f77SMatt Spinler {
1861a309f77SMatt Spinler     Chassis::PowerState chassisState;
1871a309f77SMatt Spinler 
1881a309f77SMatt Spinler     try
1891a309f77SMatt Spinler     {
1901a309f77SMatt Spinler         auto method = bus.new_method_call(service::chassisState,
1911a309f77SMatt Spinler                                           object_path::chassisState,
1921a309f77SMatt Spinler                                           interface::property, "Get");
1931a309f77SMatt Spinler         method.append(interface::chassisState, "CurrentPowerState");
1941a309f77SMatt Spinler         auto result = bus.call(method);
1951a309f77SMatt Spinler 
1961a309f77SMatt Spinler         std::variant<std::string> state;
1971a309f77SMatt Spinler         result.read(state);
1981a309f77SMatt Spinler 
1991a309f77SMatt Spinler         chassisState =
2001a309f77SMatt Spinler             Chassis::convertPowerStateFromString(std::get<std::string>(state));
2011a309f77SMatt Spinler     }
2021a309f77SMatt Spinler     catch (const sdbusplus::exception_t& e)
2031a309f77SMatt Spinler     {
2041a309f77SMatt Spinler         lg2::error("Failed checking if chassis is on: {ERROR}", "ERROR", e);
2051a309f77SMatt Spinler         throw;
2061a309f77SMatt Spinler     }
2071a309f77SMatt Spinler 
2081a309f77SMatt Spinler     return chassisState == Chassis::PowerState::On;
2091a309f77SMatt Spinler }
2101a309f77SMatt Spinler 
isBmcReady() const2111a309f77SMatt Spinler bool HostThenChassisPowerOff::isBmcReady() const
2121a309f77SMatt Spinler {
2131a309f77SMatt Spinler     BMC::BMCState bmcState;
2141a309f77SMatt Spinler 
2151a309f77SMatt Spinler     try
2161a309f77SMatt Spinler     {
217*d36b6b1dSPatrick Williams         auto method =
218*d36b6b1dSPatrick Williams             bus.new_method_call(service::bmcState, object_path::bmcState,
2191a309f77SMatt Spinler                                 interface::property, "Get");
2201a309f77SMatt Spinler         method.append(interface::bmcState, "CurrentBMCState");
2211a309f77SMatt Spinler         auto result = bus.call(method);
2221a309f77SMatt Spinler 
2231a309f77SMatt Spinler         std::variant<std::string> state;
2241a309f77SMatt Spinler         result.read(state);
2251a309f77SMatt Spinler 
2261a309f77SMatt Spinler         bmcState = BMC::convertBMCStateFromString(std::get<std::string>(state));
2271a309f77SMatt Spinler     }
2281a309f77SMatt Spinler     catch (const sdbusplus::exception_t& e)
2291a309f77SMatt Spinler     {
2301a309f77SMatt Spinler         lg2::error("Failed reading BMC state interface: {}", "ERROR", e);
2311a309f77SMatt Spinler         throw;
2321a309f77SMatt Spinler     }
2331a309f77SMatt Spinler 
2341a309f77SMatt Spinler     return bmcState == BMC::BMCState::Ready;
2351a309f77SMatt Spinler }
2361a309f77SMatt Spinler } // namespace phosphor::button
237