blob: cef375ca05f7cfe60b2682bb7132226430c0a4fe [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 Barthdf3e8d62017-05-31 11:07:24 -050032using namespace phosphor::logging;
Dinesh Chinari618027a2017-06-26 23:26:50 -050033using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
34 Error::InternalFailure;
Matt Spinler7f88fe62017-04-10 14:39:02 -050035
Matthew Barth14184132017-05-19 14:37:30 -050036Zone::Zone(Mode mode,
37 sdbusplus::bus::bus& bus,
Matthew Barth8600d9a2017-06-23 14:38:05 -050038 phosphor::fan::event::EventPtr& events,
Matt Spinler7f88fe62017-04-10 14:39:02 -050039 const ZoneDefinition& def) :
40 _bus(bus),
41 _fullSpeed(std::get<fullSpeedPos>(def)),
Matthew Barth1de66622017-06-12 13:13:02 -050042 _zoneNum(std::get<zoneNumPos>(def)),
Matthew Barthe0ca13e2017-06-13 16:29:09 -050043 _defFloorSpeed(std::get<floorSpeedPos>(def)),
Matthew Barth8600d9a2017-06-23 14:38:05 -050044 _defCeilingSpeed(std::get<fullSpeedPos>(def)),
Matthew Bartha9561842017-06-29 11:43:45 -050045 _incDelay(std::get<incDelayPos>(def)),
46 _decInterval(std::get<decIntervalPos>(def)),
Matthew Barth1ee48f22017-06-27 15:14:48 -050047 _incTimer(events, [this](){ this->incTimerExpired(); }),
Matthew Barth8600d9a2017-06-23 14:38:05 -050048 _decTimer(events, [this](){ this->decTimerExpired(); })
Matt Spinler7f88fe62017-04-10 14:39:02 -050049{
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 }
Matthew Barth38a93a82017-05-11 14:12:27 -050056
Matthew Barth14184132017-05-19 14:37:30 -050057 // Do not enable set speed events when in init mode
58 if (mode != Mode::init)
Matthew Barth17d1fe22017-05-11 15:00:36 -050059 {
Matthew Barthccc77702017-07-28 13:43:04 -050060 // Setup signal trigger for set speed events
61 for (auto& event : std::get<setSpeedEventsPos>(def))
62 {
63 initEvent(event);
64 }
Matthew Barth8600d9a2017-06-23 14:38:05 -050065 // Start timer for fan speed decreases
Matthew Bartha9561842017-06-29 11:43:45 -050066 if (!_decTimer.running() && _decInterval != seconds::zero())
Matthew Barth8600d9a2017-06-23 14:38:05 -050067 {
Matthew Bartha9561842017-06-29 11:43:45 -050068 _decTimer.start(_decInterval,
Matthew Barth8600d9a2017-06-23 14:38:05 -050069 phosphor::fan::util::Timer::TimerType::repeating);
70 }
Matthew Barth38a93a82017-05-11 14:12:27 -050071 }
Matt Spinler7f88fe62017-04-10 14:39:02 -050072}
73
74
75void Zone::setSpeed(uint64_t speed)
76{
77 for (auto& fan : _fans)
78 {
79 fan->setSpeed(speed);
80 }
81}
82
Matthew Barth861d77c2017-05-22 14:18:25 -050083void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
84{
85 _active[group] = isActiveAllow;
86 if (!isActiveAllow)
87 {
88 _isActive = false;
89 }
90 else
91 {
92 // Check all entries are set to allow control active
93 auto actPred = [](auto const& entry) {return entry.second;};
94 _isActive = std::all_of(_active.begin(),
95 _active.end(),
96 actPred);
97 }
98}
99
Matthew Barthb4a7cb92017-06-28 15:29:50 -0500100void Zone::setFloor(uint64_t speed)
101{
102 _floorSpeed = speed;
103 // Floor speed above target, update target to floor speed
104 if (_targetSpeed < _floorSpeed)
105 {
106 requestSpeedIncrease(_floorSpeed - _targetSpeed);
107 }
108}
109
Matthew Barth240397b2017-06-22 11:23:30 -0500110void Zone::requestSpeedIncrease(uint64_t targetDelta)
111{
112 // Only increase speed when delta is higher than
113 // the current increase delta for the zone and currently under ceiling
114 if (targetDelta > _incSpeedDelta &&
115 _targetSpeed < _ceilingSpeed)
116 {
117 _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
118 _incSpeedDelta = targetDelta;
Matthew Barth240397b2017-06-22 11:23:30 -0500119 // Target speed can not go above a defined ceiling speed
120 if (_targetSpeed > _ceilingSpeed)
121 {
122 _targetSpeed = _ceilingSpeed;
123 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500124 // Cancel current timer countdown
125 if (_incTimer.running())
126 {
127 _incTimer.stop();
128 }
Matthew Barth240397b2017-06-22 11:23:30 -0500129 setSpeed(_targetSpeed);
Matthew Barth1ee48f22017-06-27 15:14:48 -0500130 // Start timer countdown for fan speed increase
Matthew Bartha9561842017-06-29 11:43:45 -0500131 _incTimer.start(_incDelay,
Matthew Barth1ee48f22017-06-27 15:14:48 -0500132 phosphor::fan::util::Timer::TimerType::oneshot);
Matthew Barth240397b2017-06-22 11:23:30 -0500133 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500134}
135
136void Zone::incTimerExpired()
137{
138 // Clear increase delta when timer expires allowing additional speed
139 // increase requests or speed decreases to occur
Matthew Barth240397b2017-06-22 11:23:30 -0500140 _incSpeedDelta = 0;
141}
142
Matthew Barth0ce99d82017-06-22 15:07:29 -0500143void Zone::requestSpeedDecrease(uint64_t targetDelta)
144{
145 // Only decrease the lowest target delta requested
146 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
147 {
148 _decSpeedDelta = targetDelta;
149 }
Matthew Barth8600d9a2017-06-23 14:38:05 -0500150}
Matthew Barth0ce99d82017-06-22 15:07:29 -0500151
Matthew Barth8600d9a2017-06-23 14:38:05 -0500152void Zone::decTimerExpired()
153{
Matthew Barth1ee48f22017-06-27 15:14:48 -0500154 // Only decrease speeds when no requested increases exist and
155 // the increase timer is not running (i.e. not in the middle of increasing)
156 if (_incSpeedDelta == 0 && !_incTimer.running())
Matthew Barth0ce99d82017-06-22 15:07:29 -0500157 {
158 // Target speed can not go below the defined floor speed
159 if ((_targetSpeed < _decSpeedDelta) ||
160 (_targetSpeed - _decSpeedDelta < _floorSpeed))
161 {
162 _targetSpeed = _floorSpeed;
163 }
164 else
165 {
166 _targetSpeed = _targetSpeed - _decSpeedDelta;
167 }
168 setSpeed(_targetSpeed);
169 }
170 // Clear decrease delta when timer expires
171 _decSpeedDelta = 0;
Matthew Barth8600d9a2017-06-23 14:38:05 -0500172 // Decrease timer is restarted since its repeating
Matthew Barth0ce99d82017-06-22 15:07:29 -0500173}
174
Matthew Barthccc77702017-07-28 13:43:04 -0500175void Zone::initEvent(const SetSpeedEvent& event)
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500176{
Matthew Barthccc77702017-07-28 13:43:04 -0500177 // Get the current value for each property
Matthew Barth604329e2017-08-04 11:18:28 -0500178 for (auto& group : std::get<groupPos>(event))
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500179 {
Matthew Barth604329e2017-08-04 11:18:28 -0500180 try
181 {
182 refreshProperty(_bus,
183 group.first,
184 std::get<intfPos>(group.second),
185 std::get<propPos>(group.second));
186 }
187 catch (const InternalFailure& ife)
188 {
189 log<level::ERR>(
190 "Unable to find property: ",
191 entry("PATH=%s", group.first.c_str()),
192 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()),
193 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str()));
194 }
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500195 }
Matthew Barthccc77702017-07-28 13:43:04 -0500196 // Setup signal matches for property change events
197 for (auto& prop : std::get<propChangeListPos>(event))
198 {
Matthew Barthf6b76d82017-08-04 12:58:02 -0500199 std::unique_ptr<EventData> eventData =
200 std::make_unique<EventData>(
201 EventData
202 {
203 std::get<groupPos>(event),
204 std::get<handlerObjPos>(prop),
205 std::get<actionPos>(event)
206 }
207 );
208 std::unique_ptr<sdbusplus::server::match::match> match =
209 std::make_unique<sdbusplus::server::match::match>(
Matthew Barthccc77702017-07-28 13:43:04 -0500210 _bus,
211 std::get<signaturePos>(prop).c_str(),
212 std::bind(std::mem_fn(&Zone::handleEvent),
213 this,
214 std::placeholders::_1,
Matthew Barthf6b76d82017-08-04 12:58:02 -0500215 eventData.get())
216 );
217 _signalEvents.emplace_back(std::move(eventData), std::move(match));
Matthew Barthccc77702017-07-28 13:43:04 -0500218 }
219 // Run action function for initial event state
220 std::get<actionPos>(event)(*this,
221 std::get<groupPos>(event));
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500222}
223
Matthew Barthf6b76d82017-08-04 12:58:02 -0500224void Zone::removeEvent(const SetSpeedEvent& event)
225{
226 // Find the signal event to be removed
227 auto it = std::find_if(
228 _signalEvents.begin(),
229 _signalEvents.end(),
230 [&event](auto const& se)
231 {
232 auto seEventData = *std::get<signalEventDataPos>(se);
233 // TODO Use the action function target for comparison
234 return
235 (
236 std::get<eventGroupPos>(seEventData) ==
237 std::get<groupPos>(event) &&
238 std::get<eventActionPos>(seEventData).target_type().name() ==
239 std::get<actionPos>(event).target_type().name()
240 );
241 });
242 if (it != std::end(_signalEvents))
243 {
244 std::get<signalEventDataPos>(*it).reset();
245 std::get<signalMatchPos>(*it).reset();
246 _signalEvents.erase(it);
247 }
248}
249
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500250void Zone::refreshProperty(sdbusplus::bus::bus& bus,
251 const std::string& path,
252 const std::string& iface,
253 const std::string& prop)
254{
255 PropertyVariantType property;
256 getProperty(_bus, path, iface, prop, property);
257 setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
258}
259
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500260void Zone::getProperty(sdbusplus::bus::bus& bus,
261 const std::string& path,
262 const std::string& iface,
263 const std::string& prop,
Matthew Barth9e741ed2017-06-02 16:29:09 -0500264 PropertyVariantType& value)
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500265{
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500266 auto serv = phosphor::fan::util::getService(path, iface, bus);
267 auto hostCall = bus.new_method_call(serv.c_str(),
268 path.c_str(),
269 "org.freedesktop.DBus.Properties",
270 "Get");
271 hostCall.append(iface);
272 hostCall.append(prop);
273 auto hostResponseMsg = bus.call(hostCall);
274 if (hostResponseMsg.is_method_error())
275 {
Dinesh Chinari618027a2017-06-26 23:26:50 -0500276 log<level::ERR>("Error in host call response for retrieving property");
277 elog<InternalFailure>();
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500278 }
Matthew Barth9e741ed2017-06-02 16:29:09 -0500279 hostResponseMsg.read(value);
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500280}
281
Matthew Barth38a93a82017-05-11 14:12:27 -0500282void Zone::handleEvent(sdbusplus::message::message& msg,
Matthew Barth34f1bda2017-05-31 13:45:36 -0500283 const EventData* eventData)
Matthew Barth38a93a82017-05-11 14:12:27 -0500284{
285 // Handle the callback
Matthew Barth34f1bda2017-05-31 13:45:36 -0500286 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
Matthew Barth17d1fe22017-05-11 15:00:36 -0500287 // Perform the action
Matthew Barth34f1bda2017-05-31 13:45:36 -0500288 std::get<eventActionPos>(*eventData)(*this,
289 std::get<eventGroupPos>(*eventData));
Matthew Barth38a93a82017-05-11 14:12:27 -0500290}
291
Matt Spinler7f88fe62017-04-10 14:39:02 -0500292}
293}
294}