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