blob: 44893643ad19052c3880d029634a4b0711798dae [file] [log] [blame]
Matt Spinler1a309f72023-04-04 13:12:19 -05001#include "host_then_chassis_poweroff.hpp"
2
Delphine CC Chiu15c60e22024-04-12 13:01:32 -05003#include "config.hpp"
Matt Spinler1a309f72023-04-04 13:12:19 -05004#include "power_button_profile_factory.hpp"
5
6#include <phosphor-logging/lg2.hpp>
7#include <xyz/openbmc_project/State/BMC/server.hpp>
8#include <xyz/openbmc_project/State/Chassis/server.hpp>
9
10namespace phosphor::button
11{
12
13// Register the profile with the factory.
14static PowerButtonProfileRegister<HostThenChassisPowerOff> profileRegister;
15
16namespace service
17{
18constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
Patrick Williams9c276562024-01-19 14:23:08 -060019constexpr auto chassisState = "xyz.openbmc_project.State.Chassis0";
Matt Spinler1a309f72023-04-04 13:12:19 -050020constexpr auto hostState = "xyz.openbmc_project.State.Host";
21} // namespace service
22
23namespace object_path
24{
25constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
26constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
27constexpr auto hostState = "/xyz/openbmc_project/state/host0";
28} // namespace object_path
29
30namespace interface
31{
32constexpr auto property = "org.freedesktop.DBus.Properties";
33constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
34constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
35constexpr auto hostState = "xyz.openbmc_project.State.Host";
36} // namespace interface
37
38using namespace sdbusplus::xyz::openbmc_project::State::server;
39
40void HostThenChassisPowerOff::pressed()
41{
42 lg2::info("Power button pressed");
43
44 try
45 {
46 // If power not on - power on
47 if (!isPoweredOn())
48 {
49 if (!isBmcReady())
50 {
51 lg2::error("BMC not at ready state yet, cannot power on");
52 return;
53 }
54
55 state = PowerOpState::powerOnPress;
56 powerOn();
57 return;
58 }
59 }
60 catch (const sdbusplus::exception_t& e)
61 {
62 lg2::error(
63 "Exception while processing power button press. Cannot continue");
64 return;
65 }
66
67 // Power is on ...
68
69 if (state == PowerOpState::buttonNotPressed)
70 {
71 lg2::info("Starting countdown to power off");
72 state = PowerOpState::buttonPressed;
73 setHostOffTime();
74 timer.restart(pollInterval);
75 }
76
77 // Button press during host off to chassis off window.
78 // Causes a chassis power off.
79 else if (state == PowerOpState::buttonReleasedHostToChassisOffWindow)
80 {
81 lg2::info("Starting chassis power off due to button press");
82 state = PowerOpState::chassisOffStarted;
83 timer.setEnabled(false);
84 chassisPowerOff();
85 }
86}
87
88void HostThenChassisPowerOff::released(uint64_t /*pressTimeMS*/)
89{
90 lg2::info("Power button released");
91
92 // Button released in the host to chassis off window.
93 // Timer continues to run in case button is pressed again
94 // in the window.
95 if (state == PowerOpState::buttonPressedHostOffStarted)
96 {
97 state = PowerOpState::buttonReleasedHostToChassisOffWindow;
98 return;
99 }
100
101 state = PowerOpState::buttonNotPressed;
102 timer.setEnabled(false);
103}
104
105void HostThenChassisPowerOff::timerHandler()
106{
107 const auto now = std::chrono::steady_clock::now();
108
109 if ((state == PowerOpState::buttonPressed) && (now >= hostOffTime))
110 {
111 // Start the host power off and start the chassis
112 // power off countdown.
113 state = PowerOpState::buttonPressedHostOffStarted;
114 setChassisOffTime();
115 hostPowerOff();
116 }
117 else if ((state == PowerOpState::buttonPressedHostOffStarted) &&
118 (now >= chassisOffTime))
119 {
120 // Button still pressed and it passed the chassis off time.
121 // Issue the chassis power off.
122 state = PowerOpState::chassisOffStarted;
123 timer.setEnabled(false);
124 chassisPowerOff();
125 }
126}
127
128void HostThenChassisPowerOff::hostTransition(Host::Transition transition)
129{
130 try
131 {
132 std::variant<std::string> state = convertForMessage(transition);
133
134 lg2::info("Power button action requesting host transition of {TRANS}",
135 "TRANS", std::get<std::string>(state));
136
Patrick Williamsd36b6b12024-08-16 15:20:34 -0400137 auto method =
138 bus.new_method_call(service::hostState, object_path::hostState,
139 interface::property, "Set");
Matt Spinler1a309f72023-04-04 13:12:19 -0500140 method.append(interface::hostState, "RequestedHostTransition", state);
141
142 bus.call(method);
143 }
144 catch (const sdbusplus::exception_t& e)
145 {
146 lg2::error("Failed requesting host transition {TRANS}: {ERROR}",
147 "TRANS", convertForMessage(transition), "ERROR", e);
148 }
149}
150
151void HostThenChassisPowerOff::powerOn()
152{
153 hostTransition(Host::Transition::On);
154}
155
156void HostThenChassisPowerOff::hostPowerOff()
157{
158 hostTransition(Host::Transition::Off);
159}
160
161void HostThenChassisPowerOff::chassisPowerOff()
162{
163 lg2::info("Power button action requesting chassis power off");
164
165 try
166 {
167 std::variant<std::string> state =
168 convertForMessage(Chassis::Transition::Off);
169
170 auto method = bus.new_method_call(service::chassisState,
171 object_path::chassisState,
172 interface::property, "Set");
173 method.append(interface::chassisState, "RequestedPowerTransition",
174 state);
175
176 bus.call(method);
177 }
178 catch (const sdbusplus::exception_t& e)
179 {
180 lg2::error("Failed requesting chassis off: {ERROR}", "ERROR", e);
181 }
182}
183
184bool HostThenChassisPowerOff::isPoweredOn() const
185{
186 Chassis::PowerState chassisState;
187
188 try
189 {
190 auto method = bus.new_method_call(service::chassisState,
191 object_path::chassisState,
192 interface::property, "Get");
193 method.append(interface::chassisState, "CurrentPowerState");
194 auto result = bus.call(method);
195
196 std::variant<std::string> state;
197 result.read(state);
198
199 chassisState =
200 Chassis::convertPowerStateFromString(std::get<std::string>(state));
201 }
202 catch (const sdbusplus::exception_t& e)
203 {
204 lg2::error("Failed checking if chassis is on: {ERROR}", "ERROR", e);
205 throw;
206 }
207
208 return chassisState == Chassis::PowerState::On;
209}
210
211bool HostThenChassisPowerOff::isBmcReady() const
212{
213 BMC::BMCState bmcState;
214
215 try
216 {
Patrick Williamsd36b6b12024-08-16 15:20:34 -0400217 auto method =
218 bus.new_method_call(service::bmcState, object_path::bmcState,
219 interface::property, "Get");
Matt Spinler1a309f72023-04-04 13:12:19 -0500220 method.append(interface::bmcState, "CurrentBMCState");
221 auto result = bus.call(method);
222
223 std::variant<std::string> state;
224 result.read(state);
225
226 bmcState = BMC::convertBMCStateFromString(std::get<std::string>(state));
227 }
228 catch (const sdbusplus::exception_t& e)
229 {
230 lg2::error("Failed reading BMC state interface: {}", "ERROR", e);
231 throw;
232 }
233
234 return bmcState == BMC::BMCState::Ready;
235}
236} // namespace phosphor::button