xref: /openbmc/phosphor-buttons/src/gpio.cpp (revision 8605bdff)
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 "gpio.hpp"
18 
19 #include <fcntl.h>
20 #include <unistd.h>
21 
22 #include <experimental/filesystem>
23 #include <fstream>
24 #include <nlohmann/json.hpp>
25 #include <phosphor-logging/elog-errors.hpp>
26 #include <phosphor-logging/log.hpp>
27 #include <xyz/openbmc_project/Common/error.hpp>
28 
29 const static constexpr char* SYSMGR_SERVICE = "org.openbmc.managers.System";
30 const static constexpr char* SYSMGR_OBJ_PATH = "/org/openbmc/managers/System";
31 const static constexpr char* SYSMGR_INTERFACE = "org.openbmc.managers.System";
32 
33 static constexpr auto gpioDefs = "/etc/default/obmc/gpio/gpio_defs.json";
34 
35 using namespace phosphor::logging;
36 
37 void closeGpio(int fd)
38 {
39     if (fd > 0)
40     {
41         ::close(fd);
42     }
43 }
44 
45 bool gpioDefined(const std::string& gpioName)
46 {
47     try
48     {
49         std::ifstream gpios{gpioDefs};
50         auto json = nlohmann::json::parse(gpios, nullptr, true);
51         auto defs = json["gpio_definitions"];
52 
53         auto gpio =
54             std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
55                 return gpioName == g["name"];
56             });
57 
58         if (gpio != defs.end())
59         {
60             return true;
61         }
62     }
63     catch (std::exception& e)
64     {
65         log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
66                         entry("GPIO_NAME=%s", gpioName.c_str()));
67     }
68     return false;
69 }
70 
71 int configGpio(const char* gpioName, int* fd, sdbusplus::bus::bus& bus)
72 {
73     auto method = bus.new_method_call(SYSMGR_SERVICE, SYSMGR_OBJ_PATH,
74                                       SYSMGR_INTERFACE, "gpioInit");
75 
76     method.append(gpioName);
77 
78     auto result = bus.call(method);
79 
80     if (result.is_method_error())
81     {
82         log<level::ERR>("bus call error!");
83         return -1;
84     }
85 
86     int32_t gpioNum;
87     std::string gpioDev;
88     std::string gpioDirection;
89 
90     result.read(gpioDev, gpioNum, gpioDirection);
91 
92     std::string devPath;
93 
94     std::fstream stream;
95 
96     stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
97 
98     devPath.clear();
99     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
100 
101     std::experimental::filesystem::path fullPath(devPath);
102 
103     if (std::experimental::filesystem::exists(fullPath))
104     {
105         log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
106     }
107     else
108     {
109         devPath.clear();
110         devPath = gpioDev + "/export";
111 
112         stream.open(devPath, std::fstream::out);
113         try
114         {
115             stream << gpioNum;
116             stream.close();
117         }
118 
119         catch (const std::exception& e)
120         {
121             log<level::ERR>("Error in writing!",
122                             entry("PATH=%s", devPath.c_str()),
123                             entry("NUM=%d", gpioNum));
124             return -1;
125         }
126     }
127 
128     if (gpioDirection == "out")
129     {
130         devPath.clear();
131         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
132 
133         uint32_t currentValue;
134 
135         stream.open(devPath, std::fstream::in);
136         try
137         {
138             stream >> currentValue;
139             stream.close();
140         }
141 
142         catch (const std::exception& e)
143         {
144             log<level::ERR>("Error in reading!",
145                             entry("PATH=%s", devPath.c_str()));
146             return -1;
147         }
148 
149         const char* direction = currentValue ? "high" : "low";
150 
151         devPath.clear();
152         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
153 
154         stream.open(devPath, std::fstream::out);
155         try
156         {
157             stream << direction;
158             stream.close();
159         }
160 
161         catch (const std::exception& e)
162         {
163             log<level::ERR>("Error in writing!");
164             return -1;
165         }
166     }
167     else if (gpioDirection == "in")
168     {
169         devPath.clear();
170         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
171 
172         stream.open(devPath, std::fstream::out);
173         try
174         {
175             stream << gpioDirection;
176             stream.close();
177         }
178 
179         catch (const std::exception& e)
180         {
181             log<level::ERR>("Error in writing!");
182             return -1;
183         }
184     }
185     else if ((gpioDirection == "both"))
186     {
187 
188         // For gpio configured as ‘both’, it is an interrupt pin and trigged on
189         // both rising and falling signals
190         devPath.clear();
191         devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
192 
193         stream.open(devPath, std::fstream::out);
194         try
195         {
196             stream << gpioDirection;
197             stream.close();
198         }
199 
200         catch (const std::exception& e)
201         {
202             log<level::ERR>("Error in writing!");
203             return -1;
204         }
205     }
206 
207     devPath.clear();
208     devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
209 
210     *fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
211 
212     if (*fd < 0)
213     {
214         log<level::ERR>("open error!");
215         return -1;
216     }
217 
218     return 0;
219 }
220