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 throw InvalidSensorError(); 112 } 113 114 if (_hasTarget) 115 { 116 readProperty(_interface, 117 FAN_TARGET_PROPERTY, 118 _name, 119 _bus, 120 _tachTarget); 121 } 122 123 auto match = getMatchString(FAN_SENSOR_VALUE_INTF); 124 125 tachSignal = std::make_unique<sdbusplus::server::match::match>( 126 _bus, 127 match.c_str(), 128 [this](auto& msg){ this->handleTachChange(msg); }); 129 130 if (_hasTarget) 131 { 132 match = getMatchString(_interface); 133 134 targetSignal = std::make_unique<sdbusplus::server::match::match>( 135 _bus, 136 match.c_str(), 137 [this](auto& msg){ this->handleTargetChange(msg); }); 138 } 139 } 140 } 141 142 std::string TachSensor::getMatchString(const std::string& interface) 143 { 144 return sdbusplus::bus::match::rules::propertiesChanged( 145 _name, interface); 146 } 147 148 uint64_t TachSensor::getTarget() const 149 { 150 if (!_hasTarget) 151 { 152 return _fan.findTargetSpeed(); 153 } 154 return _tachTarget; 155 } 156 157 void TachSensor::setFunctional(bool functional) 158 { 159 _functional = functional; 160 updateInventory(_functional); 161 } 162 163 /** 164 * @brief Reads a property from the input message and stores it in value. 165 * T is the value type. 166 * 167 * Note: This can only be called once per message. 168 * 169 * @param[in] msg - the dbus message that contains the data 170 * @param[in] interface - the interface the property is on 171 * @param[in] propertName - the name of the property 172 * @param[out] value - the value to store the property value in 173 */ 174 template<typename T> 175 static void readPropertyFromMessage(sdbusplus::message::message& msg, 176 const std::string& interface, 177 const std::string& propertyName, 178 T& value) 179 { 180 std::string sensor; 181 std::map<std::string, sdbusplus::message::variant<T>> data; 182 msg.read(sensor, data); 183 184 if (sensor.compare(interface) == 0) 185 { 186 auto propertyMap = data.find(propertyName); 187 if (propertyMap != data.end()) 188 { 189 value = sdbusplus::message::variant_ns::get<T>( 190 propertyMap->second); 191 } 192 } 193 } 194 195 196 void TachSensor::handleTargetChange(sdbusplus::message::message& msg) 197 { 198 readPropertyFromMessage(msg, 199 _interface, 200 FAN_TARGET_PROPERTY, 201 _tachTarget); 202 203 //Check all tach sensors on the fan against the target 204 _fan.tachChanged(); 205 } 206 207 208 void TachSensor::handleTachChange(sdbusplus::message::message& msg) 209 { 210 readPropertyFromMessage(msg, 211 FAN_SENSOR_VALUE_INTF, 212 FAN_VALUE_PROPERTY, 213 _tachInput); 214 215 //Check just this sensor against the target 216 _fan.tachChanged(*this); 217 } 218 219 void TachSensor::startTimer(TimerMode mode) 220 { 221 if (!timerRunning()) 222 { 223 _timer.start( 224 getDelay(mode), 225 util::Timer::TimerType::oneshot); 226 _timerMode = mode; 227 } 228 else 229 { 230 if (mode != _timerMode) 231 { 232 _timer.stop(); 233 _timer.start( 234 getDelay(mode), 235 util::Timer::TimerType::oneshot); 236 _timerMode = mode; 237 } 238 } 239 } 240 241 std::chrono::microseconds TachSensor::getDelay(TimerMode mode) 242 { 243 using namespace std::chrono; 244 245 switch(mode) 246 { 247 case TimerMode::nonfunc : 248 return duration_cast<microseconds>(seconds(_timeout)); 249 case TimerMode::func : 250 return duration_cast<microseconds>(seconds(_funcDelay)); 251 default : 252 // Log an internal error for undefined timer mode 253 log<level::ERR>("Undefined timer mode", 254 entry("TIMER_MODE=%u", mode)); 255 elog<InternalFailure>(); 256 return duration_cast<microseconds>(seconds(0)); 257 } 258 } 259 260 void TachSensor::updateInventory(bool functional) 261 { 262 auto objectMap = util::getObjMap<bool>( 263 _invName, 264 util::OPERATIONAL_STATUS_INTF, 265 util::FUNCTIONAL_PROPERTY, 266 functional); 267 auto response = util::SDBusPlus::lookupAndCallMethod( 268 _bus, 269 util::INVENTORY_PATH, 270 util::INVENTORY_INTF, 271 "Notify", 272 objectMap); 273 if (response.is_method_error()) 274 { 275 log<level::ERR>("Error in notify update of tach sensor inventory"); 276 } 277 } 278 279 } 280 } 281 } 282