1 #define _GNU_SOURCE 2 3 #include <stdint.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <fcntl.h> 8 #include <unistd.h> 9 #include <argp.h> 10 #include <sys/stat.h> 11 #include <sys/mman.h> 12 #include <dirent.h> 13 #include "openbmc_intf.h" 14 #include "gpio.h" 15 #include "gpio_json.h" 16 17 #include <sys/ioctl.h> 18 #include <linux/gpio.h> 19 20 #define GPIO_PORT_OFFSET 8 21 #define GPIO_BASE_PATH "/sys/class/gpio" 22 23 cJSON* gpio_json = NULL; 24 25 int gpio_write(GPIO* gpio, uint8_t value) 26 { 27 g_assert (gpio != NULL); 28 struct gpiohandle_data data; 29 memset(&data, 0, sizeof(data)); 30 data.values[0] = value; 31 32 if (gpio->fd <= 0) 33 { 34 return GPIO_ERROR; 35 } 36 37 if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0) 38 { 39 return GPIO_WRITE_ERROR; 40 } 41 42 return GPIO_OK; 43 } 44 45 int gpio_read(GPIO* gpio, uint8_t *value) 46 { 47 g_assert (gpio != NULL); 48 struct gpiohandle_data data; 49 memset(&data, 0, sizeof(data)); 50 51 if (gpio->fd <= 0) 52 { 53 return GPIO_ERROR; 54 } 55 56 if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0) 57 { 58 return GPIO_READ_ERROR; 59 } 60 61 *value = data.values[0]; 62 63 return GPIO_OK; 64 } 65 66 /** 67 * Determine the GPIO base number for the system. It is found in 68 * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the 69 * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it. 70 * 71 * Note: This method is ASPEED specific. Could add support for 72 * additional SOCs in the future. 73 * 74 * @return int - the GPIO base number, or < 0 if not found 75 */ 76 int get_gpio_base() 77 { 78 int gpio_base = -1; 79 80 DIR* dir = opendir(GPIO_BASE_PATH); 81 if (dir == NULL) 82 { 83 fprintf(stderr, "Unable to open directory %s\n", 84 GPIO_BASE_PATH); 85 return -1; 86 } 87 88 struct dirent* entry; 89 while ((entry = readdir(dir)) != NULL) 90 { 91 92 /* Look in the gpiochip<X> directories for a file called 'label' */ 93 /* that contains '1e780000.gpio', then in that directory read */ 94 /* the GPIO base out of the 'base' file. */ 95 96 if (strncmp(entry->d_name, "gpiochip", 8) != 0) 97 { 98 continue; 99 } 100 101 gboolean is_bmc = FALSE; 102 char* label_name; 103 int rc = asprintf(&label_name, "%s/%s/label", 104 GPIO_BASE_PATH, entry->d_name); 105 106 if (!rc) 107 { 108 continue; 109 } 110 111 FILE* fd = fopen(label_name, "r"); 112 free(label_name); 113 114 if (!fd) 115 { 116 continue; 117 } 118 119 char label[14]; 120 if (fgets(label, 14, fd) != NULL) 121 { 122 if (strcmp(label, "1e780000.gpio") == 0) 123 { 124 is_bmc = TRUE; 125 } 126 } 127 fclose(fd); 128 129 if (!is_bmc) 130 { 131 continue; 132 } 133 134 char* base_name; 135 rc = asprintf(&base_name, "%s/%s/base", 136 GPIO_BASE_PATH, entry->d_name); 137 138 if (!rc) 139 { 140 continue; 141 } 142 143 fd = fopen(base_name, "r"); 144 free(base_name); 145 146 if (!fd) 147 { 148 continue; 149 } 150 151 if (fscanf(fd, "%d", &gpio_base) != 1) 152 { 153 gpio_base = -1; 154 } 155 fclose(fd); 156 157 /* We found the right file. No need to continue. */ 158 break; 159 } 160 closedir(dir); 161 162 if (gpio_base == -1) 163 { 164 fprintf(stderr, "Could not find GPIO base\n"); 165 } 166 167 return gpio_base; 168 } 169 170 /** 171 * Converts the GPIO port/offset nomenclature value 172 * to a number. Supports the ASPEED method where 173 * num = base + (port * 8) + offset, and the port is an 174 * A-Z character that converts to 0 to 25. The base 175 * is obtained form the hardware. 176 * 177 * For example: "A5" -> 5, "Z7" -> 207 178 * 179 * @param[in] gpio - the GPIO name 180 * 181 * @return int - the GPIO number or < 0 if not found 182 */ 183 int convert_gpio_to_num(const char* gpio) 184 { 185 size_t len = strlen(gpio); 186 if (len < 2) 187 { 188 fprintf(stderr, "Invalid GPIO name %s\n", gpio); 189 return -1; 190 } 191 192 /* Read the offset from the last character */ 193 if (!isdigit(gpio[len-1])) 194 { 195 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio); 196 return -1; 197 } 198 199 int offset = gpio[len-1] - '0'; 200 201 /* Read the port from the second to last character */ 202 if (!isalpha(gpio[len-2])) 203 { 204 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio); 205 return -1; 206 } 207 int port = toupper(gpio[len-2]) - 'A'; 208 209 /* Check for a 2 character port, like AA */ 210 if ((len == 3) && isalpha(gpio[len-3])) 211 { 212 port += 26 * (toupper(gpio[len-3]) - 'A' + 1); 213 } 214 215 return (port * GPIO_PORT_OFFSET) + offset; 216 } 217 218 /** 219 * Returns the cJSON pointer to the GPIO definition 220 * for the GPIO passed in. 221 * 222 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP 223 * 224 * @return cJSON* - pointer to the cJSON object or NULL 225 * if not found. 226 */ 227 cJSON* get_gpio_def(const char* gpio_name) 228 { 229 if (gpio_json == NULL) 230 { 231 gpio_json = load_json(); 232 if (gpio_json == NULL) 233 { 234 return NULL; 235 } 236 } 237 238 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions"); 239 g_assert(gpio_defs != NULL); 240 241 cJSON* def; 242 cJSON_ArrayForEach(def, gpio_defs) 243 { 244 cJSON* name = cJSON_GetObjectItem(def, "name"); 245 g_assert(name != NULL); 246 247 if (strcmp(name->valuestring, gpio_name) == 0) 248 { 249 return def; 250 } 251 } 252 return NULL; 253 } 254 255 /** 256 * Frees the gpio_json memory 257 * 258 * Can be called once when callers are done calling making calls 259 * to gpio_init() so that the JSON only needs to be loaded once. 260 */ 261 void gpio_inits_done() 262 { 263 if (gpio_json != NULL) 264 { 265 cJSON_Delete(gpio_json); 266 gpio_json = NULL; 267 } 268 } 269 270 /** 271 * Fills in the dev, direction, and num elements in 272 * the GPIO structure. 273 * 274 * @param gpio - the GPIO structure to fill in 275 * 276 * @return GPIO_OK if successful 277 */ 278 int gpio_get_params(GPIO* gpio) 279 { 280 gpio->dev = g_strdup(GPIO_BASE_PATH); 281 282 const cJSON* def = get_gpio_def(gpio->name); 283 if (def == NULL) 284 { 285 fprintf(stderr, "Unable to find GPIO %s in the JSON\n", 286 gpio->name); 287 return GPIO_LOOKUP_ERROR; 288 } 289 290 const cJSON* dir = cJSON_GetObjectItem(def, "direction"); 291 g_assert(dir != NULL); 292 gpio->direction = g_strdup(dir->valuestring); 293 294 /* Must use either 'num', like 87, or 'pin', like "A5" */ 295 const cJSON* num = cJSON_GetObjectItem(def, "num"); 296 if ((num != NULL) && cJSON_IsNumber(num)) 297 { 298 gpio->num = num->valueint; 299 } 300 else 301 { 302 const cJSON* pin = cJSON_GetObjectItem(def, "pin"); 303 g_assert(pin != NULL); 304 305 int pin_id = convert_gpio_to_num(pin->valuestring); 306 if (pin_id < 0) 307 { 308 return GPIO_LOOKUP_ERROR; 309 } 310 gpio->num = (size_t) pin_id; 311 } 312 // TODO: For the purposes of skeleton and the projects that use it, 313 // it should be safe to assume this will always be 0. Eventually skeleton 314 // should be going away, but if the need arises before then this may need 315 // to be updated to handle non-zero cases. 316 gpio->chip_id = 0; 317 return GPIO_OK; 318 } 319 320 int gpio_open(GPIO* gpio, uint8_t default_value) 321 { 322 g_assert (gpio != NULL); 323 324 char buf[255]; 325 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id); 326 gpio->fd = open(buf, 0); 327 if (gpio->fd == -1) 328 { 329 return GPIO_OPEN_ERROR; 330 } 331 332 struct gpiohandle_request req; 333 memset(&req, 0, sizeof(req)); 334 strncpy(req.consumer_label, "skeleton-gpio", sizeof(req.consumer_label)); 335 336 // open gpio for writing or reading 337 if (gpio->direction == NULL) 338 { 339 gpio_close(gpio); 340 return GPIO_OPEN_ERROR; 341 } 342 req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT 343 : GPIOHANDLE_REQUEST_OUTPUT; 344 345 req.lineoffsets[0] = gpio->num; 346 req.lines = 1; 347 348 if (strcmp(gpio->direction,"out") == 0) 349 { 350 req.default_values[0] = default_value; 351 } 352 353 int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req); 354 if (rc < 0) 355 { 356 gpio_close(gpio); 357 return GPIO_OPEN_ERROR; 358 } 359 gpio_close(gpio); 360 gpio->fd = req.fd; 361 362 return GPIO_OK; 363 } 364 365 void gpio_close(GPIO* gpio) 366 { 367 if(gpio->fd < 0) 368 return; 369 370 close(gpio->fd); 371 gpio->fd = -1; 372 } 373