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