| #!/bin/bash -efu | |
| # SPDX-License-Identifier: GPL-2.0 | |
| #exit status | |
| #0: success | |
| #1: fail | |
| #4: skip test - including run as non-root user | |
| BASE=${0%/*} | |
| DEBUGFS= | |
| GPIO_DEBUGFS= | |
| dev_type="cdev" | |
| module="gpio-mockup" | |
| verbose= | |
| full_test= | |
| random= | |
| uapi_opt= | |
| active_opt= | |
| bias_opt= | |
| line_set_pid= | |
| # Kselftest return codes | |
| ksft_fail=1 | |
| ksft_skip=4 | |
| usage() | |
| { | |
| echo "Usage:" | |
| echo "$0 [-frv] [-t type]" | |
| echo "-f: full test (minimal set run by default)" | |
| echo "-r: test random lines as well as fence posts" | |
| echo "-t: interface type:" | |
| echo " cdev (character device ABI) - default" | |
| echo " cdev_v1 (deprecated character device ABI)" | |
| echo " sysfs (deprecated SYSFS ABI)" | |
| echo "-v: verbose progress reporting" | |
| exit $ksft_fail | |
| } | |
| skip() | |
| { | |
| echo "$*" >&2 | |
| echo "GPIO $module test SKIP" | |
| exit $ksft_skip | |
| } | |
| prerequisite() | |
| { | |
| [ $(id -u) -eq 0 ] || skip "must be run as root" | |
| DEBUGFS=$(grep -w debugfs /proc/mounts | cut -f2 -d' ') | |
| [ -d "$DEBUGFS" ] || skip "debugfs is not mounted" | |
| GPIO_DEBUGFS=$DEBUGFS/$module | |
| } | |
| remove_module() | |
| { | |
| modprobe -r -q $module | |
| } | |
| cleanup() | |
| { | |
| set +e | |
| release_line | |
| remove_module | |
| jobs -p | xargs -r kill > /dev/null 2>&1 | |
| } | |
| fail() | |
| { | |
| echo "test failed: $*" >&2 | |
| echo "GPIO $module test FAIL" | |
| exit $ksft_fail | |
| } | |
| try_insert_module() | |
| { | |
| modprobe -q $module "$1" || fail "insert $module failed with error $?" | |
| } | |
| log() | |
| { | |
| [ -z "$verbose" ] || echo "$*" | |
| } | |
| # The following line helpers, release_Line, get_line and set_line, all | |
| # make use of the global $chip and $offset variables. | |
| # | |
| # This implementation drives the GPIO character device (cdev) uAPI. | |
| # Other implementations may override these to test different uAPIs. | |
| # Release any resources related to the line | |
| release_line() | |
| { | |
| [ "$line_set_pid" ] && kill $line_set_pid && wait $line_set_pid || true | |
| line_set_pid= | |
| } | |
| # Read the current value of the line | |
| get_line() | |
| { | |
| release_line | |
| local cdev_opts=${uapi_opt}${active_opt} | |
| $BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset | |
| echo $? | |
| } | |
| # Set the state of the line | |
| # | |
| # Changes to line configuration are provided as parameters. | |
| # The line is assumed to be an output if the line value 0 or 1 is | |
| # specified, else an input. | |
| set_line() | |
| { | |
| local val= | |
| release_line | |
| # parse config options... | |
| for option in $*; do | |
| case $option in | |
| active-low) | |
| active_opt="-l " | |
| ;; | |
| active-high) | |
| active_opt= | |
| ;; | |
| bias-none) | |
| bias_opt= | |
| ;; | |
| pull-down) | |
| bias_opt="-bpull-down " | |
| ;; | |
| pull-up) | |
| bias_opt="-bpull-up " | |
| ;; | |
| 0) | |
| val=0 | |
| ;; | |
| 1) | |
| val=1 | |
| ;; | |
| esac | |
| done | |
| local cdev_opts=${uapi_opt}${active_opt} | |
| if [ "$val" ]; then | |
| $BASE/gpio-mockup-cdev $cdev_opts -s$val /dev/$chip $offset & | |
| # failure to set is detected by reading mockup and toggling values | |
| line_set_pid=$! | |
| # allow for gpio-mockup-cdev to launch and request line | |
| # (there is limited value in checking if line has been requested) | |
| sleep 0.01 | |
| elif [ "$bias_opt" ]; then | |
| cdev_opts=${cdev_opts}${bias_opt} | |
| $BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset || true | |
| fi | |
| } | |
| assert_line() | |
| { | |
| local val | |
| # don't need any retry here as set_mock allows for propagation | |
| val=$(get_line) | |
| [ "$val" = "$1" ] || fail "line value is ${val:-empty} when $1 was expected" | |
| } | |
| # The following mockup helpers all make use of the $mock_line | |
| assert_mock() | |
| { | |
| local backoff_wait=10 | |
| local retry=0 | |
| local val | |
| # retry allows for set propagation from uAPI to mockup | |
| while true; do | |
| val=$(< $mock_line) | |
| [ "$val" = "$1" ] && break | |
| retry=$((retry + 1)) | |
| [ $retry -lt 5 ] || fail "mockup $mock_line value ${val:-empty} when $1 expected" | |
| sleep $(printf "%0.2f" $((backoff_wait))e-3) | |
| backoff_wait=$((backoff_wait * 2)) | |
| done | |
| } | |
| set_mock() | |
| { | |
| echo "$1" > $mock_line | |
| # allow for set propagation - so we won't be in a race with set_line | |
| assert_mock "$1" | |
| } | |
| # test the functionality of a line | |
| # | |
| # The line is set from the mockup side and is read from the userspace side | |
| # (input), and is set from the userspace side and is read from the mockup side | |
| # (output). | |
| # | |
| # Setting the mockup pull using the userspace interface bias settings is | |
| # tested where supported by the userspace interface (cdev). | |
| test_line() | |
| { | |
| chip=$1 | |
| offset=$2 | |
| log "test_line $chip $offset" | |
| mock_line=$GPIO_DEBUGFS/$chip/$offset | |
| [ -e "$mock_line" ] || fail "missing line $chip:$offset" | |
| # test input active-high | |
| set_mock 1 | |
| set_line input active-high | |
| assert_line 1 | |
| set_mock 0 | |
| assert_line 0 | |
| set_mock 1 | |
| assert_line 1 | |
| if [ "$full_test" ]; then | |
| if [ "$dev_type" != "sysfs" ]; then | |
| # test pulls | |
| set_mock 0 | |
| set_line input pull-up | |
| assert_line 1 | |
| set_mock 0 | |
| assert_line 0 | |
| set_mock 1 | |
| set_line input pull-down | |
| assert_line 0 | |
| set_mock 1 | |
| assert_line 1 | |
| set_line bias-none | |
| fi | |
| # test input active-low | |
| set_mock 0 | |
| set_line active-low | |
| assert_line 1 | |
| set_mock 1 | |
| assert_line 0 | |
| set_mock 0 | |
| assert_line 1 | |
| # test output active-high | |
| set_mock 1 | |
| set_line active-high 0 | |
| assert_mock 0 | |
| set_line 1 | |
| assert_mock 1 | |
| set_line 0 | |
| assert_mock 0 | |
| fi | |
| # test output active-low | |
| set_mock 0 | |
| set_line active-low 0 | |
| assert_mock 1 | |
| set_line 1 | |
| assert_mock 0 | |
| set_line 0 | |
| assert_mock 1 | |
| release_line | |
| } | |
| test_no_line() | |
| { | |
| log test_no_line "$*" | |
| [ ! -e "$GPIO_DEBUGFS/$1/$2" ] || fail "unexpected line $1:$2" | |
| } | |
| # Load the module and check that the expected number of gpiochips, with the | |
| # expected number of lines, are created and are functional. | |
| # | |
| # $1 is the gpio_mockup_ranges parameter for the module | |
| # The remaining parameters are the number of lines, n, expected for each of | |
| # the gpiochips expected to be created. | |
| # | |
| # For each gpiochip the fence post lines, 0 and n-1, are tested, and the | |
| # line on the far side of the fence post, n, is tested to not exist. | |
| # | |
| # If the $random flag is set then a random line in the middle of the | |
| # gpiochip is tested as well. | |
| insmod_test() | |
| { | |
| local ranges= | |
| local gc= | |
| local width= | |
| [ "${1:-}" ] || fail "missing ranges" | |
| ranges=$1 ; shift | |
| try_insert_module "gpio_mockup_ranges=$ranges" | |
| log "GPIO $module test with ranges: <$ranges>:" | |
| # e.g. /sys/kernel/debug/gpio-mockup/gpiochip1 | |
| gpiochip=$(find "$DEBUGFS/$module/" -name gpiochip* -type d | sort) | |
| for chip in $gpiochip; do | |
| gc=${chip##*/} | |
| [ "${1:-}" ] || fail "unexpected chip - $gc" | |
| width=$1 ; shift | |
| test_line $gc 0 | |
| if [ "$random" -a $width -gt 2 ]; then | |
| test_line $gc $((RANDOM % ($width - 2) + 1)) | |
| fi | |
| test_line $gc $(($width - 1)) | |
| test_no_line $gc $width | |
| done | |
| [ "${1:-}" ] && fail "missing expected chip of width $1" | |
| remove_module || fail "failed to remove module with error $?" | |
| } | |
| while getopts ":frvt:" opt; do | |
| case $opt in | |
| f) | |
| full_test=true | |
| ;; | |
| r) | |
| random=true | |
| ;; | |
| t) | |
| dev_type=$OPTARG | |
| ;; | |
| v) | |
| verbose=true | |
| ;; | |
| *) | |
| usage | |
| ;; | |
| esac | |
| done | |
| shift $((OPTIND - 1)) | |
| [ "${1:-}" ] && fail "unknown argument '$1'" | |
| prerequisite | |
| trap 'exit $ksft_fail' SIGTERM SIGINT | |
| trap cleanup EXIT | |
| case "$dev_type" in | |
| sysfs) | |
| source $BASE/gpio-mockup-sysfs.sh | |
| echo "WARNING: gpio sysfs ABI is deprecated." | |
| ;; | |
| cdev_v1) | |
| echo "WARNING: gpio cdev ABI v1 is deprecated." | |
| uapi_opt="-u1 " | |
| ;; | |
| cdev) | |
| ;; | |
| *) | |
| fail "unknown interface type: $dev_type" | |
| ;; | |
| esac | |
| remove_module || fail "can't remove existing $module module" | |
| # manual gpio allocation tests fail if a physical chip already exists | |
| [ "$full_test" -a -e "/dev/gpiochip0" ] && skip "full tests conflict with gpiochip0" | |
| echo "1. Module load tests" | |
| echo "1.1. dynamic allocation of gpio" | |
| insmod_test "-1,32" 32 | |
| insmod_test "-1,23,-1,32" 23 32 | |
| insmod_test "-1,23,-1,26,-1,32" 23 26 32 | |
| if [ "$full_test" ]; then | |
| echo "1.2. manual allocation of gpio" | |
| insmod_test "0,32" 32 | |
| insmod_test "0,32,32,60" 32 28 | |
| insmod_test "0,32,40,64,64,96" 32 24 32 | |
| echo "1.3. dynamic and manual allocation of gpio" | |
| insmod_test "-1,32,32,62" 32 30 | |
| insmod_test "-1,22,-1,23,0,24,32,64" 22 23 24 32 | |
| insmod_test "-1,32,32,60,-1,29" 32 28 29 | |
| insmod_test "-1,32,40,64,-1,5" 32 24 5 | |
| insmod_test "0,32,32,44,-1,22,-1,31" 32 12 22 31 | |
| fi | |
| echo "2. Module load error tests" | |
| echo "2.1 no lines defined" | |
| insmod_test "0,0" | |
| if [ "$full_test" ]; then | |
| echo "2.2 ignore range overlap" | |
| insmod_test "0,32,0,1" 32 | |
| insmod_test "0,32,1,5" 32 | |
| insmod_test "0,32,30,35" 32 | |
| insmod_test "0,32,31,32" 32 | |
| insmod_test "10,32,30,35" 22 | |
| insmod_test "10,32,9,14" 22 | |
| insmod_test "0,32,20,21,40,56" 32 16 | |
| insmod_test "0,32,32,64,32,40" 32 32 | |
| insmod_test "0,32,32,64,36,37" 32 32 | |
| insmod_test "0,32,35,64,34,36" 32 29 | |
| insmod_test "0,30,35,64,35,45" 30 29 | |
| insmod_test "0,32,40,56,30,33" 32 16 | |
| insmod_test "0,32,40,56,30,41" 32 16 | |
| insmod_test "0,32,40,56,39,45" 32 16 | |
| fi | |
| echo "GPIO $module test PASS" |