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