xref: /openbmc/phosphor-hwmon/sysfs.cpp (revision 75e56c67a10e9f4c617f9c72a87deb695322e212)
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 <algorithm>
17 #include <cerrno>
18 #include <cstdlib>
19 #include <experimental/filesystem>
20 #include <fstream>
21 #include <memory>
22 #include <phosphor-logging/log.hpp>
23 #include <thread>
24 #include "config.h"
25 #include "sysfs.hpp"
26 
27 using namespace std::string_literals;
28 namespace fs = std::experimental::filesystem;
29 
30 namespace sysfs {
31 
32 static const auto emptyString = ""s;
33 static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
34 
35 std::string findPhandleMatch(
36         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(
60             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(
74                 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(const std::string& ofNode)
166 {
167     static constexpr auto hwmonRoot = "/sys/class/hwmon";
168 
169     fs::path fullOfPath{ofRoot};
170     fullOfPath /= ofNode;
171 
172     for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
173     {
174         auto path = hwmonInst.path();
175         path /= "of_node";
176 
177         try
178         {
179             path = fs::canonical(path);
180         }
181         catch (const std::system_error& e)
182         {
183             // realpath may encounter ENOENT (Hwmon
184             // instances have a nasty habit of
185             // going away without warning).
186             continue;
187         }
188 
189         if (path == fullOfPath)
190         {
191             return hwmonInst.path();
192         }
193 
194         // Try to find HWMON instance via phandle values.
195         // Used for IIO device drivers.
196         auto matchpath = findPhandleMatch(path, fullOfPath);
197         if (!matchpath.empty())
198         {
199             return hwmonInst.path();
200         }
201     }
202 
203     return emptyString;
204 }
205 
206 std::string findHwmonFromDevPath(const std::string& devPath)
207 {
208     fs::path p{"/sys"};
209     p /= devPath;
210     p /= "hwmon";
211 
212     try
213     {
214         //This path is also used as a filesystem path to an environment
215         //file, and that has issues with ':'s in the path so they've
216         //been converted to '--'s.  Convert them back now.
217         size_t pos = 0;
218         std::string path = p;
219         while ((pos = path.find("--")) != std::string::npos)
220         {
221             path.replace(pos, 2, ":");
222         }
223 
224         for (const auto& hwmonInst : fs::directory_iterator(path))
225         {
226             if ((hwmonInst.path().filename().string().find("hwmon") !=
227                    std::string::npos))
228             {
229                 return hwmonInst.path();
230             }
231         }
232     }
233     catch (const std::exception& e)
234     {
235         using namespace phosphor::logging;
236         log<level::ERR>(
237                 "Unable to find hwmon directory from the dev path",
238                 entry("PATH=%s", devPath.c_str()));
239     }
240     return emptyString;
241 }
242 
243 } // namespace sysfs
244 
245 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
246