linux/tools/testing/selftests/gpio/gpio-mockup-chardev.c
<<
>>
Prefs
   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
  29enum direction {
  30        OUT,
  31        IN
  32};
  33
  34static 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
  73static 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
 128static 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
 188error_close_dir:
 189        closedir(dp);
 190error_out:
 191        if (*ret < 0)
 192                err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
 193
 194        return cinfo;
 195}
 196
 197int 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
 235fail_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
 243void 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 */
 271int 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
 313out:
 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