blob: a946aa3ed5a8505e25b2e7adc1f23450c1775df8 [file] [log] [blame]
Kuiying Wanga9d39e32018-08-14 13:47:32 +08001/*
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
George Liud6a1bae2022-06-20 13:47:31 +080017#include "config.h"
Patrick Venture0d9377d2018-11-01 19:34:59 -070018
Delphine CC Chiuccd7db02023-02-09 14:48:53 +080019#include "button_config.hpp"
Matt Spinler8f2c95a2018-11-05 15:17:29 -060020
George Liu9fb15972022-06-20 14:54:38 +080021#include <error.h>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080022#include <fcntl.h>
Patrick Venture0d9377d2018-11-01 19:34:59 -070023#include <unistd.h>
24
Matt Spinler8f2c95a2018-11-05 15:17:29 -060025#include <gpioplus/utility/aspeed.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060026#include <nlohmann/json.hpp>
George Liu9fb15972022-06-20 14:54:38 +080027#include <phosphor-logging/lg2.hpp>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080028
George Liu5b98f4d2022-06-20 13:31:14 +080029#include <filesystem>
30#include <fstream>
31
Matt Spinler8f2c95a2018-11-05 15:17:29 -060032const std::string gpioDev = "/sys/class/gpio";
Patrick Williams8eca9bb2022-06-16 17:11:51 -050033namespace fs = std::filesystem;
Naveen Mosesd219fa32022-07-20 00:01:46 +053034std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
35 {GpioPolarity::activeLow, {'0', '1'}},
36 {GpioPolarity::activeHigh, {'1', '0'}}};
Matt Spinler8605bdf2018-11-05 14:55:46 -060037
Naveen Mosesd219fa32022-07-20 00:01:46 +053038void 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}
59GpioState 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}
Kuiying Wanga9d39e32018-08-14 13:47:32 +080086
Matt Spinler8f2c95a2018-11-05 15:17:29 -060087uint32_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
George Liu9fb15972022-06-20 14:54:38 +0800114 lg2::error("Could not find GPIO base");
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600115 throw std::runtime_error("Could not find GPIO base!");
116#else
117 return 0;
118#endif
119}
120
121uint32_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
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800130int configGroupGpio(ButtonConfig& buttonIFConfig)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600131{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530132 int result = 0;
133 // iterate the list of gpios from the button interface config
134 // and initialize them
135 for (auto& gpioCfg : buttonIFConfig.gpios)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600136 {
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800137 result = configGpio(gpioCfg, buttonIFConfig);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530138 if (result < 0)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600139 {
George Liu9fb15972022-06-20 14:54:38 +0800140 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
141 buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
142 "RESULT", result);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530143
144 break;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600145 }
146 }
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530147
148 return result;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600149}
150
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800151int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig)
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600152{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530153 auto gpioNum = gpioConfig.number;
154 auto gpioDirection = gpioConfig.direction;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800155
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600156 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800157
158 std::fstream stream;
159
160 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
161
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600162 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800163
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600164 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800165
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600166 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800167 {
George Liu9fb15972022-06-20 14:54:38 +0800168 lg2::info("GPIO exported: {PATH}", "PATH", devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800169 }
170 else
171 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800172 devPath = gpioDev + "/export";
173
174 stream.open(devPath, std::fstream::out);
175 try
176 {
177 stream << gpioNum;
178 stream.close();
179 }
180
Patrick Venture0d9377d2018-11-01 19:34:59 -0700181 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800182 {
George Liu9fb15972022-06-20 14:54:38 +0800183 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
184 "PATH", devPath, "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800185 return -1;
186 }
187 }
188
189 if (gpioDirection == "out")
190 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800191 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 {
George Liu9fb15972022-06-20 14:54:38 +0800204 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
205 "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800206 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 {
George Liu9fb15972022-06-20 14:54:38 +0800223 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800224 return -1;
225 }
226 }
227 else if (gpioDirection == "in")
228 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800229 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 {
George Liu9fb15972022-06-20 14:54:38 +0800240 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800241 return -1;
242 }
243 }
244 else if ((gpioDirection == "both"))
245 {
Tim Lee582b3f02019-01-03 15:39:27 +0800246 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
247
248 stream.open(devPath, std::fstream::out);
249 try
250 {
George Liu94afa4b2022-06-20 13:36:43 +0800251 // 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";
Tim Lee582b3f02019-01-03 15:39:27 +0800255 stream << in_direction;
256 stream.close();
257 }
258
259 catch (const std::exception& e)
260 {
George Liu9fb15972022-06-20 14:54:38 +0800261 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Tim Lee582b3f02019-01-03 15:39:27 +0800262 return -1;
263 }
264 devPath.clear();
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800265
266 // For gpio configured as ‘both’, it is an interrupt pin and trigged on
267 // both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800268 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 {
George Liu9fb15972022-06-20 14:54:38 +0800279 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800280 return -1;
281 }
282 }
283
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800284 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
285
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530286 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800287
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530288 if (fd < 0)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800289 {
George Liu9fb15972022-06-20 14:54:38 +0800290 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
291 errno);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800292 return -1;
293 }
294
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530295 gpioConfig.fd = fd;
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800296 buttonIFConfig.fds.push_back(fd);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530297
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800298 return 0;
299}