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 { 41 void operator()(FILE* fp) const 42 { 43 fclose(fp); 44 } 45 }; 46 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 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 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 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 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>((std::string("Failed to read sysfs file " 155 "errno=") + 156 std::to_string(rc) + std::string(" FILENAME=") + 157 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 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 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 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 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 = fread(data.data(), sizeof(decltype(data[0])), length, 265 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>((std::string("Failed to read sysfs file " 278 "errno=") + 279 std::to_string(rc) + 280 " 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 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 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 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> PMBus::createPMBus(std::uint8_t bus, 401 const std::string& address) 402 { 403 const std::string physpath = {"/sys/bus/i2c/devices/" + 404 std::to_string(bus) + "-" + address}; 405 auto interface = std::make_unique<PMBus>(physpath); 406 407 return interface; 408 } 409 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