blob: 20ae1ba2492b8407f1e3078ea3da17c97a935f50 [file] [log] [blame]
Matt Spinler432efec2020-09-24 13:41:31 -05001#pragma once
2
3#include "sdbusplus.hpp"
4
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +05305#include <xyz/openbmc_project/State/Host/server.hpp>
Matt Spinler432efec2020-09-24 13:41:31 -05006
7#include <functional>
8
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +05309using HostState =
10 sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
11
Matt Spinler432efec2020-09-24 13:41:31 -050012namespace phosphor::fan
13{
14
15/**
16 * @class PowerState
17 *
18 * This class provides an interface to check the current power state,
19 * and to register a function that gets called when there is a power
Matt Spinler76e73c22021-04-21 11:03:05 -050020 * state change. A callback can be passed in using the constructor,
21 * or can be added later using addCallback().
Matt Spinler432efec2020-09-24 13:41:31 -050022 *
23 * Different architectures may have different ways of considering
24 * power to be on, such as a pgood property on the
25 * org.openbmc.Control.Power interface, or the CurrentPowerState
26 * property on the State.Chassis interface, so those details will
27 * be in a derived class.
28 */
29class PowerState
30{
31 public:
32 using StateChangeFunc = std::function<void(bool)>;
33
Matt Spinler432efec2020-09-24 13:41:31 -050034 virtual ~PowerState() = default;
35 PowerState(const PowerState&) = delete;
36 PowerState& operator=(const PowerState&) = delete;
37 PowerState(PowerState&&) = delete;
38 PowerState& operator=(PowerState&&) = delete;
39
40 /**
41 * @brief Constructor
42 *
43 * @param[in] bus - The D-Bus bus connection object
44 * @param[in] callback - The function that should be run when
45 * the power state changes
46 */
Patrick Williamscb356d42022-07-22 19:26:53 -050047 PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
Matt Spinler76e73c22021-04-21 11:03:05 -050048 {
49 _callbacks.emplace("default", std::move(callback));
50 }
51
52 /**
53 * @brief Constructor
54 *
55 * Callbacks can be added with addCallback().
56 */
Patrick Williams61b73292023-05-10 07:50:12 -050057 PowerState() : _bus(util::SDBusPlus::getBus()) {}
Matt Spinler432efec2020-09-24 13:41:31 -050058
59 /**
Matt Spinler76e73c22021-04-21 11:03:05 -050060 * @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 /**
Matt Spinler432efec2020-09-24 13:41:31 -050083 * @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 *
Matt Spinler76e73c22021-04-21 11:03:05 -050096 * Will call the callback functions if the state changed.
Matt Spinler432efec2020-09-24 13:41:31 -050097 *
98 * @param[in] state - The new power state
99 */
100 void setPowerState(bool state)
101 {
102 if (state != _powerState)
103 {
104 _powerState = state;
Matt Spinler76e73c22021-04-21 11:03:05 -0500105 for (const auto& [name, callback] : _callbacks)
106 {
107 callback(_powerState);
108 }
Matt Spinler432efec2020-09-24 13:41:31 -0500109 }
110 }
111
112 /**
113 * @brief Reference to the D-Bus connection object.
114 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500115 sdbusplus::bus_t& _bus;
Matt Spinler432efec2020-09-24 13:41:31 -0500116
117 /**
118 * @brief The power state value
119 */
120 bool _powerState = false;
121
122 private:
123 /**
Matt Spinler76e73c22021-04-21 11:03:05 -0500124 * @brief The callback functions to run when the power state changes
Matt Spinler432efec2020-09-24 13:41:31 -0500125 */
Matt Spinler76e73c22021-04-21 11:03:05 -0500126 std::map<std::string, StateChangeFunc> _callbacks;
Matt Spinler432efec2020-09-24 13:41:31 -0500127};
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 */
135class PGoodState : public PowerState
136{
137 public:
Matt Spinler432efec2020-09-24 13:41:31 -0500138 virtual ~PGoodState() = default;
139 PGoodState(const PGoodState&) = delete;
140 PGoodState& operator=(const PGoodState&) = delete;
141 PGoodState(PGoodState&&) = delete;
142 PGoodState& operator=(PGoodState&&) = delete;
143
Matt Spinler76e73c22021-04-21 11:03:05 -0500144 PGoodState() :
Patrick Williamsdfddd642024-08-16 15:21:51 -0400145 PowerState(),
146 _match(_bus,
147 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
148 _pgoodInterface),
149 [this](auto& msg) { this->pgoodChanged(msg); })
Matt Spinler76e73c22021-04-21 11:03:05 -0500150 {
151 readPGood();
152 }
153
Matt Spinler432efec2020-09-24 13:41:31 -0500154 /**
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 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500161 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Matt Spinler432efec2020-09-24 13:41:31 -0500162 PowerState(bus, func),
163 _match(_bus,
164 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
165 _pgoodInterface),
166 [this](auto& msg) { this->pgoodChanged(msg); })
167 {
Matt Spinler76e73c22021-04-21 11:03:05 -0500168 readPGood();
Matt Spinler432efec2020-09-24 13:41:31 -0500169 }
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 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500178 void pgoodChanged(sdbusplus::message_t& msg)
Matt Spinler432efec2020-09-24 13:41:31 -0500179 {
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:
Matt Spinler76e73c22021-04-21 11:03:05 -0500194 /**
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 }
Matthew Barthbff172a2021-06-17 11:04:18 -0500206 catch (const util::DBusServiceError& e)
Matt Spinler76e73c22021-04-21 11:03:05 -0500207 {
Matthew Barthbff172a2021-06-17 11:04:18 -0500208 // Wait for propertiesChanged signal when service starts
Matt Spinler76e73c22021-04-21 11:03:05 -0500209 }
210 }
211
Matt Spinler432efec2020-09-24 13:41:31 -0500212 /** @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 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500222 sdbusplus::bus::match_t _match;
Matt Spinler432efec2020-09-24 13:41:31 -0500223};
224
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530225/**
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 */
231class 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 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500257 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530258 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 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500274 void hostStateChanged(sdbusplus::message_t& msg)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530275 {
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);
Chau Ly0cff4ea2023-09-13 04:46:19 +0000296 setHostPowerState(hostPowerStates, true);
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530297 }
298 }
299
300 private:
Chau Ly0cff4ea2023-09-13 04:46:19 +0000301 void setHostPowerState(std::vector<HostState>& hostPowerStates,
302 bool callFromStateChange)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530303 {
304 bool powerStateflag = false;
305 for (const auto& powerState : hostPowerStates)
306 {
307 if (powerState == HostState::Standby ||
308 powerState == HostState::Running ||
309 powerState == HostState::TransitioningToRunning ||
310 powerState == HostState::Quiesced ||
311 powerState == HostState::DiagnosticMode)
312 {
313 powerStateflag = true;
314 break;
315 }
316 }
Chau Ly0cff4ea2023-09-13 04:46:19 +0000317 if (callFromStateChange)
318 {
319 setPowerState(powerStateflag);
320 }
321 else
322 {
323 // This won't call callbacks (if exists) during the constructor of
324 // the class when the first read for this flag is true which is
325 // different from the init value of _powerState.
326 // Daemon that wants to do anything when the initial power state
327 // is true can check isPowerOn() and do it.
328 _powerState = powerStateflag;
329 }
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530330 }
331
332 /**
333 * @brief Reads the CurrentHostState property from D-Bus and saves it.
334 */
335 void readHostState()
336 {
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530337 std::string hostStatePath;
338 std::string hostStateService;
339 std::string hostService = "xyz.openbmc_project.State.Host";
340 std::vector<HostState> hostPowerStates;
341
342 int32_t depth = 0;
343 const std::string path = "/";
344
345 auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
346 _bus, path, _hostStateInterface, depth);
347
348 for (const auto& path : mapperResponse)
349 {
350 for (const auto& service : path.second)
351 {
352 hostStateService = service.first;
353
354 if (hostStateService.find(hostService) != std::string::npos)
355 {
356 hostStatePath = path.first;
357
358 auto currentHostState =
359 util::SDBusPlus::getProperty<HostState>(
360 hostStateService, hostStatePath,
361 _hostStateInterface, _hostStateProperty);
362
363 hostPowerStates.emplace_back(currentHostState);
364 }
365 }
366 }
Chau Ly0cff4ea2023-09-13 04:46:19 +0000367 setHostPowerState(hostPowerStates, false);
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530368 }
369
370 const std::string _hostStatePath{"/xyz/openbmc_project/state"};
371
372 /** @brief D-Bus interface constant */
373 const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
374
375 /** @brief D-Bus property constant */
376 const std::string _hostStateProperty{"CurrentHostState"};
377
378 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500379 sdbusplus::bus::match_t _match;
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530380};
381
Matt Spinler432efec2020-09-24 13:41:31 -0500382} // namespace phosphor::fan