| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- #!/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"
|