blob: 0eb7863d76f706d8869f8d8ccffe435402b56aed [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>
Matt Spinler626df172018-03-05 12:03:55 -060022#include <phosphor-logging/log.hpp>
Brad Bishop754d38c2017-09-08 00:46:58 -040023#include <thread>
Brad Bishop613a5b32017-01-05 20:58:13 -050024#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050025
Brad Bishopf4bf63a2017-08-28 15:39:19 -040026using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050027namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060028
Patrick Venture1e6324f2017-06-01 14:07:05 -070029namespace sysfs {
30
Brad Bishop754d38c2017-09-08 00:46:58 -040031static constexpr auto retryableErrors = {
32 /*
33 * Retry on bus or device errors or timeouts in case
34 * they are transient.
35 */
36 EIO,
37 ETIMEDOUT,
38
39 /*
40 * Retry CRC errors.
41 */
42 EBADMSG,
43
44 /*
45 * Some hwmon drivers do this when they aren't ready
46 * instead of blocking. Retry.
47 */
48 EAGAIN,
49 /*
50 * We'll see this when for example i2c devices are
51 * unplugged but the driver is still bound. Retry
52 * rather than exit on the off chance the device is
53 * plugged back in and the driver doesn't do a
54 * remove/probe. If a remove does occur, we'll
55 * eventually get ENOENT.
56 */
57 ENXIO,
Matt Spinler8a37c4b2017-10-04 15:53:56 -050058
59 /*
Matt Spinler67ce4a42017-11-16 13:03:16 -060060 * Some devices return this when they are busy doing
61 * something else. Even if being busy isn't the cause,
62 * a retry still gives this app a shot at getting data
63 * as opposed to failing out on the first try.
64 */
65 ENODATA,
Brad Bishop754d38c2017-09-08 00:46:58 -040066};
67
Brad Bishopf4bf63a2017-08-28 15:39:19 -040068static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050069static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
70
Brad Bishopf4bf63a2017-08-28 15:39:19 -040071std::string findPhandleMatch(
72 const std::string& iochanneldir,
73 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050074{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040075 // TODO: At the moment this method only supports device trees
76 // with iio-hwmon nodes with a single sensor. Typically
77 // device trees are defined with all the iio sensors in a
78 // single iio-hwmon node so it would be nice to add support
79 // for lists of phandles (with variable sized entries) via
80 // libfdt or something like that, so that users are not
81 // forced into implementing unusual looking device trees
82 // with multiple iio-hwmon nodes - one for each sensor.
83
84 fs::path ioChannelsPath{iochanneldir};
85 ioChannelsPath /= "io-channels";
86
87 if (!fs::exists(ioChannelsPath))
88 {
89 return emptyString;
90 }
91
92 uint32_t ioChannelsValue;
93 std::ifstream ioChannelsFile(ioChannelsPath);
94
95 ioChannelsFile.read(
96 reinterpret_cast<char*>(&ioChannelsValue),
97 sizeof(ioChannelsValue));
98
Brandon Wyman8af8a202017-05-31 18:26:30 -050099 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -0500100 {
Brandon Wyman8af8a202017-05-31 18:26:30 -0500101 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400102 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -0500103 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400104 continue;
105 }
106 std::ifstream pHandleFile(path);
107 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -0500108
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400109 pHandleFile.read(
110 reinterpret_cast<char*>(&pHandleValue),
111 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -0500112
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400113 if (ioChannelsValue == pHandleValue)
114 {
115 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500116 }
117 }
118
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400119 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500120}
121
Brad Bishop431d26a2017-08-25 09:47:58 -0400122std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -0500123{
Brad Bishop431d26a2017-08-25 09:47:58 -0400124 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
125 // /sys/devices symlink.
126 fs::path devPath{instancePath};
127 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -0500128
Brad Bishop431d26a2017-08-25 09:47:58 -0400129 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500130 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400131 devPath = fs::canonical(devPath);
132 }
133 catch (const std::system_error& e)
134 {
135 return emptyString;
136 }
137
138 // See if the device is backed by the iio-hwmon driver.
139 fs::path p{devPath};
140 p /= "driver";
141 p = fs::canonical(p);
142
143 if (p.filename() != "iio_hwmon")
144 {
145 // Not backed by iio-hwmon. The device pointed to
146 // is the callout device.
147 return devPath;
148 }
149
150 // Find the DT path to the iio-hwmon platform device.
151 fs::path ofDevPath{devPath};
152 ofDevPath /= "of_node";
153
154 try
155 {
156 ofDevPath = fs::canonical(ofDevPath);
157 }
158 catch (const std::system_error& e)
159 {
160 return emptyString;
161 }
162
163 // Search /sys/bus/iio/devices for the phandle in io-channels.
164 // If a match is found, use the corresponding /sys/devices
165 // iio device as the callout device.
166 static constexpr auto iioDevices = "/sys/bus/iio/devices";
167 for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
168 {
169 p = iioDev.path();
170 p /= "of_node";
171
172 try
173 {
174 p = fs::canonical(p);
175 }
176 catch (const std::system_error& e)
177 {
178 continue;
179 }
180
181 auto match = findPhandleMatch(ofDevPath, p);
182 auto n = match.rfind('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500183 if (n != std::string::npos)
184 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400185 // This is the iio device referred to by io-channels.
186 // Remove iio:device<N>.
187 try
188 {
189 return fs::canonical(iioDev).parent_path();
190 }
191 catch (const std::system_error& e)
192 {
193 return emptyString;
194 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500195 }
196 }
197
Brad Bishop431d26a2017-08-25 09:47:58 -0400198 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500199}
200
Matt Spinler31dbe062018-03-05 12:01:48 -0600201std::string findHwmonFromOFPath(const std::string& ofNode)
Brandon Wyman8af8a202017-05-31 18:26:30 -0500202{
203 static constexpr auto hwmonRoot = "/sys/class/hwmon";
204
205 fs::path fullOfPath{ofRoot};
206 fullOfPath /= ofNode;
207
208 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
209 {
210 auto path = hwmonInst.path();
211 path /= "of_node";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400212
213 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500214 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400215 path = fs::canonical(path);
216 }
217 catch (const std::system_error& e)
218 {
219 // realpath may encounter ENOENT (Hwmon
220 // instances have a nasty habit of
221 // going away without warning).
222 continue;
Brad Bishop613a5b32017-01-05 20:58:13 -0500223 }
224
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400225 if (path == fullOfPath)
226 {
227 return hwmonInst.path();
228 }
229
230 // Try to find HWMON instance via phandle values.
231 // Used for IIO device drivers.
232 auto matchpath = findPhandleMatch(path, fullOfPath);
233 if (!matchpath.empty())
234 {
235 return hwmonInst.path();
236 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500237 }
238
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400239 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500240}
241
Matt Spinler626df172018-03-05 12:03:55 -0600242std::string findHwmonFromDevPath(const std::string& devPath)
243{
244 fs::path p{"/sys"};
245 p /= devPath;
246 p /= "hwmon";
247
248 try
249 {
250 //This path is also used as a filesystem path to an environment
251 //file, and that has issues with ':'s in the path so they've
252 //been converted to '--'s. Convert them back now.
253 size_t pos = 0;
254 std::string path = p;
255 while ((pos = path.find("--")) != std::string::npos)
256 {
257 path.replace(pos, 2, ":");
258 }
259
260 for (const auto& hwmonInst : fs::directory_iterator(path))
261 {
262 if ((hwmonInst.path().filename().string().find("hwmon") !=
263 std::string::npos))
264 {
265 return hwmonInst.path();
266 }
267 }
268 }
269 catch (const std::exception& e)
270 {
271 using namespace phosphor::logging;
272 log<level::ERR>(
273 "Unable to find hwmon directory from the dev path",
274 entry("PATH=%s", devPath.c_str()));
275 }
276 return emptyString;
277}
278
Brad Bishop8b574a72017-08-25 16:17:19 -0400279namespace hwmonio
280{
281
282HwmonIO::HwmonIO(const std::string& path) : p(path)
283{
284
285}
286
Matt Spinlerfee106b2017-11-29 15:18:05 -0600287int64_t HwmonIO::read(
Brad Bishop8b574a72017-08-25 16:17:19 -0400288 const std::string& type,
289 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400290 const std::string& sensor,
291 size_t retries,
292 std::chrono::milliseconds delay) const
Brad Bishop8b574a72017-08-25 16:17:19 -0400293{
Matt Spinlerfee106b2017-11-29 15:18:05 -0600294 int64_t val;
Brad Bishop8b574a72017-08-25 16:17:19 -0400295 std::ifstream ifs;
296 auto fullPath = sysfs::make_sysfs_path(
297 p, type, id, sensor);
298
299 ifs.exceptions(
300 std::ifstream::failbit |
301 std::ifstream::badbit |
302 std::ifstream::eofbit);
Brad Bishop754d38c2017-09-08 00:46:58 -0400303
304 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400305 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400306 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400307 {
Matt Spinlerc8a8e012017-12-14 10:22:22 -0600308 errno = 0;
Brad Bishop754d38c2017-09-08 00:46:58 -0400309 if (!ifs.is_open())
310 ifs.open(fullPath);
311 ifs.clear();
312 ifs.seekg(0);
313 ifs >> val;
Brad Bishop8b574a72017-08-25 16:17:19 -0400314 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400315 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400316 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400317 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400318
Brad Bishop754d38c2017-09-08 00:46:58 -0400319 if (!rc)
320 {
321 throw;
322 }
323
Edward A. James26d21732017-11-30 13:17:26 -0600324 if (rc == ENOENT || rc == ENODEV)
Brad Bishop754d38c2017-09-08 00:46:58 -0400325 {
Edward A. James26d21732017-11-30 13:17:26 -0600326 // If the directory or device disappeared then this application
327 // should gracefully exit. There are race conditions between the
Brad Bishop754d38c2017-09-08 00:46:58 -0400328 // unloading of a hwmon driver and the stopping of this service
329 // by systemd. To prevent this application from falsely failing
330 // in these scenarios, it will simply exit if the directory or
331 // file can not be found. It is up to the user(s) of this
332 // provided hwmon object to log the appropriate errors if the
333 // object disappears when it should not.
334 exit(0);
335 }
336
337 if (0 == std::count(
338 retryableErrors.begin(),
339 retryableErrors.end(),
340 rc) ||
341 !retries)
342 {
343 // Not a retryable error or out of retries.
344
345 // Work around GCC bugs 53984 and 66145 for callers by
346 // explicitly raising system_error here.
347 throw std::system_error(rc, std::generic_category());
348 }
349
350 --retries;
351 std::this_thread::sleep_for(delay);
352 continue;
353 }
354 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400355 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400356
Brad Bishop8b574a72017-08-25 16:17:19 -0400357 return val;
358}
359
360void HwmonIO::write(
361 uint32_t val,
362 const std::string& type,
363 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400364 const std::string& sensor,
365 size_t retries,
366 std::chrono::milliseconds delay) const
367
Brad Bishop8b574a72017-08-25 16:17:19 -0400368{
369 std::ofstream ofs;
370 auto fullPath = sysfs::make_sysfs_path(
371 p, type, id, sensor);
372
373 ofs.exceptions(
374 std::ofstream::failbit |
375 std::ofstream::badbit |
376 std::ofstream::eofbit);
377
378 // See comments in the read method for an explanation of the odd exception
379 // handling behavior here.
380
Brad Bishop754d38c2017-09-08 00:46:58 -0400381 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400382 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400383 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400384 {
Matt Spinlerc8a8e012017-12-14 10:22:22 -0600385 errno = 0;
Brad Bishop754d38c2017-09-08 00:46:58 -0400386 if (!ofs.is_open())
387 ofs.open(fullPath);
388 ofs.clear();
389 ofs.seekp(0);
390 ofs << val;
391 ofs.flush();
Brad Bishop8b574a72017-08-25 16:17:19 -0400392 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400393 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400394 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400395 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400396
Brad Bishop754d38c2017-09-08 00:46:58 -0400397 if (!rc)
398 {
399 throw;
400 }
401
402 if (rc == ENOENT)
403 {
404 exit(0);
405 }
406
407 if (0 == std::count(
408 retryableErrors.begin(),
409 retryableErrors.end(),
410 rc) ||
411 !retries)
412 {
413 // Not a retryable error or out of retries.
414
415 // Work around GCC bugs 53984 and 66145 for callers by
416 // explicitly raising system_error here.
417 throw std::system_error(rc, std::generic_category());
418 }
419
420 --retries;
421 std::this_thread::sleep_for(delay);
422 continue;
423 }
424 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400425 }
426}
427
428std::string HwmonIO::path() const
429{
430 return p;
431}
432
433} // namespace hwmonio
Patrick Venture1e6324f2017-06-01 14:07:05 -0700434}
Brad Bishop613a5b32017-01-05 20:58:13 -0500435// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4