xref: /openbmc/phosphor-fan-presence/monitor/tach_sensor.cpp (revision 5593560b1e1a7785a491d4650c4f3f61ffdaba90)
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