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