/** * Copyright © 2017 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pmbus.hpp" #include #include #include #include #include #include namespace phosphor { namespace pmbus { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error; namespace fs = std::filesystem; /** * @brief Helper to close a file handle */ struct FileCloser { void operator()(FILE* fp) const { fclose(fp); } }; std::string PMBus::insertPageNum(const std::string& templateName, size_t page) { auto name = templateName; // insert the page where the P was auto pos = name.find('P'); if (pos != std::string::npos) { name.replace(pos, 1, std::to_string(page)); } return name; } fs::path PMBus::getPath(Type type) { switch (type) { default: /* fall through */ case Type::Base: return basePath; break; case Type::Hwmon: return basePath / "hwmon" / hwmonDir; break; case Type::Debug: return debugPath / "pmbus" / hwmonDir; break; case Type::DeviceDebug: { auto dir = driverName + "." + std::to_string(instance); return debugPath / dir; break; } case Type::HwmonDeviceDebug: return debugPath / "pmbus" / hwmonDir / getDeviceName(); break; } } std::string PMBus::getDeviceName() { std::string name; std::ifstream file; auto path = basePath / "name"; file.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); try { file.open(path); file >> name; } catch (std::exception& e) { log((std::string("Unable to read PMBus device name " "PATH=") + path.string()) .c_str()); } return name; } bool PMBus::readBitInPage(const std::string& name, size_t page, Type type) { auto pagedBit = insertPageNum(name, page); return readBit(pagedBit, type); } bool PMBus::readBit(const std::string& name, Type type) { unsigned long int value = 0; std::ifstream file; fs::path path = getPath(type); path /= name; file.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); try { char* err = NULL; std::string val{1, '\0'}; file.open(path); file.read(&val[0], 1); value = strtoul(val.c_str(), &err, 10); if (*err) { log((std::string("Invalid character in sysfs file" " FILE=") + path.string() + std::string(" CONTENTS=") + val) .c_str()); // Catch below and handle as a read failure elog(); } } catch (std::exception& e) { auto rc = errno; log((std::string("Failed to read sysfs file " "errno=") + std::to_string(rc) + std::string(" FILENAME=") + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::ReadFailure; elog( metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); } return value != 0; } bool PMBus::exists(const std::string& name, Type type) { auto path = getPath(type); path /= name; return fs::exists(path); } uint64_t PMBus::read(const std::string& name, Type type) { uint64_t data = 0; std::ifstream file; auto path = getPath(type); path /= name; file.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); try { file.open(path); file >> std::hex >> data; } catch (std::exception& e) { auto rc = errno; log((std::string("Failed to read sysfs file " "errno=") + std::to_string(rc) + " FILENAME=" + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::ReadFailure; elog( metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); } return data; } std::string PMBus::readString(const std::string& name, Type type) { std::string data; std::ifstream file; auto path = getPath(type); path /= name; file.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); try { file.open(path); file >> data; } catch (std::exception& e) { auto rc = errno; log((std::string("Failed to read sysfs file " "errno=") + std::to_string(rc) + " FILENAME=" + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::ReadFailure; elog( metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); } return data; } std::vector PMBus::readBinary(const std::string& name, Type type, size_t length) { auto path = getPath(type) / name; // Use C style IO because it's easier to handle telling the difference // between hitting EOF or getting an actual error. std::unique_ptr file{fopen(path.c_str(), "rb")}; if (file) { std::vector data(length, 0); auto bytes = fread(data.data(), sizeof(decltype(data[0])), length, file.get()); if (bytes != length) { // If hit EOF, just return the amount of data that was read. if (feof(file.get())) { data.erase(data.begin() + bytes, data.end()); } else if (ferror(file.get())) { auto rc = errno; log((std::string("Failed to read sysfs file " "errno=") + std::to_string(rc) + " FILENAME=" + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::ReadFailure; elog(metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH( fs::canonical(basePath).c_str())); } } return data; } return std::vector{}; } void PMBus::write(const std::string& name, int value, Type type) { std::ofstream file; fs::path path = getPath(type); path /= name; file.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit); try { file.open(path); file << value; } catch (const std::exception& e) { auto rc = errno; log((std::string("Failed to write sysfs file " "errno=") + std::to_string(rc) + " FILENAME=" + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::WriteFailure; elog( metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); } } void PMBus::writeBinary(const std::string& name, std::vector data, Type type) { std::ofstream file; fs::path path = getPath(type); path /= name; file.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit); try { // I need to specify binary mode when I construct the ofstream file.open(path, std::ios::out | std::ios_base::binary); log(std::string("Write data to sysfs file " "FILENAME=" + path.string()) .c_str()); file.write(reinterpret_cast(&data[0]), data.size()); } catch (const std::exception& e) { auto rc = errno; log( (std::string("Failed to write binary data to sysfs file " "errno=") + std::to_string(rc) + " FILENAME=" + path.string()) .c_str()); using metadata = xyz::openbmc_project::Common::Device::WriteFailure; elog( metadata::CALLOUT_ERRNO(rc), metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); } } void PMBus::findHwmonDir() { fs::path path{basePath}; path /= "hwmon"; // Make sure the directory exists, otherwise for things that can be // dynamically present or not present an exception will be thrown if the // hwmon directory is not there, resulting in a program termination. if (fs::is_directory(path)) { // look for /hwmon/hwmonN/ for (auto& f : fs::directory_iterator(path)) { if ((f.path().filename().string().find("hwmon") != std::string::npos) && (fs::is_directory(f.path()))) { hwmonDir = f.path().filename(); break; } } } // Don't really want to crash here, just log it // and let accesses fail later if (hwmonDir.empty()) { log(std::string("Unable to find hwmon directory " "in device base path" " DEVICE_PATH=" + basePath.string()) .c_str()); } } std::unique_ptr PMBus::createPMBus(std::uint8_t bus, const std::string& address) { const std::string physpath = {"/sys/bus/i2c/devices/" + std::to_string(bus) + "-" + address}; auto interface = std::make_unique(physpath); return interface; } std::unique_ptr createPMBus(std::uint8_t bus, const std::string& address) { return PMBus::createPMBus(bus, address); } } // namespace pmbus } // namespace phosphor