1 #pragma once
2 
3 #include "sdbusplus.hpp"
4 
5 #include <phosphor-logging/log.hpp>
6 #include <xyz/openbmc_project/State/Host/server.hpp>
7 
8 #include <functional>
9 
10 using HostState =
11     sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
12 
13 namespace phosphor::fan
14 {
15 
16 /**
17  * @class PowerState
18  *
19  * This class provides an interface to check the current power state,
20  * and to register a function that gets called when there is a power
21  * state change.  A callback can be passed in using the constructor,
22  * or can be added later using addCallback().
23  *
24  * Different architectures may have different ways of considering
25  * power to be on, such as a pgood property on the
26  * org.openbmc.Control.Power interface, or the CurrentPowerState
27  * property on the State.Chassis interface, so those details will
28  * be in a derived class.
29  */
30 class PowerState
31 {
32   public:
33     using StateChangeFunc = std::function<void(bool)>;
34 
35     virtual ~PowerState() = default;
36     PowerState(const PowerState&) = delete;
37     PowerState& operator=(const PowerState&) = delete;
38     PowerState(PowerState&&) = delete;
39     PowerState& operator=(PowerState&&) = delete;
40 
41     /**
42      * @brief Constructor
43      *
44      * @param[in] bus - The D-Bus bus connection object
45      * @param[in] callback - The function that should be run when
46      *                       the power state changes
47      */
48     PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
49     {
50         _callbacks.emplace("default", std::move(callback));
51     }
52 
53     /**
54      * @brief Constructor
55      *
56      * Callbacks can be added with addCallback().
57      */
58     PowerState() : _bus(util::SDBusPlus::getBus()) {}
59 
60     /**
61      * @brief Adds a function to call when the power state changes
62      *
63      * @param[in] - Any unique name, so the callback can be removed later
64      *              if desired.
65      * @param[in] callback - The function that should be run when
66      *                       the power state changes
67      */
68     void addCallback(const std::string& name, StateChangeFunc callback)
69     {
70         _callbacks.emplace(name, std::move(callback));
71     }
72 
73     /**
74      * @brief Remove the callback so it is no longer called
75      *
76      * @param[in] name - The name used when it was added.
77      */
78     void deleteCallback(const std::string& name)
79     {
80         _callbacks.erase(name);
81     }
82 
83     /**
84      * @brief Says if power is on
85      *
86      * @return bool - The power state
87      */
88     bool isPowerOn() const
89     {
90         return _powerState;
91     }
92 
93   protected:
94     /**
95      * @brief Called by derived classes to set the power state value
96      *
97      * Will call the callback functions if the state changed.
98      *
99      * @param[in] state - The new power state
100      */
101     void setPowerState(bool state)
102     {
103         if (state != _powerState)
104         {
105             _powerState = state;
106             for (const auto& [name, callback] : _callbacks)
107             {
108                 callback(_powerState);
109             }
110         }
111     }
112 
113     /**
114      * @brief Reference to the D-Bus connection object.
115      */
116     sdbusplus::bus_t& _bus;
117 
118     /**
119      * @brief The power state value
120      */
121     bool _powerState = false;
122 
123   private:
124     /**
125      * @brief The callback functions to run when the power state changes
126      */
127     std::map<std::string, StateChangeFunc> _callbacks;
128 };
129 
130 /**
131  * @class PGoodState
132  *
133  * This class implements the PowerState API by looking at the 'pgood'
134  * property on the org.openbmc.Control.Power interface.
135  */
136 class PGoodState : public PowerState
137 {
138   public:
139     virtual ~PGoodState() = default;
140     PGoodState(const PGoodState&) = delete;
141     PGoodState& operator=(const PGoodState&) = delete;
142     PGoodState(PGoodState&&) = delete;
143     PGoodState& operator=(PGoodState&&) = delete;
144 
145     PGoodState() :
146         PowerState(), _match(_bus,
147                              sdbusplus::bus::match::rules::propertiesChanged(
148                                  _pgoodPath, _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      */
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      */
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      */
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 
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      */
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      */
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);
297         }
298     }
299 
300   private:
301     void setHostPowerState(std::vector<HostState>& hostPowerStates)
302     {
303         bool powerStateflag = false;
304         for (const auto& powerState : hostPowerStates)
305         {
306             if (powerState == HostState::Standby ||
307                 powerState == HostState::Running ||
308                 powerState == HostState::TransitioningToRunning ||
309                 powerState == HostState::Quiesced ||
310                 powerState == HostState::DiagnosticMode)
311             {
312                 powerStateflag = true;
313                 break;
314             }
315         }
316         setPowerState(powerStateflag);
317     }
318 
319     /**
320      * @brief Reads the CurrentHostState property from D-Bus and saves it.
321      */
322     void readHostState()
323     {
324         std::string hostStatePath;
325         std::string hostStateService;
326         std::string hostService = "xyz.openbmc_project.State.Host";
327         std::vector<HostState> hostPowerStates;
328 
329         int32_t depth = 0;
330         const std::string path = "/";
331 
332         auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
333             _bus, path, _hostStateInterface, depth);
334 
335         for (const auto& path : mapperResponse)
336         {
337             for (const auto& service : path.second)
338             {
339                 hostStateService = service.first;
340 
341                 if (hostStateService.find(hostService) != std::string::npos)
342                 {
343                     hostStatePath = path.first;
344 
345                     auto currentHostState =
346                         util::SDBusPlus::getProperty<HostState>(
347                             hostStateService, hostStatePath,
348                             _hostStateInterface, _hostStateProperty);
349 
350                     hostPowerStates.emplace_back(currentHostState);
351                 }
352             }
353         }
354         setHostPowerState(hostPowerStates);
355     }
356 
357     const std::string _hostStatePath{"/xyz/openbmc_project/state"};
358 
359     /** @brief D-Bus interface constant */
360     const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
361 
362     /** @brief D-Bus property constant */
363     const std::string _hostStateProperty{"CurrentHostState"};
364 
365     /** @brief The propertiesChanged match */
366     sdbusplus::bus::match_t _match;
367 };
368 
369 } // namespace phosphor::fan
370