2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2020, Google LLC. All rights reserved.
4 # Author: Saravana Kannan <saravanak@google.com>
8 Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
10 This script needs to be run on the target device once it has booted to a
13 The 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
15 parents) of these devices. It does a breadth first search of the dependency
16 chain, so the last entry in the output is close to the root of the
19 By default it lists the full path to the devices under /sys/devices.
21 It also takes an optional modifier flag as the first parameter to change
22 what information is listed in the output. If the requested information is
23 not available, the device name is printed.
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
32 The 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.
40 --exclude-devlinks Don't follow device links when tracking probe
43 --exclude-parents Don't follow parent devices when tracking probe
49 function dev_to_detail() {
51 while [ $i -lt ${#OUT_LIST[@]} ]
53 local C=${OUT_LIST[i]}
54 local S=${OUT_LIST[i+1]}
55 local D="'$(detail_chosen $C $S)'"
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 \'
66 function already_seen() {
68 while [ $i -lt ${#OUT_LIST[@]} ]
70 if [ "$1" = "${OUT_LIST[$i]}" ]
72 # if-statement treats 0 (no-error) as true
78 # if-statement treats 1 (error) as false
82 # Return 0 (no-error/true) if parent was added
83 function add_parent() {
85 if [ ${ALLOW_PARENTS} -eq 0 ]
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/..)
95 while [ ! -e ${PARENT}/driver ]
97 if [ "$PARENT" = "/sys/devices" ]
101 PARENT=$(realpath $PARENT/..)
105 OUT_LIST+=(${CON} ${PARENT})
109 # Return 0 (no-error/true) if one or more suppliers were added
110 function add_suppliers() {
114 if [ ${ALLOW_DEVLINKS} -eq 0 ]
119 SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null)
120 for SL in $SUPPLIER_LINKS;
122 SYNC_STATE=$(cat $SL/sync_state_only)
124 # sync_state_only links are proxy dependencies.
125 # They can also have cycles. So, don't follow them.
126 if [ "$SYNC_STATE" != '0' ]
131 SUPPLIER=$(realpath $SL/supplier)
133 if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
138 CONSUMERS+=($SUPPLIER)
139 OUT_LIST+=(${CON} ${SUPPLIER})
146 function detail_compat() {
147 f=$1/of_node/compatible
156 function detail_module() {
160 echo -n $(basename $(realpath $f))
166 function detail_driver() {
170 echo -n $(basename $(realpath $f))
176 function detail_fwnode() {
185 echo -n $(realpath $f)
191 function detail_graphviz() {
192 if [ "$2" != "ROOT" ]
194 echo -n "\"$(basename $2)\"->\"$(basename $1)\""
196 echo -n "\"$(basename $1)\""
200 function detail_tsort() {
201 echo -n "\"$2\" \"$1\""
204 function detail_device() { echo -n $1; }
206 alias detail=detail_device
220 alias detail=detail_compat
223 alias detail=detail_module
226 alias detail=detail_driver
229 alias detail=detail_fwnode
232 alias detail=detail_graphviz
235 alias detail=detail_tsort
247 # Stop at the first argument that's not an option.
254 function detail_chosen() {
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.
270 while [ $i -lt ${#CONSUMERS[@]} ]
272 CONSUMER=$(realpath ${CONSUMERS[$i]})
275 if already_seen ${CONSUMER}
280 # If this is not a device with a driver, we don't care about its
282 if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
289 # Add suppliers to CONSUMERS list and output the consumer details.
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}
300 if add_parent ${CONSUMER}
307 OUT_LIST+=(${CONSUMER} "ROOT")
311 # Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
312 # isn't really stable.
313 dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-