blob: d58207e2441174107744b0c2f708eec1b357e4a9 [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
Delphine CC Chiuccd7db02023-02-09 14:48:53 +080017#include "button_config.hpp"
Delphine CC Chiu15c60e22024-04-12 13:01:32 -050018#include "config.hpp"
Matt Spinler8f2c95a2018-11-05 15:17:29 -060019
George Liu9fb15972022-06-20 14:54:38 +080020#include <error.h>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080021#include <fcntl.h>
Patrick Venture0d9377d2018-11-01 19:34:59 -070022#include <unistd.h>
23
Matt Spinler8f2c95a2018-11-05 15:17:29 -060024#include <gpioplus/utility/aspeed.hpp>
Matt Spinler8605bdf2018-11-05 14:55:46 -060025#include <nlohmann/json.hpp>
George Liu9fb15972022-06-20 14:54:38 +080026#include <phosphor-logging/lg2.hpp>
Kuiying Wanga9d39e32018-08-14 13:47:32 +080027
George Liu5b98f4d2022-06-20 13:31:14 +080028#include <filesystem>
29#include <fstream>
30
Matt Spinler8f2c95a2018-11-05 15:17:29 -060031const std::string gpioDev = "/sys/class/gpio";
Patrick Williams8eca9bb2022-06-16 17:11:51 -050032namespace fs = std::filesystem;
Naveen Mosesd219fa32022-07-20 00:01:46 +053033std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
34 {GpioPolarity::activeLow, {'0', '1'}},
35 {GpioPolarity::activeHigh, {'1', '0'}}};
Matt Spinler8605bdf2018-11-05 14:55:46 -060036
Naveen Mosesd219fa32022-07-20 00:01:46 +053037void 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}
58GpioState 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}
Kuiying Wanga9d39e32018-08-14 13:47:32 +080085
Matt Spinler8f2c95a2018-11-05 15:17:29 -060086uint32_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
George Liu9fb15972022-06-20 14:54:38 +0800113 lg2::error("Could not find GPIO base");
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600114 throw std::runtime_error("Could not find GPIO base!");
115#else
116 return 0;
117#endif
118}
119
120uint32_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
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800129int configGroupGpio(ButtonConfig& buttonIFConfig)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600130{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530131 int result = 0;
132 // iterate the list of gpios from the button interface config
133 // and initialize them
134 for (auto& gpioCfg : buttonIFConfig.gpios)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600135 {
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800136 result = configGpio(gpioCfg, buttonIFConfig);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530137 if (result < 0)
Matt Spinler8605bdf2018-11-05 14:55:46 -0600138 {
George Liu9fb15972022-06-20 14:54:38 +0800139 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
140 buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
141 "RESULT", result);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530142
143 break;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600144 }
145 }
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530146
147 return result;
Matt Spinler8605bdf2018-11-05 14:55:46 -0600148}
149
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800150int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig)
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600151{
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530152 auto gpioNum = gpioConfig.number;
153 auto gpioDirection = gpioConfig.direction;
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800154
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600155 std::string devPath{gpioDev};
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800156
157 std::fstream stream;
158
159 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
160
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600161 devPath += "/gpio" + std::to_string(gpioNum) + "/value";
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800162
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600163 fs::path fullPath(devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800164
Matt Spinler8f2c95a2018-11-05 15:17:29 -0600165 if (fs::exists(fullPath))
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800166 {
George Liu9fb15972022-06-20 14:54:38 +0800167 lg2::info("GPIO exported: {PATH}", "PATH", devPath);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800168 }
169 else
170 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800171 devPath = gpioDev + "/export";
172
173 stream.open(devPath, std::fstream::out);
174 try
175 {
176 stream << gpioNum;
177 stream.close();
178 }
179
Patrick Venture0d9377d2018-11-01 19:34:59 -0700180 catch (const std::exception& e)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800181 {
George Liu9fb15972022-06-20 14:54:38 +0800182 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
183 "PATH", devPath, "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800184 return -1;
185 }
186 }
187
188 if (gpioDirection == "out")
189 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800190 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 {
George Liu9fb15972022-06-20 14:54:38 +0800203 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
204 "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800205 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 {
George Liu9fb15972022-06-20 14:54:38 +0800222 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800223 return -1;
224 }
225 }
226 else if (gpioDirection == "in")
227 {
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800228 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 {
George Liu9fb15972022-06-20 14:54:38 +0800239 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800240 return -1;
241 }
242 }
243 else if ((gpioDirection == "both"))
244 {
Tim Lee582b3f02019-01-03 15:39:27 +0800245 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
246
247 stream.open(devPath, std::fstream::out);
248 try
249 {
George Liu94afa4b2022-06-20 13:36:43 +0800250 // 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";
Tim Lee582b3f02019-01-03 15:39:27 +0800254 stream << in_direction;
255 stream.close();
256 }
257
258 catch (const std::exception& e)
259 {
George Liu9fb15972022-06-20 14:54:38 +0800260 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Tim Lee582b3f02019-01-03 15:39:27 +0800261 return -1;
262 }
263 devPath.clear();
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800264
Manojkiran Eda010035e2024-06-17 14:14:38 +0530265 // For gpio configured as ‘both’, it is an interrupt pin and triggered
266 // on both rising and falling signals
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800267 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 {
George Liu9fb15972022-06-20 14:54:38 +0800278 lg2::error("Error in writing: {ERROR}", "ERROR", e);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800279 return -1;
280 }
281 }
282
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800283 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
284
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530285 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800286
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530287 if (fd < 0)
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800288 {
George Liu9fb15972022-06-20 14:54:38 +0800289 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
290 errno);
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800291 return -1;
292 }
293
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530294 gpioConfig.fd = fd;
Delphine CC Chiuccd7db02023-02-09 14:48:53 +0800295 buttonIFConfig.fds.push_back(fd);
Naveen Mosesdd5495c2021-12-03 22:40:46 +0530296
Kuiying Wanga9d39e32018-08-14 13:47:32 +0800297 return 0;
298}