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