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