blob: c5b0f0d253213bf8d0fe25b2d25935018e707204 [file] [log] [blame]
Matt Spinler432efec2020-09-24 13:41:31 -05001#pragma once
2
3#include "sdbusplus.hpp"
4
Matt Spinler432efec2020-09-24 13:41:31 -05005#include <phosphor-logging/log.hpp>
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +05306#include <xyz/openbmc_project/State/Host/server.hpp>
Matt Spinler432efec2020-09-24 13:41:31 -05007
8#include <functional>
9
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +053010using HostState =
11 sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
12
Matt Spinler432efec2020-09-24 13:41:31 -050013namespace phosphor::fan
14{
15
16/**
17 * @class PowerState
18 *
19 * This class provides an interface to check the current power state,
20 * and to register a function that gets called when there is a power
Matt Spinler76e73c22021-04-21 11:03:05 -050021 * state change. A callback can be passed in using the constructor,
22 * or can be added later using addCallback().
Matt Spinler432efec2020-09-24 13:41:31 -050023 *
24 * Different architectures may have different ways of considering
25 * power to be on, such as a pgood property on the
26 * org.openbmc.Control.Power interface, or the CurrentPowerState
27 * property on the State.Chassis interface, so those details will
28 * be in a derived class.
29 */
30class PowerState
31{
32 public:
33 using StateChangeFunc = std::function<void(bool)>;
34
Matt Spinler432efec2020-09-24 13:41:31 -050035 virtual ~PowerState() = default;
36 PowerState(const PowerState&) = delete;
37 PowerState& operator=(const PowerState&) = delete;
38 PowerState(PowerState&&) = delete;
39 PowerState& operator=(PowerState&&) = delete;
40
41 /**
42 * @brief Constructor
43 *
44 * @param[in] bus - The D-Bus bus connection object
45 * @param[in] callback - The function that should be run when
46 * the power state changes
47 */
Patrick Williamscb356d42022-07-22 19:26:53 -050048 PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
Matt Spinler76e73c22021-04-21 11:03:05 -050049 {
50 _callbacks.emplace("default", std::move(callback));
51 }
52
53 /**
54 * @brief Constructor
55 *
56 * Callbacks can be added with addCallback().
57 */
Patrick Williams61b73292023-05-10 07:50:12 -050058 PowerState() : _bus(util::SDBusPlus::getBus()) {}
Matt Spinler432efec2020-09-24 13:41:31 -050059
60 /**
Matt Spinler76e73c22021-04-21 11:03:05 -050061 * @brief Adds a function to call when the power state changes
62 *
63 * @param[in] - Any unique name, so the callback can be removed later
64 * if desired.
65 * @param[in] callback - The function that should be run when
66 * the power state changes
67 */
68 void addCallback(const std::string& name, StateChangeFunc callback)
69 {
70 _callbacks.emplace(name, std::move(callback));
71 }
72
73 /**
74 * @brief Remove the callback so it is no longer called
75 *
76 * @param[in] name - The name used when it was added.
77 */
78 void deleteCallback(const std::string& name)
79 {
80 _callbacks.erase(name);
81 }
82
83 /**
Matt Spinler432efec2020-09-24 13:41:31 -050084 * @brief Says if power is on
85 *
86 * @return bool - The power state
87 */
88 bool isPowerOn() const
89 {
90 return _powerState;
91 }
92
93 protected:
94 /**
95 * @brief Called by derived classes to set the power state value
96 *
Matt Spinler76e73c22021-04-21 11:03:05 -050097 * Will call the callback functions if the state changed.
Matt Spinler432efec2020-09-24 13:41:31 -050098 *
99 * @param[in] state - The new power state
100 */
101 void setPowerState(bool state)
102 {
103 if (state != _powerState)
104 {
105 _powerState = state;
Matt Spinler76e73c22021-04-21 11:03:05 -0500106 for (const auto& [name, callback] : _callbacks)
107 {
108 callback(_powerState);
109 }
Matt Spinler432efec2020-09-24 13:41:31 -0500110 }
111 }
112
113 /**
114 * @brief Reference to the D-Bus connection object.
115 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500116 sdbusplus::bus_t& _bus;
Matt Spinler432efec2020-09-24 13:41:31 -0500117
118 /**
119 * @brief The power state value
120 */
121 bool _powerState = false;
122
123 private:
124 /**
Matt Spinler76e73c22021-04-21 11:03:05 -0500125 * @brief The callback functions to run when the power state changes
Matt Spinler432efec2020-09-24 13:41:31 -0500126 */
Matt Spinler76e73c22021-04-21 11:03:05 -0500127 std::map<std::string, StateChangeFunc> _callbacks;
Matt Spinler432efec2020-09-24 13:41:31 -0500128};
129
130/**
131 * @class PGoodState
132 *
133 * This class implements the PowerState API by looking at the 'pgood'
134 * property on the org.openbmc.Control.Power interface.
135 */
136class PGoodState : public PowerState
137{
138 public:
Matt Spinler432efec2020-09-24 13:41:31 -0500139 virtual ~PGoodState() = default;
140 PGoodState(const PGoodState&) = delete;
141 PGoodState& operator=(const PGoodState&) = delete;
142 PGoodState(PGoodState&&) = delete;
143 PGoodState& operator=(PGoodState&&) = delete;
144
Matt Spinler76e73c22021-04-21 11:03:05 -0500145 PGoodState() :
Patrick Williamsdfddd642024-08-16 15:21:51 -0400146 PowerState(),
147 _match(_bus,
148 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
149 _pgoodInterface),
150 [this](auto& msg) { this->pgoodChanged(msg); })
Matt Spinler76e73c22021-04-21 11:03:05 -0500151 {
152 readPGood();
153 }
154
Matt Spinler432efec2020-09-24 13:41:31 -0500155 /**
156 * @brief Constructor
157 *
158 * @param[in] bus - The D-Bus bus connection object
159 * @param[in] callback - The function that should be run when
160 * the power state changes
161 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500162 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Matt Spinler432efec2020-09-24 13:41:31 -0500163 PowerState(bus, func),
164 _match(_bus,
165 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
166 _pgoodInterface),
167 [this](auto& msg) { this->pgoodChanged(msg); })
168 {
Matt Spinler76e73c22021-04-21 11:03:05 -0500169 readPGood();
Matt Spinler432efec2020-09-24 13:41:31 -0500170 }
171
172 /**
173 * @brief PropertiesChanged callback for the PGOOD property.
174 *
175 * Will call the registered callback function if necessary.
176 *
177 * @param[in] msg - The payload of the propertiesChanged signal
178 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500179 void pgoodChanged(sdbusplus::message_t& msg)
Matt Spinler432efec2020-09-24 13:41:31 -0500180 {
181 std::string interface;
182 std::map<std::string, std::variant<int32_t>> properties;
183
184 msg.read(interface, properties);
185
186 auto pgoodProp = properties.find(_pgoodProperty);
187 if (pgoodProp != properties.end())
188 {
189 auto pgood = std::get<int32_t>(pgoodProp->second);
190 setPowerState(pgood);
191 }
192 }
193
194 private:
Matt Spinler76e73c22021-04-21 11:03:05 -0500195 /**
196 * @brief Reads the PGOOD property from D-Bus and saves it.
197 */
198 void readPGood()
199 {
200 try
201 {
202 auto pgood = util::SDBusPlus::getProperty<int32_t>(
203 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
204
205 _powerState = static_cast<bool>(pgood);
206 }
Matthew Barthbff172a2021-06-17 11:04:18 -0500207 catch (const util::DBusServiceError& e)
Matt Spinler76e73c22021-04-21 11:03:05 -0500208 {
Matthew Barthbff172a2021-06-17 11:04:18 -0500209 // Wait for propertiesChanged signal when service starts
Matt Spinler76e73c22021-04-21 11:03:05 -0500210 }
211 }
212
Matt Spinler432efec2020-09-24 13:41:31 -0500213 /** @brief D-Bus path constant */
214 const std::string _pgoodPath{"/org/openbmc/control/power0"};
215
216 /** @brief D-Bus interface constant */
217 const std::string _pgoodInterface{"org.openbmc.control.Power"};
218
219 /** @brief D-Bus property constant */
220 const std::string _pgoodProperty{"pgood"};
221
222 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500223 sdbusplus::bus::match_t _match;
Matt Spinler432efec2020-09-24 13:41:31 -0500224};
225
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530226/**
227 * @class HostPowerState
228 *
229 * This class implements the PowerState API by looking at the 'powerState'
230 * property on the phosphor virtual sensor interface.
231 */
232class HostPowerState : public PowerState
233{
234 public:
235 virtual ~HostPowerState() = default;
236 HostPowerState(const HostPowerState&) = delete;
237 HostPowerState& operator=(const HostPowerState&) = delete;
238 HostPowerState(HostPowerState&&) = delete;
239 HostPowerState& operator=(HostPowerState&&) = delete;
240
241 HostPowerState() :
242 PowerState(),
243 _match(_bus,
244 sdbusplus::bus::match::rules::propertiesChangedNamespace(
245 _hostStatePath, _hostStateInterface),
246 [this](auto& msg) { this->hostStateChanged(msg); })
247 {
248 readHostState();
249 }
250
251 /**
252 * @brief Constructor
253 *
254 * @param[in] bus - The D-Bus bus connection object
255 * @param[in] callback - The function that should be run when
256 * the power state changes
257 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500258 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530259 PowerState(bus, func),
260 _match(_bus,
261 sdbusplus::bus::match::rules::propertiesChangedNamespace(
262 _hostStatePath, _hostStateInterface),
263 [this](auto& msg) { this->hostStateChanged(msg); })
264 {
265 readHostState();
266 }
267
268 /**
269 * @brief PropertiesChanged callback for the CurrentHostState property.
270 *
271 * Will call the registered callback function if necessary.
272 *
273 * @param[in] msg - The payload of the propertiesChanged signal
274 */
Patrick Williamscb356d42022-07-22 19:26:53 -0500275 void hostStateChanged(sdbusplus::message_t& msg)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530276 {
277 std::string interface;
278 std::map<std::string, std::variant<std::string>> properties;
279 std::vector<HostState> hostPowerStates;
280
281 msg.read(interface, properties);
282
283 auto hostStateProp = properties.find(_hostStateProperty);
284 if (hostStateProp != properties.end())
285 {
286 auto currentHostState =
287 sdbusplus::message::convert_from_string<HostState>(
288 std::get<std::string>(hostStateProp->second));
289
290 if (!currentHostState)
291 {
292 throw sdbusplus::exception::InvalidEnumString();
293 }
294 HostState hostState = *currentHostState;
295
296 hostPowerStates.emplace_back(hostState);
Chau Ly0cff4ea2023-09-13 04:46:19 +0000297 setHostPowerState(hostPowerStates, true);
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530298 }
299 }
300
301 private:
Chau Ly0cff4ea2023-09-13 04:46:19 +0000302 void setHostPowerState(std::vector<HostState>& hostPowerStates,
303 bool callFromStateChange)
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530304 {
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 }
Chau Ly0cff4ea2023-09-13 04:46:19 +0000318 if (callFromStateChange)
319 {
320 setPowerState(powerStateflag);
321 }
322 else
323 {
324 // This won't call callbacks (if exists) during the constructor of
325 // the class when the first read for this flag is true which is
326 // different from the init value of _powerState.
327 // Daemon that wants to do anything when the initial power state
328 // is true can check isPowerOn() and do it.
329 _powerState = powerStateflag;
330 }
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530331 }
332
333 /**
334 * @brief Reads the CurrentHostState property from D-Bus and saves it.
335 */
336 void readHostState()
337 {
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530338 std::string hostStatePath;
339 std::string hostStateService;
340 std::string hostService = "xyz.openbmc_project.State.Host";
341 std::vector<HostState> hostPowerStates;
342
343 int32_t depth = 0;
344 const std::string path = "/";
345
346 auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
347 _bus, path, _hostStateInterface, depth);
348
349 for (const auto& path : mapperResponse)
350 {
351 for (const auto& service : path.second)
352 {
353 hostStateService = service.first;
354
355 if (hostStateService.find(hostService) != std::string::npos)
356 {
357 hostStatePath = path.first;
358
359 auto currentHostState =
360 util::SDBusPlus::getProperty<HostState>(
361 hostStateService, hostStatePath,
362 _hostStateInterface, _hostStateProperty);
363
364 hostPowerStates.emplace_back(currentHostState);
365 }
366 }
367 }
Chau Ly0cff4ea2023-09-13 04:46:19 +0000368 setHostPowerState(hostPowerStates, false);
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530369 }
370
371 const std::string _hostStatePath{"/xyz/openbmc_project/state"};
372
373 /** @brief D-Bus interface constant */
374 const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
375
376 /** @brief D-Bus property constant */
377 const std::string _hostStateProperty{"CurrentHostState"};
378
379 /** @brief The propertiesChanged match */
Patrick Williamscb356d42022-07-22 19:26:53 -0500380 sdbusplus::bus::match_t _match;
Kumar Thangavel2dd87bd2021-11-24 20:13:16 +0530381};
382
Matt Spinler432efec2020-09-24 13:41:31 -0500383} // namespace phosphor::fan