xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision f654267d)
1 /*
2 // Copyright (c) 2018 Intel 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 
17 #include "gpio.hpp"
18 
19 #include "settings.hpp"
20 
21 #include <fcntl.h>
22 #include <unistd.h>
23 
24 #include <experimental/filesystem>
25 #include <fstream>
26 #include <gpioplus/utility/aspeed.hpp>
27 #include <nlohmann/json.hpp>
28 #include <optional>
29 #include <phosphor-logging/log.hpp>
30 #include <tuple>
31 
32 const std::string gpioDev = "/sys/class/gpio";
33 static constexpr auto gpioDefs = "/etc/default/obmc/gpio/gpio_defs.json";
34 
35 using namespace phosphor::logging;
36 namespace fs = std::experimental::filesystem;
37 
38 void closeGpio(int fd)
39 {
40     if (fd > 0)
41     {
42         ::close(fd);
43     }
44 }
45 
46 uint32_t getGpioBase()
47 {
48     // Look for a /sys/class/gpio/gpiochip*/label file
49     // with a value of GPIO_BASE_LABEL_NAME.  Then read
50     // the base value from the 'base' file in that directory.
51 #ifdef LOOKUP_GPIO_BASE
52     for (auto& f : fs::directory_iterator(gpioDev))
53     {
54         std::string path{f.path()};
55         if (path.find("gpiochip") == std::string::npos)
56         {
57             continue;
58         }
59 
60         std::ifstream labelStream{path + "/label"};
61         std::string label;
62         labelStream >> label;
63 
64         if (label == GPIO_BASE_LABEL_NAME)
65         {
66             uint32_t base;
67             std::ifstream baseStream{path + "/base"};
68             baseStream >> base;
69             return base;
70         }
71     }
72 
73     log<level::ERR>("Could not find GPIO base");
74     throw std::runtime_error("Could not find GPIO base!");
75 #else
76     return 0;
77 #endif
78 }
79 
80 uint32_t getGpioNum(const std::string& gpioPin)
81 {
82     // gpioplus promises that they will figure out how to easily
83     // support multiple BMC vendors when the time comes.
84     auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
85 
86     return getGpioBase() + offset;
87 }
88 
89 bool gpioDefined(const std::string& gpioName)
90 {
91     try
92     {
93         std::ifstream gpios{gpioDefs};
94         auto json = nlohmann::json::parse(gpios, nullptr, true);
95         auto defs = json["gpio_definitions"];
96 
97         auto gpio =
98             std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
99                 return gpioName == g["name"];
100             });
101 
102         if (gpio != defs.end())
103         {
104             return true;
105         }
106     }
107     catch (std::exception& e)
108     {
109         log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
110                         entry("GPIO_NAME=%s", gpioName.c_str()));
111     }
112     return false;
113 }
114 
115 std::optional<std::tuple<int, std::string>>
116     getGpioConfig(const std::string& gpioName)
117 {
118 
119     try
120     {
121         std::ifstream gpios{gpioDefs};
122         auto json = nlohmann::json::parse(gpios, nullptr, true);
123         auto defs = json["gpio_definitions"];
124 
125         auto gpio =
126             std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
127                 return gpioName == g["name"];
128             });
129 
130         if (gpio != defs.end())
131         {
132             return std::make_tuple(getGpioNum((*gpio)["pin"]),
133                                    (*gpio)["direction"]);
134         }
135         else
136         {
137             log<level::ERR>("Unable to find GPIO in the definitions",
138                             entry("GPIO_NAME=%s", gpioName.c_str()));
139         }
140     }
141     catch (std::exception& e)
142     {
143         log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
144                         entry("GPIO_NAME=%s", gpioName.c_str()));
145     }
146     return {};
147 }
148 
149 int configGpio(const char* gpioName, int* fd, sdbusplus::bus::bus& bus)
150 {
151     auto config = getGpioConfig(gpioName);
152     if (!config)
153     {
154         return -1;
155     }
156 
157     auto [gpioNum, gpioDirection] = *config;
158 
159     std::string devPath{gpioDev};
160 
161     std::fstream stream;
162 
163     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
164 
165     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
166 
167     fs::path fullPath(devPath);
168 
169     if (fs::exists(fullPath))
170     {
171         log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
172     }
173     else
174     {
175         devPath = gpioDev + "/export";
176 
177         stream.open(devPath, std::fstream::out);
178         try
179         {
180             stream << gpioNum;
181             stream.close();
182         }
183 
184         catch (const std::exception& e)
185         {
186             log<level::ERR>("Error in writing!",
187                             entry("PATH=%s", devPath.c_str()),
188                             entry("NUM=%d", gpioNum));
189             return -1;
190         }
191     }
192 
193     if (gpioDirection == "out")
194     {
195         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
196 
197         uint32_t currentValue;
198 
199         stream.open(devPath, std::fstream::in);
200         try
201         {
202             stream >> currentValue;
203             stream.close();
204         }
205 
206         catch (const std::exception& e)
207         {
208             log<level::ERR>("Error in reading!",
209                             entry("PATH=%s", devPath.c_str()));
210             return -1;
211         }
212 
213         const char* direction = currentValue ? "high" : "low";
214 
215         devPath.clear();
216         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
217 
218         stream.open(devPath, std::fstream::out);
219         try
220         {
221             stream << direction;
222             stream.close();
223         }
224 
225         catch (const std::exception& e)
226         {
227             log<level::ERR>("Error in writing!");
228             return -1;
229         }
230     }
231     else if (gpioDirection == "in")
232     {
233         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
234 
235         stream.open(devPath, std::fstream::out);
236         try
237         {
238             stream << gpioDirection;
239             stream.close();
240         }
241 
242         catch (const std::exception& e)
243         {
244             log<level::ERR>("Error in writing!");
245             return -1;
246         }
247     }
248     else if ((gpioDirection == "both"))
249     {
250 
251         // For gpio configured as ‘both’, it is an interrupt pin and trigged on
252         // both rising and falling signals
253         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
254 
255         stream.open(devPath, std::fstream::out);
256         try
257         {
258             stream << gpioDirection;
259             stream.close();
260         }
261 
262         catch (const std::exception& e)
263         {
264             log<level::ERR>("Error in writing!");
265             return -1;
266         }
267     }
268 
269     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
270 
271     *fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
272 
273     if (*fd < 0)
274     {
275         log<level::ERR>("open error!");
276         return -1;
277     }
278 
279     return 0;
280 }
281