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 <phosphor-logging/log.hpp> 17 #include <phosphor-logging/elog.hpp> 18 #include <phosphor-logging/elog-errors.hpp> 19 #include <xyz/openbmc_project/Common/error.hpp> 20 #include "fan.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 using namespace phosphor::logging; 32 using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: 33 Error::InternalFailure; 34 35 constexpr auto PROPERTY_INTF = "org.freedesktop.DBus.Properties"; 36 constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/"; 37 constexpr auto FAN_SENSOR_CONTROL_INTF = "xyz.openbmc_project.Control.FanSpeed"; 38 constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value"; 39 constexpr auto FAN_TARGET_PROPERTY = "Target"; 40 constexpr auto FAN_VALUE_PROPERTY = "Value"; 41 42 43 /** 44 * @brief Helper function to read a property 45 * 46 * @param[in] interface - the interface the property is on 47 * @param[in] propertName - the name of the property 48 * @param[in] path - the dbus path 49 * @param[in] service - the dbus service 50 * @param[in] bus - the dbus object 51 * @param[out] value - filled in with the property value 52 */ 53 template<typename T> 54 static void readProperty(const std::string& interface, 55 const std::string& propertyName, 56 const std::string& path, 57 const std::string& service, 58 sdbusplus::bus::bus& bus, 59 T& value) 60 { 61 sdbusplus::message::variant<T> property; 62 63 try 64 { 65 auto method = bus.new_method_call(service.c_str(), 66 path.c_str(), 67 PROPERTY_INTF, 68 "Get"); 69 70 method.append(interface, propertyName); 71 72 auto reply = bus.call(method); 73 if (reply.is_method_error()) 74 { 75 log<level::ERR>("Error in property get call", 76 entry("PATH=%s", path.c_str())); 77 elog<InternalFailure>(); 78 } 79 80 reply.read(property); 81 value = sdbusplus::message::variant_ns::get<T>(property); 82 } 83 catch (std::exception& e) 84 { 85 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 86 } 87 } 88 89 90 TachSensor::TachSensor(sdbusplus::bus::bus& bus, 91 Fan& fan, 92 const std::string& id, 93 bool hasTarget, 94 size_t timeout, 95 phosphor::fan::event::EventPtr& events) : 96 _bus(bus), 97 _fan(fan), 98 _name(FAN_SENSOR_PATH + id), 99 _hasTarget(hasTarget), 100 _timeout(timeout), 101 _timer(events, [this, &fan](){ fan.timerExpired(*this); }) 102 { 103 auto service = getService(); 104 105 //Load in starting Target and Input values 106 readProperty(FAN_SENSOR_VALUE_INTF, 107 FAN_VALUE_PROPERTY, 108 _name, 109 service, 110 _bus, 111 _tachInput); 112 113 if (_hasTarget) 114 { 115 readProperty(FAN_SENSOR_CONTROL_INTF, 116 FAN_TARGET_PROPERTY, 117 _name, 118 service, 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 handleTachChangeSignal, 129 this); 130 131 if (_hasTarget) 132 { 133 match = getMatchString(FAN_SENSOR_CONTROL_INTF); 134 135 targetSignal = std::make_unique<sdbusplus::server::match::match>( 136 _bus, 137 match.c_str(), 138 handleTargetChangeSignal, 139 this); 140 } 141 142 } 143 144 145 //Can cache this value after openbmc/openbmc#1496 is resolved 146 std::string TachSensor::getService() 147 { 148 return phosphor::fan::util::getService(_name, 149 FAN_SENSOR_CONTROL_INTF, 150 _bus); 151 } 152 153 154 std::string TachSensor::getMatchString(const std::string& interface) 155 { 156 return std::string("type='signal'," 157 "interface='org.freedesktop.DBus.Properties'," 158 "member='PropertiesChanged'," 159 "arg0namespace='" + interface + "'," 160 "path='" + _name + "'"); 161 } 162 163 164 int TachSensor::handleTachChangeSignal(sd_bus_message* msg, 165 void* usrData, 166 sd_bus_error* err) 167 { 168 auto m = sdbusplus::message::message(msg); 169 static_cast<TachSensor*>(usrData)->handleTachChange(m, err); 170 return 0; 171 } 172 173 174 int TachSensor::handleTargetChangeSignal(sd_bus_message* msg, 175 void* usrData, 176 sd_bus_error* err) 177 { 178 auto m = sdbusplus::message::message(msg); 179 static_cast<TachSensor*>(usrData)->handleTargetChange(m, err); 180 return 0; 181 } 182 183 184 /** 185 * @brief Reads a property from the input message and stores it in value. 186 * T is the value type. 187 * 188 * Note: This can only be called once per message. 189 * 190 * @param[in] msg - the dbus message that contains the data 191 * @param[in] interface - the interface the property is on 192 * @param[in] propertName - the name of the property 193 * @param[out] value - the value to store the property value in 194 */ 195 template<typename T> 196 static void readPropertyFromMessage(sdbusplus::message::message& msg, 197 const std::string& interface, 198 const std::string& propertyName, 199 T& value) 200 { 201 std::string sensor; 202 std::map<std::string, sdbusplus::message::variant<T>> data; 203 msg.read(sensor, data); 204 205 if (sensor.compare(interface) == 0) 206 { 207 auto propertyMap = data.find(propertyName); 208 if (propertyMap != data.end()) 209 { 210 value = sdbusplus::message::variant_ns::get<T>( 211 propertyMap->second); 212 } 213 } 214 } 215 216 217 void TachSensor::handleTargetChange(sdbusplus::message::message& msg, 218 sd_bus_error* err) 219 { 220 readPropertyFromMessage(msg, 221 FAN_SENSOR_CONTROL_INTF, 222 FAN_TARGET_PROPERTY, 223 _tachTarget); 224 225 //Check all tach sensors on the fan against the target 226 _fan.tachChanged(); 227 } 228 229 230 void TachSensor::handleTachChange(sdbusplus::message::message& msg, 231 sd_bus_error* err) 232 { 233 readPropertyFromMessage(msg, 234 FAN_SENSOR_VALUE_INTF, 235 FAN_VALUE_PROPERTY, 236 _tachInput); 237 238 //Check just this sensor against the target 239 _fan.tachChanged(*this); 240 } 241 242 243 std::chrono::microseconds TachSensor::getTimeout() 244 { 245 using namespace std::chrono; 246 247 return duration_cast<microseconds>(seconds(_timeout)); 248 } 249 250 251 } 252 } 253 } 254