Skip to content

Commit 4a679c5

Browse files
nfrapradoshuahkh
authored andcommitted
selftests: Add test to verify power supply properties
Add a kselftest that verifies power supply properties from sysfs and uevent. It checks whether they are present, readable and return valid values. This initial set of properties is not comprehensive, but rather the ones that I was able to validate locally. Co-developed-by: Sebastian Reichel <sebastian.reichel@collabora.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent 2dd0b5a commit 4a679c5

File tree

5 files changed

+298
-0
lines changed

5 files changed

+298
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17524,6 +17524,7 @@ F: Documentation/devicetree/bindings/power/supply/
1752417524
F: drivers/power/supply/
1752517525
F: include/linux/power/
1752617526
F: include/linux/power_supply.h
17527+
F: tools/testing/selftests/power_supply/
1752717528

1752817529
POWERNV OPERATOR PANEL LCD DISPLAY DRIVER
1752917530
M: Suraj Jitindar Singh <sjitindarsingh@gmail.com>

tools/testing/selftests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ TARGETS += nsfs
6767
TARGETS += perf_events
6868
TARGETS += pidfd
6969
TARGETS += pid_namespace
70+
TARGETS += power_supply
7071
TARGETS += powerpc
7172
TARGETS += prctl
7273
TARGETS += proc
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
TEST_PROGS := test_power_supply_properties.sh
2+
TEST_FILES := helpers.sh
3+
4+
include ../lib.mk
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: GPL-2.0
3+
#
4+
# Copyright (c) 2022, 2024 Collabora Ltd
5+
SYSFS_SUPPLIES=/sys/class/power_supply
6+
7+
calc() {
8+
awk "BEGIN { print $* }";
9+
}
10+
11+
test_sysfs_prop() {
12+
PROP="$1"
13+
VALUE="$2" # optional
14+
15+
PROP_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP"
16+
TEST_NAME="$DEVNAME".sysfs."$PROP"
17+
18+
if [ -z "$VALUE" ]; then
19+
ktap_test_result "$TEST_NAME" [ -f "$PROP_PATH" ]
20+
else
21+
ktap_test_result "$TEST_NAME" grep -q "$VALUE" "$PROP_PATH"
22+
fi
23+
}
24+
25+
to_human_readable_unit() {
26+
VALUE="$1"
27+
UNIT="$2"
28+
29+
case "$VALUE" in
30+
*[!0-9]* ) return ;; # Not a number
31+
esac
32+
33+
if [ "$UNIT" = "uA" ]; then
34+
new_unit="mA"
35+
div=1000
36+
elif [ "$UNIT" = "uV" ]; then
37+
new_unit="V"
38+
div=1000000
39+
elif [ "$UNIT" = "uAh" ]; then
40+
new_unit="Ah"
41+
div=1000000
42+
elif [ "$UNIT" = "uW" ]; then
43+
new_unit="mW"
44+
div=1000
45+
elif [ "$UNIT" = "uWh" ]; then
46+
new_unit="Wh"
47+
div=1000000
48+
else
49+
return
50+
fi
51+
52+
value_converted=$(calc "$VALUE"/"$div")
53+
echo "$value_converted" "$new_unit"
54+
}
55+
56+
_check_sysfs_prop_available() {
57+
PROP=$1
58+
59+
PROP_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP"
60+
TEST_NAME="$DEVNAME".sysfs."$PROP"
61+
62+
if [ ! -e "$PROP_PATH" ] ; then
63+
ktap_test_skip "$TEST_NAME"
64+
return 1
65+
fi
66+
67+
if ! cat "$PROP_PATH" >/dev/null; then
68+
ktap_print_msg "Failed to read"
69+
ktap_test_fail "$TEST_NAME"
70+
return 1
71+
fi
72+
73+
return 0
74+
}
75+
76+
test_sysfs_prop_optional() {
77+
PROP=$1
78+
UNIT=$2 # optional
79+
80+
TEST_NAME="$DEVNAME".sysfs."$PROP"
81+
82+
_check_sysfs_prop_available "$PROP" || return
83+
DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP")
84+
85+
ktap_print_msg "Reported: '$DATA' $UNIT ($(to_human_readable_unit "$DATA" "$UNIT"))"
86+
ktap_test_pass "$TEST_NAME"
87+
}
88+
89+
test_sysfs_prop_optional_range() {
90+
PROP=$1
91+
MIN=$2
92+
MAX=$3
93+
UNIT=$4 # optional
94+
95+
TEST_NAME="$DEVNAME".sysfs."$PROP"
96+
97+
_check_sysfs_prop_available "$PROP" || return
98+
DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP")
99+
100+
if [ "$DATA" -lt "$MIN" ] || [ "$DATA" -gt "$MAX" ]; then
101+
ktap_print_msg "'$DATA' is out of range (min=$MIN, max=$MAX)"
102+
ktap_test_fail "$TEST_NAME"
103+
else
104+
ktap_print_msg "Reported: '$DATA' $UNIT ($(to_human_readable_unit "$DATA" "$UNIT"))"
105+
ktap_test_pass "$TEST_NAME"
106+
fi
107+
}
108+
109+
test_sysfs_prop_optional_list() {
110+
PROP=$1
111+
LIST=$2
112+
113+
TEST_NAME="$DEVNAME".sysfs."$PROP"
114+
115+
_check_sysfs_prop_available "$PROP" || return
116+
DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP")
117+
118+
valid=0
119+
120+
OLDIFS=$IFS
121+
IFS=","
122+
for item in $LIST; do
123+
if [ "$DATA" = "$item" ]; then
124+
valid=1
125+
break
126+
fi
127+
done
128+
if [ "$valid" -eq 1 ]; then
129+
ktap_print_msg "Reported: '$DATA'"
130+
ktap_test_pass "$TEST_NAME"
131+
else
132+
ktap_print_msg "'$DATA' is not a valid value for this property"
133+
ktap_test_fail "$TEST_NAME"
134+
fi
135+
IFS=$OLDIFS
136+
}
137+
138+
dump_file() {
139+
FILE="$1"
140+
while read -r line; do
141+
ktap_print_msg "$line"
142+
done < "$FILE"
143+
}
144+
145+
__test_uevent_prop() {
146+
PROP="$1"
147+
OPTIONAL="$2"
148+
VALUE="$3" # optional
149+
150+
UEVENT_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/uevent
151+
TEST_NAME="$DEVNAME".uevent."$PROP"
152+
153+
if ! grep -q "POWER_SUPPLY_$PROP=" "$UEVENT_PATH"; then
154+
if [ "$OPTIONAL" -eq 1 ]; then
155+
ktap_test_skip "$TEST_NAME"
156+
else
157+
ktap_print_msg "Missing property"
158+
ktap_test_fail "$TEST_NAME"
159+
fi
160+
return
161+
fi
162+
163+
if ! grep -q "POWER_SUPPLY_$PROP=$VALUE" "$UEVENT_PATH"; then
164+
ktap_print_msg "Invalid value for uevent property, dumping..."
165+
dump_file "$UEVENT_PATH"
166+
ktap_test_fail "$TEST_NAME"
167+
else
168+
ktap_test_pass "$TEST_NAME"
169+
fi
170+
}
171+
172+
test_uevent_prop() {
173+
__test_uevent_prop "$1" 0 "$2"
174+
}
175+
176+
test_uevent_prop_optional() {
177+
__test_uevent_prop "$1" 1 "$2"
178+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: GPL-2.0
3+
#
4+
# Copyright (c) 2022, 2024 Collabora Ltd
5+
#
6+
# This test validates the power supply uAPI: namely, the files in sysfs and
7+
# lines in uevent that expose the power supply properties.
8+
#
9+
# By default all power supplies available are tested. Optionally the name of a
10+
# power supply can be passed as a parameter to test only that one instead.
11+
DIR="$(dirname "$(readlink -f "$0")")"
12+
13+
. "${DIR}"/../kselftest/ktap_helpers.sh
14+
15+
. "${DIR}"/helpers.sh
16+
17+
count_tests() {
18+
SUPPLIES=$1
19+
20+
# This needs to be updated every time a new test is added.
21+
NUM_TESTS=33
22+
23+
total_tests=0
24+
25+
for i in $SUPPLIES; do
26+
total_tests=$(("$total_tests" + "$NUM_TESTS"))
27+
done
28+
29+
echo "$total_tests"
30+
}
31+
32+
ktap_print_header
33+
34+
SYSFS_SUPPLIES=/sys/class/power_supply/
35+
36+
if [ $# -eq 0 ]; then
37+
supplies=$(ls "$SYSFS_SUPPLIES")
38+
else
39+
supplies=$1
40+
fi
41+
42+
ktap_set_plan "$(count_tests "$supplies")"
43+
44+
for DEVNAME in $supplies; do
45+
ktap_print_msg Testing device "$DEVNAME"
46+
47+
if [ ! -d "$SYSFS_SUPPLIES"/"$DEVNAME" ]; then
48+
ktap_test_fail "$DEVNAME".exists
49+
ktap_exit_fail_msg Device does not exist
50+
fi
51+
52+
ktap_test_pass "$DEVNAME".exists
53+
54+
test_uevent_prop NAME "$DEVNAME"
55+
56+
test_sysfs_prop type
57+
SUPPLY_TYPE=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/type)
58+
# This fails on kernels < 5.8 (needs 2ad3d74e3c69f)
59+
test_uevent_prop TYPE "$SUPPLY_TYPE"
60+
61+
test_sysfs_prop_optional usb_type
62+
63+
test_sysfs_prop_optional_range online 0 2
64+
test_sysfs_prop_optional_range present 0 1
65+
66+
test_sysfs_prop_optional_list status "Unknown","Charging","Discharging","Not charging","Full"
67+
68+
# Capacity is reported as percentage, thus any value less than 0 and
69+
# greater than 100 are not allowed.
70+
test_sysfs_prop_optional_range capacity 0 100 "%"
71+
72+
test_sysfs_prop_optional_list capacity_level "Unknown","Critical","Low","Normal","High","Full"
73+
74+
test_sysfs_prop_optional model_name
75+
test_sysfs_prop_optional manufacturer
76+
test_sysfs_prop_optional serial_number
77+
test_sysfs_prop_optional_list technology "Unknown","NiMH","Li-ion","Li-poly","LiFe","NiCd","LiMn"
78+
79+
test_sysfs_prop_optional cycle_count
80+
81+
test_sysfs_prop_optional_list scope "Unknown","System","Device"
82+
83+
test_sysfs_prop_optional input_current_limit "uA"
84+
test_sysfs_prop_optional input_voltage_limit "uV"
85+
86+
# Technically the power-supply class does not limit reported values.
87+
# E.g. one could expose an RTC backup-battery, which goes below 1.5V or
88+
# an electric vehicle battery with over 300V. But most devices do not
89+
# have a step-up capable regulator behind the battery and operate with
90+
# voltages considered safe to touch, so we limit the allowed range to
91+
# 1.8V-60V to catch drivers reporting incorrectly scaled values. E.g. a
92+
# common mistake is reporting data in mV instead of µV.
93+
test_sysfs_prop_optional_range voltage_now 1800000 60000000 "uV"
94+
test_sysfs_prop_optional_range voltage_min 1800000 60000000 "uV"
95+
test_sysfs_prop_optional_range voltage_max 1800000 60000000 "uV"
96+
test_sysfs_prop_optional_range voltage_min_design 1800000 60000000 "uV"
97+
test_sysfs_prop_optional_range voltage_max_design 1800000 60000000 "uV"
98+
99+
# current based systems
100+
test_sysfs_prop_optional current_now "uA"
101+
test_sysfs_prop_optional current_max "uA"
102+
test_sysfs_prop_optional charge_now "uAh"
103+
test_sysfs_prop_optional charge_full "uAh"
104+
test_sysfs_prop_optional charge_full_design "uAh"
105+
106+
# power based systems
107+
test_sysfs_prop_optional power_now "uW"
108+
test_sysfs_prop_optional energy_now "uWh"
109+
test_sysfs_prop_optional energy_full "uWh"
110+
test_sysfs_prop_optional energy_full_design "uWh"
111+
test_sysfs_prop_optional energy_full_design "uWh"
112+
done
113+
114+
ktap_finished

0 commit comments

Comments
 (0)