18bc395a6SKent Gibson#!/bin/bash -efu
2b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0
322f6592bSBamvor Jian Zhang
422f6592bSBamvor Jian Zhang#exit status
58bc395a6SKent Gibson#0: success
68bc395a6SKent Gibson#1: fail
78bc395a6SKent Gibson#4: skip test - including run as non-root user
822f6592bSBamvor Jian Zhang
98bc395a6SKent GibsonBASE=${0%/*}
1022f6592bSBamvor Jian ZhangDEBUGFS=
1122f6592bSBamvor Jian ZhangGPIO_DEBUGFS=
128bc395a6SKent Gibsondev_type="cdev"
138bc395a6SKent Gibsonmodule="gpio-mockup"
148bc395a6SKent Gibsonverbose=
158bc395a6SKent Gibsonfull_test=
168bc395a6SKent Gibsonrandom=
17*10f33652SKent Gibsonuapi_opt=
188bc395a6SKent Gibsonactive_opt=
198bc395a6SKent Gibsonbias_opt=
208bc395a6SKent Gibsonline_set_pid=
2122f6592bSBamvor Jian Zhang
228bc395a6SKent Gibson# Kselftest return codes
238bc395a6SKent Gibsonksft_fail=1
24bd1bf88cSShuah Khan (Samsung OSG)ksft_skip=4
25bd1bf88cSShuah Khan (Samsung OSG)
2622f6592bSBamvor Jian Zhangusage()
2722f6592bSBamvor Jian Zhang{
2822f6592bSBamvor Jian Zhang	echo "Usage:"
298bc395a6SKent Gibson	echo "$0 [-frv] [-t type]"
308bc395a6SKent Gibson	echo "-f:  full test (minimal set run by default)"
318bc395a6SKent Gibson	echo "-r:  test random lines as well as fence posts"
328bc395a6SKent Gibson	echo "-t:  interface type:"
338bc395a6SKent Gibson	echo "      cdev (character device ABI) - default"
34*10f33652SKent Gibson	echo "      cdev_v1 (deprecated character device ABI)"
358bc395a6SKent Gibson	echo "      sysfs (deprecated SYSFS ABI)"
368bc395a6SKent Gibson	echo "-v:  verbose progress reporting"
378bc395a6SKent Gibson	exit $ksft_fail
388bc395a6SKent Gibson}
398bc395a6SKent Gibson
408bc395a6SKent Gibsonskip()
418bc395a6SKent Gibson{
428bc395a6SKent Gibson	echo "$*" >&2
438bc395a6SKent Gibson	echo "GPIO $module test SKIP"
448bc395a6SKent Gibson	exit $ksft_skip
4522f6592bSBamvor Jian Zhang}
4622f6592bSBamvor Jian Zhang
4722f6592bSBamvor Jian Zhangprerequisite()
4822f6592bSBamvor Jian Zhang{
498bc395a6SKent Gibson	[ $(id -u) -eq 0 ] || skip "must be run as root"
5022f6592bSBamvor Jian Zhang
518bc395a6SKent Gibson	DEBUGFS=$(grep -w debugfs /proc/mounts | cut -f2 -d' ')
528bc395a6SKent Gibson	[ -d "$DEBUGFS" ] || skip "debugfs is not mounted"
538bc395a6SKent Gibson
548bc395a6SKent Gibson	GPIO_DEBUGFS=$DEBUGFS/$module
5522f6592bSBamvor Jian Zhang}
5622f6592bSBamvor Jian Zhang
5722f6592bSBamvor Jian Zhangremove_module()
5822f6592bSBamvor Jian Zhang{
5922f6592bSBamvor Jian Zhang	modprobe -r -q $module
6022f6592bSBamvor Jian Zhang}
6122f6592bSBamvor Jian Zhang
628bc395a6SKent Gibsoncleanup()
6322f6592bSBamvor Jian Zhang{
648bc395a6SKent Gibson	set +e
658bc395a6SKent Gibson	release_line
6622f6592bSBamvor Jian Zhang	remove_module
678bc395a6SKent Gibson	jobs -p | xargs -r kill > /dev/null 2>&1
6822f6592bSBamvor Jian Zhang}
6922f6592bSBamvor Jian Zhang
708bc395a6SKent Gibsonfail()
7122f6592bSBamvor Jian Zhang{
728bc395a6SKent Gibson	echo "test failed: $*" >&2
738bc395a6SKent Gibson	echo "GPIO $module test FAIL"
748bc395a6SKent Gibson	exit $ksft_fail
7522f6592bSBamvor Jian Zhang}
7622f6592bSBamvor Jian Zhang
778bc395a6SKent Gibsontry_insert_module()
7822f6592bSBamvor Jian Zhang{
798bc395a6SKent Gibson	modprobe -q $module "$1" || fail "insert $module failed with error $?"
8022f6592bSBamvor Jian Zhang}
8122f6592bSBamvor Jian Zhang
828bc395a6SKent Gibsonlog()
838bc395a6SKent Gibson{
848bc395a6SKent Gibson	[ -z "$verbose" ] || echo "$*"
858bc395a6SKent Gibson}
8622f6592bSBamvor Jian Zhang
878bc395a6SKent Gibson# The following line helpers, release_Line, get_line and set_line, all
888bc395a6SKent Gibson# make use of the global $chip and $offset variables.
898bc395a6SKent Gibson#
908bc395a6SKent Gibson# This implementation drives the GPIO character device (cdev) uAPI.
918bc395a6SKent Gibson# Other implementations may override these to test different uAPIs.
9222f6592bSBamvor Jian Zhang
938bc395a6SKent Gibson# Release any resources related to the line
948bc395a6SKent Gibsonrelease_line()
958bc395a6SKent Gibson{
968bc395a6SKent Gibson	[ "$line_set_pid" ] && kill $line_set_pid && wait $line_set_pid || true
978bc395a6SKent Gibson	line_set_pid=
988bc395a6SKent Gibson}
9922f6592bSBamvor Jian Zhang
1008bc395a6SKent Gibson# Read the current value of the line
1018bc395a6SKent Gibsonget_line()
1028bc395a6SKent Gibson{
1038bc395a6SKent Gibson	release_line
10422f6592bSBamvor Jian Zhang
105*10f33652SKent Gibson	local cdev_opts=${uapi_opt}${active_opt}
106*10f33652SKent Gibson	$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset
1078bc395a6SKent Gibson	echo $?
1088bc395a6SKent Gibson}
1098bc395a6SKent Gibson
1108bc395a6SKent Gibson# Set the state of the line
1118bc395a6SKent Gibson#
1128bc395a6SKent Gibson# Changes to line configuration are provided as parameters.
1138bc395a6SKent Gibson# The line is assumed to be an output if the line value 0 or 1 is
1148bc395a6SKent Gibson# specified, else an input.
1158bc395a6SKent Gibsonset_line()
1168bc395a6SKent Gibson{
1178bc395a6SKent Gibson	local val=
1188bc395a6SKent Gibson
1198bc395a6SKent Gibson	release_line
1208bc395a6SKent Gibson
1218bc395a6SKent Gibson	# parse config options...
1228bc395a6SKent Gibson	for option in $*; do
1238bc395a6SKent Gibson		case $option in
1248bc395a6SKent Gibson		active-low)
1258bc395a6SKent Gibson			active_opt="-l "
12622f6592bSBamvor Jian Zhang			;;
1278bc395a6SKent Gibson		active-high)
1288bc395a6SKent Gibson			active_opt=
12922f6592bSBamvor Jian Zhang			;;
1308bc395a6SKent Gibson		bias-none)
1318bc395a6SKent Gibson			bias_opt=
13222f6592bSBamvor Jian Zhang			;;
1338bc395a6SKent Gibson		pull-down)
1348bc395a6SKent Gibson			bias_opt="-bpull-down "
13522f6592bSBamvor Jian Zhang			;;
1368bc395a6SKent Gibson		pull-up)
1378bc395a6SKent Gibson			bias_opt="-bpull-up "
13822f6592bSBamvor Jian Zhang			;;
1398bc395a6SKent Gibson		0)
1408bc395a6SKent Gibson			val=0
1418bc395a6SKent Gibson			;;
1428bc395a6SKent Gibson		1)
1438bc395a6SKent Gibson			val=1
14422f6592bSBamvor Jian Zhang			;;
14522f6592bSBamvor Jian Zhang		esac
14622f6592bSBamvor Jian Zhang	done
14722f6592bSBamvor Jian Zhang
148*10f33652SKent Gibson	local cdev_opts=${uapi_opt}${active_opt}
1498bc395a6SKent Gibson	if [ "$val" ]; then
1508bc395a6SKent Gibson		$BASE/gpio-mockup-cdev $cdev_opts -s$val /dev/$chip $offset &
1518bc395a6SKent Gibson		# failure to set is detected by reading mockup and toggling values
1528bc395a6SKent Gibson		line_set_pid=$!
1538bc395a6SKent Gibson		# allow for gpio-mockup-cdev to launch and request line
1548bc395a6SKent Gibson		# (there is limited value in checking if line has been requested)
1558bc395a6SKent Gibson		sleep 0.01
1568bc395a6SKent Gibson	elif [ "$bias_opt" ]; then
1578bc395a6SKent Gibson		cdev_opts=${cdev_opts}${bias_opt}
1588bc395a6SKent Gibson		$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset || true
1598bc395a6SKent Gibson	fi
1608bc395a6SKent Gibson}
1618bc395a6SKent Gibson
1628bc395a6SKent Gibsonassert_line()
1638bc395a6SKent Gibson{
1648bc395a6SKent Gibson	local val
1658bc395a6SKent Gibson	# don't need any retry here as set_mock allows for propagation
1668bc395a6SKent Gibson	val=$(get_line)
1678bc395a6SKent Gibson	[ "$val" = "$1" ] || fail "line value is ${val:-empty} when $1 was expected"
1688bc395a6SKent Gibson}
1698bc395a6SKent Gibson
1708bc395a6SKent Gibson# The following mockup helpers all make use of the $mock_line
1718bc395a6SKent Gibsonassert_mock()
1728bc395a6SKent Gibson{
1738bc395a6SKent Gibson	local backoff_wait=10
1748bc395a6SKent Gibson	local retry=0
1758bc395a6SKent Gibson	local val
1768bc395a6SKent Gibson	# retry allows for set propagation from uAPI to mockup
1778bc395a6SKent Gibson	while true; do
1788bc395a6SKent Gibson		val=$(< $mock_line)
1798bc395a6SKent Gibson		[ "$val" = "$1" ] && break
1808bc395a6SKent Gibson		retry=$((retry + 1))
1818bc395a6SKent Gibson		[ $retry -lt 5 ] || fail "mockup $mock_line value ${val:-empty} when $1 expected"
1828bc395a6SKent Gibson		sleep $(printf "%0.2f" $((backoff_wait))e-3)
1838bc395a6SKent Gibson		backoff_wait=$((backoff_wait * 2))
1848bc395a6SKent Gibson	done
1858bc395a6SKent Gibson}
1868bc395a6SKent Gibson
1878bc395a6SKent Gibsonset_mock()
1888bc395a6SKent Gibson{
1898bc395a6SKent Gibson	echo "$1" > $mock_line
1908bc395a6SKent Gibson	# allow for set propagation - so we won't be in a race with set_line
1918bc395a6SKent Gibson	assert_mock "$1"
1928bc395a6SKent Gibson}
1938bc395a6SKent Gibson
1948bc395a6SKent Gibson# test the functionality of a line
1958bc395a6SKent Gibson#
1968bc395a6SKent Gibson# The line is set from the mockup side and is read from the userspace side
1978bc395a6SKent Gibson# (input), and is set from the userspace side and is read from the mockup side
1988bc395a6SKent Gibson# (output).
1998bc395a6SKent Gibson#
2008bc395a6SKent Gibson# Setting the mockup pull using the userspace interface bias settings is
2018bc395a6SKent Gibson# tested where supported by the userspace interface (cdev).
2028bc395a6SKent Gibsontest_line()
2038bc395a6SKent Gibson{
2048bc395a6SKent Gibson	chip=$1
2058bc395a6SKent Gibson	offset=$2
2068bc395a6SKent Gibson	log "test_line $chip $offset"
2078bc395a6SKent Gibson	mock_line=$GPIO_DEBUGFS/$chip/$offset
2088bc395a6SKent Gibson	[ -e "$mock_line" ] || fail "missing line $chip:$offset"
2098bc395a6SKent Gibson
2108bc395a6SKent Gibson	# test input active-high
2118bc395a6SKent Gibson	set_mock 1
2128bc395a6SKent Gibson	set_line input active-high
2138bc395a6SKent Gibson	assert_line 1
2148bc395a6SKent Gibson	set_mock 0
2158bc395a6SKent Gibson	assert_line 0
2168bc395a6SKent Gibson	set_mock 1
2178bc395a6SKent Gibson	assert_line 1
2188bc395a6SKent Gibson
2198bc395a6SKent Gibson	if [ "$full_test" ]; then
2208bc395a6SKent Gibson		if [ "$dev_type" != "sysfs" ]; then
2218bc395a6SKent Gibson			# test pulls
2228bc395a6SKent Gibson			set_mock 0
2238bc395a6SKent Gibson			set_line input pull-up
2248bc395a6SKent Gibson			assert_line 1
2258bc395a6SKent Gibson			set_mock 0
2268bc395a6SKent Gibson			assert_line 0
2278bc395a6SKent Gibson
2288bc395a6SKent Gibson			set_mock 1
2298bc395a6SKent Gibson			set_line input pull-down
2308bc395a6SKent Gibson			assert_line 0
2318bc395a6SKent Gibson			set_mock 1
2328bc395a6SKent Gibson			assert_line 1
2338bc395a6SKent Gibson
2348bc395a6SKent Gibson			set_line bias-none
23522f6592bSBamvor Jian Zhang		fi
23622f6592bSBamvor Jian Zhang
2378bc395a6SKent Gibson		# test input active-low
2388bc395a6SKent Gibson		set_mock 0
2398bc395a6SKent Gibson		set_line active-low
2408bc395a6SKent Gibson		assert_line 1
2418bc395a6SKent Gibson		set_mock 1
2428bc395a6SKent Gibson		assert_line 0
2438bc395a6SKent Gibson		set_mock 0
2448bc395a6SKent Gibson		assert_line 1
2458bc395a6SKent Gibson
2468bc395a6SKent Gibson		# test output active-high
2478bc395a6SKent Gibson		set_mock 1
2488bc395a6SKent Gibson		set_line active-high 0
2498bc395a6SKent Gibson		assert_mock 0
2508bc395a6SKent Gibson		set_line 1
2518bc395a6SKent Gibson		assert_mock 1
2528bc395a6SKent Gibson		set_line 0
2538bc395a6SKent Gibson		assert_mock 0
25422f6592bSBamvor Jian Zhang	fi
25522f6592bSBamvor Jian Zhang
2568bc395a6SKent Gibson	# test output active-low
2578bc395a6SKent Gibson	set_mock 0
2588bc395a6SKent Gibson	set_line active-low 0
2598bc395a6SKent Gibson	assert_mock 1
2608bc395a6SKent Gibson	set_line 1
2618bc395a6SKent Gibson	assert_mock 0
2628bc395a6SKent Gibson	set_line 0
2638bc395a6SKent Gibson	assert_mock 1
2648bc395a6SKent Gibson
2658bc395a6SKent Gibson	release_line
2668bc395a6SKent Gibson}
2678bc395a6SKent Gibson
2688bc395a6SKent Gibsontest_no_line()
2698bc395a6SKent Gibson{
2708bc395a6SKent Gibson	log test_no_line "$*"
2718bc395a6SKent Gibson	[ ! -e "$GPIO_DEBUGFS/$1/$2" ] || fail "unexpected line $1:$2"
2728bc395a6SKent Gibson}
2738bc395a6SKent Gibson
2748bc395a6SKent Gibson# Load the module and check that the expected number of gpiochips, with the
2758bc395a6SKent Gibson# expected number of lines, are created and are functional.
2768bc395a6SKent Gibson#
2778bc395a6SKent Gibson# $1 is the gpio_mockup_ranges parameter for the module
2788bc395a6SKent Gibson# The remaining parameters are the number of lines, n, expected for each of
2798bc395a6SKent Gibson# the gpiochips expected to be created.
2808bc395a6SKent Gibson#
2818bc395a6SKent Gibson# For each gpiochip the fence post lines, 0 and n-1, are tested, and the
2828bc395a6SKent Gibson# line on the far side of the fence post, n, is tested to not exist.
2838bc395a6SKent Gibson#
2848bc395a6SKent Gibson# If the $random flag is set then a random line in the middle of the
2858bc395a6SKent Gibson# gpiochip is tested as well.
2868bc395a6SKent Gibsoninsmod_test()
2878bc395a6SKent Gibson{
2888bc395a6SKent Gibson	local ranges=
2898bc395a6SKent Gibson	local gc=
2908bc395a6SKent Gibson	local width=
2918bc395a6SKent Gibson
2928bc395a6SKent Gibson	[ "${1:-}" ] || fail "missing ranges"
2938bc395a6SKent Gibson	ranges=$1 ; shift
2948bc395a6SKent Gibson	try_insert_module "gpio_mockup_ranges=$ranges"
2958bc395a6SKent Gibson	log "GPIO $module test with ranges: <$ranges>:"
2968bc395a6SKent Gibson	# e.g. /sys/kernel/debug/gpio-mockup/gpiochip1
2978bc395a6SKent Gibson	gpiochip=$(find "$DEBUGFS/$module/" -name gpiochip* -type d | sort)
2988bc395a6SKent Gibson	for chip in $gpiochip; do
2998bc395a6SKent Gibson		gc=${chip##*/}
3008bc395a6SKent Gibson		[ "${1:-}" ] || fail "unexpected chip - $gc"
3018bc395a6SKent Gibson		width=$1 ; shift
3028bc395a6SKent Gibson		test_line $gc 0
3038bc395a6SKent Gibson		if [ "$random" -a $width -gt 2 ]; then
3048bc395a6SKent Gibson			test_line $gc $((RANDOM % ($width - 2) + 1))
3058bc395a6SKent Gibson		fi
3068bc395a6SKent Gibson		test_line $gc $(($width - 1))
3078bc395a6SKent Gibson		test_no_line $gc $width
3088bc395a6SKent Gibson	done
3098bc395a6SKent Gibson	[ "${1:-}" ] && fail "missing expected chip of width $1"
3108bc395a6SKent Gibson	remove_module || fail "failed to remove module with error $?"
3118bc395a6SKent Gibson}
3128bc395a6SKent Gibson
3138bc395a6SKent Gibsonwhile getopts ":frvt:" opt; do
3148bc395a6SKent Gibson	case $opt in
3158bc395a6SKent Gibson	f)
3168bc395a6SKent Gibson		full_test=true
3178bc395a6SKent Gibson		;;
3188bc395a6SKent Gibson	r)
3198bc395a6SKent Gibson		random=true
3208bc395a6SKent Gibson		;;
3218bc395a6SKent Gibson	t)
3228bc395a6SKent Gibson		dev_type=$OPTARG
3238bc395a6SKent Gibson		;;
3248bc395a6SKent Gibson	v)
3258bc395a6SKent Gibson		verbose=true
3268bc395a6SKent Gibson		;;
3278bc395a6SKent Gibson	*)
3288bc395a6SKent Gibson		usage
3298bc395a6SKent Gibson		;;
3308bc395a6SKent Gibson	esac
3318bc395a6SKent Gibsondone
3328bc395a6SKent Gibsonshift $((OPTIND - 1))
3338bc395a6SKent Gibson
3348bc395a6SKent Gibson[ "${1:-}" ] && fail "unknown argument '$1'"
3358bc395a6SKent Gibson
33622f6592bSBamvor Jian Zhangprerequisite
33722f6592bSBamvor Jian Zhang
3388bc395a6SKent Gibsontrap 'exit $ksft_fail' SIGTERM SIGINT
3398bc395a6SKent Gibsontrap cleanup EXIT
3408bc395a6SKent Gibson
3418bc395a6SKent Gibsoncase "$dev_type" in
3428bc395a6SKent Gibsonsysfs)
3438bc395a6SKent Gibson	source $BASE/gpio-mockup-sysfs.sh
3448bc395a6SKent Gibson	echo "WARNING: gpio sysfs ABI is deprecated."
3458bc395a6SKent Gibson	;;
346*10f33652SKent Gibsoncdev_v1)
347*10f33652SKent Gibson	echo "WARNING: gpio cdev ABI v1 is deprecated."
348*10f33652SKent Gibson	uapi_opt="-u1 "
349*10f33652SKent Gibson	;;
3508bc395a6SKent Gibsoncdev)
3518bc395a6SKent Gibson	;;
3528bc395a6SKent Gibson*)
3538bc395a6SKent Gibson	fail "unknown interface type: $dev_type"
3548bc395a6SKent Gibson	;;
3558bc395a6SKent Gibsonesac
3568bc395a6SKent Gibson
3578bc395a6SKent Gibsonremove_module || fail "can't remove existing $module module"
3588bc395a6SKent Gibson
3598bc395a6SKent Gibson# manual gpio allocation tests fail if a physical chip already exists
3608bc395a6SKent Gibson[ "$full_test" -a -e "/dev/gpiochip0" ] && skip "full tests conflict with gpiochip0"
3618bc395a6SKent Gibson
3628bc395a6SKent Gibsonecho "1.  Module load tests"
3638bc395a6SKent Gibsonecho "1.1.  dynamic allocation of gpio"
3648bc395a6SKent Gibsoninsmod_test "-1,32" 32
3658bc395a6SKent Gibsoninsmod_test "-1,23,-1,32" 23 32
3668bc395a6SKent Gibsoninsmod_test "-1,23,-1,26,-1,32" 23 26 32
3678bc395a6SKent Gibsonif [ "$full_test" ]; then
3688bc395a6SKent Gibson	echo "1.2.  manual allocation of gpio"
3698bc395a6SKent Gibson	insmod_test "0,32" 32
3708bc395a6SKent Gibson	insmod_test "0,32,32,60" 32 28
3718bc395a6SKent Gibson	insmod_test "0,32,40,64,64,96" 32 24 32
3728bc395a6SKent Gibson	echo "1.3.  dynamic and manual allocation of gpio"
3738bc395a6SKent Gibson	insmod_test "-1,32,32,62" 32 30
3748bc395a6SKent Gibson	insmod_test "-1,22,-1,23,0,24,32,64" 22 23 24 32
3758bc395a6SKent Gibson	insmod_test "-1,32,32,60,-1,29" 32 28 29
3768bc395a6SKent Gibson	insmod_test "-1,32,40,64,-1,5" 32 24 5
3778bc395a6SKent Gibson	insmod_test "0,32,32,44,-1,22,-1,31" 32 12 22 31
37822f6592bSBamvor Jian Zhangfi
3798bc395a6SKent Gibsonecho "2.  Module load error tests"
3808bc395a6SKent Gibsonecho "2.1 gpio overflow"
38122f6592bSBamvor Jian Zhang# Currently: The max number of gpio(1024) is defined in arm architecture.
3828bc395a6SKent Gibsoninsmod_test "-1,1024"
3838bc395a6SKent Gibsonif [ "$full_test" ]; then
3848bc395a6SKent Gibson	echo "2.2 no lines defined"
3858bc395a6SKent Gibson	insmod_test "0,0"
3868bc395a6SKent Gibson	echo "2.3 ignore range overlap"
3878bc395a6SKent Gibson	insmod_test "0,32,0,1" 32
3888bc395a6SKent Gibson	insmod_test "0,32,1,5" 32
3898bc395a6SKent Gibson	insmod_test "0,32,30,35" 32
3908bc395a6SKent Gibson	insmod_test "0,32,31,32" 32
3918bc395a6SKent Gibson	insmod_test "10,32,30,35" 22
3928bc395a6SKent Gibson	insmod_test "10,32,9,14" 22
3938bc395a6SKent Gibson	insmod_test "0,32,20,21,40,56" 32 16
3948bc395a6SKent Gibson	insmod_test "0,32,32,64,32,40" 32 32
3958bc395a6SKent Gibson	insmod_test "0,32,32,64,36,37" 32 32
3968bc395a6SKent Gibson	insmod_test "0,32,35,64,34,36" 32 29
3978bc395a6SKent Gibson	insmod_test "0,30,35,64,35,45" 30 29
3988bc395a6SKent Gibson	insmod_test "0,32,40,56,30,33" 32 16
3998bc395a6SKent Gibson	insmod_test "0,32,40,56,30,41" 32 16
4008bc395a6SKent Gibson	insmod_test "0,32,40,56,39,45" 32 16
40122f6592bSBamvor Jian Zhangfi
40222f6592bSBamvor Jian Zhang
4038bc395a6SKent Gibsonecho "GPIO $module test PASS"
404