123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /*
- * 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;
- }
|