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