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.
20  *
21  * Different architectures may have different ways of considering
22  * power to be on, such as a pgood property on the
23  * org.openbmc.Control.Power interface, or the CurrentPowerState
24  * property on the State.Chassis interface, so those details will
25  * be in a derived class.
26  */
27 class PowerState
28 {
29   public:
30     using StateChangeFunc = std::function<void(bool)>;
31 
32     PowerState() = delete;
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) :
47         _bus(bus), _callback(std::move(callback))
48     {}
49 
50     /**
51      * @brief Says if power is on
52      *
53      * @return bool - The power state
54      */
55     bool isPowerOn() const
56     {
57         return _powerState;
58     }
59 
60   protected:
61     /**
62      * @brief Called by derived classes to set the power state value
63      *
64      * Will call the callback function if the state changed.
65      *
66      * @param[in] state - The new power state
67      */
68     void setPowerState(bool state)
69     {
70         if (state != _powerState)
71         {
72             _powerState = state;
73             _callback(_powerState);
74         }
75     }
76 
77     /**
78      * @brief Reference to the D-Bus connection object.
79      */
80     sdbusplus::bus::bus& _bus;
81 
82     /**
83      * @brief The power state value
84      */
85     bool _powerState = false;
86 
87   private:
88     /**
89      * @brief The callback function to run when the power state changes
90      */
91     std::function<void(bool)> _callback;
92 };
93 
94 /**
95  * @class PGoodState
96  *
97  * This class implements the PowerState API by looking at the 'pgood'
98  * property on the org.openbmc.Control.Power interface.
99  */
100 class PGoodState : public PowerState
101 {
102   public:
103     PGoodState() = delete;
104     virtual ~PGoodState() = default;
105     PGoodState(const PGoodState&) = delete;
106     PGoodState& operator=(const PGoodState&) = delete;
107     PGoodState(PGoodState&&) = delete;
108     PGoodState& operator=(PGoodState&&) = delete;
109 
110     /**
111      * @brief Constructor
112      *
113      * @param[in] bus - The D-Bus bus connection object
114      * @param[in] callback - The function that should be run when
115      *                       the power state changes
116      */
117     PGoodState(sdbusplus::bus::bus& bus, StateChangeFunc func) :
118         PowerState(bus, func),
119         _match(_bus,
120                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
121                                                                _pgoodInterface),
122                [this](auto& msg) { this->pgoodChanged(msg); })
123     {
124         try
125         {
126             auto pgood = util::SDBusPlus::getProperty<int32_t>(
127                 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
128 
129             _powerState = static_cast<bool>(pgood);
130         }
131         catch (const util::DBusError& e)
132         {
133             using namespace phosphor::logging;
134             log<level::ERR>(
135                 fmt::format("Could not find PGOOD interface {} on D-Bus",
136                             _pgoodInterface)
137                     .c_str());
138             throw;
139         }
140     }
141 
142     /**
143      * @brief PropertiesChanged callback for the PGOOD property.
144      *
145      * Will call the registered callback function if necessary.
146      *
147      * @param[in] msg - The payload of the propertiesChanged signal
148      */
149     void pgoodChanged(sdbusplus::message::message& msg)
150     {
151         std::string interface;
152         std::map<std::string, std::variant<int32_t>> properties;
153 
154         msg.read(interface, properties);
155 
156         auto pgoodProp = properties.find(_pgoodProperty);
157         if (pgoodProp != properties.end())
158         {
159             auto pgood = std::get<int32_t>(pgoodProp->second);
160             setPowerState(pgood);
161         }
162     }
163 
164   private:
165     /** @brief D-Bus path constant */
166     const std::string _pgoodPath{"/org/openbmc/control/power0"};
167 
168     /** @brief D-Bus interface constant */
169     const std::string _pgoodInterface{"org.openbmc.control.Power"};
170 
171     /** @brief D-Bus property constant */
172     const std::string _pgoodProperty{"pgood"};
173 
174     /** @brief The propertiesChanged match */
175     sdbusplus::bus::match::match _match;
176 };
177 
178 } // namespace phosphor::fan
179