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 18*64129937SPatrick 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> 24*64129937SPatrick 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 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 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 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 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); 223e8771fd4SPatrick Williams auto hwmonInst = std::find_if(dir_iter, end(dir_iter), 224e8771fd4SPatrick Williams [](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 { 235*64129937SPatrick 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 2438b574a7eSBrad Bishop 244613a5b37SBrad Bishop // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 245