175e56c67SPatrick Venture /**
275e56c67SPatrick Venture * Licensed under the Apache License, Version 2.0 (the "License");
375e56c67SPatrick Venture * you may not use this file except in compliance with the License.
475e56c67SPatrick Venture * You may obtain a copy of the License at
575e56c67SPatrick Venture *
675e56c67SPatrick Venture * http://www.apache.org/licenses/LICENSE-2.0
775e56c67SPatrick Venture *
875e56c67SPatrick Venture * Unless required by applicable law or agreed to in writing, software
975e56c67SPatrick Venture * distributed under the License is distributed on an "AS IS" BASIS,
1075e56c67SPatrick Venture * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1175e56c67SPatrick Venture * See the License for the specific language governing permissions and
1275e56c67SPatrick Venture * limitations under the License.
1375e56c67SPatrick Venture */
14043d3230SPatrick Venture #include "config.h"
15043d3230SPatrick Venture
16043d3230SPatrick Venture #include "hwmonio.hpp"
17043d3230SPatrick Venture
18043d3230SPatrick Venture #include "sysfs.hpp"
19043d3230SPatrick Venture
2075e56c67SPatrick Venture #include <algorithm>
2175e56c67SPatrick Venture #include <exception>
2275e56c67SPatrick Venture #include <fstream>
2375e56c67SPatrick Venture #include <thread>
2475e56c67SPatrick Venture
25043d3230SPatrick Venture namespace hwmonio
26043d3230SPatrick Venture {
2775e56c67SPatrick Venture
read(const std::string & path) const28caaebd1fSPatrick Venture int64_t FileSystem::read(const std::string& path) const
29caaebd1fSPatrick Venture {
30caaebd1fSPatrick Venture int64_t val;
31caaebd1fSPatrick Venture std::ifstream ifs;
32caaebd1fSPatrick Venture ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
33caaebd1fSPatrick Venture std::ifstream::eofbit);
34caaebd1fSPatrick Venture
35caaebd1fSPatrick Venture errno = 0;
36caaebd1fSPatrick Venture if (!ifs.is_open())
37caaebd1fSPatrick Venture ifs.open(path);
38caaebd1fSPatrick Venture ifs.clear();
39caaebd1fSPatrick Venture ifs.seekg(0);
40caaebd1fSPatrick Venture ifs >> val;
41caaebd1fSPatrick Venture
42caaebd1fSPatrick Venture return val;
43caaebd1fSPatrick Venture }
44caaebd1fSPatrick Venture
write(const std::string & path,uint32_t value) const45caaebd1fSPatrick Venture void FileSystem::write(const std::string& path, uint32_t value) const
46caaebd1fSPatrick Venture {
47caaebd1fSPatrick Venture std::ofstream ofs;
48caaebd1fSPatrick Venture ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit |
49caaebd1fSPatrick Venture std::ofstream::eofbit);
50caaebd1fSPatrick Venture
51caaebd1fSPatrick Venture errno = 0;
52caaebd1fSPatrick Venture if (!ofs.is_open())
53caaebd1fSPatrick Venture ofs.open(path);
54caaebd1fSPatrick Venture ofs.clear();
55caaebd1fSPatrick Venture ofs.seekp(0);
56caaebd1fSPatrick Venture ofs << value;
57caaebd1fSPatrick Venture ofs.flush();
58caaebd1fSPatrick Venture }
59caaebd1fSPatrick Venture
60caaebd1fSPatrick Venture FileSystem fileSystemImpl;
61caaebd1fSPatrick Venture
6275e56c67SPatrick Venture static constexpr auto retryableErrors = {
6375e56c67SPatrick Venture /*
6475e56c67SPatrick Venture * Retry on bus or device errors or timeouts in case
6575e56c67SPatrick Venture * they are transient.
6675e56c67SPatrick Venture */
6775e56c67SPatrick Venture EIO,
6875e56c67SPatrick Venture ETIMEDOUT,
6975e56c67SPatrick Venture
7075e56c67SPatrick Venture /*
7175e56c67SPatrick Venture * Retry CRC errors.
7275e56c67SPatrick Venture */
7375e56c67SPatrick Venture EBADMSG,
7475e56c67SPatrick Venture
7575e56c67SPatrick Venture /*
7675e56c67SPatrick Venture * Some hwmon drivers do this when they aren't ready
7775e56c67SPatrick Venture * instead of blocking. Retry.
7875e56c67SPatrick Venture */
7975e56c67SPatrick Venture EAGAIN,
8075e56c67SPatrick Venture /*
8175e56c67SPatrick Venture * We'll see this when for example i2c devices are
8275e56c67SPatrick Venture * unplugged but the driver is still bound. Retry
8375e56c67SPatrick Venture * rather than exit on the off chance the device is
8475e56c67SPatrick Venture * plugged back in and the driver doesn't do a
8575e56c67SPatrick Venture * remove/probe. If a remove does occur, we'll
8675e56c67SPatrick Venture * eventually get ENOENT.
8775e56c67SPatrick Venture */
8875e56c67SPatrick Venture ENXIO,
8975e56c67SPatrick Venture
9075e56c67SPatrick Venture /*
9175e56c67SPatrick Venture * Some devices return this when they are busy doing
9275e56c67SPatrick Venture * something else. Even if being busy isn't the cause,
9375e56c67SPatrick Venture * a retry still gives this app a shot at getting data
9475e56c67SPatrick Venture * as opposed to failing out on the first try.
9575e56c67SPatrick Venture */
9675e56c67SPatrick Venture ENODATA,
97e289100eSEddie James
98e289100eSEddie James /*
99e289100eSEddie James * Some devices return this if the hardware is being
100e289100eSEddie James * powered off in a normal manner, as incomplete data
101e289100eSEddie James * is received. Retrying allows time for the system to
102e289100eSEddie James * clean up driver devices, or in the event of a real
103e289100eSEddie James * failure, attempt to get the rest of the data.
104e289100eSEddie James */
105e289100eSEddie James EMSGSIZE,
10675e56c67SPatrick Venture };
10775e56c67SPatrick Venture
HwmonIO(const std::string & path,const FileSystemInterface * intf)108caaebd1fSPatrick Venture HwmonIO::HwmonIO(const std::string& path, const FileSystemInterface* intf) :
109caaebd1fSPatrick Venture _p(path), _intf(intf)
110*e8771fd4SPatrick Williams {}
11175e56c67SPatrick Venture
read(const std::string & type,const std::string & id,const std::string & sensor,size_t retries,std::chrono::milliseconds delay) const112043d3230SPatrick Venture int64_t HwmonIO::read(const std::string& type, const std::string& id,
113043d3230SPatrick Venture const std::string& sensor, size_t retries,
1140b305058SMatthew Barth std::chrono::milliseconds delay) const
11575e56c67SPatrick Venture {
11675e56c67SPatrick Venture int64_t val;
1174d9506ccSPatrick Venture auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
11875e56c67SPatrick Venture
11975e56c67SPatrick Venture while (true)
12075e56c67SPatrick Venture {
12175e56c67SPatrick Venture try
12275e56c67SPatrick Venture {
123caaebd1fSPatrick Venture val = _intf->read(fullPath);
12475e56c67SPatrick Venture }
12575e56c67SPatrick Venture catch (const std::exception& e)
12675e56c67SPatrick Venture {
12775e56c67SPatrick Venture auto rc = errno;
12875e56c67SPatrick Venture
12975e56c67SPatrick Venture if (!rc)
13075e56c67SPatrick Venture {
13175e56c67SPatrick Venture throw;
13275e56c67SPatrick Venture }
13375e56c67SPatrick Venture
13475e56c67SPatrick Venture if (rc == ENOENT || rc == ENODEV)
13575e56c67SPatrick Venture {
13675e56c67SPatrick Venture // If the directory or device disappeared then this application
137043d3230SPatrick Venture // should gracefully exit. There are race conditions between
138043d3230SPatrick Venture // the unloading of a hwmon driver and the stopping of this
139043d3230SPatrick Venture // service by systemd. To prevent this application from falsely
140043d3230SPatrick Venture // failing in these scenarios, it will simply exit if the
141043d3230SPatrick Venture // directory or file can not be found. It is up to the user(s)
142043d3230SPatrick Venture // of this provided hwmon object to log the appropriate errors
143043d3230SPatrick Venture // if the object disappears when it should not.
14475e56c67SPatrick Venture exit(0);
14575e56c67SPatrick Venture }
14675e56c67SPatrick Venture
147043d3230SPatrick Venture if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
14875e56c67SPatrick Venture rc) ||
14975e56c67SPatrick Venture !retries)
15075e56c67SPatrick Venture {
15175e56c67SPatrick Venture // Not a retryable error or out of retries.
152d8cacfd4SMatt Spinler #if NEGATIVE_ERRNO_ON_FAIL
15375e56c67SPatrick Venture return -rc;
15475e56c67SPatrick Venture #endif
15575e56c67SPatrick Venture
15675e56c67SPatrick Venture // Work around GCC bugs 53984 and 66145 for callers by
15775e56c67SPatrick Venture // explicitly raising system_error here.
15875e56c67SPatrick Venture throw std::system_error(rc, std::generic_category());
15975e56c67SPatrick Venture }
16075e56c67SPatrick Venture
16175e56c67SPatrick Venture --retries;
16275e56c67SPatrick Venture std::this_thread::sleep_for(delay);
16375e56c67SPatrick Venture continue;
16475e56c67SPatrick Venture }
16575e56c67SPatrick Venture break;
16675e56c67SPatrick Venture }
16775e56c67SPatrick Venture
16875e56c67SPatrick Venture return val;
16975e56c67SPatrick Venture }
17075e56c67SPatrick Venture
write(uint32_t val,const std::string & type,const std::string & id,const std::string & sensor,size_t retries,std::chrono::milliseconds delay) const171043d3230SPatrick Venture void HwmonIO::write(uint32_t val, const std::string& type,
172043d3230SPatrick Venture const std::string& id, const std::string& sensor,
173043d3230SPatrick Venture size_t retries, std::chrono::milliseconds delay) const
17475e56c67SPatrick Venture
17575e56c67SPatrick Venture {
1764d9506ccSPatrick Venture auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
17775e56c67SPatrick Venture
17875e56c67SPatrick Venture // See comments in the read method for an explanation of the odd exception
17975e56c67SPatrick Venture // handling behavior here.
18075e56c67SPatrick Venture
18175e56c67SPatrick Venture while (true)
18275e56c67SPatrick Venture {
18375e56c67SPatrick Venture try
18475e56c67SPatrick Venture {
185caaebd1fSPatrick Venture _intf->write(fullPath, val);
18675e56c67SPatrick Venture }
18775e56c67SPatrick Venture catch (const std::exception& e)
18875e56c67SPatrick Venture {
18975e56c67SPatrick Venture auto rc = errno;
19075e56c67SPatrick Venture
19175e56c67SPatrick Venture if (!rc)
19275e56c67SPatrick Venture {
19375e56c67SPatrick Venture throw;
19475e56c67SPatrick Venture }
19575e56c67SPatrick Venture
19675e56c67SPatrick Venture if (rc == ENOENT)
19775e56c67SPatrick Venture {
19875e56c67SPatrick Venture exit(0);
19975e56c67SPatrick Venture }
20075e56c67SPatrick Venture
201043d3230SPatrick Venture if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
20275e56c67SPatrick Venture rc) ||
20375e56c67SPatrick Venture !retries)
20475e56c67SPatrick Venture {
20575e56c67SPatrick Venture // Not a retryable error or out of retries.
20675e56c67SPatrick Venture
20775e56c67SPatrick Venture // Work around GCC bugs 53984 and 66145 for callers by
20875e56c67SPatrick Venture // explicitly raising system_error here.
20975e56c67SPatrick Venture throw std::system_error(rc, std::generic_category());
21075e56c67SPatrick Venture }
21175e56c67SPatrick Venture
21275e56c67SPatrick Venture --retries;
21375e56c67SPatrick Venture std::this_thread::sleep_for(delay);
21475e56c67SPatrick Venture continue;
21575e56c67SPatrick Venture }
21675e56c67SPatrick Venture break;
21775e56c67SPatrick Venture }
21875e56c67SPatrick Venture }
21975e56c67SPatrick Venture
path() const22075e56c67SPatrick Venture std::string HwmonIO::path() const
22175e56c67SPatrick Venture {
2224d9506ccSPatrick Venture return _p;
22375e56c67SPatrick Venture }
22475e56c67SPatrick Venture
225043d3230SPatrick Venture } // namespace hwmonio
226