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