xref: /OK3568_Linux_fs/kernel/scripts/dev-needs.sh (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#! /bin/sh
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
3*4882a593Smuzhiyun# Copyright (c) 2020, Google LLC. All rights reserved.
4*4882a593Smuzhiyun# Author: Saravana Kannan <saravanak@google.com>
5*4882a593Smuzhiyun
6*4882a593Smuzhiyunfunction help() {
7*4882a593Smuzhiyun	cat << EOF
8*4882a593SmuzhiyunUsage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
9*4882a593Smuzhiyun
10*4882a593SmuzhiyunThis script needs to be run on the target device once it has booted to a
11*4882a593Smuzhiyunshell.
12*4882a593Smuzhiyun
13*4882a593SmuzhiyunThe script takes as input a list of one or more device directories under
14*4882a593Smuzhiyun/sys/devices and then lists the probe dependency chain (suppliers and
15*4882a593Smuzhiyunparents) of these devices. It does a breadth first search of the dependency
16*4882a593Smuzhiyunchain, so the last entry in the output is close to the root of the
17*4882a593Smuzhiyundependency chain.
18*4882a593Smuzhiyun
19*4882a593SmuzhiyunBy default it lists the full path to the devices under /sys/devices.
20*4882a593Smuzhiyun
21*4882a593SmuzhiyunIt also takes an optional modifier flag as the first parameter to change
22*4882a593Smuzhiyunwhat information is listed in the output. If the requested information is
23*4882a593Smuzhiyunnot available, the device name is printed.
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun  -c	lists the compatible string of the dependencies
26*4882a593Smuzhiyun  -d	lists the driver name of the dependencies that have probed
27*4882a593Smuzhiyun  -m	lists the module name of the dependencies that have a module
28*4882a593Smuzhiyun  -f	list the firmware node path of the dependencies
29*4882a593Smuzhiyun  -g	list the dependencies as edges and nodes for graphviz
30*4882a593Smuzhiyun  -t	list the dependencies as edges for tsort
31*4882a593Smuzhiyun
32*4882a593SmuzhiyunThe filter options provide a way to filter out some dependencies:
33*4882a593Smuzhiyun  --allow-no-driver	By default dependencies that don't have a driver
34*4882a593Smuzhiyun			attached are ignored. This is to avoid following
35*4882a593Smuzhiyun			device links to "class" devices that are created
36*4882a593Smuzhiyun			when the consumer probes (as in, not a probe
37*4882a593Smuzhiyun			dependency). If you want to follow these links
38*4882a593Smuzhiyun			anyway, use this flag.
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun  --exclude-devlinks	Don't follow device links when tracking probe
41*4882a593Smuzhiyun			dependencies.
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun  --exclude-parents	Don't follow parent devices when tracking probe
44*4882a593Smuzhiyun			dependencies.
45*4882a593Smuzhiyun
46*4882a593SmuzhiyunEOF
47*4882a593Smuzhiyun}
48*4882a593Smuzhiyun
49*4882a593Smuzhiyunfunction dev_to_detail() {
50*4882a593Smuzhiyun	local i=0
51*4882a593Smuzhiyun	while [ $i -lt ${#OUT_LIST[@]} ]
52*4882a593Smuzhiyun	do
53*4882a593Smuzhiyun		local C=${OUT_LIST[i]}
54*4882a593Smuzhiyun		local S=${OUT_LIST[i+1]}
55*4882a593Smuzhiyun		local D="'$(detail_chosen $C $S)'"
56*4882a593Smuzhiyun		if [ ! -z "$D" ]
57*4882a593Smuzhiyun		then
58*4882a593Smuzhiyun			# This weirdness is needed to work with toybox when
59*4882a593Smuzhiyun			# using the -t option.
60*4882a593Smuzhiyun			printf '%05u\t%s\n' ${i} "$D" | tr -d \'
61*4882a593Smuzhiyun		fi
62*4882a593Smuzhiyun		i=$((i+2))
63*4882a593Smuzhiyun	done
64*4882a593Smuzhiyun}
65*4882a593Smuzhiyun
66*4882a593Smuzhiyunfunction already_seen() {
67*4882a593Smuzhiyun	local i=0
68*4882a593Smuzhiyun	while [ $i -lt ${#OUT_LIST[@]} ]
69*4882a593Smuzhiyun	do
70*4882a593Smuzhiyun		if [ "$1" = "${OUT_LIST[$i]}" ]
71*4882a593Smuzhiyun		then
72*4882a593Smuzhiyun			# if-statement treats 0 (no-error) as true
73*4882a593Smuzhiyun			return 0
74*4882a593Smuzhiyun		fi
75*4882a593Smuzhiyun		i=$(($i+2))
76*4882a593Smuzhiyun	done
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun	# if-statement treats 1 (error) as false
79*4882a593Smuzhiyun	return 1
80*4882a593Smuzhiyun}
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun# Return 0 (no-error/true) if parent was added
83*4882a593Smuzhiyunfunction add_parent() {
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun	if [ ${ALLOW_PARENTS} -eq 0 ]
86*4882a593Smuzhiyun	then
87*4882a593Smuzhiyun		return 1
88*4882a593Smuzhiyun	fi
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun	local CON=$1
91*4882a593Smuzhiyun	# $CON could be a symlink path. So, we need to find the real path and
92*4882a593Smuzhiyun	# then go up one level to find the real parent.
93*4882a593Smuzhiyun	local PARENT=$(realpath $CON/..)
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun	while [ ! -e ${PARENT}/driver ]
96*4882a593Smuzhiyun	do
97*4882a593Smuzhiyun		if [ "$PARENT" = "/sys/devices" ]
98*4882a593Smuzhiyun		then
99*4882a593Smuzhiyun			return 1
100*4882a593Smuzhiyun		fi
101*4882a593Smuzhiyun		PARENT=$(realpath $PARENT/..)
102*4882a593Smuzhiyun	done
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun	CONSUMERS+=($PARENT)
105*4882a593Smuzhiyun	OUT_LIST+=(${CON} ${PARENT})
106*4882a593Smuzhiyun	return 0
107*4882a593Smuzhiyun}
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun# Return 0 (no-error/true) if one or more suppliers were added
110*4882a593Smuzhiyunfunction add_suppliers() {
111*4882a593Smuzhiyun	local CON=$1
112*4882a593Smuzhiyun	local RET=1
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun	if [ ${ALLOW_DEVLINKS} -eq 0 ]
115*4882a593Smuzhiyun	then
116*4882a593Smuzhiyun		return 1
117*4882a593Smuzhiyun	fi
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun	SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null)
120*4882a593Smuzhiyun	for SL in $SUPPLIER_LINKS;
121*4882a593Smuzhiyun	do
122*4882a593Smuzhiyun		SYNC_STATE=$(cat $SL/sync_state_only)
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun		# sync_state_only links are proxy dependencies.
125*4882a593Smuzhiyun		# They can also have cycles. So, don't follow them.
126*4882a593Smuzhiyun		if [ "$SYNC_STATE" != '0' ]
127*4882a593Smuzhiyun		then
128*4882a593Smuzhiyun			continue
129*4882a593Smuzhiyun		fi
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun		SUPPLIER=$(realpath $SL/supplier)
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun		if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
134*4882a593Smuzhiyun		then
135*4882a593Smuzhiyun			continue
136*4882a593Smuzhiyun		fi
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun		CONSUMERS+=($SUPPLIER)
139*4882a593Smuzhiyun		OUT_LIST+=(${CON} ${SUPPLIER})
140*4882a593Smuzhiyun		RET=0
141*4882a593Smuzhiyun	done
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun	return $RET
144*4882a593Smuzhiyun}
145*4882a593Smuzhiyun
146*4882a593Smuzhiyunfunction detail_compat() {
147*4882a593Smuzhiyun	f=$1/of_node/compatible
148*4882a593Smuzhiyun	if [ -e $f ]
149*4882a593Smuzhiyun	then
150*4882a593Smuzhiyun		echo -n $(cat $f)
151*4882a593Smuzhiyun	else
152*4882a593Smuzhiyun		echo -n $1
153*4882a593Smuzhiyun	fi
154*4882a593Smuzhiyun}
155*4882a593Smuzhiyun
156*4882a593Smuzhiyunfunction detail_module() {
157*4882a593Smuzhiyun	f=$1/driver/module
158*4882a593Smuzhiyun	if [ -e $f ]
159*4882a593Smuzhiyun	then
160*4882a593Smuzhiyun		echo -n $(basename $(realpath $f))
161*4882a593Smuzhiyun	else
162*4882a593Smuzhiyun		echo -n $1
163*4882a593Smuzhiyun	fi
164*4882a593Smuzhiyun}
165*4882a593Smuzhiyun
166*4882a593Smuzhiyunfunction detail_driver() {
167*4882a593Smuzhiyun	f=$1/driver
168*4882a593Smuzhiyun	if [ -e $f ]
169*4882a593Smuzhiyun	then
170*4882a593Smuzhiyun		echo -n $(basename $(realpath $f))
171*4882a593Smuzhiyun	else
172*4882a593Smuzhiyun		echo -n $1
173*4882a593Smuzhiyun	fi
174*4882a593Smuzhiyun}
175*4882a593Smuzhiyun
176*4882a593Smuzhiyunfunction detail_fwnode() {
177*4882a593Smuzhiyun	f=$1/firmware_node
178*4882a593Smuzhiyun	if [ ! -e $f ]
179*4882a593Smuzhiyun	then
180*4882a593Smuzhiyun		f=$1/of_node
181*4882a593Smuzhiyun	fi
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun	if [ -e $f ]
184*4882a593Smuzhiyun	then
185*4882a593Smuzhiyun		echo -n $(realpath $f)
186*4882a593Smuzhiyun	else
187*4882a593Smuzhiyun		echo -n $1
188*4882a593Smuzhiyun	fi
189*4882a593Smuzhiyun}
190*4882a593Smuzhiyun
191*4882a593Smuzhiyunfunction detail_graphviz() {
192*4882a593Smuzhiyun	if [ "$2" != "ROOT" ]
193*4882a593Smuzhiyun	then
194*4882a593Smuzhiyun		echo -n "\"$(basename $2)\"->\"$(basename $1)\""
195*4882a593Smuzhiyun	else
196*4882a593Smuzhiyun		echo -n "\"$(basename $1)\""
197*4882a593Smuzhiyun	fi
198*4882a593Smuzhiyun}
199*4882a593Smuzhiyun
200*4882a593Smuzhiyunfunction detail_tsort() {
201*4882a593Smuzhiyun	echo -n "\"$2\" \"$1\""
202*4882a593Smuzhiyun}
203*4882a593Smuzhiyun
204*4882a593Smuzhiyunfunction detail_device() { echo -n $1; }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyunalias detail=detail_device
207*4882a593SmuzhiyunALLOW_NO_DRIVER=0
208*4882a593SmuzhiyunALLOW_DEVLINKS=1
209*4882a593SmuzhiyunALLOW_PARENTS=1
210*4882a593Smuzhiyun
211*4882a593Smuzhiyunwhile [ $# -gt 0 ]
212*4882a593Smuzhiyundo
213*4882a593Smuzhiyun	ARG=$1
214*4882a593Smuzhiyun	case $ARG in
215*4882a593Smuzhiyun		--help)
216*4882a593Smuzhiyun			help
217*4882a593Smuzhiyun			exit 0
218*4882a593Smuzhiyun			;;
219*4882a593Smuzhiyun		-c)
220*4882a593Smuzhiyun			alias detail=detail_compat
221*4882a593Smuzhiyun			;;
222*4882a593Smuzhiyun		-m)
223*4882a593Smuzhiyun			alias detail=detail_module
224*4882a593Smuzhiyun			;;
225*4882a593Smuzhiyun		-d)
226*4882a593Smuzhiyun			alias detail=detail_driver
227*4882a593Smuzhiyun			;;
228*4882a593Smuzhiyun		-f)
229*4882a593Smuzhiyun			alias detail=detail_fwnode
230*4882a593Smuzhiyun			;;
231*4882a593Smuzhiyun		-g)
232*4882a593Smuzhiyun			alias detail=detail_graphviz
233*4882a593Smuzhiyun			;;
234*4882a593Smuzhiyun		-t)
235*4882a593Smuzhiyun			alias detail=detail_tsort
236*4882a593Smuzhiyun			;;
237*4882a593Smuzhiyun		--allow-no-driver)
238*4882a593Smuzhiyun			ALLOW_NO_DRIVER=1
239*4882a593Smuzhiyun			;;
240*4882a593Smuzhiyun		--exclude-devlinks)
241*4882a593Smuzhiyun			ALLOW_DEVLINKS=0
242*4882a593Smuzhiyun			;;
243*4882a593Smuzhiyun		--exclude-parents)
244*4882a593Smuzhiyun			ALLOW_PARENTS=0
245*4882a593Smuzhiyun			;;
246*4882a593Smuzhiyun		*)
247*4882a593Smuzhiyun			# Stop at the first argument that's not an option.
248*4882a593Smuzhiyun			break
249*4882a593Smuzhiyun			;;
250*4882a593Smuzhiyun	esac
251*4882a593Smuzhiyun	shift
252*4882a593Smuzhiyundone
253*4882a593Smuzhiyun
254*4882a593Smuzhiyunfunction detail_chosen() {
255*4882a593Smuzhiyun	detail $1 $2
256*4882a593Smuzhiyun}
257*4882a593Smuzhiyun
258*4882a593Smuzhiyunif [ $# -eq 0 ]
259*4882a593Smuzhiyunthen
260*4882a593Smuzhiyun	help
261*4882a593Smuzhiyun	exit 1
262*4882a593Smuzhiyunfi
263*4882a593Smuzhiyun
264*4882a593SmuzhiyunCONSUMERS=($@)
265*4882a593SmuzhiyunOUT_LIST=()
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun# Do a breadth first, non-recursive tracking of suppliers. The parent is also
268*4882a593Smuzhiyun# considered a "supplier" as a device can't probe without its parent.
269*4882a593Smuzhiyuni=0
270*4882a593Smuzhiyunwhile [ $i -lt ${#CONSUMERS[@]} ]
271*4882a593Smuzhiyundo
272*4882a593Smuzhiyun	CONSUMER=$(realpath ${CONSUMERS[$i]})
273*4882a593Smuzhiyun	i=$(($i+1))
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun	if already_seen ${CONSUMER}
276*4882a593Smuzhiyun	then
277*4882a593Smuzhiyun		continue
278*4882a593Smuzhiyun	fi
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun	# If this is not a device with a driver, we don't care about its
281*4882a593Smuzhiyun	# suppliers.
282*4882a593Smuzhiyun	if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
283*4882a593Smuzhiyun	then
284*4882a593Smuzhiyun		continue
285*4882a593Smuzhiyun	fi
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun	ROOT=1
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun	# Add suppliers to CONSUMERS list and output the consumer details.
290*4882a593Smuzhiyun	#
291*4882a593Smuzhiyun	# We don't need to worry about a cycle in the dependency chain causing
292*4882a593Smuzhiyun	# infinite loops. That's because the kernel doesn't allow cycles in
293*4882a593Smuzhiyun	# device links unless it's a sync_state_only device link. And we ignore
294*4882a593Smuzhiyun	# sync_state_only device links inside add_suppliers.
295*4882a593Smuzhiyun	if add_suppliers ${CONSUMER}
296*4882a593Smuzhiyun	then
297*4882a593Smuzhiyun		ROOT=0
298*4882a593Smuzhiyun	fi
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun	if add_parent ${CONSUMER}
301*4882a593Smuzhiyun	then
302*4882a593Smuzhiyun		ROOT=0
303*4882a593Smuzhiyun	fi
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun	if [ $ROOT -eq 1 ]
306*4882a593Smuzhiyun	then
307*4882a593Smuzhiyun		OUT_LIST+=(${CONSUMER} "ROOT")
308*4882a593Smuzhiyun	fi
309*4882a593Smuzhiyundone
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun# Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
312*4882a593Smuzhiyun# isn't really stable.
313*4882a593Smuzhiyundev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-
314*4882a593Smuzhiyun
315*4882a593Smuzhiyunexit 0
316