1 /**
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 */
16 #include "pmbus.hpp"
17
18 #include <phosphor-logging/elog-errors.hpp>
19 #include <phosphor-logging/elog.hpp>
20 #include <xyz/openbmc_project/Common/Device/error.hpp>
21 #include <xyz/openbmc_project/Common/error.hpp>
22
23 #include <filesystem>
24 #include <fstream>
25
26 namespace phosphor
27 {
28 namespace pmbus
29 {
30
31 using namespace phosphor::logging;
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
34 namespace fs = std::filesystem;
35
36 /**
37 * @brief Helper to close a file handle
38 */
39 struct FileCloser
40 {
operator ()phosphor::pmbus::FileCloser41 void operator()(FILE* fp) const
42 {
43 fclose(fp);
44 }
45 };
46
insertPageNum(const std::string & templateName,size_t page)47 std::string PMBus::insertPageNum(const std::string& templateName, size_t page)
48 {
49 auto name = templateName;
50
51 // insert the page where the P was
52 auto pos = name.find('P');
53 if (pos != std::string::npos)
54 {
55 name.replace(pos, 1, std::to_string(page));
56 }
57
58 return name;
59 }
60
getPath(Type type)61 fs::path PMBus::getPath(Type type)
62 {
63 switch (type)
64 {
65 default:
66 /* fall through */
67 case Type::Base:
68 return basePath;
69 break;
70 case Type::Hwmon:
71 return basePath / "hwmon" / hwmonDir;
72 break;
73 case Type::Debug:
74 return debugPath / "pmbus" / hwmonDir;
75 break;
76 case Type::DeviceDebug:
77 {
78 auto dir = driverName + "." + std::to_string(instance);
79 return debugPath / dir;
80 break;
81 }
82 case Type::HwmonDeviceDebug:
83 return debugPath / "pmbus" / hwmonDir / getDeviceName();
84 break;
85 }
86 }
87
getDeviceName()88 std::string PMBus::getDeviceName()
89 {
90 std::string name;
91 std::ifstream file;
92 auto path = basePath / "name";
93
94 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
95 std::ifstream::eofbit);
96 try
97 {
98 file.open(path);
99 file >> name;
100 }
101 catch (const std::exception& e)
102 {
103 log<level::ERR>((std::string("Unable to read PMBus device name "
104 "PATH=") +
105 path.string())
106 .c_str());
107 }
108
109 return name;
110 }
111
readBitInPage(const std::string & name,size_t page,Type type)112 bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
113 {
114 auto pagedBit = insertPageNum(name, page);
115 return readBit(pagedBit, type);
116 }
117
readBit(const std::string & name,Type type)118 bool PMBus::readBit(const std::string& name, Type type)
119 {
120 unsigned long int value = 0;
121 std::ifstream file;
122 fs::path path = getPath(type);
123
124 path /= name;
125
126 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
127 std::ifstream::eofbit);
128
129 try
130 {
131 char* err = NULL;
132 std::string val{1, '\0'};
133
134 file.open(path);
135 file.read(&val[0], 1);
136
137 value = strtoul(val.c_str(), &err, 10);
138
139 if (*err)
140 {
141 log<level::ERR>((std::string("Invalid character in sysfs file"
142 " FILE=") +
143 path.string() + std::string(" CONTENTS=") + val)
144 .c_str());
145
146 // Catch below and handle as a read failure
147 elog<InternalFailure>();
148 }
149 }
150 catch (const std::exception& e)
151 {
152 auto rc = errno;
153
154 log<level::ERR>(
155 (std::string("Failed to read sysfs file "
156 "errno=") +
157 std::to_string(rc) + std::string(" FILENAME=") + path.string())
158 .c_str());
159
160 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
161
162 elog<ReadFailure>(
163 metadata::CALLOUT_ERRNO(rc),
164 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
165 }
166
167 return value != 0;
168 }
169
exists(const std::string & name,Type type)170 bool PMBus::exists(const std::string& name, Type type)
171 {
172 auto path = getPath(type);
173 path /= name;
174 return fs::exists(path);
175 }
176
read(const std::string & name,Type type,bool errTrace)177 uint64_t PMBus::read(const std::string& name, Type type, bool errTrace)
178 {
179 uint64_t data = 0;
180 std::ifstream file;
181 auto path = getPath(type);
182 path /= name;
183
184 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
185 std::ifstream::eofbit);
186
187 try
188 {
189 file.open(path);
190 file >> std::hex >> data;
191 }
192 catch (const std::exception& e)
193 {
194 auto rc = errno;
195
196 if (errTrace)
197 {
198 log<level::ERR>((std::string("Failed to read sysfs file "
199 "errno=") +
200 std::to_string(rc) + " FILENAME=" + path.string())
201 .c_str());
202
203 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
204
205 elog<ReadFailure>(
206 metadata::CALLOUT_ERRNO(rc),
207 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
208 }
209 else
210 {
211 throw ReadFailure();
212 }
213 }
214
215 return data;
216 }
217
readString(const std::string & name,Type type)218 std::string PMBus::readString(const std::string& name, Type type)
219 {
220 std::string data;
221 std::ifstream file;
222 auto path = getPath(type);
223 path /= name;
224
225 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
226 std::ifstream::eofbit);
227
228 try
229 {
230 file.open(path);
231 file >> data;
232 }
233 catch (const std::exception& e)
234 {
235 auto rc = errno;
236 log<level::ERR>((std::string("Failed to read sysfs file "
237 "errno=") +
238 std::to_string(rc) + " FILENAME=" + path.string())
239 .c_str());
240
241 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
242
243 elog<ReadFailure>(
244 metadata::CALLOUT_ERRNO(rc),
245 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
246 }
247
248 return data;
249 }
250
readBinary(const std::string & name,Type type,size_t length)251 std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
252 size_t length)
253 {
254 auto path = getPath(type) / name;
255
256 // Use C style IO because it's easier to handle telling the difference
257 // between hitting EOF or getting an actual error.
258 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
259
260 if (file)
261 {
262 std::vector<uint8_t> data(length, 0);
263
264 auto bytes =
265 fread(data.data(), sizeof(decltype(data[0])), length, file.get());
266
267 if (bytes != length)
268 {
269 // If hit EOF, just return the amount of data that was read.
270 if (feof(file.get()))
271 {
272 data.erase(data.begin() + bytes, data.end());
273 }
274 else if (ferror(file.get()))
275 {
276 auto rc = errno;
277 log<level::ERR>(
278 (std::string("Failed to read sysfs file "
279 "errno=") +
280 std::to_string(rc) + " FILENAME=" + path.string())
281 .c_str());
282 using metadata =
283 xyz::openbmc_project::Common::Device::ReadFailure;
284
285 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
286 metadata::CALLOUT_DEVICE_PATH(
287 fs::canonical(basePath).c_str()));
288 }
289 }
290 return data;
291 }
292
293 return std::vector<uint8_t>{};
294 }
295
write(const std::string & name,int value,Type type)296 void PMBus::write(const std::string& name, int value, Type type)
297 {
298 std::ofstream file;
299 fs::path path = getPath(type);
300
301 path /= name;
302
303 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
304 std::ofstream::eofbit);
305
306 try
307 {
308 file.open(path);
309 file << value;
310 }
311 catch (const std::exception& e)
312 {
313 auto rc = errno;
314 log<level::ERR>((std::string("Failed to write sysfs file "
315 "errno=") +
316 std::to_string(rc) + " FILENAME=" + path.string())
317 .c_str());
318
319 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
320
321 elog<WriteFailure>(
322 metadata::CALLOUT_ERRNO(rc),
323 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
324 }
325 }
326
writeBinary(const std::string & name,std::vector<uint8_t> data,Type type)327 void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
328 Type type)
329 {
330 std::ofstream file;
331 fs::path path = getPath(type);
332
333 path /= name;
334
335 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
336 std::ofstream::eofbit);
337
338 try
339 {
340 // I need to specify binary mode when I construct the ofstream
341 file.open(path, std::ios::out | std::ios_base::binary);
342 log<level::DEBUG>(std::string("Write data to sysfs file "
343 "FILENAME=" +
344 path.string())
345 .c_str());
346 file.write(reinterpret_cast<const char*>(&data[0]), data.size());
347 }
348 catch (const std::exception& e)
349 {
350 auto rc = errno;
351 log<level::ERR>(
352 (std::string("Failed to write binary data to sysfs file "
353 "errno=") +
354 std::to_string(rc) + " FILENAME=" + path.string())
355 .c_str());
356
357 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
358
359 elog<WriteFailure>(
360 metadata::CALLOUT_ERRNO(rc),
361 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
362 }
363 }
364
findHwmonDir()365 void PMBus::findHwmonDir()
366 {
367 fs::path path{basePath};
368 path /= "hwmon";
369
370 // Make sure the directory exists, otherwise for things that can be
371 // dynamically present or not present an exception will be thrown if the
372 // hwmon directory is not there, resulting in a program termination.
373 if (fs::is_directory(path))
374 {
375 // look for <basePath>/hwmon/hwmonN/
376 for (auto& f : fs::directory_iterator(path))
377 {
378 if ((f.path().filename().string().find("hwmon") !=
379 std::string::npos) &&
380 (fs::is_directory(f.path())))
381 {
382 hwmonDir = f.path().filename();
383 break;
384 }
385 }
386 }
387
388 // Don't really want to crash here, just log it
389 // and let accesses fail later
390 if (hwmonDir.empty())
391 {
392 log<level::INFO>(std::string("Unable to find hwmon directory "
393 "in device base path"
394 " DEVICE_PATH=" +
395 basePath.string())
396 .c_str());
397 }
398 }
399
400 std::unique_ptr<PMBusBase>
createPMBus(std::uint8_t bus,const std::string & address)401 PMBus::createPMBus(std::uint8_t bus, const std::string& address)
402 {
403 const std::string physpath = {
404 "/sys/bus/i2c/devices/" + std::to_string(bus) + "-" + address};
405 auto interface = std::make_unique<PMBus>(physpath);
406
407 return interface;
408 }
409
createPMBus(std::uint8_t bus,const std::string & address)410 std::unique_ptr<PMBusBase> createPMBus(std::uint8_t bus,
411 const std::string& address)
412 {
413 return PMBus::createPMBus(bus, address);
414 }
415
416 } // namespace pmbus
417 } // namespace phosphor
418