blob: 1b1d4191c5812bc0bdd4aeba38a954486e8c2e1d [file] [log] [blame]
Matt Spinler432efec2020-09-24 13:41:31 -05001#pragma once
2
3#include "sdbusplus.hpp"
4
5#include <fmt/format.h>
6
7#include <phosphor-logging/log.hpp>
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +05308#include <xyz/openbmc_project/State/Host/server.hpp>
Matt Spinler432efec2020-09-24 13:41:31 -05009
10#include <functional>
11
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +053012using HostState =
13 sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
14
Matt Spinler432efec2020-09-24 13:41:31 -050015namespace 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
Matt Spinler76e73c22021-04-21 11:03:05 -050023 * state change. A callback can be passed in using the constructor,
24 * or can be added later using addCallback().
Matt Spinler432efec2020-09-24 13:41:31 -050025 *
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 */
32class PowerState
33{
34 public:
35 using StateChangeFunc = std::function<void(bool)>;
36
Matt Spinler432efec2020-09-24 13:41:31 -050037 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 */
Patrick Williamscb356d42022-07-22 19:26:53 -050050 PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
Matt Spinler76e73c22021-04-21 11:03:05 -050051 {
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())
Matt Spinler432efec2020-09-24 13:41:31 -050061 {}
62
63 /**
Matt Spinler76e73c22021-04-21 11:03:05 -050064 * @brief Adds a function to call when the power state changes
65 *
66 * @param[in] - Any unique name, so the callback can be removed later
67 * if desired.
68 * @param[in] callback - The function that should be run when
69 * the power state changes
70 */
71 void addCallback(const std::string& name, StateChangeFunc callback)
72 {
73 _callbacks.emplace(name, std::move(callback));
74 }
75
76 /**
77 * @brief Remove the callback so it is no longer called
78 *
79 * @param[in] name - The name used when it was added.
80 */
81 void deleteCallback(const std::string& name)
82 {
83 _callbacks.erase(name);
84 }
85
86 /**
Matt Spinler432efec2020-09-24 13:41:31 -050087 * @brief Says if power is on
88 *
89 * @return bool - The power state
90 */
91 bool isPowerOn() const
92 {
93 return _powerState;
94 }
95
96 protected:
97 /**
98 * @brief Called by derived classes to set the power state value
99 *
Matt Spinler76e73c22021-04-21 11:03:05 -0500100 * Will call the callback functions if the state changed.
Matt Spinler432efec2020-09-24 13:41:31 -0500101 *
102 * @param[in] state - The new power state
103 */
104 void setPowerState(bool state)
105 {
106 if (state != _powerState)
107 {
108 _powerState = state;
Matt Spinler76e73c22021-04-21 11:03:05 -0500109 for (const auto& [name, callback] : _callbacks)
110 {
111 callback(_powerState);
112 }
Matt Spinler432efec2020-09-24 13:41:31 -0500113 }
114 }
115
116 /**
117 * @brief Reference to the D-Bus connection object.
118 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500119 sdbusplus::bus_t& _bus;
Matt Spinler432efec2020-09-24 13:41:31 -0500120
121 /**
122 * @brief The power state value
123 */
124 bool _powerState = false;
125
126 private:
127 /**
Matt Spinler76e73c22021-04-21 11:03:05 -0500128 * @brief The callback functions to run when the power state changes
Matt Spinler432efec2020-09-24 13:41:31 -0500129 */
Matt Spinler76e73c22021-04-21 11:03:05 -0500130 std::map<std::string, StateChangeFunc> _callbacks;
Matt Spinler432efec2020-09-24 13:41:31 -0500131};
132
133/**
134 * @class PGoodState
135 *
136 * This class implements the PowerState API by looking at the 'pgood'
137 * property on the org.openbmc.Control.Power interface.
138 */
139class PGoodState : public PowerState
140{
141 public:
Matt Spinler432efec2020-09-24 13:41:31 -0500142 virtual ~PGoodState() = default;
143 PGoodState(const PGoodState&) = delete;
144 PGoodState& operator=(const PGoodState&) = delete;
145 PGoodState(PGoodState&&) = delete;
146 PGoodState& operator=(PGoodState&&) = delete;
147
Matt Spinler76e73c22021-04-21 11:03:05 -0500148 PGoodState() :
149 PowerState(), _match(_bus,
150 sdbusplus::bus::match::rules::propertiesChanged(
151 _pgoodPath, _pgoodInterface),
152 [this](auto& msg) { this->pgoodChanged(msg); })
153 {
154 readPGood();
155 }
156
Matt Spinler432efec2020-09-24 13:41:31 -0500157 /**
158 * @brief Constructor
159 *
160 * @param[in] bus - The D-Bus bus connection object
161 * @param[in] callback - The function that should be run when
162 * the power state changes
163 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500164 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Matt Spinler432efec2020-09-24 13:41:31 -0500165 PowerState(bus, func),
166 _match(_bus,
167 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
168 _pgoodInterface),
169 [this](auto& msg) { this->pgoodChanged(msg); })
170 {
Matt Spinler76e73c22021-04-21 11:03:05 -0500171 readPGood();
Matt Spinler432efec2020-09-24 13:41:31 -0500172 }
173
174 /**
175 * @brief PropertiesChanged callback for the PGOOD property.
176 *
177 * Will call the registered callback function if necessary.
178 *
179 * @param[in] msg - The payload of the propertiesChanged signal
180 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500181 void pgoodChanged(sdbusplus::message_t& msg)
Matt Spinler432efec2020-09-24 13:41:31 -0500182 {
183 std::string interface;
184 std::map<std::string, std::variant<int32_t>> properties;
185
186 msg.read(interface, properties);
187
188 auto pgoodProp = properties.find(_pgoodProperty);
189 if (pgoodProp != properties.end())
190 {
191 auto pgood = std::get<int32_t>(pgoodProp->second);
192 setPowerState(pgood);
193 }
194 }
195
196 private:
Matt Spinler76e73c22021-04-21 11:03:05 -0500197 /**
198 * @brief Reads the PGOOD property from D-Bus and saves it.
199 */
200 void readPGood()
201 {
202 try
203 {
204 auto pgood = util::SDBusPlus::getProperty<int32_t>(
205 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
206
207 _powerState = static_cast<bool>(pgood);
208 }
Matthew Barthbff172a2021-06-17 11:04:18 -0500209 catch (const util::DBusServiceError& e)
Matt Spinler76e73c22021-04-21 11:03:05 -0500210 {
Matthew Barthbff172a2021-06-17 11:04:18 -0500211 // Wait for propertiesChanged signal when service starts
Matt Spinler76e73c22021-04-21 11:03:05 -0500212 }
213 }
214
Matt Spinler432efec2020-09-24 13:41:31 -0500215 /** @brief D-Bus path constant */
216 const std::string _pgoodPath{"/org/openbmc/control/power0"};
217
218 /** @brief D-Bus interface constant */
219 const std::string _pgoodInterface{"org.openbmc.control.Power"};
220
221 /** @brief D-Bus property constant */
222 const std::string _pgoodProperty{"pgood"};
223
224 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500225 sdbusplus::bus::match_t _match;
Matt Spinler432efec2020-09-24 13:41:31 -0500226};
227
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530228/**
229 * @class HostPowerState
230 *
231 * This class implements the PowerState API by looking at the 'powerState'
232 * property on the phosphor virtual sensor interface.
233 */
234class HostPowerState : public PowerState
235{
236 public:
237 virtual ~HostPowerState() = default;
238 HostPowerState(const HostPowerState&) = delete;
239 HostPowerState& operator=(const HostPowerState&) = delete;
240 HostPowerState(HostPowerState&&) = delete;
241 HostPowerState& operator=(HostPowerState&&) = delete;
242
243 HostPowerState() :
244 PowerState(),
245 _match(_bus,
246 sdbusplus::bus::match::rules::propertiesChangedNamespace(
247 _hostStatePath, _hostStateInterface),
248 [this](auto& msg) { this->hostStateChanged(msg); })
249 {
250 readHostState();
251 }
252
253 /**
254 * @brief Constructor
255 *
256 * @param[in] bus - The D-Bus bus connection object
257 * @param[in] callback - The function that should be run when
258 * the power state changes
259 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500260 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530261 PowerState(bus, func),
262 _match(_bus,
263 sdbusplus::bus::match::rules::propertiesChangedNamespace(
264 _hostStatePath, _hostStateInterface),
265 [this](auto& msg) { this->hostStateChanged(msg); })
266 {
267 readHostState();
268 }
269
270 /**
271 * @brief PropertiesChanged callback for the CurrentHostState property.
272 *
273 * Will call the registered callback function if necessary.
274 *
275 * @param[in] msg - The payload of the propertiesChanged signal
276 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500277 void hostStateChanged(sdbusplus::message_t& msg)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530278 {
279 std::string interface;
280 std::map<std::string, std::variant<std::string>> properties;
281 std::vector<HostState> hostPowerStates;
282
283 msg.read(interface, properties);
284
285 auto hostStateProp = properties.find(_hostStateProperty);
286 if (hostStateProp != properties.end())
287 {
288 auto currentHostState =
289 sdbusplus::message::convert_from_string<HostState>(
290 std::get<std::string>(hostStateProp->second));
291
292 if (!currentHostState)
293 {
294 throw sdbusplus::exception::InvalidEnumString();
295 }
296 HostState hostState = *currentHostState;
297
298 hostPowerStates.emplace_back(hostState);
299 setHostPowerState(hostPowerStates);
300 }
301 }
302
303 private:
304 void setHostPowerState(std::vector<HostState>& hostPowerStates)
305 {
306 bool powerStateflag = false;
307 for (const auto& powerState : hostPowerStates)
308 {
309 if (powerState == HostState::Standby ||
310 powerState == HostState::Running ||
311 powerState == HostState::TransitioningToRunning ||
312 powerState == HostState::Quiesced ||
313 powerState == HostState::DiagnosticMode)
314 {
315 powerStateflag = true;
316 break;
317 }
318 }
319 setPowerState(powerStateflag);
320 }
321
322 /**
323 * @brief Reads the CurrentHostState property from D-Bus and saves it.
324 */
325 void readHostState()
326 {
327
328 std::string hostStatePath;
329 std::string hostStateService;
330 std::string hostService = "xyz.openbmc_project.State.Host";
331 std::vector<HostState> hostPowerStates;
332
333 int32_t depth = 0;
334 const std::string path = "/";
335
336 auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
337 _bus, path, _hostStateInterface, depth);
338
339 for (const auto& path : mapperResponse)
340 {
341 for (const auto& service : path.second)
342 {
343 hostStateService = service.first;
344
345 if (hostStateService.find(hostService) != std::string::npos)
346 {
347 hostStatePath = path.first;
348
349 auto currentHostState =
350 util::SDBusPlus::getProperty<HostState>(
351 hostStateService, hostStatePath,
352 _hostStateInterface, _hostStateProperty);
353
354 hostPowerStates.emplace_back(currentHostState);
355 }
356 }
357 }
358 setHostPowerState(hostPowerStates);
359 }
360
361 const std::string _hostStatePath{"/xyz/openbmc_project/state"};
362
363 /** @brief D-Bus interface constant */
364 const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
365
366 /** @brief D-Bus property constant */
367 const std::string _hostStateProperty{"CurrentHostState"};
368
369 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500370 sdbusplus::bus::match_t _match;
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530371};
372
Matt Spinler432efec2020-09-24 13:41:31 -0500373} // namespace phosphor::fan