From patchwork Mon Nov 21 10:22:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kent Gibson X-Patchwork-Id: 627398 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC115C43219 for ; Mon, 21 Nov 2022 10:23:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231358AbiKUKXV (ORCPT ); Mon, 21 Nov 2022 05:23:21 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60104 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231355AbiKUKXU (ORCPT ); Mon, 21 Nov 2022 05:23:20 -0500 Received: from mail-pl1-x629.google.com (mail-pl1-x629.google.com [IPv6:2607:f8b0:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 428141C10F for ; Mon, 21 Nov 2022 02:23:17 -0800 (PST) Received: by mail-pl1-x629.google.com with SMTP id 4so10214307pli.0 for ; Mon, 21 Nov 2022 02:23:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=gm0YjHEAS3haysUbR7ldAyNuhTY3SPamX9ZiU/QswcE=; b=N0uMoPkbYs/LdYuxRRh8td3n/e4+9gRJaP20Cte+8l36eOD+erOu4bKQ6w5WXvGgz7 LVOeRK5goXXk5nOIjI3IE7Its+2BKHiODafuwOk9mGdwh+JUiLA44cqgiLiJkATpy7ZY bLoC9aXHVO9gr/E0ppq35FqKYvTIo0B6gnMcl8yHk6ZXp20llpnSiE7Z/Qdmqqws5PLK gKLfQu/sKXd7ao4PPrnC+pr4Sj0QoHQBhq96cOPIWt8c7M4xKmNRxZSHfZYlrE307Ypq xiDfgu2JkBmar638hDfJEYZ37dptdeqtiWQTrMmkrgvv6Tg8azuIoT+azI+BCLZWmy6B ZblQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=gm0YjHEAS3haysUbR7ldAyNuhTY3SPamX9ZiU/QswcE=; b=XG+AZnF/L+yoLuEe0noDVT4BKRXLCtSk7M9oWjutVQMSAKerjLHtcQ1BC4/GLIWyHI 8ZZmrupDXdTqx1Shshh1GTEp15CdNUBv28yvibHbWfSFFs728X+Hmi/44uE8ja5IjbtV xiq+xUE0pb+IfLHWrDYcPuJ4eOmrYwMKYH57cjNCNoWNkjcYCGzE5zcV9fHDV943iLuu EVaGzft6CT/xGJzB3+dqdM6T6UsnLXqfNkzw4a7ICnDmX8GRewid85B3iq+NtByTtTiK 1jUI6Nh43ieS0oGDpXwDLCWTn2DXvgcjVH+3tUJwZw/H3tD896c3WZB9YnxNTm+lZqRx VCow== X-Gm-Message-State: ANoB5plYxKRnQ4+p6YGjVxu2VwXT9fkdY0TTS3BE6kqFVjRzLQkNfeGK iCzL3UWl7Ml9fVeoqE0fdPYKSkXN2ao= X-Google-Smtp-Source: AA0mqf7T5hKaBQrg8g6ipi3jcJzCm7NDomqNFqRy9QvBVD+keSVHKdz0x1qW5dq2VAchByT+rnmJQA== X-Received: by 2002:a17:903:1cc:b0:185:5453:5e01 with SMTP id e12-20020a17090301cc00b0018554535e01mr2347767plh.113.1669026195588; Mon, 21 Nov 2022 02:23:15 -0800 (PST) Received: from sol.home.arpa (14-200-229-209.tpgi.com.au. [14.200.229.209]) by smtp.gmail.com with ESMTPSA id s184-20020a625ec1000000b0056ba7ce4d5asm8347419pfb.52.2022.11.21.02.23.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Nov 2022 02:23:14 -0800 (PST) From: Kent Gibson To: linux-gpio@vger.kernel.org, brgl@bgdev.pl Cc: Kent Gibson Subject: [libgpiod v2][PATCH v5 1/6] tools: remove old code to simplify review Date: Mon, 21 Nov 2022 18:22:48 +0800 Message-Id: <20221121102253.38306-2-warthog618@gmail.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221121102253.38306-1-warthog618@gmail.com> References: <20221121102253.38306-1-warthog618@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Remove functions that are so heavily modified in the subsequent patch that they are effectively complete re-writes, so the old code just obfuscates the new. The gpiofind tool is removed completely, as its functionality is absorbed into the other tools. Signed-off-by: Kent Gibson --- man/Makefile.am | 2 +- tools/.gitignore | 1 - tools/Makefile.am | 4 +- tools/gpio-tools-test.bats | 893 ------------------------------------- tools/gpiodetect.c | 79 ---- tools/gpiofind.c | 93 ---- tools/gpioget.c | 153 ------- tools/gpioinfo.c | 240 ---------- tools/gpiomon.c | 314 ------------- tools/gpioset.c | 330 -------------- tools/tools-common.c | 81 ---- 11 files changed, 2 insertions(+), 2188 deletions(-) delete mode 100644 tools/gpiofind.c diff --git a/man/Makefile.am b/man/Makefile.am index 4d2c29b..8d1d9b3 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -3,7 +3,7 @@ if WITH_MANPAGES -dist_man1_MANS = gpiodetect.man gpioinfo.man gpioget.man gpioset.man gpiofind.man gpiomon.man +dist_man1_MANS = gpiodetect.man gpioinfo.man gpioget.man gpioset.man gpiomon.man %.man: $(top_builddir)/tools/$(*F) help2man $(top_builddir)/tools/$(*F) --include=$(srcdir)/template --output=$(builddir)/$@ --no-info diff --git a/tools/.gitignore b/tools/.gitignore index 0d53de9..d6b2f44 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -6,4 +6,3 @@ gpioinfo gpioget gpioset gpiomon -gpiofind diff --git a/tools/Makefile.am b/tools/Makefile.am index 4a13266..fc074b9 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -9,7 +9,7 @@ libtools_common_la_SOURCES = tools-common.c tools-common.h LDADD = libtools-common.la $(top_builddir)/lib/libgpiod.la -bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind +bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiodetect_SOURCES = gpiodetect.c @@ -21,8 +21,6 @@ gpioset_SOURCES = gpioset.c gpiomon_SOURCES = gpiomon.c -gpiofind_SOURCES = gpiofind.c - EXTRA_DIST = gpio-tools-test gpio-tools-test.bats if WITH_TESTS diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats index a259eae..d200df1 100755 --- a/tools/gpio-tools-test.bats +++ b/tools/gpio-tools-test.bats @@ -220,896 +220,3 @@ teardown() { gpiosim_cleanup } -# -# gpiodetect test cases -# - -@test "gpiodetect: list chips" { - gpiosim_chip sim0 num_lines=4 - gpiosim_chip sim1 num_lines=8 - gpiosim_chip sim2 num_lines=16 - - run_tool gpiodetect - - test "$status" -eq 0 - output_contains_line "$(gpiosim_chip_name sim0) [$(gpiosim_dev_name sim0)-node0] (4 lines)" - output_contains_line "$(gpiosim_chip_name sim1) [$(gpiosim_dev_name sim1)-node0] (8 lines)" - output_contains_line "$(gpiosim_chip_name sim2) [$(gpiosim_dev_name sim2)-node0] (16 lines)" -} - -@test "gpiodetect: invalid args" { - run_tool gpiodetect unimplemented-arg - test "$status" -eq 1 -} - -# -# gpioinfo test cases -# - -@test "gpioinfo: dump all chips" { - gpiosim_chip sim0 num_lines=4 - gpiosim_chip sim1 num_lines=8 - - run_tool gpioinfo - - test "$status" -eq 0 - output_contains_line "$(gpiosim_chip_name sim0) - 4 lines:" - output_contains_line "$(gpiosim_chip_name sim1) - 8 lines:" - - output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+unused\\s+input\\s+active-high" - output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+unused\\s+input\\s+active-high" -} - -@test "gpioinfo: dump all chips with one line exported" { - gpiosim_chip sim0 num_lines=4 - gpiosim_chip sim1 num_lines=8 - - coproc_run_tool gpioset --mode=signal --active-low "$(gpiosim_chip_name sim1)" 7=1 - - run_tool gpioinfo - - test "$status" -eq 0 - output_contains_line "$(gpiosim_chip_name sim0) - 4 lines:" - output_contains_line "$(gpiosim_chip_name sim1) - 8 lines:" - output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+unused\\s+input\\s+active-high" - output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+\\\"gpioset\\\"\\s+output\\s+active-low" - - coproc_tool_kill - coproc_tool_wait -} - -@test "gpioinfo: dump one chip" { - gpiosim_chip sim0 num_lines=8 - gpiosim_chip sim1 num_lines=4 - - run_tool gpioinfo "$(gpiosim_chip_name sim1)" - - test "$status" -eq 0 - assert_fail output_contains_line "$(gpiosim_chip_name sim0) - 8 lines:" - output_contains_line "$(gpiosim_chip_name sim1) - 4 lines:" - output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+unused\\s+input\\s+active-high" - assert_fail output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+unused\\s+input\\s+active-high" -} - -@test "gpioinfo: dump all but one chip" { - gpiosim_chip sim0 num_lines=4 - gpiosim_chip sim1 num_lines=4 - gpiosim_chip sim2 num_lines=8 - gpiosim_chip sim3 num_lines=4 - - run_tool gpioinfo "$(gpiosim_chip_name sim0)" \ - "$(gpiosim_chip_name sim1)" "$(gpiosim_chip_name sim3)" - - test "$status" -eq 0 - output_contains_line "$(gpiosim_chip_name sim0) - 4 lines:" - output_contains_line "$(gpiosim_chip_name sim1) - 4 lines:" - assert_fail output_contains_line "$(gpiosim_chip_name sim2) - 8 lines:" - output_contains_line "$(gpiosim_chip_name sim3) - 4 lines:" - output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+unused\\s+input\\s+active-high" - assert_fail output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+unused\\s+input\\s+active-high" -} - -@test "gpioinfo: inexistent chip" { - run_tool gpioinfo "inexistent" - - test "$status" -eq 1 -} - -# -# gpiofind test cases -# - -@test "gpiofind: line found" { - gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=3:bar - gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz line_name=7:foobar - gpiosim_chip sim2 num_lines=16 - - run_tool gpiofind foobar - - test "$status" -eq "0" - test "$output" = "$(gpiosim_chip_name sim1) 7" -} - -@test "gpiofind: line not found" { - gpiosim_chip sim0 num_lines=4 - gpiosim_chip sim1 num_lines=8 - gpiosim_chip sim2 num_lines=16 - - run_tool gpiofind nonexistent-line - - test "$status" -eq "1" -} - -@test "gpiofind: invalid args" { - run_tool gpiodetect unimplemented-arg - test "$status" -eq 1 -} - -# -# gpioget test cases -# - -@test "gpioget: read all lines" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - run_tool gpioget "$(gpiosim_chip_name sim0)" 0 1 2 3 4 5 6 7 - - test "$status" -eq "0" - test "$output" = "0 0 1 1 0 1 0 1" -} - -@test "gpioget: read all lines (active-low)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - run_tool gpioget --active-low "$(gpiosim_chip_name sim0)" 0 1 2 3 4 5 6 7 - - test "$status" -eq "0" - test "$output" = "1 1 0 0 1 0 1 0" -} - -@test "gpioget: read all lines (pull-up)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - run_tool gpioget --bias=pull-up "$(gpiosim_chip_name sim0)" 0 1 2 3 4 5 6 7 - - test "$status" -eq "0" - test "$output" = "1 1 1 1 1 1 1 1" -} - -@test "gpioget: read all lines (pull-down)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - run_tool gpioget --bias=pull-down "$(gpiosim_chip_name sim0)" 0 1 2 3 4 5 6 7 - - test "$status" -eq "0" - test "$output" = "0 0 0 0 0 0 0 0" -} - -@test "gpioget: read some lines" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 1 pull-up - gpiosim_set_pull sim0 4 pull-up - gpiosim_set_pull sim0 6 pull-up - - run_tool gpioget "$(gpiosim_chip_name sim0)" 0 1 4 6 - - test "$status" -eq "0" - test "$output" = "0 1 1 1" -} - -@test "gpioget: no arguments" { - run_tool gpioget - - test "$status" -eq "1" - output_regex_match ".*gpiochip must be specified" -} - -@test "gpioget: no lines specified" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioget "$(gpiosim_chip_name sim0)" - - test "$status" -eq "1" - output_regex_match ".*at least one GPIO line offset must be specified" -} - -@test "gpioget: too many lines specified" { - gpiosim_chip sim0 num_lines=4 - - run_tool gpioget "$(gpiosim_chip_name sim0)" 0 1 2 3 4 - - test "$status" -eq "1" - output_regex_match ".*unable to request lines.*" -} - -@test "gpioget: same line twice" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioget "$(gpiosim_chip_name sim0)" 0 0 - - test "$status" -eq "1" - output_regex_match ".*offsets must be unique" -} - -@test "gpioget: invalid bias" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioget --bias=bad "$(gpiosim_chip_name sim0)" 0 1 - - test "$status" -eq "1" - output_regex_match ".*invalid bias.*" -} - -# -# gpioset test cases -# - -@test "gpioset: set lines and wait for SIGTERM" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpioset --mode=signal "$(gpiosim_chip_name sim0)" \ - 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 0 0 - gpiosim_check_value sim0 1 0 - gpiosim_check_value sim0 2 1 - gpiosim_check_value sim0 3 1 - gpiosim_check_value sim0 4 1 - gpiosim_check_value sim0 5 1 - gpiosim_check_value sim0 6 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set lines and wait for SIGTERM (active-low)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpioset --active-low --mode=signal "$(gpiosim_chip_name sim0)" \ - 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 0 1 - gpiosim_check_value sim0 1 1 - gpiosim_check_value sim0 2 0 - gpiosim_check_value sim0 3 0 - gpiosim_check_value sim0 4 0 - gpiosim_check_value sim0 5 0 - gpiosim_check_value sim0 6 1 - gpiosim_check_value sim0 7 0 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set lines and wait for SIGTERM (push-pull)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpioset --drive=push-pull --mode=signal "$(gpiosim_chip_name sim0)" \ - 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 0 0 - gpiosim_check_value sim0 1 0 - gpiosim_check_value sim0 2 1 - gpiosim_check_value sim0 3 1 - gpiosim_check_value sim0 4 1 - gpiosim_check_value sim0 5 1 - gpiosim_check_value sim0 6 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set lines and wait for SIGTERM (open-drain)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - coproc_run_tool gpioset --drive=open-drain --mode=signal "$(gpiosim_chip_name sim0)" \ - 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 0 0 - gpiosim_check_value sim0 1 0 - gpiosim_check_value sim0 2 1 - gpiosim_check_value sim0 3 1 - gpiosim_check_value sim0 4 0 - gpiosim_check_value sim0 5 1 - gpiosim_check_value sim0 6 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set lines and wait for SIGTERM (open-source)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 5 pull-up - gpiosim_set_pull sim0 7 pull-up - - coproc_run_tool gpioset --drive=open-source --mode=signal "$(gpiosim_chip_name sim0)" \ - 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 0 0 - gpiosim_check_value sim0 1 0 - gpiosim_check_value sim0 2 1 - gpiosim_check_value sim0 3 1 - gpiosim_check_value sim0 4 1 - gpiosim_check_value sim0 5 1 - gpiosim_check_value sim0 6 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set some lines and wait for ENTER" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpioset --mode=wait "$(gpiosim_chip_name sim0)" \ - 1=0 2=1 5=1 6=0 7=1 - - gpiosim_check_value sim0 1 0 - gpiosim_check_value sim0 2 1 - gpiosim_check_value sim0 5 1 - gpiosim_check_value sim0 6 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_stdin_write "" - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set some lines and wait for SIGINT" { - gpiosim_chip sim0 num_lines=4 - - coproc_run_tool gpioset --mode=signal "$(gpiosim_chip_name sim0)" 0=1 - - gpiosim_check_value sim0 0 1 - - coproc_tool_kill -SIGINT - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: set some lines and wait with --mode=time" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpioset --mode=time --sec=1 --usec=200000 \ - "$(gpiosim_chip_name sim0)" 0=1 5=0 7=1 - - gpiosim_check_value sim0 0 1 - gpiosim_check_value sim0 5 0 - gpiosim_check_value sim0 7 1 - - coproc_tool_wait - - test "$status" -eq "0" -} - -@test "gpioset: no arguments" { - run_tool gpioset - - test "$status" -eq "1" - output_regex_match ".*gpiochip must be specified" -} - -@test "gpioset: no lines specified" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim1)" - - test "$status" -eq "1" - output_regex_match ".*at least one GPIO line offset to value mapping must be specified" -} - -@test "gpioset: too many lines specified" { - gpiosim_chip sim0 num_lines=4 - - run_tool gpioset "$(gpiosim_chip_name sim0)" 0=1 1=1 2=1 3=1 4=1 5=1 - - test "$status" -eq "1" - output_regex_match ".*unable to request lines.*" -} - -@test "gpioset: use --sec without --mode=time" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset --mode=exit --sec=1 "$(gpiosim_chip_name sim0)" 0=1 - - test "$status" -eq "1" - output_regex_match ".*can't specify wait time in this mode" -} - -@test "gpioset: use --usec without --mode=time" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset --mode=exit --usec=1 "$(gpiosim_chip_name sim1)" 0=1 - - test "$status" -eq "1" - output_regex_match ".*can't specify wait time in this mode" -} - -@test "gpioset: default mode" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim0)" 0=1 - - test "$status" -eq "0" -} - -@test "gpioset: invalid mapping" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim1)" 0=c - - test "$status" -eq "1" - output_regex_match ".*invalid offset<->value mapping" -} - -@test "gpioset: invalid value" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim1)" 0=3 - - test "$status" -eq "1" - output_regex_match ".*value must be 0 or 1" -} - -@test "gpioset: invalid offset" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim1)" 4000000000=0 - - test "$status" -eq "1" - output_regex_match ".*invalid offset" -} - -@test "gpioset: invalid bias" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset --bias=bad "$(gpiosim_chip_name sim1)" 0=1 1=1 - - test "$status" -eq "1" - output_regex_match ".*invalid bias.*" -} - -@test "gpioset: invalid drive" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset --drive=bad "$(gpiosim_chip_name sim1)" 0=1 1=1 - - test "$status" -eq "1" - output_regex_match ".*invalid drive.*" -} - -@test "gpioset: daemonize in invalid mode" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset --background "$(gpiosim_chip_name sim1)" 0=1 - - test "$status" -eq "1" - output_regex_match ".*can't daemonize in this mode" -} - -@test "gpioset: same line twice" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpioset "$(gpiosim_chip_name sim0)" 0=1 0=1 - - test "$status" -eq "1" - output_regex_match ".*offsets must be unique" -} - -# -# gpiomon test cases -# - -@test "gpiomon: single rising edge event" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --rising-edge "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event:\\s+RISING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[\s*[0-9]+\.[0-9]+\]" -} - -@test "gpiomon: single falling edge event" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --falling-edge "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event:\\s+FALLING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[\s*[0-9]+\.[0-9]+\]" -} - -@test "gpiomon: single falling edge event (pull-up)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 4 pull-down - - coproc_run_tool gpiomon --bias=pull-up "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event:\\s+FALLING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[\s*[0-9]+\.[0-9]+\]" -} - -@test "gpiomon: single rising edge event (pull-down)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 4 pull-up - - coproc_run_tool gpiomon --bias=pull-down "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event:\\s+RISING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[\s*[0-9]+\.[0-9]+\]" -} - -@test "gpiomon: single rising edge event (active-low)" { - gpiosim_chip sim0 num_lines=8 - - gpiosim_set_pull sim0 4 pull-up - - coproc_run_tool gpiomon --rising-edge --active-low "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event:\\s+RISING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[\s*[0-9]+\.[0-9]+\]" -} - -@test "gpiomon: single rising edge event (silent mode)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --rising-edge --silent "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test -z "$output" -} - -@test "gpiomon: four alternating events" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --num-events=4 "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event\\:\\s+FALLING\\s+EDGE\\s+offset\\:\\s+4\\s+timestamp:\\s+\\[\s*[0-9]+\\.[0-9]+\\]" - output_regex_match \ -"event\\:\\s+RISING\\s+EDGE\\s+offset\\:\\s+4\\s+timestamp:\\s+\\[\s*[0-9]+\\.[0-9]+\\]" -} - -@test "gpiomon: exit after SIGINT" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "$(gpiosim_chip_name sim0)" 4 - - coproc_tool_kill -SIGINT - coproc_tool_wait - - test "$status" -eq "0" - test -z "$output" -} - -@test "gpiomon: exit after SIGTERM" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "$(gpiosim_chip_name sim0)" 4 - - coproc_tool_kill -SIGTERM - coproc_tool_wait - - test "$status" -eq "0" - test -z "$output" -} - -@test "gpiomon: both event flags" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --falling-edge --rising-edge "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - gpiosim_set_pull sim0 4 pull-down - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match \ -"event\\:\\s+FALLING\\s+EDGE\\s+offset\\:\\s+4\\s+timestamp:\\s+\\[\s*[0-9]+\\.[0-9]+\\]" - output_regex_match \ -"event\\:\\s+RISING\\s+EDGE\\s+offset\\:\\s+4\\s+timestamp:\\s+\\[\s*[0-9]+\\.[0-9]+\\]" -} - -@test "gpiomon: watch multiple lines" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --format=%o "$(gpiosim_chip_name sim0)" 1 2 3 4 5 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 3 pull-up - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "${lines[0]}" = "2" - test "${lines[1]}" = "3" - test "${lines[2]}" = "4" -} - -@test "gpiomon: watch multiple lines (lines in mixed-up order)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon --format=%o "$(gpiosim_chip_name sim0)" 5 2 7 1 6 - - gpiosim_set_pull sim0 2 pull-up - gpiosim_set_pull sim0 1 pull-up - gpiosim_set_pull sim0 6 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "${lines[0]}" = "2" - test "${lines[1]}" = "1" - test "${lines[2]}" = "6" -} - -@test "gpiomon: same line twice" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpiomon "$(gpiosim_chip_name sim0)" 0 0 - - test "$status" -eq "1" - output_regex_match ".*offsets must be unique" -} - -@test "gpiomon: no arguments" { - run_tool gpiomon - - test "$status" -eq "1" - output_regex_match ".*gpiochip must be specified" -} - -@test "gpiomon: line not specified" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpiomon "$(gpiosim_chip_name sim0)" - - test "$status" -eq "1" - output_regex_match ".*GPIO line offset must be specified" -} - -@test "gpiomon: line out of range" { - gpiosim_chip sim0 num_lines=4 - - run_tool gpiomon "$(gpiosim_chip_name sim0)" 5 - - test "$status" -eq "1" - output_regex_match ".*unable to request lines" -} - -@test "gpiomon: invalid bias" { - gpiosim_chip sim0 num_lines=8 - - run_tool gpiomon --bias=bad "$(gpiosim_chip_name sim0)" 0 1 - - test "$status" -eq "1" - output_regex_match ".*invalid bias.*" -} - -@test "gpiomon: custom format (event type + offset)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%e %o" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "1 4" -} - -@test "gpiomon: custom format (event type + offset joined)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%e%o" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "14" -} - -@test "gpiomon: custom format (timestamp)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%e %o %s.%n" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - output_regex_match "1 4 [0-9]+\\.[0-9]+" -} - -@test "gpiomon: custom format (double percent sign)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%%" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "%" -} - -@test "gpiomon: custom format (double percent sign + event type specifier)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%%e" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "%e" -} - -@test "gpiomon: custom format (single percent sign)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "%" -} - -@test "gpiomon: custom format (single percent sign between other characters)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=foo % bar" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "foo % bar" -} - -@test "gpiomon: custom format (unknown specifier)" { - gpiosim_chip sim0 num_lines=8 - - coproc_run_tool gpiomon "--format=%x" "$(gpiosim_chip_name sim0)" 4 - - gpiosim_set_pull sim0 4 pull-up - sleep 0.2 - - coproc_tool_kill - coproc_tool_wait - - test "$status" -eq "0" - test "$output" = "%x" -} diff --git a/tools/gpiodetect.c b/tools/gpiodetect.c index 8f6e8b3..30bde32 100644 --- a/tools/gpiodetect.c +++ b/tools/gpiodetect.c @@ -11,82 +11,3 @@ #include "tools-common.h" -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS]\n", get_progname()); - printf("\n"); - printf("List all GPIO chips, print their labels and number of GPIO lines.\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); -} - -int main(int argc, char **argv) -{ - int optc, opti, num_chips, i; - struct gpiod_chip *chip; - struct gpiod_chip_info *info; - struct dirent **entries; - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (argc > 0) - die("unrecognized argument: %s", argv[0]); - - num_chips = scandir("/dev/", &entries, chip_dir_filter, alphasort); - if (num_chips < 0) - die_perror("unable to scan /dev"); - - for (i = 0; i < num_chips; i++) { - chip = chip_open_by_name(entries[i]->d_name); - if (!chip) - die_perror("unable to open %s", entries[i]->d_name); - - info = gpiod_chip_get_info(chip); - if (!info) - die_perror("unable to get info for %s", entries[i]->d_name); - - - printf("%s [%s] (%zu lines)\n", - gpiod_chip_info_get_name(info), - gpiod_chip_info_get_label(info), - gpiod_chip_info_get_num_lines(info)); - - gpiod_chip_info_free(info); - gpiod_chip_close(chip); - free(entries[i]); - } - - free(entries); - - return EXIT_SUCCESS; -} diff --git a/tools/gpiofind.c b/tools/gpiofind.c deleted file mode 100644 index 03b15c9..0000000 --- a/tools/gpiofind.c +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski - -#include -#include -#include -#include -#include -#include -#include - -#include "tools-common.h" - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS] \n", get_progname()); - printf("\n"); - printf("Find a GPIO line by name. The output of this command can be used as input for gpioget/set.\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); -} - -int main(int argc, char **argv) -{ - int i, num_chips, optc, opti, offset; - struct gpiod_chip *chip; - struct gpiod_chip_info *info; - struct dirent **entries; - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (argc != 1) - die("exactly one GPIO line name must be specified"); - - num_chips = scandir("/dev/", &entries, chip_dir_filter, alphasort); - if (num_chips < 0) - die_perror("unable to scan /dev"); - - for (i = 0; i < num_chips; i++) { - chip = chip_open_by_name(entries[i]->d_name); - if (!chip) { - if (errno == EACCES) - continue; - - die_perror("unable to open %s", entries[i]->d_name); - } - - offset = gpiod_chip_get_line_offset_from_name(chip, argv[0]); - if (offset >= 0) { - info = gpiod_chip_get_info(chip); - if (!info) - die_perror("unable to get info for %s", entries[i]->d_name); - - printf("%s %u\n", - gpiod_chip_info_get_name(info), offset); - gpiod_chip_info_free(info); - gpiod_chip_close(chip); - return EXIT_SUCCESS; - } - } - - return EXIT_FAILURE; -} diff --git a/tools/gpioget.c b/tools/gpioget.c index b68212d..1b3e666 100644 --- a/tools/gpioget.c +++ b/tools/gpioget.c @@ -10,156 +10,3 @@ #include "tools-common.h" -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { "dir-as-is", no_argument, NULL, 'n' }, - { "bias", required_argument, NULL, 'B' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hvlnB:"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS] ...\n", - get_progname()); - printf("\n"); - printf("Read line value(s) from a GPIO chip\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); - printf(" -l, --active-low:\tset the line active state to low\n"); - printf(" -n, --dir-as-is:\tdon't force-reconfigure line direction\n"); - printf(" -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n"); - printf(" set the line bias\n"); - printf("\n"); - print_bias_help(); -} - -int main(int argc, char **argv) -{ - int direction = GPIOD_LINE_DIRECTION_INPUT; - int optc, opti, bias = 0, ret, *values; - struct gpiod_line_settings *settings; - struct gpiod_request_config *req_cfg; - struct gpiod_line_request *request; - struct gpiod_line_config *line_cfg; - struct gpiod_chip *chip; - bool active_low = false; - unsigned int *offsets; - size_t i, num_lines; - char *device, *end; - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case 'l': - active_low = true; - break; - case 'n': - direction = GPIOD_LINE_DIRECTION_AS_IS; - break; - case 'B': - bias = parse_bias(optarg); - break; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (argc < 1) - die("gpiochip must be specified"); - - if (argc < 2) - die("at least one GPIO line offset must be specified"); - - device = argv[0]; - num_lines = argc - 1; - - offsets = calloc(num_lines, sizeof(*offsets)); - values = calloc(num_lines, sizeof(*values)); - if (!offsets || ! values) - die("out of memory"); - - for (i = 0; i < num_lines; i++) { - offsets[i] = strtoul(argv[i + 1], &end, 10); - if (*end != '\0' || offsets[i] > INT_MAX) - die("invalid GPIO offset: %s", argv[i + 1]); - } - - if (has_duplicate_offsets(num_lines, offsets)) - die("offsets must be unique"); - - chip = chip_open_lookup(device); - if (!chip) - die_perror("unable to open %s", device); - - settings = gpiod_line_settings_new(); - if (!settings) - die_perror("unable to allocate line settings"); - - gpiod_line_settings_set_direction(settings, direction); - - if (bias) - gpiod_line_settings_set_bias(settings, bias); - - if (active_low) - gpiod_line_settings_set_active_low(settings, active_low); - - req_cfg = gpiod_request_config_new(); - if (!req_cfg) - die_perror("unable to allocate the request config structure"); - - gpiod_request_config_set_consumer(req_cfg, "gpioget"); - - line_cfg = gpiod_line_config_new(); - if (!line_cfg) - die_perror("unable to allocate the line config structure"); - - ret = gpiod_line_config_add_line_settings(line_cfg, offsets, - num_lines, settings); - if (ret) - die_perror("unable to add line settings"); - - request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); - if (!request) - die_perror("unable to request lines"); - - ret = gpiod_line_request_get_values(request, values); - if (ret) - die_perror("unable to read GPIO line values"); - - for (i = 0; i < num_lines; i++) { - printf("%d", values[i]); - if (i != num_lines - 1) - printf(" "); - } - printf("\n"); - - gpiod_line_request_release(request); - gpiod_request_config_free(req_cfg); - gpiod_line_config_free(line_cfg); - gpiod_line_settings_free(settings); - gpiod_chip_close(chip); - free(offsets); - free(values); - - return EXIT_SUCCESS; -} diff --git a/tools/gpioinfo.c b/tools/gpioinfo.c index fbe2a13..ae368fa 100644 --- a/tools/gpioinfo.c +++ b/tools/gpioinfo.c @@ -12,243 +12,3 @@ #include "tools-common.h" -typedef bool (*is_set_func)(struct gpiod_line_info *); - -struct flag { - const char *name; - is_set_func is_set; -}; - -static bool line_bias_is_pullup(struct gpiod_line_info *info) -{ - return gpiod_line_info_get_bias(info) == GPIOD_LINE_BIAS_PULL_UP; -} - -static bool line_bias_is_pulldown(struct gpiod_line_info *info) -{ - return gpiod_line_info_get_bias(info) == GPIOD_LINE_BIAS_PULL_DOWN; -} - -static bool line_bias_is_disabled(struct gpiod_line_info *info) -{ - return gpiod_line_info_get_bias(info) == GPIOD_LINE_BIAS_DISABLED; -} - -static bool line_drive_is_open_drain(struct gpiod_line_info *info) -{ - return gpiod_line_info_get_drive(info) == GPIOD_LINE_DRIVE_OPEN_DRAIN; -} - -static bool line_drive_is_open_source(struct gpiod_line_info *info) -{ - return gpiod_line_info_get_drive(info) == GPIOD_LINE_DRIVE_OPEN_SOURCE; -} - -static const struct flag flags[] = { - { - .name = "used", - .is_set = gpiod_line_info_is_used, - }, - { - .name = "open-drain", - .is_set = line_drive_is_open_drain, - }, - { - .name = "open-source", - .is_set = line_drive_is_open_source, - }, - { - .name = "pull-up", - .is_set = line_bias_is_pullup, - }, - { - .name = "pull-down", - .is_set = line_bias_is_pulldown, - }, - { - .name = "bias-disabled", - .is_set = line_bias_is_disabled, - }, -}; - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS] ...\n", get_progname()); - printf("\n"); - printf("Print information about all lines of the specified GPIO chip(s) (or all gpiochips if none are specified).\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); -} - -static PRINTF(3, 4) void prinfo(bool *of, - unsigned int prlen, const char *fmt, ...) -{ - char *buf, *buffmt = NULL; - size_t len; - va_list va; - int rv; - - va_start(va, fmt); - rv = vasprintf(&buf, fmt, va); - va_end(va); - if (rv < 0) - die("vasprintf: %s\n", strerror(errno)); - - len = strlen(buf) - 1; - - if (len >= prlen || *of) { - *of = true; - printf("%s", buf); - } else { - rv = asprintf(&buffmt, "%%%us", prlen); - if (rv < 0) - die("asprintf: %s\n", strerror(errno)); - - printf(buffmt, buf); - } - - free(buf); - if (fmt) - free(buffmt); -} - -static void list_lines(struct gpiod_chip *chip) -{ - bool flag_printed, of, active_low; - struct gpiod_chip_info *chip_info; - struct gpiod_line_info *info; - const char *name, *consumer; - size_t i, offset, num_lines; - int direction; - - chip_info = gpiod_chip_get_info(chip); - if (!chip_info) - die_perror("unable to retrieve the chip info from chip"); - - num_lines = gpiod_chip_info_get_num_lines(chip_info); - printf("%s - %zu lines:\n", - gpiod_chip_info_get_name(chip_info), num_lines); - - for (offset = 0; offset < num_lines; offset++) { - info = gpiod_chip_get_line_info(chip, offset); - if (!info) - die_perror("unable to retrieve the line info from chip"); - name = gpiod_line_info_get_name(info); - consumer = gpiod_line_info_get_consumer(info); - direction = gpiod_line_info_get_direction(info); - active_low = gpiod_line_info_is_active_low(info); - - of = false; - - printf("\tline "); - prinfo(&of, 3, "%zu", offset); - printf(": "); - - name ? prinfo(&of, 12, "\"%s\"", name) - : prinfo(&of, 12, "unnamed"); - printf(" "); - - if (!gpiod_line_info_is_used(info)) - prinfo(&of, 12, "unused"); - else - consumer ? prinfo(&of, 12, "\"%s\"", consumer) - : prinfo(&of, 12, "kernel"); - - printf(" "); - - prinfo(&of, 8, "%s ", direction == GPIOD_LINE_DIRECTION_INPUT - ? "input" : "output"); - prinfo(&of, 13, "%s ", - active_low ? "active-low" : "active-high"); - - flag_printed = false; - for (i = 0; i < ARRAY_SIZE(flags); i++) { - if (flags[i].is_set(info)) { - if (flag_printed) - printf(" "); - else - printf("["); - printf("%s", flags[i].name); - flag_printed = true; - } - } - if (flag_printed) - printf("]"); - - printf("\n"); - - gpiod_line_info_free(info); - } - gpiod_chip_info_free(chip_info); -} - -int main(int argc, char **argv) -{ - int num_chips, i, optc, opti; - struct gpiod_chip *chip; - struct dirent **entries; - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (argc == 0) { - num_chips = scandir("/dev/", &entries, - chip_dir_filter, alphasort); - if (num_chips < 0) - die_perror("unable to scan /dev"); - - for (i = 0; i < num_chips; i++) { - chip = chip_open_by_name(entries[i]->d_name); - if (!chip) - die_perror("unable to open %s", - entries[i]->d_name); - - list_lines(chip); - - gpiod_chip_close(chip); - free(entries[i]); - } - free(entries); - } else { - for (i = 0; i < argc; i++) { - chip = chip_open_lookup(argv[i]); - if (!chip) - die_perror("looking up chip %s", argv[i]); - - list_lines(chip); - - gpiod_chip_close(chip); - } - } - - return EXIT_SUCCESS; -} diff --git a/tools/gpiomon.c b/tools/gpiomon.c index dff12ea..6fa19b6 100644 --- a/tools/gpiomon.c +++ b/tools/gpiomon.c @@ -17,317 +17,3 @@ #define EVENT_BUF_SIZE 32 -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { "bias", required_argument, NULL, 'B' }, - { "num-events", required_argument, NULL, 'n' }, - { "silent", no_argument, NULL, 's' }, - { "rising-edge", no_argument, NULL, 'r' }, - { "falling-edge", no_argument, NULL, 'f' }, - { "line-buffered", no_argument, NULL, 'b' }, - { "format", required_argument, NULL, 'F' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hvlB:n:srfbF:"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS] ...\n", - get_progname()); - printf("\n"); - printf("Wait for events on GPIO lines and print them to standard output\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); - printf(" -l, --active-low:\tset the line active state to low\n"); - printf(" -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n"); - printf(" set the line bias\n"); - printf(" -n, --num-events=NUM:\texit after processing NUM events\n"); - printf(" -s, --silent:\t\tdon't print event info\n"); - printf(" -r, --rising-edge:\tonly process rising edge events\n"); - printf(" -f, --falling-edge:\tonly process falling edge events\n"); - printf(" -b, --line-buffered:\tset standard output as line buffered\n"); - printf(" -F, --format=FMT\tspecify custom output format\n"); - printf("\n"); - print_bias_help(); - printf("\n"); - printf("Format specifiers:\n"); - printf(" %%o: GPIO line offset\n"); - printf(" %%e: event type (0 - falling edge, 1 rising edge)\n"); - printf(" %%s: seconds part of the event timestamp\n"); - printf(" %%n: nanoseconds part of the event timestamp\n"); -} - -struct mon_ctx { - unsigned int offset; - bool silent; - char *fmt; -}; - -static void event_print_custom(unsigned int offset, uint64_t timeout, - int event_type, struct mon_ctx *ctx) -{ - char *prev, *curr, fmt; - - for (prev = curr = ctx->fmt;;) { - curr = strchr(curr, '%'); - if (!curr) { - fputs(prev, stdout); - break; - } - - if (prev != curr) - fwrite(prev, curr - prev, 1, stdout); - - fmt = *(curr + 1); - - switch (fmt) { - case 'o': - printf("%u", offset); - break; - case 'e': - if (event_type == GPIOD_EDGE_EVENT_RISING_EDGE) - fputc('1', stdout); - else - fputc('0', stdout); - break; - case 's': - printf("%"PRIu64, timeout / 1000000000); - break; - case 'n': - printf("%"PRIu64, timeout % 1000000000); - break; - case '%': - fputc('%', stdout); - break; - case '\0': - fputc('%', stdout); - goto end; - default: - fwrite(curr, 2, 1, stdout); - break; - } - - curr += 2; - prev = curr; - } - -end: - fputc('\n', stdout); -} - -static void event_print_human_readable(unsigned int offset, - uint64_t timeout, int event_type) -{ - char *evname; - - if (event_type == GPIOD_EDGE_EVENT_RISING_EDGE) - evname = " RISING EDGE"; - else - evname = "FALLING EDGE"; - - printf("event: %s offset: %u timestamp: [%8"PRIu64".%09"PRIu64"]\n", - evname, offset, timeout / 1000000000, timeout % 1000000000); -} - -static void handle_event(unsigned int line_offset, unsigned int event_type, - uint64_t timestamp, struct mon_ctx *ctx) -{ - if (!ctx->silent) { - if (ctx->fmt) - event_print_custom(line_offset, timestamp, - event_type, ctx); - else - event_print_human_readable(line_offset, - timestamp, event_type); - } -} - -static void handle_signal(int signum UNUSED) -{ - exit(EXIT_SUCCESS); -} - -int main(int argc, char **argv) -{ - bool watch_rising = false, watch_falling = false, active_low = false; - size_t num_lines = 0, events_wanted = 0, events_done = 0; - struct gpiod_edge_event_buffer *event_buffer; - int optc, opti, ret, i, edge, bias = 0; - uint64_t timeout = 10 * 1000000000LLU; - struct gpiod_line_settings *settings; - struct gpiod_request_config *req_cfg; - struct gpiod_line_request *request; - struct gpiod_line_config *line_cfg; - unsigned int offsets[64], offset; - struct gpiod_edge_event *event; - struct gpiod_chip *chip; - struct mon_ctx ctx; - char *end; - - /* - * FIXME: use signalfd once the API has been converted to using a single file - * descriptor as provided by uAPI v2. - */ - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); - - memset(&ctx, 0, sizeof(ctx)); - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case 'l': - active_low = true; - break; - case 'B': - bias = parse_bias(optarg); - break; - case 'n': - events_wanted = strtoul(optarg, &end, 10); - if (*end != '\0') - die("invalid number: %s", optarg); - break; - case 's': - ctx.silent = true; - break; - case 'r': - watch_rising = true; - break; - case 'f': - watch_falling = true; - break; - case 'b': - setlinebuf(stdout); - break; - case 'F': - ctx.fmt = optarg; - break; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (watch_rising && !watch_falling) - edge = GPIOD_LINE_EDGE_RISING; - else if (watch_falling && !watch_rising) - edge = GPIOD_LINE_EDGE_FALLING; - else - edge = GPIOD_LINE_EDGE_BOTH; - - if (argc < 1) - die("gpiochip must be specified"); - - if (argc < 2) - die("at least one GPIO line offset must be specified"); - - if (argc > 65) - die("too many offsets given"); - - for (i = 1; i < argc; i++) { - offset = strtoul(argv[i], &end, 10); - if (*end != '\0' || offset > INT_MAX) - die("invalid GPIO offset: %s", argv[i]); - - offsets[i - 1] = offset; - num_lines++; - } - - if (has_duplicate_offsets(num_lines, offsets)) - die("offsets must be unique"); - - chip = chip_open_lookup(argv[0]); - if (!chip) - die_perror("unable to open %s", argv[0]); - - settings = gpiod_line_settings_new(); - if (!settings) - die_perror("unable to allocate line settings"); - - if (bias) - gpiod_line_settings_set_bias(settings, bias); - if (active_low) - gpiod_line_settings_set_active_low(settings, active_low); - gpiod_line_settings_set_edge_detection(settings, edge); - - req_cfg = gpiod_request_config_new(); - if (!req_cfg) - die_perror("unable to allocate the request config structure"); - - gpiod_request_config_set_consumer(req_cfg, "gpiomon"); - - line_cfg = gpiod_line_config_new(); - if (!line_cfg) - die_perror("unable to allocate the line config structure"); - - ret = gpiod_line_config_add_line_settings(line_cfg, offsets, - num_lines, settings); - if (ret) - die_perror("unable to add line settings"); - - request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); - if (!request) - die_perror("unable to request lines"); - - event_buffer = gpiod_edge_event_buffer_new(EVENT_BUF_SIZE); - if (!event_buffer) - die_perror("unable to allocate the line event buffer"); - - for (;;) { - ret = gpiod_line_request_wait_edge_event(request, timeout); - if (ret < 0) - die_perror("error waiting for events"); - if (ret == 0) - continue; - - ret = gpiod_line_request_read_edge_event(request, event_buffer, - EVENT_BUF_SIZE); - if (ret < 0) - die_perror("error reading line events"); - - for (i = 0; i < ret; i++) { - event = gpiod_edge_event_buffer_get_event(event_buffer, - i); - if (!event) - die_perror("unable to retrieve the event from the buffer"); - - handle_event(gpiod_edge_event_get_line_offset(event), - gpiod_edge_event_get_event_type(event), - gpiod_edge_event_get_timestamp_ns(event), - &ctx); - - events_done++; - - if (events_wanted && events_done >= events_wanted) - goto done; - } - } - -done: - gpiod_edge_event_buffer_free(event_buffer); - gpiod_line_request_release(request); - gpiod_request_config_free(req_cfg); - gpiod_line_config_free(line_cfg); - gpiod_line_settings_free(settings); - gpiod_chip_close(chip); - - return EXIT_SUCCESS; -} diff --git a/tools/gpioset.c b/tools/gpioset.c index 290d1a3..0e3a1e4 100644 --- a/tools/gpioset.c +++ b/tools/gpioset.c @@ -14,333 +14,3 @@ #include "tools-common.h" -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { "bias", required_argument, NULL, 'B' }, - { "drive", required_argument, NULL, 'D' }, - { "mode", required_argument, NULL, 'm' }, - { "sec", required_argument, NULL, 's' }, - { "usec", required_argument, NULL, 'u' }, - { "background", no_argument, NULL, 'b' }, - { GETOPT_NULL_LONGOPT }, -}; - -static const char *const shortopts = "+hvlB:D:m:s:u:b"; - -static void print_help(void) -{ - printf("Usage: %s [OPTIONS] = = ...\n", - get_progname()); - printf("\n"); - printf("Set GPIO line values of a GPIO chip and maintain the state until the process exits\n"); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help:\t\tdisplay this message and exit\n"); - printf(" -v, --version:\tdisplay the version and exit\n"); - printf(" -l, --active-low:\tset the line active state to low\n"); - printf(" -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n"); - printf(" set the line bias\n"); - printf(" -D, --drive=[push-pull|open-drain|open-source] (defaults to 'push-pull'):\n"); - printf(" set the line drive mode\n"); - printf(" -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n"); - printf(" tell the program what to do after setting values\n"); - printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n"); - printf(" -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n"); - printf(" -b, --background:\tafter setting values: detach from the controlling terminal\n"); - printf("\n"); - print_bias_help(); - printf("\n"); - printf("Drives:\n"); - printf(" push-pull:\tdrive the line both high and low\n"); - printf(" open-drain:\tdrive the line low or go high impedance\n"); - printf(" open-source:\tdrive the line high or go high impedance\n"); - printf("\n"); - printf("Modes:\n"); - printf(" exit:\t\tset values and exit immediately\n"); - printf(" wait:\t\tset values and wait for user to press ENTER\n"); - printf(" time:\t\tset values and sleep for a specified amount of time\n"); - printf(" signal:\tset values and wait for SIGINT or SIGTERM\n"); - printf("\n"); - printf("Note: the state of a GPIO line controlled over the character device reverts to default\n"); - printf("when the last process referencing the file descriptor representing the device file exits.\n"); - printf("This means that it's wrong to run gpioset, have it exit and expect the line to continue\n"); - printf("being driven high or low. It may happen if given pin is floating but it must be interpreted\n"); - printf("as undefined behavior.\n"); -} - -struct callback_data { - /* Replace with a union once we have more modes using callback data. */ - struct timeval tv; - bool daemonize; -}; - -static void maybe_daemonize(bool daemonize) -{ - int rv; - - if (daemonize) { - rv = daemon(0, 0); - if (rv < 0) - die("unable to daemonize: %s", strerror(errno)); - } -} - -static void wait_enter(void *data UNUSED) -{ - getchar(); -} - -static void wait_time(void *data) -{ - struct callback_data *cbdata = data; - - maybe_daemonize(cbdata->daemonize); - select(0, NULL, NULL, NULL, &cbdata->tv); -} - -static void wait_signal(void *data) -{ - struct callback_data *cbdata = data; - struct pollfd pfd; - int sigfd, rv; - - sigfd = make_signalfd(); - - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = sigfd; - pfd.events = POLLIN | POLLPRI; - - maybe_daemonize(cbdata->daemonize); - - for (;;) { - rv = poll(&pfd, 1, 1000 /* one second */); - if (rv < 0) - die("error polling for signals: %s", strerror(errno)); - else if (rv > 0) - break; - } - - /* - * Don't bother reading siginfo - it's enough to know that we - * received any signal. - */ - close(sigfd); -} - -enum { - MODE_EXIT = 0, - MODE_WAIT, - MODE_TIME, - MODE_SIGNAL, -}; - -struct mode_mapping { - int id; - const char *name; - void (*callback)(void *); -}; - -static const struct mode_mapping modes[] = { - [MODE_EXIT] = { - .id = MODE_EXIT, - .name = "exit", - .callback = NULL, - }, - [MODE_WAIT] = { - .id = MODE_WAIT, - .name = "wait", - .callback = wait_enter, - }, - [MODE_TIME] = { - .id = MODE_TIME, - .name = "time", - .callback = wait_time, - }, - [MODE_SIGNAL] = { - .id = MODE_SIGNAL, - .name = "signal", - .callback = wait_signal, - }, -}; - -static const struct mode_mapping *parse_mode(const char *mode) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(modes); i++) - if (strcmp(mode, modes[i].name) == 0) - return &modes[i]; - - return NULL; -} - -static int parse_drive(const char *option) -{ - if (strcmp(option, "open-drain") == 0) - return GPIOD_LINE_DRIVE_OPEN_DRAIN; - if (strcmp(option, "open-source") == 0) - return GPIOD_LINE_DRIVE_OPEN_SOURCE; - if (strcmp(option, "push-pull") != 0) - die("invalid drive: %s", option); - return 0; -} - -int main(int argc, char **argv) -{ - const struct mode_mapping *mode = &modes[MODE_EXIT]; - int ret, optc, opti, bias = 0, drive = 0, *values; - struct gpiod_line_settings *settings; - struct gpiod_request_config *req_cfg; - struct gpiod_line_request *request; - struct gpiod_line_config *line_cfg; - struct callback_data cbdata; - struct gpiod_chip *chip; - bool active_low = false; - unsigned int *offsets; - size_t i, num_lines; - char *device, *end; - - memset(&cbdata, 0, sizeof(cbdata)); - - for (;;) { - optc = getopt_long(argc, argv, shortopts, longopts, &opti); - if (optc < 0) - break; - - switch (optc) { - case 'h': - print_help(); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case 'l': - active_low = true; - break; - case 'B': - bias = parse_bias(optarg); - break; - case 'D': - drive = parse_drive(optarg); - break; - case 'm': - mode = parse_mode(optarg); - if (!mode) - die("invalid mode: %s", optarg); - break; - case 's': - cbdata.tv.tv_sec = strtoul(optarg, &end, 10); - if (*end != '\0') - die("invalid time value in seconds: %s", optarg); - break; - case 'u': - cbdata.tv.tv_usec = strtoul(optarg, &end, 10); - if (*end != '\0') - die("invalid time value in microseconds: %s", - optarg); - break; - case 'b': - cbdata.daemonize = true; - break; - case '?': - die("try %s --help", get_progname()); - default: - abort(); - } - } - - argc -= optind; - argv += optind; - - if (mode->id != MODE_TIME && (cbdata.tv.tv_sec || cbdata.tv.tv_usec)) - die("can't specify wait time in this mode"); - - if (mode->id != MODE_SIGNAL && - mode->id != MODE_TIME && - cbdata.daemonize) - die("can't daemonize in this mode"); - - if (argc < 1) - die("gpiochip must be specified"); - - if (argc < 2) - die("at least one GPIO line offset to value mapping must be specified"); - - device = argv[0]; - - num_lines = argc - 1; - - offsets = calloc(num_lines, sizeof(*offsets)); - values = calloc(num_lines, sizeof(*values)); - if (!offsets) - die("out of memory"); - - for (i = 0; i < num_lines; i++) { - ret = sscanf(argv[i + 1], "%u=%d", &offsets[i], &values[i]); - if (ret != 2) - die("invalid offset<->value mapping: %s", argv[i + 1]); - - if (values[i] != 0 && values[i] != 1) - die("value must be 0 or 1: %s", argv[i + 1]); - - if (offsets[i] > INT_MAX) - die("invalid offset: %s", argv[i + 1]); - } - - if (has_duplicate_offsets(num_lines, offsets)) - die("offsets must be unique"); - - chip = chip_open_lookup(device); - if (!chip) - die_perror("unable to open %s", device); - - settings = gpiod_line_settings_new(); - if (!settings) - die_perror("unable to allocate line settings"); - - if (bias) - gpiod_line_settings_set_bias(settings, bias); - if (drive) - gpiod_line_settings_set_drive(settings, drive); - if (active_low) - gpiod_line_settings_set_active_low(settings, active_low); - gpiod_line_settings_set_direction(settings, - GPIOD_LINE_DIRECTION_OUTPUT); - - req_cfg = gpiod_request_config_new(); - if (!req_cfg) - die_perror("unable to allocate the request config structure"); - - gpiod_request_config_set_consumer(req_cfg, "gpioset"); - - line_cfg = gpiod_line_config_new(); - if (!line_cfg) - die_perror("unable to allocate the line config structure"); - - for (i = 0; i < num_lines; i++) { - gpiod_line_settings_set_output_value(settings, values[i]); - - ret = gpiod_line_config_add_line_settings(line_cfg, &offsets[i], - 1, settings); - if (ret) - die_perror("unable to add line settings"); - } - - request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); - if (!request) - die_perror("unable to request lines"); - - if (mode->callback) - mode->callback(&cbdata); - - gpiod_line_request_release(request); - gpiod_request_config_free(req_cfg); - gpiod_line_config_free(line_cfg); - gpiod_line_settings_free(settings); - gpiod_chip_close(chip); - free(offsets); - - return EXIT_SUCCESS; -} diff --git a/tools/tools-common.c b/tools/tools-common.c index 8957293..ea7dfa8 100644 --- a/tools/tools-common.c +++ b/tools/tools-common.c @@ -79,26 +79,6 @@ void print_bias_help(void) printf(" pull-down:\tenable pull-down\n"); } -int make_signalfd(void) -{ - sigset_t sigmask; - int sigfd, rv; - - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGTERM); - sigaddset(&sigmask, SIGINT); - - rv = sigprocmask(SIG_BLOCK, &sigmask, NULL); - if (rv < 0) - die("error masking signals: %s", strerror(errno)); - - sigfd = signalfd(-1, &sigmask, 0); - if (sigfd < 0) - die("error creating signalfd: %s", strerror(errno)); - - return sigfd; -} - int chip_dir_filter(const struct dirent *entry) { bool is_chip; @@ -114,38 +94,6 @@ int chip_dir_filter(const struct dirent *entry) return !!is_chip; } -struct gpiod_chip *chip_open_by_name(const char *name) -{ - struct gpiod_chip *chip; - char *path; - int ret; - - ret = asprintf(&path, "/dev/%s", name); - if (ret < 0) - return NULL; - - chip = gpiod_chip_open(path); - free(path); - - return chip; -} - -static struct gpiod_chip *chip_open_by_number(unsigned int num) -{ - struct gpiod_chip *chip; - char *path; - int ret; - - ret = asprintf(&path, "/dev/gpiochip%u", num); - if (!ret) - return NULL; - - chip = gpiod_chip_open(path); - free(path); - - return chip; -} - static bool isuint(const char *str) { for (; *str && isdigit(*str); str++) @@ -153,32 +101,3 @@ static bool isuint(const char *str) return *str == '\0'; } - -struct gpiod_chip *chip_open_lookup(const char *device) -{ - struct gpiod_chip *chip; - - if (isuint(device)) { - chip = chip_open_by_number(strtoul(device, NULL, 10)); - } else { - if (strncmp(device, "/dev/", 5)) - chip = chip_open_by_name(device); - else - chip = gpiod_chip_open(device); - } - - return chip; -} - -bool has_duplicate_offsets(size_t num_offsets, unsigned int *offsets) -{ - size_t i, j; - - for (i = 0; i < num_offsets; i++) { - for (j = i + 1; j < num_offsets; j++) - if (offsets[i] == offsets[j]) - return true; - } - - return false; -} From patchwork Mon Nov 21 10:22:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kent Gibson X-Patchwork-Id: 627397 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0695AC4332F for ; Mon, 21 Nov 2022 10:23:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231352AbiKUKXz (ORCPT ); Mon, 21 Nov 2022 05:23:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231428AbiKUKXp (ORCPT ); Mon, 21 Nov 2022 05:23:45 -0500 Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 73F55193E2 for ; Mon, 21 Nov 2022 02:23:41 -0800 (PST) Received: by mail-pl1-x62d.google.com with SMTP id y10so8985921plp.3 for ; Mon, 21 Nov 2022 02:23:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kB+bW7HuX79OCa3zqn7XGt+zZL4OBUVIYMdY6m4i73M=; b=O/4w+kqorTyv6/rZ4X1oHG8LfUEZA/qTLTJzU64PHnNXBWhuF1p7O6bsTxhakUy1jc LL/dH7mj5tgfyuW1yO9OxjDoMsNWrl+bOsB9n0L7P/5jW8B/Ws8JhG3xVbpD452NmPKP frk2mB1IhYsB2lIegvxIBc1dMSFDGMfbcQDb3HJwnifRypkP5RGbl2kJPdMx1qWUbskw GxWSSbVP72cXZZl99RtoIOmw7BTfqHaZVu1UsSCnzQrS4rqsytjT/ypfXJPF7lh16HcO VG6ycdC6kz1RcB0tFEEGl2dLThTa2RQ1V0v0lpnsp242in0vG9bSjguQUG3y27yhPG9R BCxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kB+bW7HuX79OCa3zqn7XGt+zZL4OBUVIYMdY6m4i73M=; b=jSnBXguwHLGRsggm2DkhhcoPxBpvojVu4p7HAnLPNAZfdRvuuWVk272/ErDVEFj+mZ eKt2uUmu5zEW+8nzS9yH5TnndhR7mERWoUD0cGvospRrZl/ORmCcaar8pUFtyeRq6E5/ FCFn91gVB9mNIBm8DqID5IexcMIGvT+voc3BG6YHoQxuPYDluBueveHAjs5Js2khvsGx LQa53Y2gz8/NRZFFrwkaJzzsCtghkqdbU3eyadNYNkqhVEwFuudiXGe/UNy2TG3/okig NRj2VKJnw4FrJy++mjI7K39GmKUT3DIyDIL/TXXazujBJxUULJUsqXaRMmCdyQziBglE wLlg== X-Gm-Message-State: ANoB5pmjfw9dtEKv98oaMuxFvZp3LXvyKiQLeMOnRvv0pCFaEXrMHfre Spxu0C7ctBbAYxCb5vN6P/F9gd8QQDw= X-Google-Smtp-Source: AA0mqf7QyHaQO0JfPF8SBqlah/b8flvhhx2Icg2J2CQfiFakGuAeQ5EEkplLXItrj7Kb6OV3PE4PYg== X-Received: by 2002:a17:903:44e:b0:176:a9d6:ed53 with SMTP id iw14-20020a170903044e00b00176a9d6ed53mr2218050plb.5.1669026219840; Mon, 21 Nov 2022 02:23:39 -0800 (PST) Received: from sol.home.arpa (14-200-229-209.tpgi.com.au. [14.200.229.209]) by smtp.gmail.com with ESMTPSA id s184-20020a625ec1000000b0056ba7ce4d5asm8347419pfb.52.2022.11.21.02.23.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Nov 2022 02:23:39 -0800 (PST) From: Kent Gibson To: linux-gpio@vger.kernel.org, brgl@bgdev.pl Cc: Kent Gibson Subject: [libgpiod v2][PATCH v5 3/6] tools: tests for line name focussed rework Date: Mon, 21 Nov 2022 18:22:50 +0800 Message-Id: <20221121102253.38306-4-warthog618@gmail.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221121102253.38306-1-warthog618@gmail.com> References: <20221121102253.38306-1-warthog618@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Rework the tools tests and expand to cover new functionality. Signed-off-by: Kent Gibson --- tools/gpio-tools-test | 2 - tools/gpio-tools-test.bats | 2367 ++++++++++++++++++++++++++++++++++-- 2 files changed, 2282 insertions(+), 87 deletions(-) diff --git a/tools/gpio-tools-test b/tools/gpio-tools-test index 234f9bd..56d7f7e 100755 --- a/tools/gpio-tools-test +++ b/tools/gpio-tools-test @@ -37,8 +37,6 @@ check_prog() { # Check all required non-coreutils tools check_prog bats check_prog modprobe -check_prog rmmod -check_prog udevadm check_prog timeout # Check if we're running a kernel at the required version or later diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats index d200df1..88de9bf 100755 --- a/tools/gpio-tools-test.bats +++ b/tools/gpio-tools-test.bats @@ -1,18 +1,24 @@ #!/usr/bin/env bats # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski +# SPDX-FileCopyrightText: 2022 Kent Gibson # Simple test harness for the gpio-tools. -# Where output from coprocesses is stored -COPROC_OUTPUT=$BATS_TMPDIR/gpio-tools-test-output +# Where output from the dut is stored +DUT_OUTPUT=$BATS_TMPDIR/gpio-tools-test-output # Save the PID of coprocess - otherwise we won't be able to wait for it # once it exits as the COPROC_PID will be cleared. -COPROC_SAVED_PID="" +DUT_PID="" -GPIOSIM_CHIPS="" -GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim/" +# mappings from local name to system chip name, path, dev name +# -g required for the associative arrays, cos BATS... +declare -g -A GPIOSIM_CHIP_NAME +declare -g -A GPIOSIM_CHIP_PATH +declare -g -A GPIOSIM_DEV_NAME +GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim" GPIOSIM_SYSFS="/sys/devices/platform/" +GPIOSIM_APP_NAME="gpio-tools-test" # Run the command in $* and return 0 if the command failed. The way we do it # here is a workaround for the way bats handles failing processes. @@ -23,10 +29,7 @@ assert_fail() { # Check if the string in $2 matches against the pattern in $1. regex_matches() { - local PATTERN=$1 - local STRING=$2 - - [[ $STRING =~ $PATTERN ]] + [[ $2 =~ $1 ]] || (echo "Mismatched: \"$2\"" && false) } # Iterate over all lines in the output of the last command invoked with bats' @@ -38,31 +41,39 @@ output_contains_line() { do test "$line" = "$LINE" && return 0 done - + echo "Mismatched:" + echo "$output" return 1 } +output_is() { + test "$output" = "$1" || (echo "Mismatched: \"$output\"" && false) +} + +num_lines_is() { + test ${#lines[@]} -eq $1 || (echo "Num lines is: ${#lines[@]}" && false) +} + +status_is() { + test "$status" -eq "$1" +} + # Same as above but match against the regex pattern in $1. output_regex_match() { - local PATTERN=$1 - for line in "${lines[@]}" do - regex_matches "$PATTERN" "$line" && return 0 + [[ "$line" =~ $1 ]] && return 0 done - + echo "Mismatched:" + echo "$output" return 1 } -random_name() { - cat /proc/sys/kernel/random/uuid -} - gpiosim_chip() { local VAR=$1 - local NAME=$(random_name) + local NAME=${GPIOSIM_APP_NAME}-$$-${VAR} local DEVPATH=$GPIOSIM_CONFIGFS/$NAME - local BANKPATH=$DEVPATH/$NAME + local BANKPATH=$DEVPATH/bank0 mkdir -p $BANKPATH @@ -87,75 +98,60 @@ gpiosim_chip() { echo 1 > $DEVPATH/live - GPIOSIM_CHIPS="$VAR:$NAME $GPIOSIM_CHIPS" + local chip_name=$(<$BANKPATH/chip_name) + GPIOSIM_CHIP_NAME[$1]=$chip_name + GPIOSIM_CHIP_PATH[$1]="/dev/$chip_name" + GPIOSIM_DEV_NAME[$1]=$(<$DEVPATH/dev_name) } -gpiosim_chip_map_name() { - local VAR=$1 - - for CHIP in $GPIOSIM_CHIPS - do - KEY=$(echo $CHIP | cut -d":" -f1) - VAL=$(echo $CHIP | cut -d":" -f2) - - if [ "$KEY" = "$VAR" ] - then - echo $VAL - fi - done +gpiosim_chip_number() { + local NAME=${GPIOSIM_CHIP_NAME[$1]} + echo ${NAME#"gpiochip"} } -gpiosim_chip_name() { - local VAR=$1 - local NAME=$(gpiosim_chip_map_name $VAR) - - cat $GPIOSIM_CONFIGFS/$NAME/$NAME/chip_name +gpiosim_chip_symlink() { + GPIOSIM_CHIP_LINK="$2/${GPIOSIM_APP_NAME}-$$-lnk" + ln -s ${GPIOSIM_CHIP_PATH[$1]} "$GPIOSIM_CHIP_LINK" } -gpiosim_dev_name() { - local VAR=$1 - local NAME=$(gpiosim_chip_map_name $VAR) - - cat $GPIOSIM_CONFIGFS/$NAME/dev_name +gpiosim_chip_symlink_cleanup() { + if [ -n "$GPIOSIM_CHIP_LINK" ] + then + rm "$GPIOSIM_CHIP_LINK" + fi + unset GPIOSIM_CHIP_LINK } gpiosim_set_pull() { - local VAR=$1 local OFFSET=$2 local PULL=$3 - local DEVNAME=$(gpiosim_dev_name $VAR) - local CHIPNAME=$(gpiosim_chip_name $VAR) + local DEVNAME=${GPIOSIM_DEV_NAME[$1]} + local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} echo $PULL > $GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull } gpiosim_check_value() { - local VAR=$1 local OFFSET=$2 local EXPECTED=$3 - local DEVNAME=$(gpiosim_dev_name $VAR) - local CHIPNAME=$(gpiosim_chip_name $VAR) - - VAL=$(cat $GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value) - if [ "$VAL" = "$EXPECTED" ] - then - return 0 - fi + local DEVNAME=${GPIOSIM_DEV_NAME[$1]} + local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} - return 1 + VAL=$(<$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value) + [ "$VAL" = "$EXPECTED" ] } gpiosim_cleanup() { - for CHIP in $GPIOSIM_CHIPS + for CHIP in ${!GPIOSIM_CHIP_NAME[@]} do - local NAME=$(echo $CHIP | cut -d":" -f2) + local NAME=${GPIOSIM_APP_NAME}-$$-$CHIP local DEVPATH=$GPIOSIM_CONFIGFS/$NAME - local BANKPATH=$DEVPATH/$NAME + local BANKPATH=$DEVPATH/bank0 echo 0 > $DEVPATH/live - ls $BANKPATH/line* 2> /dev/null + ls $BANKPATH/line* > /dev/null 2>&1 if [ "$?" = "0" ] then for LINE in $(find $BANKPATH/ | egrep "line[0-9]+$") @@ -169,7 +165,11 @@ gpiosim_cleanup() { rmdir $DEVPATH done - GPIOSIM_CHIPS="" + gpiosim_chip_symlink_cleanup + + GPIOSIM_CHIP_NAME=() + GPIOSIM_CHIP_PATH=() + GPIOSIM_DEV_NAME=() } run_tool() { @@ -178,45 +178,2242 @@ run_tool() { run timeout 10s $BATS_TEST_DIRNAME/"$@" } -coproc_run_tool() { - rm -f $BR_PROC_OUTPUT - coproc timeout 10s $BATS_TEST_DIRNAME/"$@" > $COPROC_OUTPUT 2> $COPROC_OUTPUT - COPROC_SAVED_PID=$COPROC_PID - # FIXME We're giving the background process some time to get up, but really this - # should be more reliable... +dut_run() { + coproc timeout 10s $BATS_TEST_DIRNAME/"$@" 2>&1 + DUT_PID=$COPROC_PID + read -t1 -n1 -u ${COPROC[0]} DUT_FIRST_CHAR +} + +dut_run_redirect() { + coproc timeout 10s $BATS_TEST_DIRNAME/"$@" > $DUT_OUTPUT 2>&1 + DUT_PID=$COPROC_PID + # give the process time to spin up + # FIXME - find a better solution sleep 0.2 } -coproc_tool_stdin_write() { +dut_read_redirect() { + output=$(<$DUT_OUTPUT) + local ORIG_IFS="$IFS" + IFS=$'\n' lines=($output) + IFS="$ORIG_IFS" +} + +dut_read() { + local LINE + lines=() + while read -t 0.2 -u ${COPROC[0]} LINE; + do + if [ -n "$DUT_FIRST_CHAR" ] + then + LINE=${DUT_FIRST_CHAR}${LINE} + unset DUT_FIRST_CHAR + fi + lines+=("$LINE") + done + output="${lines[@]}" +} + +dut_readable() { + read -t 0 -u ${COPROC[0]} LINE +} + +dut_flush() { + local JUNK + lines=() + output= + unset DUT_FIRST_CHAR + while read -t 0 -u ${COPROC[0]} JUNK; + do + read -t 0.1 -u ${COPROC[0]} JUNK || true + done +} + +# check the next line of output matches the regex +dut_regex_match() { + PATTERN=$1 + + read -t 0.2 -u ${COPROC[0]} LINE || (echo Timeout && false) + if [ -n "$DUT_FIRST_CHAR" ] + then + LINE=${DUT_FIRST_CHAR}${LINE} + unset DUT_FIRST_CHAR + fi + [[ $LINE =~ $PATTERN ]] || (echo "Mismatched: \"$LINE\"" && false) +} + +dut_write() { echo $* >&${COPROC[1]} } -coproc_tool_kill() { +dut_kill() { SIGNUM=$1 - kill $SIGNUM $COPROC_SAVED_PID + kill $SIGNUM $DUT_PID } -coproc_tool_wait() { +dut_wait() { status="0" # A workaround for the way bats handles command failures. - wait $COPROC_SAVED_PID || export status=$? + wait $DUT_PID || export status=$? test "$status" -ne 0 || export status="0" - output=$(cat $COPROC_OUTPUT) - local ORIG_IFS="$IFS" - IFS=$'\n' lines=($output) - IFS="$ORIG_IFS" - rm -f $COPROC_OUTPUT + unset DUT_PID } -teardown() { - if [ -n "$BG_PROC_PID" ] - then - kill -9 $BG_PROC_PID - run wait $BG_PROC_PID - BG_PROC_PID="" - fi +dut_cleanup() { + if [ -n "$DUT_PID" ] + then + kill -SIGTERM $DUT_PID + wait $DUT_PID || false + fi + rm -f $DUT_OUTPUT +} +teardown() { + dut_cleanup gpiosim_cleanup } +request_release_line() { + $BATS_TEST_DIRNAME/gpioget -c $* >/dev/null +} + +# +# gpiodetect test cases +# + +@test "gpiodetect: all chips" { + gpiosim_chip sim0 num_lines=4 + gpiosim_chip sim1 num_lines=8 + gpiosim_chip sim2 num_lines=16 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + local sim2=${GPIOSIM_CHIP_NAME[sim2]} + local sim0dev=${GPIOSIM_DEV_NAME[sim0]} + local sim1dev=${GPIOSIM_DEV_NAME[sim1]} + local sim2dev=${GPIOSIM_DEV_NAME[sim2]} + + run_tool gpiodetect + + output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)" + output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)" + output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)" + status_is 0 + + # ignoring symlinks + local initial_output=$output + gpiosim_chip_symlink sim1 /dev + + run_tool gpiodetect + + output_is "$initial_output" + status_is 0 +} + +@test "gpiodetect: a chip" { + gpiosim_chip sim0 num_lines=4 + gpiosim_chip sim1 num_lines=8 + gpiosim_chip sim2 num_lines=16 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + local sim2=${GPIOSIM_CHIP_NAME[sim2]} + local sim0dev=${GPIOSIM_DEV_NAME[sim0]} + local sim1dev=${GPIOSIM_DEV_NAME[sim1]} + local sim2dev=${GPIOSIM_DEV_NAME[sim2]} + + # by name + run_tool gpiodetect $sim0 + + output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)" + num_lines_is 1 + status_is 0 + + # by path + run_tool gpiodetect ${GPIOSIM_CHIP_PATH[sim1]} + + output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)" + num_lines_is 1 + status_is 0 + + # by number + run_tool gpiodetect $(gpiosim_chip_number sim2) + + output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)" + num_lines_is 1 + status_is 0 + + # by symlink + gpiosim_chip_symlink sim2 . + run_tool gpiodetect $GPIOSIM_CHIP_LINK + + output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)" + num_lines_is 1 + status_is 0 +} + +@test "gpiodetect: multiple chips" { + gpiosim_chip sim0 num_lines=4 + gpiosim_chip sim1 num_lines=8 + gpiosim_chip sim2 num_lines=16 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + local sim2=${GPIOSIM_CHIP_NAME[sim2]} + local sim0dev=${GPIOSIM_DEV_NAME[sim0]} + local sim1dev=${GPIOSIM_DEV_NAME[sim1]} + local sim2dev=${GPIOSIM_DEV_NAME[sim2]} + + run_tool gpiodetect $sim0 $sim1 $sim2 + + output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)" + output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)" + output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)" + num_lines_is 3 + status_is 0 +} + +@test "gpiodetect: with nonexistent chip" { + run_tool gpiodetect nonexistent-chip + + status_is 1 + output_regex_match \ +".*cannot find GPIO chip character device 'nonexistent-chip'" +} + +# +# gpioinfo test cases +# + +@test "gpioinfo: all chips" { + gpiosim_chip sim0 num_lines=4 + gpiosim_chip sim1 num_lines=8 + + run_tool gpioinfo + + echo "$output" + output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:" + output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+input" + status_is 0 + + # ignoring symlinks + local initial_output=$output + gpiosim_chip_symlink sim1 /dev + + run_tool gpioinfo + + output_is "$initial_output" + status_is 0 +} + +@test "gpioinfo: all chips with some used lines" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz + + dut_run gpioset --banner --active-low foo=1 baz=0 + + run_tool gpioinfo + + output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:" + output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match \ +"\\s+line\\s+1:\\s+\"foo\"\\s+output active-low consumer=\"gpioset\"" + output_regex_match \ +"\\s+line\\s+3:\\s+\"baz\"\\s+output active-low consumer=\"gpioset\"" + status_is 0 +} + +@test "gpioinfo: a chip" { + gpiosim_chip sim0 num_lines=8 + gpiosim_chip sim1 num_lines=4 + + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + # by name + run_tool gpioinfo --chip $sim1 + + output_contains_line "$sim1 - 4 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input" + num_lines_is 5 + status_is 0 + + # by path + run_tool gpioinfo --chip $sim1 + + output_contains_line "$sim1 - 4 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input" + num_lines_is 5 + status_is 0 + + # by number + run_tool gpioinfo --chip $sim1 + + output_contains_line "$sim1 - 4 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input" + num_lines_is 5 + status_is 0 + + # by symlink + gpiosim_chip_symlink sim1 . + run_tool gpioinfo --chip $GPIOSIM_CHIP_LINK + + output_contains_line "$sim1 - 4 lines:" + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input" + output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input" + num_lines_is 5 + status_is 0 +} + +@test "gpioinfo: a line" { + gpiosim_chip sim0 num_lines=8 line_name=5:bar + gpiosim_chip sim1 num_lines=4 line_name=2:bar + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + # by offset + run_tool gpioinfo --chip $sim1 2 + + output_regex_match "$sim1 2\\s+\"bar\"\\s+input" + num_lines_is 1 + status_is 0 + + # by name + run_tool gpioinfo bar + + output_regex_match "$sim0 5\\s+\"bar\"\\s+input" + num_lines_is 1 + status_is 0 + + # by chip and name + run_tool gpioinfo --chip $sim1 2 + + output_regex_match "$sim1 2\\s+\"bar\"\\s+input" + num_lines_is 1 + status_is 0 + + # unquoted + run_tool gpioinfo --unquoted --chip $sim1 2 + + output_regex_match "$sim1 2\\s+bar\\s+input" + num_lines_is 1 + status_is 0 + +} + +@test "gpioinfo: first matching named line" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioinfo foobar + + output_regex_match "$sim0 3\\s+\"foobar\"\\s+input" + num_lines_is 1 + status_is 0 +} + +@test "gpioinfo: multiple lines" { + gpiosim_chip sim0 num_lines=8 line_name=5:bar + gpiosim_chip sim1 num_lines=4 line_name=2:baz + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + # by offset + run_tool gpioinfo --chip $sim1 1 2 + + output_regex_match "$sim1 1\\s+unnamed\\s+input" + output_regex_match "$sim1 2\\s+\"baz\"\\s+input" + num_lines_is 2 + status_is 0 + + # by name + run_tool gpioinfo bar baz + + output_regex_match "$sim0 5\\s+\"bar\"\\s+input" + output_regex_match "$sim1 2\\s+\"baz\"\\s+input" + num_lines_is 2 + status_is 0 + + # by name and offset + run_tool gpioinfo --chip $sim0 bar 3 + + output_regex_match "$sim0 5\\s+\"bar\"\\s+input" + output_regex_match "$sim0 3\\s+unnamed\\s+input" + num_lines_is 2 + status_is 0 +} + +@test "gpioinfo: line attribute menagerie" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo + gpiosim_chip sim1 num_lines=8 line_name=3:baz + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + dut_run gpioset --banner --active-low --bias=pull-up --drive=open-source foo=1 baz=0 + + run_tool gpioinfo foo baz + + output_regex_match \ +"$sim0 1\\s+\"foo\"\\s+output active-low drive=open-source bias=pull-up consumer=\"gpioset\"" + output_regex_match \ +"$sim1 3\\s+\"baz\"\\s+output active-low drive=open-source bias=pull-up consumer=\"gpioset\"" + num_lines_is 2 + status_is 0 + + dut_kill + dut_wait + + dut_run gpioset --banner --bias=pull-down --drive=open-drain foo=1 baz=0 + + run_tool gpioinfo foo baz + + output_regex_match \ +"$sim0 1\\s+\"foo\"\\s+output drive=open-drain bias=pull-down consumer=\"gpioset\"" + output_regex_match \ +"$sim1 3\\s+\"baz\"\\s+output drive=open-drain bias=pull-down consumer=\"gpioset\"" + num_lines_is 2 + status_is 0 + + dut_kill + dut_wait + + dut_run gpiomon --banner --bias=disabled --utc -p 10ms foo baz + + run_tool gpioinfo foo baz + + output_regex_match \ +"$sim0 1\\s+\"foo\"\\s+input bias=disabled edges=both event-clock=realtime debounce-period=10ms consumer=\"gpiomon\"" + output_regex_match \ +"$sim1 3\\s+\"baz\"\\s+input bias=disabled edges=both event-clock=realtime debounce-period=10ms consumer=\"gpiomon\"" + num_lines_is 2 + status_is 0 + + dut_kill + dut_wait + + dut_run gpiomon --banner --edges=rising --localtime foo baz + + run_tool gpioinfo foo baz + + output_regex_match \ +"$sim0 1\\s+\"foo\"\\s+input edges=rising event-clock=realtime consumer=\"gpiomon\"" + output_regex_match \ +"$sim1 3\\s+\"baz\"\\s+input edges=rising event-clock=realtime consumer=\"gpiomon\"" + num_lines_is 2 + status_is 0 + + dut_kill + dut_wait + + dut_run gpiomon --banner --edges=falling foo baz + + run_tool gpioinfo foo baz + + output_regex_match \ +"$sim0 1\\s+\"foo\"\\s+input edges=falling consumer=\"gpiomon\"" + output_regex_match \ +"$sim1 3\\s+\"baz\"\\s+input edges=falling consumer=\"gpiomon\"" + num_lines_is 2 + status_is 0 +} + +@test "gpioinfo: with same line twice" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by offset + run_tool gpioinfo --chip $sim0 1 1 + + output_regex_match "$sim0 1\\s+\"foo\"\\s+input" + output_regex_match ".*lines '1' and '1' are the same line" + num_lines_is 2 + status_is 1 + + # by name + run_tool gpioinfo foo foo + + output_regex_match "$sim0 1\\s+\"foo\"\\s+input" + output_regex_match ".*lines 'foo' and 'foo' are the same line" + num_lines_is 2 + status_is 1 + + # by name and offset + run_tool gpioinfo --chip $sim0 foo 1 + + output_regex_match "$sim0 1\\s+\"foo\"\\s+input" + output_regex_match ".*lines 'foo' and '1' are the same line" + num_lines_is 2 + status_is 1 +} + +@test "gpioinfo: all lines matching name" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + run_tool gpioinfo --strict foobar + + output_regex_match "$sim0 3\\s+\"foobar\"\\s+input" + output_regex_match "$sim1 2\\s+\"foobar\"\\s+input" + output_regex_match "$sim1 7\\s+\"foobar\"\\s+input" + num_lines_is 3 + status_is 1 +} + +@test "gpioinfo: with lines strictly by name" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # first by offset (to show offsets match first) + run_tool gpioinfo --chip $sim0 1 6 + + output_regex_match "$sim0 1\\s+\"6\"\\s+input" + output_regex_match "$sim0 6\\s+\"1\"\\s+input" + num_lines_is 2 + status_is 0 + + # then strictly by name + run_tool gpioinfo --by-name --chip $sim0 1 + + output_regex_match "$sim0 6\\s+\"1\"\\s+input" + num_lines_is 1 + status_is 0 +} + +@test "gpioinfo: with nonexistent chip" { + run_tool gpioinfo --chip nonexistent-chip + + output_regex_match \ +".*cannot find GPIO chip character device 'nonexistent-chip'" + status_is 1 +} + +@test "gpioinfo: with nonexistent line" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioinfo nonexistent-line + + output_regex_match ".*cannot find line 'nonexistent-line'" + status_is 1 + + run_tool gpioinfo --chip ${GPIOSIM_CHIP_NAME[sim0]} nonexistent-line + + output_regex_match ".*cannot find line 'nonexistent-line'" + status_is 1 +} + +@test "gpioinfo: with offset out of range" { + gpiosim_chip sim0 num_lines=4 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioinfo --chip $sim0 0 1 2 3 4 5 + + output_regex_match "$sim0 0\\s+unnamed\\s+input" + output_regex_match "$sim0 1\\s+unnamed\\s+input" + output_regex_match "$sim0 2\\s+unnamed\\s+input" + output_regex_match "$sim0 3\\s+unnamed\\s+input" + output_regex_match ".*offset 4 is out of range on chip '$sim0'" + output_regex_match ".*offset 5 is out of range on chip '$sim0'" + num_lines_is 6 + status_is 1 +} + +# +# gpioget test cases +# + +@test "gpioget: by name" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + gpiosim_set_pull sim0 1 pull-up + + run_tool gpioget foo + + output_is "\"foo\"=active" + status_is 0 + + run_tool gpioget --unquoted foo + + output_is "foo=active" + status_is 0 +} + +@test "gpioget: by offset" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 1 pull-up + + run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 + + output_is "\"1\"=active" + status_is 0 + + run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 + + output_is "1=active" + status_is 0 +} + +@test "gpioget: by symlink" { + gpiosim_chip sim0 num_lines=8 + gpiosim_chip_symlink sim0 . + + gpiosim_set_pull sim0 1 pull-up + + run_tool gpioget --chip $GPIOSIM_CHIP_LINK 1 + + output_is "\"1\"=active" + status_is 0 +} + +@test "gpioget: by chip and name" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + gpiosim_chip sim1 num_lines=8 line_name=3:foo + + gpiosim_set_pull sim1 3 pull-up + + run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim1]} foo + + output_is "\"foo\"=active" + status_is 0 + + run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim1]} foo + + output_is "foo=active" + status_is 0 +} + +@test "gpioget: first matching named line" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar line_name=7:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz + gpiosim_chip sim2 num_lines=16 + + gpiosim_set_pull sim0 3 pull-up + + run_tool gpioget foobar + + output_is "\"foobar\"=active" + status_is 0 +} + +@test "gpioget: multiple lines" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 0 1 2 3 4 5 6 7 + + output_is \ +"0=inactive 1=inactive 2=active 3=active 4=inactive 5=active 6=inactive 7=active" + status_is 0 +} + +@test "gpioget: multiple lines by name and offset" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=6:bar + gpiosim_chip sim1 num_lines=8 line_name=1:baz line_name=3:bar + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 1 pull-up + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 6 pull-up + + run_tool gpioget --chip $sim0 0 foo 4 bar + + output_is "\"0\"=inactive \"foo\"=active \"4\"=active \"bar\"=active" + status_is 0 +} + +@test "gpioget: multiple lines across multiple chips" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz + + gpiosim_set_pull sim0 1 pull-up + gpiosim_set_pull sim1 4 pull-up + + run_tool gpioget baz bar foo xyz + + output_is "\"baz\"=inactive \"bar\"=inactive \"foo\"=active \"xyz\"=active" + status_is 0 +} + +@test "gpioget: with numeric values" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioget --numeric --chip $sim0 0 1 2 3 4 5 6 7 + + output_is "0 0 1 1 0 1 0 1" + status_is 0 +} + +@test "gpioget: with active-low" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioget --active-low --unquoted --chip $sim0 0 1 2 3 4 5 6 7 + + output_is \ +"0=active 1=active 2=inactive 3=inactive 4=active 5=inactive 6=active 7=inactive" + status_is 0 +} + +@test "gpioget: with pull-up" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioget --bias=pull-up --unquoted --chip $sim0 0 1 2 3 4 5 6 7 + + output_is \ +"0=active 1=active 2=active 3=active 4=active 5=active 6=active 7=active" + status_is 0 +} + +@test "gpioget: with pull-down" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioget --bias=pull-down --unquoted --chip $sim0 0 1 2 3 4 5 6 7 + + output_is \ +"0=inactive 1=inactive 2=inactive 3=inactive 4=inactive 5=inactive 6=inactive 7=inactive" + status_is 0 +} + +@test "gpioget: with direction as-is" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # flip to output + run_tool gpioset -t0 foo=1 + + status_is 0 + + run_tool gpioinfo foo + output_regex_match "$sim0 1\\s+\"foo\"\\s+output" + status_is 0 + + run_tool gpioget --as-is foo + # note gpio-sim reverts line to its pull when released + output_is "\"foo\"=inactive" + status_is 0 + + run_tool gpioinfo foo + output_regex_match "$sim0 1\\s+\"foo\"\\s+output" + status_is 0 + + # whereas the default behaviour forces to input + run_tool gpioget foo + # note gpio-sim reverts line to its pull when released + # (defaults to pull-down) + output_is "\"foo\"=inactive" + status_is 0 + + run_tool gpioinfo foo + output_regex_match "$sim0 1\\s+\"foo\"\\s+input" + status_is 0 +} + +@test "gpioget: with hold-period" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + # only test parsing - testing the hold-period itself is tricky + run_tool gpioget --hold-period=100ms foo + output_is "\"foo\"=inactive" + status_is 0 +} + +@test "gpioget: with strict named line check" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + run_tool gpioget --strict foobar + + output_regex_match ".*line 'foobar' is not unique" + status_is 1 +} + +@test "gpioget: with lines by offset" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + gpiosim_set_pull sim0 1 pull-up + gpiosim_set_pull sim0 6 pull-down + + run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6 + + output_is "\"1\"=active \"6\"=inactive" + status_is 0 + + run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6 + + output_is "1=active 6=inactive" + status_is 0 +} + +@test "gpioget: with lines strictly by name" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + gpiosim_set_pull sim0 1 pull-up + gpiosim_set_pull sim0 6 pull-down + + run_tool gpioget --by-name --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6 + + output_is "\"1\"=inactive \"6\"=active" + status_is 0 + + run_tool gpioget --by-name --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6 + + output_is "1=inactive 6=active" + status_is 0 +} + +@test "gpioget: with no arguments" { + run_tool gpioget + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpioget: with chip but no line specified" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]} + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpioget: with offset out of range" { + gpiosim_chip sim0 num_lines=4 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioget --chip $sim0 0 1 2 3 4 5 + + output_regex_match ".*offset 4 is out of range on chip '$sim0'" + output_regex_match ".*offset 5 is out of range on chip '$sim0'" + status_is 1 +} + +@test "gpioget: with nonexistent line" { + run_tool gpioget nonexistent-line + + output_regex_match ".*cannot find line 'nonexistent-line'" + status_is 1 +} + +@test "gpioget: with same line twice" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by offset + run_tool gpioget --chip $sim0 0 0 + + output_regex_match ".*lines '0' and '0' are the same line" + status_is 1 + + # by name + run_tool gpioget foo foo + + output_regex_match ".*lines 'foo' and 'foo' are the same line" + status_is 1 + + # by chip and name + run_tool gpioget --chip $sim0 foo foo + + output_regex_match ".*lines 'foo' and 'foo' are the same line" + status_is 1 + + # by name and offset + run_tool gpioget --chip $sim0 foo 1 + + output_regex_match ".*lines 'foo' and '1' are the same line" + status_is 1 + + # by offset and name + run_tool gpioget --chip $sim0 1 foo + + output_regex_match ".*lines '1' and 'foo' are the same line" + status_is 1 +} + +@test "gpioget: with invalid bias" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioget --bias=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0 1 + + output_regex_match ".*invalid bias.*" + status_is 1 +} + +@test "gpioget: with invalid hold-period" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioget --hold-period=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0 + + output_regex_match ".*invalid period.*" + status_is 1 +} + +# +# gpioset test cases +# + +@test "gpioset: by name" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + dut_run gpioset --banner foo=1 + + gpiosim_check_value sim0 1 1 +} + +@test "gpioset: by offset" { + gpiosim_chip sim0 num_lines=8 + + dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 1=1 + + gpiosim_check_value sim0 1 1 +} + +@test "gpioset: by symlink" { + gpiosim_chip sim0 num_lines=8 + gpiosim_chip_symlink sim0 . + + dut_run gpioset --banner --chip $GPIOSIM_CHIP_LINK 1=1 + + gpiosim_check_value sim0 1 1 +} + +@test "gpioset: by chip and name" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + gpiosim_chip sim1 num_lines=8 line_name=3:foo + + dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim1]} foo=1 + + gpiosim_check_value sim1 3 1 +} + +@test "gpioset: first matching named line" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + dut_run gpioset --banner foobar=1 + + gpiosim_check_value sim0 3 1 +} + +@test "gpioset: multiple lines" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --chip $sim0 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 1 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: multiple lines by name and offset" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + + dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 foo=1 bar=1 3=1 + + gpiosim_check_value sim0 0 1 + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 1 +} + + +@test "gpioset: multiple lines across multiple chips" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz + + dut_run gpioset --banner foo=1 bar=1 baz=1 xyz=1 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim1 0 1 + gpiosim_check_value sim1 4 1 +} + +@test "gpioset: with active-low" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --active-low -c $sim0 \ + 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 1 + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 2 0 + gpiosim_check_value sim0 3 0 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 5 0 + gpiosim_check_value sim0 6 1 + gpiosim_check_value sim0 7 0 +} + +@test "gpioset: with consumer" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz + + dut_run gpioset --banner --consumer gpio-tools-tests foo=1 baz=0 + + run_tool gpioinfo + + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match \ +"\\s+line\\s+1:\\s+\"foo\"\\s+output consumer=\"gpio-tools-tests\"" + output_regex_match \ +"\\s+line\\s+3:\\s+\"baz\"\\s+output consumer=\"gpio-tools-tests\"" + status_is 0 +} + +@test "gpioset: with push-pull" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --drive=push-pull --chip $sim0 \ + 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 1 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with open-drain" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + dut_run gpioset --banner --drive=open-drain --chip $sim0 \ + 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with open-source" { + gpiosim_chip sim0 num_lines=8 + + gpiosim_set_pull sim0 2 pull-up + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 7 pull-up + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --drive=open-source --chip $sim0 \ + 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 1 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with pull-up" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --bias=pull-up --drive=open-drain \ + --chip $sim0 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with pull-down" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --banner --bias=pull-down --drive=open-source \ + --chip $sim0 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with value variants" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 0 pull-up + gpiosim_set_pull sim0 1 pull-down + gpiosim_set_pull sim0 2 pull-down + gpiosim_set_pull sim0 3 pull-up + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 5 pull-up + gpiosim_set_pull sim0 6 pull-up + gpiosim_set_pull sim0 7 pull-down + + dut_run gpioset --banner --chip $sim0 0=0 1=1 2=active \ + 3=inactive 4=on 5=off 6=false 7=true + + gpiosim_check_value sim0 0 0 + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 3 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 5 0 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 +} + +@test "gpioset: with hold-period" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 5 pull-up + + dut_run gpioset --banner --hold-period=1200ms -t0 --chip $sim0 0=1 5=0 7=1 + + gpiosim_check_value sim0 0 1 + gpiosim_check_value sim0 5 0 + gpiosim_check_value sim0 7 1 + + dut_wait + + status_is 0 +} + +@test "gpioset: interactive exit" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpioset --interactive --chip $sim0 1=0 2=1 5=1 6=0 7=1 + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 2 1 + gpiosim_check_value sim0 5 1 + gpiosim_check_value sim0 6 0 + gpiosim_check_value sim0 7 1 + + dut_write "exit" + dut_wait + + status_is 0 +} + +@test "gpioset: interactive help" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + dut_run gpioset --interactive foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + dut_write "help" + + dut_read + output_regex_match "COMMANDS:.*" + output_regex_match ".*get \[line\]\.\.\..*" + output_regex_match ".*set \.\.\..*" + output_regex_match ".*toggle \[line\]\.\.\..*" + output_regex_match ".*sleep .*" +} + +@test "gpioset: interactive get" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + dut_run gpioset --interactive foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + dut_write "get" + + dut_read + output_regex_match "\"foo\"=active \"bar\"=inactive \"baz\"=inactive" + + dut_write "get bar" + + dut_read + output_regex_match "\"bar\"=inactive" +} + +@test "gpioset: interactive get unquoted" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + dut_run gpioset --interactive --unquoted foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + dut_write "get" + + dut_read + output_regex_match "foo=active bar=inactive baz=inactive" + + dut_write "get bar" + + dut_read + output_regex_match "bar=inactive" +} + +@test "gpioset: interactive set" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + dut_run gpioset --interactive foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + dut_write "set bar=active" + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 7 0 + dut_write "get" + dut_read + output_regex_match "\"foo\"=active \"bar\"=active \"baz\"=inactive" +} + +@test "gpioset: interactive toggle" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 7 pull-up + + dut_run gpioset -i foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + dut_write "toggle" + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 7 1 + dut_write "get" + dut_read + output_regex_match "\"foo\"=inactive\\s+\"bar\"=active\\s+\"baz\"=active\\s*" + + dut_write "toggle baz" + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 7 0 + dut_write "get" + dut_read + output_regex_match "\"foo\"=inactive\\s+\"bar\"=active\\s+\"baz\"=inactive\\s*" +} + +@test "gpioset: interactive sleep" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + dut_run gpioset --interactive foo=1 bar=0 baz=0 + + dut_write "sleep 500ms" + dut_flush + + assert_fail dut_readable + + sleep 1 + + # prompt, but not a full line... + dut_readable +} + +@test "gpioset: toggle (continuous)" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 7 pull-up + + dut_run gpioset --banner --toggle 1s foo=1 bar=0 baz=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 + + sleep 1 + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 7 1 + + sleep 1 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 0 +} + +@test "gpioset: toggle (terminated)" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + gpiosim_set_pull sim0 4 pull-up + + # hold-period to allow test to sample before gpioset exits + dut_run gpioset --banner --toggle 1s,0 -p 600ms foo=1 bar=0 baz=1 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 1 + + sleep 1 + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 4 1 + gpiosim_check_value sim0 7 0 + + dut_wait + + status_is 0 + + # using --toggle 0 to exit + # hold-period to allow test to sample before gpioset exits + dut_run gpioset --banner -t0 -p 600ms foo=1 bar=0 baz=1 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 4 0 + gpiosim_check_value sim0 7 1 + + dut_wait + + status_is 0 +} + +@test "gpioset: with invalid toggle period" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ + line_name=7:baz + + run_tool gpioset --toggle 1ns foo=1 bar=0 baz=0 + + output_regex_match ".*invalid period.*" + status_is 1 +} + +@test "gpioset: with strict named line check" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + run_tool gpioset --strict foobar=active + + output_regex_match ".*line 'foobar' is not unique" + status_is 1 +} + +@test "gpioset: with lines by offset" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + gpiosim_set_pull sim0 1 pull-down + gpiosim_set_pull sim0 6 pull-up + + dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 6=1 1=0 + + gpiosim_check_value sim0 1 0 + gpiosim_check_value sim0 6 1 +} + +@test "gpioset: with lines strictly by name" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + gpiosim_set_pull sim0 1 pull-down + gpiosim_set_pull sim0 6 pull-up + + dut_run gpioset --banner --by-name --chip ${GPIOSIM_CHIP_NAME[sim0]} 6=1 1=0 + + gpiosim_check_value sim0 1 1 + gpiosim_check_value sim0 6 0 +} + +@test "gpioset: interactive after SIGINT" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + dut_run gpioset -i foo=1 + + dut_kill -SIGINT + dut_wait + + status_is 130 +} + +@test "gpioset: interactive after SIGTERM" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + dut_run gpioset -i foo=1 + + dut_kill -SIGTERM + dut_wait + + status_is 143 +} + +@test "gpioset: with no arguments" { + run_tool gpioset + + status_is 1 + output_regex_match ".*at least one GPIO line value must be specified" +} + +@test "gpioset: with chip but no line specified" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioset --chip ${GPIOSIM_CHIP_NAME[sim0]} + + output_regex_match ".*at least one GPIO line value must be specified" + status_is 1 +} + +@test "gpioset: with offset out of range" { + gpiosim_chip sim0 num_lines=4 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioset --chip $sim0 0=1 1=1 2=1 3=1 4=1 5=1 + + output_regex_match ".*offset 4 is out of range on chip '$sim0'" + output_regex_match ".*offset 5 is out of range on chip '$sim0'" + status_is 1 +} + +@test "gpioset: with invalid hold-period" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioset --hold-period=bad --chip $sim0 0=1 + + output_regex_match ".*invalid period.*" + status_is 1 +} + +@test "gpioset: with invalid value" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by name + run_tool gpioset --chip $sim0 0=c + + output_regex_match ".*invalid line value.*" + status_is 1 + + # by value + run_tool gpioset --chip $sim0 0=3 + + output_regex_match ".*invalid line value.*" + status_is 1 +} + +@test "gpioset: with invalid offset" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioset --chip ${GPIOSIM_CHIP_NAME[sim0]} 4000000000=0 + + output_regex_match ".*cannot find line '4000000000'" + status_is 1 +} + +@test "gpioset: with invalid bias" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioset --bias=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 1=1 + + output_regex_match ".*invalid bias.*" + status_is 1 +} + +@test "gpioset: with invalid drive" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpioset --drive=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 1=1 + + output_regex_match ".*invalid drive.*" + status_is 1 +} + +@test "gpioset: with interactive and toggle" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpioset --interactive --toggle 1s --chip $sim0 0=1 + + output_regex_match ".*can't combine interactive with toggle" + status_is 1 +} + +@test "gpioset: with nonexistent line" { + run_tool gpioset nonexistent-line=0 + + output_regex_match ".*cannot find line 'nonexistent-line'" + status_is 1 +} + +@test "gpioset: with same line twice" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by offset + run_tool gpioset --chip $sim0 0=1 0=1 + + output_regex_match ".*lines '0' and '0' are the same line" + status_is 1 + + # by name + run_tool gpioset --chip $sim0 foo=1 foo=1 + + output_regex_match ".*lines 'foo' and 'foo' are the same line" + status_is 1 + + # by name and offset + run_tool gpioset --chip $sim0 foo=1 1=1 + + output_regex_match ".*lines 'foo' and '1' are the same line" + status_is 1 + + # by offset and name + run_tool gpioset --chip $sim0 1=1 foo=1 + + output_regex_match ".*lines '1' and 'foo' are the same line" + status_is 1 +} + +# +# gpiomon test cases +# + +@test "gpiomon: by name" { + gpiosim_chip sim0 num_lines=8 line_name=4:foo + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --edges=rising foo + dut_flush + + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+\"foo\"" + assert_fail dut_readable +} + +@test "gpiomon: by offset" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --edges=rising --chip $sim0 4 + dut_regex_match "Monitoring line .*" + + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" + assert_fail dut_readable +} + +@test "gpiomon: by symlink" { + gpiosim_chip sim0 num_lines=8 + gpiosim_chip_symlink sim0 . + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --edges=rising --chip $GPIOSIM_CHIP_LINK 4 + dut_regex_match "Monitoring line .*" + + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0\\s+4" + assert_fail dut_readable +} + + +@test "gpiomon: by chip and name" { + gpiosim_chip sim0 num_lines=8 line_name=0:foo + gpiosim_chip sim1 num_lines=8 line_name=2:foo + + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + gpiosim_set_pull sim1 0 pull-up + + dut_run gpiomon --banner --edges=rising --chip $sim1 foo + dut_regex_match "Monitoring line .*" + + gpiosim_set_pull sim1 2 pull-down + gpiosim_set_pull sim1 2 pull-up + gpiosim_set_pull sim1 2 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim1 2 \"foo\"" + assert_fail dut_readable +} + +@test "gpiomon: first matching named line" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + dut_run gpiomon --banner foobar + dut_regex_match "Monitoring line .*" + + gpiosim_set_pull sim0 3 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+\"foobar\"" + assert_fail dut_readable +} + +@test "gpiomon: rising edge" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --edges=rising --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" + assert_fail dut_readable +} + +@test "gpiomon: falling edge" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-down + + dut_run gpiomon --banner --edges=falling --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" + assert_fail dut_readable +} + +@test "gpiomon: both edges" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --edges=both --chip $sim0 4 + dut_regex_match "Monitoring line .*" + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" + + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" +} + +@test "gpiomon: with pull-up" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-down + + dut_run gpiomon --banner --bias=pull-up --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" + + assert_fail dut_readable +} + +@test "gpiomon: with pull-down" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --bias=pull-down --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" + + assert_fail dut_readable +} + +@test "gpiomon: with active-low" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --active-low --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" + + assert_fail dut_readable +} + +@test "gpiomon: with consumer" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz + + dut_run gpiomon --banner --consumer gpio-tools-tests foo baz + + run_tool gpioinfo + + output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" + output_regex_match \ +"\\s+line\\s+1:\\s+\"foo\"\\s+input edges=both consumer=\"gpio-tools-tests\"" + output_regex_match \ +"\\s+line\\s+3:\\s+\"baz\"\\s+input edges=both consumer=\"gpio-tools-tests\"" + status_is 0 +} + +@test "gpiomon: with quiet mode" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --edges=rising --quiet --chip $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + assert_fail dut_readable +} + +@test "gpiomon: with unquoted" { + gpiosim_chip sim0 num_lines=8 line_name=4:foo + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpiomon --banner --unquoted --edges=rising foo + dut_flush + + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+foo" + assert_fail dut_readable +} + +@test "gpiomon: with num-events" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # redirect, as gpiomon exits after 4 events + dut_run_redirect gpiomon --num-events=4 --chip $sim0 4 + + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + gpiosim_set_pull sim0 4 pull-up + gpiosim_set_pull sim0 4 pull-down + + dut_wait + status_is 0 + dut_read_redirect + + regex_matches "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" "${lines[0]}" + regex_matches "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" "${lines[1]}" + regex_matches "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" "${lines[2]}" + regex_matches "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" "${lines[3]}" + num_lines_is 4 +} + +@test "gpiomon: multiple lines" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --format=%o --chip $sim0 1 3 2 5 4 + dut_regex_match "Monitoring lines .*" + + gpiosim_set_pull sim0 2 pull-up + dut_regex_match "2" + gpiosim_set_pull sim0 3 pull-up + dut_regex_match "3" + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "4" + + assert_fail dut_readable +} + +@test "gpiomon: multiple lines by name and offset" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --format=%o --chip $sim0 foo bar 3 + dut_regex_match "Monitoring lines .*" + + gpiosim_set_pull sim0 2 pull-up + dut_regex_match "2" + gpiosim_set_pull sim0 3 pull-up + dut_regex_match "3" + gpiosim_set_pull sim0 1 pull-up + dut_regex_match "1" + + assert_fail dut_readable +} + +@test "gpiomon: multiple lines across multiple chips" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz + + dut_run gpiomon --banner --format=%l foo bar baz + dut_regex_match "Monitoring lines .*" + + gpiosim_set_pull sim0 2 pull-up + dut_regex_match "bar" + gpiosim_set_pull sim1 0 pull-up + dut_regex_match "baz" + gpiosim_set_pull sim0 1 pull-up + dut_regex_match "foo" + + assert_fail dut_readable +} + +@test "gpiomon: exit after SIGINT" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --chip $sim0 4 + dut_regex_match "Monitoring line .*" + + dut_kill -SIGINT + dut_wait + + status_is 130 +} + +@test "gpiomon: exit after SIGTERM" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner --chip $sim0 4 + dut_regex_match "Monitoring line .*" + + dut_kill -SIGTERM + dut_wait + + status_is 143 +} + +@test "gpiomon: with nonexistent line" { + run_tool gpiomon nonexistent-line + + status_is 1 + output_regex_match ".*cannot find line 'nonexistent-line'" +} + +@test "gpiomon: with same line twice" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by offset + run_tool gpiomon --chip $sim0 0 0 + + output_regex_match ".*lines '0' and '0' are the same line" + status_is 1 + + # by name + run_tool gpiomon foo foo + + output_regex_match ".*lines 'foo' and 'foo' are the same line" + status_is 1 + + # by name and offset + run_tool gpiomon --chip $sim0 1 foo + + output_regex_match ".*lines '1' and 'foo' are the same line" + status_is 1 +} + +@test "gpiomon: with strict named line check" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + run_tool gpiomon --strict foobar + + output_regex_match ".*line 'foobar' is not unique" + status_is 1 +} +@test "gpiomon: with lines by offset" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 1 pull-up + + dut_run gpiomon --banner --chip $sim0 6 1 + dut_flush + + gpiosim_set_pull sim0 1 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 1" + + gpiosim_set_pull sim0 1 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 1" + + gpiosim_set_pull sim0 6 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 6" + + gpiosim_set_pull sim0 6 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 6" + + assert_fail dut_readable +} + +@test "gpiomon: with lines strictly by name" { + # not suggesting this setup makes sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:42 line_name=6:13 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 1 pull-up + + dut_run gpiomon --banner --by-name --chip $sim0 42 13 + dut_flush + + gpiosim_set_pull sim0 1 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 1" + + gpiosim_set_pull sim0 1 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 1" + + gpiosim_set_pull sim0 6 pull-up + dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 6" + + gpiosim_set_pull sim0 6 pull-down + dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 6" + + assert_fail dut_readable +} + +@test "gpiomon: with no arguments" { + run_tool gpiomon + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpiomon: with no line specified" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpiomon --chip ${GPIOSIM_CHIP_NAME[sim0]} + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpiomon: with offset out of range" { + gpiosim_chip sim0 num_lines=4 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpiomon --chip $sim0 5 + + output_regex_match ".*offset 5 is out of range on chip '$sim0'" + status_is 1 +} + +@test "gpiomon: with invalid bias" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpiomon --bias=bad -c $sim0 0 1 + + output_regex_match ".*invalid bias.*" + status_is 1 +} + +@test "gpiomon: with custom format (event type + offset)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%e %o" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "1 4" +} + +@test "gpiomon: with custom format (event type + offset joined)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%e%o" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "14" +} + +@test "gpiomon: with custom format (edge, chip and line)" { + gpiosim_chip sim0 num_lines=8 line_name=4:baz + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%e %o %E %c %l" -c $sim0 baz + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "1 4 rising $sim0 baz" +} + +@test "gpiomon: with custom format (seconds timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%e %o %S" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match "1 4 [0-9]+\\.[0-9]+" +} + +@test "gpiomon: with custom format (UTC timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%U %e %o " --event-clock=realtime \ + -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match \ +"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+Z 1 4" +} + +@test "gpiomon: with custom format (localtime timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%L %e %o" --event-clock=realtime \ + -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_regex_match \ +"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+ 1 4" +} + +@test "gpiomon: with custom format (double percent sign)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=start%%end" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "start%end" +} + +@test "gpiomon: with custom format (double percent sign + event type specifier)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%%e" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "%e" +} + +@test "gpiomon: with custom format (single percent sign)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "%" +} + +@test "gpiomon: with custom format (single percent sign between other characters)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=foo % bar" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "foo % bar" +} + +@test "gpiomon: with custom format (unknown specifier)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpiomon --banner "--format=%x" -c $sim0 4 + dut_flush + + gpiosim_set_pull sim0 4 pull-up + dut_read + output_is "%x" +} From patchwork Mon Nov 21 10:22:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kent Gibson X-Patchwork-Id: 627396 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D47F9C4332F for ; Mon, 21 Nov 2022 10:24:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231383AbiKUKYG (ORCPT ); Mon, 21 Nov 2022 05:24:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60606 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231364AbiKUKXw (ORCPT ); Mon, 21 Nov 2022 05:23:52 -0500 Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 687C6AD9B3 for ; Mon, 21 Nov 2022 02:23:51 -0800 (PST) Received: by mail-pf1-x435.google.com with SMTP id y13so10914349pfp.7 for ; Mon, 21 Nov 2022 02:23:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=bFHUUOexPM5tZtPnn+1tIXnw6r2LLGXZYc8qiI0vDE0=; b=lFIWSerDqoRzzLVBS8buUUhQMMDKoju+2IsXs4VKVXm6I3tm4udUk3qyKVe9H8Kqo6 NccEWAegtycpboHopPhu5VlLc/wW/F4KSRQc2n7V4GACenEQA66QkBrlFZk7s0SrCEbL otQYS8+ti1R/W60CvexzclfjEKulTaHJEYF7kHC/vsRWljBSDjBzQaLpupXxe4NO9vMK oR6fwAIIn5NuExr4nMx7AzXSDAqp/KI8XJdy+U30GpHSrhxKI0b6cmgsZUjgGno+p6Jw r8sHSgSys0+Vdt8TwTo9WZ906FmMVFr208HRRiee/x27BN+4CTqd0G0oSZ2CoXtzPXLx u7qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bFHUUOexPM5tZtPnn+1tIXnw6r2LLGXZYc8qiI0vDE0=; b=35vs8PZ6If89SCnIim60wDD1FyJ6/4IMmkLkx3HeRqqonOLTNyt1ftKPDMeFdmSUQc zuEr7DTLyYY6vtvUU5gN+DzWIC0l0ohSSD1TLQeHn7mjZw+uthOd1MLI89MSfGcyy6UN TzJJRb5cPp0tIPugwNsUec/3axS0xH2OxSZU31+OJF3NC/AP2otzPjzCPHh571Oj94lT pFtF+/kJzC6dVK3Sh9TG8HaV4P8u+/2y1v54ktBkqDPo5c/UgYGEumLqhead8Ggoeq0W i1yZyK5fZpqYP4l8a2faAYtTnIqXC0Wdrmpmnxm1J3ujusnC18vDiWTnb7Ru/CJbE6FI BxnQ== X-Gm-Message-State: ANoB5pnSPOYDY7l3epQLsjMp67PNzo0G1vnrPtaESX25ipc8jeVulhaD zBsLfTDlHh68eETBDXroQ0QmI82P3MM= X-Google-Smtp-Source: AA0mqf7MgUdtSooIojuvi1atexrgFDAdQ8CRSYTudlKkd6Y8HUHDqTV3yIA1yiTD4mcckMRLjpOWPg== X-Received: by 2002:a63:d34e:0:b0:477:650a:705c with SMTP id u14-20020a63d34e000000b00477650a705cmr3732766pgi.588.1669026230519; Mon, 21 Nov 2022 02:23:50 -0800 (PST) Received: from sol.home.arpa (14-200-229-209.tpgi.com.au. [14.200.229.209]) by smtp.gmail.com with ESMTPSA id s184-20020a625ec1000000b0056ba7ce4d5asm8347419pfb.52.2022.11.21.02.23.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Nov 2022 02:23:50 -0800 (PST) From: Kent Gibson To: linux-gpio@vger.kernel.org, brgl@bgdev.pl Cc: Kent Gibson Subject: [libgpiod v2][PATCH v5 5/6] tools: gpionotify tests Date: Mon, 21 Nov 2022 18:22:52 +0800 Message-Id: <20221121102253.38306-6-warthog618@gmail.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221121102253.38306-1-warthog618@gmail.com> References: <20221121102253.38306-1-warthog618@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Extend the tool test suite to cover the gpionotify tool. Signed-off-by: Kent Gibson --- tools/gpio-tools-test.bats | 531 +++++++++++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats index 88de9bf..bf7f3d6 100755 --- a/tools/gpio-tools-test.bats +++ b/tools/gpio-tools-test.bats @@ -906,6 +906,20 @@ request_release_line() { status_is 0 } +@test "gpioget: with consumer" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz + + dut_run gpionotify --banner -F "%l %E %C" foo baz + + run_tool gpioget --consumer gpio-tools-tests foo baz + status_is 0 + + dut_read + output_regex_match "foo requested gpio-tools-tests" + output_regex_match "baz requested gpio-tools-tests" +} + @test "gpioget: with pull-up" { gpiosim_chip sim0 num_lines=8 @@ -2417,3 +2431,520 @@ request_release_line() { dut_read output_is "%x" } + +# +# gpionotify test cases +# + +@test "gpionotify: by name" { + gpiosim_chip sim0 num_lines=8 line_name=4:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner foo + dut_regex_match "Watching line .*" + + request_release_line $sim0 4 + + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"foo\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"foo\"" + # tools currently have no way to generate a reconfig event +} + +@test "gpionotify: by offset" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --chip $sim0 4 + dut_regex_match "Watching line .*" + + request_release_line $sim0 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 4" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 4" + + assert_fail dut_readable +} + +@test "gpionotify: by symlink" { + gpiosim_chip sim0 num_lines=8 + gpiosim_chip_symlink sim0 . + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --chip $GPIOSIM_CHIP_LINK 4 + dut_regex_match "Watching line .*" + + request_release_line $sim0 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0\\s+4" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0\\s+4" + + assert_fail dut_readable +} + +@test "gpionotify: by chip and name" { + gpiosim_chip sim0 num_lines=8 line_name=4:foo + gpiosim_chip sim1 num_lines=8 line_name=2:foo + + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + dut_run gpionotify --banner --chip $sim1 foo + dut_regex_match "Watching line .*" + + request_release_line $sim1 2 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim1 2 \"foo\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim1 2 \"foo\"" + + assert_fail dut_readable +} + +@test "gpionotify: first matching named line" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + dut_run gpionotify --banner foobar + dut_regex_match "Watching line .*" + + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 3 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"foobar\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"foobar\"" + + assert_fail dut_readable +} + +@test "gpionotify: with requested" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-up + + dut_run gpionotify --banner --event=requested --chip $sim0 4 + dut_flush + + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 4" + assert_fail dut_readable +} + +@test "gpionotify: with released" { + gpiosim_chip sim0 num_lines=8 + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + gpiosim_set_pull sim0 4 pull-down + + dut_run gpionotify --banner --event=released --chip $sim0 4 + dut_flush + + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 4" + assert_fail dut_readable +} + +@test "gpionotify: with quiet mode" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --quiet --chip $sim0 4 + dut_flush + + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 4 + assert_fail dut_readable +} + +@test "gpionotify: with unquoted" { + gpiosim_chip sim0 num_lines=8 line_name=4:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --unquoted foo + dut_regex_match "Watching line .*" + + request_release_line $sim0 4 + + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+foo" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+foo" +} + +@test "gpionotify: with num-events" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # redirect, as gpionotify exits after 4 events + dut_run_redirect gpionotify --num-events=4 --chip $sim0 3 4 + + + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 4 + request_release_line ${GPIOSIM_CHIP_NAME[sim0]} 3 + + dut_wait + status_is 0 + dut_read_redirect + + regex_matches "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 4" "${lines[0]}" + regex_matches "[0-9]+\.[0-9]+\\s+released\\s+$sim0 4" "${lines[1]}" + regex_matches "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 3" "${lines[2]}" + regex_matches "[0-9]+\.[0-9]+\\s+released\\s+$sim0 3" "${lines[3]}" + num_lines_is 4 +} + +@test "gpionotify: multiple lines" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --chip $sim0 1 2 3 4 5 + dut_regex_match "Watching lines .*" + + request_release_line $sim0 2 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 2" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 2" + + request_release_line $sim0 3 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 3" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 3" + + request_release_line $sim0 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 4" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 4" + + assert_fail dut_readable +} + +@test "gpionotify: multiple lines by name and offset" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --chip $sim0 bar foo 3 + dut_regex_match "Watching lines .*" + + request_release_line $sim0 2 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 2\\s+\"bar\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 2\\s+\"bar\"" + + request_release_line $sim0 1 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 1\\s+\"foo\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 1\\s+\"foo\"" + + request_release_line $sim0 3 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 3" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 3" + + assert_fail dut_readable +} + +@test "gpionotify: multiple lines across multiple chips" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + local sim1=${GPIOSIM_CHIP_NAME[sim1]} + + dut_run gpionotify --banner baz bar foo xyz + dut_regex_match "Watching lines .*" + + request_release_line $sim0 2 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"bar\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"bar\"" + + request_release_line $sim0 1 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"foo\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"foo\"" + + request_release_line $sim1 4 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"xyz\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"xyz\"" + + request_release_line $sim1 0 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+\"baz\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+\"baz\"" + + assert_fail dut_readable +} + +@test "gpionotify: exit after SIGINT" { + gpiosim_chip sim0 num_lines=8 + + dut_run gpionotify --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 4 + dut_regex_match "Watching line .*" + + dut_kill -SIGINT + dut_wait + + status_is 130 +} + +@test "gpionotify: exit after SIGTERM" { + gpiosim_chip sim0 num_lines=8 + + dut_run gpionotify --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 4 + dut_regex_match "Watching line .*" + + dut_kill -SIGTERM + dut_wait + + status_is 143 +} + +@test "gpionotify: with nonexistent line" { + run_tool gpionotify nonexistent-line + + status_is 1 + output_regex_match ".*cannot find line 'nonexistent-line'" +} + +@test "gpionotify: with same line twice" { + gpiosim_chip sim0 num_lines=8 line_name=1:foo + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + # by offset + run_tool gpionotify --chip $sim0 0 0 + + output_regex_match ".*lines '0' and '0' are the same line" + num_lines_is 1 + status_is 1 + + # by name + run_tool gpionotify foo foo + + output_regex_match ".*lines 'foo' and 'foo' are the same line" + num_lines_is 1 + status_is 1 + + # by name and offset + run_tool gpionotify --chip $sim0 1 foo + + output_regex_match ".*lines '1' and 'foo' are the same line" + num_lines_is 1 + status_is 1 +} + +@test "gpionotify: with strict named line check" { + gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \ + line_name=3:foobar + gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \ + line_name=4:xyz line_name=7:foobar + gpiosim_chip sim2 num_lines=16 + + run_tool gpionotify --strict foobar + + output_regex_match ".*line 'foobar' is not unique" + status_is 1 +} + +@test "gpionotify: with lines by offset" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --chip $sim0 1 + dut_flush + + request_release_line $sim0 1 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 1" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 1" + + request_release_line $sim0 6 + + assert_fail dut_readable +} + +@test "gpionotify: with lines strictly by name" { + # not suggesting this setup makes any sense + # - just test that we can deal with it + gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --by-name --chip $sim0 1 + dut_flush + + request_release_line $sim0 6 + dut_regex_match "[0-9]+\.[0-9]+\\s+requested\\s+$sim0 6 \"1\"" + dut_regex_match "[0-9]+\.[0-9]+\\s+released\\s+$sim0 6 \"1\"" + + request_release_line $sim0 1 + assert_fail dut_readable +} + +@test "gpionotify: with no arguments" { + run_tool gpionotify + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpionotify: with no line specified" { + gpiosim_chip sim0 num_lines=8 + + run_tool gpionotify --chip ${GPIOSIM_CHIP_NAME[sim0]} + + output_regex_match ".*at least one GPIO line must be specified" + status_is 1 +} + +@test "gpionotify: with offset out of range" { + gpiosim_chip sim0 num_lines=4 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + run_tool gpionotify --chip $sim0 5 + + output_regex_match ".*offset 5 is out of range on chip '$sim0'" + status_is 1 +} + +@test "gpionotify: with custom format (event type + offset)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%e %o" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "1 4" +} + +@test "gpionotify: with custom format (event type + offset joined)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%e%o" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "14" +} + +@test "gpionotify: with custom format (event, chip and line)" { + gpiosim_chip sim0 num_lines=8 line_name=4:baz + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=released \ + "--format=%e %o %E %c %l" -c $sim0 baz + dut_flush + + request_release_line $sim0 4 + dut_regex_match "2 4 released $sim0 baz" +} + +@test "gpionotify: with custom format (seconds timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%e %o %S" \ + -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_regex_match "1 4 [0-9]+\\.[0-9]+" +} + +@test "gpionotify: with custom format (UTC timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=released \ + "--format=%U %e %o" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_regex_match \ +"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+Z 2 4" +} + +@test "gpionotify: with custom format (localtime timestamp)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=released \ + "--format=%L %e %o" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_regex_match \ +"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+ 2 4" +} + +@test "gpionotify: with custom format (double percent sign)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=start%%end" \ + -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "start%end" +} + +@test "gpionotify: with custom format (double percent sign + event type specifier)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%%e" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "%e" +} + +@test "gpionotify: with custom format (single percent sign)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "%" +} + +@test "gpionotify: with custom format (single percent sign between other characters)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=foo % bar" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "foo % bar" +} + +@test "gpionotify: with custom format (unknown specifier)" { + gpiosim_chip sim0 num_lines=8 + + local sim0=${GPIOSIM_CHIP_NAME[sim0]} + + dut_run gpionotify --banner --event=requested "--format=%x" -c $sim0 4 + dut_flush + + request_release_line $sim0 4 + dut_read + output_is "%x" +}