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 "settings.hpp" 20 21 #include <fcntl.h> 22 #include <unistd.h> 23 24 #include <experimental/filesystem> 25 #include <fstream> 26 #include <gpioplus/utility/aspeed.hpp> 27 #include <nlohmann/json.hpp> 28 #include <optional> 29 #include <phosphor-logging/log.hpp> 30 #include <tuple> 31 32 const std::string gpioDev = "/sys/class/gpio"; 33 static constexpr auto gpioDefs = "/etc/default/obmc/gpio/gpio_defs.json"; 34 35 using namespace phosphor::logging; 36 namespace fs = std::experimental::filesystem; 37 38 void closeGpio(int fd) 39 { 40 if (fd > 0) 41 { 42 ::close(fd); 43 } 44 } 45 46 uint32_t getGpioBase() 47 { 48 // Look for a /sys/class/gpio/gpiochip*/label file 49 // with a value of GPIO_BASE_LABEL_NAME. Then read 50 // the base value from the 'base' file in that directory. 51 #ifdef LOOKUP_GPIO_BASE 52 for (auto& f : fs::directory_iterator(gpioDev)) 53 { 54 std::string path{f.path()}; 55 if (path.find("gpiochip") == std::string::npos) 56 { 57 continue; 58 } 59 60 std::ifstream labelStream{path + "/label"}; 61 std::string label; 62 labelStream >> label; 63 64 if (label == GPIO_BASE_LABEL_NAME) 65 { 66 uint32_t base; 67 std::ifstream baseStream{path + "/base"}; 68 baseStream >> base; 69 return base; 70 } 71 } 72 73 log<level::ERR>("Could not find GPIO base"); 74 throw std::runtime_error("Could not find GPIO base!"); 75 #else 76 return 0; 77 #endif 78 } 79 80 uint32_t getGpioNum(const std::string& gpioPin) 81 { 82 // gpioplus promises that they will figure out how to easily 83 // support multiple BMC vendors when the time comes. 84 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin); 85 86 return getGpioBase() + offset; 87 } 88 89 bool gpioDefined(const std::string& gpioName) 90 { 91 try 92 { 93 std::ifstream gpios{gpioDefs}; 94 auto json = nlohmann::json::parse(gpios, nullptr, true); 95 auto defs = json["gpio_definitions"]; 96 97 auto gpio = 98 std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) { 99 return gpioName == g["name"]; 100 }); 101 102 if (gpio != defs.end()) 103 { 104 return true; 105 } 106 } 107 catch (std::exception& e) 108 { 109 log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()), 110 entry("GPIO_NAME=%s", gpioName.c_str())); 111 } 112 return false; 113 } 114 115 std::optional<std::tuple<int, std::string>> 116 getGpioConfig(const std::string& gpioName) 117 { 118 119 try 120 { 121 std::ifstream gpios{gpioDefs}; 122 auto json = nlohmann::json::parse(gpios, nullptr, true); 123 auto defs = json["gpio_definitions"]; 124 125 auto gpio = 126 std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) { 127 return gpioName == g["name"]; 128 }); 129 130 if (gpio != defs.end()) 131 { 132 return std::make_tuple(getGpioNum((*gpio)["pin"]), 133 (*gpio)["direction"]); 134 } 135 else 136 { 137 log<level::ERR>("Unable to find GPIO in the definitions", 138 entry("GPIO_NAME=%s", gpioName.c_str())); 139 } 140 } 141 catch (std::exception& e) 142 { 143 log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()), 144 entry("GPIO_NAME=%s", gpioName.c_str())); 145 } 146 return {}; 147 } 148 149 int configGpio(const char* gpioName, int* fd, sdbusplus::bus::bus& bus) 150 { 151 auto config = getGpioConfig(gpioName); 152 if (!config) 153 { 154 return -1; 155 } 156 157 auto [gpioNum, gpioDirection] = *config; 158 159 std::string devPath{gpioDev}; 160 161 std::fstream stream; 162 163 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); 164 165 devPath += "/gpio" + std::to_string(gpioNum) + "/value"; 166 167 fs::path fullPath(devPath); 168 169 if (fs::exists(fullPath)) 170 { 171 log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str())); 172 } 173 else 174 { 175 devPath = gpioDev + "/export"; 176 177 stream.open(devPath, std::fstream::out); 178 try 179 { 180 stream << gpioNum; 181 stream.close(); 182 } 183 184 catch (const std::exception& e) 185 { 186 log<level::ERR>("Error in writing!", 187 entry("PATH=%s", devPath.c_str()), 188 entry("NUM=%d", gpioNum)); 189 return -1; 190 } 191 } 192 193 if (gpioDirection == "out") 194 { 195 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 196 197 uint32_t currentValue; 198 199 stream.open(devPath, std::fstream::in); 200 try 201 { 202 stream >> currentValue; 203 stream.close(); 204 } 205 206 catch (const std::exception& e) 207 { 208 log<level::ERR>("Error in reading!", 209 entry("PATH=%s", devPath.c_str())); 210 return -1; 211 } 212 213 const char* direction = currentValue ? "high" : "low"; 214 215 devPath.clear(); 216 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 217 218 stream.open(devPath, std::fstream::out); 219 try 220 { 221 stream << direction; 222 stream.close(); 223 } 224 225 catch (const std::exception& e) 226 { 227 log<level::ERR>("Error in writing!"); 228 return -1; 229 } 230 } 231 else if (gpioDirection == "in") 232 { 233 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 234 235 stream.open(devPath, std::fstream::out); 236 try 237 { 238 stream << gpioDirection; 239 stream.close(); 240 } 241 242 catch (const std::exception& e) 243 { 244 log<level::ERR>("Error in writing!"); 245 return -1; 246 } 247 } 248 else if ((gpioDirection == "both")) 249 { 250 251 // For gpio configured as ‘both’, it is an interrupt pin and trigged on 252 // both rising and falling signals 253 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge"; 254 255 stream.open(devPath, std::fstream::out); 256 try 257 { 258 stream << gpioDirection; 259 stream.close(); 260 } 261 262 catch (const std::exception& e) 263 { 264 log<level::ERR>("Error in writing!"); 265 return -1; 266 } 267 } 268 269 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 270 271 *fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK); 272 273 if (*fd < 0) 274 { 275 log<level::ERR>("open error!"); 276 return -1; 277 } 278 279 return 0; 280 } 281