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