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 bool PMBus::exists(const std::string& name, Type type) 131 { 132 auto path = getPath(type); 133 path /= name; 134 return fs::exists(path); 135 } 136 137 uint64_t PMBus::read(const std::string& name, Type type) 138 { 139 uint64_t data = 0; 140 std::ifstream file; 141 auto path = getPath(type); 142 path /= name; 143 144 file.exceptions(std::ifstream::failbit | 145 std::ifstream::badbit | 146 std::ifstream::eofbit); 147 148 try 149 { 150 file.open(path); 151 file >> std::hex >> data; 152 } 153 catch (std::exception& e) 154 { 155 auto rc = errno; 156 log<level::ERR>("Failed to read sysfs file", 157 entry("FILENAME=%s", path.c_str())); 158 159 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure; 160 161 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc), 162 metadata::CALLOUT_DEVICE_PATH( 163 fs::canonical(basePath).c_str())); 164 } 165 166 return data; 167 } 168 169 void PMBus::write(const std::string& name, int value, Type type) 170 { 171 std::ofstream file; 172 fs::path path = getPath(type); 173 174 path /= name; 175 176 file.exceptions(std::ofstream::failbit | 177 std::ofstream::badbit | 178 std::ofstream::eofbit); 179 180 try 181 { 182 file.open(path); 183 file << value; 184 } 185 catch (const std::exception& e) 186 { 187 auto rc = errno; 188 189 log<level::ERR>("Failed to write sysfs file", 190 entry("FILENAME=%s", path.c_str())); 191 192 elog<WriteFailure>(xyz::openbmc_project::Control::Device:: 193 WriteFailure::CALLOUT_ERRNO(rc), 194 xyz::openbmc_project::Control::Device:: 195 WriteFailure::CALLOUT_DEVICE_PATH( 196 fs::canonical(basePath).c_str())); 197 } 198 } 199 200 void PMBus::findHwmonDir() 201 { 202 fs::path path{basePath}; 203 path /= "hwmon"; 204 205 // Make sure the directory exists, otherwise for things that can be 206 // dynamically present or not present an exception will be thrown if the 207 // hwmon directory is not there, resulting in a program termination. 208 if (fs::is_directory(path)) 209 { 210 //look for <basePath>/hwmon/hwmonN/ 211 for (auto& f : fs::directory_iterator(path)) 212 { 213 if ((f.path().filename().string().find("hwmon") != 214 std::string::npos) && 215 (fs::is_directory(f.path()))) 216 { 217 hwmonDir = f.path().filename(); 218 break; 219 } 220 } 221 } 222 223 //Don't really want to crash here, just log it 224 //and let accesses fail later 225 if (hwmonDir.empty()) 226 { 227 log<level::INFO>("Unable to find hwmon directory " 228 "in device base path", 229 entry("DEVICE_PATH=%s", basePath.c_str())); 230 } 231 232 } 233 234 } 235 } 236