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