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