xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision a1af329f5746d754f5f3b13f70078f19e773726f)
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 <phosphor-logging/log.hpp>
29 
30 const std::string gpioDev = "/sys/class/gpio";
31 
32 using namespace phosphor::logging;
33 namespace fs = std::experimental::filesystem;
34 
35 void closeGpio(int fd)
36 {
37     if (fd > 0)
38     {
39         ::close(fd);
40     }
41 }
42 
43 uint32_t getGpioBase()
44 {
45     // Look for a /sys/class/gpio/gpiochip*/label file
46     // with a value of GPIO_BASE_LABEL_NAME.  Then read
47     // the base value from the 'base' file in that directory.
48 #ifdef LOOKUP_GPIO_BASE
49     for (auto& f : fs::directory_iterator(gpioDev))
50     {
51         std::string path{f.path()};
52         if (path.find("gpiochip") == std::string::npos)
53         {
54             continue;
55         }
56 
57         std::ifstream labelStream{path + "/label"};
58         std::string label;
59         labelStream >> label;
60 
61         if (label == GPIO_BASE_LABEL_NAME)
62         {
63             uint32_t base;
64             std::ifstream baseStream{path + "/base"};
65             baseStream >> base;
66             return base;
67         }
68     }
69 
70     log<level::ERR>("Could not find GPIO base");
71     throw std::runtime_error("Could not find GPIO base!");
72 #else
73     return 0;
74 #endif
75 }
76 
77 uint32_t getGpioNum(const std::string& gpioPin)
78 {
79     // gpioplus promises that they will figure out how to easily
80     // support multiple BMC vendors when the time comes.
81     auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
82 
83     return getGpioBase() + offset;
84 }
85 
86 int configGroupGpio(buttonConfig& buttonIFConfig)
87 {
88     int result = 0;
89     // iterate the list of gpios from the button interface config
90     // and initialize them
91     for (auto& gpioCfg : buttonIFConfig.gpios)
92     {
93         result = configGpio(gpioCfg);
94         if (result < 0)
95         {
96 
97             std::string errorMsg =
98                 "Error configuring gpio: GPIO_NUM=" +
99                 std::to_string(gpioCfg.number) +
100                 ",BUTTON_NAME=" + buttonIFConfig.formFactorName;
101             log<level::ERR>(errorMsg.c_str());
102 
103             break;
104         }
105     }
106 
107     return result;
108 }
109 
110 int configGpio(gpioInfo& gpioConfig)
111 {
112 
113     auto gpioNum = gpioConfig.number;
114     auto gpioDirection = gpioConfig.direction;
115 
116     std::string devPath{gpioDev};
117 
118     std::fstream stream;
119 
120     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
121 
122     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
123 
124     fs::path fullPath(devPath);
125 
126     if (fs::exists(fullPath))
127     {
128         log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
129     }
130     else
131     {
132         devPath = gpioDev + "/export";
133 
134         stream.open(devPath, std::fstream::out);
135         try
136         {
137             stream << gpioNum;
138             stream.close();
139         }
140 
141         catch (const std::exception& e)
142         {
143             log<level::ERR>("Error in writing!",
144                             entry("PATH=%s", devPath.c_str()),
145                             entry("NUM=%d", gpioNum));
146             return -1;
147         }
148     }
149 
150     if (gpioDirection == "out")
151     {
152         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
153 
154         uint32_t currentValue;
155 
156         stream.open(devPath, std::fstream::in);
157         try
158         {
159             stream >> currentValue;
160             stream.close();
161         }
162 
163         catch (const std::exception& e)
164         {
165             log<level::ERR>("Error in reading!",
166                             entry("PATH=%s", devPath.c_str()));
167             return -1;
168         }
169 
170         const char* direction = currentValue ? "high" : "low";
171 
172         devPath.clear();
173         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
174 
175         stream.open(devPath, std::fstream::out);
176         try
177         {
178             stream << direction;
179             stream.close();
180         }
181 
182         catch (const std::exception& e)
183         {
184             log<level::ERR>("Error in writing!");
185             return -1;
186         }
187     }
188     else if (gpioDirection == "in")
189     {
190         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
191 
192         stream.open(devPath, std::fstream::out);
193         try
194         {
195             stream << gpioDirection;
196             stream.close();
197         }
198 
199         catch (const std::exception& e)
200         {
201             log<level::ERR>("Error in writing!");
202             return -1;
203         }
204     }
205     else if ((gpioDirection == "both"))
206     {
207         // Before set gpio configure as an interrupt pin, need to set direction
208         // as 'in' or edge can't set as 'rising', 'falling' and 'both'
209         const char* in_direction = "in";
210         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
211 
212         stream.open(devPath, std::fstream::out);
213         try
214         {
215             stream << in_direction;
216             stream.close();
217         }
218 
219         catch (const std::exception& e)
220         {
221             log<level::ERR>("Error in writing!");
222             return -1;
223         }
224         devPath.clear();
225 
226         // For gpio configured as ‘both’, it is an interrupt pin and trigged on
227         // both rising and falling signals
228         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
229 
230         stream.open(devPath, std::fstream::out);
231         try
232         {
233             stream << gpioDirection;
234             stream.close();
235         }
236 
237         catch (const std::exception& e)
238         {
239             log<level::ERR>("Error in writing!");
240             return -1;
241         }
242     }
243 
244     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
245 
246     auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
247 
248     if (fd < 0)
249     {
250         log<level::ERR>("open error!");
251         return -1;
252     }
253 
254     gpioConfig.fd = fd;
255 
256     return 0;
257 }
258