162306a36Sopenharmony_ci#! /bin/sh 262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 362306a36Sopenharmony_ci# Copyright (c) 2020, Google LLC. All rights reserved. 462306a36Sopenharmony_ci# Author: Saravana Kannan <saravanak@google.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_cifunction help() { 762306a36Sopenharmony_ci cat << EOF 862306a36Sopenharmony_ciUsage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciThis script needs to be run on the target device once it has booted to a 1162306a36Sopenharmony_cishell. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciThe script takes as input a list of one or more device directories under 1462306a36Sopenharmony_ci/sys/devices and then lists the probe dependency chain (suppliers and 1562306a36Sopenharmony_ciparents) of these devices. It does a breadth first search of the dependency 1662306a36Sopenharmony_cichain, so the last entry in the output is close to the root of the 1762306a36Sopenharmony_cidependency chain. 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciBy default it lists the full path to the devices under /sys/devices. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciIt also takes an optional modifier flag as the first parameter to change 2262306a36Sopenharmony_ciwhat information is listed in the output. If the requested information is 2362306a36Sopenharmony_cinot available, the device name is printed. 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci -c lists the compatible string of the dependencies 2662306a36Sopenharmony_ci -d lists the driver name of the dependencies that have probed 2762306a36Sopenharmony_ci -m lists the module name of the dependencies that have a module 2862306a36Sopenharmony_ci -f list the firmware node path of the dependencies 2962306a36Sopenharmony_ci -g list the dependencies as edges and nodes for graphviz 3062306a36Sopenharmony_ci -t list the dependencies as edges for tsort 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciThe filter options provide a way to filter out some dependencies: 3362306a36Sopenharmony_ci --allow-no-driver By default dependencies that don't have a driver 3462306a36Sopenharmony_ci attached are ignored. This is to avoid following 3562306a36Sopenharmony_ci device links to "class" devices that are created 3662306a36Sopenharmony_ci when the consumer probes (as in, not a probe 3762306a36Sopenharmony_ci dependency). If you want to follow these links 3862306a36Sopenharmony_ci anyway, use this flag. 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci --exclude-devlinks Don't follow device links when tracking probe 4162306a36Sopenharmony_ci dependencies. 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci --exclude-parents Don't follow parent devices when tracking probe 4462306a36Sopenharmony_ci dependencies. 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciEOF 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cifunction dev_to_detail() { 5062306a36Sopenharmony_ci local i=0 5162306a36Sopenharmony_ci while [ $i -lt ${#OUT_LIST[@]} ] 5262306a36Sopenharmony_ci do 5362306a36Sopenharmony_ci local C=${OUT_LIST[i]} 5462306a36Sopenharmony_ci local S=${OUT_LIST[i+1]} 5562306a36Sopenharmony_ci local D="'$(detail_chosen $C $S)'" 5662306a36Sopenharmony_ci if [ ! -z "$D" ] 5762306a36Sopenharmony_ci then 5862306a36Sopenharmony_ci # This weirdness is needed to work with toybox when 5962306a36Sopenharmony_ci # using the -t option. 6062306a36Sopenharmony_ci printf '%05u\t%s\n' ${i} "$D" | tr -d \' 6162306a36Sopenharmony_ci fi 6262306a36Sopenharmony_ci i=$((i+2)) 6362306a36Sopenharmony_ci done 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cifunction already_seen() { 6762306a36Sopenharmony_ci local i=0 6862306a36Sopenharmony_ci while [ $i -lt ${#OUT_LIST[@]} ] 6962306a36Sopenharmony_ci do 7062306a36Sopenharmony_ci if [ "$1" = "${OUT_LIST[$i]}" ] 7162306a36Sopenharmony_ci then 7262306a36Sopenharmony_ci # if-statement treats 0 (no-error) as true 7362306a36Sopenharmony_ci return 0 7462306a36Sopenharmony_ci fi 7562306a36Sopenharmony_ci i=$(($i+2)) 7662306a36Sopenharmony_ci done 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci # if-statement treats 1 (error) as false 7962306a36Sopenharmony_ci return 1 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci# Return 0 (no-error/true) if parent was added 8362306a36Sopenharmony_cifunction add_parent() { 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if [ ${ALLOW_PARENTS} -eq 0 ] 8662306a36Sopenharmony_ci then 8762306a36Sopenharmony_ci return 1 8862306a36Sopenharmony_ci fi 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci local CON=$1 9162306a36Sopenharmony_ci # $CON could be a symlink path. So, we need to find the real path and 9262306a36Sopenharmony_ci # then go up one level to find the real parent. 9362306a36Sopenharmony_ci local PARENT=$(realpath $CON/..) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci while [ ! -e ${PARENT}/driver ] 9662306a36Sopenharmony_ci do 9762306a36Sopenharmony_ci if [ "$PARENT" = "/sys/devices" ] 9862306a36Sopenharmony_ci then 9962306a36Sopenharmony_ci return 1 10062306a36Sopenharmony_ci fi 10162306a36Sopenharmony_ci PARENT=$(realpath $PARENT/..) 10262306a36Sopenharmony_ci done 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci CONSUMERS+=($PARENT) 10562306a36Sopenharmony_ci OUT_LIST+=(${CON} ${PARENT}) 10662306a36Sopenharmony_ci return 0 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci# Return 0 (no-error/true) if one or more suppliers were added 11062306a36Sopenharmony_cifunction add_suppliers() { 11162306a36Sopenharmony_ci local CON=$1 11262306a36Sopenharmony_ci local RET=1 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if [ ${ALLOW_DEVLINKS} -eq 0 ] 11562306a36Sopenharmony_ci then 11662306a36Sopenharmony_ci return 1 11762306a36Sopenharmony_ci fi 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null) 12062306a36Sopenharmony_ci for SL in $SUPPLIER_LINKS; 12162306a36Sopenharmony_ci do 12262306a36Sopenharmony_ci SYNC_STATE=$(cat $SL/sync_state_only) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci # sync_state_only links are proxy dependencies. 12562306a36Sopenharmony_ci # They can also have cycles. So, don't follow them. 12662306a36Sopenharmony_ci if [ "$SYNC_STATE" != '0' ] 12762306a36Sopenharmony_ci then 12862306a36Sopenharmony_ci continue 12962306a36Sopenharmony_ci fi 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci SUPPLIER=$(realpath $SL/supplier) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] 13462306a36Sopenharmony_ci then 13562306a36Sopenharmony_ci continue 13662306a36Sopenharmony_ci fi 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci CONSUMERS+=($SUPPLIER) 13962306a36Sopenharmony_ci OUT_LIST+=(${CON} ${SUPPLIER}) 14062306a36Sopenharmony_ci RET=0 14162306a36Sopenharmony_ci done 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return $RET 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cifunction detail_compat() { 14762306a36Sopenharmony_ci f=$1/of_node/compatible 14862306a36Sopenharmony_ci if [ -e $f ] 14962306a36Sopenharmony_ci then 15062306a36Sopenharmony_ci echo -n $(cat $f) 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci echo -n $1 15362306a36Sopenharmony_ci fi 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cifunction detail_module() { 15762306a36Sopenharmony_ci f=$1/driver/module 15862306a36Sopenharmony_ci if [ -e $f ] 15962306a36Sopenharmony_ci then 16062306a36Sopenharmony_ci echo -n $(basename $(realpath $f)) 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci echo -n $1 16362306a36Sopenharmony_ci fi 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cifunction detail_driver() { 16762306a36Sopenharmony_ci f=$1/driver 16862306a36Sopenharmony_ci if [ -e $f ] 16962306a36Sopenharmony_ci then 17062306a36Sopenharmony_ci echo -n $(basename $(realpath $f)) 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci echo -n $1 17362306a36Sopenharmony_ci fi 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cifunction detail_fwnode() { 17762306a36Sopenharmony_ci f=$1/firmware_node 17862306a36Sopenharmony_ci if [ ! -e $f ] 17962306a36Sopenharmony_ci then 18062306a36Sopenharmony_ci f=$1/of_node 18162306a36Sopenharmony_ci fi 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if [ -e $f ] 18462306a36Sopenharmony_ci then 18562306a36Sopenharmony_ci echo -n $(realpath $f) 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci echo -n $1 18862306a36Sopenharmony_ci fi 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cifunction detail_graphviz() { 19262306a36Sopenharmony_ci if [ "$2" != "ROOT" ] 19362306a36Sopenharmony_ci then 19462306a36Sopenharmony_ci echo -n "\"$(basename $2)\"->\"$(basename $1)\"" 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci echo -n "\"$(basename $1)\"" 19762306a36Sopenharmony_ci fi 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cifunction detail_tsort() { 20162306a36Sopenharmony_ci echo -n "\"$2\" \"$1\"" 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cifunction detail_device() { echo -n $1; } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cialias detail=detail_device 20762306a36Sopenharmony_ciALLOW_NO_DRIVER=0 20862306a36Sopenharmony_ciALLOW_DEVLINKS=1 20962306a36Sopenharmony_ciALLOW_PARENTS=1 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciwhile [ $# -gt 0 ] 21262306a36Sopenharmony_cido 21362306a36Sopenharmony_ci ARG=$1 21462306a36Sopenharmony_ci case $ARG in 21562306a36Sopenharmony_ci --help) 21662306a36Sopenharmony_ci help 21762306a36Sopenharmony_ci exit 0 21862306a36Sopenharmony_ci ;; 21962306a36Sopenharmony_ci -c) 22062306a36Sopenharmony_ci alias detail=detail_compat 22162306a36Sopenharmony_ci ;; 22262306a36Sopenharmony_ci -m) 22362306a36Sopenharmony_ci alias detail=detail_module 22462306a36Sopenharmony_ci ;; 22562306a36Sopenharmony_ci -d) 22662306a36Sopenharmony_ci alias detail=detail_driver 22762306a36Sopenharmony_ci ;; 22862306a36Sopenharmony_ci -f) 22962306a36Sopenharmony_ci alias detail=detail_fwnode 23062306a36Sopenharmony_ci ;; 23162306a36Sopenharmony_ci -g) 23262306a36Sopenharmony_ci alias detail=detail_graphviz 23362306a36Sopenharmony_ci ;; 23462306a36Sopenharmony_ci -t) 23562306a36Sopenharmony_ci alias detail=detail_tsort 23662306a36Sopenharmony_ci ;; 23762306a36Sopenharmony_ci --allow-no-driver) 23862306a36Sopenharmony_ci ALLOW_NO_DRIVER=1 23962306a36Sopenharmony_ci ;; 24062306a36Sopenharmony_ci --exclude-devlinks) 24162306a36Sopenharmony_ci ALLOW_DEVLINKS=0 24262306a36Sopenharmony_ci ;; 24362306a36Sopenharmony_ci --exclude-parents) 24462306a36Sopenharmony_ci ALLOW_PARENTS=0 24562306a36Sopenharmony_ci ;; 24662306a36Sopenharmony_ci *) 24762306a36Sopenharmony_ci # Stop at the first argument that's not an option. 24862306a36Sopenharmony_ci break 24962306a36Sopenharmony_ci ;; 25062306a36Sopenharmony_ci esac 25162306a36Sopenharmony_ci shift 25262306a36Sopenharmony_cidone 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cifunction detail_chosen() { 25562306a36Sopenharmony_ci detail $1 $2 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciif [ $# -eq 0 ] 25962306a36Sopenharmony_cithen 26062306a36Sopenharmony_ci help 26162306a36Sopenharmony_ci exit 1 26262306a36Sopenharmony_cifi 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciCONSUMERS=($@) 26562306a36Sopenharmony_ciOUT_LIST=() 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci# Do a breadth first, non-recursive tracking of suppliers. The parent is also 26862306a36Sopenharmony_ci# considered a "supplier" as a device can't probe without its parent. 26962306a36Sopenharmony_cii=0 27062306a36Sopenharmony_ciwhile [ $i -lt ${#CONSUMERS[@]} ] 27162306a36Sopenharmony_cido 27262306a36Sopenharmony_ci CONSUMER=$(realpath ${CONSUMERS[$i]}) 27362306a36Sopenharmony_ci i=$(($i+1)) 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if already_seen ${CONSUMER} 27662306a36Sopenharmony_ci then 27762306a36Sopenharmony_ci continue 27862306a36Sopenharmony_ci fi 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci # If this is not a device with a driver, we don't care about its 28162306a36Sopenharmony_ci # suppliers. 28262306a36Sopenharmony_ci if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] 28362306a36Sopenharmony_ci then 28462306a36Sopenharmony_ci continue 28562306a36Sopenharmony_ci fi 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ROOT=1 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci # Add suppliers to CONSUMERS list and output the consumer details. 29062306a36Sopenharmony_ci # 29162306a36Sopenharmony_ci # We don't need to worry about a cycle in the dependency chain causing 29262306a36Sopenharmony_ci # infinite loops. That's because the kernel doesn't allow cycles in 29362306a36Sopenharmony_ci # device links unless it's a sync_state_only device link. And we ignore 29462306a36Sopenharmony_ci # sync_state_only device links inside add_suppliers. 29562306a36Sopenharmony_ci if add_suppliers ${CONSUMER} 29662306a36Sopenharmony_ci then 29762306a36Sopenharmony_ci ROOT=0 29862306a36Sopenharmony_ci fi 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if add_parent ${CONSUMER} 30162306a36Sopenharmony_ci then 30262306a36Sopenharmony_ci ROOT=0 30362306a36Sopenharmony_ci fi 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if [ $ROOT -eq 1 ] 30662306a36Sopenharmony_ci then 30762306a36Sopenharmony_ci OUT_LIST+=(${CONSUMER} "ROOT") 30862306a36Sopenharmony_ci fi 30962306a36Sopenharmony_cidone 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci# Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox 31262306a36Sopenharmony_ci# isn't really stable. 31362306a36Sopenharmony_cidev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2- 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciexit 0 316