1613a5b37SBrad Bishop /**
2613a5b37SBrad Bishop * Copyright © 2016 IBM Corporation
3613a5b37SBrad Bishop *
4613a5b37SBrad Bishop * Licensed under the Apache License, Version 2.0 (the "License");
5613a5b37SBrad Bishop * you may not use this file except in compliance with the License.
6613a5b37SBrad Bishop * You may obtain a copy of the License at
7613a5b37SBrad Bishop *
8613a5b37SBrad Bishop * http://www.apache.org/licenses/LICENSE-2.0
9613a5b37SBrad Bishop *
10613a5b37SBrad Bishop * Unless required by applicable law or agreed to in writing, software
11613a5b37SBrad Bishop * distributed under the License is distributed on an "AS IS" BASIS,
12613a5b37SBrad Bishop * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13613a5b37SBrad Bishop * See the License for the specific language governing permissions and
14613a5b37SBrad Bishop * limitations under the License.
15613a5b37SBrad Bishop */
16043d3230SPatrick Venture #include "sysfs.hpp"
17043d3230SPatrick Venture
1864129937SPatrick Williams #include <stdplus/print.hpp>
19f9aff805SWilliam A. Kennington III
20754d38cfSBrad Bishop #include <algorithm>
218b574a7eSBrad Bishop #include <cerrno>
22613a5b37SBrad Bishop #include <cstdlib>
239e997b4dSPatrick Venture #include <filesystem>
2464129937SPatrick Williams #include <format>
2568c43b21SBrad Bishop #include <fstream>
26f9aff805SWilliam A. Kennington III #include <string>
27613a5b37SBrad Bishop
28f4bf63adSBrad Bishop using namespace std::string_literals;
299e997b4dSPatrick Venture namespace fs = std::filesystem;
308af8a200SBrandon Wyman
31043d3230SPatrick Venture namespace sysfs
32043d3230SPatrick Venture {
331e6324faSPatrick Venture
34f4bf63adSBrad Bishop static const auto emptyString = ""s;
3508379a31SBrad Bishop static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
36613a5b37SBrad Bishop
findPhandleMatch(const std::string & iochanneldir,const std::string & phandledir)37043d3230SPatrick Venture std::string findPhandleMatch(const std::string& iochanneldir,
388af8a200SBrandon Wyman const std::string& phandledir)
39613a5b37SBrad Bishop {
40f4bf63adSBrad Bishop // TODO: At the moment this method only supports device trees
41f4bf63adSBrad Bishop // with iio-hwmon nodes with a single sensor. Typically
42f4bf63adSBrad Bishop // device trees are defined with all the iio sensors in a
43f4bf63adSBrad Bishop // single iio-hwmon node so it would be nice to add support
44f4bf63adSBrad Bishop // for lists of phandles (with variable sized entries) via
45f4bf63adSBrad Bishop // libfdt or something like that, so that users are not
46f4bf63adSBrad Bishop // forced into implementing unusual looking device trees
47f4bf63adSBrad Bishop // with multiple iio-hwmon nodes - one for each sensor.
48f4bf63adSBrad Bishop
49f4bf63adSBrad Bishop fs::path ioChannelsPath{iochanneldir};
50f4bf63adSBrad Bishop ioChannelsPath /= "io-channels";
51f4bf63adSBrad Bishop
52f4bf63adSBrad Bishop if (!fs::exists(ioChannelsPath))
53f4bf63adSBrad Bishop {
54f4bf63adSBrad Bishop return emptyString;
55f4bf63adSBrad Bishop }
56f4bf63adSBrad Bishop
57f4bf63adSBrad Bishop uint32_t ioChannelsValue;
58f4bf63adSBrad Bishop std::ifstream ioChannelsFile(ioChannelsPath);
59f4bf63adSBrad Bishop
60043d3230SPatrick Venture ioChannelsFile.read(reinterpret_cast<char*>(&ioChannelsValue),
61f4bf63adSBrad Bishop sizeof(ioChannelsValue));
62f4bf63adSBrad Bishop
638af8a200SBrandon Wyman for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
64613a5b37SBrad Bishop {
658af8a200SBrandon Wyman auto path = ofInst.path();
66f4bf63adSBrad Bishop if ("phandle" != path.filename())
674eb9858dSBrandon Wyman {
68f4bf63adSBrad Bishop continue;
69f4bf63adSBrad Bishop }
70f4bf63adSBrad Bishop std::ifstream pHandleFile(path);
714eb9858dSBrandon Wyman uint32_t pHandleValue;
724eb9858dSBrandon Wyman
73043d3230SPatrick Venture pHandleFile.read(reinterpret_cast<char*>(&pHandleValue),
744eb9858dSBrandon Wyman sizeof(pHandleValue));
754eb9858dSBrandon Wyman
764eb9858dSBrandon Wyman if (ioChannelsValue == pHandleValue)
774eb9858dSBrandon Wyman {
78f4bf63adSBrad Bishop return path;
798af8a200SBrandon Wyman }
808af8a200SBrandon Wyman }
818af8a200SBrandon Wyman
82f4bf63adSBrad Bishop return emptyString;
838af8a200SBrandon Wyman }
848af8a200SBrandon Wyman
findCalloutPath(const std::string & instancePath)85431d26a5SBrad Bishop std::string findCalloutPath(const std::string& instancePath)
868af8a200SBrandon Wyman {
87431d26a5SBrad Bishop // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
88431d26a5SBrad Bishop // /sys/devices symlink.
89431d26a5SBrad Bishop fs::path devPath{instancePath};
90431d26a5SBrad Bishop devPath /= "device";
918af8a200SBrandon Wyman
92431d26a5SBrad Bishop try
938af8a200SBrandon Wyman {
94431d26a5SBrad Bishop devPath = fs::canonical(devPath);
95431d26a5SBrad Bishop }
96431d26a5SBrad Bishop catch (const std::system_error& e)
97431d26a5SBrad Bishop {
98431d26a5SBrad Bishop return emptyString;
99431d26a5SBrad Bishop }
100431d26a5SBrad Bishop
101431d26a5SBrad Bishop // See if the device is backed by the iio-hwmon driver.
102431d26a5SBrad Bishop fs::path p{devPath};
103431d26a5SBrad Bishop p /= "driver";
104431d26a5SBrad Bishop p = fs::canonical(p);
105431d26a5SBrad Bishop
106431d26a5SBrad Bishop if (p.filename() != "iio_hwmon")
107431d26a5SBrad Bishop {
108431d26a5SBrad Bishop // Not backed by iio-hwmon. The device pointed to
109431d26a5SBrad Bishop // is the callout device.
110431d26a5SBrad Bishop return devPath;
111431d26a5SBrad Bishop }
112431d26a5SBrad Bishop
113431d26a5SBrad Bishop // Find the DT path to the iio-hwmon platform device.
114431d26a5SBrad Bishop fs::path ofDevPath{devPath};
115431d26a5SBrad Bishop ofDevPath /= "of_node";
116431d26a5SBrad Bishop
117431d26a5SBrad Bishop try
118431d26a5SBrad Bishop {
119431d26a5SBrad Bishop ofDevPath = fs::canonical(ofDevPath);
120431d26a5SBrad Bishop }
121431d26a5SBrad Bishop catch (const std::system_error& e)
122431d26a5SBrad Bishop {
123431d26a5SBrad Bishop return emptyString;
124431d26a5SBrad Bishop }
125431d26a5SBrad Bishop
126431d26a5SBrad Bishop // Search /sys/bus/iio/devices for the phandle in io-channels.
127431d26a5SBrad Bishop // If a match is found, use the corresponding /sys/devices
128431d26a5SBrad Bishop // iio device as the callout device.
129431d26a5SBrad Bishop static constexpr auto iioDevices = "/sys/bus/iio/devices";
130431d26a5SBrad Bishop for (const auto& iioDev : fs::recursive_directory_iterator(iioDevices))
131431d26a5SBrad Bishop {
132431d26a5SBrad Bishop p = iioDev.path();
133431d26a5SBrad Bishop p /= "of_node";
134431d26a5SBrad Bishop
135431d26a5SBrad Bishop try
136431d26a5SBrad Bishop {
137431d26a5SBrad Bishop p = fs::canonical(p);
138431d26a5SBrad Bishop }
139431d26a5SBrad Bishop catch (const std::system_error& e)
140431d26a5SBrad Bishop {
141431d26a5SBrad Bishop continue;
142431d26a5SBrad Bishop }
143431d26a5SBrad Bishop
144431d26a5SBrad Bishop auto match = findPhandleMatch(ofDevPath, p);
145431d26a5SBrad Bishop auto n = match.rfind('/');
1468af8a200SBrandon Wyman if (n != std::string::npos)
1478af8a200SBrandon Wyman {
148431d26a5SBrad Bishop // This is the iio device referred to by io-channels.
149431d26a5SBrad Bishop // Remove iio:device<N>.
150431d26a5SBrad Bishop try
151431d26a5SBrad Bishop {
152431d26a5SBrad Bishop return fs::canonical(iioDev).parent_path();
153431d26a5SBrad Bishop }
154431d26a5SBrad Bishop catch (const std::system_error& e)
155431d26a5SBrad Bishop {
156431d26a5SBrad Bishop return emptyString;
157431d26a5SBrad Bishop }
1588af8a200SBrandon Wyman }
1598af8a200SBrandon Wyman }
1608af8a200SBrandon Wyman
161431d26a5SBrad Bishop return emptyString;
1628af8a200SBrandon Wyman }
1638af8a200SBrandon Wyman
findHwmonFromOFPath(const std::string & ofNode)1645c014d2bSMatt Spinler std::string findHwmonFromOFPath(const std::string& ofNode)
1658af8a200SBrandon Wyman {
1668af8a200SBrandon Wyman static constexpr auto hwmonRoot = "/sys/class/hwmon";
1678af8a200SBrandon Wyman
1685c014d2bSMatt Spinler auto fullOfPath = fs::path(ofRoot) / fs::path(ofNode).relative_path();
1698af8a200SBrandon Wyman
1708af8a200SBrandon Wyman for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
1718af8a200SBrandon Wyman {
1728af8a200SBrandon Wyman auto path = hwmonInst.path();
1738af8a200SBrandon Wyman path /= "of_node";
1744e24ebd6SBrad Bishop
1754e24ebd6SBrad Bishop try
1768af8a200SBrandon Wyman {
1774e24ebd6SBrad Bishop path = fs::canonical(path);
1788af8a200SBrandon Wyman }
1794e24ebd6SBrad Bishop catch (const std::system_error& e)
1808af8a200SBrandon Wyman {
1814e24ebd6SBrad Bishop // realpath may encounter ENOENT (Hwmon
1824e24ebd6SBrad Bishop // instances have a nasty habit of
1834e24ebd6SBrad Bishop // going away without warning).
1848af8a200SBrandon Wyman continue;
1858af8a200SBrandon Wyman }
1868af8a200SBrandon Wyman
1874e24ebd6SBrad Bishop if (path == fullOfPath)
1884e24ebd6SBrad Bishop {
18908379a31SBrad Bishop return hwmonInst.path();
190613a5b37SBrad Bishop }
191613a5b37SBrad Bishop
1924e24ebd6SBrad Bishop // Try to find HWMON instance via phandle values.
1934e24ebd6SBrad Bishop // Used for IIO device drivers.
1944e24ebd6SBrad Bishop auto matchpath = findPhandleMatch(path, fullOfPath);
1954e24ebd6SBrad Bishop if (!matchpath.empty())
1964e24ebd6SBrad Bishop {
1974e24ebd6SBrad Bishop return hwmonInst.path();
1984e24ebd6SBrad Bishop }
1994e24ebd6SBrad Bishop }
2004e24ebd6SBrad Bishop
2014e24ebd6SBrad Bishop return emptyString;
202613a5b37SBrad Bishop }
203613a5b37SBrad Bishop
findHwmonFromDevPath(const std::string & devPath)2045c014d2bSMatt Spinler std::string findHwmonFromDevPath(const std::string& devPath)
205626df17aSMatt Spinler {
206626df17aSMatt Spinler fs::path p{"/sys"};
2075c014d2bSMatt Spinler p /= fs::path(devPath).relative_path();
208626df17aSMatt Spinler p /= "hwmon";
209626df17aSMatt Spinler
210626df17aSMatt Spinler try
211626df17aSMatt Spinler {
212626df17aSMatt Spinler // This path is also used as a filesystem path to an environment
213626df17aSMatt Spinler // file, and that has issues with ':'s in the path so they've
214626df17aSMatt Spinler // been converted to '--'s. Convert them back now.
215626df17aSMatt Spinler size_t pos = 0;
216626df17aSMatt Spinler std::string path = p;
217626df17aSMatt Spinler while ((pos = path.find("--")) != std::string::npos)
218626df17aSMatt Spinler {
219626df17aSMatt Spinler path.replace(pos, 2, ":");
220626df17aSMatt Spinler }
221626df17aSMatt Spinler
22209560174SPatrick Venture auto dir_iter = fs::directory_iterator(path);
223*02e598abSPatrick Williams auto hwmonInst = std::find_if(
224*02e598abSPatrick Williams dir_iter, end(dir_iter), [](const fs::directory_entry& d) {
22509560174SPatrick Venture return (d.path().filename().string().find("hwmon") !=
22609560174SPatrick Venture std::string::npos);
22709560174SPatrick Venture });
22809560174SPatrick Venture if (hwmonInst != end(dir_iter))
229626df17aSMatt Spinler {
23009560174SPatrick Venture return hwmonInst->path();
231626df17aSMatt Spinler }
232626df17aSMatt Spinler }
233626df17aSMatt Spinler catch (const std::exception& e)
234626df17aSMatt Spinler {
23564129937SPatrick Williams stdplus::print(stderr,
236f9aff805SWilliam A. Kennington III "Unable to find hwmon directory from the dev path: {}\n",
237f9aff805SWilliam A. Kennington III devPath.c_str());
238626df17aSMatt Spinler }
239626df17aSMatt Spinler return emptyString;
240626df17aSMatt Spinler }
241626df17aSMatt Spinler
24275e56c67SPatrick Venture } // namespace sysfs
243