1 #! /bin/sh 1 #! /bin/sh 2 # SPDX-License-Identifier: GPL-2.0 2 # SPDX-License-Identifier: GPL-2.0 3 # Copyright (c) 2020, Google LLC. All rights r 3 # Copyright (c) 2020, Google LLC. All rights reserved. 4 # Author: Saravana Kannan <saravanak@google.com 4 # Author: Saravana Kannan <saravanak@google.com> 5 5 6 function help() { 6 function help() { 7 cat << EOF 7 cat << EOF 8 Usage: $(basename $0) [-c|-d|-m|-f] [filter op 8 Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices> 9 9 10 This script needs to be run on the target devi 10 This script needs to be run on the target device once it has booted to a 11 shell. 11 shell. 12 12 13 The script takes as input a list of one or mor 13 The script takes as input a list of one or more device directories under 14 /sys/devices and then lists the probe dependen 14 /sys/devices and then lists the probe dependency chain (suppliers and 15 parents) of these devices. It does a breadth f 15 parents) of these devices. It does a breadth first search of the dependency 16 chain, so the last entry in the output is clos 16 chain, so the last entry in the output is close to the root of the 17 dependency chain. 17 dependency chain. 18 18 19 By default it lists the full path to the devic 19 By default it lists the full path to the devices under /sys/devices. 20 20 21 It also takes an optional modifier flag as the 21 It also takes an optional modifier flag as the first parameter to change 22 what information is listed in the output. If t 22 what information is listed in the output. If the requested information is 23 not available, the device name is printed. 23 not available, the device name is printed. 24 24 25 -c lists the compatible string of the dep 25 -c lists the compatible string of the dependencies 26 -d lists the driver name of the dependenc 26 -d lists the driver name of the dependencies that have probed 27 -m lists the module name of the dependenc 27 -m lists the module name of the dependencies that have a module 28 -f list the firmware node path of the dep 28 -f list the firmware node path of the dependencies 29 -g list the dependencies as edges and nod 29 -g list the dependencies as edges and nodes for graphviz 30 -t list the dependencies as edges for tso 30 -t list the dependencies as edges for tsort 31 31 32 The filter options provide a way to filter out 32 The filter options provide a way to filter out some dependencies: 33 --allow-no-driver By default dependencie 33 --allow-no-driver By default dependencies that don't have a driver 34 attached are ignored. 34 attached are ignored. This is to avoid following 35 device links to "class 35 device links to "class" devices that are created 36 when the consumer prob 36 when the consumer probes (as in, not a probe 37 dependency). If you wa 37 dependency). If you want to follow these links 38 anyway, use this flag. 38 anyway, use this flag. 39 39 40 --exclude-devlinks Don't follow device li 40 --exclude-devlinks Don't follow device links when tracking probe 41 dependencies. 41 dependencies. 42 42 43 --exclude-parents Don't follow parent de 43 --exclude-parents Don't follow parent devices when tracking probe 44 dependencies. 44 dependencies. 45 45 46 EOF 46 EOF 47 } 47 } 48 48 49 function dev_to_detail() { 49 function dev_to_detail() { 50 local i=0 50 local i=0 51 while [ $i -lt ${#OUT_LIST[@]} ] 51 while [ $i -lt ${#OUT_LIST[@]} ] 52 do 52 do 53 local C=${OUT_LIST[i]} 53 local C=${OUT_LIST[i]} 54 local S=${OUT_LIST[i+1]} 54 local S=${OUT_LIST[i+1]} 55 local D="'$(detail_chosen $C $ 55 local D="'$(detail_chosen $C $S)'" 56 if [ ! -z "$D" ] 56 if [ ! -z "$D" ] 57 then 57 then 58 # This weirdness is ne 58 # This weirdness is needed to work with toybox when 59 # using the -t option. 59 # using the -t option. 60 printf '%05u\t%s\n' ${ 60 printf '%05u\t%s\n' ${i} "$D" | tr -d \' 61 fi 61 fi 62 i=$((i+2)) 62 i=$((i+2)) 63 done 63 done 64 } 64 } 65 65 66 function already_seen() { 66 function already_seen() { 67 local i=0 67 local i=0 68 while [ $i -lt ${#OUT_LIST[@]} ] 68 while [ $i -lt ${#OUT_LIST[@]} ] 69 do 69 do 70 if [ "$1" = "${OUT_LIST[$i]}" 70 if [ "$1" = "${OUT_LIST[$i]}" ] 71 then 71 then 72 # if-statement treats 72 # if-statement treats 0 (no-error) as true 73 return 0 73 return 0 74 fi 74 fi 75 i=$(($i+2)) 75 i=$(($i+2)) 76 done 76 done 77 77 78 # if-statement treats 1 (error) as fal 78 # if-statement treats 1 (error) as false 79 return 1 79 return 1 80 } 80 } 81 81 82 # Return 0 (no-error/true) if parent was added 82 # Return 0 (no-error/true) if parent was added 83 function add_parent() { 83 function add_parent() { 84 84 85 if [ ${ALLOW_PARENTS} -eq 0 ] 85 if [ ${ALLOW_PARENTS} -eq 0 ] 86 then 86 then 87 return 1 87 return 1 88 fi 88 fi 89 89 90 local CON=$1 90 local CON=$1 91 # $CON could be a symlink path. So, we 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 rea 92 # then go up one level to find the real parent. 93 local PARENT=$(realpath $CON/..) 93 local PARENT=$(realpath $CON/..) 94 94 95 while [ ! -e ${PARENT}/driver ] 95 while [ ! -e ${PARENT}/driver ] 96 do 96 do 97 if [ "$PARENT" = "/sys/devices 97 if [ "$PARENT" = "/sys/devices" ] 98 then 98 then 99 return 1 99 return 1 100 fi 100 fi 101 PARENT=$(realpath $PARENT/..) 101 PARENT=$(realpath $PARENT/..) 102 done 102 done 103 103 104 CONSUMERS+=($PARENT) 104 CONSUMERS+=($PARENT) 105 OUT_LIST+=(${CON} ${PARENT}) 105 OUT_LIST+=(${CON} ${PARENT}) 106 return 0 106 return 0 107 } 107 } 108 108 109 # Return 0 (no-error/true) if one or more supp 109 # Return 0 (no-error/true) if one or more suppliers were added 110 function add_suppliers() { 110 function add_suppliers() { 111 local CON=$1 111 local CON=$1 112 local RET=1 112 local RET=1 113 113 114 if [ ${ALLOW_DEVLINKS} -eq 0 ] 114 if [ ${ALLOW_DEVLINKS} -eq 0 ] 115 then 115 then 116 return 1 116 return 1 117 fi 117 fi 118 118 119 SUPPLIER_LINKS=$(ls -1d $CON/supplier: 119 SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null) 120 for SL in $SUPPLIER_LINKS; 120 for SL in $SUPPLIER_LINKS; 121 do 121 do 122 SYNC_STATE=$(cat $SL/sync_stat 122 SYNC_STATE=$(cat $SL/sync_state_only) 123 123 124 # sync_state_only links are pr 124 # sync_state_only links are proxy dependencies. 125 # They can also have cycles. S 125 # They can also have cycles. So, don't follow them. 126 if [ "$SYNC_STATE" != '0' ] 126 if [ "$SYNC_STATE" != '0' ] 127 then 127 then 128 continue 128 continue 129 fi 129 fi 130 130 131 SUPPLIER=$(realpath $SL/suppli 131 SUPPLIER=$(realpath $SL/supplier) 132 132 133 if [ ! -e $SUPPLIER/driver -a 133 if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] 134 then 134 then 135 continue 135 continue 136 fi 136 fi 137 137 138 CONSUMERS+=($SUPPLIER) 138 CONSUMERS+=($SUPPLIER) 139 OUT_LIST+=(${CON} ${SUPPLIER}) 139 OUT_LIST+=(${CON} ${SUPPLIER}) 140 RET=0 140 RET=0 141 done 141 done 142 142 143 return $RET 143 return $RET 144 } 144 } 145 145 146 function detail_compat() { 146 function detail_compat() { 147 f=$1/of_node/compatible 147 f=$1/of_node/compatible 148 if [ -e $f ] 148 if [ -e $f ] 149 then 149 then 150 echo -n $(cat $f) 150 echo -n $(cat $f) 151 else 151 else 152 echo -n $1 152 echo -n $1 153 fi 153 fi 154 } 154 } 155 155 156 function detail_module() { 156 function detail_module() { 157 f=$1/driver/module 157 f=$1/driver/module 158 if [ -e $f ] 158 if [ -e $f ] 159 then 159 then 160 echo -n $(basename $(realpath 160 echo -n $(basename $(realpath $f)) 161 else 161 else 162 echo -n $1 162 echo -n $1 163 fi 163 fi 164 } 164 } 165 165 166 function detail_driver() { 166 function detail_driver() { 167 f=$1/driver 167 f=$1/driver 168 if [ -e $f ] 168 if [ -e $f ] 169 then 169 then 170 echo -n $(basename $(realpath 170 echo -n $(basename $(realpath $f)) 171 else 171 else 172 echo -n $1 172 echo -n $1 173 fi 173 fi 174 } 174 } 175 175 176 function detail_fwnode() { 176 function detail_fwnode() { 177 f=$1/firmware_node 177 f=$1/firmware_node 178 if [ ! -e $f ] 178 if [ ! -e $f ] 179 then 179 then 180 f=$1/of_node 180 f=$1/of_node 181 fi 181 fi 182 182 183 if [ -e $f ] 183 if [ -e $f ] 184 then 184 then 185 echo -n $(realpath $f) 185 echo -n $(realpath $f) 186 else 186 else 187 echo -n $1 187 echo -n $1 188 fi 188 fi 189 } 189 } 190 190 191 function detail_graphviz() { 191 function detail_graphviz() { 192 if [ "$2" != "ROOT" ] 192 if [ "$2" != "ROOT" ] 193 then 193 then 194 echo -n "\"$(basename $2)\"->\ 194 echo -n "\"$(basename $2)\"->\"$(basename $1)\"" 195 else 195 else 196 echo -n "\"$(basename $1)\"" 196 echo -n "\"$(basename $1)\"" 197 fi 197 fi 198 } 198 } 199 199 200 function detail_tsort() { 200 function detail_tsort() { 201 echo -n "\"$2\" \"$1\"" 201 echo -n "\"$2\" \"$1\"" 202 } 202 } 203 203 204 function detail_device() { echo -n $1; } 204 function detail_device() { echo -n $1; } 205 205 206 alias detail=detail_device 206 alias detail=detail_device 207 ALLOW_NO_DRIVER=0 207 ALLOW_NO_DRIVER=0 208 ALLOW_DEVLINKS=1 208 ALLOW_DEVLINKS=1 209 ALLOW_PARENTS=1 209 ALLOW_PARENTS=1 210 210 211 while [ $# -gt 0 ] 211 while [ $# -gt 0 ] 212 do 212 do 213 ARG=$1 213 ARG=$1 214 case $ARG in 214 case $ARG in 215 --help) 215 --help) 216 help 216 help 217 exit 0 217 exit 0 218 ;; 218 ;; 219 -c) 219 -c) 220 alias detail=detail_co 220 alias detail=detail_compat 221 ;; 221 ;; 222 -m) 222 -m) 223 alias detail=detail_mo 223 alias detail=detail_module 224 ;; 224 ;; 225 -d) 225 -d) 226 alias detail=detail_dr 226 alias detail=detail_driver 227 ;; 227 ;; 228 -f) 228 -f) 229 alias detail=detail_fw 229 alias detail=detail_fwnode 230 ;; 230 ;; 231 -g) 231 -g) 232 alias detail=detail_gr 232 alias detail=detail_graphviz 233 ;; 233 ;; 234 -t) 234 -t) 235 alias detail=detail_ts 235 alias detail=detail_tsort 236 ;; 236 ;; 237 --allow-no-driver) 237 --allow-no-driver) 238 ALLOW_NO_DRIVER=1 238 ALLOW_NO_DRIVER=1 239 ;; 239 ;; 240 --exclude-devlinks) 240 --exclude-devlinks) 241 ALLOW_DEVLINKS=0 241 ALLOW_DEVLINKS=0 242 ;; 242 ;; 243 --exclude-parents) 243 --exclude-parents) 244 ALLOW_PARENTS=0 244 ALLOW_PARENTS=0 245 ;; 245 ;; 246 *) 246 *) 247 # Stop at the first ar 247 # Stop at the first argument that's not an option. 248 break 248 break 249 ;; 249 ;; 250 esac 250 esac 251 shift 251 shift 252 done 252 done 253 253 254 function detail_chosen() { 254 function detail_chosen() { 255 detail $1 $2 255 detail $1 $2 256 } 256 } 257 257 258 if [ $# -eq 0 ] 258 if [ $# -eq 0 ] 259 then 259 then 260 help 260 help 261 exit 1 261 exit 1 262 fi 262 fi 263 263 264 CONSUMERS=($@) 264 CONSUMERS=($@) 265 OUT_LIST=() 265 OUT_LIST=() 266 266 267 # Do a breadth first, non-recursive tracking o 267 # Do a breadth first, non-recursive tracking of suppliers. The parent is also 268 # considered a "supplier" as a device can't pr 268 # considered a "supplier" as a device can't probe without its parent. 269 i=0 269 i=0 270 while [ $i -lt ${#CONSUMERS[@]} ] 270 while [ $i -lt ${#CONSUMERS[@]} ] 271 do 271 do 272 CONSUMER=$(realpath ${CONSUMERS[$i]}) 272 CONSUMER=$(realpath ${CONSUMERS[$i]}) 273 i=$(($i+1)) 273 i=$(($i+1)) 274 274 275 if already_seen ${CONSUMER} 275 if already_seen ${CONSUMER} 276 then 276 then 277 continue 277 continue 278 fi 278 fi 279 279 280 # If this is not a device with a drive 280 # If this is not a device with a driver, we don't care about its 281 # suppliers. 281 # suppliers. 282 if [ ! -e ${CONSUMER}/driver -a ${ALLO 282 if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] 283 then 283 then 284 continue 284 continue 285 fi 285 fi 286 286 287 ROOT=1 287 ROOT=1 288 288 289 # Add suppliers to CONSUMERS list and 289 # Add suppliers to CONSUMERS list and output the consumer details. 290 # 290 # 291 # We don't need to worry about a cycle 291 # We don't need to worry about a cycle in the dependency chain causing 292 # infinite loops. That's because the k 292 # infinite loops. That's because the kernel doesn't allow cycles in 293 # device links unless it's a sync_stat 293 # device links unless it's a sync_state_only device link. And we ignore 294 # sync_state_only device links inside 294 # sync_state_only device links inside add_suppliers. 295 if add_suppliers ${CONSUMER} 295 if add_suppliers ${CONSUMER} 296 then 296 then 297 ROOT=0 297 ROOT=0 298 fi 298 fi 299 299 300 if add_parent ${CONSUMER} 300 if add_parent ${CONSUMER} 301 then 301 then 302 ROOT=0 302 ROOT=0 303 fi 303 fi 304 304 305 if [ $ROOT -eq 1 ] 305 if [ $ROOT -eq 1 ] 306 then 306 then 307 OUT_LIST+=(${CONSUMER} "ROOT") 307 OUT_LIST+=(${CONSUMER} "ROOT") 308 fi 308 fi 309 done 309 done 310 310 311 # Can NOT combine sort and uniq using sort -su 311 # Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox 312 # isn't really stable. 312 # isn't really stable. 313 dev_to_detail | sort -k2 -k1 | uniq -f 1 | sor 313 dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2- 314 314 315 exit 0 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.