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 <filesystem> 19 #include <fstream> 20 #include <phosphor-logging/elog-errors.hpp> 21 #include <phosphor-logging/elog.hpp> 22 #include <xyz/openbmc_project/Common/Device/error.hpp> 23 #include <xyz/openbmc_project/Common/error.hpp> 24 25 namespace witherspoon 26 { 27 namespace pmbus 28 { 29 30 using namespace phosphor::logging; 31 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 32 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error; 33 namespace fs = std::filesystem; 34 35 /** 36 * @brief Helper to close a file handle 37 */ 38 struct FileCloser 39 { 40 void operator()(FILE* fp) const 41 { 42 fclose(fp); 43 } 44 }; 45 46 std::string PMBus::insertPageNum(const std::string& templateName, size_t page) 47 { 48 auto name = templateName; 49 50 // insert the page where the P was 51 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 60 fs::path PMBus::getPath(Type type) 61 { 62 switch (type) 63 { 64 default: 65 /* fall through */ 66 case Type::Base: 67 return basePath; 68 break; 69 case Type::Hwmon: 70 return basePath / "hwmon" / hwmonDir; 71 break; 72 case Type::Debug: 73 return debugPath / "pmbus" / hwmonDir; 74 break; 75 case Type::DeviceDebug: 76 { 77 auto dir = driverName + "." + std::to_string(instance); 78 return debugPath / dir; 79 break; 80 } 81 case Type::HwmonDeviceDebug: 82 return debugPath / "pmbus" / hwmonDir / getDeviceName(); 83 break; 84 } 85 } 86 87 std::string PMBus::getDeviceName() 88 { 89 std::string name; 90 std::ifstream file; 91 auto path = basePath / "name"; 92 93 file.exceptions(std::ifstream::failbit | std::ifstream::badbit | 94 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 109 bool PMBus::readBitInPage(const std::string& name, size_t page, Type type) 110 { 111 auto pagedBit = insertPageNum(name, page); 112 return readBit(pagedBit, type); 113 } 114 115 bool PMBus::readBit(const std::string& name, Type type) 116 { 117 unsigned long int value = 0; 118 std::ifstream file; 119 fs::path path = getPath(type); 120 121 path /= name; 122 123 file.exceptions(std::ifstream::failbit | std::ifstream::badbit | 124 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 142 // Catch below and handle as a read failure 143 elog<InternalFailure>(); 144 } 145 } 146 catch (std::exception& e) 147 { 148 auto rc = errno; 149 150 log<level::ERR>("Failed to read sysfs file", 151 entry("FILENAME=%s", path.c_str())); 152 153 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 154 155 elog<ReadFailure>( 156 metadata::CALLOUT_ERRNO(rc), 157 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); 158 } 159 160 return value != 0; 161 } 162 163 bool PMBus::exists(const std::string& name, Type type) 164 { 165 auto path = getPath(type); 166 path /= name; 167 return fs::exists(path); 168 } 169 170 uint64_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 177 file.exceptions(std::ifstream::failbit | std::ifstream::badbit | 178 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 191 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 192 193 elog<ReadFailure>( 194 metadata::CALLOUT_ERRNO(rc), 195 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); 196 } 197 198 return data; 199 } 200 201 std::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 208 file.exceptions(std::ifstream::failbit | std::ifstream::badbit | 209 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 224 elog<ReadFailure>( 225 metadata::CALLOUT_ERRNO(rc), 226 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); 227 } 228 229 return data; 230 } 231 232 std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type, 233 size_t length) 234 { 235 auto path = getPath(type) / name; 236 237 // Use C style IO because it's easier to handle telling the difference 238 // between hitting EOF or getting an actual error. 239 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 245 auto bytes = 246 fread(data.data(), sizeof(decltype(data[0])), length, file.get()); 247 248 if (bytes != length) 249 { 250 // If hit EOF, just return the amount of data that was read. 251 if (feof(file.get())) 252 { 253 data.erase(data.begin() + bytes, data.end()); 254 } 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 261 using metadata = 262 xyz::openbmc_project::Common::Device::ReadFailure; 263 264 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 265 metadata::CALLOUT_DEVICE_PATH( 266 fs::canonical(basePath).c_str())); 267 } 268 } 269 return data; 270 } 271 272 return std::vector<uint8_t>{}; 273 } 274 275 void PMBus::write(const std::string& name, int value, Type type) 276 { 277 std::ofstream file; 278 fs::path path = getPath(type); 279 280 path /= name; 281 282 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 283 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", 295 entry("FILENAME=%s", path.c_str())); 296 297 using metadata = xyz::openbmc_project::Common::Device::WriteFailure; 298 299 elog<WriteFailure>( 300 metadata::CALLOUT_ERRNO(rc), 301 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str())); 302 } 303 } 304 305 void PMBus::findHwmonDir() 306 { 307 fs::path path{basePath}; 308 path /= "hwmon"; 309 310 // 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)) 314 { 315 // look for <basePath>/hwmon/hwmonN/ 316 for (auto& f : fs::directory_iterator(path)) 317 { 318 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 } 325 } 326 } 327 328 // Don't really want to crash here, just log it 329 // and let accesses fail later 330 if (hwmonDir.empty()) 331 { 332 log<level::INFO>("Unable to find hwmon directory " 333 "in device base path", 334 entry("DEVICE_PATH=%s", basePath.c_str())); 335 } 336 } 337 338 } // namespace pmbus 339 } // namespace witherspoon 340