xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision 9fb15970)
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 "config.h"
18 
19 #include "gpio.hpp"
20 
21 #include <error.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 
25 #include <gpioplus/utility/aspeed.hpp>
26 #include <nlohmann/json.hpp>
27 #include <phosphor-logging/lg2.hpp>
28 
29 #include <filesystem>
30 #include <fstream>
31 
32 const std::string gpioDev = "/sys/class/gpio";
33 
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     lg2::error("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             lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
98                        buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
99                        "RESULT", result);
100 
101             break;
102         }
103     }
104 
105     return result;
106 }
107 
108 int configGpio(gpioInfo& gpioConfig)
109 {
110     auto gpioNum = gpioConfig.number;
111     auto gpioDirection = gpioConfig.direction;
112 
113     std::string devPath{gpioDev};
114 
115     std::fstream stream;
116 
117     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
118 
119     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
120 
121     fs::path fullPath(devPath);
122 
123     if (fs::exists(fullPath))
124     {
125         lg2::info("GPIO exported: {PATH}", "PATH", devPath);
126     }
127     else
128     {
129         devPath = gpioDev + "/export";
130 
131         stream.open(devPath, std::fstream::out);
132         try
133         {
134             stream << gpioNum;
135             stream.close();
136         }
137 
138         catch (const std::exception& e)
139         {
140             lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
141                        "PATH", devPath, "ERROR", e);
142             return -1;
143         }
144     }
145 
146     if (gpioDirection == "out")
147     {
148         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
149 
150         uint32_t currentValue;
151 
152         stream.open(devPath, std::fstream::in);
153         try
154         {
155             stream >> currentValue;
156             stream.close();
157         }
158 
159         catch (const std::exception& e)
160         {
161             lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
162                        "ERROR", e);
163             return -1;
164         }
165 
166         const char* direction = currentValue ? "high" : "low";
167 
168         devPath.clear();
169         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
170 
171         stream.open(devPath, std::fstream::out);
172         try
173         {
174             stream << direction;
175             stream.close();
176         }
177 
178         catch (const std::exception& e)
179         {
180             lg2::error("Error in writing: {ERROR}", "ERROR", e);
181             return -1;
182         }
183     }
184     else if (gpioDirection == "in")
185     {
186         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
187 
188         stream.open(devPath, std::fstream::out);
189         try
190         {
191             stream << gpioDirection;
192             stream.close();
193         }
194 
195         catch (const std::exception& e)
196         {
197             lg2::error("Error in writing: {ERROR}", "ERROR", e);
198             return -1;
199         }
200     }
201     else if ((gpioDirection == "both"))
202     {
203         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
204 
205         stream.open(devPath, std::fstream::out);
206         try
207         {
208             // Before set gpio configure as an interrupt pin, need to set
209             // direction as 'in' or edge can't set as 'rising', 'falling' and
210             // 'both'
211             const char* in_direction = "in";
212             stream << in_direction;
213             stream.close();
214         }
215 
216         catch (const std::exception& e)
217         {
218             lg2::error("Error in writing: {ERROR}", "ERROR", e);
219             return -1;
220         }
221         devPath.clear();
222 
223         // For gpio configured as ‘both’, it is an interrupt pin and trigged on
224         // both rising and falling signals
225         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
226 
227         stream.open(devPath, std::fstream::out);
228         try
229         {
230             stream << gpioDirection;
231             stream.close();
232         }
233 
234         catch (const std::exception& e)
235         {
236             lg2::error("Error in writing: {ERROR}", "ERROR", e);
237             return -1;
238         }
239     }
240 
241     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
242 
243     auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
244 
245     if (fd < 0)
246     {
247         lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
248                    errno);
249         return -1;
250     }
251 
252     gpioConfig.fd = fd;
253 
254     return 0;
255 }
256