1 #pragma once 2 3 #include "sdbusplus.hpp" 4 5 #include <fmt/format.h> 6 7 #include <phosphor-logging/log.hpp> 8 #include <xyz/openbmc_project/State/Host/server.hpp> 9 10 #include <functional> 11 12 using HostState = 13 sdbusplus::xyz::openbmc_project::State::server::Host::HostState; 14 15 namespace 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 23 * state change. A callback can be passed in using the constructor, 24 * or can be added later using addCallback(). 25 * 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 */ 32 class PowerState 33 { 34 public: 35 using StateChangeFunc = std::function<void(bool)>; 36 37 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 */ 50 PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus) 51 { 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()) {} 61 62 /** 63 * @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 /** 86 * @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 * 99 * Will call the callback functions if the state changed. 100 * 101 * @param[in] state - The new power state 102 */ 103 void setPowerState(bool state) 104 { 105 if (state != _powerState) 106 { 107 _powerState = state; 108 for (const auto& [name, callback] : _callbacks) 109 { 110 callback(_powerState); 111 } 112 } 113 } 114 115 /** 116 * @brief Reference to the D-Bus connection object. 117 */ 118 sdbusplus::bus_t& _bus; 119 120 /** 121 * @brief The power state value 122 */ 123 bool _powerState = false; 124 125 private: 126 /** 127 * @brief The callback functions to run when the power state changes 128 */ 129 std::map<std::string, StateChangeFunc> _callbacks; 130 }; 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 */ 138 class PGoodState : public PowerState 139 { 140 public: 141 virtual ~PGoodState() = default; 142 PGoodState(const PGoodState&) = delete; 143 PGoodState& operator=(const PGoodState&) = delete; 144 PGoodState(PGoodState&&) = delete; 145 PGoodState& operator=(PGoodState&&) = delete; 146 147 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 156 /** 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 */ 163 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) : 164 PowerState(bus, func), 165 _match(_bus, 166 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath, 167 _pgoodInterface), 168 [this](auto& msg) { this->pgoodChanged(msg); }) 169 { 170 readPGood(); 171 } 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 */ 180 void pgoodChanged(sdbusplus::message_t& msg) 181 { 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: 196 /** 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 } 208 catch (const util::DBusServiceError& e) 209 { 210 // Wait for propertiesChanged signal when service starts 211 } 212 } 213 214 /** @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 */ 224 sdbusplus::bus::match_t _match; 225 }; 226 227 /** 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 */ 233 class 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 */ 259 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) : 260 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 */ 276 void hostStateChanged(sdbusplus::message_t& msg) 277 { 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 { 326 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 */ 368 sdbusplus::bus::match_t _match; 369 }; 370 371 } // namespace phosphor::fan 372