blob: ca22fe88ab1907d5d1ab901d4e4ab1da1d418ab4 [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;
Brandon Wyman8af8a202017-05-31 18:26:30 -050027namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060028
Patrick Venture1e6324f2017-06-01 14:07:05 -070029namespace sysfs {
30
Brandon Wyman8af8a202017-05-31 18:26:30 -050031static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
32
33/**
34 * @brief Return the path to the phandle file matching value in io-channels.
35 *
36 * This function will take two passed in paths.
37 * One path is used to find the io-channels file.
38 * The other path is used to find the phandle file.
39 * The 4 byte phandle value is read from the phandle file(s).
40 * The 4 byte phandle value and 4 byte index value is read from io-channels.
41 * When a match is found, the path to the matching phandle file is returned.
42 *
43 * @param[in] iochanneldir - Path to file for getting phandle from io-channels
44 * @param[in] phandledir - Path to use for reading from phandle file
45 *
46 * @return Path to phandle file with value matching that in io-channels
47 */
48std::string findPhandleMatch(const std::string& iochanneldir,
49 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050050{
Brandon Wyman8af8a202017-05-31 18:26:30 -050051 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050052 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050053 auto path = ofInst.path();
54 if ("phandle" == ofInst.path().filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050055 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050056 auto ioChannelsPath = iochanneldir + "/io-channels";
57 if (fs::exists(ioChannelsPath))
Brandon Wyman4eb98582017-05-24 14:24:00 -050058 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050059 auto fullOfPathPhandle = ofInst.path();
Brandon Wyman4eb98582017-05-24 14:24:00 -050060 std::ifstream ioChannelsFile(path);
61 std::ifstream pHandleFile(fullOfPathPhandle);
62
63 uint32_t ioChannelsValue;
64 uint32_t pHandleValue;
65
66 try
67 {
68 ioChannelsFile.read(reinterpret_cast<char*>(&ioChannelsValue),
69 sizeof(ioChannelsValue));
70 pHandleFile.read(reinterpret_cast<char*>(&pHandleValue),
71 sizeof(pHandleValue));
72
73 if (ioChannelsValue == pHandleValue)
74 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050075 return ofInst.path();
Brandon Wyman4eb98582017-05-24 14:24:00 -050076 }
77 }
78 catch (const std::exception& e)
79 {
80 log<level::INFO>(e.what());
Brandon Wyman8af8a202017-05-31 18:26:30 -050081 continue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050082 }
83
Brandon Wyman4eb98582017-05-24 14:24:00 -050084 }
Brandon Wyman8af8a202017-05-31 18:26:30 -050085 }
86 }
87
88 return std::string();
89}
90
91/**
92 * @brief Return the path to use for a call out.
93 *
94 * If the path does not contain iio-hwmon, assume passed in path is the call
95 * out path.
96 *
97 * @param[in] ofPath - Open firmware path to search for matching phandle value
98 *
99 * @return Path to use for call out
100 */
101std::string findCalloutPath(const std::string& ofPath)
102{
103 static constexpr auto iioHwmonStr = "iio-hwmon";
104
105 if (ofPath.find(iioHwmonStr) != std::string::npos)
106 {
107 auto matchpath = findPhandleMatch(ofPath, ofRoot);
108 auto n = matchpath.rfind('/');
109 if (n != std::string::npos)
110 {
111 return matchpath.substr(0, n);
112 }
113 }
114
115 return ofPath;
116}
117
118std::string findHwmon(const std::string& ofNode)
119{
120 static constexpr auto hwmonRoot = "/sys/class/hwmon";
121
122 fs::path fullOfPath{ofRoot};
123 fullOfPath /= ofNode;
124
125 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
126 {
127 auto path = hwmonInst.path();
128 path /= "of_node";
129 if (fs::canonical(path) != fullOfPath)
130 {
131 // Try to find HWMON instance via phandle values.
132 // Used for IIO device drivers.
133 auto ofpath = fullOfPath.string();
134 auto matchpath = findPhandleMatch(path, ofpath);
135 if (!std::string(matchpath).empty())
136 {
137 return hwmonInst.path();
138 }
139 else
140 {
141 continue;
142 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500143 }
144
Brad Bishop08379a32017-03-06 21:28:46 -0500145 return hwmonInst.path();
Brad Bishop613a5b32017-01-05 20:58:13 -0500146 }
147
148 return std::string();
149}
150
Brad Bishop4db64422017-02-16 11:33:32 -0500151int readSysfsWithCallout(const std::string& root,
152 const std::string& instance,
153 const std::string& type,
154 const std::string& id,
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500155 const std::string& sensor,
156 bool throwDeviceBusy)
Brad Bishop4db64422017-02-16 11:33:32 -0500157{
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400158 namespace fs = std::experimental::filesystem;
159
Brad Bishop4db64422017-02-16 11:33:32 -0500160 int value = 0;
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400161 std::ifstream ifs;
162 fs::path instancePath{root};
163 instancePath /= instance;
Brad Bishop4db64422017-02-16 11:33:32 -0500164 std::string fullPath = make_sysfs_path(instancePath,
165 type, id, sensor);
Brad Bishop4db64422017-02-16 11:33:32 -0500166
167 ifs.exceptions(std::ifstream::failbit
168 | std::ifstream::badbit
169 | std::ifstream::eofbit);
170 try
171 {
172 ifs.open(fullPath);
173 ifs >> value;
174 }
175 catch (const std::exception& e)
176 {
177 // Too many GCC bugs (53984, 66145) to do
178 // this the right way...
Brad Bishop4db64422017-02-16 11:33:32 -0500179
180 // errno should still reflect the error from the failing open
181 // or read system calls that got us here.
182 auto rc = errno;
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500183
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500184 if ((rc == EAGAIN) && throwDeviceBusy)
185 {
186 throw DeviceBusyException(fullPath);
187 }
188
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500189 // If the directory disappeared then this application should gracefully
190 // exit. There are race conditions between the unloading of a hwmon
191 // driver and the stopping of this service by systemd. To prevent
192 // this application from falsely failing in these scenarios, it will
193 // simply exit if the directory or file can not be found. It is up
194 // to the user(s) of this provided hwmon object to log the appropriate
195 // errors if the object disappears when it should not.
196 if (rc == ENOENT)
197 {
198 exit(0);
199 }
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400200 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500201 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth4e1f30f2017-03-21 16:13:27 -0500202 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
Patrick Venture1e6324f2017-06-01 14:07:05 -0700203
204 // this throws a ReadFailure.
205 elog<ReadFailure>(
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500206 xyz::openbmc_project::Sensor::Device::
207 ReadFailure::CALLOUT_ERRNO(rc),
208 xyz::openbmc_project::Sensor::Device::
209 ReadFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500210 fs::canonical(callOutPath).c_str()));
Brad Bishop4db64422017-02-16 11:33:32 -0500211 }
212
213 return value;
214}
215
Matthew Barth048ac872017-03-09 14:36:08 -0600216uint64_t writeSysfsWithCallout(const uint64_t& value,
217 const std::string& root,
218 const std::string& instance,
219 const std::string& type,
220 const std::string& id,
221 const std::string& sensor)
222{
223 namespace fs = std::experimental::filesystem;
224
225 std::string valueStr = std::to_string(value);
226 std::ofstream ofs;
227 fs::path instancePath{root};
228 instancePath /= instance;
229 std::string fullPath = make_sysfs_path(instancePath,
230 type, id, sensor);
231
232 ofs.exceptions(std::ofstream::failbit
233 | std::ofstream::badbit
234 | std::ofstream::eofbit);
235 try
236 {
237 ofs.open(fullPath);
238 ofs << valueStr;
239 }
240 catch (const std::exception& e)
241 {
242 // errno should still reflect the error from the failing open
243 // or write system calls that got us here.
244 auto rc = errno;
245 instancePath /= "device";
Brandon Wyman2f370c42017-06-21 13:17:35 -0500246 auto callOutPath = findCalloutPath(fs::canonical(instancePath));
Matthew Barth048ac872017-03-09 14:36:08 -0600247 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500248 report<WriteFailure>(
249 xyz::openbmc_project::Control::Device::
250 WriteFailure::CALLOUT_ERRNO(rc),
251 xyz::openbmc_project::Control::Device::
252 WriteFailure::CALLOUT_DEVICE_PATH(
Brandon Wyman8af8a202017-05-31 18:26:30 -0500253 fs::canonical(callOutPath).c_str()));
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500254
Matthew Barth048ac872017-03-09 14:36:08 -0600255 exit(EXIT_FAILURE);
256 }
257
258 return value;
259}
260
Patrick Venture1e6324f2017-06-01 14:07:05 -0700261}
Brad Bishop613a5b32017-01-05 20:58:13 -0500262// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4