xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision d219fa3c)
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 namespace fs = std::filesystem;
34 std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
35     {GpioPolarity::activeLow, {'0', '1'}},
36     {GpioPolarity::activeHigh, {'1', '0'}}};
37 
38 void setGpioState(int fd, GpioPolarity polarity, GpioState state)
39 {
40     char writeBuffer;
41 
42     if (state == GpioState::assert)
43     {
44         writeBuffer = GpioValueMap[polarity].assert;
45     }
46     else
47     {
48         writeBuffer = GpioValueMap[polarity].deassert;
49     }
50 
51     auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
52     if (result < 0)
53     {
54         lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
55                    "ERRORNO", errno);
56     }
57     return;
58 }
59 GpioState getGpioState(int fd, GpioPolarity polarity)
60 {
61     int result = -1;
62     char readBuffer = '0';
63 
64     result = ::lseek(fd, 0, SEEK_SET);
65 
66     if (result < 0)
67     {
68         lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
69                    errno);
70         return GpioState::invalid;
71     }
72 
73     result = ::read(fd, &readBuffer, sizeof(readBuffer));
74     if (result < 0)
75     {
76         lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
77                    "ERRORNO", errno);
78         throw std::runtime_error("GPIO read failed");
79     }
80     // read the gpio state for the io event received
81     GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
82                               ? (GpioState::assert)
83                               : (GpioState::deassert);
84     return gpioState;
85 }
86 void closeGpio(int fd)
87 {
88     if (fd > 0)
89     {
90         ::close(fd);
91     }
92 }
93 
94 uint32_t getGpioBase()
95 {
96     // Look for a /sys/class/gpio/gpiochip*/label file
97     // with a value of GPIO_BASE_LABEL_NAME.  Then read
98     // the base value from the 'base' file in that directory.
99 #ifdef LOOKUP_GPIO_BASE
100     for (auto& f : fs::directory_iterator(gpioDev))
101     {
102         std::string path{f.path()};
103         if (path.find("gpiochip") == std::string::npos)
104         {
105             continue;
106         }
107 
108         std::ifstream labelStream{path + "/label"};
109         std::string label;
110         labelStream >> label;
111 
112         if (label == GPIO_BASE_LABEL_NAME)
113         {
114             uint32_t base;
115             std::ifstream baseStream{path + "/base"};
116             baseStream >> base;
117             return base;
118         }
119     }
120 
121     lg2::error("Could not find GPIO base");
122     throw std::runtime_error("Could not find GPIO base!");
123 #else
124     return 0;
125 #endif
126 }
127 
128 uint32_t getGpioNum(const std::string& gpioPin)
129 {
130     // gpioplus promises that they will figure out how to easily
131     // support multiple BMC vendors when the time comes.
132     auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
133 
134     return getGpioBase() + offset;
135 }
136 
137 int configGroupGpio(buttonConfig& buttonIFConfig)
138 {
139     int result = 0;
140     // iterate the list of gpios from the button interface config
141     // and initialize them
142     for (auto& gpioCfg : buttonIFConfig.gpios)
143     {
144         result = configGpio(gpioCfg);
145         if (result < 0)
146         {
147             lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
148                        buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
149                        "RESULT", result);
150 
151             break;
152         }
153     }
154 
155     return result;
156 }
157 
158 int configGpio(gpioInfo& gpioConfig)
159 {
160     auto gpioNum = gpioConfig.number;
161     auto gpioDirection = gpioConfig.direction;
162 
163     std::string devPath{gpioDev};
164 
165     std::fstream stream;
166 
167     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
168 
169     devPath += "/gpio" + std::to_string(gpioNum) + "/value";
170 
171     fs::path fullPath(devPath);
172 
173     if (fs::exists(fullPath))
174     {
175         lg2::info("GPIO exported: {PATH}", "PATH", devPath);
176     }
177     else
178     {
179         devPath = gpioDev + "/export";
180 
181         stream.open(devPath, std::fstream::out);
182         try
183         {
184             stream << gpioNum;
185             stream.close();
186         }
187 
188         catch (const std::exception& e)
189         {
190             lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
191                        "PATH", devPath, "ERROR", e);
192             return -1;
193         }
194     }
195 
196     if (gpioDirection == "out")
197     {
198         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
199 
200         uint32_t currentValue;
201 
202         stream.open(devPath, std::fstream::in);
203         try
204         {
205             stream >> currentValue;
206             stream.close();
207         }
208 
209         catch (const std::exception& e)
210         {
211             lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
212                        "ERROR", e);
213             return -1;
214         }
215 
216         const char* direction = currentValue ? "high" : "low";
217 
218         devPath.clear();
219         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
220 
221         stream.open(devPath, std::fstream::out);
222         try
223         {
224             stream << direction;
225             stream.close();
226         }
227 
228         catch (const std::exception& e)
229         {
230             lg2::error("Error in writing: {ERROR}", "ERROR", e);
231             return -1;
232         }
233     }
234     else if (gpioDirection == "in")
235     {
236         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
237 
238         stream.open(devPath, std::fstream::out);
239         try
240         {
241             stream << gpioDirection;
242             stream.close();
243         }
244 
245         catch (const std::exception& e)
246         {
247             lg2::error("Error in writing: {ERROR}", "ERROR", e);
248             return -1;
249         }
250     }
251     else if ((gpioDirection == "both"))
252     {
253         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
254 
255         stream.open(devPath, std::fstream::out);
256         try
257         {
258             // Before set gpio configure as an interrupt pin, need to set
259             // direction as 'in' or edge can't set as 'rising', 'falling' and
260             // 'both'
261             const char* in_direction = "in";
262             stream << in_direction;
263             stream.close();
264         }
265 
266         catch (const std::exception& e)
267         {
268             lg2::error("Error in writing: {ERROR}", "ERROR", e);
269             return -1;
270         }
271         devPath.clear();
272 
273         // For gpio configured as ‘both’, it is an interrupt pin and trigged on
274         // both rising and falling signals
275         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
276 
277         stream.open(devPath, std::fstream::out);
278         try
279         {
280             stream << gpioDirection;
281             stream.close();
282         }
283 
284         catch (const std::exception& e)
285         {
286             lg2::error("Error in writing: {ERROR}", "ERROR", e);
287             return -1;
288         }
289     }
290 
291     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
292 
293     auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
294 
295     if (fd < 0)
296     {
297         lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
298                    errno);
299         return -1;
300     }
301 
302     gpioConfig.fd = fd;
303 
304     return 0;
305 }
306