Back to home page

OSCL-LXR

 
 

    


0001 #! /bin/sh
0002 # SPDX-License-Identifier: GPL-2.0
0003 # Copyright (c) 2020, Google LLC. All rights reserved.
0004 # Author: Saravana Kannan <saravanak@google.com>
0005 
0006 function help() {
0007         cat << EOF
0008 Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
0009 
0010 This script needs to be run on the target device once it has booted to a
0011 shell.
0012 
0013 The script takes as input a list of one or more device directories under
0014 /sys/devices and then lists the probe dependency chain (suppliers and
0015 parents) of these devices. It does a breadth first search of the dependency
0016 chain, so the last entry in the output is close to the root of the
0017 dependency chain.
0018 
0019 By default it lists the full path to the devices under /sys/devices.
0020 
0021 It also takes an optional modifier flag as the first parameter to change
0022 what information is listed in the output. If the requested information is
0023 not available, the device name is printed.
0024 
0025   -c    lists the compatible string of the dependencies
0026   -d    lists the driver name of the dependencies that have probed
0027   -m    lists the module name of the dependencies that have a module
0028   -f    list the firmware node path of the dependencies
0029   -g    list the dependencies as edges and nodes for graphviz
0030   -t    list the dependencies as edges for tsort
0031 
0032 The filter options provide a way to filter out some dependencies:
0033   --allow-no-driver     By default dependencies that don't have a driver
0034                         attached are ignored. This is to avoid following
0035                         device links to "class" devices that are created
0036                         when the consumer probes (as in, not a probe
0037                         dependency). If you want to follow these links
0038                         anyway, use this flag.
0039 
0040   --exclude-devlinks    Don't follow device links when tracking probe
0041                         dependencies.
0042 
0043   --exclude-parents     Don't follow parent devices when tracking probe
0044                         dependencies.
0045 
0046 EOF
0047 }
0048 
0049 function dev_to_detail() {
0050         local i=0
0051         while [ $i -lt ${#OUT_LIST[@]} ]
0052         do
0053                 local C=${OUT_LIST[i]}
0054                 local S=${OUT_LIST[i+1]}
0055                 local D="'$(detail_chosen $C $S)'"
0056                 if [ ! -z "$D" ]
0057                 then
0058                         # This weirdness is needed to work with toybox when
0059                         # using the -t option.
0060                         printf '%05u\t%s\n' ${i} "$D" | tr -d \'
0061                 fi
0062                 i=$((i+2))
0063         done
0064 }
0065 
0066 function already_seen() {
0067         local i=0
0068         while [ $i -lt ${#OUT_LIST[@]} ]
0069         do
0070                 if [ "$1" = "${OUT_LIST[$i]}" ]
0071                 then
0072                         # if-statement treats 0 (no-error) as true
0073                         return 0
0074                 fi
0075                 i=$(($i+2))
0076         done
0077 
0078         # if-statement treats 1 (error) as false
0079         return 1
0080 }
0081 
0082 # Return 0 (no-error/true) if parent was added
0083 function add_parent() {
0084 
0085         if [ ${ALLOW_PARENTS} -eq 0 ]
0086         then
0087                 return 1
0088         fi
0089 
0090         local CON=$1
0091         # $CON could be a symlink path. So, we need to find the real path and
0092         # then go up one level to find the real parent.
0093         local PARENT=$(realpath $CON/..)
0094 
0095         while [ ! -e ${PARENT}/driver ]
0096         do
0097                 if [ "$PARENT" = "/sys/devices" ]
0098                 then
0099                         return 1
0100                 fi
0101                 PARENT=$(realpath $PARENT/..)
0102         done
0103 
0104         CONSUMERS+=($PARENT)
0105         OUT_LIST+=(${CON} ${PARENT})
0106         return 0
0107 }
0108 
0109 # Return 0 (no-error/true) if one or more suppliers were added
0110 function add_suppliers() {
0111         local CON=$1
0112         local RET=1
0113 
0114         if [ ${ALLOW_DEVLINKS} -eq 0 ]
0115         then
0116                 return 1
0117         fi
0118 
0119         SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null)
0120         for SL in $SUPPLIER_LINKS;
0121         do
0122                 SYNC_STATE=$(cat $SL/sync_state_only)
0123 
0124                 # sync_state_only links are proxy dependencies.
0125                 # They can also have cycles. So, don't follow them.
0126                 if [ "$SYNC_STATE" != '0' ]
0127                 then
0128                         continue
0129                 fi
0130 
0131                 SUPPLIER=$(realpath $SL/supplier)
0132 
0133                 if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
0134                 then
0135                         continue
0136                 fi
0137 
0138                 CONSUMERS+=($SUPPLIER)
0139                 OUT_LIST+=(${CON} ${SUPPLIER})
0140                 RET=0
0141         done
0142 
0143         return $RET
0144 }
0145 
0146 function detail_compat() {
0147         f=$1/of_node/compatible
0148         if [ -e $f ]
0149         then
0150                 echo -n $(cat $f)
0151         else
0152                 echo -n $1
0153         fi
0154 }
0155 
0156 function detail_module() {
0157         f=$1/driver/module
0158         if [ -e $f ]
0159         then
0160                 echo -n $(basename $(realpath $f))
0161         else
0162                 echo -n $1
0163         fi
0164 }
0165 
0166 function detail_driver() {
0167         f=$1/driver
0168         if [ -e $f ]
0169         then
0170                 echo -n $(basename $(realpath $f))
0171         else
0172                 echo -n $1
0173         fi
0174 }
0175 
0176 function detail_fwnode() {
0177         f=$1/firmware_node
0178         if [ ! -e $f ]
0179         then
0180                 f=$1/of_node
0181         fi
0182 
0183         if [ -e $f ]
0184         then
0185                 echo -n $(realpath $f)
0186         else
0187                 echo -n $1
0188         fi
0189 }
0190 
0191 function detail_graphviz() {
0192         if [ "$2" != "ROOT" ]
0193         then
0194                 echo -n "\"$(basename $2)\"->\"$(basename $1)\""
0195         else
0196                 echo -n "\"$(basename $1)\""
0197         fi
0198 }
0199 
0200 function detail_tsort() {
0201         echo -n "\"$2\" \"$1\""
0202 }
0203 
0204 function detail_device() { echo -n $1; }
0205 
0206 alias detail=detail_device
0207 ALLOW_NO_DRIVER=0
0208 ALLOW_DEVLINKS=1
0209 ALLOW_PARENTS=1
0210 
0211 while [ $# -gt 0 ]
0212 do
0213         ARG=$1
0214         case $ARG in
0215                 --help)
0216                         help
0217                         exit 0
0218                         ;;
0219                 -c)
0220                         alias detail=detail_compat
0221                         ;;
0222                 -m)
0223                         alias detail=detail_module
0224                         ;;
0225                 -d)
0226                         alias detail=detail_driver
0227                         ;;
0228                 -f)
0229                         alias detail=detail_fwnode
0230                         ;;
0231                 -g)
0232                         alias detail=detail_graphviz
0233                         ;;
0234                 -t)
0235                         alias detail=detail_tsort
0236                         ;;
0237                 --allow-no-driver)
0238                         ALLOW_NO_DRIVER=1
0239                         ;;
0240                 --exclude-devlinks)
0241                         ALLOW_DEVLINKS=0
0242                         ;;
0243                 --exclude-parents)
0244                         ALLOW_PARENTS=0
0245                         ;;
0246                 *)
0247                         # Stop at the first argument that's not an option.
0248                         break
0249                         ;;
0250         esac
0251         shift
0252 done
0253 
0254 function detail_chosen() {
0255         detail $1 $2
0256 }
0257 
0258 if [ $# -eq 0 ]
0259 then
0260         help
0261         exit 1
0262 fi
0263 
0264 CONSUMERS=($@)
0265 OUT_LIST=()
0266 
0267 # Do a breadth first, non-recursive tracking of suppliers. The parent is also
0268 # considered a "supplier" as a device can't probe without its parent.
0269 i=0
0270 while [ $i -lt ${#CONSUMERS[@]} ]
0271 do
0272         CONSUMER=$(realpath ${CONSUMERS[$i]})
0273         i=$(($i+1))
0274 
0275         if already_seen ${CONSUMER}
0276         then
0277                 continue
0278         fi
0279 
0280         # If this is not a device with a driver, we don't care about its
0281         # suppliers.
0282         if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ]
0283         then
0284                 continue
0285         fi
0286 
0287         ROOT=1
0288 
0289         # Add suppliers to CONSUMERS list and output the consumer details.
0290         #
0291         # We don't need to worry about a cycle in the dependency chain causing
0292         # infinite loops. That's because the kernel doesn't allow cycles in
0293         # device links unless it's a sync_state_only device link. And we ignore
0294         # sync_state_only device links inside add_suppliers.
0295         if add_suppliers ${CONSUMER}
0296         then
0297                 ROOT=0
0298         fi
0299 
0300         if add_parent ${CONSUMER}
0301         then
0302                 ROOT=0
0303         fi
0304 
0305         if [ $ROOT -eq 1 ]
0306         then
0307                 OUT_LIST+=(${CONSUMER} "ROOT")
0308         fi
0309 done
0310 
0311 # Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
0312 # isn't really stable.
0313 dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-
0314 
0315 exit 0