blob: a155b8ebe49724b123a69b7f59b158d12147f170 [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
86/**
87 * @brief Return the path to use for a call out.
88 *
89 * If the path does not contain iio-hwmon, assume passed in path is the call
90 * out path.
91 *
92 * @param[in] ofPath - Open firmware path to search for matching phandle value
93 *
94 * @return Path to use for call out
95 */
96std::string findCalloutPath(const std::string& ofPath)
97{
98 static constexpr auto iioHwmonStr = "iio-hwmon";
99
100 if (ofPath.find(iioHwmonStr) != std::string::npos)
101 {
102 auto matchpath = findPhandleMatch(ofPath, ofRoot);
103 auto n = matchpath.rfind('/');
104 if (n != std::string::npos)
105 {
106 return matchpath.substr(0, n);
107 }
108 }
109
110 return ofPath;
111}
112
113std::string findHwmon(const std::string& ofNode)
114{
115 static constexpr auto hwmonRoot = "/sys/class/hwmon";
116
117 fs::path fullOfPath{ofRoot};
118 fullOfPath /= ofNode;
119
120 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
121 {
122 auto path = hwmonInst.path();
123 path /= "of_node";
124 if (fs::canonical(path) != fullOfPath)
125 {
126 // Try to find HWMON instance via phandle values.
127 // Used for IIO device drivers.
128 auto ofpath = fullOfPath.string();
129 auto matchpath = findPhandleMatch(path, ofpath);
130 if (!std::string(matchpath).empty())
131 {
132 return hwmonInst.path();
133 }
134 else
135 {
136 continue;
137 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500138 }
139
Brad Bishop08379a32017-03-06 21:28:46 -0500140 return hwmonInst.path();
Brad Bishop613a5b32017-01-05 20:58:13 -0500141 }
142
143 return std::string();
144}
145
Brad Bishop4db64422017-02-16 11:33:32 -0500146int readSysfsWithCallout(const std::string& root,
147 const std::string& instance,
148 const std::string& type,
149 const std::string& id,
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500150 const std::string& sensor,
151 bool throwDeviceBusy)
Brad Bishop4db64422017-02-16 11:33:32 -0500152{
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400153 namespace fs = std::experimental::filesystem;
154
Brad Bishop4db64422017-02-16 11:33:32 -0500155 int value = 0;
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400156 std::ifstream ifs;
157 fs::path instancePath{root};
158 instancePath /= instance;
Brad Bishop4db64422017-02-16 11:33:32 -0500159 std::string fullPath = make_sysfs_path(instancePath,
160 type, id, sensor);
Brad Bishop4db64422017-02-16 11:33:32 -0500161
162 ifs.exceptions(std::ifstream::failbit
163 | std::ifstream::badbit
164 | std::ifstream::eofbit);
165 try
166 {
167 ifs.open(fullPath);
168 ifs >> value;
169 }
170 catch (const std::exception& e)
171 {
172 // Too many GCC bugs (53984, 66145) to do
173 // this the right way...
Brad Bishop4db64422017-02-16 11:33:32 -0500174
175 // errno should still reflect the error from the failing open
176 // or read system calls that got us here.
177 auto rc = errno;
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500178
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500179 if ((rc == EAGAIN) && throwDeviceBusy)
180 {
181 throw DeviceBusyException(fullPath);
182 }
183
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500184 // If the directory disappeared then this application should gracefully
185 // exit. There are race conditions between the unloading of a hwmon
186 // driver and the stopping of this service by systemd. To prevent
187 // this application from falsely failing in these scenarios, it will
188 // simply exit if the directory or file can not be found. It is up
189 // to the user(s) of this provided hwmon object to log the appropriate
190 // errors if the object disappears when it should not.
191 if (rc == ENOENT)
192 {
193 exit(0);
194 }
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400195 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500196 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth4e1f30f2017-03-21 16:13:27 -0500197 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
Patrick Venture1e6324f2017-06-01 14:07:05 -0700198
199 // this throws a ReadFailure.
200 elog<ReadFailure>(
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500201 xyz::openbmc_project::Sensor::Device::
202 ReadFailure::CALLOUT_ERRNO(rc),
203 xyz::openbmc_project::Sensor::Device::
204 ReadFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500205 fs::canonical(callOutPath).c_str()));
Brad Bishop4db64422017-02-16 11:33:32 -0500206 }
207
208 return value;
209}
210
Matthew Barth048ac872017-03-09 14:36:08 -0600211uint64_t writeSysfsWithCallout(const uint64_t& value,
212 const std::string& root,
213 const std::string& instance,
214 const std::string& type,
215 const std::string& id,
216 const std::string& sensor)
217{
218 namespace fs = std::experimental::filesystem;
219
220 std::string valueStr = std::to_string(value);
221 std::ofstream ofs;
222 fs::path instancePath{root};
223 instancePath /= instance;
224 std::string fullPath = make_sysfs_path(instancePath,
225 type, id, sensor);
226
227 ofs.exceptions(std::ofstream::failbit
228 | std::ofstream::badbit
229 | std::ofstream::eofbit);
230 try
231 {
232 ofs.open(fullPath);
233 ofs << valueStr;
234 }
235 catch (const std::exception& e)
236 {
237 // errno should still reflect the error from the failing open
238 // or write system calls that got us here.
239 auto rc = errno;
240 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500241 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth048ac872017-03-09 14:36:08 -0600242 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500243 report<WriteFailure>(
244 xyz::openbmc_project::Control::Device::
245 WriteFailure::CALLOUT_ERRNO(rc),
246 xyz::openbmc_project::Control::Device::
247 WriteFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500248 fs::canonical(callOutPath).c_str()));
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500249
Matthew Barth048ac872017-03-09 14:36:08 -0600250 exit(EXIT_FAILURE);
251 }
252
253 return value;
254}
255
Patrick Venture1e6324f2017-06-01 14:07:05 -0700256}
Brad Bishop613a5b32017-01-05 20:58:13 -0500257// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4