blob: 96701275a44e339f0daafabce2324f0c662414c6 [file] [log] [blame]
Matt Spinler7f88fe62017-04-10 14:39:02 -05001/**
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 */
Matthew Barth8600d9a2017-06-23 14:38:05 -050016#include <chrono>
Dinesh Chinari618027a2017-06-26 23:26:50 -050017#include <phosphor-logging/log.hpp>
Matthew Barthdf3e8d62017-05-31 11:07:24 -050018#include <phosphor-logging/elog.hpp>
Dinesh Chinari618027a2017-06-26 23:26:50 -050019#include <phosphor-logging/elog-errors.hpp>
20#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler7f88fe62017-04-10 14:39:02 -050021#include "zone.hpp"
Matthew Barthdf3e8d62017-05-31 11:07:24 -050022#include "utility.hpp"
Matt Spinler7f88fe62017-04-10 14:39:02 -050023
24namespace phosphor
25{
26namespace fan
27{
28namespace control
29{
30
Matthew Barth8600d9a2017-06-23 14:38:05 -050031using namespace std::chrono;
Matthew Barth90149802017-08-15 10:51:37 -050032using namespace phosphor::fan;
Matthew Barthdf3e8d62017-05-31 11:07:24 -050033using namespace phosphor::logging;
Dinesh Chinari618027a2017-06-26 23:26:50 -050034using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
35 Error::InternalFailure;
Matt Spinler7f88fe62017-04-10 14:39:02 -050036
Matthew Barth14184132017-05-19 14:37:30 -050037Zone::Zone(Mode mode,
38 sdbusplus::bus::bus& bus,
Matthew Barth8600d9a2017-06-23 14:38:05 -050039 phosphor::fan::event::EventPtr& events,
Matt Spinler7f88fe62017-04-10 14:39:02 -050040 const ZoneDefinition& def) :
41 _bus(bus),
42 _fullSpeed(std::get<fullSpeedPos>(def)),
Matthew Barth1de66622017-06-12 13:13:02 -050043 _zoneNum(std::get<zoneNumPos>(def)),
Matthew Barthe0ca13e2017-06-13 16:29:09 -050044 _defFloorSpeed(std::get<floorSpeedPos>(def)),
Matthew Barth8600d9a2017-06-23 14:38:05 -050045 _defCeilingSpeed(std::get<fullSpeedPos>(def)),
Matthew Bartha9561842017-06-29 11:43:45 -050046 _incDelay(std::get<incDelayPos>(def)),
47 _decInterval(std::get<decIntervalPos>(def)),
Matthew Barth1ee48f22017-06-27 15:14:48 -050048 _incTimer(events, [this](){ this->incTimerExpired(); }),
Matthew Barth90149802017-08-15 10:51:37 -050049 _decTimer(events, [this](){ this->decTimerExpired(); }),
50 _sdEvents(events)
Matt Spinler7f88fe62017-04-10 14:39:02 -050051{
52 auto& fanDefs = std::get<fanListPos>(def);
53
54 for (auto& def : fanDefs)
55 {
56 _fans.emplace_back(std::make_unique<Fan>(bus, def));
57 }
Matthew Barth38a93a82017-05-11 14:12:27 -050058
Matthew Barth14184132017-05-19 14:37:30 -050059 // Do not enable set speed events when in init mode
60 if (mode != Mode::init)
Matthew Barth17d1fe22017-05-11 15:00:36 -050061 {
Matthew Barthccc77702017-07-28 13:43:04 -050062 // Setup signal trigger for set speed events
63 for (auto& event : std::get<setSpeedEventsPos>(def))
64 {
65 initEvent(event);
66 }
Matthew Barth8600d9a2017-06-23 14:38:05 -050067 // Start timer for fan speed decreases
Matthew Bartha9561842017-06-29 11:43:45 -050068 if (!_decTimer.running() && _decInterval != seconds::zero())
Matthew Barth8600d9a2017-06-23 14:38:05 -050069 {
Matthew Bartha9561842017-06-29 11:43:45 -050070 _decTimer.start(_decInterval,
Matthew Barth8600d9a2017-06-23 14:38:05 -050071 phosphor::fan::util::Timer::TimerType::repeating);
72 }
Matthew Barth38a93a82017-05-11 14:12:27 -050073 }
Matt Spinler7f88fe62017-04-10 14:39:02 -050074}
75
76
77void Zone::setSpeed(uint64_t speed)
78{
79 for (auto& fan : _fans)
80 {
81 fan->setSpeed(speed);
82 }
83}
84
Matthew Barth861d77c2017-05-22 14:18:25 -050085void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
86{
87 _active[group] = isActiveAllow;
88 if (!isActiveAllow)
89 {
90 _isActive = false;
91 }
92 else
93 {
94 // Check all entries are set to allow control active
95 auto actPred = [](auto const& entry) {return entry.second;};
96 _isActive = std::all_of(_active.begin(),
97 _active.end(),
98 actPred);
99 }
100}
101
Matthew Barthb4a7cb92017-06-28 15:29:50 -0500102void Zone::setFloor(uint64_t speed)
103{
104 _floorSpeed = speed;
105 // Floor speed above target, update target to floor speed
106 if (_targetSpeed < _floorSpeed)
107 {
108 requestSpeedIncrease(_floorSpeed - _targetSpeed);
109 }
110}
111
Matthew Barth240397b2017-06-22 11:23:30 -0500112void Zone::requestSpeedIncrease(uint64_t targetDelta)
113{
114 // Only increase speed when delta is higher than
115 // the current increase delta for the zone and currently under ceiling
116 if (targetDelta > _incSpeedDelta &&
117 _targetSpeed < _ceilingSpeed)
118 {
119 _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
120 _incSpeedDelta = targetDelta;
Matthew Barth240397b2017-06-22 11:23:30 -0500121 // Target speed can not go above a defined ceiling speed
122 if (_targetSpeed > _ceilingSpeed)
123 {
124 _targetSpeed = _ceilingSpeed;
125 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500126 // Cancel current timer countdown
127 if (_incTimer.running())
128 {
129 _incTimer.stop();
130 }
Matthew Barth240397b2017-06-22 11:23:30 -0500131 setSpeed(_targetSpeed);
Matthew Barth1ee48f22017-06-27 15:14:48 -0500132 // Start timer countdown for fan speed increase
Matthew Bartha9561842017-06-29 11:43:45 -0500133 _incTimer.start(_incDelay,
Matthew Barth1ee48f22017-06-27 15:14:48 -0500134 phosphor::fan::util::Timer::TimerType::oneshot);
Matthew Barth240397b2017-06-22 11:23:30 -0500135 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500136}
137
138void Zone::incTimerExpired()
139{
140 // Clear increase delta when timer expires allowing additional speed
141 // increase requests or speed decreases to occur
Matthew Barth240397b2017-06-22 11:23:30 -0500142 _incSpeedDelta = 0;
143}
144
Matthew Barth0ce99d82017-06-22 15:07:29 -0500145void Zone::requestSpeedDecrease(uint64_t targetDelta)
146{
147 // Only decrease the lowest target delta requested
148 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
149 {
150 _decSpeedDelta = targetDelta;
151 }
Matthew Barth8600d9a2017-06-23 14:38:05 -0500152}
Matthew Barth0ce99d82017-06-22 15:07:29 -0500153
Matthew Barth8600d9a2017-06-23 14:38:05 -0500154void Zone::decTimerExpired()
155{
Matthew Barth1ee48f22017-06-27 15:14:48 -0500156 // Only decrease speeds when no requested increases exist and
157 // the increase timer is not running (i.e. not in the middle of increasing)
158 if (_incSpeedDelta == 0 && !_incTimer.running())
Matthew Barth0ce99d82017-06-22 15:07:29 -0500159 {
160 // Target speed can not go below the defined floor speed
161 if ((_targetSpeed < _decSpeedDelta) ||
162 (_targetSpeed - _decSpeedDelta < _floorSpeed))
163 {
164 _targetSpeed = _floorSpeed;
165 }
166 else
167 {
168 _targetSpeed = _targetSpeed - _decSpeedDelta;
169 }
170 setSpeed(_targetSpeed);
171 }
172 // Clear decrease delta when timer expires
173 _decSpeedDelta = 0;
Matthew Barth8600d9a2017-06-23 14:38:05 -0500174 // Decrease timer is restarted since its repeating
Matthew Barth0ce99d82017-06-22 15:07:29 -0500175}
176
Matthew Barthccc77702017-07-28 13:43:04 -0500177void Zone::initEvent(const SetSpeedEvent& event)
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500178{
Matthew Barthccc77702017-07-28 13:43:04 -0500179 // Get the current value for each property
Matthew Barth604329e2017-08-04 11:18:28 -0500180 for (auto& group : std::get<groupPos>(event))
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500181 {
Matthew Barth604329e2017-08-04 11:18:28 -0500182 try
183 {
184 refreshProperty(_bus,
185 group.first,
186 std::get<intfPos>(group.second),
187 std::get<propPos>(group.second));
188 }
189 catch (const InternalFailure& ife)
190 {
191 log<level::ERR>(
192 "Unable to find property: ",
193 entry("PATH=%s", group.first.c_str()),
194 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()),
195 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str()));
196 }
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500197 }
Matthew Barthccc77702017-07-28 13:43:04 -0500198 // Setup signal matches for property change events
199 for (auto& prop : std::get<propChangeListPos>(event))
200 {
Matthew Barthf6b76d82017-08-04 12:58:02 -0500201 std::unique_ptr<EventData> eventData =
202 std::make_unique<EventData>(
203 EventData
204 {
205 std::get<groupPos>(event),
206 std::get<handlerObjPos>(prop),
207 std::get<actionPos>(event)
208 }
209 );
210 std::unique_ptr<sdbusplus::server::match::match> match =
211 std::make_unique<sdbusplus::server::match::match>(
Matthew Barthccc77702017-07-28 13:43:04 -0500212 _bus,
213 std::get<signaturePos>(prop).c_str(),
214 std::bind(std::mem_fn(&Zone::handleEvent),
215 this,
216 std::placeholders::_1,
Matthew Barthf6b76d82017-08-04 12:58:02 -0500217 eventData.get())
218 );
219 _signalEvents.emplace_back(std::move(eventData), std::move(match));
Matthew Barthccc77702017-07-28 13:43:04 -0500220 }
Matthew Barth90149802017-08-15 10:51:37 -0500221 // Attach a timer to run the action of an event
222 auto eventTimer = std::get<timerPos>(event);
223 if (std::get<intervalPos>(eventTimer) != seconds(0))
224 {
225 std::unique_ptr<util::Timer> timer =
226 std::make_unique<util::Timer>(
227 _sdEvents,
228 [this,
229 action = &(std::get<actionPos>(event)),
230 group = &(std::get<groupPos>(event))]()
231 {
232 this->timerExpired(*group, *action);
233 });
234 if (!timer->running())
235 {
236 timer->start(std::get<intervalPos>(eventTimer),
237 util::Timer::TimerType::repeating);
238 }
239 _timerEvents.emplace_back(std::move(timer));
240 }
Matthew Barthccc77702017-07-28 13:43:04 -0500241 // Run action function for initial event state
242 std::get<actionPos>(event)(*this,
243 std::get<groupPos>(event));
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500244}
245
Matthew Barthf6b76d82017-08-04 12:58:02 -0500246void Zone::removeEvent(const SetSpeedEvent& event)
247{
248 // Find the signal event to be removed
249 auto it = std::find_if(
250 _signalEvents.begin(),
251 _signalEvents.end(),
252 [&event](auto const& se)
253 {
254 auto seEventData = *std::get<signalEventDataPos>(se);
255 // TODO Use the action function target for comparison
256 return
257 (
258 std::get<eventGroupPos>(seEventData) ==
259 std::get<groupPos>(event) &&
260 std::get<eventActionPos>(seEventData).target_type().name() ==
261 std::get<actionPos>(event).target_type().name()
262 );
263 });
264 if (it != std::end(_signalEvents))
265 {
266 std::get<signalEventDataPos>(*it).reset();
267 std::get<signalMatchPos>(*it).reset();
268 _signalEvents.erase(it);
269 }
270}
271
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500272void Zone::refreshProperty(sdbusplus::bus::bus& bus,
273 const std::string& path,
274 const std::string& iface,
275 const std::string& prop)
276{
277 PropertyVariantType property;
278 getProperty(_bus, path, iface, prop, property);
279 setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
280}
281
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500282void Zone::getProperty(sdbusplus::bus::bus& bus,
283 const std::string& path,
284 const std::string& iface,
285 const std::string& prop,
Matthew Barth9e741ed2017-06-02 16:29:09 -0500286 PropertyVariantType& value)
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500287{
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500288 auto serv = phosphor::fan::util::getService(path, iface, bus);
289 auto hostCall = bus.new_method_call(serv.c_str(),
290 path.c_str(),
291 "org.freedesktop.DBus.Properties",
292 "Get");
293 hostCall.append(iface);
294 hostCall.append(prop);
295 auto hostResponseMsg = bus.call(hostCall);
296 if (hostResponseMsg.is_method_error())
297 {
Dinesh Chinari618027a2017-06-26 23:26:50 -0500298 log<level::ERR>("Error in host call response for retrieving property");
299 elog<InternalFailure>();
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500300 }
Matthew Barth9e741ed2017-06-02 16:29:09 -0500301 hostResponseMsg.read(value);
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500302}
303
Matthew Barth90149802017-08-15 10:51:37 -0500304void Zone::timerExpired(Group eventGroup, Action eventAction)
305{
306 // Perform the action
307 eventAction(*this, eventGroup);
308}
309
Matthew Barth38a93a82017-05-11 14:12:27 -0500310void Zone::handleEvent(sdbusplus::message::message& msg,
Matthew Barth34f1bda2017-05-31 13:45:36 -0500311 const EventData* eventData)
Matthew Barth38a93a82017-05-11 14:12:27 -0500312{
313 // Handle the callback
Matthew Barth34f1bda2017-05-31 13:45:36 -0500314 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
Matthew Barth17d1fe22017-05-11 15:00:36 -0500315 // Perform the action
Matthew Barth34f1bda2017-05-31 13:45:36 -0500316 std::get<eventActionPos>(*eventData)(*this,
317 std::get<eventGroupPos>(*eventData));
Matthew Barth38a93a82017-05-11 14:12:27 -0500318}
319
Matt Spinler7f88fe62017-04-10 14:39:02 -0500320}
321}
322}