1 #pragma once
2 #include "power_button_profile.hpp"
3 
4 #include <sdbusplus/bus/match.hpp>
5 #include <sdeventplus/event.hpp>
6 #include <sdeventplus/utility/timer.hpp>
7 #include <xyz/openbmc_project/State/Host/server.hpp>
8 
9 #include <chrono>
10 
11 namespace phosphor::button
12 {
13 
14 /**
15  * @class HostThenChassisPowerOff
16  *
17  * A custom power button handler that will do the following:
18  *
19  * If power is off:
20  *  - A button press will power on as long as the BMC is
21  *    in the ready state.
22  *
23  * If power is on:
24  *  - A button press less than 4s won't do anything.
25  *  - At 4s, issue a host power off and start a 10s timer.
26  *    - If the button is released within that 10s and not pressed
27  *      again, continue with the host power off.
28  *    - If the button is released within that 10s and also
29  *      pressed again in that 10s, do a hard power (chassis)
30  *      off.
31  *    - If the button is pressed throughout that 10s
32  *      issue a hard power off.
33  */
34 class HostThenChassisPowerOff : public PowerButtonProfile
35 {
36   public:
37     enum class PowerOpState
38     {
39         powerOnPress,
40         buttonNotPressed,
41         buttonPressed,
42         buttonPressedHostOffStarted,
43         buttonReleasedHostToChassisOffWindow,
44         chassisOffStarted
45     };
46 
47     /**
48      * @brief Constructor
49      * @param[in] bus - The sdbusplus bus object
50      */
HostThenChassisPowerOff(sdbusplus::bus_t & bus)51     explicit HostThenChassisPowerOff(sdbusplus::bus_t& bus) :
52         PowerButtonProfile(bus), state(PowerOpState::buttonNotPressed),
53         timer(bus.get_event(),
54               std::bind(&HostThenChassisPowerOff::timerHandler, this),
55               pollInterval)
56     {
57         timer.setEnabled(false);
58     }
59 
60     /**
61      * @brief Returns the name that matches the value in
62      *        meson_options.txt.
63      */
getName()64     static constexpr std::string_view getName()
65     {
66         return "host_then_chassis_poweroff";
67     }
68 
69     HostThenChassisPowerOff() = delete;
70     ~HostThenChassisPowerOff() = default;
71 
72     /**
73      * @brief Called when the power button is pressed.
74      */
75     virtual void pressed() override;
76 
77     /**
78      * @brief Called when the power button is released.
79      *
80      * @param[in] pressTimeMS - How long the button was pressed
81      *                          in milliseconds.
82      */
83     virtual void released(uint64_t pressTimeMS) override;
84 
85   private:
86     /**
87      * @brief Determines if the BMC is in the ready state.
88      * @return bool If the BMC is in the ready state
89      */
90     bool isBmcReady() const;
91 
92     /**
93      * @brief Determines if system (chassis) is powered on.
94      *
95      * @return bool - If power is on
96      */
97     bool isPoweredOn() const;
98 
99     /**
100      * @brief Requests a host state transition
101      * @param[in] transition - The transition (like On or Off)
102      */
103     void hostTransition(
104         sdbusplus::xyz::openbmc_project::State::server::Host::Transition
105             transition);
106 
107     /**
108      * @brief Powers on the system
109      */
110     void powerOn();
111 
112     /**
113      * @brief Requests a host power off
114      */
115     void hostPowerOff();
116 
117     /**
118      * @brief Requests a chassis power off
119      */
120     void chassisPowerOff();
121 
122     /**
123      * @brief The handler for the 1s timer that runs when determining
124      *        how to power off.
125      *
126      * A 1 second timer is used so that there is the ability to emit
127      * a power off countdown if necessary.
128      */
129     void timerHandler();
130 
131     /**
132      * @brief Sets the time the host will be powered off if the
133      *        button is still pressed - 4 seconds in the future.
134      */
setHostOffTime()135     inline void setHostOffTime()
136     {
137         hostOffTime = std::chrono::steady_clock::now() + hostOffInterval;
138     }
139 
140     /**
141      * @brief Sets the time the chassis will be powered off if the
142      *        button is still pressed or pressed again - 10 seconds
143      *        in the future.
144      */
setChassisOffTime()145     inline void setChassisOffTime()
146     {
147         chassisOffTime = std::chrono::steady_clock::now() + chassisOffInterval;
148     }
149 
150     /**
151      * @brief The interval the timer handler is called at.
152      */
153     static constexpr std::chrono::milliseconds pollInterval{1000};
154 
155     /**
156      * @brief Default button hold down interval constant
157      */
158     static constexpr std::chrono::milliseconds hostOffInterval{4000};
159 
160     /**
161      * @brief The time between a host power off and chassis power off.
162      */
163     static constexpr std::chrono::milliseconds chassisOffInterval{10000};
164 
165     /**
166      * @brief The current state of the handler.
167      */
168     PowerOpState state;
169 
170     /**
171      * @brief When the host will be powered off.
172      */
173     std::chrono::time_point<std::chrono::steady_clock> hostOffTime;
174 
175     /**
176      * @brief When the chassis will be powered off.
177      */
178     std::chrono::time_point<std::chrono::steady_clock> chassisOffTime;
179 
180     /**
181      * @brief The timer object.
182      */
183     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
184 };
185 } // namespace phosphor::button
186