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 <experimental/filesystem> 17 #include <fstream> 18 #include <phosphor-logging/elog.hpp> 19 #include <phosphor-logging/elog-errors.hpp> 20 #include <xyz/openbmc_project/Common/error.hpp> 21 #include <xyz/openbmc_project/Common/Device/error.hpp> 22 #include "pmbus.hpp" 23 24 namespace witherspoon 25 { 26 namespace pmbus 27 { 28 29 using namespace phosphor::logging; 30 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 31 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error; 32 namespace fs = std::experimental::filesystem; 33 34 std::string PMBus::insertPageNum(const std::string& templateName, 35 size_t page) 36 { 37 auto name = templateName; 38 39 //insert the page where the P was 40 auto pos = name.find('P'); 41 if (pos != std::string::npos) 42 { 43 name.replace(pos, 1, std::to_string(page)); 44 } 45 46 return name; 47 } 48 49 fs::path PMBus::getPath(Type type) 50 { 51 switch (type) 52 { 53 default: 54 /* fall through */ 55 case Type::Base: 56 return basePath; 57 break; 58 case Type::Hwmon: 59 return basePath / "hwmon" / hwmonDir; 60 break; 61 case Type::Debug: 62 return debugPath / "pmbus" / hwmonDir; 63 break; 64 case Type::DeviceDebug: 65 auto dir = driverName + "." + std::to_string(instance); 66 return debugPath / dir; 67 break; 68 } 69 } 70 71 bool PMBus::readBitInPage(const std::string& name, 72 size_t page, 73 Type type) 74 { 75 auto pagedBit = insertPageNum(name, page); 76 return readBit(pagedBit, type); 77 } 78 79 bool PMBus::readBit(const std::string& name, Type type) 80 { 81 unsigned long int value = 0; 82 std::ifstream file; 83 fs::path path = getPath(type); 84 85 path /= name; 86 87 file.exceptions(std::ifstream::failbit | 88 std::ifstream::badbit | 89 std::ifstream::eofbit); 90 91 try 92 { 93 char* err = NULL; 94 std::string val{1, '\0'}; 95 96 file.open(path); 97 file.read(&val[0], 1); 98 99 value = strtoul(val.c_str(), &err, 10); 100 101 if (*err) 102 { 103 log<level::ERR>("Invalid character in sysfs file", 104 entry("FILE=%s", path.c_str()), 105 entry("CONTENTS=%s", val.c_str())); 106 107 //Catch below and handle as a read failure 108 elog<InternalFailure>(); 109 } 110 } 111 catch (std::exception& e) 112 { 113 auto rc = errno; 114 115 log<level::ERR>("Failed to read sysfs file", 116 entry("FILENAME=%s", path.c_str())); 117 118 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 119 120 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 121 metadata::CALLOUT_DEVICE_PATH( 122 fs::canonical(basePath).c_str())); 123 } 124 125 return value != 0; 126 } 127 128 bool PMBus::exists(const std::string& name, Type type) 129 { 130 auto path = getPath(type); 131 path /= name; 132 return fs::exists(path); 133 } 134 135 uint64_t PMBus::read(const std::string& name, Type type) 136 { 137 uint64_t data = 0; 138 std::ifstream file; 139 auto path = getPath(type); 140 path /= name; 141 142 file.exceptions(std::ifstream::failbit | 143 std::ifstream::badbit | 144 std::ifstream::eofbit); 145 146 try 147 { 148 file.open(path); 149 file >> std::hex >> data; 150 } 151 catch (std::exception& e) 152 { 153 auto rc = errno; 154 log<level::ERR>("Failed to read sysfs file", 155 entry("FILENAME=%s", path.c_str())); 156 157 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 158 159 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 160 metadata::CALLOUT_DEVICE_PATH( 161 fs::canonical(basePath).c_str())); 162 } 163 164 return data; 165 } 166 167 void PMBus::write(const std::string& name, int value, Type type) 168 { 169 std::ofstream file; 170 fs::path path = getPath(type); 171 172 path /= name; 173 174 file.exceptions(std::ofstream::failbit | 175 std::ofstream::badbit | 176 std::ofstream::eofbit); 177 178 try 179 { 180 file.open(path); 181 file << value; 182 } 183 catch (const std::exception& e) 184 { 185 auto rc = errno; 186 187 log<level::ERR>("Failed to write sysfs file", 188 entry("FILENAME=%s", path.c_str())); 189 190 using metadata = xyz::openbmc_project::Common::Device::WriteFailure; 191 192 elog<WriteFailure>(metadata::CALLOUT_ERRNO(rc), 193 metadata::CALLOUT_DEVICE_PATH( 194 fs::canonical(basePath).c_str())); 195 } 196 } 197 198 void PMBus::findHwmonDir() 199 { 200 fs::path path{basePath}; 201 path /= "hwmon"; 202 203 // Make sure the directory exists, otherwise for things that can be 204 // dynamically present or not present an exception will be thrown if the 205 // hwmon directory is not there, resulting in a program termination. 206 if (fs::is_directory(path)) 207 { 208 //look for <basePath>/hwmon/hwmonN/ 209 for (auto& f : fs::directory_iterator(path)) 210 { 211 if ((f.path().filename().string().find("hwmon") != 212 std::string::npos) && 213 (fs::is_directory(f.path()))) 214 { 215 hwmonDir = f.path().filename(); 216 break; 217 } 218 } 219 } 220 221 //Don't really want to crash here, just log it 222 //and let accesses fail later 223 if (hwmonDir.empty()) 224 { 225 log<level::INFO>("Unable to find hwmon directory " 226 "in device base path", 227 entry("DEVICE_PATH=%s", basePath.c_str())); 228 } 229 230 } 231 232 } 233 } 234