1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * GPIO chardev test helper 4 * 5 * Copyright (C) 2016 Bamvor Jian Zhang 6 */ 7 8#define _GNU_SOURCE 9#include <unistd.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <errno.h> 13#include <string.h> 14#include <fcntl.h> 15#include <getopt.h> 16#include <sys/ioctl.h> 17#include <libmount.h> 18#include <err.h> 19#include <dirent.h> 20#include <linux/gpio.h> 21#include "../../../gpio/gpio-utils.h" 22 23#define CONSUMER "gpio-selftest" 24#define GC_NUM 10 25enum direction { 26 OUT, 27 IN 28}; 29 30static int get_debugfs(char **path) 31{ 32 struct libmnt_context *cxt; 33 struct libmnt_table *tb; 34 struct libmnt_iter *itr = NULL; 35 struct libmnt_fs *fs; 36 int found = 0, ret; 37 38 cxt = mnt_new_context(); 39 if (!cxt) 40 err(EXIT_FAILURE, "libmount context allocation failed"); 41 42 itr = mnt_new_iter(MNT_ITER_FORWARD); 43 if (!itr) 44 err(EXIT_FAILURE, "failed to initialize libmount iterator"); 45 46 if (mnt_context_get_mtab(cxt, &tb)) 47 err(EXIT_FAILURE, "failed to read mtab"); 48 49 while (mnt_table_next_fs(tb, itr, &fs) == 0) { 50 const char *type = mnt_fs_get_fstype(fs); 51 52 if (!strcmp(type, "debugfs")) { 53 found = 1; 54 break; 55 } 56 } 57 if (found) { 58 ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); 59 if (ret < 0) 60 err(EXIT_FAILURE, "failed to format string"); 61 } 62 63 mnt_free_iter(itr); 64 mnt_free_context(cxt); 65 66 if (!found) 67 return -1; 68 69 return 0; 70} 71 72static int gpio_debugfs_get(const char *consumer, int *dir, int *value) 73{ 74 char *debugfs; 75 FILE *f; 76 char *line = NULL; 77 size_t len = 0; 78 char *cur; 79 int found = 0; 80 81 if (get_debugfs(&debugfs) != 0) 82 err(EXIT_FAILURE, "debugfs is not mounted"); 83 84 f = fopen(debugfs, "r"); 85 if (!f) 86 err(EXIT_FAILURE, "read from gpio debugfs failed"); 87 88 /* 89 * gpio-2 ( |gpio-selftest ) in lo 90 */ 91 while (getline(&line, &len, f) != -1) { 92 cur = strstr(line, consumer); 93 if (cur == NULL) 94 continue; 95 96 cur = strchr(line, ')'); 97 if (!cur) 98 continue; 99 100 cur += 2; 101 if (!strncmp(cur, "out", 3)) { 102 *dir = OUT; 103 cur += 4; 104 } else if (!strncmp(cur, "in", 2)) { 105 *dir = IN; 106 cur += 4; 107 } 108 109 if (!strncmp(cur, "hi", 2)) 110 *value = 1; 111 else if (!strncmp(cur, "lo", 2)) 112 *value = 0; 113 114 found = 1; 115 break; 116 } 117 free(debugfs); 118 fclose(f); 119 free(line); 120 121 if (!found) 122 return -1; 123 124 return 0; 125} 126 127static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret) 128{ 129 struct gpiochip_info *cinfo; 130 struct gpiochip_info *current; 131 const struct dirent *ent; 132 DIR *dp; 133 char *chrdev_name; 134 int fd; 135 int i = 0; 136 137 cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1); 138 if (!cinfo) 139 err(EXIT_FAILURE, "gpiochip_info allocation failed"); 140 141 current = cinfo; 142 dp = opendir("/dev"); 143 if (!dp) { 144 *ret = -errno; 145 goto error_out; 146 } else { 147 *ret = 0; 148 } 149 150 while (ent = readdir(dp), ent) { 151 if (check_prefix(ent->d_name, "gpiochip")) { 152 *ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name); 153 if (*ret < 0) 154 goto error_out; 155 156 fd = open(chrdev_name, 0); 157 if (fd == -1) { 158 *ret = -errno; 159 fprintf(stderr, "Failed to open %s\n", 160 chrdev_name); 161 goto error_close_dir; 162 } 163 *ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current); 164 if (*ret == -1) { 165 perror("Failed to issue CHIPINFO IOCTL\n"); 166 goto error_close_dir; 167 } 168 close(fd); 169 if (strcmp(current->label, gpiochip_name) == 0 170 || check_prefix(current->label, gpiochip_name)) { 171 *ret = 0; 172 current++; 173 i++; 174 } 175 } 176 } 177 178 if ((!*ret && i == 0) || *ret < 0) { 179 free(cinfo); 180 cinfo = NULL; 181 } 182 if (!*ret && i > 0) { 183 cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i); 184 *ret = i; 185 } 186 187error_close_dir: 188 closedir(dp); 189error_out: 190 if (*ret < 0) 191 err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret)); 192 193 return cinfo; 194} 195 196int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value) 197{ 198 struct gpiohandle_data data; 199 unsigned int lines[] = {line}; 200 int fd; 201 int debugfs_dir = IN; 202 int debugfs_value = 0; 203 int ret; 204 205 data.values[0] = value; 206 ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data, 207 CONSUMER); 208 if (ret < 0) 209 goto fail_out; 210 else 211 fd = ret; 212 213 ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value); 214 if (ret) { 215 ret = -EINVAL; 216 goto fail_out; 217 } 218 if (flag & GPIOHANDLE_REQUEST_INPUT) { 219 if (debugfs_dir != IN) { 220 errno = -EINVAL; 221 ret = -errno; 222 } 223 } else if (flag & GPIOHANDLE_REQUEST_OUTPUT) { 224 if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW) 225 debugfs_value = !debugfs_value; 226 227 if (!(debugfs_dir == OUT && value == debugfs_value)) { 228 errno = -EINVAL; 229 ret = -errno; 230 } 231 } 232 gpiotools_release_linehandle(fd); 233 234fail_out: 235 if (ret) 236 err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>", 237 cinfo->name, line, flag, value); 238 239 return ret; 240} 241 242void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line) 243{ 244 printf("line<%d>", line); 245 gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0); 246 printf("."); 247 gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1); 248 printf("."); 249 gpio_pin_test(cinfo, line, 250 GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, 251 0); 252 printf("."); 253 gpio_pin_test(cinfo, line, 254 GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, 255 1); 256 printf("."); 257 gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0); 258 printf("."); 259} 260 261/* 262 * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip 263 * Return 0 if successful or exit with EXIT_FAILURE if test failed. 264 * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g. 265 * gpio-mockup 266 * is_valid_gpio_chip: Whether the gpio_chip is valid. 1 means valid, 267 * 0 means invalid which could not be found by 268 * list_gpiochip. 269 */ 270int main(int argc, char *argv[]) 271{ 272 char *prefix; 273 int valid; 274 struct gpiochip_info *cinfo; 275 struct gpiochip_info *current; 276 int i; 277 int ret; 278 279 if (argc < 3) { 280 printf("Usage: %s prefix is_valid", argv[0]); 281 exit(EXIT_FAILURE); 282 } 283 284 prefix = argv[1]; 285 valid = strcmp(argv[2], "true") == 0 ? 1 : 0; 286 287 printf("Test gpiochip %s: ", prefix); 288 cinfo = list_gpiochip(prefix, &ret); 289 if (!cinfo) { 290 if (!valid && ret == 0) { 291 printf("Invalid test successful\n"); 292 ret = 0; 293 goto out; 294 } else { 295 ret = -EINVAL; 296 goto out; 297 } 298 } else if (cinfo && !valid) { 299 ret = -EINVAL; 300 goto out; 301 } 302 current = cinfo; 303 for (i = 0; i < ret; i++) { 304 gpio_pin_tests(current, 0); 305 gpio_pin_tests(current, current->lines - 1); 306 gpio_pin_tests(current, random() % current->lines); 307 current++; 308 } 309 ret = 0; 310 printf("successful\n"); 311 312out: 313 if (ret) 314 fprintf(stderr, "gpio<%s> test failed\n", prefix); 315 316 if (cinfo) 317 free(cinfo); 318 319 if (ret) 320 exit(EXIT_FAILURE); 321 322 return ret; 323} 324