xref: /openbmc/phosphor-fan-presence/monitor/tach_sensor.cpp (revision 33618bc1845e5e7c4e12a2d2ed7f1c47a6a03de1)
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             throw InvalidSensorError();
112         }
113 
114         if (_hasTarget)
115         {
116             readProperty(_interface,
117                          FAN_TARGET_PROPERTY,
118                          _name,
119                          _bus,
120                          _tachTarget);
121         }
122 
123         auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
124 
125         tachSignal = std::make_unique<sdbusplus::server::match::match>(
126                 _bus,
127                 match.c_str(),
128                 [this](auto& msg){ this->handleTachChange(msg); });
129 
130         if (_hasTarget)
131         {
132             match = getMatchString(_interface);
133 
134             targetSignal = std::make_unique<sdbusplus::server::match::match>(
135                     _bus,
136                     match.c_str(),
137                     [this](auto& msg){ this->handleTargetChange(msg); });
138         }
139     }
140 }
141 
142 std::string TachSensor::getMatchString(const std::string& interface)
143 {
144     return sdbusplus::bus::match::rules::propertiesChanged(
145             _name, interface);
146 }
147 
148 uint64_t TachSensor::getTarget() const
149 {
150     if (!_hasTarget)
151     {
152         return _fan.findTargetSpeed();
153     }
154     return _tachTarget;
155 }
156 
157 void TachSensor::setFunctional(bool functional)
158 {
159     _functional = functional;
160     updateInventory(_functional);
161 }
162 
163 /**
164  * @brief Reads a property from the input message and stores it in value.
165  *        T is the value type.
166  *
167  *        Note: This can only be called once per message.
168  *
169  * @param[in] msg - the dbus message that contains the data
170  * @param[in] interface - the interface the property is on
171  * @param[in] propertName - the name of the property
172  * @param[out] value - the value to store the property value in
173  */
174 template<typename T>
175 static void readPropertyFromMessage(sdbusplus::message::message& msg,
176                                     const std::string& interface,
177                                     const std::string& propertyName,
178                                     T& value)
179 {
180     std::string sensor;
181     std::map<std::string, sdbusplus::message::variant<T>> data;
182     msg.read(sensor, data);
183 
184     if (sensor.compare(interface) == 0)
185     {
186         auto propertyMap = data.find(propertyName);
187         if (propertyMap != data.end())
188         {
189             value = sdbusplus::message::variant_ns::get<T>(
190                         propertyMap->second);
191         }
192     }
193 }
194 
195 
196 void TachSensor::handleTargetChange(sdbusplus::message::message& msg)
197 {
198     readPropertyFromMessage(msg,
199                             _interface,
200                             FAN_TARGET_PROPERTY,
201                             _tachTarget);
202 
203     //Check all tach sensors on the fan against the target
204     _fan.tachChanged();
205 }
206 
207 
208 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
209 {
210    readPropertyFromMessage(msg,
211                            FAN_SENSOR_VALUE_INTF,
212                            FAN_VALUE_PROPERTY,
213                            _tachInput);
214 
215    //Check just this sensor against the target
216    _fan.tachChanged(*this);
217 }
218 
219 void TachSensor::startTimer(TimerMode mode)
220 {
221     if (!timerRunning())
222     {
223         _timer.start(
224                 getDelay(mode),
225                 util::Timer::TimerType::oneshot);
226         _timerMode = mode;
227     }
228     else
229     {
230         if (mode != _timerMode)
231         {
232             _timer.stop();
233             _timer.start(
234                     getDelay(mode),
235                     util::Timer::TimerType::oneshot);
236             _timerMode = mode;
237         }
238     }
239 }
240 
241 std::chrono::microseconds TachSensor::getDelay(TimerMode mode)
242 {
243     using namespace std::chrono;
244 
245     switch(mode)
246     {
247         case TimerMode::nonfunc :
248                 return duration_cast<microseconds>(seconds(_timeout));
249         case TimerMode::func :
250                 return duration_cast<microseconds>(seconds(_funcDelay));
251         default :
252                 // Log an internal error for undefined timer mode
253                 log<level::ERR>("Undefined timer mode",
254                         entry("TIMER_MODE=%u", mode));
255                 elog<InternalFailure>();
256                 return duration_cast<microseconds>(seconds(0));
257     }
258 }
259 
260 void TachSensor::updateInventory(bool functional)
261 {
262     auto objectMap = util::getObjMap<bool>(
263             _invName,
264             util::OPERATIONAL_STATUS_INTF,
265             util::FUNCTIONAL_PROPERTY,
266             functional);
267     auto response = util::SDBusPlus::lookupAndCallMethod(
268             _bus,
269             util::INVENTORY_PATH,
270             util::INVENTORY_INTF,
271             "Notify",
272             objectMap);
273     if (response.is_method_error())
274     {
275         log<level::ERR>("Error in notify update of tach sensor inventory");
276     }
277 }
278 
279 }
280 }
281 }
282