1 #include "host_then_chassis_poweroff.hpp"
2
3 #include "config.hpp"
4 #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
10 namespace phosphor::button
11 {
12
13 // Register the profile with the factory.
14 static PowerButtonProfileRegister<HostThenChassisPowerOff> profileRegister;
15
16 namespace service
17 {
18 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
19 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis0";
20 constexpr auto hostState = "xyz.openbmc_project.State.Host";
21 } // namespace service
22
23 namespace object_path
24 {
25 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
26 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
27 constexpr auto hostState = "/xyz/openbmc_project/state/host0";
28 } // namespace object_path
29
30 namespace interface
31 {
32 constexpr auto property = "org.freedesktop.DBus.Properties";
33 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
34 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
35 constexpr auto hostState = "xyz.openbmc_project.State.Host";
36 } // namespace interface
37
38 using namespace sdbusplus::xyz::openbmc_project::State::server;
39
pressed()40 void 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
released(uint64_t)88 void 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
timerHandler()105 void 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
hostTransition(Host::Transition transition)128 void 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
137 auto method =
138 bus.new_method_call(service::hostState, object_path::hostState,
139 interface::property, "Set");
140 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
powerOn()151 void HostThenChassisPowerOff::powerOn()
152 {
153 hostTransition(Host::Transition::On);
154 }
155
hostPowerOff()156 void HostThenChassisPowerOff::hostPowerOff()
157 {
158 hostTransition(Host::Transition::Off);
159 }
160
chassisPowerOff()161 void 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
isPoweredOn() const184 bool 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
isBmcReady() const211 bool HostThenChassisPowerOff::isBmcReady() const
212 {
213 BMC::BMCState bmcState;
214
215 try
216 {
217 auto method =
218 bus.new_method_call(service::bmcState, object_path::bmcState,
219 interface::property, "Get");
220 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
237