xref: /openbmc/phosphor-hwmon/mainloop.cpp (revision 1e6324fa4fc233c3f2913b3d5278aa06e45ff42f)
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 <cstdlib>
19 #include <algorithm>
20 
21 #include <phosphor-logging/elog-errors.hpp>
22 #include "sensorset.hpp"
23 #include "hwmon.hpp"
24 #include "sysfs.hpp"
25 #include "mainloop.hpp"
26 #include "env.hpp"
27 #include "thresholds.hpp"
28 #include "targets.hpp"
29 #include "fan_speed.hpp"
30 
31 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
32 
33 using namespace phosphor::logging;
34 
35 // Initialization for Warning Objects
36 decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo =
37     &WarningObject::warningLow;
38 decltype(Thresholds<WarningObject>::setHi) Thresholds<WarningObject>::setHi =
39     &WarningObject::warningHigh;
40 decltype(Thresholds<WarningObject>::getLo) Thresholds<WarningObject>::getLo =
41     &WarningObject::warningLow;
42 decltype(Thresholds<WarningObject>::getHi) Thresholds<WarningObject>::getHi =
43     &WarningObject::warningHigh;
44 decltype(Thresholds<WarningObject>::alarmLo) Thresholds<WarningObject>::alarmLo =
45     &WarningObject::warningAlarmLow;
46 decltype(Thresholds<WarningObject>::alarmHi) Thresholds<WarningObject>::alarmHi =
47     &WarningObject::warningAlarmHigh;
48 
49 // Initialization for Critical Objects
50 decltype(Thresholds<CriticalObject>::setLo) Thresholds<CriticalObject>::setLo =
51     &CriticalObject::criticalLow;
52 decltype(Thresholds<CriticalObject>::setHi) Thresholds<CriticalObject>::setHi =
53     &CriticalObject::criticalHigh;
54 decltype(Thresholds<CriticalObject>::getLo) Thresholds<CriticalObject>::getLo =
55     &CriticalObject::criticalLow;
56 decltype(Thresholds<CriticalObject>::getHi) Thresholds<CriticalObject>::getHi =
57     &CriticalObject::criticalHigh;
58 decltype(Thresholds<CriticalObject>::alarmLo) Thresholds<CriticalObject>::alarmLo =
59     &CriticalObject::criticalAlarmLow;
60 decltype(Thresholds<CriticalObject>::alarmHi) Thresholds<CriticalObject>::alarmHi =
61     &CriticalObject::criticalAlarmHigh;
62 
63 
64 
65 static constexpr auto typeAttrMap =
66 {
67     // 1 - hwmon class
68     // 2 - unit
69     // 3 - sysfs scaling factor
70     std::make_tuple(
71         hwmon::type::ctemp,
72         ValueInterface::Unit::DegreesC,
73         -3,
74         "temperature"),
75     std::make_tuple(
76         hwmon::type::cfan,
77         ValueInterface::Unit::RPMS,
78         0,
79         "fan_tach"),
80     std::make_tuple(
81         hwmon::type::cvolt,
82         ValueInterface::Unit::Volts,
83         -3,
84         "voltage"),
85     std::make_tuple(
86         hwmon::type::ccurr,
87         ValueInterface::Unit::Amperes,
88         -3,
89         "current"),
90     std::make_tuple(
91         hwmon::type::cenergy,
92         ValueInterface::Unit::Joules,
93         -6,
94         "energy"),
95     std::make_tuple(
96         hwmon::type::cpower,
97         ValueInterface::Unit::Watts,
98         -6,
99         "power"),
100 };
101 
102 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs)
103 {
104     return std::get<0>(attrs);
105 }
106 
107 auto getUnit(decltype(typeAttrMap)::const_reference attrs)
108 {
109     return std::get<1>(attrs);
110 }
111 
112 auto getScale(decltype(typeAttrMap)::const_reference attrs)
113 {
114     return std::get<2>(attrs);
115 }
116 
117 auto getNamespace(decltype(typeAttrMap)::const_reference attrs)
118 {
119     return std::get<3>(attrs);
120 }
121 
122 using AttributeIterator = decltype(*typeAttrMap.begin());
123 using Attributes
124     = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type;
125 
126 auto getAttributes(const std::string& type, Attributes& attributes)
127 {
128     // *INDENT-OFF*
129     auto a = std::find_if(
130                 typeAttrMap.begin(),
131                 typeAttrMap.end(),
132                 [&](const auto & e)
133                 {
134                    return type == getHwmonType(e);
135                 });
136     // *INDENT-ON*
137 
138     if (a == typeAttrMap.end())
139     {
140         return false;
141     }
142 
143     attributes = *a;
144     return true;
145 }
146 
147 auto addValue(const SensorSet::key_type& sensor,
148               const std::string& hwmonRoot,
149               const std::string& instance,
150               ObjectInfo& info)
151 {
152     static constexpr bool deferSignals = true;
153 
154     // Get the initial value for the value interface.
155     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
156     auto& obj = std::get<Object>(info);
157     auto& objPath = std::get<std::string>(info);
158 
159     int val;
160     try
161     {
162         val = sysfs::readSysfsWithCallout(hwmonRoot,
163                                           instance,
164                                           sensor.first,
165                                           sensor.second,
166                                           hwmon::entry::input);
167     }
168     catch(const std::exception& ioe)
169     {
170         using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
171         commit<ReadFailure>();
172 
173         return static_cast<std::shared_ptr<ValueObject>>(nullptr);
174     }
175 
176     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
177     iface->value(val);
178 
179     Attributes attrs;
180     if (getAttributes(sensor.first, attrs))
181     {
182         iface->unit(getUnit(attrs));
183         iface->scale(getScale(attrs));
184     }
185 
186     obj[InterfaceType::VALUE] = iface;
187     return iface;
188 }
189 
190 MainLoop::MainLoop(
191     sdbusplus::bus::bus&& bus,
192     const std::string& path,
193     const char* prefix,
194     const char* root)
195     : _bus(std::move(bus)),
196       _manager(_bus, root),
197       _shutdown(false),
198       _hwmonRoot(),
199       _instance(),
200       _prefix(prefix),
201       _root(root),
202       state()
203 {
204     std::string p = path;
205     while (!p.empty() && p.back() == '/')
206     {
207         p.pop_back();
208     }
209 
210     auto n = p.rfind('/');
211     if (n != std::string::npos)
212     {
213         _instance.assign(p.substr(n + 1));
214         _hwmonRoot.assign(p.substr(0, n));
215     }
216 
217     assert(!_instance.empty());
218     assert(!_hwmonRoot.empty());
219 }
220 
221 void MainLoop::shutdown() noexcept
222 {
223     _shutdown = true;
224 }
225 
226 void MainLoop::run()
227 {
228     // Check sysfs for available sensors.
229     auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance);
230 
231     for (auto& i : *sensors)
232     {
233         std::string label;
234 
235         /*
236          * Check if the value of the MODE_<item><X> env variable for the sensor
237          * is "label", then read the sensor number from the <item><X>_label
238          * file. The name of the DBUS object would be the value of the env
239          * variable LABEL_<item><sensorNum>. If the MODE_<item><X> env variable
240          * does'nt exist, then the name of DBUS object is the value of the env
241          * variable LABEL_<item><X>.
242          */
243         auto mode = getEnv("MODE", i.first);
244         if (!mode.compare(hwmon::entry::label))
245         {
246             label = getIndirectLabelEnv(
247                 "LABEL", _hwmonRoot + '/' + _instance + '/', i.first);
248             if (label.empty())
249             {
250                 continue;
251             }
252         }
253         else
254         {
255             // Ignore inputs without a label.
256             label = getEnv("LABEL", i.first);
257             if (label.empty())
258             {
259                 continue;
260             }
261         }
262 
263         Attributes attrs;
264         if (!getAttributes(i.first.first, attrs))
265         {
266             continue;
267         }
268 
269         std::string objectPath{_root};
270         objectPath.append(1, '/');
271         objectPath.append(getNamespace(attrs));
272         objectPath.append(1, '/');
273         objectPath.append(label);
274 
275         ObjectInfo info(&_bus, std::move(objectPath), Object());
276         auto valueInterface = addValue(i.first, _hwmonRoot, _instance, info);
277         if (!valueInterface)
278         {
279             continue; /* skip adding this sensor for now. */
280         }
281         auto sensorValue = valueInterface->value();
282         addThreshold<WarningObject>(i.first, sensorValue, info);
283         addThreshold<CriticalObject>(i.first, sensorValue, info);
284         //TODO openbmc/openbmc#1347
285         //     Handle application restarts to set/refresh fan speed values
286         auto target = addTarget<hwmon::FanSpeed>(
287                 i.first, _hwmonRoot, _instance, info);
288 
289         if (target)
290         {
291             target->enable();
292         }
293 
294         // All the interfaces have been created.  Go ahead
295         // and emit InterfacesAdded.
296         valueInterface->emit_object_added();
297 
298         auto value = std::make_tuple(
299                          std::move(i.second),
300                          std::move(label),
301                          std::move(info));
302 
303         state[std::move(i.first)] = std::move(value);
304     }
305 
306     /* If there are no sensors specified by labels, exit. */
307     if (0 == state.size())
308     {
309         return;
310     }
311 
312     {
313         std::string busname{_prefix};
314         busname.append(1, '.');
315         busname.append(_instance);
316         _bus.request_name(busname.c_str());
317     }
318 
319     {
320         auto interval = getenv("INTERVAL");
321         if (interval)
322         {
323             _interval = strtoull(interval, NULL, 10);
324         }
325     }
326 
327     // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
328     //       ensure the objects all exist?
329 
330     // Polling loop.
331     while (!_shutdown)
332     {
333         std::vector<SensorSet::key_type> destroy;
334         // Iterate through all the sensors.
335         for (auto& i : state)
336         {
337             auto& attrs = std::get<0>(i.second);
338             if (attrs.find(hwmon::entry::input) != attrs.end())
339             {
340                 // Read value from sensor.
341                 int value;
342                 try
343                 {
344                     value = sysfs::readSysfsWithCallout(_hwmonRoot,
345                                                         _instance,
346                                                         i.first.first,
347                                                         i.first.second,
348                                                         hwmon::entry::input);
349 
350                     auto& objInfo = std::get<ObjectInfo>(i.second);
351                     auto& obj = std::get<Object>(objInfo);
352 
353                     for (auto& iface : obj)
354                     {
355                         auto valueIface = std::shared_ptr<ValueObject>();
356                         auto warnIface = std::shared_ptr<WarningObject>();
357                         auto critIface = std::shared_ptr<CriticalObject>();
358 
359                         switch (iface.first)
360                         {
361                             case InterfaceType::VALUE:
362                                 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>>
363                                             (iface.second);
364                                 valueIface->value(value);
365                                 break;
366                             case InterfaceType::WARN:
367                                 checkThresholds<WarningObject>(iface.second, value);
368                                 break;
369                             case InterfaceType::CRIT:
370                                 checkThresholds<CriticalObject>(iface.second, value);
371                                 break;
372                             default:
373                                 break;
374                         }
375                     }
376                 }
377                 catch (const std::exception& e)
378                 {
379                     using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
380                     commit<ReadFailure>();
381 
382                     destroy.push_back(i.first);
383                 }
384             }
385         }
386 
387         for (auto& i : destroy)
388         {
389             state.erase(i);
390         }
391 
392         // Respond to DBus
393         _bus.process_discard();
394 
395         // Sleep until next interval.
396         // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
397         _bus.wait(_interval);
398 
399         // TODO: Issue#7 - Should probably periodically check the SensorSet
400         //       for new entries.
401     }
402 }
403 
404 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
405