blob: 9d991a6f2ce8cefbbda4bc9c4397dba6ec1b7d3d [file] [log] [blame]
Matt Spinler015e3ad2017-08-01 11:20:47 -05001/**
2 * Copyright © 2017 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 */
Matt Spinlerf0f02b92018-10-25 16:12:43 -050016#include "pmbus.hpp"
17
Matt Spinler015e3ad2017-08-01 11:20:47 -050018#include <experimental/filesystem>
19#include <fstream>
Matt Spinler015e3ad2017-08-01 11:20:47 -050020#include <phosphor-logging/elog-errors.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050021#include <phosphor-logging/elog.hpp>
Matt Spinlerceacf942017-10-05 13:55:02 -050022#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050023#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler015e3ad2017-08-01 11:20:47 -050024
25namespace witherspoon
26{
27namespace pmbus
28{
29
30using namespace phosphor::logging;
31using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matt Spinlerceacf942017-10-05 13:55:02 -050032using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler015e3ad2017-08-01 11:20:47 -050033namespace fs = std::experimental::filesystem;
34
Matt Spinlerfa23e332018-01-18 11:24:58 -060035/**
36 * @brief Helper to close a file handle
37 */
38struct FileCloser
39{
40 void operator()(FILE* fp) const
41 {
42 fclose(fp);
43 }
44};
45
Matt Spinlerf0f02b92018-10-25 16:12:43 -050046std::string PMBus::insertPageNum(const std::string& templateName, size_t page)
Matt Spinler015e3ad2017-08-01 11:20:47 -050047{
48 auto name = templateName;
49
Matt Spinlerf0f02b92018-10-25 16:12:43 -050050 // insert the page where the P was
Matt Spinler015e3ad2017-08-01 11:20:47 -050051 auto pos = name.find('P');
52 if (pos != std::string::npos)
53 {
54 name.replace(pos, 1, std::to_string(page));
55 }
56
57 return name;
58}
59
Brandon Wymanff5f3392017-08-11 17:43:22 -050060fs::path PMBus::getPath(Type type)
61{
62 switch (type)
63 {
64 default:
Brandon Wymanf855e822017-08-08 18:04:47 -050065 /* fall through */
Brandon Wymanff5f3392017-08-11 17:43:22 -050066 case Type::Base:
67 return basePath;
68 break;
69 case Type::Hwmon:
70 return basePath / "hwmon" / hwmonDir;
71 break;
72 case Type::Debug:
Matt Spinler8f0d9532017-08-21 11:22:37 -050073 return debugPath / "pmbus" / hwmonDir;
74 break;
75 case Type::DeviceDebug:
Matt Spinler4dc46782018-01-04 14:29:16 -060076 {
Matt Spinler8f0d9532017-08-21 11:22:37 -050077 auto dir = driverName + "." + std::to_string(instance);
78 return debugPath / dir;
Brandon Wymanff5f3392017-08-11 17:43:22 -050079 break;
Matt Spinler4dc46782018-01-04 14:29:16 -060080 }
81 case Type::HwmonDeviceDebug:
82 return debugPath / "pmbus" / hwmonDir / getDeviceName();
83 break;
Brandon Wymanff5f3392017-08-11 17:43:22 -050084 }
85}
86
Matt Spinlerba053482018-01-04 14:26:05 -060087std::string PMBus::getDeviceName()
88{
89 std::string name;
90 std::ifstream file;
91 auto path = basePath / "name";
92
Matt Spinlerf0f02b92018-10-25 16:12:43 -050093 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerba053482018-01-04 14:26:05 -060094 std::ifstream::eofbit);
95 try
96 {
97 file.open(path);
98 file >> name;
99 }
100 catch (std::exception& e)
101 {
102 log<level::ERR>("Unable to read PMBus device name",
103 entry("PATH=%s", path.c_str()));
104 }
105
106 return name;
107}
108
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500109bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500110{
111 auto pagedBit = insertPageNum(name, page);
Matt Spinler57868bc2017-08-03 10:07:41 -0500112 return readBit(pagedBit, type);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500113}
114
Matt Spinler57868bc2017-08-03 10:07:41 -0500115bool PMBus::readBit(const std::string& name, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500116{
117 unsigned long int value = 0;
118 std::ifstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500119 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500120
Matt Spinler015e3ad2017-08-01 11:20:47 -0500121 path /= name;
122
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500123 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500124 std::ifstream::eofbit);
125
126 try
127 {
128 char* err = NULL;
129 std::string val{1, '\0'};
130
131 file.open(path);
132 file.read(&val[0], 1);
133
134 value = strtoul(val.c_str(), &err, 10);
135
136 if (*err)
137 {
138 log<level::ERR>("Invalid character in sysfs file",
139 entry("FILE=%s", path.c_str()),
140 entry("CONTENTS=%s", val.c_str()));
141
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500142 // Catch below and handle as a read failure
Matt Spinler015e3ad2017-08-01 11:20:47 -0500143 elog<InternalFailure>();
144 }
145 }
146 catch (std::exception& e)
147 {
148 auto rc = errno;
149
150 log<level::ERR>("Failed to read sysfs file",
Brandon Wymanf855e822017-08-08 18:04:47 -0500151 entry("FILENAME=%s", path.c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500152
Matt Spinlerceacf942017-10-05 13:55:02 -0500153 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
154
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500155 elog<ReadFailure>(
156 metadata::CALLOUT_ERRNO(rc),
157 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500158 }
159
160 return value != 0;
161}
162
Brandon Wyman3b7b38b2017-09-25 16:43:45 -0500163bool PMBus::exists(const std::string& name, Type type)
164{
165 auto path = getPath(type);
166 path /= name;
167 return fs::exists(path);
168}
169
Brandon Wymanf855e822017-08-08 18:04:47 -0500170uint64_t PMBus::read(const std::string& name, Type type)
171{
172 uint64_t data = 0;
173 std::ifstream file;
174 auto path = getPath(type);
175 path /= name;
176
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500177 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Brandon Wymanf855e822017-08-08 18:04:47 -0500178 std::ifstream::eofbit);
179
180 try
181 {
182 file.open(path);
183 file >> std::hex >> data;
184 }
185 catch (std::exception& e)
186 {
187 auto rc = errno;
188 log<level::ERR>("Failed to read sysfs file",
189 entry("FILENAME=%s", path.c_str()));
190
Matt Spinlerceacf942017-10-05 13:55:02 -0500191 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
Brandon Wymanf855e822017-08-08 18:04:47 -0500192
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500193 elog<ReadFailure>(
194 metadata::CALLOUT_ERRNO(rc),
195 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Brandon Wymanf855e822017-08-08 18:04:47 -0500196 }
197
198 return data;
199}
200
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600201std::string PMBus::readString(const std::string& name, Type type)
202{
203 std::string data;
204 std::ifstream file;
205 auto path = getPath(type);
206 path /= name;
207
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500208 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600209 std::ifstream::eofbit);
210
211 try
212 {
213 file.open(path);
214 file >> data;
215 }
216 catch (std::exception& e)
217 {
218 auto rc = errno;
219 log<level::ERR>("Failed to read sysfs file",
220 entry("FILENAME=%s", path.c_str()));
221
222 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
223
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500224 elog<ReadFailure>(
225 metadata::CALLOUT_ERRNO(rc),
226 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600227 }
228
229 return data;
230}
231
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500232std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
Matt Spinlerfa23e332018-01-18 11:24:58 -0600233 size_t length)
234{
235 auto path = getPath(type) / name;
236
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500237 // Use C style IO because it's easier to handle telling the difference
238 // between hitting EOF or getting an actual error.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600239 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
240
241 if (file)
242 {
243 std::vector<uint8_t> data(length, 0);
244
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500245 auto bytes =
246 fread(data.data(), sizeof(decltype(data[0])), length, file.get());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600247
248 if (bytes != length)
249 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500250 // If hit EOF, just return the amount of data that was read.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600251 if (feof(file.get()))
252 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500253 data.erase(data.begin() + bytes, data.end());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600254 }
255 else if (ferror(file.get()))
256 {
257 auto rc = errno;
258 log<level::ERR>("Failed to read sysfs file",
259 entry("FILENAME=%s", path.c_str()));
260
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500261 using metadata =
262 xyz::openbmc_project::Common::Device::ReadFailure;
Matt Spinlerfa23e332018-01-18 11:24:58 -0600263
264 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
265 metadata::CALLOUT_DEVICE_PATH(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500266 fs::canonical(basePath).c_str()));
Matt Spinlerfa23e332018-01-18 11:24:58 -0600267 }
268 }
269 return data;
270 }
271
272 return std::vector<uint8_t>{};
273}
274
Matt Spinler57868bc2017-08-03 10:07:41 -0500275void PMBus::write(const std::string& name, int value, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500276{
277 std::ofstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500278 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500279
Matt Spinler015e3ad2017-08-01 11:20:47 -0500280 path /= name;
281
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500282 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500283 std::ofstream::eofbit);
284
285 try
286 {
287 file.open(path);
288 file << value;
289 }
290 catch (const std::exception& e)
291 {
292 auto rc = errno;
293
294 log<level::ERR>("Failed to write sysfs file",
Brandon Wymanf855e822017-08-08 18:04:47 -0500295 entry("FILENAME=%s", path.c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500296
Matt Spinlerceacf942017-10-05 13:55:02 -0500297 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
298
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500299 elog<WriteFailure>(
300 metadata::CALLOUT_ERRNO(rc),
301 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500302 }
303}
304
Brandon Wymanff5f3392017-08-11 17:43:22 -0500305void PMBus::findHwmonDir()
Matt Spinler57868bc2017-08-03 10:07:41 -0500306{
307 fs::path path{basePath};
308 path /= "hwmon";
309
Brandon Wymanaad73e92017-08-16 16:27:54 -0500310 // Make sure the directory exists, otherwise for things that can be
311 // dynamically present or not present an exception will be thrown if the
312 // hwmon directory is not there, resulting in a program termination.
313 if (fs::is_directory(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500314 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500315 // look for <basePath>/hwmon/hwmonN/
Brandon Wymanaad73e92017-08-16 16:27:54 -0500316 for (auto& f : fs::directory_iterator(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500317 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500318 if ((f.path().filename().string().find("hwmon") !=
319 std::string::npos) &&
320 (fs::is_directory(f.path())))
321 {
322 hwmonDir = f.path().filename();
323 break;
324 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500325 }
326 }
327
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500328 // Don't really want to crash here, just log it
329 // and let accesses fail later
Brandon Wymanff5f3392017-08-11 17:43:22 -0500330 if (hwmonDir.empty())
Matt Spinler57868bc2017-08-03 10:07:41 -0500331 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500332 log<level::INFO>("Unable to find hwmon directory "
333 "in device base path",
334 entry("DEVICE_PATH=%s", basePath.c_str()));
Matt Spinler57868bc2017-08-03 10:07:41 -0500335 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500336}
337
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500338} // namespace pmbus
339} // namespace witherspoon