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