1 #pragma once 2 3 #include "sdbusplus.hpp" 4 5 #include <xyz/openbmc_project/State/Host/server.hpp> 6 7 #include <functional> 8 9 using HostState = 10 sdbusplus::xyz::openbmc_project::State::server::Host::HostState; 11 12 namespace 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 20 * state change. A callback can be passed in using the constructor, 21 * or can be added later using addCallback(). 22 * 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 */ 29 class PowerState 30 { 31 public: 32 using StateChangeFunc = std::function<void(bool)>; 33 34 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 */ PowerState(sdbusplus::bus_t & bus,StateChangeFunc callback)47 PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus) 48 { 49 _callbacks.emplace("default", std::move(callback)); 50 } 51 52 /** 53 * @brief Constructor 54 * 55 * Callbacks can be added with addCallback(). 56 */ PowerState()57 PowerState() : _bus(util::SDBusPlus::getBus()) {} 58 59 /** 60 * @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 */ addCallback(const std::string & name,StateChangeFunc callback)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 */ deleteCallback(const std::string & name)77 void deleteCallback(const std::string& name) 78 { 79 _callbacks.erase(name); 80 } 81 82 /** 83 * @brief Says if power is on 84 * 85 * @return bool - The power state 86 */ isPowerOn() const87 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 * 96 * Will call the callback functions if the state changed. 97 * 98 * @param[in] state - The new power state 99 */ setPowerState(bool state)100 void setPowerState(bool state) 101 { 102 if (state != _powerState) 103 { 104 _powerState = state; 105 for (const auto& [name, callback] : _callbacks) 106 { 107 callback(_powerState); 108 } 109 } 110 } 111 112 /** 113 * @brief Reference to the D-Bus connection object. 114 */ 115 sdbusplus::bus_t& _bus; 116 117 /** 118 * @brief The power state value 119 */ 120 bool _powerState = false; 121 122 private: 123 /** 124 * @brief The callback functions to run when the power state changes 125 */ 126 std::map<std::string, StateChangeFunc> _callbacks; 127 }; 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 */ 135 class PGoodState : public PowerState 136 { 137 public: 138 virtual ~PGoodState() = default; 139 PGoodState(const PGoodState&) = delete; 140 PGoodState& operator=(const PGoodState&) = delete; 141 PGoodState(PGoodState&&) = delete; 142 PGoodState& operator=(PGoodState&&) = delete; 143 PGoodState()144 PGoodState() : 145 PowerState(), 146 _match(_bus, 147 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath, 148 _pgoodInterface), 149 [this](auto& msg) { this->pgoodChanged(msg); }) 150 { 151 readPGood(); 152 } 153 154 /** 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 */ PGoodState(sdbusplus::bus_t & bus,StateChangeFunc func)161 PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) : 162 PowerState(bus, func), 163 _match(_bus, 164 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath, 165 _pgoodInterface), 166 [this](auto& msg) { this->pgoodChanged(msg); }) 167 { 168 readPGood(); 169 } 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 */ pgoodChanged(sdbusplus::message_t & msg)178 void pgoodChanged(sdbusplus::message_t& msg) 179 { 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: 194 /** 195 * @brief Reads the PGOOD property from D-Bus and saves it. 196 */ readPGood()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 } 206 catch (const util::DBusServiceError& e) 207 { 208 // Wait for propertiesChanged signal when service starts 209 } 210 } 211 212 /** @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 */ 222 sdbusplus::bus::match_t _match; 223 }; 224 225 /** 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 */ 231 class 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 HostPowerState()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 */ HostPowerState(sdbusplus::bus_t & bus,StateChangeFunc func)257 HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) : 258 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 */ hostStateChanged(sdbusplus::message_t & msg)274 void hostStateChanged(sdbusplus::message_t& msg) 275 { 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); 296 setHostPowerState(hostPowerStates, true); 297 } 298 } 299 300 private: setHostPowerState(std::vector<HostState> & hostPowerStates,bool callFromStateChange)301 void setHostPowerState(std::vector<HostState>& hostPowerStates, 302 bool callFromStateChange) 303 { 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 } 317 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 } 330 } 331 332 /** 333 * @brief Reads the CurrentHostState property from D-Bus and saves it. 334 */ readHostState()335 void readHostState() 336 { 337 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 } 367 setHostPowerState(hostPowerStates, false); 368 } 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 */ 379 sdbusplus::bus::match_t _match; 380 }; 381 382 } // namespace phosphor::fan 383