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