xref: /openbmc/phosphor-fan-presence/power_state.hpp (revision 9d533806250cea56406bdd39e025f0d820c4ed90)
1 #pragma once
2 
3 #include "sdbusplus.hpp"
4 
5 #include <xyz/openbmc_project/State/Host/server.hpp>
6 
7 #include <functional>
8 
9 using HostState =
10     sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
11 
12 namespace phosphor::fan
13 {
14 
15 /**
16  * @class PowerState
17  *
18  * This class provides an interface to check the current power state,
19  * and to register a function that gets called when there is a power
20  * state change.  A callback can be passed in using the constructor,
21  * or can be added later using addCallback().
22  *
23  * Different architectures may have different ways of considering
24  * power to be on, such as a pgood property on the
25  * org.openbmc.Control.Power interface, or the CurrentPowerState
26  * property on the State.Chassis interface, so those details will
27  * be in a derived class.
28  */
29 class PowerState
30 {
31   public:
32     using StateChangeFunc = std::function<void(bool)>;
33 
34     virtual ~PowerState() = default;
35     PowerState(const PowerState&) = delete;
36     PowerState& operator=(const PowerState&) = delete;
37     PowerState(PowerState&&) = delete;
38     PowerState& operator=(PowerState&&) = delete;
39 
40     /**
41      * @brief Constructor
42      *
43      * @param[in] bus - The D-Bus bus connection object
44      * @param[in] callback - The function that should be run when
45      *                       the power state changes
46      */
PowerState(sdbusplus::bus_t & bus,StateChangeFunc callback)47     PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
48     {
49         _callbacks.emplace("default", std::move(callback));
50     }
51 
52     /**
53      * @brief Constructor
54      *
55      * Callbacks can be added with addCallback().
56      */
PowerState()57     PowerState() : _bus(util::SDBusPlus::getBus()) {}
58 
59     /**
60      * @brief Adds a function to call when the power state changes
61      *
62      * @param[in] - Any unique name, so the callback can be removed later
63      *              if desired.
64      * @param[in] callback - The function that should be run when
65      *                       the power state changes
66      */
addCallback(const std::string & name,StateChangeFunc callback)67     void addCallback(const std::string& name, StateChangeFunc callback)
68     {
69         _callbacks.emplace(name, std::move(callback));
70     }
71 
72     /**
73      * @brief Remove the callback so it is no longer called
74      *
75      * @param[in] name - The name used when it was added.
76      */
deleteCallback(const std::string & name)77     void deleteCallback(const std::string& name)
78     {
79         _callbacks.erase(name);
80     }
81 
82     /**
83      * @brief Says if power is on
84      *
85      * @return bool - The power state
86      */
isPowerOn() const87     bool isPowerOn() const
88     {
89         return _powerState;
90     }
91 
92   protected:
93     /**
94      * @brief Called by derived classes to set the power state value
95      *
96      * Will call the callback functions if the state changed.
97      *
98      * @param[in] state - The new power state
99      */
setPowerState(bool state)100     void setPowerState(bool state)
101     {
102         if (state != _powerState)
103         {
104             _powerState = state;
105             for (const auto& [name, callback] : _callbacks)
106             {
107                 callback(_powerState);
108             }
109         }
110     }
111 
112     /**
113      * @brief Reference to the D-Bus connection object.
114      */
115     sdbusplus::bus_t& _bus;
116 
117     /**
118      * @brief The power state value
119      */
120     bool _powerState = false;
121 
122   private:
123     /**
124      * @brief The callback functions to run when the power state changes
125      */
126     std::map<std::string, StateChangeFunc> _callbacks;
127 };
128 
129 /**
130  * @class PGoodState
131  *
132  * This class implements the PowerState API by looking at the 'pgood'
133  * property on the org.openbmc.Control.Power interface.
134  */
135 class PGoodState : public PowerState
136 {
137   public:
138     virtual ~PGoodState() = default;
139     PGoodState(const PGoodState&) = delete;
140     PGoodState& operator=(const PGoodState&) = delete;
141     PGoodState(PGoodState&&) = delete;
142     PGoodState& operator=(PGoodState&&) = delete;
143 
PGoodState()144     PGoodState() :
145         PowerState(),
146         _match(_bus,
147                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
148                                                                _pgoodInterface),
149                [this](auto& msg) { this->pgoodChanged(msg); })
150     {
151         readPGood();
152     }
153 
154     /**
155      * @brief Constructor
156      *
157      * @param[in] bus - The D-Bus bus connection object
158      * @param[in] callback - The function that should be run when
159      *                       the power state changes
160      */
PGoodState(sdbusplus::bus_t & bus,StateChangeFunc func)161     PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
162         PowerState(bus, func),
163         _match(_bus,
164                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
165                                                                _pgoodInterface),
166                [this](auto& msg) { this->pgoodChanged(msg); })
167     {
168         readPGood();
169     }
170 
171     /**
172      * @brief PropertiesChanged callback for the PGOOD property.
173      *
174      * Will call the registered callback function if necessary.
175      *
176      * @param[in] msg - The payload of the propertiesChanged signal
177      */
pgoodChanged(sdbusplus::message_t & msg)178     void pgoodChanged(sdbusplus::message_t& msg)
179     {
180         std::string interface;
181         std::map<std::string, std::variant<int32_t>> properties;
182 
183         msg.read(interface, properties);
184 
185         auto pgoodProp = properties.find(_pgoodProperty);
186         if (pgoodProp != properties.end())
187         {
188             auto pgood = std::get<int32_t>(pgoodProp->second);
189             setPowerState(pgood);
190         }
191     }
192 
193   private:
194     /**
195      * @brief Reads the PGOOD property from D-Bus and saves it.
196      */
readPGood()197     void readPGood()
198     {
199         try
200         {
201             auto pgood = util::SDBusPlus::getProperty<int32_t>(
202                 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
203 
204             _powerState = static_cast<bool>(pgood);
205         }
206         catch (const util::DBusServiceError& e)
207         {
208             // Wait for propertiesChanged signal when service starts
209         }
210     }
211 
212     /** @brief D-Bus path constant */
213     const std::string _pgoodPath{"/org/openbmc/control/power0"};
214 
215     /** @brief D-Bus interface constant */
216     const std::string _pgoodInterface{"org.openbmc.control.Power"};
217 
218     /** @brief D-Bus property constant */
219     const std::string _pgoodProperty{"pgood"};
220 
221     /** @brief The propertiesChanged match */
222     sdbusplus::bus::match_t _match;
223 };
224 
225 /**
226  * @class HostPowerState
227  *
228  * This class implements the PowerState API by looking at the 'powerState'
229  * property on the phosphor virtual sensor interface.
230  */
231 class HostPowerState : public PowerState
232 {
233   public:
234     virtual ~HostPowerState() = default;
235     HostPowerState(const HostPowerState&) = delete;
236     HostPowerState& operator=(const HostPowerState&) = delete;
237     HostPowerState(HostPowerState&&) = delete;
238     HostPowerState& operator=(HostPowerState&&) = delete;
239 
HostPowerState()240     HostPowerState() :
241         PowerState(),
242         _match(_bus,
243                sdbusplus::bus::match::rules::propertiesChangedNamespace(
244                    _hostStatePath, _hostStateInterface),
245                [this](auto& msg) { this->hostStateChanged(msg); })
246     {
247         readHostState();
248     }
249 
250     /**
251      * @brief Constructor
252      *
253      * @param[in] bus - The D-Bus bus connection object
254      * @param[in] callback - The function that should be run when
255      *                       the power state changes
256      */
HostPowerState(sdbusplus::bus_t & bus,StateChangeFunc func)257     HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
258         PowerState(bus, func),
259         _match(_bus,
260                sdbusplus::bus::match::rules::propertiesChangedNamespace(
261                    _hostStatePath, _hostStateInterface),
262                [this](auto& msg) { this->hostStateChanged(msg); })
263     {
264         readHostState();
265     }
266 
267     /**
268      * @brief PropertiesChanged callback for the CurrentHostState property.
269      *
270      * Will call the registered callback function if necessary.
271      *
272      * @param[in] msg - The payload of the propertiesChanged signal
273      */
hostStateChanged(sdbusplus::message_t & msg)274     void hostStateChanged(sdbusplus::message_t& msg)
275     {
276         std::string interface;
277         std::map<std::string, std::variant<std::string>> properties;
278         std::vector<HostState> hostPowerStates;
279 
280         msg.read(interface, properties);
281 
282         auto hostStateProp = properties.find(_hostStateProperty);
283         if (hostStateProp != properties.end())
284         {
285             auto currentHostState =
286                 sdbusplus::message::convert_from_string<HostState>(
287                     std::get<std::string>(hostStateProp->second));
288 
289             if (!currentHostState)
290             {
291                 throw sdbusplus::exception::InvalidEnumString();
292             }
293             HostState hostState = *currentHostState;
294 
295             hostPowerStates.emplace_back(hostState);
296             setHostPowerState(hostPowerStates, true);
297         }
298     }
299 
300   private:
setHostPowerState(std::vector<HostState> & hostPowerStates,bool callFromStateChange)301     void setHostPowerState(std::vector<HostState>& hostPowerStates,
302                            bool callFromStateChange)
303     {
304         bool powerStateflag = false;
305         for (const auto& powerState : hostPowerStates)
306         {
307             if (powerState == HostState::Standby ||
308                 powerState == HostState::Running ||
309                 powerState == HostState::TransitioningToRunning ||
310                 powerState == HostState::Quiesced ||
311                 powerState == HostState::DiagnosticMode)
312             {
313                 powerStateflag = true;
314                 break;
315             }
316         }
317         if (callFromStateChange)
318         {
319             setPowerState(powerStateflag);
320         }
321         else
322         {
323             // This won't call callbacks (if exists) during the constructor of
324             // the class when the first read for this flag is true which is
325             // different from the init value of _powerState.
326             // Daemon that wants to do anything when the initial power state
327             // is true can check isPowerOn() and do it.
328             _powerState = powerStateflag;
329         }
330     }
331 
332     /**
333      * @brief Reads the CurrentHostState property from D-Bus and saves it.
334      */
readHostState()335     void readHostState()
336     {
337         std::string hostStatePath;
338         std::string hostStateService;
339         std::string hostService = "xyz.openbmc_project.State.Host";
340         std::vector<HostState> hostPowerStates;
341 
342         int32_t depth = 0;
343         const std::string path = "/";
344 
345         auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
346             _bus, path, _hostStateInterface, depth);
347 
348         for (const auto& path : mapperResponse)
349         {
350             for (const auto& service : path.second)
351             {
352                 hostStateService = service.first;
353 
354                 if (hostStateService.find(hostService) != std::string::npos)
355                 {
356                     hostStatePath = path.first;
357 
358                     auto currentHostState =
359                         util::SDBusPlus::getProperty<HostState>(
360                             hostStateService, hostStatePath,
361                             _hostStateInterface, _hostStateProperty);
362 
363                     hostPowerStates.emplace_back(currentHostState);
364                 }
365             }
366         }
367         setHostPowerState(hostPowerStates, false);
368     }
369 
370     const std::string _hostStatePath{"/xyz/openbmc_project/state"};
371 
372     /** @brief D-Bus interface constant */
373     const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
374 
375     /** @brief D-Bus property constant */
376     const std::string _hostStateProperty{"CurrentHostState"};
377 
378     /** @brief The propertiesChanged match */
379     sdbusplus::bus::match_t _match;
380 };
381 
382 } // namespace phosphor::fan
383