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