xref: /openbmc/phosphor-fan-presence/monitor/tach_sensor.cpp (revision 9396bcc3d3a60fdad5875433210038c1b9d20ac5)
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 "fan.hpp"
19 #include "sdbusplus.hpp"
20 #include "tach_sensor.hpp"
21 #include "utility.hpp"
22 
23 namespace phosphor
24 {
25 namespace fan
26 {
27 namespace monitor
28 {
29 
30 constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
31 constexpr auto FAN_TARGET_PROPERTY = "Target";
32 constexpr auto FAN_VALUE_PROPERTY = "Value";
33 
34 using namespace std::experimental::filesystem;
35 
36 /**
37  * @brief Helper function to read a property
38  *
39  * @param[in] interface - the interface the property is on
40  * @param[in] propertName - the name of the property
41  * @param[in] path - the dbus path
42  * @param[in] bus - the dbus object
43  * @param[out] value - filled in with the property value
44  */
45 template<typename T>
46 static void readProperty(const std::string& interface,
47                          const std::string& propertyName,
48                          const std::string& path,
49                          sdbusplus::bus::bus& bus,
50                          T& value)
51 {
52     try
53     {
54         value = util::SDBusPlus::getProperty<T>(bus,
55                                                 path,
56                                                 interface,
57                                                 propertyName);
58     }
59     catch (std::exception& e)
60     {
61         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
62     }
63 }
64 
65 
66 TachSensor::TachSensor(Mode mode,
67                        sdbusplus::bus::bus& bus,
68                        Fan& fan,
69                        const std::string& id,
70                        bool hasTarget,
71                        size_t funcDelay,
72                        const std::string& interface,
73                        size_t factor,
74                        size_t offset,
75                        size_t timeout,
76                        phosphor::fan::event::EventPtr& events) :
77     _bus(bus),
78     _fan(fan),
79     _name(FAN_SENSOR_PATH + id),
80     _invName(path(fan.getName()) / id),
81     _hasTarget(hasTarget),
82     _funcDelay(funcDelay),
83     _interface(interface),
84     _factor(factor),
85     _offset(offset),
86     _timeout(timeout),
87     _timer(events, [this, &fan](){ fan.timerExpired(*this); })
88 {
89     // Start from a known state of functional
90     setFunctional(true);
91 
92     // Load in current Target and Input values when entering monitor mode
93     if (mode != Mode::init)
94     {
95         try
96         {
97             // Use getProperty directly to allow a missing sensor object
98             // to abort construction.
99             _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>(
100                     _bus,
101                     _name,
102                     FAN_SENSOR_VALUE_INTF,
103                     FAN_VALUE_PROPERTY);
104         }
105         catch (std::exception& e)
106         {
107             throw InvalidSensorError();
108         }
109 
110         if (_hasTarget)
111         {
112             readProperty(_interface,
113                          FAN_TARGET_PROPERTY,
114                          _name,
115                          _bus,
116                          _tachTarget);
117         }
118 
119         auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
120 
121         tachSignal = std::make_unique<sdbusplus::server::match::match>(
122                 _bus,
123                 match.c_str(),
124                 [this](auto& msg){ this->handleTachChange(msg); });
125 
126         if (_hasTarget)
127         {
128             match = getMatchString(_interface);
129 
130             targetSignal = std::make_unique<sdbusplus::server::match::match>(
131                     _bus,
132                     match.c_str(),
133                     [this](auto& msg){ this->handleTargetChange(msg); });
134         }
135     }
136 }
137 
138 std::string TachSensor::getMatchString(const std::string& interface)
139 {
140     return sdbusplus::bus::match::rules::propertiesChanged(
141             _name, interface);
142 }
143 
144 uint64_t TachSensor::getTarget() const
145 {
146     if (!_hasTarget)
147     {
148         return _fan.findTargetSpeed();
149     }
150     return _tachTarget;
151 }
152 
153 void TachSensor::setFunctional(bool functional)
154 {
155     _functional = functional;
156     updateInventory(_functional);
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 {
194     readPropertyFromMessage(msg,
195                             _interface,
196                             FAN_TARGET_PROPERTY,
197                             _tachTarget);
198 
199     //Check all tach sensors on the fan against the target
200     _fan.tachChanged();
201 }
202 
203 
204 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
205 {
206    readPropertyFromMessage(msg,
207                            FAN_SENSOR_VALUE_INTF,
208                            FAN_VALUE_PROPERTY,
209                            _tachInput);
210 
211    //Check just this sensor against the target
212    _fan.tachChanged(*this);
213 }
214 
215 
216 std::chrono::microseconds TachSensor::getTimeout()
217 {
218     using namespace std::chrono;
219 
220     return duration_cast<microseconds>(seconds(_timeout));
221 }
222 
223 void TachSensor::updateInventory(bool functional)
224 {
225     auto objectMap = util::getObjMap<bool>(
226             _invName,
227             util::OPERATIONAL_STATUS_INTF,
228             util::FUNCTIONAL_PROPERTY,
229             functional);
230     auto response = util::SDBusPlus::lookupAndCallMethod(
231             _bus,
232             util::INVENTORY_PATH,
233             util::INVENTORY_INTF,
234             "Notify",
235             objectMap);
236     if (response.is_method_error())
237     {
238         log<level::ERR>("Error in notify update of tach sensor inventory");
239     }
240 }
241 
242 }
243 }
244 }
245