xref: /openbmc/phosphor-fan-presence/monitor/tach_sensor.cpp (revision 80f271b296488e12d4edd29b5bb908a407fbda1f)
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                        const std::string& interface,
72                        size_t factor,
73                        size_t offset,
74                        size_t timeout,
75                        phosphor::fan::event::EventPtr& events) :
76     _bus(bus),
77     _fan(fan),
78     _name(FAN_SENSOR_PATH + id),
79     _invName(path(fan.getName()) / id),
80     _hasTarget(hasTarget),
81     _interface(interface),
82     _factor(factor),
83     _offset(offset),
84     _timeout(timeout),
85     _timer(events, [this, &fan](){ fan.timerExpired(*this); })
86 {
87     // Start from a known state of functional
88     setFunctional(true);
89 
90     // Load in current Target and Input values when entering monitor mode
91     if (mode != Mode::init)
92     {
93         try
94         {
95             // Use getProperty directly to allow a missing sensor object
96             // to abort construction.
97             _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>(
98                     _bus,
99                     _name,
100                     FAN_SENSOR_VALUE_INTF,
101                     FAN_VALUE_PROPERTY);
102         }
103         catch (std::exception& e)
104         {
105             throw InvalidSensorError();
106         }
107 
108         if (_hasTarget)
109         {
110             readProperty(_interface,
111                          FAN_TARGET_PROPERTY,
112                          _name,
113                          _bus,
114                          _tachTarget);
115         }
116 
117         auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
118 
119         tachSignal = std::make_unique<sdbusplus::server::match::match>(
120                 _bus,
121                 match.c_str(),
122                 [this](auto& msg){ this->handleTachChange(msg); });
123 
124         if (_hasTarget)
125         {
126             match = getMatchString(_interface);
127 
128             targetSignal = std::make_unique<sdbusplus::server::match::match>(
129                     _bus,
130                     match.c_str(),
131                     [this](auto& msg){ this->handleTargetChange(msg); });
132         }
133     }
134 }
135 
136 std::string TachSensor::getMatchString(const std::string& interface)
137 {
138     return sdbusplus::bus::match::rules::propertiesChanged(
139             _name, interface);
140 }
141 
142 uint64_t TachSensor::getTarget() const
143 {
144     if (!_hasTarget)
145     {
146         return _fan.findTargetSpeed();
147     }
148     return _tachTarget;
149 }
150 
151 void TachSensor::setFunctional(bool functional)
152 {
153     _functional = functional;
154     updateInventory(_functional);
155 }
156 
157 /**
158  * @brief Reads a property from the input message and stores it in value.
159  *        T is the value type.
160  *
161  *        Note: This can only be called once per message.
162  *
163  * @param[in] msg - the dbus message that contains the data
164  * @param[in] interface - the interface the property is on
165  * @param[in] propertName - the name of the property
166  * @param[out] value - the value to store the property value in
167  */
168 template<typename T>
169 static void readPropertyFromMessage(sdbusplus::message::message& msg,
170                                     const std::string& interface,
171                                     const std::string& propertyName,
172                                     T& value)
173 {
174     std::string sensor;
175     std::map<std::string, sdbusplus::message::variant<T>> data;
176     msg.read(sensor, data);
177 
178     if (sensor.compare(interface) == 0)
179     {
180         auto propertyMap = data.find(propertyName);
181         if (propertyMap != data.end())
182         {
183             value = sdbusplus::message::variant_ns::get<T>(
184                         propertyMap->second);
185         }
186     }
187 }
188 
189 
190 void TachSensor::handleTargetChange(sdbusplus::message::message& msg)
191 {
192     readPropertyFromMessage(msg,
193                             _interface,
194                             FAN_TARGET_PROPERTY,
195                             _tachTarget);
196 
197     //Check all tach sensors on the fan against the target
198     _fan.tachChanged();
199 }
200 
201 
202 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
203 {
204    readPropertyFromMessage(msg,
205                            FAN_SENSOR_VALUE_INTF,
206                            FAN_VALUE_PROPERTY,
207                            _tachInput);
208 
209    //Check just this sensor against the target
210    _fan.tachChanged(*this);
211 }
212 
213 
214 std::chrono::microseconds TachSensor::getTimeout()
215 {
216     using namespace std::chrono;
217 
218     return duration_cast<microseconds>(seconds(_timeout));
219 }
220 
221 void TachSensor::updateInventory(bool functional)
222 {
223     auto objectMap = util::getObjMap<bool>(
224             _invName,
225             util::OPERATIONAL_STATUS_INTF,
226             util::FUNCTIONAL_PROPERTY,
227             functional);
228     auto response = util::SDBusPlus::lookupAndCallMethod(
229             _bus,
230             util::INVENTORY_PATH,
231             util::INVENTORY_INTF,
232             "Notify",
233             objectMap);
234     if (response.is_method_error())
235     {
236         log<level::ERR>("Error in notify update of tach sensor inventory");
237     }
238 }
239 
240 }
241 }
242 }
243