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 namespace fs = std::filesystem; 34 std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = { 35 {GpioPolarity::activeLow, {'0', '1'}}, 36 {GpioPolarity::activeHigh, {'1', '0'}}}; 37 38 void setGpioState(int fd, GpioPolarity polarity, GpioState state) 39 { 40 char writeBuffer; 41 42 if (state == GpioState::assert) 43 { 44 writeBuffer = GpioValueMap[polarity].assert; 45 } 46 else 47 { 48 writeBuffer = GpioValueMap[polarity].deassert; 49 } 50 51 auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer)); 52 if (result < 0) 53 { 54 lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd, 55 "ERRORNO", errno); 56 } 57 return; 58 } 59 GpioState getGpioState(int fd, GpioPolarity polarity) 60 { 61 int result = -1; 62 char readBuffer = '0'; 63 64 result = ::lseek(fd, 0, SEEK_SET); 65 66 if (result < 0) 67 { 68 lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR", 69 errno); 70 return GpioState::invalid; 71 } 72 73 result = ::read(fd, &readBuffer, sizeof(readBuffer)); 74 if (result < 0) 75 { 76 lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd, 77 "ERRORNO", errno); 78 throw std::runtime_error("GPIO read failed"); 79 } 80 // read the gpio state for the io event received 81 GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert) 82 ? (GpioState::assert) 83 : (GpioState::deassert); 84 return gpioState; 85 } 86 void closeGpio(int fd) 87 { 88 if (fd > 0) 89 { 90 ::close(fd); 91 } 92 } 93 94 uint32_t getGpioBase() 95 { 96 // Look for a /sys/class/gpio/gpiochip*/label file 97 // with a value of GPIO_BASE_LABEL_NAME. Then read 98 // the base value from the 'base' file in that directory. 99 #ifdef LOOKUP_GPIO_BASE 100 for (auto& f : fs::directory_iterator(gpioDev)) 101 { 102 std::string path{f.path()}; 103 if (path.find("gpiochip") == std::string::npos) 104 { 105 continue; 106 } 107 108 std::ifstream labelStream{path + "/label"}; 109 std::string label; 110 labelStream >> label; 111 112 if (label == GPIO_BASE_LABEL_NAME) 113 { 114 uint32_t base; 115 std::ifstream baseStream{path + "/base"}; 116 baseStream >> base; 117 return base; 118 } 119 } 120 121 lg2::error("Could not find GPIO base"); 122 throw std::runtime_error("Could not find GPIO base!"); 123 #else 124 return 0; 125 #endif 126 } 127 128 uint32_t getGpioNum(const std::string& gpioPin) 129 { 130 // gpioplus promises that they will figure out how to easily 131 // support multiple BMC vendors when the time comes. 132 auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin); 133 134 return getGpioBase() + offset; 135 } 136 137 int configGroupGpio(buttonConfig& buttonIFConfig) 138 { 139 int result = 0; 140 // iterate the list of gpios from the button interface config 141 // and initialize them 142 for (auto& gpioCfg : buttonIFConfig.gpios) 143 { 144 result = configGpio(gpioCfg); 145 if (result < 0) 146 { 147 lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME", 148 buttonIFConfig.formFactorName, "NUM", gpioCfg.number, 149 "RESULT", result); 150 151 break; 152 } 153 } 154 155 return result; 156 } 157 158 int configGpio(gpioInfo& gpioConfig) 159 { 160 auto gpioNum = gpioConfig.number; 161 auto gpioDirection = gpioConfig.direction; 162 163 std::string devPath{gpioDev}; 164 165 std::fstream stream; 166 167 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); 168 169 devPath += "/gpio" + std::to_string(gpioNum) + "/value"; 170 171 fs::path fullPath(devPath); 172 173 if (fs::exists(fullPath)) 174 { 175 lg2::info("GPIO exported: {PATH}", "PATH", devPath); 176 } 177 else 178 { 179 devPath = gpioDev + "/export"; 180 181 stream.open(devPath, std::fstream::out); 182 try 183 { 184 stream << gpioNum; 185 stream.close(); 186 } 187 188 catch (const std::exception& e) 189 { 190 lg2::error("{NUM} error in writing {PATH}: {ERROR}", "NUM", gpioNum, 191 "PATH", devPath, "ERROR", e); 192 return -1; 193 } 194 } 195 196 if (gpioDirection == "out") 197 { 198 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 199 200 uint32_t currentValue; 201 202 stream.open(devPath, std::fstream::in); 203 try 204 { 205 stream >> currentValue; 206 stream.close(); 207 } 208 209 catch (const std::exception& e) 210 { 211 lg2::error("Error in reading {PATH}: {ERROR}", "PATH", devPath, 212 "ERROR", e); 213 return -1; 214 } 215 216 const char* direction = currentValue ? "high" : "low"; 217 218 devPath.clear(); 219 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 220 221 stream.open(devPath, std::fstream::out); 222 try 223 { 224 stream << direction; 225 stream.close(); 226 } 227 228 catch (const std::exception& e) 229 { 230 lg2::error("Error in writing: {ERROR}", "ERROR", e); 231 return -1; 232 } 233 } 234 else if (gpioDirection == "in") 235 { 236 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 237 238 stream.open(devPath, std::fstream::out); 239 try 240 { 241 stream << gpioDirection; 242 stream.close(); 243 } 244 245 catch (const std::exception& e) 246 { 247 lg2::error("Error in writing: {ERROR}", "ERROR", e); 248 return -1; 249 } 250 } 251 else if ((gpioDirection == "both")) 252 { 253 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction"; 254 255 stream.open(devPath, std::fstream::out); 256 try 257 { 258 // Before set gpio configure as an interrupt pin, need to set 259 // direction as 'in' or edge can't set as 'rising', 'falling' and 260 // 'both' 261 const char* in_direction = "in"; 262 stream << in_direction; 263 stream.close(); 264 } 265 266 catch (const std::exception& e) 267 { 268 lg2::error("Error in writing: {ERROR}", "ERROR", e); 269 return -1; 270 } 271 devPath.clear(); 272 273 // For gpio configured as ‘both’, it is an interrupt pin and trigged on 274 // both rising and falling signals 275 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge"; 276 277 stream.open(devPath, std::fstream::out); 278 try 279 { 280 stream << gpioDirection; 281 stream.close(); 282 } 283 284 catch (const std::exception& e) 285 { 286 lg2::error("Error in writing: {ERROR}", "ERROR", e); 287 return -1; 288 } 289 } 290 291 devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value"; 292 293 auto fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK); 294 295 if (fd < 0) 296 { 297 lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR", 298 errno); 299 return -1; 300 } 301 302 gpioConfig.fd = fd; 303 304 return 0; 305 } 306