xref: /openbmc/phosphor-hwmon/mainloop.cpp (revision e9fdee0c)
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 
29 using namespace std::literals::chrono_literals;
30 
31 static constexpr auto typeAttrMap =
32 {
33     // 1 - hwmon class
34     // 2 - unit
35     // 3 - sysfs scaling factor
36     std::make_tuple(
37         hwmon::type::ctemp,
38         ValueInterface::Unit::DegreesC,
39         -3),
40     std::make_tuple(
41         hwmon::type::cfan,
42         ValueInterface::Unit::RPMS,
43         0),
44     std::make_tuple(
45         hwmon::type::cvolt,
46         ValueInterface::Unit::Volts,
47         -3),
48 };
49 
50 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs)
51 {
52     return std::get<0>(attrs);
53 }
54 
55 auto getUnit(decltype(typeAttrMap)::const_reference attrs)
56 {
57     return std::get<1>(attrs);
58 }
59 
60 auto getScale(decltype(typeAttrMap)::const_reference attrs)
61 {
62     return std::get<2>(attrs);
63 }
64 
65 auto addValue(const SensorSet::key_type& sensor,
66               const std::string& sysfsRoot, ObjectInfo& info)
67 {
68     // Get the initial value for the value interface.
69     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
70     auto& obj = std::get<Object>(info);
71     auto& objPath = std::get<std::string>(info);
72 
73     auto sysfsPath = make_sysfs_path(
74                          sysfsRoot,
75                          sensor.first,
76                          sensor.second,
77                          hwmon::entry::input);
78     int val = 0;
79     read_sysfs(sysfsPath, val);
80 
81     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str());
82     iface->value(val);
83 
84     // *INDENT-OFF*
85     const auto& attrs = std::find_if(
86                             typeAttrMap.begin(),
87                             typeAttrMap.end(),
88                             [&](const auto & e)
89                             {
90                                 return sensor.first == getHwmonType(e);
91                             });
92     // *INDENT-ON*
93 
94     if (attrs != typeAttrMap.end())
95     {
96         iface->unit(getUnit(*attrs));
97         iface->scale(getScale(*attrs));
98     }
99 
100     obj[InterfaceType::VALUE] = iface;
101     return iface;
102 }
103 
104 MainLoop::MainLoop(
105     sdbusplus::bus::bus&& bus,
106     const std::string& path,
107     const char* prefix,
108     const char* root)
109     : _bus(std::move(bus)),
110       _manager(sdbusplus::server::manager::manager(_bus, root)),
111       _shutdown(false),
112       _path(path),
113       _prefix(prefix),
114       _root(root),
115       state()
116 {
117     if (_path.back() == '/')
118     {
119         _path.pop_back();
120     }
121 }
122 
123 void MainLoop::shutdown() noexcept
124 {
125     _shutdown = true;
126 }
127 
128 void MainLoop::run()
129 {
130     // Check sysfs for available sensors.
131     auto sensors = std::make_unique<SensorSet>(_path);
132 
133     for (auto& i : *sensors)
134     {
135         // Get sensor configuration from the environment.
136 
137         // Ignore inputs without a label.
138         auto label = getEnv("LABEL", i.first);
139         if (label.empty())
140         {
141             continue;
142         }
143 
144         std::string objectPath{_root};
145         objectPath.append("/");
146         objectPath.append(i.first.first);
147         objectPath.append("/");
148         objectPath.append(label);
149 
150         ObjectInfo info(&_bus, std::move(objectPath), Object());
151         auto valueInterface = addValue(i.first, _path, info);
152 
153         auto value = std::make_tuple(
154                          std::move(i.second),
155                          std::move(label),
156                          std::move(info));
157 
158         state[std::move(i.first)] = std::move(value);
159     }
160 
161     {
162         auto copy = std::unique_ptr<char, phosphor::utility::Free<char>>(strdup(
163                         _path.c_str()));
164         auto busname = std::string(_prefix) + '.' + basename(copy.get());
165         _bus.request_name(busname.c_str());
166     }
167 
168     // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
169     //       ensure the objects all exist?
170 
171     // Polling loop.
172     while (!_shutdown)
173     {
174         // Iterate through all the sensors.
175         for (auto& i : state)
176         {
177             auto& attrs = std::get<0>(i.second);
178             if (attrs.find(hwmon::entry::input) != attrs.end())
179             {
180                 // Read value from sensor.
181                 int value = 0;
182                 read_sysfs(make_sysfs_path(_path,
183                                            i.first.first, i.first.second,
184                                            hwmon::entry::input),
185                            value);
186 
187                 auto& objInfo = std::get<ObjectInfo>(i.second);
188                 auto& obj = std::get<Object>(objInfo);
189                 auto iface = obj.find(InterfaceType::VALUE);
190 
191                 if (iface != obj.end())
192                 {
193                     auto realIface = std::experimental::any_cast<std::shared_ptr<ValueObject>>
194                                      (iface->second);
195                     realIface->value(value);
196                 }
197             }
198         }
199 
200         // Respond to DBus
201         _bus.process_discard();
202 
203         // Sleep until next interval.
204         // TODO: Issue#5 - Make this configurable.
205         // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
206         _bus.wait((1000000us).count());
207 
208         // TODO: Issue#7 - Should probably periodically check the SensorSet
209         //       for new entries.
210     }
211 }
212 
213 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
214