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