blob: 6d520295712bb738f267aa973a65ac9016c0e40a [file] [log] [blame]
Brad Bishop613a5b32017-01-05 20:58:13 -05001/**
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 <cstdlib>
Brad Bishop08379a32017-03-06 21:28:46 -050017#include <experimental/filesystem>
Brad Bishop68c43b22017-08-28 16:24:00 -040018#include <fstream>
Brad Bishop613a5b32017-01-05 20:58:13 -050019#include <memory>
Matthew Barth048ac872017-03-09 14:36:08 -060020#include <phosphor-logging/elog.hpp>
21#include <phosphor-logging/elog-errors.hpp>
22#include <xyz/openbmc_project/Control/Device/error.hpp>
Matthew Barth4e1f30f2017-03-21 16:13:27 -050023#include <xyz/openbmc_project/Sensor/Device/error.hpp>
Brad Bishop613a5b32017-01-05 20:58:13 -050024#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050025
Matthew Barth048ac872017-03-09 14:36:08 -060026using namespace phosphor::logging;
Brad Bishopf4bf63a2017-08-28 15:39:19 -040027using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050028namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060029
Patrick Venture1e6324f2017-06-01 14:07:05 -070030namespace sysfs {
31
Brad Bishopf4bf63a2017-08-28 15:39:19 -040032static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050033static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
34
Brad Bishopf4bf63a2017-08-28 15:39:19 -040035std::string findPhandleMatch(
36 const std::string& iochanneldir,
37 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050038{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040039 // 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
Brandon Wyman8af8a202017-05-31 18:26:30 -050063 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050064 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050065 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -040066 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050067 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -040068 continue;
69 }
70 std::ifstream pHandleFile(path);
71 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050072
Brad Bishopf4bf63a2017-08-28 15:39:19 -040073 pHandleFile.read(
74 reinterpret_cast<char*>(&pHandleValue),
75 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -050076
Brad Bishopf4bf63a2017-08-28 15:39:19 -040077 if (ioChannelsValue == pHandleValue)
78 {
79 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -050080 }
81 }
82
Brad Bishopf4bf63a2017-08-28 15:39:19 -040083 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -050084}
85
Brad Bishop431d26a2017-08-25 09:47:58 -040086std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -050087{
Brad Bishop431d26a2017-08-25 09:47:58 -040088 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
89 // /sys/devices symlink.
90 fs::path devPath{instancePath};
91 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -050092
Brad Bishop431d26a2017-08-25 09:47:58 -040093 try
Brandon Wyman8af8a202017-05-31 18:26:30 -050094 {
Brad Bishop431d26a2017-08-25 09:47:58 -040095 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('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500147 if (n != std::string::npos)
148 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400149 // 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 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500159 }
160 }
161
Brad Bishop431d26a2017-08-25 09:47:58 -0400162 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500163}
164
165std::string findHwmon(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";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400176
177 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500178 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400179 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;
Brad Bishop613a5b32017-01-05 20:58:13 -0500187 }
188
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400189 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 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500201 }
202
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400203 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500204}
205
Brad Bishop4db64422017-02-16 11:33:32 -0500206int readSysfsWithCallout(const std::string& root,
207 const std::string& instance,
208 const std::string& type,
209 const std::string& id,
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500210 const std::string& sensor,
211 bool throwDeviceBusy)
Brad Bishop4db64422017-02-16 11:33:32 -0500212{
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400213 namespace fs = std::experimental::filesystem;
214
Brad Bishop4db64422017-02-16 11:33:32 -0500215 int value = 0;
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400216 std::ifstream ifs;
217 fs::path instancePath{root};
218 instancePath /= instance;
Brad Bishop4db64422017-02-16 11:33:32 -0500219 std::string fullPath = make_sysfs_path(instancePath,
220 type, id, sensor);
Brad Bishop4db64422017-02-16 11:33:32 -0500221
222 ifs.exceptions(std::ifstream::failbit
223 | std::ifstream::badbit
224 | std::ifstream::eofbit);
225 try
226 {
227 ifs.open(fullPath);
228 ifs >> value;
229 }
230 catch (const std::exception& e)
231 {
232 // Too many GCC bugs (53984, 66145) to do
233 // this the right way...
Brad Bishop4db64422017-02-16 11:33:32 -0500234
235 // errno should still reflect the error from the failing open
236 // or read system calls that got us here.
237 auto rc = errno;
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500238
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500239 if ((rc == EAGAIN) && throwDeviceBusy)
240 {
241 throw DeviceBusyException(fullPath);
242 }
243
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500244 // If the directory disappeared then this application should gracefully
245 // exit. There are race conditions between the unloading of a hwmon
246 // driver and the stopping of this service by systemd. To prevent
247 // this application from falsely failing in these scenarios, it will
248 // simply exit if the directory or file can not be found. It is up
249 // to the user(s) of this provided hwmon object to log the appropriate
250 // errors if the object disappears when it should not.
251 if (rc == ENOENT)
252 {
253 exit(0);
254 }
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400255 instancePath /= "device";
Brad Bishop431d26a2017-08-25 09:47:58 -0400256 auto callOutPath = findCalloutPath(instancePath);
Matthew Barth4e1f30f2017-03-21 16:13:27 -0500257 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
Patrick Venture1e6324f2017-06-01 14:07:05 -0700258
259 // this throws a ReadFailure.
260 elog<ReadFailure>(
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500261 xyz::openbmc_project::Sensor::Device::
262 ReadFailure::CALLOUT_ERRNO(rc),
263 xyz::openbmc_project::Sensor::Device::
Brad Bishop431d26a2017-08-25 09:47:58 -0400264 ReadFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
Brad Bishop4db64422017-02-16 11:33:32 -0500265 }
266
267 return value;
268}
269
Matthew Barth048ac872017-03-09 14:36:08 -0600270uint64_t writeSysfsWithCallout(const uint64_t& value,
271 const std::string& root,
272 const std::string& instance,
273 const std::string& type,
274 const std::string& id,
275 const std::string& sensor)
276{
277 namespace fs = std::experimental::filesystem;
278
279 std::string valueStr = std::to_string(value);
280 std::ofstream ofs;
281 fs::path instancePath{root};
282 instancePath /= instance;
283 std::string fullPath = make_sysfs_path(instancePath,
284 type, id, sensor);
285
286 ofs.exceptions(std::ofstream::failbit
287 | std::ofstream::badbit
288 | std::ofstream::eofbit);
289 try
290 {
291 ofs.open(fullPath);
292 ofs << valueStr;
293 }
294 catch (const std::exception& e)
295 {
296 // errno should still reflect the error from the failing open
297 // or write system calls that got us here.
298 auto rc = errno;
299 instancePath /= "device";
Brad Bishop431d26a2017-08-25 09:47:58 -0400300 auto callOutPath = findCalloutPath(instancePath);
Matthew Barth048ac872017-03-09 14:36:08 -0600301 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500302 report<WriteFailure>(
303 xyz::openbmc_project::Control::Device::
304 WriteFailure::CALLOUT_ERRNO(rc),
305 xyz::openbmc_project::Control::Device::
Brad Bishop431d26a2017-08-25 09:47:58 -0400306 WriteFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500307
Matthew Barth048ac872017-03-09 14:36:08 -0600308 exit(EXIT_FAILURE);
309 }
310
311 return value;
312}
313
Patrick Venture1e6324f2017-06-01 14:07:05 -0700314}
Brad Bishop613a5b32017-01-05 20:58:13 -0500315// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4