blob: 5c7dbeeec47ec7aa0950143f3eec88c67df4410a [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>
Patrick Venturec1cece72017-11-07 12:09:49 -080024#include "config.h"
Brad Bishop613a5b32017-01-05 20:58:13 -050025#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050026
Brad Bishopf4bf63a2017-08-28 15:39:19 -040027using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050028namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060029
Patrick Venture1e6324f2017-06-01 14:07:05 -070030namespace sysfs {
31
Brad Bishop754d38c2017-09-08 00:46:58 -040032static constexpr auto retryableErrors = {
33 /*
34 * Retry on bus or device errors or timeouts in case
35 * they are transient.
36 */
37 EIO,
38 ETIMEDOUT,
39
40 /*
41 * Retry CRC errors.
42 */
43 EBADMSG,
44
45 /*
46 * Some hwmon drivers do this when they aren't ready
47 * instead of blocking. Retry.
48 */
49 EAGAIN,
50 /*
51 * We'll see this when for example i2c devices are
52 * unplugged but the driver is still bound. Retry
53 * rather than exit on the off chance the device is
54 * plugged back in and the driver doesn't do a
55 * remove/probe. If a remove does occur, we'll
56 * eventually get ENOENT.
57 */
58 ENXIO,
Matt Spinler8a37c4b2017-10-04 15:53:56 -050059
60 /*
Matt Spinler67ce4a42017-11-16 13:03:16 -060061 * Some devices return this when they are busy doing
62 * something else. Even if being busy isn't the cause,
63 * a retry still gives this app a shot at getting data
64 * as opposed to failing out on the first try.
65 */
66 ENODATA,
Brad Bishop754d38c2017-09-08 00:46:58 -040067};
68
Brad Bishopf4bf63a2017-08-28 15:39:19 -040069static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050070static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
71
Brad Bishopf4bf63a2017-08-28 15:39:19 -040072std::string findPhandleMatch(
73 const std::string& iochanneldir,
74 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050075{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040076 // TODO: At the moment this method only supports device trees
77 // with iio-hwmon nodes with a single sensor. Typically
78 // device trees are defined with all the iio sensors in a
79 // single iio-hwmon node so it would be nice to add support
80 // for lists of phandles (with variable sized entries) via
81 // libfdt or something like that, so that users are not
82 // forced into implementing unusual looking device trees
83 // with multiple iio-hwmon nodes - one for each sensor.
84
85 fs::path ioChannelsPath{iochanneldir};
86 ioChannelsPath /= "io-channels";
87
88 if (!fs::exists(ioChannelsPath))
89 {
90 return emptyString;
91 }
92
93 uint32_t ioChannelsValue;
94 std::ifstream ioChannelsFile(ioChannelsPath);
95
96 ioChannelsFile.read(
97 reinterpret_cast<char*>(&ioChannelsValue),
98 sizeof(ioChannelsValue));
99
Brandon Wyman8af8a202017-05-31 18:26:30 -0500100 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -0500101 {
Brandon Wyman8af8a202017-05-31 18:26:30 -0500102 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400103 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -0500104 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400105 continue;
106 }
107 std::ifstream pHandleFile(path);
108 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -0500109
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400110 pHandleFile.read(
111 reinterpret_cast<char*>(&pHandleValue),
112 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -0500113
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400114 if (ioChannelsValue == pHandleValue)
115 {
116 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500117 }
118 }
119
Brad Bishopf4bf63a2017-08-28 15:39:19 -0400120 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500121}
122
Brad Bishop431d26a2017-08-25 09:47:58 -0400123std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -0500124{
Brad Bishop431d26a2017-08-25 09:47:58 -0400125 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
126 // /sys/devices symlink.
127 fs::path devPath{instancePath};
128 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -0500129
Brad Bishop431d26a2017-08-25 09:47:58 -0400130 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500131 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400132 devPath = fs::canonical(devPath);
133 }
134 catch (const std::system_error& e)
135 {
136 return emptyString;
137 }
138
139 // See if the device is backed by the iio-hwmon driver.
140 fs::path p{devPath};
141 p /= "driver";
142 p = fs::canonical(p);
143
144 if (p.filename() != "iio_hwmon")
145 {
146 // Not backed by iio-hwmon. The device pointed to
147 // is the callout device.
148 return devPath;
149 }
150
151 // Find the DT path to the iio-hwmon platform device.
152 fs::path ofDevPath{devPath};
153 ofDevPath /= "of_node";
154
155 try
156 {
157 ofDevPath = fs::canonical(ofDevPath);
158 }
159 catch (const std::system_error& e)
160 {
161 return emptyString;
162 }
163
164 // Search /sys/bus/iio/devices for the phandle in io-channels.
165 // If a match is found, use the corresponding /sys/devices
166 // iio device as the callout device.
167 static constexpr auto iioDevices = "/sys/bus/iio/devices";
168 for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
169 {
170 p = iioDev.path();
171 p /= "of_node";
172
173 try
174 {
175 p = fs::canonical(p);
176 }
177 catch (const std::system_error& e)
178 {
179 continue;
180 }
181
182 auto match = findPhandleMatch(ofDevPath, p);
183 auto n = match.rfind('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500184 if (n != std::string::npos)
185 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400186 // This is the iio device referred to by io-channels.
187 // Remove iio:device<N>.
188 try
189 {
190 return fs::canonical(iioDev).parent_path();
191 }
192 catch (const std::system_error& e)
193 {
194 return emptyString;
195 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500196 }
197 }
198
Brad Bishop431d26a2017-08-25 09:47:58 -0400199 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500200}
201
Matt Spinler31dbe062018-03-05 12:01:48 -0600202std::string findHwmonFromOFPath(const std::string& ofNode)
Brandon Wyman8af8a202017-05-31 18:26:30 -0500203{
204 static constexpr auto hwmonRoot = "/sys/class/hwmon";
205
206 fs::path fullOfPath{ofRoot};
207 fullOfPath /= ofNode;
208
209 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
210 {
211 auto path = hwmonInst.path();
212 path /= "of_node";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400213
214 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500215 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400216 path = fs::canonical(path);
217 }
218 catch (const std::system_error& e)
219 {
220 // realpath may encounter ENOENT (Hwmon
221 // instances have a nasty habit of
222 // going away without warning).
223 continue;
Brad Bishop613a5b32017-01-05 20:58:13 -0500224 }
225
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400226 if (path == fullOfPath)
227 {
228 return hwmonInst.path();
229 }
230
231 // Try to find HWMON instance via phandle values.
232 // Used for IIO device drivers.
233 auto matchpath = findPhandleMatch(path, fullOfPath);
234 if (!matchpath.empty())
235 {
236 return hwmonInst.path();
237 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500238 }
239
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400240 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500241}
242
Matt Spinler626df172018-03-05 12:03:55 -0600243std::string findHwmonFromDevPath(const std::string& devPath)
244{
245 fs::path p{"/sys"};
246 p /= devPath;
247 p /= "hwmon";
248
249 try
250 {
251 //This path is also used as a filesystem path to an environment
252 //file, and that has issues with ':'s in the path so they've
253 //been converted to '--'s. Convert them back now.
254 size_t pos = 0;
255 std::string path = p;
256 while ((pos = path.find("--")) != std::string::npos)
257 {
258 path.replace(pos, 2, ":");
259 }
260
261 for (const auto& hwmonInst : fs::directory_iterator(path))
262 {
263 if ((hwmonInst.path().filename().string().find("hwmon") !=
264 std::string::npos))
265 {
266 return hwmonInst.path();
267 }
268 }
269 }
270 catch (const std::exception& e)
271 {
272 using namespace phosphor::logging;
273 log<level::ERR>(
274 "Unable to find hwmon directory from the dev path",
275 entry("PATH=%s", devPath.c_str()));
276 }
277 return emptyString;
278}
279
Brad Bishop8b574a72017-08-25 16:17:19 -0400280namespace hwmonio
281{
282
283HwmonIO::HwmonIO(const std::string& path) : p(path)
284{
285
286}
287
Matt Spinlerfee106b2017-11-29 15:18:05 -0600288int64_t HwmonIO::read(
Brad Bishop8b574a72017-08-25 16:17:19 -0400289 const std::string& type,
290 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400291 const std::string& sensor,
292 size_t retries,
Matthew Bartha23babd2018-03-16 10:03:27 -0500293 std::chrono::milliseconds delay,
294 bool isOCC) const
Brad Bishop8b574a72017-08-25 16:17:19 -0400295{
Matt Spinlerfee106b2017-11-29 15:18:05 -0600296 int64_t val;
Brad Bishop8b574a72017-08-25 16:17:19 -0400297 std::ifstream ifs;
298 auto fullPath = sysfs::make_sysfs_path(
299 p, type, id, sensor);
300
301 ifs.exceptions(
302 std::ifstream::failbit |
303 std::ifstream::badbit |
304 std::ifstream::eofbit);
Brad Bishop754d38c2017-09-08 00:46:58 -0400305
306 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400307 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400308 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400309 {
Matt Spinlerc8a8e012017-12-14 10:22:22 -0600310 errno = 0;
Brad Bishop754d38c2017-09-08 00:46:58 -0400311 if (!ifs.is_open())
312 ifs.open(fullPath);
313 ifs.clear();
314 ifs.seekg(0);
315 ifs >> val;
Brad Bishop8b574a72017-08-25 16:17:19 -0400316 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400317 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400318 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400319 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400320
Brad Bishop754d38c2017-09-08 00:46:58 -0400321 if (!rc)
322 {
323 throw;
324 }
325
Edward A. James26d21732017-11-30 13:17:26 -0600326 if (rc == ENOENT || rc == ENODEV)
Brad Bishop754d38c2017-09-08 00:46:58 -0400327 {
Edward A. James26d21732017-11-30 13:17:26 -0600328 // If the directory or device disappeared then this application
329 // should gracefully exit. There are race conditions between the
Brad Bishop754d38c2017-09-08 00:46:58 -0400330 // unloading of a hwmon driver and the stopping of this service
331 // by systemd. To prevent this application from falsely failing
332 // in these scenarios, it will simply exit if the directory or
333 // file can not be found. It is up to the user(s) of this
334 // provided hwmon object to log the appropriate errors if the
335 // object disappears when it should not.
336 exit(0);
337 }
338
Matthew Bartha23babd2018-03-16 10:03:27 -0500339 if (isOCC)
340 {
341 if (rc == EAGAIN)
342 {
343 // For the OCCs, when an EAGAIN is return, just set the
344 // value to 0 (0x00 = sensor is unavailable)
345 val = 0;
346 break;
347 }
348 else if (rc == EREMOTEIO)
349 {
350 // For the OCCs, when an EREMOTEIO is return, set the
351 // value to 255*1000
352 // (0xFF = sensor is failed, 1000 = sensor factor)
353 val = 255000;
354 break;
355 }
356 }
357
Brad Bishop754d38c2017-09-08 00:46:58 -0400358 if (0 == std::count(
359 retryableErrors.begin(),
360 retryableErrors.end(),
361 rc) ||
362 !retries)
363 {
364 // Not a retryable error or out of retries.
Patrick Venturec1cece72017-11-07 12:09:49 -0800365#ifdef NEGATIVE_ERRNO_ON_FAIL
366 return -rc;
367#endif
Brad Bishop754d38c2017-09-08 00:46:58 -0400368
369 // Work around GCC bugs 53984 and 66145 for callers by
370 // explicitly raising system_error here.
371 throw std::system_error(rc, std::generic_category());
372 }
373
374 --retries;
375 std::this_thread::sleep_for(delay);
376 continue;
377 }
378 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400379 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400380
Brad Bishop8b574a72017-08-25 16:17:19 -0400381 return val;
382}
383
384void HwmonIO::write(
385 uint32_t val,
386 const std::string& type,
387 const std::string& id,
Brad Bishop754d38c2017-09-08 00:46:58 -0400388 const std::string& sensor,
389 size_t retries,
390 std::chrono::milliseconds delay) const
391
Brad Bishop8b574a72017-08-25 16:17:19 -0400392{
393 std::ofstream ofs;
394 auto fullPath = sysfs::make_sysfs_path(
395 p, type, id, sensor);
396
397 ofs.exceptions(
398 std::ofstream::failbit |
399 std::ofstream::badbit |
400 std::ofstream::eofbit);
401
402 // See comments in the read method for an explanation of the odd exception
403 // handling behavior here.
404
Brad Bishop754d38c2017-09-08 00:46:58 -0400405 while (true)
Brad Bishop8b574a72017-08-25 16:17:19 -0400406 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400407 try
Brad Bishop8b574a72017-08-25 16:17:19 -0400408 {
Matt Spinlerc8a8e012017-12-14 10:22:22 -0600409 errno = 0;
Brad Bishop754d38c2017-09-08 00:46:58 -0400410 if (!ofs.is_open())
411 ofs.open(fullPath);
412 ofs.clear();
413 ofs.seekp(0);
414 ofs << val;
415 ofs.flush();
Brad Bishop8b574a72017-08-25 16:17:19 -0400416 }
Brad Bishop754d38c2017-09-08 00:46:58 -0400417 catch (const std::exception& e)
Brad Bishop8b574a72017-08-25 16:17:19 -0400418 {
Brad Bishop754d38c2017-09-08 00:46:58 -0400419 auto rc = errno;
Brad Bishop8b574a72017-08-25 16:17:19 -0400420
Brad Bishop754d38c2017-09-08 00:46:58 -0400421 if (!rc)
422 {
423 throw;
424 }
425
426 if (rc == ENOENT)
427 {
428 exit(0);
429 }
430
431 if (0 == std::count(
432 retryableErrors.begin(),
433 retryableErrors.end(),
434 rc) ||
435 !retries)
436 {
437 // Not a retryable error or out of retries.
438
439 // Work around GCC bugs 53984 and 66145 for callers by
440 // explicitly raising system_error here.
441 throw std::system_error(rc, std::generic_category());
442 }
443
444 --retries;
445 std::this_thread::sleep_for(delay);
446 continue;
447 }
448 break;
Brad Bishop8b574a72017-08-25 16:17:19 -0400449 }
450}
451
452std::string HwmonIO::path() const
453{
454 return p;
455}
456
457} // namespace hwmonio
Patrick Venture1e6324f2017-06-01 14:07:05 -0700458}
Brad Bishop613a5b32017-01-05 20:58:13 -0500459// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4