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 if (!jsonObj.contains("target_path")) 67 { 68 // If target_path is not set in configuration, 69 // it is default to /xyz/openbmc_project/sensors/fan_tach/ 70 path = FAN_SENSOR_PATH + sensor.get<std::string>(); 71 } 72 else 73 { 74 path = jsonObj["target_path"].get<std::string>() + 75 sensor.get<std::string>(); 76 } 77 78 auto service = util::SDBusPlus::getService(_bus, path, _interface); 79 _sensors[path] = service; 80 } 81 // All sensors associated with this fan are set to the same target, 82 // so only need to read target property from one of them 83 if (!path.empty()) 84 { 85 _target = util::SDBusPlus::getProperty<uint64_t>( 86 _bus, _sensors.at(path), path, _interface, FAN_TARGET_PROPERTY); 87 } 88 } 89 90 void Fan::setZone(const json& jsonObj) 91 { 92 if (!jsonObj.contains("zone")) 93 { 94 log<level::ERR>("Missing required fan zone", 95 entry("JSON=%s", jsonObj.dump().c_str())); 96 throw std::runtime_error("Missing required fan zone"); 97 } 98 _zone = jsonObj["zone"].get<std::string>(); 99 } 100 101 void Fan::setTarget(uint64_t target) 102 { 103 if ((_target == target) || !_lockedTargets.empty()) 104 { 105 return; 106 } 107 108 for (const auto& sensor : _sensors) 109 { 110 auto value = target; 111 try 112 { 113 util::SDBusPlus::setProperty<uint64_t>( 114 _bus, sensor.second, sensor.first, _interface, 115 FAN_TARGET_PROPERTY, std::move(value)); 116 } 117 catch (const sdbusplus::exception_t&) 118 { 119 throw util::DBusPropertyError{ 120 fmt::format("Failed to set target for fan {}", _name).c_str(), 121 sensor.second, sensor.first, _interface, FAN_TARGET_PROPERTY}; 122 } 123 } 124 _target = target; 125 } 126 127 void Fan::lockTarget(uint64_t target) 128 { 129 // if multiple locks, take highest, else allow only the 130 // first lock to lower the target 131 if (target >= _target || _lockedTargets.empty()) 132 { 133 // setTarget wont work if any locked targets exist 134 decltype(_lockedTargets) temp; 135 _lockedTargets.swap(temp); 136 137 setTarget(target); 138 _lockedTargets.swap(temp); 139 } 140 141 _lockedTargets.push_back(target); 142 } 143 144 void Fan::unlockTarget(uint64_t target) 145 { 146 // find and remove the requested lock 147 auto itr(std::find_if(_lockedTargets.begin(), _lockedTargets.end(), 148 [target](auto lockedTarget) { 149 return target == lockedTarget; 150 })); 151 152 if (_lockedTargets.end() != itr) 153 { 154 _lockedTargets.erase(itr); 155 156 // if additional locks, re-lock at next-highest target 157 if (!_lockedTargets.empty()) 158 { 159 itr = std::max_element(_lockedTargets.begin(), 160 _lockedTargets.end()); 161 162 // setTarget wont work if any locked targets exist 163 decltype(_lockedTargets) temp; 164 _lockedTargets.swap(temp); 165 setTarget(*itr); 166 _lockedTargets.swap(temp); 167 } 168 } 169 } 170 171 } // namespace phosphor::fan::control::json 172