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