1 #pragma once
2 
3 #include "sdbusplus.hpp"
4 
5 #include <fmt/format.h>
6 
7 #include <phosphor-logging/log.hpp>
8 
9 #include <functional>
10 
11 namespace phosphor::fan
12 {
13 
14 /**
15  * @class PowerState
16  *
17  * This class provides an interface to check the current power state,
18  * and to register a function that gets called when there is a power
19  * state change.  A callback can be passed in using the constructor,
20  * or can be added later using addCallback().
21  *
22  * Different architectures may have different ways of considering
23  * power to be on, such as a pgood property on the
24  * org.openbmc.Control.Power interface, or the CurrentPowerState
25  * property on the State.Chassis interface, so those details will
26  * be in a derived class.
27  */
28 class PowerState
29 {
30   public:
31     using StateChangeFunc = std::function<void(bool)>;
32 
33     virtual ~PowerState() = default;
34     PowerState(const PowerState&) = delete;
35     PowerState& operator=(const PowerState&) = delete;
36     PowerState(PowerState&&) = delete;
37     PowerState& operator=(PowerState&&) = delete;
38 
39     /**
40      * @brief Constructor
41      *
42      * @param[in] bus - The D-Bus bus connection object
43      * @param[in] callback - The function that should be run when
44      *                       the power state changes
45      */
46     PowerState(sdbusplus::bus::bus& bus, StateChangeFunc callback) : _bus(bus)
47     {
48         _callbacks.emplace("default", std::move(callback));
49     }
50 
51     /**
52      * @brief Constructor
53      *
54      * Callbacks can be added with addCallback().
55      */
56     PowerState() : _bus(util::SDBusPlus::getBus())
57     {}
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      */
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      */
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      */
87     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      */
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::bus& _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 
144     PGoodState() :
145         PowerState(), _match(_bus,
146                              sdbusplus::bus::match::rules::propertiesChanged(
147                                  _pgoodPath, _pgoodInterface),
148                              [this](auto& msg) { this->pgoodChanged(msg); })
149     {
150         readPGood();
151     }
152 
153     /**
154      * @brief Constructor
155      *
156      * @param[in] bus - The D-Bus bus connection object
157      * @param[in] callback - The function that should be run when
158      *                       the power state changes
159      */
160     PGoodState(sdbusplus::bus::bus& bus, StateChangeFunc func) :
161         PowerState(bus, func),
162         _match(_bus,
163                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
164                                                                _pgoodInterface),
165                [this](auto& msg) { this->pgoodChanged(msg); })
166     {
167         readPGood();
168     }
169 
170     /**
171      * @brief PropertiesChanged callback for the PGOOD property.
172      *
173      * Will call the registered callback function if necessary.
174      *
175      * @param[in] msg - The payload of the propertiesChanged signal
176      */
177     void pgoodChanged(sdbusplus::message::message& msg)
178     {
179         std::string interface;
180         std::map<std::string, std::variant<int32_t>> properties;
181 
182         msg.read(interface, properties);
183 
184         auto pgoodProp = properties.find(_pgoodProperty);
185         if (pgoodProp != properties.end())
186         {
187             auto pgood = std::get<int32_t>(pgoodProp->second);
188             setPowerState(pgood);
189         }
190     }
191 
192   private:
193     /**
194      * @brief Reads the PGOOD property from D-Bus and saves it.
195      */
196     void readPGood()
197     {
198         try
199         {
200             auto pgood = util::SDBusPlus::getProperty<int32_t>(
201                 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
202 
203             _powerState = static_cast<bool>(pgood);
204         }
205         catch (const util::DBusServiceError& e)
206         {
207             // Wait for propertiesChanged signal when service starts
208         }
209     }
210 
211     /** @brief D-Bus path constant */
212     const std::string _pgoodPath{"/org/openbmc/control/power0"};
213 
214     /** @brief D-Bus interface constant */
215     const std::string _pgoodInterface{"org.openbmc.control.Power"};
216 
217     /** @brief D-Bus property constant */
218     const std::string _pgoodProperty{"pgood"};
219 
220     /** @brief The propertiesChanged match */
221     sdbusplus::bus::match::match _match;
222 };
223 
224 } // namespace phosphor::fan
225