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