328 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * GPIO chardev test helper
 | 
						|
 *
 | 
						|
 * Copyright (C) 2016 Bamvor Jian Zhang
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License version 2 as published by
 | 
						|
 * the Free Software Foundation.
 | 
						|
 */
 | 
						|
 | 
						|
#define _GNU_SOURCE
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <string.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <getopt.h>
 | 
						|
#include <sys/ioctl.h>
 | 
						|
#include <libmount.h>
 | 
						|
#include <err.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <linux/gpio.h>
 | 
						|
#include "../../../gpio/gpio-utils.h"
 | 
						|
 | 
						|
#define CONSUMER	"gpio-selftest"
 | 
						|
#define	GC_NUM		10
 | 
						|
enum direction {
 | 
						|
	OUT,
 | 
						|
	IN
 | 
						|
};
 | 
						|
 | 
						|
static int get_debugfs(char **path)
 | 
						|
{
 | 
						|
	struct libmnt_context *cxt;
 | 
						|
	struct libmnt_table *tb;
 | 
						|
	struct libmnt_iter *itr = NULL;
 | 
						|
	struct libmnt_fs *fs;
 | 
						|
	int found = 0, ret;
 | 
						|
 | 
						|
	cxt = mnt_new_context();
 | 
						|
	if (!cxt)
 | 
						|
		err(EXIT_FAILURE, "libmount context allocation failed");
 | 
						|
 | 
						|
	itr = mnt_new_iter(MNT_ITER_FORWARD);
 | 
						|
	if (!itr)
 | 
						|
		err(EXIT_FAILURE, "failed to initialize libmount iterator");
 | 
						|
 | 
						|
	if (mnt_context_get_mtab(cxt, &tb))
 | 
						|
		err(EXIT_FAILURE, "failed to read mtab");
 | 
						|
 | 
						|
	while (mnt_table_next_fs(tb, itr, &fs) == 0) {
 | 
						|
		const char *type = mnt_fs_get_fstype(fs);
 | 
						|
 | 
						|
		if (!strcmp(type, "debugfs")) {
 | 
						|
			found = 1;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (found) {
 | 
						|
		ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs));
 | 
						|
		if (ret < 0)
 | 
						|
			err(EXIT_FAILURE, "failed to format string");
 | 
						|
	}
 | 
						|
 | 
						|
	mnt_free_iter(itr);
 | 
						|
	mnt_free_context(cxt);
 | 
						|
 | 
						|
	if (!found)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int gpio_debugfs_get(const char *consumer, int *dir, int *value)
 | 
						|
{
 | 
						|
	char *debugfs;
 | 
						|
	FILE *f;
 | 
						|
	char *line = NULL;
 | 
						|
	size_t len = 0;
 | 
						|
	char *cur;
 | 
						|
	int found = 0;
 | 
						|
 | 
						|
	if (get_debugfs(&debugfs) != 0)
 | 
						|
		err(EXIT_FAILURE, "debugfs is not mounted");
 | 
						|
 | 
						|
	f = fopen(debugfs, "r");
 | 
						|
	if (!f)
 | 
						|
		err(EXIT_FAILURE, "read from gpio debugfs failed");
 | 
						|
 | 
						|
	/*
 | 
						|
	 * gpio-2   (                    |gpio-selftest               ) in  lo
 | 
						|
	 */
 | 
						|
	while (getline(&line, &len, f) != -1) {
 | 
						|
		cur = strstr(line, consumer);
 | 
						|
		if (cur == NULL)
 | 
						|
			continue;
 | 
						|
 | 
						|
		cur = strchr(line, ')');
 | 
						|
		if (!cur)
 | 
						|
			continue;
 | 
						|
 | 
						|
		cur += 2;
 | 
						|
		if (!strncmp(cur, "out", 3)) {
 | 
						|
			*dir = OUT;
 | 
						|
			cur += 4;
 | 
						|
		} else if (!strncmp(cur, "in", 2)) {
 | 
						|
			*dir = IN;
 | 
						|
			cur += 4;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!strncmp(cur, "hi", 2))
 | 
						|
			*value = 1;
 | 
						|
		else if (!strncmp(cur, "lo", 2))
 | 
						|
			*value = 0;
 | 
						|
 | 
						|
		found = 1;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	free(debugfs);
 | 
						|
	fclose(f);
 | 
						|
	free(line);
 | 
						|
 | 
						|
	if (!found)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret)
 | 
						|
{
 | 
						|
	struct gpiochip_info *cinfo;
 | 
						|
	struct gpiochip_info *current;
 | 
						|
	const struct dirent *ent;
 | 
						|
	DIR *dp;
 | 
						|
	char *chrdev_name;
 | 
						|
	int fd;
 | 
						|
	int i = 0;
 | 
						|
 | 
						|
	cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1);
 | 
						|
	if (!cinfo)
 | 
						|
		err(EXIT_FAILURE, "gpiochip_info allocation failed");
 | 
						|
 | 
						|
	current = cinfo;
 | 
						|
	dp = opendir("/dev");
 | 
						|
	if (!dp) {
 | 
						|
		*ret = -errno;
 | 
						|
		goto error_out;
 | 
						|
	} else {
 | 
						|
		*ret = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	while (ent = readdir(dp), ent) {
 | 
						|
		if (check_prefix(ent->d_name, "gpiochip")) {
 | 
						|
			*ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name);
 | 
						|
			if (*ret < 0)
 | 
						|
				goto error_out;
 | 
						|
 | 
						|
			fd = open(chrdev_name, 0);
 | 
						|
			if (fd == -1) {
 | 
						|
				*ret = -errno;
 | 
						|
				fprintf(stderr, "Failed to open %s\n",
 | 
						|
					chrdev_name);
 | 
						|
				goto error_close_dir;
 | 
						|
			}
 | 
						|
			*ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current);
 | 
						|
			if (*ret == -1) {
 | 
						|
				perror("Failed to issue CHIPINFO IOCTL\n");
 | 
						|
				goto error_close_dir;
 | 
						|
			}
 | 
						|
			close(fd);
 | 
						|
			if (strcmp(current->label, gpiochip_name) == 0
 | 
						|
			    || check_prefix(current->label, gpiochip_name)) {
 | 
						|
				*ret = 0;
 | 
						|
				current++;
 | 
						|
				i++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ((!*ret && i == 0) || *ret < 0) {
 | 
						|
		free(cinfo);
 | 
						|
		cinfo = NULL;
 | 
						|
	}
 | 
						|
	if (!*ret && i > 0) {
 | 
						|
		cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i);
 | 
						|
		*ret = i;
 | 
						|
	}
 | 
						|
 | 
						|
error_close_dir:
 | 
						|
	closedir(dp);
 | 
						|
error_out:
 | 
						|
	if (*ret < 0)
 | 
						|
		err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
 | 
						|
 | 
						|
	return cinfo;
 | 
						|
}
 | 
						|
 | 
						|
int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value)
 | 
						|
{
 | 
						|
	struct gpiohandle_data data;
 | 
						|
	unsigned int lines[] = {line};
 | 
						|
	int fd;
 | 
						|
	int debugfs_dir = IN;
 | 
						|
	int debugfs_value = 0;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	data.values[0] = value;
 | 
						|
	ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data,
 | 
						|
					   CONSUMER);
 | 
						|
	if (ret < 0)
 | 
						|
		goto fail_out;
 | 
						|
	else
 | 
						|
		fd = ret;
 | 
						|
 | 
						|
	ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value);
 | 
						|
	if (ret) {
 | 
						|
		ret = -EINVAL;
 | 
						|
		goto fail_out;
 | 
						|
	}
 | 
						|
	if (flag & GPIOHANDLE_REQUEST_INPUT) {
 | 
						|
		if (debugfs_dir != IN) {
 | 
						|
			errno = -EINVAL;
 | 
						|
			ret = -errno;
 | 
						|
		}
 | 
						|
	} else if (flag & GPIOHANDLE_REQUEST_OUTPUT) {
 | 
						|
		if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW)
 | 
						|
			debugfs_value = !debugfs_value;
 | 
						|
 | 
						|
		if (!(debugfs_dir == OUT && value == debugfs_value)) {
 | 
						|
			errno = -EINVAL;
 | 
						|
			ret = -errno;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	gpiotools_release_linehandle(fd);
 | 
						|
 | 
						|
fail_out:
 | 
						|
	if (ret)
 | 
						|
		err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>",
 | 
						|
		    cinfo->name, line, flag, value);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line)
 | 
						|
{
 | 
						|
	printf("line<%d>", line);
 | 
						|
	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0);
 | 
						|
	printf(".");
 | 
						|
	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1);
 | 
						|
	printf(".");
 | 
						|
	gpio_pin_test(cinfo, line,
 | 
						|
		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
 | 
						|
		      0);
 | 
						|
	printf(".");
 | 
						|
	gpio_pin_test(cinfo, line,
 | 
						|
		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
 | 
						|
		      1);
 | 
						|
	printf(".");
 | 
						|
	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0);
 | 
						|
	printf(".");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip
 | 
						|
 * Return 0 if successful or exit with EXIT_FAILURE if test failed.
 | 
						|
 * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g.
 | 
						|
 *			  gpio-mockup
 | 
						|
 * is_valid_gpio_chip:	  Whether the gpio_chip is valid. 1 means valid,
 | 
						|
 *			  0 means invalid which could not be found by
 | 
						|
 *			  list_gpiochip.
 | 
						|
 */
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	char *prefix;
 | 
						|
	int valid;
 | 
						|
	struct gpiochip_info *cinfo;
 | 
						|
	struct gpiochip_info *current;
 | 
						|
	int i;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (argc < 3) {
 | 
						|
		printf("Usage: %s prefix is_valid", argv[0]);
 | 
						|
		exit(EXIT_FAILURE);
 | 
						|
	}
 | 
						|
 | 
						|
	prefix = argv[1];
 | 
						|
	valid = strcmp(argv[2], "true") == 0 ? 1 : 0;
 | 
						|
 | 
						|
	printf("Test gpiochip %s: ", prefix);
 | 
						|
	cinfo = list_gpiochip(prefix, &ret);
 | 
						|
	if (!cinfo) {
 | 
						|
		if (!valid && ret == 0) {
 | 
						|
			printf("Invalid test successful\n");
 | 
						|
			ret = 0;
 | 
						|
			goto out;
 | 
						|
		} else {
 | 
						|
			ret = -EINVAL;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
	} else if (cinfo && !valid) {
 | 
						|
		ret = -EINVAL;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	current = cinfo;
 | 
						|
	for (i = 0; i < ret; i++) {
 | 
						|
		gpio_pin_tests(current, 0);
 | 
						|
		gpio_pin_tests(current, current->lines - 1);
 | 
						|
		gpio_pin_tests(current, random() % current->lines);
 | 
						|
		current++;
 | 
						|
	}
 | 
						|
	ret = 0;
 | 
						|
	printf("successful\n");
 | 
						|
 | 
						|
out:
 | 
						|
	if (ret)
 | 
						|
		fprintf(stderr, "gpio<%s> test failed\n", prefix);
 | 
						|
 | 
						|
	if (cinfo)
 | 
						|
		free(cinfo);
 | 
						|
 | 
						|
	if (ret)
 | 
						|
		exit(EXIT_FAILURE);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 |