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