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