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