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 /** 35 * @brief Helper to close a file handle 36 */ 37 struct FileCloser 38 { 39 void operator()(FILE* fp) const 40 { 41 fclose(fp); 42 } 43 }; 44 45 std::string PMBus::insertPageNum(const std::string& templateName, 46 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 | 94 std::ifstream::badbit | 95 std::ifstream::eofbit); 96 try 97 { 98 file.open(path); 99 file >> name; 100 } 101 catch (std::exception& e) 102 { 103 log<level::ERR>("Unable to read PMBus device name", 104 entry("PATH=%s", path.c_str())); 105 } 106 107 return name; 108 } 109 110 bool PMBus::readBitInPage(const std::string& name, 111 size_t page, 112 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 | 127 std::ifstream::badbit | 128 std::ifstream::eofbit); 129 130 try 131 { 132 char* err = NULL; 133 std::string val{1, '\0'}; 134 135 file.open(path); 136 file.read(&val[0], 1); 137 138 value = strtoul(val.c_str(), &err, 10); 139 140 if (*err) 141 { 142 log<level::ERR>("Invalid character in sysfs file", 143 entry("FILE=%s", path.c_str()), 144 entry("CONTENTS=%s", val.c_str())); 145 146 //Catch below and handle as a read failure 147 elog<InternalFailure>(); 148 } 149 } 150 catch (std::exception& e) 151 { 152 auto rc = errno; 153 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 value != 0; 165 } 166 167 bool PMBus::exists(const std::string& name, Type type) 168 { 169 auto path = getPath(type); 170 path /= name; 171 return fs::exists(path); 172 } 173 174 uint64_t PMBus::read(const std::string& name, Type type) 175 { 176 uint64_t data = 0; 177 std::ifstream file; 178 auto path = getPath(type); 179 path /= name; 180 181 file.exceptions(std::ifstream::failbit | 182 std::ifstream::badbit | 183 std::ifstream::eofbit); 184 185 try 186 { 187 file.open(path); 188 file >> std::hex >> data; 189 } 190 catch (std::exception& e) 191 { 192 auto rc = errno; 193 log<level::ERR>("Failed to read sysfs file", 194 entry("FILENAME=%s", path.c_str())); 195 196 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 197 198 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 199 metadata::CALLOUT_DEVICE_PATH( 200 fs::canonical(basePath).c_str())); 201 } 202 203 return data; 204 } 205 206 std::string PMBus::readString(const std::string& name, Type type) 207 { 208 std::string data; 209 std::ifstream file; 210 auto path = getPath(type); 211 path /= name; 212 213 file.exceptions(std::ifstream::failbit | 214 std::ifstream::badbit | 215 std::ifstream::eofbit); 216 217 try 218 { 219 file.open(path); 220 file >> data; 221 } 222 catch (std::exception& e) 223 { 224 auto rc = errno; 225 log<level::ERR>("Failed to read sysfs file", 226 entry("FILENAME=%s", path.c_str())); 227 228 using metadata = xyz::openbmc_project::Common::Device::ReadFailure; 229 230 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 231 metadata::CALLOUT_DEVICE_PATH( 232 fs::canonical(basePath).c_str())); 233 } 234 235 return data; 236 } 237 238 std::vector<uint8_t> PMBus::readBinary(const std::string& name, 239 Type type, 240 size_t length) 241 { 242 auto path = getPath(type) / name; 243 244 //Use C style IO because it's easier to handle telling the difference 245 //between hitting EOF or getting an actual error. 246 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")}; 247 248 if (file) 249 { 250 std::vector<uint8_t> data(length, 0); 251 252 auto bytes = fread(data.data(), 253 sizeof(decltype(data[0])), 254 length, 255 file.get()); 256 257 if (bytes != length) 258 { 259 //If hit EOF, just return the amount of data that was read. 260 if (feof(file.get())) 261 { 262 data.erase(data.begin() + bytes, data.end()); 263 } 264 else if (ferror(file.get())) 265 { 266 auto rc = errno; 267 log<level::ERR>("Failed to read sysfs file", 268 entry("FILENAME=%s", path.c_str())); 269 270 using metadata = xyz::openbmc_project::Common:: 271 Device::ReadFailure; 272 273 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 274 metadata::CALLOUT_DEVICE_PATH( 275 fs::canonical(basePath).c_str())); 276 } 277 } 278 return data; 279 } 280 281 return std::vector<uint8_t>{}; 282 } 283 284 void PMBus::write(const std::string& name, int value, Type type) 285 { 286 std::ofstream file; 287 fs::path path = getPath(type); 288 289 path /= name; 290 291 file.exceptions(std::ofstream::failbit | 292 std::ofstream::badbit | 293 std::ofstream::eofbit); 294 295 try 296 { 297 file.open(path); 298 file << value; 299 } 300 catch (const std::exception& e) 301 { 302 auto rc = errno; 303 304 log<level::ERR>("Failed to write sysfs file", 305 entry("FILENAME=%s", path.c_str())); 306 307 using metadata = xyz::openbmc_project::Common::Device::WriteFailure; 308 309 elog<WriteFailure>(metadata::CALLOUT_ERRNO(rc), 310 metadata::CALLOUT_DEVICE_PATH( 311 fs::canonical(basePath).c_str())); 312 } 313 } 314 315 void PMBus::findHwmonDir() 316 { 317 fs::path path{basePath}; 318 path /= "hwmon"; 319 320 // Make sure the directory exists, otherwise for things that can be 321 // dynamically present or not present an exception will be thrown if the 322 // hwmon directory is not there, resulting in a program termination. 323 if (fs::is_directory(path)) 324 { 325 //look for <basePath>/hwmon/hwmonN/ 326 for (auto& f : fs::directory_iterator(path)) 327 { 328 if ((f.path().filename().string().find("hwmon") != 329 std::string::npos) && 330 (fs::is_directory(f.path()))) 331 { 332 hwmonDir = f.path().filename(); 333 break; 334 } 335 } 336 } 337 338 //Don't really want to crash here, just log it 339 //and let accesses fail later 340 if (hwmonDir.empty()) 341 { 342 log<level::INFO>("Unable to find hwmon directory " 343 "in device base path", 344 entry("DEVICE_PATH=%s", basePath.c_str())); 345 } 346 347 } 348 349 } 350 } 351