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 <phosphor-logging/log.hpp>
17 #include "fan.hpp"
18 #include "sdbusplus.hpp"
19 #include "tach_sensor.hpp"
20 #include "../utility.hpp"
21 
22 namespace phosphor
23 {
24 namespace fan
25 {
26 namespace monitor
27 {
28 
29 constexpr auto FAN_SENSOR_CONTROL_INTF = "xyz.openbmc_project.Control.FanSpeed";
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 
35 /**
36  * @brief Helper function to read a property
37  *
38  * @param[in] interface - the interface the property is on
39  * @param[in] propertName - the name of the property
40  * @param[in] path - the dbus path
41  * @param[in] bus - the dbus object
42  * @param[out] value - filled in with the property value
43  */
44 template<typename T>
45 static void readProperty(const std::string& interface,
46                          const std::string& propertyName,
47                          const std::string& path,
48                          sdbusplus::bus::bus& bus,
49                          T& value)
50 {
51     try
52     {
53         value = util::SDBusPlus::getProperty<T>(bus,
54                                                 path,
55                                                 interface,
56                                                 propertyName);
57     }
58     catch (std::exception& e)
59     {
60         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
61     }
62 }
63 
64 
65 TachSensor::TachSensor(sdbusplus::bus::bus& bus,
66                        Fan& fan,
67                        const std::string& id,
68                        bool hasTarget,
69                        size_t timeout,
70                        phosphor::fan::event::EventPtr& events) :
71     _bus(bus),
72     _fan(fan),
73     _name(FAN_SENSOR_PATH + id),
74     _hasTarget(hasTarget),
75     _timeout(timeout),
76     _timer(events, [this, &fan](){ fan.timerExpired(*this); })
77 {
78     //Load in starting Target and Input values
79 
80     try
81     {
82         // Use getProperty directly to allow a missing sensor object
83         // to abort construction.
84         _tachInput = util::SDBusPlus::getProperty<decltype(_tachInput)>(
85                 _bus,
86                 _name,
87                 FAN_SENSOR_VALUE_INTF,
88                 FAN_VALUE_PROPERTY);
89     }
90     catch (std::exception& e)
91     {
92         throw InvalidSensorError();
93     }
94 
95     if (_hasTarget)
96     {
97         readProperty(FAN_SENSOR_CONTROL_INTF,
98                      FAN_TARGET_PROPERTY,
99                      _name,
100                      _bus,
101                      _tachTarget);
102     }
103 
104     auto match = getMatchString(FAN_SENSOR_VALUE_INTF);
105 
106     tachSignal = std::make_unique<sdbusplus::server::match::match>(
107                      _bus,
108                      match.c_str(),
109                      [this](auto& msg){ this->handleTachChange(msg); });
110 
111     if (_hasTarget)
112     {
113         match = getMatchString(FAN_SENSOR_CONTROL_INTF);
114 
115         targetSignal = std::make_unique<sdbusplus::server::match::match>(
116                            _bus,
117                            match.c_str(),
118                            [this](auto& msg){ this->handleTargetChange(msg); });
119     }
120 
121 }
122 
123 
124 std::string TachSensor::getMatchString(const std::string& interface)
125 {
126     return sdbusplus::bus::match::rules::propertiesChanged(
127             _name, interface);
128 }
129 
130 
131 /**
132  * @brief Reads a property from the input message and stores it in value.
133  *        T is the value type.
134  *
135  *        Note: This can only be called once per message.
136  *
137  * @param[in] msg - the dbus message that contains the data
138  * @param[in] interface - the interface the property is on
139  * @param[in] propertName - the name of the property
140  * @param[out] value - the value to store the property value in
141  */
142 template<typename T>
143 static void readPropertyFromMessage(sdbusplus::message::message& msg,
144                                     const std::string& interface,
145                                     const std::string& propertyName,
146                                     T& value)
147 {
148     std::string sensor;
149     std::map<std::string, sdbusplus::message::variant<T>> data;
150     msg.read(sensor, data);
151 
152     if (sensor.compare(interface) == 0)
153     {
154         auto propertyMap = data.find(propertyName);
155         if (propertyMap != data.end())
156         {
157             value = sdbusplus::message::variant_ns::get<T>(
158                         propertyMap->second);
159         }
160     }
161 }
162 
163 
164 void TachSensor::handleTargetChange(sdbusplus::message::message& msg)
165 {
166     readPropertyFromMessage(msg,
167                             FAN_SENSOR_CONTROL_INTF,
168                             FAN_TARGET_PROPERTY,
169                             _tachTarget);
170 
171     //Check all tach sensors on the fan against the target
172     _fan.tachChanged();
173 }
174 
175 
176 void TachSensor::handleTachChange(sdbusplus::message::message& msg)
177 {
178    readPropertyFromMessage(msg,
179                            FAN_SENSOR_VALUE_INTF,
180                            FAN_VALUE_PROPERTY,
181                            _tachInput);
182 
183    //Check just this sensor against the target
184    _fan.tachChanged(*this);
185 }
186 
187 
188 std::chrono::microseconds TachSensor::getTimeout()
189 {
190     using namespace std::chrono;
191 
192     return duration_cast<microseconds>(seconds(_timeout));
193 }
194 
195 
196 }
197 }
198 }
199