|
@@ -0,0 +1,324 @@
|
|
|
+/*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ 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)
|
|
|
+ asprintf(path, "%s/gpio", mnt_fs_get_target(fs));
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|