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 phosphor::fan::event::EventPtr& events) : 89 _bus(bus), 90 _fan(fan), 91 _name(FAN_SENSOR_PATH + id), 92 _hasTarget(hasTarget), 93 _timeout(timeout), 94 _timer(events, [this, &fan](){ fan.timerExpired(*this); }) 95 { 96 auto service = getService(); 97 98 //Load in starting Target and Input values 99 readProperty(FAN_SENSOR_VALUE_INTF, 100 FAN_VALUE_PROPERTY, 101 _name, 102 service, 103 _bus, 104 _tachInput); 105 106 if (_hasTarget) 107 { 108 readProperty(FAN_SENSOR_CONTROL_INTF, 109 FAN_TARGET_PROPERTY, 110 _name, 111 service, 112 _bus, 113 _tachTarget); 114 } 115 116 auto match = getMatchString(FAN_SENSOR_VALUE_INTF); 117 118 tachSignal = std::make_unique<sdbusplus::server::match::match>( 119 _bus, 120 match.c_str(), 121 handleTachChangeSignal, 122 this); 123 124 if (_hasTarget) 125 { 126 match = getMatchString(FAN_SENSOR_CONTROL_INTF); 127 128 targetSignal = std::make_unique<sdbusplus::server::match::match>( 129 _bus, 130 match.c_str(), 131 handleTargetChangeSignal, 132 this); 133 } 134 135 } 136 137 138 //Can cache this value after openbmc/openbmc#1496 is resolved 139 std::string TachSensor::getService() 140 { 141 return phosphor::fan::util::getService(_name, 142 FAN_SENSOR_CONTROL_INTF, 143 _bus); 144 } 145 146 147 std::string TachSensor::getMatchString(const std::string& interface) 148 { 149 return std::string("type='signal'," 150 "interface='org.freedesktop.DBus.Properties'," 151 "member='PropertiesChanged'," 152 "arg0namespace='" + interface + "'," 153 "path='" + _name + "'"); 154 } 155 156 157 int TachSensor::handleTachChangeSignal(sd_bus_message* msg, 158 void* usrData, 159 sd_bus_error* err) 160 { 161 auto m = sdbusplus::message::message(msg); 162 static_cast<TachSensor*>(usrData)->handleTachChange(m, err); 163 return 0; 164 } 165 166 167 int TachSensor::handleTargetChangeSignal(sd_bus_message* msg, 168 void* usrData, 169 sd_bus_error* err) 170 { 171 auto m = sdbusplus::message::message(msg); 172 static_cast<TachSensor*>(usrData)->handleTargetChange(m, err); 173 return 0; 174 } 175 176 177 /** 178 * @brief Reads a property from the input message and stores it in value. 179 * T is the value type. 180 * 181 * Note: This can only be called once per message. 182 * 183 * @param[in] msg - the dbus message that contains the data 184 * @param[in] interface - the interface the property is on 185 * @param[in] propertName - the name of the property 186 * @param[out] value - the value to store the property value in 187 */ 188 template<typename T> 189 static void readPropertyFromMessage(sdbusplus::message::message& msg, 190 const std::string& interface, 191 const std::string& propertyName, 192 T& value) 193 { 194 std::string sensor; 195 std::map<std::string, sdbusplus::message::variant<T>> data; 196 msg.read(sensor, data); 197 198 if (sensor.compare(interface) == 0) 199 { 200 auto propertyMap = data.find(propertyName); 201 if (propertyMap != data.end()) 202 { 203 value = sdbusplus::message::variant_ns::get<T>( 204 propertyMap->second); 205 } 206 } 207 } 208 209 210 void TachSensor::handleTargetChange(sdbusplus::message::message& msg, 211 sd_bus_error* err) 212 { 213 readPropertyFromMessage(msg, 214 FAN_SENSOR_CONTROL_INTF, 215 FAN_TARGET_PROPERTY, 216 _tachTarget); 217 218 //Check all tach sensors on the fan against the target 219 _fan.tachChanged(); 220 } 221 222 223 void TachSensor::handleTachChange(sdbusplus::message::message& msg, 224 sd_bus_error* err) 225 { 226 readPropertyFromMessage(msg, 227 FAN_SENSOR_VALUE_INTF, 228 FAN_VALUE_PROPERTY, 229 _tachInput); 230 231 //Check just this sensor against the target 232 _fan.tachChanged(*this); 233 } 234 235 236 std::chrono::microseconds TachSensor::getTimeout() 237 { 238 using namespace std::chrono; 239 240 return duration_cast<microseconds>(seconds(_timeout)); 241 } 242 243 244 } 245 } 246 } 247