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 "config.h" 18 19 #include "gpio.hpp" 20 21 #include <error.h> 22 #include <fcntl.h> 23 #include <unistd.h> 24 25 #include <gpioplus/utility/aspeed.hpp> 26 #include <nlohmann/json.hpp> 27 #include <phosphor-logging/lg2.hpp> 28 29 #include <filesystem> 30 #include <fstream> 31 32 const std::string gpioDev = "/sys/class/gpio"; 33 34 namespace fs = std::filesystem; 35 36 void closeGpio(int fd) 37 { 38 if (fd > 0) 39 { 40 ::close(fd); 41 } 42 } 43 44 uint32_t getGpioBase() 45 { 46 // Look for a /sys/class/gpio/gpiochip*/label file 47 // with a value of GPIO_BASE_LABEL_NAME. Then read 48 // the base value from the 'base' file in that directory. 49 #ifdef LOOKUP_GPIO_BASE 50 for (auto& f : fs::directory_iterator(gpioDev)) 51 { 52 std::string path{f.path()}; 53 if (path.find("gpiochip") == std::string::npos) 54 { 55 continue; 56 } 57 58 std::ifstream labelStream{path + "/label"}; 59 std::string label; 60 labelStream >> label; 61 62 if (label == GPIO_BASE_LABEL_NAME) 63 { 64 uint32_t base; 65 std::ifstream baseStream{path + "/base"}; 66 baseStream >> base; 67 return base; 68 } 69 } 70 71 lg2::error("Could not find GPIO base"); 72 throw std::runtime_error("Could not find GPIO base!"); 73 #else 74 return 0; 75 #endif 76 } 77 78 uint32_t getGpioNum(const std::string& gpioPin) 79 { 80 // gpioplus promises that they will figure out how to easily 81 // support multiple BMC vendors when the time comes. 82 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin); 83 84 return getGpioBase() + offset; 85 } 86 87 int configGroupGpio(buttonConfig& buttonIFConfig) 88 { 89 int result = 0; 90 // iterate the list of gpios from the button interface config 91 // and initialize them 92 for (auto& gpioCfg : buttonIFConfig.gpios) 93 { 94 result = configGpio(gpioCfg); 95 if (result < 0) 96 { 97 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME", 98 buttonIFConfig.formFactorName, "NUM", gpioCfg.number, 99 "RESULT", result); 100 101 break; 102 } 103 } 104 105 return result; 106 } 107 108 int configGpio(gpioInfo& gpioConfig) 109 { 110 auto gpioNum = gpioConfig.number; 111 auto gpioDirection = gpioConfig.direction; 112 113 std::string devPath{gpioDev}; 114 115 std::fstream stream; 116 117 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); 118 119 devPath += "/gpio" + std::to_string(gpioNum) + "/value"; 120 121 fs::path fullPath(devPath); 122 123 if (fs::exists(fullPath)) 124 { 125 lg2::info("GPIO exported: {PATH}", "PATH", devPath); 126 } 127 else 128 { 129 devPath = gpioDev + "/export"; 130 131 stream.open(devPath, std::fstream::out); 132 try 133 { 134 stream << gpioNum; 135 stream.close(); 136 } 137 138 catch (const std::exception& e) 139 { 140 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum, 141 "PATH", devPath, "ERROR", e); 142 return -1; 143 } 144 } 145 146 if (gpioDirection == "out") 147 { 148 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 149 150 uint32_t currentValue; 151 152 stream.open(devPath, std::fstream::in); 153 try 154 { 155 stream >> currentValue; 156 stream.close(); 157 } 158 159 catch (const std::exception& e) 160 { 161 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath, 162 "ERROR", e); 163 return -1; 164 } 165 166 const char* direction = currentValue ? "high" : "low"; 167 168 devPath.clear(); 169 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 170 171 stream.open(devPath, std::fstream::out); 172 try 173 { 174 stream << direction; 175 stream.close(); 176 } 177 178 catch (const std::exception& e) 179 { 180 lg2::error("Error in writing: {ERROR}", "ERROR", e); 181 return -1; 182 } 183 } 184 else if (gpioDirection == "in") 185 { 186 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 187 188 stream.open(devPath, std::fstream::out); 189 try 190 { 191 stream << gpioDirection; 192 stream.close(); 193 } 194 195 catch (const std::exception& e) 196 { 197 lg2::error("Error in writing: {ERROR}", "ERROR", e); 198 return -1; 199 } 200 } 201 else if ((gpioDirection == "both")) 202 { 203 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 204 205 stream.open(devPath, std::fstream::out); 206 try 207 { 208 // Before set gpio configure as an interrupt pin, need to set 209 // direction as 'in' or edge can't set as 'rising', 'falling' and 210 // 'both' 211 const char* in_direction = "in"; 212 stream << in_direction; 213 stream.close(); 214 } 215 216 catch (const std::exception& e) 217 { 218 lg2::error("Error in writing: {ERROR}", "ERROR", e); 219 return -1; 220 } 221 devPath.clear(); 222 223 // For gpio configured as ‘both’, it is an interrupt pin and trigged on 224 // both rising and falling signals 225 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge"; 226 227 stream.open(devPath, std::fstream::out); 228 try 229 { 230 stream << gpioDirection; 231 stream.close(); 232 } 233 234 catch (const std::exception& e) 235 { 236 lg2::error("Error in writing: {ERROR}", "ERROR", e); 237 return -1; 238 } 239 } 240 241 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 242 243 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK); 244 245 if (fd < 0) 246 { 247 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR", 248 errno); 249 return -1; 250 } 251 252 gpioConfig.fd = fd; 253 254 return 0; 255 } 256