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