xref: /linux/tools/testing/selftests/gpio/gpio-aggregator.sh (revision aacc73ceeb8bf664426f0e53db2778a59325bd9f)
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3# Copyright (C) 2025 Bartosz Golaszewski <brgl@bgdev.pl>
4# Copyright (C) 2025 Koichiro Den <koichiro.den@canonical.com>
5
6BASE_DIR=$(dirname "$0")
7CONFIGFS_SIM_DIR="/sys/kernel/config/gpio-sim"
8CONFIGFS_AGG_DIR="/sys/kernel/config/gpio-aggregator"
9SYSFS_AGG_DIR="/sys/bus/platform/drivers/gpio-aggregator"
10MODULE="gpio-aggregator"
11
12fail() {
13	echo "$*" >&2
14	echo "GPIO $MODULE test FAIL"
15	exit 1
16}
17
18skip() {
19	echo "$*" >&2
20	echo "GPIO $MODULE test SKIP"
21	exit 4
22}
23
24# gpio-sim
25sim_enable_chip() {
26	local CHIP=$1
27
28	echo 1 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to enable the chip"
29}
30
31sim_disable_chip() {
32	local CHIP=$1
33
34	echo 0 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to disable the chip"
35}
36
37sim_configfs_cleanup() {
38	local NOCHECK=${1:-0}
39
40	for CHIP_DIR in "$CONFIGFS_SIM_DIR"/*; do
41		[ -d "$CHIP_DIR" ] || continue
42		echo 0 > "$CHIP_DIR/live"
43		find "$CHIP_DIR" -depth -type d -exec rmdir {} \;
44	done
45	[ "$NOCHECK" -eq 1 ] && return;
46	remaining=$(find "$CONFIGFS_SIM_DIR" -mindepth 1 -type d 2> /dev/null)
47	if [ -n "$remaining" ]; then
48		fail "Directories remain in $CONFIGFS_SIM_DIR: $remaining"
49	fi
50}
51
52sim_get_chip_label() {
53	local CHIP=$1
54	local BANK=$2
55	local CHIP_NAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name" 2> /dev/null) || \
56		fail "Unable to read the chip name from configfs"
57
58	$BASE_DIR/gpio-chip-info "/dev/$CHIP_NAME" label || \
59		fail "Unable to read the chip label from the character device"
60}
61
62# gpio-aggregator
63agg_create_chip() {
64	local CHIP=$1
65
66	mkdir "$CONFIGFS_AGG_DIR/$CHIP"
67}
68
69agg_remove_chip() {
70	local CHIP=$1
71
72	find "$CONFIGFS_AGG_DIR/$CHIP/" -depth -type d -exec rmdir {} \; || \
73		fail "Unable to remove $CONFIGFS_AGG_DIR/$CHIP"
74}
75
76agg_create_line() {
77	local CHIP=$1
78	local LINE=$2
79
80	mkdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE"
81}
82
83agg_remove_line() {
84	local CHIP=$1
85	local LINE=$2
86
87	rmdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE"
88}
89
90agg_set_key() {
91	local CHIP=$1
92	local LINE=$2
93	local KEY=$3
94
95	echo "$KEY" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/key" || fail "Unable to set the lookup key"
96}
97
98agg_set_offset() {
99	local CHIP=$1
100	local LINE=$2
101	local OFFSET=$3
102
103	echo "$OFFSET" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/offset" || \
104		fail "Unable to set the lookup offset"
105}
106
107agg_set_line_name() {
108	local CHIP=$1
109	local LINE=$2
110	local NAME=$3
111
112	echo "$NAME" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/name" || fail "Unable to set the line name"
113}
114
115agg_enable_chip() {
116	local CHIP=$1
117
118	echo 1 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to enable the chip"
119}
120
121agg_disable_chip() {
122	local CHIP=$1
123
124	echo 0 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to disable the chip"
125}
126
127agg_configfs_cleanup() {
128	local NOCHECK=${1:-0}
129
130	for CHIP_DIR in "$CONFIGFS_AGG_DIR"/*; do
131		[ -d "$CHIP_DIR" ] || continue
132		echo 0 > "$CHIP_DIR/live" 2> /dev/null
133		find "$CHIP_DIR" -depth -type d -exec rmdir {} \;
134	done
135	[ "$NOCHECK" -eq 1 ] && return;
136	remaining=$(find "$CONFIGFS_AGG_DIR" -mindepth 1 -type d 2> /dev/null)
137	if [ -n "$remaining" ]; then
138		fail "Directories remain in $CONFIGFS_AGG_DIR: $remaining"
139	fi
140}
141
142agg_configfs_dev_name() {
143	local CHIP=$1
144
145	cat "$CONFIGFS_AGG_DIR/$CHIP/dev_name" 2> /dev/null || \
146		fail "Unable to read the device name from configfs"
147}
148
149agg_configfs_chip_name() {
150	local CHIP=$1
151	local DEV_NAME=$(agg_configfs_dev_name "$CHIP")
152	local CHIP_LIST=$(find "/sys/devices/platform/$DEV_NAME" \
153		-maxdepth 1 -type d -name "gpiochip[0-9]*" 2> /dev/null)
154	local CHIP_COUNT=$(echo "$CHIP_LIST" | wc -l)
155
156	if [ -z "$CHIP_LIST" ]; then
157		fail "No gpiochip in /sys/devices/platform/$DEV_NAME/"
158	elif [ "$CHIP_COUNT" -ne 1 ]; then
159		fail "Multiple gpiochips unexpectedly found: $CHIP_LIST"
160	fi
161	basename "$CHIP_LIST"
162}
163
164agg_get_chip_num_lines() {
165	local CHIP=$1
166	local N_DIR=$(ls -d $CONFIGFS_AGG_DIR/$CHIP/line[0-9]* 2> /dev/null | wc -l)
167	local N_LINES
168
169	if [ "$(cat $CONFIGFS_AGG_DIR/$CHIP/live)" = 0 ]; then
170		echo "$N_DIR"
171	else
172		N_LINES=$(
173			$BASE_DIR/gpio-chip-info \
174				"/dev/$(agg_configfs_chip_name "$CHIP")" num-lines
175		) || fail "Unable to read the number of lines from the character device"
176		if [ $N_DIR != $N_LINES ]; then
177			fail "Discrepancy between two sources for the number of lines"
178		fi
179		echo "$N_LINES"
180	fi
181}
182
183agg_get_chip_label() {
184	local CHIP=$1
185
186	$BASE_DIR/gpio-chip-info "/dev/$(agg_configfs_chip_name "$CHIP")" label || \
187		fail "Unable to read the chip label from the character device"
188}
189
190agg_get_line_name() {
191	local CHIP=$1
192	local OFFSET=$2
193	local NAME_CONFIGFS=$(cat "$CONFIGFS_AGG_DIR/$CHIP/line${OFFSET}/name")
194	local NAME_CDEV
195
196	if [ "$(cat "$CONFIGFS_AGG_DIR/$CHIP/live")" = 0 ]; then
197		echo "$NAME_CONFIGFS"
198	else
199		NAME_CDEV=$(
200			$BASE_DIR/gpio-line-name \
201				"/dev/$(agg_configfs_chip_name "$CHIP")" "$OFFSET"
202		) || fail "Unable to read the line name from the character device"
203		if [ "$NAME_CONFIGFS" != "$NAME_CDEV" ]; then
204			fail "Discrepancy between two sources for the name of line"
205		fi
206		echo "$NAME_CDEV"
207	fi
208}
209
210
211# Load the modules. This will pull in configfs if needed too.
212modprobe gpio-sim || skip "unable to load the gpio-sim module"
213modprobe gpio-aggregator || skip "unable to load the gpio-aggregator module"
214
215# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed.
216for IDX in $(seq 5); do
217	if [ "$IDX" -eq "5" ]; then
218		skip "configfs not mounted at /sys/kernel/config"
219	fi
220
221	mountpoint -q /sys/kernel/config && break
222	sleep 0.1
223done
224
225# If the module was already loaded: remove all previous chips
226agg_configfs_cleanup
227sim_configfs_cleanup
228
229trap "exit 1" SIGTERM SIGINT
230trap "agg_configfs_cleanup 1; sim_configfs_cleanup 1" EXIT
231
232# Use gpio-sim chips as the test backend
233for CHIP in $(seq -f "chip%g" 0 1); do
234	mkdir $CONFIGFS_SIM_DIR/$CHIP
235	for BANK in $(seq -f "bank%g" 0 1); do
236		mkdir -p "$CONFIGFS_SIM_DIR/$CHIP/$BANK"
237		echo "${CHIP}_${BANK}" > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/label" || \
238			fail "unable to set the chip label"
239		echo 16 > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/num_lines" || \
240			fail "unable to set the number of lines"
241		for IDX in $(seq 0 15); do
242			LINE_NAME="${CHIP}${BANK}_${IDX}"
243			LINE_DIR="$CONFIGFS_SIM_DIR/$CHIP/$BANK/line$IDX"
244			mkdir -p $LINE_DIR
245			echo "$LINE_NAME" > "$LINE_DIR/name" || fail "unable to set the line name"
246		done
247	done
248	sim_enable_chip "$CHIP"
249done
250
251echo "1. GPIO aggregator creation/deletion"
252
253echo "1.1. Creation/deletion via configfs"
254
255echo "1.1.1. Minimum creation/deletion"
256agg_create_chip   agg0
257agg_create_line   agg0 line0
258agg_set_key       agg0 line0 "$(sim_get_chip_label chip0 bank0)"
259agg_set_offset    agg0 line0 5
260agg_set_line_name agg0 line0 test0
261agg_enable_chip   agg0
262test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 1 || fail "chip unexpectedly dead"
263test "$(agg_get_chip_label agg0)" = "$(agg_configfs_dev_name agg0)" || \
264	fail "label is inconsistent"
265test "$(agg_get_chip_num_lines agg0)" = "1" || fail "number of lines is not 1"
266test "$(agg_get_line_name agg0 0)" = "test0" || fail "line name is unset"
267agg_disable_chip  agg0
268agg_remove_line   agg0 line0
269agg_remove_chip   agg0
270
271echo "1.1.2. Complex creation/deletion"
272agg_create_chip   agg0
273agg_create_line   agg0 line0
274agg_create_line   agg0 line1
275agg_create_line   agg0 line2
276agg_create_line   agg0 line3
277agg_set_key       agg0 line0 "$(sim_get_chip_label chip0 bank0)"
278agg_set_key       agg0 line1 "$(sim_get_chip_label chip0 bank1)"
279agg_set_key       agg0 line2 "$(sim_get_chip_label chip1 bank0)"
280agg_set_key       agg0 line3 "$(sim_get_chip_label chip1 bank1)"
281agg_set_offset    agg0 line0 1
282agg_set_offset    agg0 line1 3
283agg_set_offset    agg0 line2 5
284agg_set_offset    agg0 line3 7
285agg_set_line_name agg0 line0 test0
286agg_set_line_name agg0 line1 test1
287agg_set_line_name agg0 line2 test2
288agg_set_line_name agg0 line3 test3
289agg_enable_chip   agg0
290test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 1 || fail "chip unexpectedly dead"
291test "$(agg_get_chip_label agg0)" = "$(agg_configfs_dev_name agg0)" || \
292	fail "label is inconsistent"
293test "$(agg_get_chip_num_lines agg0)" = "4" || fail "number of lines is not 1"
294test "$(agg_get_line_name agg0 0)" = "test0" || fail "line name is unset"
295test "$(agg_get_line_name agg0 1)" = "test1" || fail "line name is unset"
296test "$(agg_get_line_name agg0 2)" = "test2" || fail "line name is unset"
297test "$(agg_get_line_name agg0 3)" = "test3" || fail "line name is unset"
298agg_disable_chip  agg0
299agg_remove_line   agg0 line0
300agg_remove_line   agg0 line1
301agg_remove_line   agg0 line2
302agg_remove_line   agg0 line3
303agg_remove_chip   agg0
304
305echo "1.1.3. Can't instantiate a chip without any line"
306agg_create_chip   agg0
307echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpectedly enabled"
308test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly alive"
309agg_remove_chip   agg0
310
311echo "1.1.4. Can't instantiate a chip with invalid configuration"
312agg_create_chip   agg0
313agg_create_line   agg0 line0
314agg_set_key       agg0 line0 "chipX_bankX"
315agg_set_offset    agg0 line0 99
316agg_set_line_name agg0 line0 test0
317echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpectedly enabled"
318test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly alive"
319agg_remove_line   agg0 line0
320agg_remove_chip   agg0
321
322echo "1.1.5. Can't instantiate a chip asynchronously via deferred probe"
323agg_create_chip   agg0
324agg_create_line   agg0 line0
325agg_set_key       agg0 line0 "chip0_bank0"
326agg_set_offset    agg0 line0 5
327agg_set_line_name agg0 line0 test0
328sim_disable_chip  chip0
329echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpectedly enabled"
330test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly alive"
331sim_enable_chip   chip0
332sleep 1
333test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || \
334	fail "chip unexpectedly transitioned to 'live' state"
335agg_remove_line   agg0 line0
336agg_remove_chip   agg0
337
338echo "1.1.6. Can't instantiate a chip with _sysfs prefix"
339mkdir "$CONFIGFS_AGG_DIR/_sysfs" 2> /dev/null && fail "chip _sysfs unexpectedly created"
340mkdir "$CONFIGFS_AGG_DIR/_sysfs.foo" 2> /dev/null && fail "chip _sysfs.foo unexpectedly created"
341
342echo "1.2. Creation/deletion via sysfs"
343
344echo "1.2.1. Minimum creation/deletion"
345echo "chip0_bank0 0" > "$SYSFS_AGG_DIR/new_device"
346CHIPNAME=$(agg_configfs_chip_name _sysfs.0)
347test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly dead"
348test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \
349	fail "label is inconsistent"
350test "$(agg_get_chip_num_lines _sysfs.0)" = "1" || fail "number of lines is not 1"
351test "$(agg_get_line_name _sysfs.0 0)" = "" || fail "line name is unset"
352echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device"
353test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains"
354test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains"
355
356echo "1.2.2. Complex creation/deletion"
357echo "chip0bank0_0 chip1_bank1 10-11" > "$SYSFS_AGG_DIR/new_device"
358CHIPNAME=$(agg_configfs_chip_name _sysfs.0)
359test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly dead"
360test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \
361	fail "label is inconsistent"
362test "$(agg_get_chip_num_lines _sysfs.0)" = "3" || fail "number of lines is not 3"
363test "$(agg_get_line_name _sysfs.0 0)" = "" || fail "line name is unset"
364test "$(agg_get_line_name _sysfs.0 1)" = "" || fail "line name is unset"
365test "$(agg_get_line_name _sysfs.0 2)" = "" || fail "line name is unset"
366echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device"
367test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains"
368test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains"
369
370echo "1.2.3. Asynchronous creation with deferred probe"
371sim_disable_chip  chip0
372echo 'chip0_bank0 0' > $SYSFS_AGG_DIR/new_device
373sleep 1
374test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 0 || fail "chip unexpectedly alive"
375sim_enable_chip  chip0
376sleep 1
377CHIPNAME=$(agg_configfs_chip_name _sysfs.0)
378test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly remains dead"
379test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \
380	fail "label is inconsistent"
381test "$(agg_get_chip_num_lines _sysfs.0)" = "1" || fail "number of lines is not 1"
382test "$(agg_get_line_name _sysfs.0 0)" = "" || fail "line name unexpectedly set"
383echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device"
384test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains"
385test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains"
386
387echo "1.2.4. Can't instantiate a chip with invalid configuration"
388echo "xyz 0" > "$SYSFS_AGG_DIR/new_device"
389test "$(cat $CONFIGFS_AGG_DIR/_sysfs.0/live)" = 0 || fail "chip unexpectedly alive"
390echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device"
391
392echo "2. GPIO aggregator configuration"
393
394echo "2.1. Configuring aggregators instantiated via configfs"
395setup_2_1() {
396	agg_create_chip   agg0
397	agg_create_line   agg0 line0
398	agg_create_line   agg0 line1
399	agg_set_key       agg0 line0 "$(sim_get_chip_label chip0 bank0)"
400	agg_set_key       agg0 line1 "$(sim_get_chip_label chip1 bank0)"
401	agg_set_offset    agg0 line0 1
402	agg_set_offset    agg0 line1 3
403	agg_set_line_name agg0 line0 test0
404	agg_set_line_name agg0 line1 test1
405	agg_enable_chip   agg0
406}
407teardown_2_1() {
408	agg_configfs_cleanup
409}
410
411echo "2.1.1. While offline"
412
413echo "2.1.1.1. Line can be added/removed"
414setup_2_1
415agg_disable_chip  agg0
416agg_create_line   agg0 line2
417agg_set_key       agg0 line2 "$(sim_get_chip_label chip0 bank1)"
418agg_set_offset    agg0 line2 5
419agg_enable_chip   agg0
420test "$(agg_get_chip_num_lines agg0)" = "3" || fail "number of lines is not 1"
421teardown_2_1
422
423echo "2.1.1.2. Line key can be modified"
424setup_2_1
425agg_disable_chip  agg0
426agg_set_key       agg0 line0 "$(sim_get_chip_label chip0 bank1)"
427agg_set_key       agg0 line1 "$(sim_get_chip_label chip1 bank1)"
428agg_enable_chip   agg0
429teardown_2_1
430
431echo "2.1.1.3. Line name can be modified"
432setup_2_1
433agg_disable_chip  agg0
434agg_set_line_name agg0 line0 new0
435agg_set_line_name agg0 line1 new1
436agg_enable_chip   agg0
437test "$(agg_get_line_name agg0 0)" = "new0" || fail "line name is unset"
438test "$(agg_get_line_name agg0 1)" = "new1" || fail "line name is unset"
439teardown_2_1
440
441echo "2.1.1.4. Line offset can be modified"
442setup_2_1
443agg_disable_chip  agg0
444agg_set_offset    agg0 line0 5
445agg_set_offset    agg0 line1 7
446agg_enable_chip   agg0
447teardown_2_1
448
449echo "2.1.1.5. Can re-enable a chip after valid reconfiguration"
450setup_2_1
451agg_disable_chip  agg0
452agg_set_key       agg0 line0 "$(sim_get_chip_label chip1 bank1)"
453agg_set_offset    agg0 line0 15
454agg_set_key       agg0 line1 "$(sim_get_chip_label chip0 bank1)"
455agg_set_offset    agg0 line0 14
456agg_create_line   agg0 line2
457agg_set_key       agg0 line2 "$(sim_get_chip_label chip0 bank1)"
458agg_set_offset    agg0 line2 13
459agg_enable_chip   agg0
460test "$(agg_get_chip_num_lines agg0)" = "3" || fail "number of lines is not 1"
461teardown_2_1
462
463echo "2.1.1.7. Can't re-enable a chip with invalid reconfiguration"
464setup_2_1
465agg_disable_chip  agg0
466agg_set_key       agg0 line0 invalidkey
467echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpectedly enabled"
468teardown_2_1
469setup_2_1
470agg_disable_chip  agg0
471agg_set_offset    agg0 line0 99
472echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpectedly enabled"
473teardown_2_1
474
475echo "2.1.2. While online"
476
477echo "2.1.2.1. Can't add/remove line"
478setup_2_1
479mkdir "$CONFIGFS_AGG_DIR/agg0/line2" 2> /dev/null && fail "line unexpectedly added"
480rmdir "$CONFIGFS_AGG_DIR/agg0/line1" 2> /dev/null && fail "line unexpectedly removed"
481teardown_2_1
482
483echo "2.1.2.2. Can't modify line key"
484setup_2_1
485echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/agg0/line0/key" 2> /dev/null && \
486	fail "lookup key unexpectedly updated"
487teardown_2_1
488
489echo "2.1.2.3. Can't modify line name"
490setup_2_1
491echo "new0" > "$CONFIGFS_AGG_DIR/agg0/line0/name" 2> /dev/null && \
492	fail "name unexpectedly updated"
493teardown_2_1
494
495echo "2.1.2.4. Can't modify line offset"
496setup_2_1
497echo "5" > "$CONFIGFS_AGG_DIR/agg0/line0/offset" 2> /dev/null && \
498	fail "offset unexpectedly updated"
499teardown_2_1
500
501echo "2.2. Configuring aggregators instantiated via sysfs"
502setup_2_2() {
503	echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device"
504}
505teardown_2_2() {
506	echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device"
507}
508
509echo "2.2.1. While online"
510
511echo "2.2.1.1. Can toggle live"
512setup_2_2
513agg_disable_chip  _sysfs.0
514agg_enable_chip   _sysfs.0
515teardown_2_2
516
517echo "2.2.1.2. Can't add/remove line"
518setup_2_2
519mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpectedly added"
520rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly removed"
521teardown_2_2
522
523echo "2.2.1.3. Can't modify line key"
524setup_2_2
525echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null && \
526	fail "lookup key unexpectedly updated"
527teardown_2_2
528
529echo "2.2.1.4. Can't modify line name"
530setup_2_2
531echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \
532	fail "name unexpectedly updated"
533teardown_2_2
534
535echo "2.2.1.5. Can't modify line offset"
536setup_2_2
537echo "5" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \
538	fail "offset unexpectedly updated"
539teardown_2_2
540
541echo "2.2.2. While waiting for deferred probe"
542
543echo "2.2.2.1. Can't add/remove line despite live = 0"
544sim_disable_chip chip0
545setup_2_2
546mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpectedly added"
547rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly removed"
548teardown_2_2
549sim_enable_chip chip0
550
551echo "2.2.2.2. Can't modify line key"
552sim_disable_chip chip0
553setup_2_2
554echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null && \
555	fail "lookup key unexpectedly updated"
556teardown_2_2
557sim_enable_chip chip0
558
559echo "2.2.2.3. Can't modify line name"
560sim_disable_chip chip0
561setup_2_2
562echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \
563	fail "name unexpectedly updated"
564teardown_2_2
565sim_enable_chip chip0
566
567echo "2.2.2.4. Can't modify line offset"
568sim_disable_chip chip0
569setup_2_2
570echo 5 > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \
571	fail "offset unexpectedly updated"
572teardown_2_2
573sim_enable_chip chip0
574
575echo "2.2.2.5. Can't toggle live"
576sim_disable_chip chip0
577setup_2_2
578test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 0 || fail "chip unexpectedly alive"
579echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unexpectedly enabled"
580teardown_2_2
581sim_enable_chip chip0
582
583echo "2.2.3. While offline"
584
585echo "2.2.3.1. Can't add/remove line despite live = 0"
586setup_2_2
587agg_disable_chip _sysfs.0
588mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpectedly added"
589rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly removed"
590teardown_2_2
591
592echo "2.2.3.2. Line key can be modified"
593setup_2_2
594agg_disable_chip  _sysfs.0
595agg_set_key       _sysfs.0 line0 "$(sim_get_chip_label chip0 bank1)"
596agg_set_key       _sysfs.0 line1 "$(sim_get_chip_label chip1 bank1)"
597agg_enable_chip   _sysfs.0
598teardown_2_2
599
600echo "2.2.3.3. Line name can be modified"
601setup_2_2
602agg_disable_chip  _sysfs.0
603agg_set_line_name _sysfs.0 line0 new0
604agg_set_line_name _sysfs.0 line1 new1
605agg_enable_chip   _sysfs.0
606test "$(agg_get_line_name _sysfs.0 0)" = "new0" || fail "line name is unset"
607test "$(agg_get_line_name _sysfs.0 1)" = "new1" || fail "line name is unset"
608teardown_2_2
609
610echo "2.2.3.4. Line offset can be modified"
611setup_2_2
612agg_disable_chip  _sysfs.0
613agg_set_offset    _sysfs.0 line0 5
614agg_set_offset    _sysfs.0 line1 7
615agg_enable_chip   _sysfs.0
616teardown_2_2
617
618echo "2.2.3.5. Can re-enable a chip with valid reconfiguration"
619setup_2_2
620agg_disable_chip  _sysfs.0
621agg_set_key       _sysfs.0 line0 "$(sim_get_chip_label chip1 bank1)"
622agg_set_offset    _sysfs.0 line0 15
623agg_set_key       _sysfs.0 line1 "$(sim_get_chip_label chip0 bank1)"
624agg_set_offset    _sysfs.0 line0 14
625agg_enable_chip   _sysfs.0
626teardown_2_2
627
628echo "2.2.3.6. Can't re-enable a chip with invalid reconfiguration"
629setup_2_2
630agg_disable_chip  _sysfs.0
631agg_set_key       _sysfs.0 line0 invalidkey
632echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unexpectedly enabled"
633teardown_2_2
634setup_2_2
635agg_disable_chip  _sysfs.0
636agg_set_offset    _sysfs.0 line0 99
637echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unexpectedly enabled"
638teardown_2_2
639
640echo "3. Module unload"
641
642echo "3.1. Can't unload module if there is at least one device created via configfs"
643agg_create_chip agg0
644modprobe -r gpio-aggregator 2> /dev/null
645test -d /sys/module/gpio_aggregator || fail "module unexpectedly unloaded"
646agg_remove_chip agg0
647
648echo "3.2. Can unload module if there is no device created via configfs"
649echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device"
650modprobe -r gpio-aggregator 2> /dev/null
651test -d /sys/module/gpio_aggregator && fail "module unexpectedly remains to be loaded"
652modprobe gpio-aggregator 2> /dev/null
653
654echo "4. GPIO forwarder functional"
655SETTINGS="chip0:bank0:2 chip0:bank1:4 chip1:bank0:6 chip1:bank1:8"
656setup_4() {
657	local OFFSET=0
658	agg_create_chip agg0
659	for SETTING in $SETTINGS; do
660		CHIP=$(echo "$SETTING" | cut -d: -f1)
661		BANK=$(echo "$SETTING" | cut -d: -f2)
662		LINE=$(echo "$SETTING" | cut -d: -f3)
663		agg_create_line agg0 "line${OFFSET}"
664		agg_set_key     agg0 "line${OFFSET}" "$(sim_get_chip_label "$CHIP" "$BANK")"
665		agg_set_offset  agg0 "line${OFFSET}" "$LINE"
666		OFFSET=$(expr $OFFSET + 1)
667	done
668	agg_enable_chip agg0
669}
670teardown_4() {
671	agg_configfs_cleanup
672}
673
674echo "4.1. Forwarding set values"
675setup_4
676OFFSET=0
677for SETTING in $SETTINGS; do
678	CHIP=$(echo "$SETTING" | cut -d: -f1)
679	BANK=$(echo "$SETTING" | cut -d: -f2)
680	LINE=$(echo "$SETTING" | cut -d: -f3)
681	DEVNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name")
682	CHIPNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name")
683	VAL_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/value"
684	test $(cat $VAL_PATH) = "0" || fail "incorrect value read from sysfs"
685	$BASE_DIR/gpio-mockup-cdev -s 1 "/dev/$(agg_configfs_chip_name agg0)" "$OFFSET" &
686	mock_pid=$!
687	sleep 0.1 # FIXME Any better way?
688	test "$(cat $VAL_PATH)" = "1" || fail "incorrect value read from sysfs"
689	kill "$mock_pid"
690	OFFSET=$(expr $OFFSET + 1)
691done
692teardown_4
693
694echo "4.2. Forwarding set config"
695setup_4
696OFFSET=0
697for SETTING in $SETTINGS; do
698	CHIP=$(echo "$SETTING" | cut -d: -f1)
699	BANK=$(echo "$SETTING" | cut -d: -f2)
700	LINE=$(echo "$SETTING" | cut -d: -f3)
701	DEVNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name")
702	CHIPNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name")
703	VAL_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/value"
704	$BASE_DIR/gpio-mockup-cdev -b pull-up "/dev/$(agg_configfs_chip_name agg0)" "$OFFSET"
705	test $(cat "$VAL_PATH") = "1" || fail "incorrect value read from sysfs"
706	OFFSET=$(expr $OFFSET + 1)
707done
708teardown_4
709
710echo "5. Race condition verification"
711
712echo "5.1. Stress test of new_device/delete_device and module load/unload"
713for _ in $(seq 1000); do
714	{
715		echo "dummy 0" > "$SYSFS_AGG_DIR/new_device"
716		cat "$CONFIGFS_AGG_DIR/_sysfs.0/dev_name" > "$SYSFS_AGG_DIR/delete_device"
717	} 2> /dev/null
718done &
719writer_pid=$!
720while kill -0 "$writer_pid" 2> /dev/null; do
721	{
722		modprobe gpio-aggregator
723		modprobe -r gpio-aggregator
724	} 2> /dev/null
725done
726
727echo "GPIO $MODULE test PASS"
728