|
@@ -0,0 +1,315 @@
|
|
|
+#! /bin/sh
|
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
|
+# Copyright (c) 2020, Google LLC. All rights reserved.
|
|
|
+# Author: Saravana Kannan <[email protected]>
|
|
|
+
|
|
|
+function help() {
|
|
|
+ cat << EOF
|
|
|
+Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
|
|
|
+
|
|
|
+This script needs to be run on the target device once it has booted to a
|
|
|
+shell.
|
|
|
+
|
|
|
+The script takes as input a list of one or more device directories under
|
|
|
+/sys/devices and then lists the probe dependency chain (suppliers and
|
|
|
+parents) of these devices. It does a breadth first search of the dependency
|
|
|
+chain, so the last entry in the output is close to the root of the
|
|
|
+dependency chain.
|
|
|
+
|
|
|
+By default it lists the full path to the devices under /sys/devices.
|
|
|
+
|
|
|
+It also takes an optional modifier flag as the first parameter to change
|
|
|
+what information is listed in the output. If the requested information is
|
|
|
+not available, the device name is printed.
|
|
|
+
|
|
|
+ -c lists the compatible string of the dependencies
|
|
|
+ -d lists the driver name of the dependencies that have probed
|
|
|
+ -m lists the module name of the dependencies that have a module
|
|
|
+ -f list the firmware node path of the dependencies
|
|
|
+ -g list the dependencies as edges and nodes for graphviz
|
|
|
+ -t list the dependencies as edges for tsort
|
|
|
+
|
|
|
+The filter options provide a way to filter out some dependencies:
|
|
|
+ --allow-no-driver By default dependencies that don't have a driver
|
|
|
+ attached are ignored. This is to avoid following
|
|
|
+ device links to "class" devices that are created
|
|
|
+ when the consumer probes (as in, not a probe
|
|
|
+ dependency). If you want to follow these links
|
|
|
+ anyway, use this flag.
|
|
|
+
|
|
|
+ --exclude-devlinks Don't follow device links when tracking probe
|
|
|
+ dependencies.
|
|
|
+
|
|
|
+ --exclude-parents Don't follow parent devices when tracking probe
|
|
|
+ dependencies.
|
|
|
+
|
|
|
+EOF
|
|
|
+}
|
|
|
+
|
|
|
+function dev_to_detail() {
|
|
|
+ local i=0
|
|
|
+ while [ $i -lt ${#OUT_LIST[@]} ]
|
|
|
+ do
|
|
|
+ local C=${OUT_LIST[i]}
|
|
|
+ local S=${OUT_LIST[i+1]}
|
|
|
+ local D="'$(detail_chosen $C $S)'"
|
|
|
+ if [ ! -z "$D" ]
|
|
|
+ then
|
|
|
+ # This weirdness is needed to work with toybox when
|
|
|
+ # using the -t option.
|
|
|
+ printf '%05u\t%s\n' ${i} "$D" | tr -d \'
|
|
|
+ fi
|
|
|
+ i=$((i+2))
|
|
|
+ done
|
|
|
+}
|
|
|
+
|
|
|
+function already_seen() {
|
|
|
+ local i=0
|
|
|
+ while [ $i -lt ${#OUT_LIST[@]} ]
|
|
|
+ do
|
|
|
+ if [ "$1" = "${OUT_LIST[$i]}" ]
|
|
|
+ then
|
|
|
+ # if-statement treats 0 (no-error) as true
|
|
|
+ return 0
|
|
|
+ fi
|
|
|
+ i=$(($i+2))
|
|
|
+ done
|
|
|
+
|
|
|
+ # if-statement treats 1 (error) as false
|
|
|
+ return 1
|
|
|
+}
|
|
|
+
|
|
|
+# Return 0 (no-error/true) if parent was added
|
|
|
+function add_parent() {
|
|
|
+
|
|
|
+ if [ ${ALLOW_PARENTS} -eq 0 ]
|
|
|
+ then
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ local CON=$1
|
|
|
+ # $CON could be a symlink path. So, we need to find the real path and
|
|
|
+ # then go up one level to find the real parent.
|
|
|
+ local PARENT=$(realpath $CON/..)
|
|
|
+
|
|
|
+ while [ ! -e ${PARENT}/driver ]
|
|
|
+ do
|
|
|
+ if [ "$PARENT" = "/sys/devices" ]
|
|
|
+ then
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+ PARENT=$(realpath $PARENT/..)
|
|
|
+ done
|
|
|
+
|
|
|
+ CONSUMERS+=($PARENT)
|
|
|
+ OUT_LIST+=(${CON} ${PARENT})
|
|
|
+ return 0
|
|
|
+}
|
|
|
+
|
|
|
+# Return 0 (no-error/true) if one or more suppliers were added
|
|
|
+function add_suppliers() {
|
|
|
+ local CON=$1
|
|
|
+ local RET=1
|
|
|
+
|
|
|
+ if [ ${ALLOW_DEVLINKS} -eq 0 ]
|
|
|
+ then
|
|
|
+ return 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null)
|
|
|
+ for SL in $SUPPLIER_LINKS;
|
|
|
+ do
|
|
|
+ SYNC_STATE=$(cat $SL/sync_state_only)
|
|
|
+
|
|
|
+ # sync_state_only links are proxy dependencies.
|
|
|
+ # They can also have cycles. So, don't follow them.
|
|
|
+ if [ "$SYNC_STATE" != '0' ]
|
|
|
+ then
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ SUPPLIER=$(realpath $SL/supplier)
|
|
|
+
|
|
|
+ if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
|
|
|
+ then
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ CONSUMERS+=($SUPPLIER)
|
|
|
+ OUT_LIST+=(${CON} ${SUPPLIER})
|
|
|
+ RET=0
|
|
|
+ done
|
|
|
+
|
|
|
+ return $RET
|
|
|
+}
|
|
|
+
|
|
|
+function detail_compat() {
|
|
|
+ f=$1/of_node/compatible
|
|
|
+ if [ -e $f ]
|
|
|
+ then
|
|
|
+ echo -n $(cat $f)
|
|
|
+ else
|
|
|
+ echo -n $1
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function detail_module() {
|
|
|
+ f=$1/driver/module
|
|
|
+ if [ -e $f ]
|
|
|
+ then
|
|
|
+ echo -n $(basename $(realpath $f))
|
|
|
+ else
|
|
|
+ echo -n $1
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function detail_driver() {
|
|
|
+ f=$1/driver
|
|
|
+ if [ -e $f ]
|
|
|
+ then
|
|
|
+ echo -n $(basename $(realpath $f))
|
|
|
+ else
|
|
|
+ echo -n $1
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function detail_fwnode() {
|
|
|
+ f=$1/firmware_node
|
|
|
+ if [ ! -e $f ]
|
|
|
+ then
|
|
|
+ f=$1/of_node
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [ -e $f ]
|
|
|
+ then
|
|
|
+ echo -n $(realpath $f)
|
|
|
+ else
|
|
|
+ echo -n $1
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function detail_graphviz() {
|
|
|
+ if [ "$2" != "ROOT" ]
|
|
|
+ then
|
|
|
+ echo -n "\"$(basename $2)\"->\"$(basename $1)\""
|
|
|
+ else
|
|
|
+ echo -n "\"$(basename $1)\""
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function detail_tsort() {
|
|
|
+ echo -n "\"$2\" \"$1\""
|
|
|
+}
|
|
|
+
|
|
|
+function detail_device() { echo -n $1; }
|
|
|
+
|
|
|
+alias detail=detail_device
|
|
|
+ALLOW_NO_DRIVER=0
|
|
|
+ALLOW_DEVLINKS=1
|
|
|
+ALLOW_PARENTS=1
|
|
|
+
|
|
|
+while [ $# -gt 0 ]
|
|
|
+do
|
|
|
+ ARG=$1
|
|
|
+ case $ARG in
|
|
|
+ --help)
|
|
|
+ help
|
|
|
+ exit 0
|
|
|
+ ;;
|
|
|
+ -c)
|
|
|
+ alias detail=detail_compat
|
|
|
+ ;;
|
|
|
+ -m)
|
|
|
+ alias detail=detail_module
|
|
|
+ ;;
|
|
|
+ -d)
|
|
|
+ alias detail=detail_driver
|
|
|
+ ;;
|
|
|
+ -f)
|
|
|
+ alias detail=detail_fwnode
|
|
|
+ ;;
|
|
|
+ -g)
|
|
|
+ alias detail=detail_graphviz
|
|
|
+ ;;
|
|
|
+ -t)
|
|
|
+ alias detail=detail_tsort
|
|
|
+ ;;
|
|
|
+ --allow-no-driver)
|
|
|
+ ALLOW_NO_DRIVER=1
|
|
|
+ ;;
|
|
|
+ --exclude-devlinks)
|
|
|
+ ALLOW_DEVLINKS=0
|
|
|
+ ;;
|
|
|
+ --exclude-parents)
|
|
|
+ ALLOW_PARENTS=0
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ # Stop at the first argument that's not an option.
|
|
|
+ break
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+ shift
|
|
|
+done
|
|
|
+
|
|
|
+function detail_chosen() {
|
|
|
+ detail $1 $2
|
|
|
+}
|
|
|
+
|
|
|
+if [ $# -eq 0 ]
|
|
|
+then
|
|
|
+ help
|
|
|
+ exit 1
|
|
|
+fi
|
|
|
+
|
|
|
+CONSUMERS=($@)
|
|
|
+OUT_LIST=()
|
|
|
+
|
|
|
+# Do a breadth first, non-recursive tracking of suppliers. The parent is also
|
|
|
+# considered a "supplier" as a device can't probe without its parent.
|
|
|
+i=0
|
|
|
+while [ $i -lt ${#CONSUMERS[@]} ]
|
|
|
+do
|
|
|
+ CONSUMER=$(realpath ${CONSUMERS[$i]})
|
|
|
+ i=$(($i+1))
|
|
|
+
|
|
|
+ if already_seen ${CONSUMER}
|
|
|
+ then
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ # If this is not a device with a driver, we don't care about its
|
|
|
+ # suppliers.
|
|
|
+ if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
|
|
|
+ then
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ ROOT=1
|
|
|
+
|
|
|
+ # Add suppliers to CONSUMERS list and output the consumer details.
|
|
|
+ #
|
|
|
+ # We don't need to worry about a cycle in the dependency chain causing
|
|
|
+ # infinite loops. That's because the kernel doesn't allow cycles in
|
|
|
+ # device links unless it's a sync_state_only device link. And we ignore
|
|
|
+ # sync_state_only device links inside add_suppliers.
|
|
|
+ if add_suppliers ${CONSUMER}
|
|
|
+ then
|
|
|
+ ROOT=0
|
|
|
+ fi
|
|
|
+
|
|
|
+ if add_parent ${CONSUMER}
|
|
|
+ then
|
|
|
+ ROOT=0
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [ $ROOT -eq 1 ]
|
|
|
+ then
|
|
|
+ OUT_LIST+=(${CONSUMER} "ROOT")
|
|
|
+ fi
|
|
|
+done
|
|
|
+
|
|
|
+# Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
|
|
|
+# isn't really stable.
|
|
|
+dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-
|
|
|
+
|
|
|
+exit 0
|