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