xref: /openbmc/phosphor-fan-presence/power_state.hpp (revision dfddd648cb81b27492afead4e2346f5fcd1397cb)
1432efeccSMatt Spinler #pragma once
2432efeccSMatt Spinler 
3432efeccSMatt Spinler #include "sdbusplus.hpp"
4432efeccSMatt Spinler 
5432efeccSMatt Spinler #include <phosphor-logging/log.hpp>
62dd87bd1SKumar Thangavel #include <xyz/openbmc_project/State/Host/server.hpp>
7432efeccSMatt Spinler 
8432efeccSMatt Spinler #include <functional>
9432efeccSMatt Spinler 
102dd87bd1SKumar Thangavel using HostState =
112dd87bd1SKumar Thangavel     sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
122dd87bd1SKumar Thangavel 
13432efeccSMatt Spinler namespace phosphor::fan
14432efeccSMatt Spinler {
15432efeccSMatt Spinler 
16432efeccSMatt Spinler /**
17432efeccSMatt Spinler  * @class PowerState
18432efeccSMatt Spinler  *
19432efeccSMatt Spinler  * This class provides an interface to check the current power state,
20432efeccSMatt Spinler  * and to register a function that gets called when there is a power
2176e73c2aSMatt Spinler  * state change.  A callback can be passed in using the constructor,
2276e73c2aSMatt Spinler  * or can be added later using addCallback().
23432efeccSMatt Spinler  *
24432efeccSMatt Spinler  * Different architectures may have different ways of considering
25432efeccSMatt Spinler  * power to be on, such as a pgood property on the
26432efeccSMatt Spinler  * org.openbmc.Control.Power interface, or the CurrentPowerState
27432efeccSMatt Spinler  * property on the State.Chassis interface, so those details will
28432efeccSMatt Spinler  * be in a derived class.
29432efeccSMatt Spinler  */
30432efeccSMatt Spinler class PowerState
31432efeccSMatt Spinler {
32432efeccSMatt Spinler   public:
33432efeccSMatt Spinler     using StateChangeFunc = std::function<void(bool)>;
34432efeccSMatt Spinler 
35432efeccSMatt Spinler     virtual ~PowerState() = default;
36432efeccSMatt Spinler     PowerState(const PowerState&) = delete;
37432efeccSMatt Spinler     PowerState& operator=(const PowerState&) = delete;
38432efeccSMatt Spinler     PowerState(PowerState&&) = delete;
39432efeccSMatt Spinler     PowerState& operator=(PowerState&&) = delete;
40432efeccSMatt Spinler 
41432efeccSMatt Spinler     /**
42432efeccSMatt Spinler      * @brief Constructor
43432efeccSMatt Spinler      *
44432efeccSMatt Spinler      * @param[in] bus - The D-Bus bus connection object
45432efeccSMatt Spinler      * @param[in] callback - The function that should be run when
46432efeccSMatt Spinler      *                       the power state changes
47432efeccSMatt Spinler      */
PowerState(sdbusplus::bus_t & bus,StateChangeFunc callback)48cb356d48SPatrick Williams     PowerState(sdbusplus::bus_t& bus, StateChangeFunc callback) : _bus(bus)
4976e73c2aSMatt Spinler     {
5076e73c2aSMatt Spinler         _callbacks.emplace("default", std::move(callback));
5176e73c2aSMatt Spinler     }
5276e73c2aSMatt Spinler 
5376e73c2aSMatt Spinler     /**
5476e73c2aSMatt Spinler      * @brief Constructor
5576e73c2aSMatt Spinler      *
5676e73c2aSMatt Spinler      * Callbacks can be added with addCallback().
5776e73c2aSMatt Spinler      */
PowerState()5861b73296SPatrick Williams     PowerState() : _bus(util::SDBusPlus::getBus()) {}
59432efeccSMatt Spinler 
60432efeccSMatt Spinler     /**
6176e73c2aSMatt Spinler      * @brief Adds a function to call when the power state changes
6276e73c2aSMatt Spinler      *
6376e73c2aSMatt Spinler      * @param[in] - Any unique name, so the callback can be removed later
6476e73c2aSMatt Spinler      *              if desired.
6576e73c2aSMatt Spinler      * @param[in] callback - The function that should be run when
6676e73c2aSMatt Spinler      *                       the power state changes
6776e73c2aSMatt Spinler      */
addCallback(const std::string & name,StateChangeFunc callback)6876e73c2aSMatt Spinler     void addCallback(const std::string& name, StateChangeFunc callback)
6976e73c2aSMatt Spinler     {
7076e73c2aSMatt Spinler         _callbacks.emplace(name, std::move(callback));
7176e73c2aSMatt Spinler     }
7276e73c2aSMatt Spinler 
7376e73c2aSMatt Spinler     /**
7476e73c2aSMatt Spinler      * @brief Remove the callback so it is no longer called
7576e73c2aSMatt Spinler      *
7676e73c2aSMatt Spinler      * @param[in] name - The name used when it was added.
7776e73c2aSMatt Spinler      */
deleteCallback(const std::string & name)7876e73c2aSMatt Spinler     void deleteCallback(const std::string& name)
7976e73c2aSMatt Spinler     {
8076e73c2aSMatt Spinler         _callbacks.erase(name);
8176e73c2aSMatt Spinler     }
8276e73c2aSMatt Spinler 
8376e73c2aSMatt Spinler     /**
84432efeccSMatt Spinler      * @brief Says if power is on
85432efeccSMatt Spinler      *
86432efeccSMatt Spinler      * @return bool - The power state
87432efeccSMatt Spinler      */
isPowerOn() const88432efeccSMatt Spinler     bool isPowerOn() const
89432efeccSMatt Spinler     {
90432efeccSMatt Spinler         return _powerState;
91432efeccSMatt Spinler     }
92432efeccSMatt Spinler 
93432efeccSMatt Spinler   protected:
94432efeccSMatt Spinler     /**
95432efeccSMatt Spinler      * @brief Called by derived classes to set the power state value
96432efeccSMatt Spinler      *
9776e73c2aSMatt Spinler      * Will call the callback functions if the state changed.
98432efeccSMatt Spinler      *
99432efeccSMatt Spinler      * @param[in] state - The new power state
100432efeccSMatt Spinler      */
setPowerState(bool state)101432efeccSMatt Spinler     void setPowerState(bool state)
102432efeccSMatt Spinler     {
103432efeccSMatt Spinler         if (state != _powerState)
104432efeccSMatt Spinler         {
105432efeccSMatt Spinler             _powerState = state;
10676e73c2aSMatt Spinler             for (const auto& [name, callback] : _callbacks)
10776e73c2aSMatt Spinler             {
10876e73c2aSMatt Spinler                 callback(_powerState);
10976e73c2aSMatt Spinler             }
110432efeccSMatt Spinler         }
111432efeccSMatt Spinler     }
112432efeccSMatt Spinler 
113432efeccSMatt Spinler     /**
114432efeccSMatt Spinler      * @brief Reference to the D-Bus connection object.
115432efeccSMatt Spinler      */
116cb356d48SPatrick Williams     sdbusplus::bus_t& _bus;
117432efeccSMatt Spinler 
118432efeccSMatt Spinler     /**
119432efeccSMatt Spinler      * @brief The power state value
120432efeccSMatt Spinler      */
121432efeccSMatt Spinler     bool _powerState = false;
122432efeccSMatt Spinler 
123432efeccSMatt Spinler   private:
124432efeccSMatt Spinler     /**
12576e73c2aSMatt Spinler      * @brief The callback functions to run when the power state changes
126432efeccSMatt Spinler      */
12776e73c2aSMatt Spinler     std::map<std::string, StateChangeFunc> _callbacks;
128432efeccSMatt Spinler };
129432efeccSMatt Spinler 
130432efeccSMatt Spinler /**
131432efeccSMatt Spinler  * @class PGoodState
132432efeccSMatt Spinler  *
133432efeccSMatt Spinler  * This class implements the PowerState API by looking at the 'pgood'
134432efeccSMatt Spinler  * property on the org.openbmc.Control.Power interface.
135432efeccSMatt Spinler  */
136432efeccSMatt Spinler class PGoodState : public PowerState
137432efeccSMatt Spinler {
138432efeccSMatt Spinler   public:
139432efeccSMatt Spinler     virtual ~PGoodState() = default;
140432efeccSMatt Spinler     PGoodState(const PGoodState&) = delete;
141432efeccSMatt Spinler     PGoodState& operator=(const PGoodState&) = delete;
142432efeccSMatt Spinler     PGoodState(PGoodState&&) = delete;
143432efeccSMatt Spinler     PGoodState& operator=(PGoodState&&) = delete;
144432efeccSMatt Spinler 
PGoodState()14576e73c2aSMatt Spinler     PGoodState() :
146*dfddd648SPatrick Williams         PowerState(),
147*dfddd648SPatrick Williams         _match(_bus,
148*dfddd648SPatrick Williams                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
149*dfddd648SPatrick Williams                                                                _pgoodInterface),
15076e73c2aSMatt Spinler                [this](auto& msg) { this->pgoodChanged(msg); })
15176e73c2aSMatt Spinler     {
15276e73c2aSMatt Spinler         readPGood();
15376e73c2aSMatt Spinler     }
15476e73c2aSMatt Spinler 
155432efeccSMatt Spinler     /**
156432efeccSMatt Spinler      * @brief Constructor
157432efeccSMatt Spinler      *
158432efeccSMatt Spinler      * @param[in] bus - The D-Bus bus connection object
159432efeccSMatt Spinler      * @param[in] callback - The function that should be run when
160432efeccSMatt Spinler      *                       the power state changes
161432efeccSMatt Spinler      */
PGoodState(sdbusplus::bus_t & bus,StateChangeFunc func)162cb356d48SPatrick Williams     PGoodState(sdbusplus::bus_t& bus, StateChangeFunc func) :
163432efeccSMatt Spinler         PowerState(bus, func),
164432efeccSMatt Spinler         _match(_bus,
165432efeccSMatt Spinler                sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath,
166432efeccSMatt Spinler                                                                _pgoodInterface),
167432efeccSMatt Spinler                [this](auto& msg) { this->pgoodChanged(msg); })
168432efeccSMatt Spinler     {
16976e73c2aSMatt Spinler         readPGood();
170432efeccSMatt Spinler     }
171432efeccSMatt Spinler 
172432efeccSMatt Spinler     /**
173432efeccSMatt Spinler      * @brief PropertiesChanged callback for the PGOOD property.
174432efeccSMatt Spinler      *
175432efeccSMatt Spinler      * Will call the registered callback function if necessary.
176432efeccSMatt Spinler      *
177432efeccSMatt Spinler      * @param[in] msg - The payload of the propertiesChanged signal
178432efeccSMatt Spinler      */
pgoodChanged(sdbusplus::message_t & msg)179cb356d48SPatrick Williams     void pgoodChanged(sdbusplus::message_t& msg)
180432efeccSMatt Spinler     {
181432efeccSMatt Spinler         std::string interface;
182432efeccSMatt Spinler         std::map<std::string, std::variant<int32_t>> properties;
183432efeccSMatt Spinler 
184432efeccSMatt Spinler         msg.read(interface, properties);
185432efeccSMatt Spinler 
186432efeccSMatt Spinler         auto pgoodProp = properties.find(_pgoodProperty);
187432efeccSMatt Spinler         if (pgoodProp != properties.end())
188432efeccSMatt Spinler         {
189432efeccSMatt Spinler             auto pgood = std::get<int32_t>(pgoodProp->second);
190432efeccSMatt Spinler             setPowerState(pgood);
191432efeccSMatt Spinler         }
192432efeccSMatt Spinler     }
193432efeccSMatt Spinler 
194432efeccSMatt Spinler   private:
19576e73c2aSMatt Spinler     /**
19676e73c2aSMatt Spinler      * @brief Reads the PGOOD property from D-Bus and saves it.
19776e73c2aSMatt Spinler      */
readPGood()19876e73c2aSMatt Spinler     void readPGood()
19976e73c2aSMatt Spinler     {
20076e73c2aSMatt Spinler         try
20176e73c2aSMatt Spinler         {
20276e73c2aSMatt Spinler             auto pgood = util::SDBusPlus::getProperty<int32_t>(
20376e73c2aSMatt Spinler                 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty);
20476e73c2aSMatt Spinler 
20576e73c2aSMatt Spinler             _powerState = static_cast<bool>(pgood);
20676e73c2aSMatt Spinler         }
207bff172aaSMatthew Barth         catch (const util::DBusServiceError& e)
20876e73c2aSMatt Spinler         {
209bff172aaSMatthew Barth             // Wait for propertiesChanged signal when service starts
21076e73c2aSMatt Spinler         }
21176e73c2aSMatt Spinler     }
21276e73c2aSMatt Spinler 
213432efeccSMatt Spinler     /** @brief D-Bus path constant */
214432efeccSMatt Spinler     const std::string _pgoodPath{"/org/openbmc/control/power0"};
215432efeccSMatt Spinler 
216432efeccSMatt Spinler     /** @brief D-Bus interface constant */
217432efeccSMatt Spinler     const std::string _pgoodInterface{"org.openbmc.control.Power"};
218432efeccSMatt Spinler 
219432efeccSMatt Spinler     /** @brief D-Bus property constant */
220432efeccSMatt Spinler     const std::string _pgoodProperty{"pgood"};
221432efeccSMatt Spinler 
222432efeccSMatt Spinler     /** @brief The propertiesChanged match */
223cb356d48SPatrick Williams     sdbusplus::bus::match_t _match;
224432efeccSMatt Spinler };
225432efeccSMatt Spinler 
2262dd87bd1SKumar Thangavel /**
2272dd87bd1SKumar Thangavel  * @class HostPowerState
2282dd87bd1SKumar Thangavel  *
2292dd87bd1SKumar Thangavel  * This class implements the PowerState API by looking at the 'powerState'
2302dd87bd1SKumar Thangavel  * property on the phosphor virtual sensor interface.
2312dd87bd1SKumar Thangavel  */
2322dd87bd1SKumar Thangavel class HostPowerState : public PowerState
2332dd87bd1SKumar Thangavel {
2342dd87bd1SKumar Thangavel   public:
2352dd87bd1SKumar Thangavel     virtual ~HostPowerState() = default;
2362dd87bd1SKumar Thangavel     HostPowerState(const HostPowerState&) = delete;
2372dd87bd1SKumar Thangavel     HostPowerState& operator=(const HostPowerState&) = delete;
2382dd87bd1SKumar Thangavel     HostPowerState(HostPowerState&&) = delete;
2392dd87bd1SKumar Thangavel     HostPowerState& operator=(HostPowerState&&) = delete;
2402dd87bd1SKumar Thangavel 
HostPowerState()2412dd87bd1SKumar Thangavel     HostPowerState() :
2422dd87bd1SKumar Thangavel         PowerState(),
2432dd87bd1SKumar Thangavel         _match(_bus,
2442dd87bd1SKumar Thangavel                sdbusplus::bus::match::rules::propertiesChangedNamespace(
2452dd87bd1SKumar Thangavel                    _hostStatePath, _hostStateInterface),
2462dd87bd1SKumar Thangavel                [this](auto& msg) { this->hostStateChanged(msg); })
2472dd87bd1SKumar Thangavel     {
2482dd87bd1SKumar Thangavel         readHostState();
2492dd87bd1SKumar Thangavel     }
2502dd87bd1SKumar Thangavel 
2512dd87bd1SKumar Thangavel     /**
2522dd87bd1SKumar Thangavel      * @brief Constructor
2532dd87bd1SKumar Thangavel      *
2542dd87bd1SKumar Thangavel      * @param[in] bus - The D-Bus bus connection object
2552dd87bd1SKumar Thangavel      * @param[in] callback - The function that should be run when
2562dd87bd1SKumar Thangavel      *                       the power state changes
2572dd87bd1SKumar Thangavel      */
HostPowerState(sdbusplus::bus_t & bus,StateChangeFunc func)258cb356d48SPatrick Williams     HostPowerState(sdbusplus::bus_t& bus, StateChangeFunc func) :
2592dd87bd1SKumar Thangavel         PowerState(bus, func),
2602dd87bd1SKumar Thangavel         _match(_bus,
2612dd87bd1SKumar Thangavel                sdbusplus::bus::match::rules::propertiesChangedNamespace(
2622dd87bd1SKumar Thangavel                    _hostStatePath, _hostStateInterface),
2632dd87bd1SKumar Thangavel                [this](auto& msg) { this->hostStateChanged(msg); })
2642dd87bd1SKumar Thangavel     {
2652dd87bd1SKumar Thangavel         readHostState();
2662dd87bd1SKumar Thangavel     }
2672dd87bd1SKumar Thangavel 
2682dd87bd1SKumar Thangavel     /**
2692dd87bd1SKumar Thangavel      * @brief PropertiesChanged callback for the CurrentHostState property.
2702dd87bd1SKumar Thangavel      *
2712dd87bd1SKumar Thangavel      * Will call the registered callback function if necessary.
2722dd87bd1SKumar Thangavel      *
2732dd87bd1SKumar Thangavel      * @param[in] msg - The payload of the propertiesChanged signal
2742dd87bd1SKumar Thangavel      */
hostStateChanged(sdbusplus::message_t & msg)275cb356d48SPatrick Williams     void hostStateChanged(sdbusplus::message_t& msg)
2762dd87bd1SKumar Thangavel     {
2772dd87bd1SKumar Thangavel         std::string interface;
2782dd87bd1SKumar Thangavel         std::map<std::string, std::variant<std::string>> properties;
2792dd87bd1SKumar Thangavel         std::vector<HostState> hostPowerStates;
2802dd87bd1SKumar Thangavel 
2812dd87bd1SKumar Thangavel         msg.read(interface, properties);
2822dd87bd1SKumar Thangavel 
2832dd87bd1SKumar Thangavel         auto hostStateProp = properties.find(_hostStateProperty);
2842dd87bd1SKumar Thangavel         if (hostStateProp != properties.end())
2852dd87bd1SKumar Thangavel         {
2862dd87bd1SKumar Thangavel             auto currentHostState =
2872dd87bd1SKumar Thangavel                 sdbusplus::message::convert_from_string<HostState>(
2882dd87bd1SKumar Thangavel                     std::get<std::string>(hostStateProp->second));
2892dd87bd1SKumar Thangavel 
2902dd87bd1SKumar Thangavel             if (!currentHostState)
2912dd87bd1SKumar Thangavel             {
2922dd87bd1SKumar Thangavel                 throw sdbusplus::exception::InvalidEnumString();
2932dd87bd1SKumar Thangavel             }
2942dd87bd1SKumar Thangavel             HostState hostState = *currentHostState;
2952dd87bd1SKumar Thangavel 
2962dd87bd1SKumar Thangavel             hostPowerStates.emplace_back(hostState);
2970cff4ea6SChau Ly             setHostPowerState(hostPowerStates, true);
2982dd87bd1SKumar Thangavel         }
2992dd87bd1SKumar Thangavel     }
3002dd87bd1SKumar Thangavel 
3012dd87bd1SKumar Thangavel   private:
setHostPowerState(std::vector<HostState> & hostPowerStates,bool callFromStateChange)3020cff4ea6SChau Ly     void setHostPowerState(std::vector<HostState>& hostPowerStates,
3030cff4ea6SChau Ly                            bool callFromStateChange)
3042dd87bd1SKumar Thangavel     {
3052dd87bd1SKumar Thangavel         bool powerStateflag = false;
3062dd87bd1SKumar Thangavel         for (const auto& powerState : hostPowerStates)
3072dd87bd1SKumar Thangavel         {
3082dd87bd1SKumar Thangavel             if (powerState == HostState::Standby ||
3092dd87bd1SKumar Thangavel                 powerState == HostState::Running ||
3102dd87bd1SKumar Thangavel                 powerState == HostState::TransitioningToRunning ||
3112dd87bd1SKumar Thangavel                 powerState == HostState::Quiesced ||
3122dd87bd1SKumar Thangavel                 powerState == HostState::DiagnosticMode)
3132dd87bd1SKumar Thangavel             {
3142dd87bd1SKumar Thangavel                 powerStateflag = true;
3152dd87bd1SKumar Thangavel                 break;
3162dd87bd1SKumar Thangavel             }
3172dd87bd1SKumar Thangavel         }
3180cff4ea6SChau Ly         if (callFromStateChange)
3190cff4ea6SChau Ly         {
3202dd87bd1SKumar Thangavel             setPowerState(powerStateflag);
3212dd87bd1SKumar Thangavel         }
3220cff4ea6SChau Ly         else
3230cff4ea6SChau Ly         {
3240cff4ea6SChau Ly             // This won't call callbacks (if exists) during the constructor of
3250cff4ea6SChau Ly             // the class when the first read for this flag is true which is
3260cff4ea6SChau Ly             // different from the init value of _powerState.
3270cff4ea6SChau Ly             // Daemon that wants to do anything when the initial power state
3280cff4ea6SChau Ly             // is true can check isPowerOn() and do it.
3290cff4ea6SChau Ly             _powerState = powerStateflag;
3300cff4ea6SChau Ly         }
3310cff4ea6SChau Ly     }
3322dd87bd1SKumar Thangavel 
3332dd87bd1SKumar Thangavel     /**
3342dd87bd1SKumar Thangavel      * @brief Reads the CurrentHostState property from D-Bus and saves it.
3352dd87bd1SKumar Thangavel      */
readHostState()3362dd87bd1SKumar Thangavel     void readHostState()
3372dd87bd1SKumar Thangavel     {
3382dd87bd1SKumar Thangavel         std::string hostStatePath;
3392dd87bd1SKumar Thangavel         std::string hostStateService;
3402dd87bd1SKumar Thangavel         std::string hostService = "xyz.openbmc_project.State.Host";
3412dd87bd1SKumar Thangavel         std::vector<HostState> hostPowerStates;
3422dd87bd1SKumar Thangavel 
3432dd87bd1SKumar Thangavel         int32_t depth = 0;
3442dd87bd1SKumar Thangavel         const std::string path = "/";
3452dd87bd1SKumar Thangavel 
3462dd87bd1SKumar Thangavel         auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
3472dd87bd1SKumar Thangavel             _bus, path, _hostStateInterface, depth);
3482dd87bd1SKumar Thangavel 
3492dd87bd1SKumar Thangavel         for (const auto& path : mapperResponse)
3502dd87bd1SKumar Thangavel         {
3512dd87bd1SKumar Thangavel             for (const auto& service : path.second)
3522dd87bd1SKumar Thangavel             {
3532dd87bd1SKumar Thangavel                 hostStateService = service.first;
3542dd87bd1SKumar Thangavel 
3552dd87bd1SKumar Thangavel                 if (hostStateService.find(hostService) != std::string::npos)
3562dd87bd1SKumar Thangavel                 {
3572dd87bd1SKumar Thangavel                     hostStatePath = path.first;
3582dd87bd1SKumar Thangavel 
3592dd87bd1SKumar Thangavel                     auto currentHostState =
3602dd87bd1SKumar Thangavel                         util::SDBusPlus::getProperty<HostState>(
3612dd87bd1SKumar Thangavel                             hostStateService, hostStatePath,
3622dd87bd1SKumar Thangavel                             _hostStateInterface, _hostStateProperty);
3632dd87bd1SKumar Thangavel 
3642dd87bd1SKumar Thangavel                     hostPowerStates.emplace_back(currentHostState);
3652dd87bd1SKumar Thangavel                 }
3662dd87bd1SKumar Thangavel             }
3672dd87bd1SKumar Thangavel         }
3680cff4ea6SChau Ly         setHostPowerState(hostPowerStates, false);
3692dd87bd1SKumar Thangavel     }
3702dd87bd1SKumar Thangavel 
3712dd87bd1SKumar Thangavel     const std::string _hostStatePath{"/xyz/openbmc_project/state"};
3722dd87bd1SKumar Thangavel 
3732dd87bd1SKumar Thangavel     /** @brief D-Bus interface constant */
3742dd87bd1SKumar Thangavel     const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
3752dd87bd1SKumar Thangavel 
3762dd87bd1SKumar Thangavel     /** @brief D-Bus property constant */
3772dd87bd1SKumar Thangavel     const std::string _hostStateProperty{"CurrentHostState"};
3782dd87bd1SKumar Thangavel 
3792dd87bd1SKumar Thangavel     /** @brief The propertiesChanged match */
380cb356d48SPatrick Williams     sdbusplus::bus::match_t _match;
3812dd87bd1SKumar Thangavel };
3822dd87bd1SKumar Thangavel 
383432efeccSMatt Spinler } // namespace phosphor::fan
384