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 // Use the Value interface since not all sensors implement 149 // the control interface. 150 return phosphor::fan::util::getService(_name, 151 FAN_SENSOR_VALUE_INTF, 152 _bus); 153 } 154 155 156 std::string TachSensor::getMatchString(const std::string& interface) 157 { 158 return std::string("type='signal'," 159 "interface='org.freedesktop.DBus.Properties'," 160 "member='PropertiesChanged'," 161 "arg0namespace='" + interface + "'," 162 "path='" + _name + "'"); 163 } 164 165 166 int TachSensor::handleTachChangeSignal(sd_bus_message* msg, 167 void* usrData, 168 sd_bus_error* err) 169 { 170 auto m = sdbusplus::message::message(msg); 171 static_cast<TachSensor*>(usrData)->handleTachChange(m, err); 172 return 0; 173 } 174 175 176 int TachSensor::handleTargetChangeSignal(sd_bus_message* msg, 177 void* usrData, 178 sd_bus_error* err) 179 { 180 auto m = sdbusplus::message::message(msg); 181 static_cast<TachSensor*>(usrData)->handleTargetChange(m, err); 182 return 0; 183 } 184 185 186 /** 187 * @brief Reads a property from the input message and stores it in value. 188 * T is the value type. 189 * 190 * Note: This can only be called once per message. 191 * 192 * @param[in] msg - the dbus message that contains the data 193 * @param[in] interface - the interface the property is on 194 * @param[in] propertName - the name of the property 195 * @param[out] value - the value to store the property value in 196 */ 197 template<typename T> 198 static void readPropertyFromMessage(sdbusplus::message::message& msg, 199 const std::string& interface, 200 const std::string& propertyName, 201 T& value) 202 { 203 std::string sensor; 204 std::map<std::string, sdbusplus::message::variant<T>> data; 205 msg.read(sensor, data); 206 207 if (sensor.compare(interface) == 0) 208 { 209 auto propertyMap = data.find(propertyName); 210 if (propertyMap != data.end()) 211 { 212 value = sdbusplus::message::variant_ns::get<T>( 213 propertyMap->second); 214 } 215 } 216 } 217 218 219 void TachSensor::handleTargetChange(sdbusplus::message::message& msg, 220 sd_bus_error* err) 221 { 222 readPropertyFromMessage(msg, 223 FAN_SENSOR_CONTROL_INTF, 224 FAN_TARGET_PROPERTY, 225 _tachTarget); 226 227 //Check all tach sensors on the fan against the target 228 _fan.tachChanged(); 229 } 230 231 232 void TachSensor::handleTachChange(sdbusplus::message::message& msg, 233 sd_bus_error* err) 234 { 235 readPropertyFromMessage(msg, 236 FAN_SENSOR_VALUE_INTF, 237 FAN_VALUE_PROPERTY, 238 _tachInput); 239 240 //Check just this sensor against the target 241 _fan.tachChanged(*this); 242 } 243 244 245 std::chrono::microseconds TachSensor::getTimeout() 246 { 247 using namespace std::chrono; 248 249 return duration_cast<microseconds>(seconds(_timeout)); 250 } 251 252 253 } 254 } 255 } 256