linux/scripts/dev-needs.sh
<<
>>
Prefs
   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
   6function help() {
   7        cat << EOF
   8Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices>
   9
  10This script needs to be run on the target device once it has booted to a
  11shell.
  12
  13The 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
  15parents) of these devices. It does a breadth first search of the dependency
  16chain, so the last entry in the output is close to the root of the
  17dependency chain.
  18
  19By default it lists the full path to the devices under /sys/devices.
  20
  21It also takes an optional modifier flag as the first parameter to change
  22what information is listed in the output. If the requested information is
  23not 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
  32The 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
  46EOF
  47}
  48
  49function 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
  66function 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
  83function 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
 110function 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
 146function 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
 156function 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
 166function 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
 176function 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
 191function 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
 200function detail_tsort() {
 201        echo -n "\"$2\" \"$1\""
 202}
 203
 204function detail_device() { echo -n $1; }
 205
 206alias detail=detail_device
 207ALLOW_NO_DRIVER=0
 208ALLOW_DEVLINKS=1
 209ALLOW_PARENTS=1
 210
 211while [ $# -gt 0 ]
 212do
 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
 252done
 253
 254function detail_chosen() {
 255        detail $1 $2
 256}
 257
 258if [ $# -eq 0 ]
 259then
 260        help
 261        exit 1
 262fi
 263
 264CONSUMERS=($@)
 265OUT_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.
 269i=0
 270while [ $i -lt ${#CONSUMERS[@]} ]
 271do
 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
 309done
 310
 311# Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox
 312# isn't really stable.
 313dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2-
 314
 315exit 0
 316