xref: /openbmc/phosphor-power/pmbus.cpp (revision a1e96347)
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