blob: efdba9203edbbf995488684f7bc4e4935d2a36ec [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 Bishop613a5b32017-01-05 20:58:13 -050018#include <memory>
Matthew Barth048ac872017-03-09 14:36:08 -060019#include <phosphor-logging/elog.hpp>
20#include <phosphor-logging/elog-errors.hpp>
21#include <xyz/openbmc_project/Control/Device/error.hpp>
Matthew Barth4e1f30f2017-03-21 16:13:27 -050022#include <xyz/openbmc_project/Sensor/Device/error.hpp>
Brad Bishop613a5b32017-01-05 20:58:13 -050023#include "sysfs.hpp"
24#include "util.hpp"
Brandon Wyman4eb98582017-05-24 14:24:00 -050025#include <fstream>
Brad Bishop613a5b32017-01-05 20:58:13 -050026
Matthew Barth048ac872017-03-09 14:36:08 -060027using namespace phosphor::logging;
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
Brandon Wyman8af8a202017-05-31 18:26:30 -050032static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
33
34/**
35 * @brief Return the path to the phandle file matching value in io-channels.
36 *
37 * This function will take two passed in paths.
38 * One path is used to find the io-channels file.
39 * The other path is used to find the phandle file.
40 * The 4 byte phandle value is read from the phandle file(s).
41 * The 4 byte phandle value and 4 byte index value is read from io-channels.
42 * When a match is found, the path to the matching phandle file is returned.
43 *
44 * @param[in] iochanneldir - Path to file for getting phandle from io-channels
45 * @param[in] phandledir - Path to use for reading from phandle file
46 *
47 * @return Path to phandle file with value matching that in io-channels
48 */
49std::string findPhandleMatch(const std::string& iochanneldir,
50 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050051{
Brandon Wyman8af8a202017-05-31 18:26:30 -050052 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050053 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050054 auto path = ofInst.path();
55 if ("phandle" == ofInst.path().filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050056 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050057 auto ioChannelsPath = iochanneldir + "/io-channels";
58 if (fs::exists(ioChannelsPath))
Brandon Wyman4eb98582017-05-24 14:24:00 -050059 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050060 auto fullOfPathPhandle = ofInst.path();
Brandon Wyman4eb98582017-05-24 14:24:00 -050061 std::ifstream ioChannelsFile(path);
62 std::ifstream pHandleFile(fullOfPathPhandle);
63
64 uint32_t ioChannelsValue;
65 uint32_t pHandleValue;
66
67 try
68 {
69 ioChannelsFile.read(reinterpret_cast<char*>(&ioChannelsValue),
70 sizeof(ioChannelsValue));
71 pHandleFile.read(reinterpret_cast<char*>(&pHandleValue),
72 sizeof(pHandleValue));
73
74 if (ioChannelsValue == pHandleValue)
75 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050076 return ofInst.path();
Brandon Wyman4eb98582017-05-24 14:24:00 -050077 }
78 }
79 catch (const std::exception& e)
80 {
81 log<level::INFO>(e.what());
Brandon Wyman8af8a202017-05-31 18:26:30 -050082 continue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050083 }
84
Brandon Wyman4eb98582017-05-24 14:24:00 -050085 }
Brandon Wyman8af8a202017-05-31 18:26:30 -050086 }
87 }
88
89 return std::string();
90}
91
92/**
93 * @brief Return the path to use for a call out.
94 *
95 * If the path does not contain iio-hwmon, assume passed in path is the call
96 * out path.
97 *
98 * @param[in] ofPath - Open firmware path to search for matching phandle value
99 *
100 * @return Path to use for call out
101 */
102std::string findCalloutPath(const std::string& ofPath)
103{
104 static constexpr auto iioHwmonStr = "iio-hwmon";
105
106 if (ofPath.find(iioHwmonStr) != std::string::npos)
107 {
108 auto matchpath = findPhandleMatch(ofPath, ofRoot);
109 auto n = matchpath.rfind('/');
110 if (n != std::string::npos)
111 {
112 return matchpath.substr(0, n);
113 }
114 }
115
116 return ofPath;
117}
118
119std::string findHwmon(const std::string& ofNode)
120{
121 static constexpr auto hwmonRoot = "/sys/class/hwmon";
122
123 fs::path fullOfPath{ofRoot};
124 fullOfPath /= ofNode;
125
126 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
127 {
128 auto path = hwmonInst.path();
129 path /= "of_node";
130 if (fs::canonical(path) != fullOfPath)
131 {
132 // Try to find HWMON instance via phandle values.
133 // Used for IIO device drivers.
134 auto ofpath = fullOfPath.string();
135 auto matchpath = findPhandleMatch(path, ofpath);
136 if (!std::string(matchpath).empty())
137 {
138 return hwmonInst.path();
139 }
140 else
141 {
142 continue;
143 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500144 }
145
Brad Bishop08379a32017-03-06 21:28:46 -0500146 return hwmonInst.path();
Brad Bishop613a5b32017-01-05 20:58:13 -0500147 }
148
149 return std::string();
150}
151
Brad Bishop4db64422017-02-16 11:33:32 -0500152int readSysfsWithCallout(const std::string& root,
153 const std::string& instance,
154 const std::string& type,
155 const std::string& id,
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500156 const std::string& sensor,
157 bool throwDeviceBusy)
Brad Bishop4db64422017-02-16 11:33:32 -0500158{
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400159 namespace fs = std::experimental::filesystem;
160
Brad Bishop4db64422017-02-16 11:33:32 -0500161 int value = 0;
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400162 std::ifstream ifs;
163 fs::path instancePath{root};
164 instancePath /= instance;
Brad Bishop4db64422017-02-16 11:33:32 -0500165 std::string fullPath = make_sysfs_path(instancePath,
166 type, id, sensor);
Brad Bishop4db64422017-02-16 11:33:32 -0500167
168 ifs.exceptions(std::ifstream::failbit
169 | std::ifstream::badbit
170 | std::ifstream::eofbit);
171 try
172 {
173 ifs.open(fullPath);
174 ifs >> value;
175 }
176 catch (const std::exception& e)
177 {
178 // Too many GCC bugs (53984, 66145) to do
179 // this the right way...
Brad Bishop4db64422017-02-16 11:33:32 -0500180
181 // errno should still reflect the error from the failing open
182 // or read system calls that got us here.
183 auto rc = errno;
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500184
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500185 if ((rc == EAGAIN) && throwDeviceBusy)
186 {
187 throw DeviceBusyException(fullPath);
188 }
189
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500190 // If the directory disappeared then this application should gracefully
191 // exit. There are race conditions between the unloading of a hwmon
192 // driver and the stopping of this service by systemd. To prevent
193 // this application from falsely failing in these scenarios, it will
194 // simply exit if the directory or file can not be found. It is up
195 // to the user(s) of this provided hwmon object to log the appropriate
196 // errors if the object disappears when it should not.
197 if (rc == ENOENT)
198 {
199 exit(0);
200 }
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400201 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500202 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth4e1f30f2017-03-21 16:13:27 -0500203 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
Patrick Venture1e6324f2017-06-01 14:07:05 -0700204
205 // this throws a ReadFailure.
206 elog<ReadFailure>(
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500207 xyz::openbmc_project::Sensor::Device::
208 ReadFailure::CALLOUT_ERRNO(rc),
209 xyz::openbmc_project::Sensor::Device::
210 ReadFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500211 fs::canonical(callOutPath).c_str()));
Brad Bishop4db64422017-02-16 11:33:32 -0500212 }
213
214 return value;
215}
216
Matthew Barth048ac872017-03-09 14:36:08 -0600217uint64_t writeSysfsWithCallout(const uint64_t& value,
218 const std::string& root,
219 const std::string& instance,
220 const std::string& type,
221 const std::string& id,
222 const std::string& sensor)
223{
224 namespace fs = std::experimental::filesystem;
225
226 std::string valueStr = std::to_string(value);
227 std::ofstream ofs;
228 fs::path instancePath{root};
229 instancePath /= instance;
230 std::string fullPath = make_sysfs_path(instancePath,
231 type, id, sensor);
232
233 ofs.exceptions(std::ofstream::failbit
234 | std::ofstream::badbit
235 | std::ofstream::eofbit);
236 try
237 {
238 ofs.open(fullPath);
239 ofs << valueStr;
240 }
241 catch (const std::exception& e)
242 {
243 // errno should still reflect the error from the failing open
244 // or write system calls that got us here.
245 auto rc = errno;
246 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500247 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth048ac872017-03-09 14:36:08 -0600248 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500249 report<WriteFailure>(
250 xyz::openbmc_project::Control::Device::
251 WriteFailure::CALLOUT_ERRNO(rc),
252 xyz::openbmc_project::Control::Device::
253 WriteFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500254 fs::canonical(callOutPath).c_str()));
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500255
Matthew Barth048ac872017-03-09 14:36:08 -0600256 exit(EXIT_FAILURE);
257 }
258
259 return value;
260}
261
Patrick Venture1e6324f2017-06-01 14:07:05 -0700262}
Brad Bishop613a5b32017-01-05 20:58:13 -0500263// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4