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