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 <experimental/filesystem>
17 #include <phosphor-logging/log.hpp>
18 #include <phosphor-logging/elog.hpp>
19 #include "fan.hpp"
20 #include "sdbusplus.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 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 using namespace std::experimental::filesystem;
36 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
37                             Error::InternalFailure;
38 
39 /**
40  * @brief Helper function to read a property
41  *
42  * @param[in] interface - the interface the property is on
43  * @param[in] propertName - the name of the property
44  * @param[in] path - the dbus path
45  * @param[in] bus - the dbus object
46  * @param[out] value - filled in with the property value
47  */
48 template<typename T>
49 static void readProperty(const std::string& interface,
50                          const std::string& propertyName,
51                          const std::string& path,
52                          sdbusplus::bus::bus& bus,
53                          T& value)
54 {
55     try
56     {
57         value = util::SDBusPlus::getProperty<T>(bus,
58                                                 path,
59                                                 interface,
60                                                 propertyName);
61     }
62     catch (std::exception& e)
63     {
64         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
65     }
66 }
67 
68 
69 TachSensor::TachSensor(Mode mode,
70                        sdbusplus::bus::bus& bus,
71                        Fan& fan,
72                        const std::string& id,
73                        bool hasTarget,
74                        size_t funcDelay,
75                        const std::string& interface,
76                        size_t factor,
77                        size_t offset,
78                        size_t timeout,
79                        phosphor::fan::event::EventPtr& events) :
80     _bus(bus),
81     _fan(fan),
82     _name(FAN_SENSOR_PATH + id),
83     _invName(path(fan.getName()) / id),
84     _hasTarget(hasTarget),
85     _funcDelay(funcDelay),
86     _interface(interface),
87     _factor(factor),
88     _offset(offset),
89     _timeout(timeout),
90     _timerMode(TimerMode::func),
91     _timer(events, [this, &fan](){ fan.timerExpired(*this); })
92 {
93     // Start from a known state of functional
94     setFunctional(true);
95 
96     // Load in current Target and Input values when entering monitor mode
97     if (mode != Mode::init)
98     {
99         try
100         {
101             // Use getProperty directly to allow a missing sensor object
102             // to abort construction.
103             _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>(
104                     _bus,
105                     _name,
106                     FAN_SENSOR_VALUE_INTF,
107                     FAN_VALUE_PROPERTY);
108         }
109         catch (std::exception& e)
110         {
111             log<level::INFO>("Not monitoring a tach sensor",
112                     entry("SENSOR=%s", _name.c_str()));
113             throw InvalidSensorError();
114         }
115 
116         if (_hasTarget)
117         {
118             readProperty(_interface,
119                          FAN_TARGET_PROPERTY,
120                          _name,
121                          _bus,
122                          _tachTarget);
123         }
124 
125         auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
126 
127         tachSignal = std::make_unique<sdbusplus::server::match::match>(
128                 _bus,
129                 match.c_str(),
130                 [this](auto& msg){ this->handleTachChange(msg); });
131 
132         if (_hasTarget)
133         {
134             match = getMatchString(_interface);
135 
136             targetSignal = std::make_unique<sdbusplus::server::match::match>(
137                     _bus,
138                     match.c_str(),
139                     [this](auto& msg){ this->handleTargetChange(msg); });
140         }
141     }
142 }
143 
144 std::string TachSensor::getMatchString(const std::string& interface)
145 {
146     return sdbusplus::bus::match::rules::propertiesChanged(
147             _name, interface);
148 }
149 
150 uint64_t TachSensor::getTarget() const
151 {
152     if (!_hasTarget)
153     {
154         return _fan.findTargetSpeed();
155     }
156     return _tachTarget;
157 }
158 
159 void TachSensor::setFunctional(bool functional)
160 {
161     _functional = functional;
162     updateInventory(_functional);
163 }
164 
165 /**
166  * @brief Reads a property from the input message and stores it in value.
167  *        T is the value type.
168  *
169  *        Note: This can only be called once per message.
170  *
171  * @param[in] msg - the dbus message that contains the data
172  * @param[in] interface - the interface the property is on
173  * @param[in] propertName - the name of the property
174  * @param[out] value - the value to store the property value in
175  */
176 template<typename T>
177 static void readPropertyFromMessage(sdbusplus::message::message& msg,
178                                     const std::string& interface,
179                                     const std::string& propertyName,
180                                     T& value)
181 {
182     std::string sensor;
183     std::map<std::string, sdbusplus::message::variant<T>> data;
184     msg.read(sensor, data);
185 
186     if (sensor.compare(interface) == 0)
187     {
188         auto propertyMap = data.find(propertyName);
189         if (propertyMap != data.end())
190         {
191             value = sdbusplus::message::variant_ns::get<T>(
192                         propertyMap->second);
193         }
194     }
195 }
196 
197 
198 void TachSensor::handleTargetChange(sdbusplus::message::message& msg)
199 {
200     readPropertyFromMessage(msg,
201                             _interface,
202                             FAN_TARGET_PROPERTY,
203                             _tachTarget);
204 
205     //Check all tach sensors on the fan against the target
206     _fan.tachChanged();
207 }
208 
209 
210 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
211 {
212    readPropertyFromMessage(msg,
213                            FAN_SENSOR_VALUE_INTF,
214                            FAN_VALUE_PROPERTY,
215                            _tachInput);
216 
217    //Check just this sensor against the target
218    _fan.tachChanged(*this);
219 }
220 
221 void TachSensor::startTimer(TimerMode mode)
222 {
223     if (!timerRunning())
224     {
225         _timer.start(
226                 getDelay(mode),
227                 util::Timer::TimerType::oneshot);
228         _timerMode = mode;
229     }
230     else
231     {
232         if (mode != _timerMode)
233         {
234             _timer.stop();
235             _timer.start(
236                     getDelay(mode),
237                     util::Timer::TimerType::oneshot);
238             _timerMode = mode;
239         }
240     }
241 }
242 
243 std::chrono::microseconds TachSensor::getDelay(TimerMode mode)
244 {
245     using namespace std::chrono;
246 
247     switch(mode)
248     {
249         case TimerMode::nonfunc :
250                 return duration_cast<microseconds>(seconds(_timeout));
251         case TimerMode::func :
252                 return duration_cast<microseconds>(seconds(_funcDelay));
253         default :
254                 // Log an internal error for undefined timer mode
255                 log<level::ERR>("Undefined timer mode",
256                         entry("TIMER_MODE=%u", mode));
257                 elog<InternalFailure>();
258                 return duration_cast<microseconds>(seconds(0));
259     }
260 }
261 
262 void TachSensor::updateInventory(bool functional)
263 {
264     auto objectMap = util::getObjMap<bool>(
265             _invName,
266             util::OPERATIONAL_STATUS_INTF,
267             util::FUNCTIONAL_PROPERTY,
268             functional);
269     auto response = util::SDBusPlus::lookupAndCallMethod(
270             _bus,
271             util::INVENTORY_PATH,
272             util::INVENTORY_INTF,
273             "Notify",
274             objectMap);
275     if (response.is_method_error())
276     {
277         log<level::ERR>("Error in notify update of tach sensor inventory");
278     }
279 }
280 
281 }
282 }
283 }
284