blob: 5f53b31df157f7ba103603d3e1d917cb3c6d3b38 [file] [log] [blame]
Brad Bishop613a5b32017-01-05 20:58:13 -05001/**
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 */
Brad Bishop8b574a72017-08-25 16:17:19 -040016#include <cerrno>
Brad Bishop613a5b32017-01-05 20:58:13 -050017#include <cstdlib>
Brad Bishop08379a32017-03-06 21:28:46 -050018#include <experimental/filesystem>
Brad Bishop68c43b22017-08-28 16:24:00 -040019#include <fstream>
Brad Bishop613a5b32017-01-05 20:58:13 -050020#include <memory>
21#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050022
Brad Bishopf4bf63a2017-08-28 15:39:19 -040023using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050024namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060025
Patrick Venture1e6324f2017-06-01 14:07:05 -070026namespace sysfs {
27
Brad Bishopf4bf63a2017-08-28 15:39:19 -040028static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050029static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
30
Brad Bishopf4bf63a2017-08-28 15:39:19 -040031std::string findPhandleMatch(
32 const std::string& iochanneldir,
33 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050034{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040035 // TODO: At the moment this method only supports device trees
36 // with iio-hwmon nodes with a single sensor. Typically
37 // device trees are defined with all the iio sensors in a
38 // single iio-hwmon node so it would be nice to add support
39 // for lists of phandles (with variable sized entries) via
40 // libfdt or something like that, so that users are not
41 // forced into implementing unusual looking device trees
42 // with multiple iio-hwmon nodes - one for each sensor.
43
44 fs::path ioChannelsPath{iochanneldir};
45 ioChannelsPath /= "io-channels";
46
47 if (!fs::exists(ioChannelsPath))
48 {
49 return emptyString;
50 }
51
52 uint32_t ioChannelsValue;
53 std::ifstream ioChannelsFile(ioChannelsPath);
54
55 ioChannelsFile.read(
56 reinterpret_cast<char*>(&ioChannelsValue),
57 sizeof(ioChannelsValue));
58
Brandon Wyman8af8a202017-05-31 18:26:30 -050059 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050060 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050061 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -040062 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050063 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -040064 continue;
65 }
66 std::ifstream pHandleFile(path);
67 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050068
Brad Bishopf4bf63a2017-08-28 15:39:19 -040069 pHandleFile.read(
70 reinterpret_cast<char*>(&pHandleValue),
71 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -050072
Brad Bishopf4bf63a2017-08-28 15:39:19 -040073 if (ioChannelsValue == pHandleValue)
74 {
75 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -050076 }
77 }
78
Brad Bishopf4bf63a2017-08-28 15:39:19 -040079 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -050080}
81
Brad Bishop431d26a2017-08-25 09:47:58 -040082std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -050083{
Brad Bishop431d26a2017-08-25 09:47:58 -040084 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
85 // /sys/devices symlink.
86 fs::path devPath{instancePath};
87 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -050088
Brad Bishop431d26a2017-08-25 09:47:58 -040089 try
Brandon Wyman8af8a202017-05-31 18:26:30 -050090 {
Brad Bishop431d26a2017-08-25 09:47:58 -040091 devPath = fs::canonical(devPath);
92 }
93 catch (const std::system_error& e)
94 {
95 return emptyString;
96 }
97
98 // See if the device is backed by the iio-hwmon driver.
99 fs::path p{devPath};
100 p /= "driver";
101 p = fs::canonical(p);
102
103 if (p.filename() != "iio_hwmon")
104 {
105 // Not backed by iio-hwmon. The device pointed to
106 // is the callout device.
107 return devPath;
108 }
109
110 // Find the DT path to the iio-hwmon platform device.
111 fs::path ofDevPath{devPath};
112 ofDevPath /= "of_node";
113
114 try
115 {
116 ofDevPath = fs::canonical(ofDevPath);
117 }
118 catch (const std::system_error& e)
119 {
120 return emptyString;
121 }
122
123 // Search /sys/bus/iio/devices for the phandle in io-channels.
124 // If a match is found, use the corresponding /sys/devices
125 // iio device as the callout device.
126 static constexpr auto iioDevices = "/sys/bus/iio/devices";
127 for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
128 {
129 p = iioDev.path();
130 p /= "of_node";
131
132 try
133 {
134 p = fs::canonical(p);
135 }
136 catch (const std::system_error& e)
137 {
138 continue;
139 }
140
141 auto match = findPhandleMatch(ofDevPath, p);
142 auto n = match.rfind('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500143 if (n != std::string::npos)
144 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400145 // This is the iio device referred to by io-channels.
146 // Remove iio:device<N>.
147 try
148 {
149 return fs::canonical(iioDev).parent_path();
150 }
151 catch (const std::system_error& e)
152 {
153 return emptyString;
154 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500155 }
156 }
157
Brad Bishop431d26a2017-08-25 09:47:58 -0400158 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500159}
160
161std::string findHwmon(const std::string& ofNode)
162{
163 static constexpr auto hwmonRoot = "/sys/class/hwmon";
164
165 fs::path fullOfPath{ofRoot};
166 fullOfPath /= ofNode;
167
168 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
169 {
170 auto path = hwmonInst.path();
171 path /= "of_node";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400172
173 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500174 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400175 path = fs::canonical(path);
176 }
177 catch (const std::system_error& e)
178 {
179 // realpath may encounter ENOENT (Hwmon
180 // instances have a nasty habit of
181 // going away without warning).
182 continue;
Brad Bishop613a5b32017-01-05 20:58:13 -0500183 }
184
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400185 if (path == fullOfPath)
186 {
187 return hwmonInst.path();
188 }
189
190 // Try to find HWMON instance via phandle values.
191 // Used for IIO device drivers.
192 auto matchpath = findPhandleMatch(path, fullOfPath);
193 if (!matchpath.empty())
194 {
195 return hwmonInst.path();
196 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500197 }
198
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400199 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500200}
201
Brad Bishop8b574a72017-08-25 16:17:19 -0400202namespace hwmonio
203{
204
205HwmonIO::HwmonIO(const std::string& path) : p(path)
206{
207
208}
209
210uint32_t HwmonIO::read(
211 const std::string& type,
212 const std::string& id,
213 const std::string& sensor) const
214{
215 uint32_t val;
216 std::ifstream ifs;
217 auto fullPath = sysfs::make_sysfs_path(
218 p, type, id, sensor);
219
220 ifs.exceptions(
221 std::ifstream::failbit |
222 std::ifstream::badbit |
223 std::ifstream::eofbit);
224 try
225 {
226 ifs.open(fullPath);
227 ifs >> val;
228 }
229 catch (const std::exception& e)
230 {
231 auto rc = errno;
232
233 if (rc == ENOENT)
234 {
235 // If the directory disappeared then this application should
236 // gracefully exit. There are race conditions between the
237 // unloading of a hwmon driver and the stopping of this service
238 // by systemd. To prevent this application from falsely failing
239 // in these scenarios, it will simply exit if the directory or
240 // file can not be found. It is up to the user(s) of this
241 // provided hwmon object to log the appropriate errors if the
242 // object disappears when it should not.
243 exit(0);
244 }
245
246 if (rc)
247 {
248 // Work around GCC bugs 53984 and 66145 for callers by
249 // explicitly raising system_error here.
250 throw std::system_error(rc, std::generic_category());
251 }
252
253 throw;
254 }
255 return val;
256}
257
258void HwmonIO::write(
259 uint32_t val,
260 const std::string& type,
261 const std::string& id,
262 const std::string& sensor) const
263{
264 std::ofstream ofs;
265 auto fullPath = sysfs::make_sysfs_path(
266 p, type, id, sensor);
267
268 ofs.exceptions(
269 std::ofstream::failbit |
270 std::ofstream::badbit |
271 std::ofstream::eofbit);
272
273 // See comments in the read method for an explanation of the odd exception
274 // handling behavior here.
275
276 try
277 {
278 ofs.open(fullPath);
279 ofs << val;
280 }
281 catch (const std::exception& e)
282 {
283 auto rc = errno;
284
285 if (rc == ENOENT)
286 {
287 exit(0);
288 }
289
290 if (rc)
291 {
292 throw std::system_error(rc, std::generic_category());
293 }
294
295 throw;
296 }
297}
298
299std::string HwmonIO::path() const
300{
301 return p;
302}
303
304} // namespace hwmonio
Patrick Venture1e6324f2017-06-01 14:07:05 -0700305}
Brad Bishop613a5b32017-01-05 20:58:13 -0500306// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4