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