xref: /openbmc/phosphor-fan-presence/control/zone.cpp (revision 5593560b1e1a7785a491d4650c4f3f61ffdaba90)
1 /**
2  * Copyright © 2017 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <chrono>
17 #include <phosphor-logging/log.hpp>
18 #include <phosphor-logging/elog.hpp>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <xyz/openbmc_project/Common/error.hpp>
21 #include "zone.hpp"
22 #include "utility.hpp"
23 
24 namespace phosphor
25 {
26 namespace fan
27 {
28 namespace control
29 {
30 
31 using namespace std::chrono;
32 using namespace phosphor::logging;
33 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
34                              Error::InternalFailure;
35 
36 Zone::Zone(Mode mode,
37            sdbusplus::bus::bus& bus,
38            phosphor::fan::event::EventPtr& events,
39            const ZoneDefinition& def) :
40     _bus(bus),
41     _fullSpeed(std::get<fullSpeedPos>(def)),
42     _zoneNum(std::get<zoneNumPos>(def)),
43     _defFloorSpeed(std::get<floorSpeedPos>(def)),
44     _defCeilingSpeed(std::get<fullSpeedPos>(def)),
45     _incDelay(std::get<incDelayPos>(def)),
46     _decInterval(std::get<decIntervalPos>(def)),
47     _incTimer(events, [this](){ this->incTimerExpired(); }),
48     _decTimer(events, [this](){ this->decTimerExpired(); })
49 {
50     auto& fanDefs = std::get<fanListPos>(def);
51 
52     for (auto& def : fanDefs)
53     {
54         _fans.emplace_back(std::make_unique<Fan>(bus, def));
55     }
56 
57     // Do not enable set speed events when in init mode
58     if (mode != Mode::init)
59     {
60         initEvents(def);
61         // Start timer for fan speed decreases
62         if (!_decTimer.running() && _decInterval != seconds::zero())
63         {
64             _decTimer.start(_decInterval,
65                             phosphor::fan::util::Timer::TimerType::repeating);
66         }
67     }
68 }
69 
70 
71 void Zone::setSpeed(uint64_t speed)
72 {
73     for (auto& fan : _fans)
74     {
75         fan->setSpeed(speed);
76     }
77 }
78 
79 void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
80 {
81     _active[group] = isActiveAllow;
82     if (!isActiveAllow)
83     {
84         _isActive = false;
85     }
86     else
87     {
88         // Check all entries are set to allow control active
89         auto actPred = [](auto const& entry) {return entry.second;};
90         _isActive = std::all_of(_active.begin(),
91                                 _active.end(),
92                                 actPred);
93     }
94 }
95 
96 void Zone::setFloor(uint64_t speed)
97 {
98     _floorSpeed = speed;
99     // Floor speed above target, update target to floor speed
100     if (_targetSpeed < _floorSpeed)
101     {
102         requestSpeedIncrease(_floorSpeed - _targetSpeed);
103     }
104 }
105 
106 void Zone::requestSpeedIncrease(uint64_t targetDelta)
107 {
108     // Only increase speed when delta is higher than
109     // the current increase delta for the zone and currently under ceiling
110     if (targetDelta > _incSpeedDelta &&
111         _targetSpeed < _ceilingSpeed)
112     {
113         _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
114         _incSpeedDelta = targetDelta;
115         // Target speed can not go above a defined ceiling speed
116         if (_targetSpeed > _ceilingSpeed)
117         {
118             _targetSpeed = _ceilingSpeed;
119         }
120         // Cancel current timer countdown
121         if (_incTimer.running())
122         {
123             _incTimer.stop();
124         }
125         setSpeed(_targetSpeed);
126         // Start timer countdown for fan speed increase
127         _incTimer.start(_incDelay,
128                         phosphor::fan::util::Timer::TimerType::oneshot);
129     }
130 }
131 
132 void Zone::incTimerExpired()
133 {
134     // Clear increase delta when timer expires allowing additional speed
135     // increase requests or speed decreases to occur
136     _incSpeedDelta = 0;
137 }
138 
139 void Zone::requestSpeedDecrease(uint64_t targetDelta)
140 {
141     // Only decrease the lowest target delta requested
142     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
143     {
144         _decSpeedDelta = targetDelta;
145     }
146 }
147 
148 void Zone::decTimerExpired()
149 {
150     // Only decrease speeds when no requested increases exist and
151     // the increase timer is not running (i.e. not in the middle of increasing)
152     if (_incSpeedDelta == 0 && !_incTimer.running())
153     {
154         // Target speed can not go below the defined floor speed
155         if ((_targetSpeed < _decSpeedDelta) ||
156             (_targetSpeed - _decSpeedDelta < _floorSpeed))
157         {
158             _targetSpeed = _floorSpeed;
159         }
160         else
161         {
162             _targetSpeed = _targetSpeed - _decSpeedDelta;
163         }
164         setSpeed(_targetSpeed);
165     }
166     // Clear decrease delta when timer expires
167     _decSpeedDelta = 0;
168     // Decrease timer is restarted since its repeating
169 }
170 
171 void Zone::initEvents(const ZoneDefinition& def)
172 {
173     // Setup signal trigger for set speed events
174     for (auto& event : std::get<setSpeedEventsPos>(def))
175     {
176         // Get the current value for each property
177         for (auto& entry : std::get<groupPos>(event))
178         {
179             refreshProperty(_bus,
180                             entry.first,
181                             std::get<intfPos>(entry.second),
182                             std::get<propPos>(entry.second));
183         }
184         // Setup signal matches for property change events
185         for (auto& prop : std::get<propChangeListPos>(event))
186         {
187             _signalEvents.emplace_back(
188                     std::make_unique<EventData>(
189                             EventData
190                             {
191                                 std::get<groupPos>(event),
192                                 std::get<handlerObjPos>(prop),
193                                 std::get<actionPos>(event)
194                             }));
195             _matches.emplace_back(
196                     _bus,
197                     std::get<signaturePos>(prop).c_str(),
198                     std::bind(std::mem_fn(&Zone::handleEvent),
199                               this,
200                               std::placeholders::_1,
201                               _signalEvents.back().get()));
202         }
203         // Run action function for initial event state
204         std::get<actionPos>(event)(*this,
205                                    std::get<groupPos>(event));
206     }
207 }
208 
209 void Zone::refreshProperty(sdbusplus::bus::bus& bus,
210                            const std::string& path,
211                            const std::string& iface,
212                            const std::string& prop)
213 {
214     PropertyVariantType property;
215     getProperty(_bus, path, iface, prop, property);
216     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
217 }
218 
219 void Zone::getProperty(sdbusplus::bus::bus& bus,
220                        const std::string& path,
221                        const std::string& iface,
222                        const std::string& prop,
223                        PropertyVariantType& value)
224 {
225     auto serv = phosphor::fan::util::getService(path, iface, bus);
226     auto hostCall = bus.new_method_call(serv.c_str(),
227                                         path.c_str(),
228                                         "org.freedesktop.DBus.Properties",
229                                         "Get");
230     hostCall.append(iface);
231     hostCall.append(prop);
232     auto hostResponseMsg = bus.call(hostCall);
233     if (hostResponseMsg.is_method_error())
234     {
235         log<level::ERR>("Error in host call response for retrieving property");
236         elog<InternalFailure>();
237     }
238     hostResponseMsg.read(value);
239 }
240 
241 void Zone::handleEvent(sdbusplus::message::message& msg,
242                        const EventData* eventData)
243 {
244     // Handle the callback
245     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
246     // Perform the action
247     std::get<eventActionPos>(*eventData)(*this,
248                                          std::get<eventGroupPos>(*eventData));
249 }
250 
251 }
252 }
253 }
254