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 "tach_sensor.hpp" 17 18 #include "fan.hpp" 19 #include "sdbusplus.hpp" 20 #include "utility.hpp" 21 22 #include <fmt/format.h> 23 24 #include <phosphor-logging/elog.hpp> 25 #include <phosphor-logging/log.hpp> 26 27 #include <experimental/filesystem> 28 #include <functional> 29 30 namespace phosphor 31 { 32 namespace fan 33 { 34 namespace monitor 35 { 36 37 constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value"; 38 constexpr auto FAN_TARGET_PROPERTY = "Target"; 39 constexpr auto FAN_VALUE_PROPERTY = "Value"; 40 41 using namespace std::experimental::filesystem; 42 using InternalFailure = 43 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 44 45 /** 46 * @brief Helper function to read a property 47 * 48 * @param[in] interface - the interface the property is on 49 * @param[in] propertName - the name of the property 50 * @param[in] path - the dbus path 51 * @param[in] bus - the dbus object 52 * @param[out] value - filled in with the property value 53 */ 54 template <typename T> 55 static void 56 readProperty(const std::string& interface, const std::string& propertyName, 57 const std::string& path, sdbusplus::bus::bus& bus, T& value) 58 { 59 try 60 { 61 value = 62 util::SDBusPlus::getProperty<T>(bus, path, interface, propertyName); 63 } 64 catch (std::exception& e) 65 { 66 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 67 } 68 } 69 70 TachSensor::TachSensor(Mode mode, sdbusplus::bus::bus& bus, Fan& fan, 71 const std::string& id, bool hasTarget, size_t funcDelay, 72 const std::string& interface, double factor, 73 int64_t offset, size_t timeout, 74 const sdeventplus::Event& event) : 75 _bus(bus), 76 _fan(fan), _name(FAN_SENSOR_PATH + id), _invName(path(fan.getName()) / id), 77 _hasTarget(hasTarget), _funcDelay(funcDelay), _interface(interface), 78 _factor(factor), _offset(offset), _timeout(timeout), 79 _timerMode(TimerMode::func), 80 _timer(event, std::bind(&Fan::timerExpired, &fan, std::ref(*this))) 81 { 82 // Start from a known state of functional 83 setFunctional(true); 84 85 // Load in current Target and Input values when entering monitor mode 86 if (mode != Mode::init) 87 { 88 try 89 { 90 // Use getProperty directly to allow a missing sensor object 91 // to abort construction. 92 _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>( 93 _bus, _name, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); 94 } 95 catch (std::exception& e) 96 { 97 log<level::ERR>( 98 fmt::format("Failed to retrieve tach sensor {}", _name) 99 .c_str()); 100 // mark tach sensor as nonfunctional 101 setFunctional(false); 102 throw InvalidSensorError(); 103 } 104 105 if (_hasTarget) 106 { 107 readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, 108 _tachTarget); 109 } 110 111 auto match = getMatchString(FAN_SENSOR_VALUE_INTF); 112 113 tachSignal = std::make_unique<sdbusplus::server::match::match>( 114 _bus, match.c_str(), 115 [this](auto& msg) { this->handleTachChange(msg); }); 116 117 if (_hasTarget) 118 { 119 match = getMatchString(_interface); 120 121 targetSignal = std::make_unique<sdbusplus::server::match::match>( 122 _bus, match.c_str(), 123 [this](auto& msg) { this->handleTargetChange(msg); }); 124 } 125 } 126 } 127 128 std::string TachSensor::getMatchString(const std::string& interface) 129 { 130 return sdbusplus::bus::match::rules::propertiesChanged(_name, interface); 131 } 132 133 uint64_t TachSensor::getTarget() const 134 { 135 if (!_hasTarget) 136 { 137 return _fan.findTargetSpeed(); 138 } 139 return _tachTarget; 140 } 141 142 void TachSensor::setFunctional(bool functional) 143 { 144 _functional = functional; 145 updateInventory(_functional); 146 } 147 148 /** 149 * @brief Reads a property from the input message and stores it in value. 150 * T is the value type. 151 * 152 * Note: This can only be called once per message. 153 * 154 * @param[in] msg - the dbus message that contains the data 155 * @param[in] interface - the interface the property is on 156 * @param[in] propertName - the name of the property 157 * @param[out] value - the value to store the property value in 158 */ 159 template <typename T> 160 static void readPropertyFromMessage(sdbusplus::message::message& msg, 161 const std::string& interface, 162 const std::string& propertyName, T& value) 163 { 164 std::string sensor; 165 std::map<std::string, std::variant<T>> data; 166 msg.read(sensor, data); 167 168 if (sensor.compare(interface) == 0) 169 { 170 auto propertyMap = data.find(propertyName); 171 if (propertyMap != data.end()) 172 { 173 value = std::get<T>(propertyMap->second); 174 } 175 } 176 } 177 178 void TachSensor::handleTargetChange(sdbusplus::message::message& msg) 179 { 180 readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); 181 182 // Check all tach sensors on the fan against the target 183 _fan.tachChanged(); 184 } 185 186 void TachSensor::handleTachChange(sdbusplus::message::message& msg) 187 { 188 readPropertyFromMessage(msg, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY, 189 _tachInput); 190 191 // Check just this sensor against the target 192 _fan.tachChanged(*this); 193 } 194 195 void TachSensor::startTimer(TimerMode mode) 196 { 197 if (!timerRunning() || mode != _timerMode) 198 { 199 _timer.restartOnce(getDelay(mode)); 200 _timerMode = mode; 201 } 202 } 203 204 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 205 { 206 using namespace std::chrono; 207 208 switch (mode) 209 { 210 case TimerMode::nonfunc: 211 return duration_cast<microseconds>(seconds(_timeout)); 212 case TimerMode::func: 213 return duration_cast<microseconds>(seconds(_funcDelay)); 214 default: 215 // Log an internal error for undefined timer mode 216 log<level::ERR>("Undefined timer mode", 217 entry("TIMER_MODE=%u", mode)); 218 elog<InternalFailure>(); 219 return duration_cast<microseconds>(seconds(0)); 220 } 221 } 222 223 void TachSensor::updateInventory(bool functional) 224 { 225 auto objectMap = 226 util::getObjMap<bool>(_invName, util::OPERATIONAL_STATUS_INTF, 227 util::FUNCTIONAL_PROPERTY, functional); 228 auto response = util::SDBusPlus::lookupAndCallMethod( 229 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); 230 if (response.is_method_error()) 231 { 232 log<level::ERR>("Error in notify update of tach sensor inventory"); 233 } 234 } 235 236 } // namespace monitor 237 } // namespace fan 238 } // namespace phosphor 239