1 /** 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 */ 16 #include "sysfs.hpp" 17 18 #include <stdplus/print.hpp> 19 20 #include <algorithm> 21 #include <cerrno> 22 #include <cstdlib> 23 #include <filesystem> 24 #include <format> 25 #include <fstream> 26 #include <string> 27 28 using namespace std::string_literals; 29 namespace fs = std::filesystem; 30 31 namespace sysfs 32 { 33 34 static const auto emptyString = ""s; 35 static constexpr auto ofRoot = "/sys/firmware/devicetree/base"; 36 37 std::string findPhandleMatch(const std::string& iochanneldir, 38 const std::string& phandledir) 39 { 40 // TODO: At the moment this method only supports device trees 41 // with iio-hwmon nodes with a single sensor. Typically 42 // device trees are defined with all the iio sensors in a 43 // single iio-hwmon node so it would be nice to add support 44 // for lists of phandles (with variable sized entries) via 45 // libfdt or something like that, so that users are not 46 // forced into implementing unusual looking device trees 47 // with multiple iio-hwmon nodes - one for each sensor. 48 49 fs::path ioChannelsPath{iochanneldir}; 50 ioChannelsPath /= "io-channels"; 51 52 if (!fs::exists(ioChannelsPath)) 53 { 54 return emptyString; 55 } 56 57 uint32_t ioChannelsValue; 58 std::ifstream ioChannelsFile(ioChannelsPath); 59 60 ioChannelsFile.read(reinterpret_cast<char*>(&ioChannelsValue), 61 sizeof(ioChannelsValue)); 62 63 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir)) 64 { 65 auto path = ofInst.path(); 66 if ("phandle" != path.filename()) 67 { 68 continue; 69 } 70 std::ifstream pHandleFile(path); 71 uint32_t pHandleValue; 72 73 pHandleFile.read(reinterpret_cast<char*>(&pHandleValue), 74 sizeof(pHandleValue)); 75 76 if (ioChannelsValue == pHandleValue) 77 { 78 return path; 79 } 80 } 81 82 return emptyString; 83 } 84 85 std::string findCalloutPath(const std::string& instancePath) 86 { 87 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>) 88 // /sys/devices symlink. 89 fs::path devPath{instancePath}; 90 devPath /= "device"; 91 92 try 93 { 94 devPath = fs::canonical(devPath); 95 } 96 catch (const std::system_error& e) 97 { 98 return emptyString; 99 } 100 101 // See if the device is backed by the iio-hwmon driver. 102 fs::path p{devPath}; 103 p /= "driver"; 104 p = fs::canonical(p); 105 106 if (p.filename() != "iio_hwmon") 107 { 108 // Not backed by iio-hwmon. The device pointed to 109 // is the callout device. 110 return devPath; 111 } 112 113 // Find the DT path to the iio-hwmon platform device. 114 fs::path ofDevPath{devPath}; 115 ofDevPath /= "of_node"; 116 117 try 118 { 119 ofDevPath = fs::canonical(ofDevPath); 120 } 121 catch (const std::system_error& e) 122 { 123 return emptyString; 124 } 125 126 // Search /sys/bus/iio/devices for the phandle in io-channels. 127 // If a match is found, use the corresponding /sys/devices 128 // iio device as the callout device. 129 static constexpr auto iioDevices = "/sys/bus/iio/devices"; 130 for (const auto& iioDev : fs::recursive_directory_iterator(iioDevices)) 131 { 132 p = iioDev.path(); 133 p /= "of_node"; 134 135 try 136 { 137 p = fs::canonical(p); 138 } 139 catch (const std::system_error& e) 140 { 141 continue; 142 } 143 144 auto match = findPhandleMatch(ofDevPath, p); 145 auto n = match.rfind('/'); 146 if (n != std::string::npos) 147 { 148 // This is the iio device referred to by io-channels. 149 // Remove iio:device<N>. 150 try 151 { 152 return fs::canonical(iioDev).parent_path(); 153 } 154 catch (const std::system_error& e) 155 { 156 return emptyString; 157 } 158 } 159 } 160 161 return emptyString; 162 } 163 164 std::string findHwmonFromOFPath(const std::string& ofNode) 165 { 166 static constexpr auto hwmonRoot = "/sys/class/hwmon"; 167 168 auto fullOfPath = fs::path(ofRoot) / fs::path(ofNode).relative_path(); 169 170 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot)) 171 { 172 auto path = hwmonInst.path(); 173 path /= "of_node"; 174 175 try 176 { 177 path = fs::canonical(path); 178 } 179 catch (const std::system_error& e) 180 { 181 // realpath may encounter ENOENT (Hwmon 182 // instances have a nasty habit of 183 // going away without warning). 184 continue; 185 } 186 187 if (path == fullOfPath) 188 { 189 return hwmonInst.path(); 190 } 191 192 // Try to find HWMON instance via phandle values. 193 // Used for IIO device drivers. 194 auto matchpath = findPhandleMatch(path, fullOfPath); 195 if (!matchpath.empty()) 196 { 197 return hwmonInst.path(); 198 } 199 } 200 201 return emptyString; 202 } 203 204 std::string findHwmonFromDevPath(const std::string& devPath) 205 { 206 fs::path p{"/sys"}; 207 p /= fs::path(devPath).relative_path(); 208 p /= "hwmon"; 209 210 try 211 { 212 // This path is also used as a filesystem path to an environment 213 // file, and that has issues with ':'s in the path so they've 214 // been converted to '--'s. Convert them back now. 215 size_t pos = 0; 216 std::string path = p; 217 while ((pos = path.find("--")) != std::string::npos) 218 { 219 path.replace(pos, 2, ":"); 220 } 221 222 auto dir_iter = fs::directory_iterator(path); 223 auto hwmonInst = std::find_if( 224 dir_iter, end(dir_iter), [](const fs::directory_entry& d) { 225 return (d.path().filename().string().find("hwmon") != 226 std::string::npos); 227 }); 228 if (hwmonInst != end(dir_iter)) 229 { 230 return hwmonInst->path(); 231 } 232 } 233 catch (const std::exception& e) 234 { 235 stdplus::print(stderr, 236 "Unable to find hwmon directory from the dev path: {}\n", 237 devPath.c_str()); 238 } 239 return emptyString; 240 } 241 242 } // namespace sysfs 243 244 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 245