xref: /openbmc/phosphor-hwmon/mainloop.cpp (revision 30dbceec)
1 /**
2  * Copyright © 2016 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 <iostream>
17 #include <memory>
18 #include <cstring>
19 #include <cstdlib>
20 #include <chrono>
21 #include <algorithm>
22 #include "sensorset.hpp"
23 #include "hwmon.hpp"
24 #include "sysfs.hpp"
25 #include "mainloop.hpp"
26 #include "util.hpp"
27 #include "env.hpp"
28 #include "thresholds.hpp"
29 
30 using namespace std::literals::chrono_literals;
31 
32 static constexpr auto typeAttrMap =
33 {
34     // 1 - hwmon class
35     // 2 - unit
36     // 3 - sysfs scaling factor
37     std::make_tuple(
38         hwmon::type::ctemp,
39         ValueInterface::Unit::DegreesC,
40         -3,
41         "temperature"),
42     std::make_tuple(
43         hwmon::type::cfan,
44         ValueInterface::Unit::RPMS,
45         0,
46         "fan_tach"),
47     std::make_tuple(
48         hwmon::type::cvolt,
49         ValueInterface::Unit::Volts,
50         -3,
51         "voltage"),
52     std::make_tuple(
53         hwmon::type::ccurr,
54         ValueInterface::Unit::Amperes,
55         -3,
56         "current"),
57     std::make_tuple(
58         hwmon::type::cenergy,
59         ValueInterface::Unit::Joules,
60         -6,
61         "energy"),
62     std::make_tuple(
63         hwmon::type::cpower,
64         ValueInterface::Unit::Watts,
65         -6,
66         "power"),
67 };
68 
69 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs)
70 {
71     return std::get<0>(attrs);
72 }
73 
74 auto getUnit(decltype(typeAttrMap)::const_reference attrs)
75 {
76     return std::get<1>(attrs);
77 }
78 
79 auto getScale(decltype(typeAttrMap)::const_reference attrs)
80 {
81     return std::get<2>(attrs);
82 }
83 
84 auto getNamespace(decltype(typeAttrMap)::const_reference attrs)
85 {
86     return std::get<3>(attrs);
87 }
88 
89 using AttributeIterator = decltype(*typeAttrMap.begin());
90 using Attributes
91     = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type;
92 
93 auto getAttributes(const std::string& type, Attributes& attributes)
94 {
95     // *INDENT-OFF*
96     auto a = std::find_if(
97                 typeAttrMap.begin(),
98                 typeAttrMap.end(),
99                 [&](const auto & e)
100                 {
101                    return type == getHwmonType(e);
102                 });
103     // *INDENT-ON*
104 
105     if (a == typeAttrMap.end())
106     {
107         return false;
108     }
109 
110     attributes = *a;
111     return true;
112 }
113 
114 auto addValue(const SensorSet::key_type& sensor,
115               const std::string& sysfsRoot, ObjectInfo& info)
116 {
117     static constexpr bool deferSignals = true;
118 
119     // Get the initial value for the value interface.
120     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
121     auto& obj = std::get<Object>(info);
122     auto& objPath = std::get<std::string>(info);
123 
124     auto sysfsPath = make_sysfs_path(
125                          sysfsRoot,
126                          sensor.first,
127                          sensor.second,
128                          hwmon::entry::input);
129     int val = 0;
130     read_sysfs(sysfsPath, val);
131 
132     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
133     iface->value(val);
134 
135     Attributes attrs;
136     if (getAttributes(sensor.first, attrs))
137     {
138         iface->unit(getUnit(attrs));
139         iface->scale(getScale(attrs));
140     }
141 
142     obj[InterfaceType::VALUE] = iface;
143     return iface;
144 }
145 
146 MainLoop::MainLoop(
147     sdbusplus::bus::bus&& bus,
148     const std::string& path,
149     const char* prefix,
150     const char* root)
151     : _bus(std::move(bus)),
152       _manager(sdbusplus::server::manager::manager(_bus, root)),
153       _shutdown(false),
154       _path(path),
155       _prefix(prefix),
156       _root(root),
157       state()
158 {
159     if (_path.back() == '/')
160     {
161         _path.pop_back();
162     }
163 }
164 
165 void MainLoop::shutdown() noexcept
166 {
167     _shutdown = true;
168 }
169 
170 void MainLoop::run()
171 {
172     // Check sysfs for available sensors.
173     auto sensors = std::make_unique<SensorSet>(_path);
174 
175     for (auto& i : *sensors)
176     {
177         // Get sensor configuration from the environment.
178 
179         // Ignore inputs without a label.
180         auto label = getEnv("LABEL", i.first);
181         if (label.empty())
182         {
183             continue;
184         }
185 
186         Attributes attrs;
187         if (!getAttributes(i.first.first, attrs))
188         {
189             continue;
190         }
191 
192         std::string objectPath{_root};
193         objectPath.append("/");
194         objectPath.append(getNamespace(attrs));
195         objectPath.append("/");
196         objectPath.append(label);
197 
198         ObjectInfo info(&_bus, std::move(objectPath), Object());
199         auto valueInterface = addValue(i.first, _path, info);
200         auto sensorValue = valueInterface->value();
201         addThreshold<WarningObject>(i.first, sensorValue, info);
202         addThreshold<CriticalObject>(i.first, sensorValue, info);
203 
204         // All the interfaces have been created.  Go ahead
205         // and emit InterfacesAdded.
206         valueInterface->emit_object_added();
207 
208         auto value = std::make_tuple(
209                          std::move(i.second),
210                          std::move(label),
211                          std::move(info));
212 
213         state[std::move(i.first)] = std::move(value);
214     }
215 
216     {
217         auto copy = std::unique_ptr<char, phosphor::utility::Free<char>>(strdup(
218                         _path.c_str()));
219         auto busname = std::string(_prefix) + '.' + basename(copy.get());
220         _bus.request_name(busname.c_str());
221     }
222 
223     // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
224     //       ensure the objects all exist?
225 
226     // Polling loop.
227     while (!_shutdown)
228     {
229         // Iterate through all the sensors.
230         for (auto& i : state)
231         {
232             auto& attrs = std::get<0>(i.second);
233             if (attrs.find(hwmon::entry::input) != attrs.end())
234             {
235                 // Read value from sensor.
236                 int value = 0;
237                 read_sysfs(make_sysfs_path(_path,
238                                            i.first.first, i.first.second,
239                                            hwmon::entry::input),
240                            value);
241 
242                 auto& objInfo = std::get<ObjectInfo>(i.second);
243                 auto& obj = std::get<Object>(objInfo);
244 
245                 for (auto& iface : obj)
246                 {
247                     auto valueIface = std::shared_ptr<ValueObject>();
248                     auto warnIface = std::shared_ptr<WarningObject>();
249                     auto critIface = std::shared_ptr<CriticalObject>();
250 
251                     switch (iface.first)
252                     {
253                         case InterfaceType::VALUE:
254                             valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>>
255                                          (iface.second);
256                             valueIface->value(value);
257                             break;
258                         case InterfaceType::WARN:
259                             checkThresholds<WarningObject>(iface.second, value);
260                             break;
261                         case InterfaceType::CRIT:
262                             checkThresholds<CriticalObject>(iface.second, value);
263                             break;
264                         default:
265                             break;
266                     }
267                 }
268             }
269         }
270 
271         // Respond to DBus
272         _bus.process_discard();
273 
274         // Sleep until next interval.
275         // TODO: Issue#5 - Make this configurable.
276         // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
277         _bus.wait((1000000us).count());
278 
279         // TODO: Issue#7 - Should probably periodically check the SensorSet
280         //       for new entries.
281     }
282 }
283 
284 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
285