xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision 94afa4bacfda2c86fdccdd2c1a8cbeae3f2129ed)
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 <gpioplus/utility/aspeed.hpp>
25 #include <nlohmann/json.hpp>
26 #include <phosphor-logging/log.hpp>
27 
28 #include <filesystem>
29 #include <fstream>
30 
31 const std::string gpioDev = "/sys/class/gpio";
32 
33 using namespace phosphor::logging;
34 namespace fs = std::filesystem;
35 
36 void closeGpio(int fd)
37 {
38     if (fd > 0)
39     {
40         ::close(fd);
41     }
42 }
43 
44 uint32_t getGpioBase()
45 {
46     // Look for a /sys/class/gpio/gpiochip*/label file
47     // with a value of GPIO_BASE_LABEL_NAME.  Then read
48     // the base value from the 'base' file in that directory.
49 #ifdef LOOKUP_GPIO_BASE
50     for (auto& f : fs::directory_iterator(gpioDev))
51     {
52         std::string path{f.path()};
53         if (path.find("gpiochip") == std::string::npos)
54         {
55             continue;
56         }
57 
58         std::ifstream labelStream{path + "/label"};
59         std::string label;
60         labelStream >> label;
61 
62         if (label == GPIO_BASE_LABEL_NAME)
63         {
64             uint32_t base;
65             std::ifstream baseStream{path + "/base"};
66             baseStream >> base;
67             return base;
68         }
69     }
70 
71     log<level::ERR>("Could not find GPIO base");
72     throw std::runtime_error("Could not find GPIO base!");
73 #else
74     return 0;
75 #endif
76 }
77 
78 uint32_t getGpioNum(const std::string& gpioPin)
79 {
80     // gpioplus promises that they will figure out how to easily
81     // support multiple BMC vendors when the time comes.
82     auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
83 
84     return getGpioBase() + offset;
85 }
86 
87 int configGroupGpio(buttonConfig& buttonIFConfig)
88 {
89     int result = 0;
90     // iterate the list of gpios from the button interface config
91     // and initialize them
92     for (auto& gpioCfg : buttonIFConfig.gpios)
93     {
94         result = configGpio(gpioCfg);
95         if (result < 0)
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     auto gpioNum = gpioConfig.number;
113     auto gpioDirection = gpioConfig.direction;
114 
115     std::string devPath{gpioDev};
116 
117     std::fstream stream;
118 
119     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
120 
121     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
122 
123     fs::path fullPath(devPath);
124 
125     if (fs::exists(fullPath))
126     {
127         log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
128     }
129     else
130     {
131         devPath = gpioDev + "/export";
132 
133         stream.open(devPath, std::fstream::out);
134         try
135         {
136             stream << gpioNum;
137             stream.close();
138         }
139 
140         catch (const std::exception& e)
141         {
142             log<level::ERR>("Error in writing!",
143                             entry("PATH=%s", devPath.c_str()),
144                             entry("NUM=%d", gpioNum));
145             return -1;
146         }
147     }
148 
149     if (gpioDirection == "out")
150     {
151         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
152 
153         uint32_t currentValue;
154 
155         stream.open(devPath, std::fstream::in);
156         try
157         {
158             stream >> currentValue;
159             stream.close();
160         }
161 
162         catch (const std::exception& e)
163         {
164             log<level::ERR>("Error in reading!",
165                             entry("PATH=%s", devPath.c_str()));
166             return -1;
167         }
168 
169         const char* direction = currentValue ? "high" : "low";
170 
171         devPath.clear();
172         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
173 
174         stream.open(devPath, std::fstream::out);
175         try
176         {
177             stream << direction;
178             stream.close();
179         }
180 
181         catch (const std::exception& e)
182         {
183             log<level::ERR>("Error in writing!");
184             return -1;
185         }
186     }
187     else if (gpioDirection == "in")
188     {
189         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
190 
191         stream.open(devPath, std::fstream::out);
192         try
193         {
194             stream << gpioDirection;
195             stream.close();
196         }
197 
198         catch (const std::exception& e)
199         {
200             log<level::ERR>("Error in writing!");
201             return -1;
202         }
203     }
204     else if ((gpioDirection == "both"))
205     {
206         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
207 
208         stream.open(devPath, std::fstream::out);
209         try
210         {
211             // Before set gpio configure as an interrupt pin, need to set
212             // direction as 'in' or edge can't set as 'rising', 'falling' and
213             // 'both'
214             const char* in_direction = "in";
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