blob: 24b6c9d90becd4ffab19967e06b1ae67faef432a [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 */
Patrick Williams61b73292023-05-10 07:50:12 -050060 PowerState() : _bus(util::SDBusPlus::getBus()) {}
Matt Spinler432efec2020-09-24 13:41:31 -050061
62 /**
Matt Spinler76e73c22021-04-21 11:03:05 -050063 * @brief Adds a function to call when the power state changes
64 *
65 * @param[in] - Any unique name, so the callback can be removed later
66 * if desired.
67 * @param[in] callback - The function that should be run when
68 * the power state changes
69 */
70 void addCallback(const std::string& name, StateChangeFunc callback)
71 {
72 _callbacks.emplace(name, std::move(callback));
73 }
74
75 /**
76 * @brief Remove the callback so it is no longer called
77 *
78 * @param[in] name - The name used when it was added.
79 */
80 void deleteCallback(const std::string& name)
81 {
82 _callbacks.erase(name);
83 }
84
85 /**
Matt Spinler432efec2020-09-24 13:41:31 -050086 * @brief Says if power is on
87 *
88 * @return bool - The power state
89 */
90 bool isPowerOn() const
91 {
92 return _powerState;
93 }
94
95 protected:
96 /**
97 * @brief Called by derived classes to set the power state value
98 *
Matt Spinler76e73c22021-04-21 11:03:05 -050099 * Will call the callback functions if the state changed.
Matt Spinler432efec2020-09-24 13:41:31 -0500100 *
101 * @param[in] state - The new power state
102 */
103 void setPowerState(bool state)
104 {
105 if (state != _powerState)
106 {
107 _powerState = state;
Matt Spinler76e73c22021-04-21 11:03:05 -0500108 for (const auto& [name, callback] : _callbacks)
109 {
110 callback(_powerState);
111 }
Matt Spinler432efec2020-09-24 13:41:31 -0500112 }
113 }
114
115 /**
116 * @brief Reference to the D-Bus connection object.
117 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500118 sdbusplus::bus_t& _bus;
Matt Spinler432efec2020-09-24 13:41:31 -0500119
120 /**
121 * @brief The power state value
122 */
123 bool _powerState = false;
124
125 private:
126 /**
Matt Spinler76e73c22021-04-21 11:03:05 -0500127 * @brief The callback functions to run when the power state changes
Matt Spinler432efec2020-09-24 13:41:31 -0500128 */
Matt Spinler76e73c22021-04-21 11:03:05 -0500129 std::map<std::string, StateChangeFunc> _callbacks;
Matt Spinler432efec2020-09-24 13:41:31 -0500130};
131
132/**
133 * @class PGoodState
134 *
135 * This class implements the PowerState API by looking at the 'pgood'
136 * property on the org.openbmc.Control.Power interface.
137 */
138class PGoodState : public PowerState
139{
140 public:
Matt Spinler432efec2020-09-24 13:41:31 -0500141 virtual ~PGoodState() = default;
142 PGoodState(const PGoodState&) = delete;
143 PGoodState& operator=(const PGoodState&) = delete;
144 PGoodState(PGoodState&&) = delete;
145 PGoodState& operator=(PGoodState&&) = delete;
146
Matt Spinler76e73c22021-04-21 11:03:05 -0500147 PGoodState() :
148 PowerState(), _match(_bus,
149 sdbusplus::bus::match::rules::propertiesChanged(
150 _pgoodPath, _pgoodInterface),
151 [this](auto& msg) { this->pgoodChanged(msg); })
152 {
153 readPGood();
154 }
155
Matt Spinler432efec2020-09-24 13:41:31 -0500156 /**
157 * @brief Constructor
158 *
159 * @param[in] bus - The D-Bus bus connection object
160 * @param[in] callback - The function that should be run when
161 * the power state changes
162 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500163 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Matt Spinler432efec2020-09-24 13:41:31 -0500164 PowerState(bus, func),
165 _match(_bus,
166 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
167 _pgoodInterface),
168 [this](auto& msg) { this->pgoodChanged(msg); })
169 {
Matt Spinler76e73c22021-04-21 11:03:05 -0500170 readPGood();
Matt Spinler432efec2020-09-24 13:41:31 -0500171 }
172
173 /**
174 * @brief PropertiesChanged callback for the PGOOD property.
175 *
176 * Will call the registered callback function if necessary.
177 *
178 * @param[in] msg - The payload of the propertiesChanged signal
179 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500180 void pgoodChanged(sdbusplus::message_t& msg)
Matt Spinler432efec2020-09-24 13:41:31 -0500181 {
182 std::string interface;
183 std::map<std::string, std::variant<int32_t>> properties;
184
185 msg.read(interface, properties);
186
187 auto pgoodProp = properties.find(_pgoodProperty);
188 if (pgoodProp != properties.end())
189 {
190 auto pgood = std::get<int32_t>(pgoodProp->second);
191 setPowerState(pgood);
192 }
193 }
194
195 private:
Matt Spinler76e73c22021-04-21 11:03:05 -0500196 /**
197 * @brief Reads the PGOOD property from D-Bus and saves it.
198 */
199 void readPGood()
200 {
201 try
202 {
203 auto pgood = util::SDBusPlus::getProperty<int32_t>(
204 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
205
206 _powerState = static_cast<bool>(pgood);
207 }
Matthew Barthbff172a2021-06-17 11:04:18 -0500208 catch (const util::DBusServiceError& e)
Matt Spinler76e73c22021-04-21 11:03:05 -0500209 {
Matthew Barthbff172a2021-06-17 11:04:18 -0500210 // Wait for propertiesChanged signal when service starts
Matt Spinler76e73c22021-04-21 11:03:05 -0500211 }
212 }
213
Matt Spinler432efec2020-09-24 13:41:31 -0500214 /** @brief D-Bus path constant */
215 const std::string _pgoodPath{"/org/openbmc/control/power0"};
216
217 /** @brief D-Bus interface constant */
218 const std::string _pgoodInterface{"org.openbmc.control.Power"};
219
220 /** @brief D-Bus property constant */
221 const std::string _pgoodProperty{"pgood"};
222
223 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500224 sdbusplus::bus::match_t _match;
Matt Spinler432efec2020-09-24 13:41:31 -0500225};
226
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530227/**
228 * @class HostPowerState
229 *
230 * This class implements the PowerState API by looking at the 'powerState'
231 * property on the phosphor virtual sensor interface.
232 */
233class HostPowerState : public PowerState
234{
235 public:
236 virtual ~HostPowerState() = default;
237 HostPowerState(const HostPowerState&) = delete;
238 HostPowerState& operator=(const HostPowerState&) = delete;
239 HostPowerState(HostPowerState&&) = delete;
240 HostPowerState& operator=(HostPowerState&&) = delete;
241
242 HostPowerState() :
243 PowerState(),
244 _match(_bus,
245 sdbusplus::bus::match::rules::propertiesChangedNamespace(
246 _hostStatePath, _hostStateInterface),
247 [this](auto& msg) { this->hostStateChanged(msg); })
248 {
249 readHostState();
250 }
251
252 /**
253 * @brief Constructor
254 *
255 * @param[in] bus - The D-Bus bus connection object
256 * @param[in] callback - The function that should be run when
257 * the power state changes
258 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500259 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530260 PowerState(bus, func),
261 _match(_bus,
262 sdbusplus::bus::match::rules::propertiesChangedNamespace(
263 _hostStatePath, _hostStateInterface),
264 [this](auto& msg) { this->hostStateChanged(msg); })
265 {
266 readHostState();
267 }
268
269 /**
270 * @brief PropertiesChanged callback for the CurrentHostState property.
271 *
272 * Will call the registered callback function if necessary.
273 *
274 * @param[in] msg - The payload of the propertiesChanged signal
275 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500276 void hostStateChanged(sdbusplus::message_t& msg)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530277 {
278 std::string interface;
279 std::map<std::string, std::variant<std::string>> properties;
280 std::vector<HostState> hostPowerStates;
281
282 msg.read(interface, properties);
283
284 auto hostStateProp = properties.find(_hostStateProperty);
285 if (hostStateProp != properties.end())
286 {
287 auto currentHostState =
288 sdbusplus::message::convert_from_string<HostState>(
289 std::get<std::string>(hostStateProp->second));
290
291 if (!currentHostState)
292 {
293 throw sdbusplus::exception::InvalidEnumString();
294 }
295 HostState hostState = *currentHostState;
296
297 hostPowerStates.emplace_back(hostState);
298 setHostPowerState(hostPowerStates);
299 }
300 }
301
302 private:
303 void setHostPowerState(std::vector<HostState>& hostPowerStates)
304 {
305 bool powerStateflag = false;
306 for (const auto& powerState : hostPowerStates)
307 {
308 if (powerState == HostState::Standby ||
309 powerState == HostState::Running ||
310 powerState == HostState::TransitioningToRunning ||
311 powerState == HostState::Quiesced ||
312 powerState == HostState::DiagnosticMode)
313 {
314 powerStateflag = true;
315 break;
316 }
317 }
318 setPowerState(powerStateflag);
319 }
320
321 /**
322 * @brief Reads the CurrentHostState property from D-Bus and saves it.
323 */
324 void readHostState()
325 {
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530326 std::string hostStatePath;
327 std::string hostStateService;
328 std::string hostService = "xyz.openbmc_project.State.Host";
329 std::vector<HostState> hostPowerStates;
330
331 int32_t depth = 0;
332 const std::string path = "/";
333
334 auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
335 _bus, path, _hostStateInterface, depth);
336
337 for (const auto& path : mapperResponse)
338 {
339 for (const auto& service : path.second)
340 {
341 hostStateService = service.first;
342
343 if (hostStateService.find(hostService) != std::string::npos)
344 {
345 hostStatePath = path.first;
346
347 auto currentHostState =
348 util::SDBusPlus::getProperty<HostState>(
349 hostStateService, hostStatePath,
350 _hostStateInterface, _hostStateProperty);
351
352 hostPowerStates.emplace_back(currentHostState);
353 }
354 }
355 }
356 setHostPowerState(hostPowerStates);
357 }
358
359 const std::string _hostStatePath{"/xyz/openbmc_project/state"};
360
361 /** @brief D-Bus interface constant */
362 const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
363
364 /** @brief D-Bus property constant */
365 const std::string _hostStateProperty{"CurrentHostState"};
366
367 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500368 sdbusplus::bus::match_t _match;
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530369};
370
Matt Spinler432efec2020-09-24 13:41:31 -0500371} // namespace phosphor::fan