blob: 9cf155226d2bb15537acea904d5b1c905d00325c [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 Bishop754d38c2017-09-08 00:46:58 -040016#include <algorithm>
Brad Bishop8b574a72017-08-25 16:17:19 -040017#include <cerrno>
Brad Bishop613a5b32017-01-05 20:58:13 -050018#include <cstdlib>
Brad Bishop08379a32017-03-06 21:28:46 -050019#include <experimental/filesystem>
Brad Bishop68c43b22017-08-28 16:24:00 -040020#include <fstream>
Brad Bishop613a5b32017-01-05 20:58:13 -050021#include <memory>
Brad Bishop754d38c2017-09-08 00:46:58 -040022#include <thread>
Brad Bishop613a5b32017-01-05 20:58:13 -050023#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050024
Brad Bishopf4bf63a2017-08-28 15:39:19 -040025using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050026namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060027
Patrick Venture1e6324f2017-06-01 14:07:05 -070028namespace sysfs {
29
Brad Bishop754d38c2017-09-08 00:46:58 -040030static constexpr auto retryableErrors = {
31 /*
32 * Retry on bus or device errors or timeouts in case
33 * they are transient.
34 */
35 EIO,
36 ETIMEDOUT,
37
38 /*
39 * Retry CRC errors.
40 */
41 EBADMSG,
42
43 /*
44 * Some hwmon drivers do this when they aren't ready
45 * instead of blocking. Retry.
46 */
47 EAGAIN,
48 /*
49 * We'll see this when for example i2c devices are
50 * unplugged but the driver is still bound. Retry
51 * rather than exit on the off chance the device is
52 * plugged back in and the driver doesn't do a
53 * remove/probe. If a remove does occur, we'll
54 * eventually get ENOENT.
55 */
56 ENXIO,
57};
58
Brad Bishopf4bf63a2017-08-28 15:39:19 -040059static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050060static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
61
Brad Bishopf4bf63a2017-08-28 15:39:19 -040062std::string findPhandleMatch(
63 const std::string& iochanneldir,
64 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050065{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040066 // TODO: At the moment this method only supports device trees
67 // with iio-hwmon nodes with a single sensor. Typically
68 // device trees are defined with all the iio sensors in a
69 // single iio-hwmon node so it would be nice to add support
70 // for lists of phandles (with variable sized entries) via
71 // libfdt or something like that, so that users are not
72 // forced into implementing unusual looking device trees
73 // with multiple iio-hwmon nodes - one for each sensor.
74
75 fs::path ioChannelsPath{iochanneldir};
76 ioChannelsPath /= "io-channels";
77
78 if (!fs::exists(ioChannelsPath))
79 {
80 return emptyString;
81 }
82
83 uint32_t ioChannelsValue;
84 std::ifstream ioChannelsFile(ioChannelsPath);
85
86 ioChannelsFile.read(
87 reinterpret_cast<char*>(&ioChannelsValue),
88 sizeof(ioChannelsValue));
89
Brandon Wyman8af8a202017-05-31 18:26:30 -050090 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050091 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050092 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -040093 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050094 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -040095 continue;
96 }
97 std::ifstream pHandleFile(path);
98 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050099
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400100 pHandleFile.read(
101 reinterpret_cast<char*>(&pHandleValue),
102 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -0500103
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400104 if (ioChannelsValue == pHandleValue)
105 {
106 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500107 }
108 }
109
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400110 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500111}
112
Brad Bishop431d26a2017-08-25 09:47:58 -0400113std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -0500114{
Brad Bishop431d26a2017-08-25 09:47:58 -0400115 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
116 // /sys/devices symlink.
117 fs::path devPath{instancePath};
118 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -0500119
Brad Bishop431d26a2017-08-25 09:47:58 -0400120 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500121 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400122 devPath = fs::canonical(devPath);
123 }
124 catch (const std::system_error& e)
125 {
126 return emptyString;
127 }
128
129 // See if the device is backed by the iio-hwmon driver.
130 fs::path p{devPath};
131 p /= "driver";
132 p = fs::canonical(p);
133
134 if (p.filename() != "iio_hwmon")
135 {
136 // Not backed by iio-hwmon. The device pointed to
137 // is the callout device.
138 return devPath;
139 }
140
141 // Find the DT path to the iio-hwmon platform device.
142 fs::path ofDevPath{devPath};
143 ofDevPath /= "of_node";
144
145 try
146 {
147 ofDevPath = fs::canonical(ofDevPath);
148 }
149 catch (const std::system_error& e)
150 {
151 return emptyString;
152 }
153
154 // Search /sys/bus/iio/devices for the phandle in io-channels.
155 // If a match is found, use the corresponding /sys/devices
156 // iio device as the callout device.
157 static constexpr auto iioDevices = "/sys/bus/iio/devices";
158 for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
159 {
160 p = iioDev.path();
161 p /= "of_node";
162
163 try
164 {
165 p = fs::canonical(p);
166 }
167 catch (const std::system_error& e)
168 {
169 continue;
170 }
171
172 auto match = findPhandleMatch(ofDevPath, p);
173 auto n = match.rfind('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500174 if (n != std::string::npos)
175 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400176 // This is the iio device referred to by io-channels.
177 // Remove iio:device<N>.
178 try
179 {
180 return fs::canonical(iioDev).parent_path();
181 }
182 catch (const std::system_error& e)
183 {
184 return emptyString;
185 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500186 }
187 }
188
Brad Bishop431d26a2017-08-25 09:47:58 -0400189 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500190}
191
192std::string findHwmon(const std::string& ofNode)
193{
194 static constexpr auto hwmonRoot = "/sys/class/hwmon";
195
196 fs::path fullOfPath{ofRoot};
197 fullOfPath /= ofNode;
198
199 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
200 {
201 auto path = hwmonInst.path();
202 path /= "of_node";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400203
204 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500205 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400206 path = fs::canonical(path);
207 }
208 catch (const std::system_error& e)
209 {
210 // realpath may encounter ENOENT (Hwmon
211 // instances have a nasty habit of
212 // going away without warning).
213 continue;
Brad Bishop613a5b32017-01-05 20:58:13 -0500214 }
215
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400216 if (path == fullOfPath)
217 {
218 return hwmonInst.path();
219 }
220
221 // Try to find HWMON instance via phandle values.
222 // Used for IIO device drivers.
223 auto matchpath = findPhandleMatch(path, fullOfPath);
224 if (!matchpath.empty())
225 {
226 return hwmonInst.path();
227 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500228 }
229
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400230 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500231}
232
Brad Bishop8b574a72017-08-25 16:17:19 -0400233namespace hwmonio
234{
235
236HwmonIO::HwmonIO(const std::string& path) : p(path)
237{
238
239}
240
241uint32_t HwmonIO::read(
242 const std::string& type,
243 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400244 const std::string& sensor,
245 size_t retries,
246 std::chrono::milliseconds delay) const
Brad Bishop8b574a72017-08-25 16:17:19 -0400247{
248 uint32_t val;
249 std::ifstream ifs;
250 auto fullPath = sysfs::make_sysfs_path(
251 p, type, id, sensor);
252
253 ifs.exceptions(
254 std::ifstream::failbit |
255 std::ifstream::badbit |
256 std::ifstream::eofbit);
Brad Bishop754d38c2017-09-08 00:46:58 -0400257
258 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400259 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400260 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400261 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400262 if (!ifs.is_open())
263 ifs.open(fullPath);
264 ifs.clear();
265 ifs.seekg(0);
266 ifs >> val;
Brad Bishop8b574a72017-08-25 16:17:19 -0400267 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400268 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400269 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400270 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400271
Brad Bishop754d38c2017-09-08 00:46:58 -0400272 if (!rc)
273 {
274 throw;
275 }
276
277 if (rc == ENOENT)
278 {
279 // If the directory disappeared then this application should
280 // gracefully exit. There are race conditions between the
281 // unloading of a hwmon driver and the stopping of this service
282 // by systemd. To prevent this application from falsely failing
283 // in these scenarios, it will simply exit if the directory or
284 // file can not be found. It is up to the user(s) of this
285 // provided hwmon object to log the appropriate errors if the
286 // object disappears when it should not.
287 exit(0);
288 }
289
290 if (0 == std::count(
291 retryableErrors.begin(),
292 retryableErrors.end(),
293 rc) ||
294 !retries)
295 {
296 // Not a retryable error or out of retries.
297
298 // Work around GCC bugs 53984 and 66145 for callers by
299 // explicitly raising system_error here.
300 throw std::system_error(rc, std::generic_category());
301 }
302
303 --retries;
304 std::this_thread::sleep_for(delay);
305 continue;
306 }
307 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400308 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400309
Brad Bishop8b574a72017-08-25 16:17:19 -0400310 return val;
311}
312
313void HwmonIO::write(
314 uint32_t val,
315 const std::string& type,
316 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400317 const std::string& sensor,
318 size_t retries,
319 std::chrono::milliseconds delay) const
320
Brad Bishop8b574a72017-08-25 16:17:19 -0400321{
322 std::ofstream ofs;
323 auto fullPath = sysfs::make_sysfs_path(
324 p, type, id, sensor);
325
326 ofs.exceptions(
327 std::ofstream::failbit |
328 std::ofstream::badbit |
329 std::ofstream::eofbit);
330
331 // See comments in the read method for an explanation of the odd exception
332 // handling behavior here.
333
Brad Bishop754d38c2017-09-08 00:46:58 -0400334 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400335 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400336 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400337 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400338 if (!ofs.is_open())
339 ofs.open(fullPath);
340 ofs.clear();
341 ofs.seekp(0);
342 ofs << val;
343 ofs.flush();
Brad Bishop8b574a72017-08-25 16:17:19 -0400344 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400345 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400346 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400347 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400348
Brad Bishop754d38c2017-09-08 00:46:58 -0400349 if (!rc)
350 {
351 throw;
352 }
353
354 if (rc == ENOENT)
355 {
356 exit(0);
357 }
358
359 if (0 == std::count(
360 retryableErrors.begin(),
361 retryableErrors.end(),
362 rc) ||
363 !retries)
364 {
365 // Not a retryable error or out of retries.
366
367 // Work around GCC bugs 53984 and 66145 for callers by
368 // explicitly raising system_error here.
369 throw std::system_error(rc, std::generic_category());
370 }
371
372 --retries;
373 std::this_thread::sleep_for(delay);
374 continue;
375 }
376 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400377 }
378}
379
380std::string HwmonIO::path() const
381{
382 return p;
383}
384
385} // namespace hwmonio
Patrick Venture1e6324f2017-06-01 14:07:05 -0700386}
Brad Bishop613a5b32017-01-05 20:58:13 -0500387// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4