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