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