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 "sdbusplus.hpp"
19 #include "tach_sensor.hpp"
20 #include "../utility.hpp"
21 
22 namespace phosphor
23 {
24 namespace fan
25 {
26 namespace monitor
27 {
28 
29 constexpr auto PROPERTY_INTF = "org.freedesktop.DBus.Properties";
30 constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/";
31 constexpr auto FAN_SENSOR_CONTROL_INTF = "xyz.openbmc_project.Control.FanSpeed";
32 constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
33 constexpr auto FAN_TARGET_PROPERTY = "Target";
34 constexpr auto FAN_VALUE_PROPERTY = "Value";
35 
36 
37 /**
38  * @brief Helper function to read a property
39  *
40  * @param[in] interface - the interface the property is on
41  * @param[in] propertName - the name of the property
42  * @param[in] path - the dbus path
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                          sdbusplus::bus::bus& bus,
51                          T& value)
52 {
53     try
54     {
55         value = util::SDBusPlus::getProperty<T>(bus,
56                                                 path,
57                                                 interface,
58                                                 propertyName);
59     }
60     catch (std::exception& e)
61     {
62         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
63     }
64 }
65 
66 
67 TachSensor::TachSensor(sdbusplus::bus::bus& bus,
68                        Fan& fan,
69                        const std::string& id,
70                        bool hasTarget,
71                        size_t timeout,
72                        phosphor::fan::event::EventPtr& events) :
73     _bus(bus),
74     _fan(fan),
75     _name(FAN_SENSOR_PATH + id),
76     _hasTarget(hasTarget),
77     _timeout(timeout),
78     _timer(events, [this, &fan](){ fan.timerExpired(*this); })
79 {
80     //Load in starting Target and Input values
81     readProperty(FAN_SENSOR_VALUE_INTF,
82                  FAN_VALUE_PROPERTY,
83                  _name,
84                  _bus,
85                  _tachInput);
86 
87     if (_hasTarget)
88     {
89         readProperty(FAN_SENSOR_CONTROL_INTF,
90                      FAN_TARGET_PROPERTY,
91                      _name,
92                      _bus,
93                      _tachTarget);
94     }
95 
96     auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
97 
98     tachSignal = std::make_unique<sdbusplus::server::match::match>(
99                      _bus,
100                      match.c_str(),
101                      handleTachChangeSignal,
102                      this);
103 
104     if (_hasTarget)
105     {
106         match = getMatchString(FAN_SENSOR_CONTROL_INTF);
107 
108         targetSignal = std::make_unique<sdbusplus::server::match::match>(
109                            _bus,
110                            match.c_str(),
111                            handleTargetChangeSignal,
112                            this);
113     }
114 
115 }
116 
117 
118 //Can cache this value after openbmc/openbmc#1496 is resolved
119 std::string TachSensor::getService()
120 {
121     // Use the Value interface since not all sensors implement
122     // the control interface.
123     return phosphor::fan::util::getService(_name,
124                                            FAN_SENSOR_VALUE_INTF,
125                                            _bus);
126 }
127 
128 
129 std::string TachSensor::getMatchString(const std::string& interface)
130 {
131     return std::string("type='signal',"
132                        "interface='org.freedesktop.DBus.Properties',"
133                        "member='PropertiesChanged',"
134                        "arg0namespace='" + interface + "',"
135                        "path='" + _name + "'");
136 }
137 
138 
139 int TachSensor::handleTachChangeSignal(sd_bus_message* msg,
140                                        void* usrData,
141                                        sd_bus_error* err)
142 {
143     auto m = sdbusplus::message::message(msg);
144     static_cast<TachSensor*>(usrData)->handleTachChange(m, err);
145     return 0;
146 }
147 
148 
149 int TachSensor::handleTargetChangeSignal(sd_bus_message* msg,
150                                          void* usrData,
151                                          sd_bus_error* err)
152 {
153     auto m = sdbusplus::message::message(msg);
154     static_cast<TachSensor*>(usrData)->handleTargetChange(m, err);
155     return 0;
156 }
157 
158 
159 /**
160  * @brief Reads a property from the input message and stores it in value.
161  *        T is the value type.
162  *
163  *        Note: This can only be called once per message.
164  *
165  * @param[in] msg - the dbus message that contains the data
166  * @param[in] interface - the interface the property is on
167  * @param[in] propertName - the name of the property
168  * @param[out] value - the value to store the property value in
169  */
170 template<typename T>
171 static void readPropertyFromMessage(sdbusplus::message::message& msg,
172                                     const std::string& interface,
173                                     const std::string& propertyName,
174                                     T& value)
175 {
176     std::string sensor;
177     std::map<std::string, sdbusplus::message::variant<T>> data;
178     msg.read(sensor, data);
179 
180     if (sensor.compare(interface) == 0)
181     {
182         auto propertyMap = data.find(propertyName);
183         if (propertyMap != data.end())
184         {
185             value = sdbusplus::message::variant_ns::get<T>(
186                         propertyMap->second);
187         }
188     }
189 }
190 
191 
192 void TachSensor::handleTargetChange(sdbusplus::message::message& msg,
193                                     sd_bus_error* err)
194 {
195     readPropertyFromMessage(msg,
196                             FAN_SENSOR_CONTROL_INTF,
197                             FAN_TARGET_PROPERTY,
198                             _tachTarget);
199 
200     //Check all tach sensors on the fan against the target
201     _fan.tachChanged();
202 }
203 
204 
205 void TachSensor::handleTachChange(sdbusplus::message::message& msg,
206                                   sd_bus_error* err)
207 {
208    readPropertyFromMessage(msg,
209                            FAN_SENSOR_VALUE_INTF,
210                            FAN_VALUE_PROPERTY,
211                            _tachInput);
212 
213    //Check just this sensor against the target
214    _fan.tachChanged(*this);
215 }
216 
217 
218 std::chrono::microseconds TachSensor::getTimeout()
219 {
220     using namespace std::chrono;
221 
222     return duration_cast<microseconds>(seconds(_timeout));
223 }
224 
225 
226 }
227 }
228 }
229