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