1 #! /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 6 function help() { 7 cat << EOF 8 Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices> 9 10 This script needs to be run on the target device once it has booted to a 11 shell. 12 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 17 dependency chain. 18 19 By default it lists the full path to the devices under /sys/devices. 20 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. 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 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. 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 46 EOF 47 } 48 49 function 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 66 function 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 83 function 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 110 function 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 146 function 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 156 function 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 166 function 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 176 function 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 191 function 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 200 function detail_tsort() { 201 echo -n "\"$2\" \"$1\"" 202 } 203 204 function detail_device() { echo -n $1; } 205 206 alias detail=detail_device 207 ALLOW_NO_DRIVER=0 208 ALLOW_DEVLINKS=1 209 ALLOW_PARENTS=1 210 211 while [ $# -gt 0 ] 212 do 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 252 done 253 254 function detail_chosen() { 255 detail $1 $2 256 } 257 258 if [ $# -eq 0 ] 259 then 260 help 261 exit 1 262 fi 263 264 CONSUMERS=($@) 265 OUT_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. 269 i=0 270 while [ $i -lt ${#CONSUMERS[@]} ] 271 do 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 309 done 310 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- 314 315 exit 0
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.