1 /** 2 * Copyright © 2022 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 "fan.hpp" 17 18 #include "sdbusplus.hpp" 19 20 #include <fmt/format.h> 21 22 #include <nlohmann/json.hpp> 23 #include <phosphor-logging/log.hpp> 24 #include <sdbusplus/bus.hpp> 25 26 namespace phosphor::fan::control::json 27 { 28 29 using json = nlohmann::json; 30 using namespace phosphor::logging; 31 32 constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/"; 33 constexpr auto FAN_TARGET_PROPERTY = "Target"; 34 35 Fan::Fan(const json& jsonObj) : 36 ConfigBase(jsonObj), _bus(util::SDBusPlus::getBus()) 37 { 38 setInterface(jsonObj); 39 setSensors(jsonObj); 40 setZone(jsonObj); 41 } 42 43 void Fan::setInterface(const json& jsonObj) 44 { 45 if (!jsonObj.contains("target_interface")) 46 { 47 log<level::ERR>("Missing required fan sensor target interface", 48 entry("JSON=%s", jsonObj.dump().c_str())); 49 throw std::runtime_error( 50 "Missing required fan sensor target interface"); 51 } 52 _interface = jsonObj["target_interface"].get<std::string>(); 53 } 54 55 void Fan::setSensors(const json& jsonObj) 56 { 57 if (!jsonObj.contains("sensors")) 58 { 59 log<level::ERR>("Missing required fan sensors list", 60 entry("JSON=%s", jsonObj.dump().c_str())); 61 throw std::runtime_error("Missing required fan sensors list"); 62 } 63 std::string path; 64 for (const auto& sensor : jsonObj["sensors"]) 65 { 66 path = FAN_SENSOR_PATH + sensor.get<std::string>(); 67 auto service = util::SDBusPlus::getService(_bus, path, _interface); 68 _sensors[path] = service; 69 } 70 // All sensors associated with this fan are set to the same target, 71 // so only need to read target property from one of them 72 if (!path.empty()) 73 { 74 _target = util::SDBusPlus::getProperty<uint64_t>( 75 _bus, _sensors.at(path), path, _interface, FAN_TARGET_PROPERTY); 76 } 77 } 78 79 void Fan::setZone(const json& jsonObj) 80 { 81 if (!jsonObj.contains("zone")) 82 { 83 log<level::ERR>("Missing required fan zone", 84 entry("JSON=%s", jsonObj.dump().c_str())); 85 throw std::runtime_error("Missing required fan zone"); 86 } 87 _zone = jsonObj["zone"].get<std::string>(); 88 } 89 90 void Fan::setTarget(uint64_t target) 91 { 92 if ((_target == target) || !_lockedTargets.empty()) 93 { 94 return; 95 } 96 97 for (const auto& sensor : _sensors) 98 { 99 auto value = target; 100 try 101 { 102 util::SDBusPlus::setProperty<uint64_t>( 103 _bus, sensor.second, sensor.first, _interface, 104 FAN_TARGET_PROPERTY, std::move(value)); 105 } 106 catch (const sdbusplus::exception_t&) 107 { 108 throw util::DBusPropertyError{ 109 fmt::format("Failed to set target for fan {}", _name).c_str(), 110 sensor.second, sensor.first, _interface, FAN_TARGET_PROPERTY}; 111 } 112 } 113 _target = target; 114 } 115 116 void Fan::lockTarget(uint64_t target) 117 { 118 // if multiple locks, take highest, else allow only the 119 // first lock to lower the target 120 if (target >= _target || _lockedTargets.empty()) 121 { 122 // setTarget wont work if any locked targets exist 123 decltype(_lockedTargets) temp; 124 _lockedTargets.swap(temp); 125 126 setTarget(target); 127 _lockedTargets.swap(temp); 128 } 129 130 _lockedTargets.push_back(target); 131 } 132 133 void Fan::unlockTarget(uint64_t target) 134 { 135 // find and remove the requested lock 136 auto itr(std::find_if( 137 _lockedTargets.begin(), _lockedTargets.end(), 138 [target](auto lockedTarget) { return target == lockedTarget; })); 139 140 if (_lockedTargets.end() != itr) 141 { 142 _lockedTargets.erase(itr); 143 144 // if additional locks, re-lock at next-highest target 145 if (!_lockedTargets.empty()) 146 { 147 itr = 148 std::max_element(_lockedTargets.begin(), _lockedTargets.end()); 149 150 // setTarget wont work if any locked targets exist 151 decltype(_lockedTargets) temp; 152 _lockedTargets.swap(temp); 153 setTarget(*itr); 154 _lockedTargets.swap(temp); 155 } 156 } 157 } 158 159 } // namespace phosphor::fan::control::json 160