1a9d39e30SKuiying Wang /*
2a9d39e30SKuiying Wang // Copyright (c) 2018 Intel Corporation
3a9d39e30SKuiying Wang //
4a9d39e30SKuiying Wang // Licensed under the Apache License, Version 2.0 (the "License");
5a9d39e30SKuiying Wang // you may not use this file except in compliance with the License.
6a9d39e30SKuiying Wang // You may obtain a copy of the License at
7a9d39e30SKuiying Wang //
8a9d39e30SKuiying Wang // http://www.apache.org/licenses/LICENSE-2.0
9a9d39e30SKuiying Wang //
10a9d39e30SKuiying Wang // Unless required by applicable law or agreed to in writing, software
11a9d39e30SKuiying Wang // distributed under the License is distributed on an "AS IS" BASIS,
12a9d39e30SKuiying Wang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a9d39e30SKuiying Wang // See the License for the specific language governing permissions and
14a9d39e30SKuiying Wang // limitations under the License.
15a9d39e30SKuiying Wang */
16a9d39e30SKuiying Wang
17ccd7db05SDelphine CC Chiu #include "button_config.hpp"
1815c60e2fSDelphine CC Chiu #include "config.hpp"
198f2c95a4SMatt Spinler
209fb15970SGeorge Liu #include <error.h>
21a9d39e30SKuiying Wang #include <fcntl.h>
220d9377d2SPatrick Venture #include <unistd.h>
230d9377d2SPatrick Venture
248f2c95a4SMatt Spinler #include <gpioplus/utility/aspeed.hpp>
258605bdffSMatt Spinler #include <nlohmann/json.hpp>
269fb15970SGeorge Liu #include <phosphor-logging/lg2.hpp>
27a9d39e30SKuiying Wang
285b98f4dbSGeorge Liu #include <filesystem>
295b98f4dbSGeorge Liu #include <fstream>
305b98f4dbSGeorge Liu
318f2c95a4SMatt Spinler const std::string gpioDev = "/sys/class/gpio";
328eca9bb6SPatrick Williams namespace fs = std::filesystem;
33d219fa3cSNaveen Moses std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
34d219fa3cSNaveen Moses {GpioPolarity::activeLow, {'0', '1'}},
35d219fa3cSNaveen Moses {GpioPolarity::activeHigh, {'1', '0'}}};
368605bdffSMatt Spinler
setGpioState(int fd,GpioPolarity polarity,GpioState state)37d219fa3cSNaveen Moses void setGpioState(int fd, GpioPolarity polarity, GpioState state)
38d219fa3cSNaveen Moses {
39d219fa3cSNaveen Moses char writeBuffer;
40d219fa3cSNaveen Moses
41d219fa3cSNaveen Moses if (state == GpioState::assert)
42d219fa3cSNaveen Moses {
43d219fa3cSNaveen Moses writeBuffer = GpioValueMap[polarity].assert;
44d219fa3cSNaveen Moses }
45d219fa3cSNaveen Moses else
46d219fa3cSNaveen Moses {
47d219fa3cSNaveen Moses writeBuffer = GpioValueMap[polarity].deassert;
48d219fa3cSNaveen Moses }
49d219fa3cSNaveen Moses
50d219fa3cSNaveen Moses auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
51d219fa3cSNaveen Moses if (result < 0)
52d219fa3cSNaveen Moses {
53d219fa3cSNaveen Moses lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
54d219fa3cSNaveen Moses "ERRORNO", errno);
55d219fa3cSNaveen Moses }
56d219fa3cSNaveen Moses return;
57d219fa3cSNaveen Moses }
getGpioState(int fd,GpioPolarity polarity)58d219fa3cSNaveen Moses GpioState getGpioState(int fd, GpioPolarity polarity)
59d219fa3cSNaveen Moses {
60d219fa3cSNaveen Moses int result = -1;
61d219fa3cSNaveen Moses char readBuffer = '0';
62d219fa3cSNaveen Moses
63d219fa3cSNaveen Moses result = ::lseek(fd, 0, SEEK_SET);
64d219fa3cSNaveen Moses
65d219fa3cSNaveen Moses if (result < 0)
66d219fa3cSNaveen Moses {
67d219fa3cSNaveen Moses lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
68d219fa3cSNaveen Moses errno);
69d219fa3cSNaveen Moses return GpioState::invalid;
70d219fa3cSNaveen Moses }
71d219fa3cSNaveen Moses
72d219fa3cSNaveen Moses result = ::read(fd, &readBuffer, sizeof(readBuffer));
73d219fa3cSNaveen Moses if (result < 0)
74d219fa3cSNaveen Moses {
75d219fa3cSNaveen Moses lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
76d219fa3cSNaveen Moses "ERRORNO", errno);
77d219fa3cSNaveen Moses throw std::runtime_error("GPIO read failed");
78d219fa3cSNaveen Moses }
79d219fa3cSNaveen Moses // read the gpio state for the io event received
80d219fa3cSNaveen Moses GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
81d219fa3cSNaveen Moses ? (GpioState::assert)
82d219fa3cSNaveen Moses : (GpioState::deassert);
83d219fa3cSNaveen Moses return gpioState;
84d219fa3cSNaveen Moses }
85a9d39e30SKuiying Wang
getGpioBase()868f2c95a4SMatt Spinler uint32_t getGpioBase()
878f2c95a4SMatt Spinler {
888f2c95a4SMatt Spinler // Look for a /sys/class/gpio/gpiochip*/label file
898f2c95a4SMatt Spinler // with a value of GPIO_BASE_LABEL_NAME. Then read
908f2c95a4SMatt Spinler // the base value from the 'base' file in that directory.
918f2c95a4SMatt Spinler #ifdef LOOKUP_GPIO_BASE
928f2c95a4SMatt Spinler for (auto& f : fs::directory_iterator(gpioDev))
938f2c95a4SMatt Spinler {
948f2c95a4SMatt Spinler std::string path{f.path()};
958f2c95a4SMatt Spinler if (path.find("gpiochip") == std::string::npos)
968f2c95a4SMatt Spinler {
978f2c95a4SMatt Spinler continue;
988f2c95a4SMatt Spinler }
998f2c95a4SMatt Spinler
1008f2c95a4SMatt Spinler std::ifstream labelStream{path + "/label"};
1018f2c95a4SMatt Spinler std::string label;
1028f2c95a4SMatt Spinler labelStream >> label;
1038f2c95a4SMatt Spinler
1048f2c95a4SMatt Spinler if (label == GPIO_BASE_LABEL_NAME)
1058f2c95a4SMatt Spinler {
1068f2c95a4SMatt Spinler uint32_t base;
1078f2c95a4SMatt Spinler std::ifstream baseStream{path + "/base"};
1088f2c95a4SMatt Spinler baseStream >> base;
1098f2c95a4SMatt Spinler return base;
1108f2c95a4SMatt Spinler }
1118f2c95a4SMatt Spinler }
1128f2c95a4SMatt Spinler
1139fb15970SGeorge Liu lg2::error("Could not find GPIO base");
1148f2c95a4SMatt Spinler throw std::runtime_error("Could not find GPIO base!");
1158f2c95a4SMatt Spinler #else
1168f2c95a4SMatt Spinler return 0;
1178f2c95a4SMatt Spinler #endif
1188f2c95a4SMatt Spinler }
1198f2c95a4SMatt Spinler
getGpioNum(const std::string & gpioPin)1208f2c95a4SMatt Spinler uint32_t getGpioNum(const std::string& gpioPin)
1218f2c95a4SMatt Spinler {
1228f2c95a4SMatt Spinler // gpioplus promises that they will figure out how to easily
1238f2c95a4SMatt Spinler // support multiple BMC vendors when the time comes.
1248f2c95a4SMatt Spinler auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
1258f2c95a4SMatt Spinler
1268f2c95a4SMatt Spinler return getGpioBase() + offset;
1278f2c95a4SMatt Spinler }
1288f2c95a4SMatt Spinler
configGroupGpio(ButtonConfig & buttonIFConfig)129ccd7db05SDelphine CC Chiu int configGroupGpio(ButtonConfig& buttonIFConfig)
1308605bdffSMatt Spinler {
131dd5495cfSNaveen Moses int result = 0;
132dd5495cfSNaveen Moses // iterate the list of gpios from the button interface config
133dd5495cfSNaveen Moses // and initialize them
134dd5495cfSNaveen Moses for (auto& gpioCfg : buttonIFConfig.gpios)
1358605bdffSMatt Spinler {
136ccd7db05SDelphine CC Chiu result = configGpio(gpioCfg, buttonIFConfig);
137dd5495cfSNaveen Moses if (result < 0)
1388f2c95a4SMatt Spinler {
1399fb15970SGeorge Liu lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
1409fb15970SGeorge Liu buttonIFConfig.formFactorName, "NUM", gpioCfg.number,
1419fb15970SGeorge Liu "RESULT", result);
1428f2c95a4SMatt Spinler
143dd5495cfSNaveen Moses break;
1448f2c95a4SMatt Spinler }
1458f2c95a4SMatt Spinler }
1468f2c95a4SMatt Spinler
147dd5495cfSNaveen Moses return result;
148a9d39e30SKuiying Wang }
149a9d39e30SKuiying Wang
configGpio(GpioInfo & gpioConfig,ButtonConfig & buttonIFConfig)150ccd7db05SDelphine CC Chiu int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig)
151dd5495cfSNaveen Moses {
152dd5495cfSNaveen Moses auto gpioNum = gpioConfig.number;
153dd5495cfSNaveen Moses auto gpioDirection = gpioConfig.direction;
154a9d39e30SKuiying Wang
1558f2c95a4SMatt Spinler std::string devPath{gpioDev};
156a9d39e30SKuiying Wang
157a9d39e30SKuiying Wang std::fstream stream;
158a9d39e30SKuiying Wang
159a9d39e30SKuiying Wang stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
160a9d39e30SKuiying Wang
1618f2c95a4SMatt Spinler devPath += "/gpio" + std::to_string(gpioNum) + "/value";
162a9d39e30SKuiying Wang
1638f2c95a4SMatt Spinler fs::path fullPath(devPath);
164a9d39e30SKuiying Wang
1658f2c95a4SMatt Spinler if (fs::exists(fullPath))
166a9d39e30SKuiying Wang {
1679fb15970SGeorge Liu lg2::info("GPIO exported: {PATH}", "PATH", devPath);
168a9d39e30SKuiying Wang }
169a9d39e30SKuiying Wang else
170a9d39e30SKuiying Wang {
171a9d39e30SKuiying Wang devPath = gpioDev + "/export";
172a9d39e30SKuiying Wang
173a9d39e30SKuiying Wang stream.open(devPath, std::fstream::out);
174a9d39e30SKuiying Wang try
175a9d39e30SKuiying Wang {
176a9d39e30SKuiying Wang stream << gpioNum;
177a9d39e30SKuiying Wang stream.close();
178a9d39e30SKuiying Wang }
179a9d39e30SKuiying Wang
180a9d39e30SKuiying Wang catch (const std::exception& e)
181a9d39e30SKuiying Wang {
1829fb15970SGeorge Liu lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum,
1839fb15970SGeorge Liu "PATH", devPath, "ERROR", e);
184a9d39e30SKuiying Wang return -1;
185a9d39e30SKuiying Wang }
186a9d39e30SKuiying Wang }
187a9d39e30SKuiying Wang
188a9d39e30SKuiying Wang if (gpioDirection == "out")
189a9d39e30SKuiying Wang {
190a9d39e30SKuiying Wang devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
191a9d39e30SKuiying Wang
192a9d39e30SKuiying Wang uint32_t currentValue;
193a9d39e30SKuiying Wang
194a9d39e30SKuiying Wang stream.open(devPath, std::fstream::in);
195a9d39e30SKuiying Wang try
196a9d39e30SKuiying Wang {
197a9d39e30SKuiying Wang stream >> currentValue;
198a9d39e30SKuiying Wang stream.close();
199a9d39e30SKuiying Wang }
200a9d39e30SKuiying Wang
201a9d39e30SKuiying Wang catch (const std::exception& e)
202a9d39e30SKuiying Wang {
2039fb15970SGeorge Liu lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath,
2049fb15970SGeorge Liu "ERROR", e);
205a9d39e30SKuiying Wang return -1;
206a9d39e30SKuiying Wang }
207a9d39e30SKuiying Wang
208a9d39e30SKuiying Wang const char* direction = currentValue ? "high" : "low";
209a9d39e30SKuiying Wang
210a9d39e30SKuiying Wang devPath.clear();
211a9d39e30SKuiying Wang devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
212a9d39e30SKuiying Wang
213a9d39e30SKuiying Wang stream.open(devPath, std::fstream::out);
214a9d39e30SKuiying Wang try
215a9d39e30SKuiying Wang {
216a9d39e30SKuiying Wang stream << direction;
217a9d39e30SKuiying Wang stream.close();
218a9d39e30SKuiying Wang }
219a9d39e30SKuiying Wang
220a9d39e30SKuiying Wang catch (const std::exception& e)
221a9d39e30SKuiying Wang {
2229fb15970SGeorge Liu lg2::error("Error in writing: {ERROR}", "ERROR", e);
223a9d39e30SKuiying Wang return -1;
224a9d39e30SKuiying Wang }
225a9d39e30SKuiying Wang }
226a9d39e30SKuiying Wang else if (gpioDirection == "in")
227a9d39e30SKuiying Wang {
228a9d39e30SKuiying Wang devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
229a9d39e30SKuiying Wang
230a9d39e30SKuiying Wang stream.open(devPath, std::fstream::out);
231a9d39e30SKuiying Wang try
232a9d39e30SKuiying Wang {
233a9d39e30SKuiying Wang stream << gpioDirection;
234a9d39e30SKuiying Wang stream.close();
235a9d39e30SKuiying Wang }
236a9d39e30SKuiying Wang
237a9d39e30SKuiying Wang catch (const std::exception& e)
238a9d39e30SKuiying Wang {
2399fb15970SGeorge Liu lg2::error("Error in writing: {ERROR}", "ERROR", e);
240a9d39e30SKuiying Wang return -1;
241a9d39e30SKuiying Wang }
242a9d39e30SKuiying Wang }
243a9d39e30SKuiying Wang else if ((gpioDirection == "both"))
244a9d39e30SKuiying Wang {
245582b3f02STim Lee devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
246582b3f02STim Lee
247582b3f02STim Lee stream.open(devPath, std::fstream::out);
248582b3f02STim Lee try
249582b3f02STim Lee {
25094afa4baSGeorge Liu // Before set gpio configure as an interrupt pin, need to set
25194afa4baSGeorge Liu // direction as 'in' or edge can't set as 'rising', 'falling' and
25294afa4baSGeorge Liu // 'both'
25394afa4baSGeorge Liu const char* in_direction = "in";
254582b3f02STim Lee stream << in_direction;
255582b3f02STim Lee stream.close();
256582b3f02STim Lee }
257582b3f02STim Lee
258582b3f02STim Lee catch (const std::exception& e)
259582b3f02STim Lee {
2609fb15970SGeorge Liu lg2::error("Error in writing: {ERROR}", "ERROR", e);
261582b3f02STim Lee return -1;
262582b3f02STim Lee }
263582b3f02STim Lee devPath.clear();
264a9d39e30SKuiying Wang
265*010035eeSManojkiran Eda // For gpio configured as ‘both’, it is an interrupt pin and triggered
266*010035eeSManojkiran Eda // on both rising and falling signals
267a9d39e30SKuiying Wang devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
268a9d39e30SKuiying Wang
269a9d39e30SKuiying Wang stream.open(devPath, std::fstream::out);
270a9d39e30SKuiying Wang try
271a9d39e30SKuiying Wang {
272a9d39e30SKuiying Wang stream << gpioDirection;
273a9d39e30SKuiying Wang stream.close();
274a9d39e30SKuiying Wang }
275a9d39e30SKuiying Wang
276a9d39e30SKuiying Wang catch (const std::exception& e)
277a9d39e30SKuiying Wang {
2789fb15970SGeorge Liu lg2::error("Error in writing: {ERROR}", "ERROR", e);
279a9d39e30SKuiying Wang return -1;
280a9d39e30SKuiying Wang }
281a9d39e30SKuiying Wang }
282a9d39e30SKuiying Wang
283a9d39e30SKuiying Wang devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
284a9d39e30SKuiying Wang
285dd5495cfSNaveen Moses auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
286a9d39e30SKuiying Wang
287dd5495cfSNaveen Moses if (fd < 0)
288a9d39e30SKuiying Wang {
2899fb15970SGeorge Liu lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
2909fb15970SGeorge Liu errno);
291a9d39e30SKuiying Wang return -1;
292a9d39e30SKuiying Wang }
293a9d39e30SKuiying Wang
294dd5495cfSNaveen Moses gpioConfig.fd = fd;
295ccd7db05SDelphine CC Chiu buttonIFConfig.fds.push_back(fd);
296dd5495cfSNaveen Moses
297a9d39e30SKuiying Wang return 0;
298a9d39e30SKuiying Wang }
299