1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3# Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl>
4
5BASE_DIR=`dirname $0`
6CONFIGFS_DIR="/sys/kernel/config/gpio-sim"
7MODULE="gpio-sim"
8
9fail() {
10	echo "$*" >&2
11	echo "GPIO $MODULE test FAIL"
12	exit 1
13}
14
15skip() {
16	echo "$*" >&2
17	echo "GPIO $MODULE test SKIP"
18	exit 4
19}
20
21remove_chip() {
22	local CHIP=$1
23
24	for FILE in $CONFIGFS_DIR/$CHIP/*; do
25		BANK=`basename $FILE`
26		if [ "$BANK" = "live" -o "$BANK" = "dev_name" ]; then
27			continue
28		fi
29
30		LINES=`ls $CONFIGFS_DIR/$CHIP/$BANK/ | egrep ^line`
31		if [ "$?" = 0 ]; then
32			for LINE in $LINES; do
33				if [ -e $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog ]; then
34					rmdir $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog || \
35						fail "Unable to remove the hog"
36				fi
37
38				rmdir $CONFIGFS_DIR/$CHIP/$BANK/$LINE || \
39					fail "Unable to remove the line"
40			done
41		fi
42
43		rmdir $CONFIGFS_DIR/$CHIP/$BANK
44	done
45
46	rmdir $CONFIGFS_DIR/$CHIP || fail "Unable to remove the chip"
47}
48
49configfs_cleanup() {
50	for CHIP in `ls $CONFIGFS_DIR/`; do
51		remove_chip $CHIP
52	done
53}
54
55create_chip() {
56	local CHIP=$1
57
58	mkdir $CONFIGFS_DIR/$CHIP
59}
60
61create_bank() {
62	local CHIP=$1
63	local BANK=$2
64
65	mkdir $CONFIGFS_DIR/$CHIP/$BANK
66}
67
68set_label() {
69	local CHIP=$1
70	local BANK=$2
71	local LABEL=$3
72
73	echo $LABEL > $CONFIGFS_DIR/$CHIP/$BANK/label || fail "Unable to set the chip label"
74}
75
76set_num_lines() {
77	local CHIP=$1
78	local BANK=$2
79	local NUM_LINES=$3
80
81	echo $NUM_LINES > $CONFIGFS_DIR/$CHIP/$BANK/num_lines || \
82		fail "Unable to set the number of lines"
83}
84
85set_line_name() {
86	local CHIP=$1
87	local BANK=$2
88	local OFFSET=$3
89	local NAME=$4
90	local LINE_DIR=$CONFIGFS_DIR/$CHIP/$BANK/line$OFFSET
91
92	test -d $LINE_DIR || mkdir $LINE_DIR
93	echo $NAME > $LINE_DIR/name || fail "Unable to set the line name"
94}
95
96enable_chip() {
97	local CHIP=$1
98
99	echo 1 > $CONFIGFS_DIR/$CHIP/live || fail "Unable to enable the chip"
100}
101
102disable_chip() {
103	local CHIP=$1
104
105	echo 0 > $CONFIGFS_DIR/$CHIP/live || fail "Unable to disable the chip"
106}
107
108configfs_chip_name() {
109	local CHIP=$1
110	local BANK=$2
111
112	cat $CONFIGFS_DIR/$CHIP/$BANK/chip_name 2> /dev/null || \
113		fail "unable to read the chip name from configfs"
114}
115
116configfs_dev_name() {
117	local CHIP=$1
118
119	cat $CONFIGFS_DIR/$CHIP/dev_name 2> /dev/null || \
120		fail "unable to read the device name from configfs"
121}
122
123get_chip_num_lines() {
124	local CHIP=$1
125	local BANK=$2
126
127	$BASE_DIR/gpio-chip-info /dev/`configfs_chip_name $CHIP $BANK` num-lines || \
128		fail "unable to read the number of lines from the character device"
129}
130
131get_chip_label() {
132	local CHIP=$1
133	local BANK=$2
134
135	$BASE_DIR/gpio-chip-info /dev/`configfs_chip_name $CHIP $BANK` label || \
136		fail "unable to read the chip label from the character device"
137}
138
139get_line_name() {
140	local CHIP=$1
141	local BANK=$2
142	local OFFSET=$3
143
144	$BASE_DIR/gpio-line-name /dev/`configfs_chip_name $CHIP $BANK` $OFFSET || \
145		fail "unable to read the line name from the character device"
146}
147
148sysfs_set_pull() {
149	local DEV=$1
150	local BANK=$2
151	local OFFSET=$3
152	local PULL=$4
153	local DEVNAME=`configfs_dev_name $DEV`
154	local CHIPNAME=`configfs_chip_name $DEV $BANK`
155	local SYSFSPATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull"
156
157	echo $PULL > $SYSFSPATH || fail "Unable to set line pull in sysfs"
158}
159
160# Load the gpio-sim module. This will pull in configfs if needed too.
161modprobe gpio-sim || skip "unable to load the gpio-sim module"
162# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed.
163for IDX in `seq 5`; do
164	if [ "$IDX" -eq "5" ]; then
165		skip "configfs not mounted at /sys/kernel/config"
166	fi
167
168	mountpoint -q /sys/kernel/config && break
169	sleep 0.1
170done
171# If the module was already loaded: remove all previous chips
172configfs_cleanup
173
174trap "exit 1" SIGTERM SIGINT
175trap configfs_cleanup EXIT
176
177echo "1. chip_name and dev_name attributes"
178
179echo "1.1. Chip name is communicated to user"
180create_chip chip
181create_bank chip bank
182enable_chip chip
183test -n `cat $CONFIGFS_DIR/chip/bank/chip_name` || fail "chip_name doesn't work"
184remove_chip chip
185
186echo "1.2. chip_name returns 'none' if the chip is still pending"
187create_chip chip
188create_bank chip bank
189test "`cat $CONFIGFS_DIR/chip/bank/chip_name`" = "none" || \
190	fail "chip_name doesn't return 'none' for a pending chip"
191remove_chip chip
192
193echo "1.3. Device name is communicated to user"
194create_chip chip
195create_bank chip bank
196enable_chip chip
197test -n `cat $CONFIGFS_DIR/chip/dev_name` || fail "dev_name doesn't work"
198remove_chip chip
199
200echo "2. Creating and configuring simulated chips"
201
202echo "2.1. Default number of lines is 1"
203create_chip chip
204create_bank chip bank
205enable_chip chip
206test "`get_chip_num_lines chip bank`" = "1" || fail "default number of lines is not 1"
207remove_chip chip
208
209echo "2.2. Number of lines can be specified"
210create_chip chip
211create_bank chip bank
212set_num_lines chip bank 16
213enable_chip chip
214test "`get_chip_num_lines chip bank`" = "16" || fail "number of lines is not 16"
215remove_chip chip
216
217echo "2.3. Label can be set"
218create_chip chip
219create_bank chip bank
220set_label chip bank foobar
221enable_chip chip
222test "`get_chip_label chip bank`" = "foobar" || fail "label is incorrect"
223remove_chip chip
224
225echo "2.4. Label can be left empty"
226create_chip chip
227create_bank chip bank
228enable_chip chip
229test -z "`cat $CONFIGFS_DIR/chip/bank/label`" || fail "label is not empty"
230remove_chip chip
231
232echo "2.5. Line names can be configured"
233create_chip chip
234create_bank chip bank
235set_num_lines chip bank 16
236set_line_name chip bank 0 foo
237set_line_name chip bank 2 bar
238enable_chip chip
239test "`get_line_name chip bank 0`" = "foo" || fail "line name is incorrect"
240test "`get_line_name chip bank 2`" = "bar" || fail "line name is incorrect"
241remove_chip chip
242
243echo "2.6. Line config can remain unused if offset is greater than number of lines"
244create_chip chip
245create_bank chip bank
246set_num_lines chip bank 2
247set_line_name chip bank 5 foobar
248enable_chip chip
249test "`get_line_name chip bank 0`" = "" || fail "line name is incorrect"
250test "`get_line_name chip bank 1`" = "" || fail "line name is incorrect"
251remove_chip chip
252
253echo "2.7. Line configfs directory names are sanitized"
254create_chip chip
255create_bank chip bank
256mkdir $CONFIGFS_DIR/chip/bank/line12foobar 2> /dev/null && \
257	fail "invalid configfs line name accepted"
258mkdir $CONFIGFS_DIR/chip/bank/line_no_offset 2> /dev/null && \
259	fail "invalid configfs line name accepted"
260remove_chip chip
261
262echo "2.8. Multiple chips can be created"
263CHIPS="chip0 chip1 chip2"
264for CHIP in $CHIPS; do
265	create_chip $CHIP
266	create_bank $CHIP bank
267	enable_chip $CHIP
268done
269for CHIP in $CHIPS; do
270	remove_chip $CHIP
271done
272
273echo "2.9. Can't modify settings when chip is live"
274create_chip chip
275create_bank chip bank
276enable_chip chip
277echo foobar > $CONFIGFS_DIR/chip/bank/label 2> /dev/null && \
278	fail "Setting label of a live chip should fail"
279echo 8 > $CONFIGFS_DIR/chip/bank/num_lines 2> /dev/null && \
280	fail "Setting number of lines of a live chip should fail"
281remove_chip chip
282
283echo "2.10. Can't create line items when chip is live"
284create_chip chip
285create_bank chip bank
286enable_chip chip
287mkdir $CONFIGFS_DIR/chip/bank/line0 2> /dev/null && fail "Creating line item should fail"
288remove_chip chip
289
290echo "2.11. Probe errors are propagated to user-space"
291create_chip chip
292create_bank chip bank
293set_num_lines chip bank 99999
294echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Probe error was not propagated"
295remove_chip chip
296
297echo "2.12. Cannot enable a chip without any GPIO banks"
298create_chip chip
299echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Chip enabled without any GPIO banks"
300remove_chip chip
301
302echo "2.13. Duplicate chip labels are not allowed"
303create_chip chip
304create_bank chip bank0
305set_label chip bank0 foobar
306create_bank chip bank1
307set_label chip bank1 foobar
308echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Duplicate chip labels were not rejected"
309remove_chip chip
310
311echo "2.14. Lines can be hogged"
312create_chip chip
313create_bank chip bank
314set_num_lines chip bank 8
315mkdir -p $CONFIGFS_DIR/chip/bank/line4/hog
316enable_chip chip
317$BASE_DIR/gpio-mockup-cdev -s 1 /dev/`configfs_chip_name chip bank` 4 2> /dev/null && \
318	fail "Setting the value of a hogged line shouldn't succeed"
319remove_chip chip
320
321echo "3. Controlling simulated chips"
322
323echo "3.1. Pull can be set over sysfs"
324create_chip chip
325create_bank chip bank
326set_num_lines chip bank 8
327enable_chip chip
328sysfs_set_pull chip bank 0 pull-up
329$BASE_DIR/gpio-mockup-cdev /dev/`configfs_chip_name chip bank` 0
330test "$?" = "1" || fail "pull set incorrectly"
331sysfs_set_pull chip bank 0 pull-down
332$BASE_DIR/gpio-mockup-cdev /dev/`configfs_chip_name chip bank` 1
333test "$?" = "0" || fail "pull set incorrectly"
334remove_chip chip
335
336echo "3.2. Pull can be read from sysfs"
337create_chip chip
338create_bank chip bank
339set_num_lines chip bank 8
340enable_chip chip
341DEVNAME=`configfs_dev_name chip`
342CHIPNAME=`configfs_chip_name chip bank`
343SYSFS_PATH=/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/pull
344test `cat $SYSFS_PATH` = "pull-down" || fail "reading the pull failed"
345sysfs_set_pull chip bank 0 pull-up
346test `cat $SYSFS_PATH` = "pull-up" || fail "reading the pull failed"
347remove_chip chip
348
349echo "3.3. Incorrect input in sysfs is rejected"
350create_chip chip
351create_bank chip bank
352set_num_lines chip bank 8
353enable_chip chip
354DEVNAME=`configfs_dev_name chip`
355CHIPNAME=`configfs_chip_name chip bank`
356SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/pull"
357echo foobar > $SYSFS_PATH 2> /dev/null && fail "invalid input not detected"
358remove_chip chip
359
360echo "3.4. Can't write to value"
361create_chip chip
362create_bank chip bank
363enable_chip chip
364DEVNAME=`configfs_dev_name chip`
365CHIPNAME=`configfs_chip_name chip bank`
366SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/value"
367echo 1 > $SYSFS_PATH 2> /dev/null && fail "writing to 'value' succeeded unexpectedly"
368remove_chip chip
369
370echo "4. Simulated GPIO chips are functional"
371
372echo "4.1. Values can be read from sysfs"
373create_chip chip
374create_bank chip bank
375set_num_lines chip bank 8
376enable_chip chip
377DEVNAME=`configfs_dev_name chip`
378CHIPNAME=`configfs_chip_name chip bank`
379SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/value"
380test `cat $SYSFS_PATH` = "0" || fail "incorrect value read from sysfs"
381$BASE_DIR/gpio-mockup-cdev -s 1 /dev/`configfs_chip_name chip bank` 0 &
382sleep 0.1 # FIXME Any better way?
383test `cat $SYSFS_PATH` = "1" || fail "incorrect value read from sysfs"
384kill $!
385remove_chip chip
386
387echo "4.2. Bias settings work correctly"
388create_chip chip
389create_bank chip bank
390set_num_lines chip bank 8
391enable_chip chip
392$BASE_DIR/gpio-mockup-cdev -b pull-up /dev/`configfs_chip_name chip bank` 0
393test `cat $SYSFS_PATH` = "1" || fail "bias setting does not work"
394remove_chip chip
395
396echo "GPIO $MODULE test PASS"
397