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