forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
6.1 KiB
315 lines
6.1 KiB
#! /bin/sh |
|
# SPDX-License-Identifier: GPL-2.0 |
|
# Copyright (c) 2020, Google LLC. All rights reserved. |
|
# Author: Saravana Kannan <[email protected]> |
|
|
|
function help() { |
|
cat << EOF |
|
Usage: $(basename $0) [-c|-d|-m|-f] [filter options] <list of devices> |
|
|
|
This script needs to be run on the target device once it has booted to a |
|
shell. |
|
|
|
The script takes as input a list of one or more device directories under |
|
/sys/devices and then lists the probe dependency chain (suppliers and |
|
parents) of these devices. It does a breadth first search of the dependency |
|
chain, so the last entry in the output is close to the root of the |
|
dependency chain. |
|
|
|
By default it lists the full path to the devices under /sys/devices. |
|
|
|
It also takes an optional modifier flag as the first parameter to change |
|
what information is listed in the output. If the requested information is |
|
not available, the device name is printed. |
|
|
|
-c lists the compatible string of the dependencies |
|
-d lists the driver name of the dependencies that have probed |
|
-m lists the module name of the dependencies that have a module |
|
-f list the firmware node path of the dependencies |
|
-g list the dependencies as edges and nodes for graphviz |
|
-t list the dependencies as edges for tsort |
|
|
|
The filter options provide a way to filter out some dependencies: |
|
--allow-no-driver By default dependencies that don't have a driver |
|
attached are ignored. This is to avoid following |
|
device links to "class" devices that are created |
|
when the consumer probes (as in, not a probe |
|
dependency). If you want to follow these links |
|
anyway, use this flag. |
|
|
|
--exclude-devlinks Don't follow device links when tracking probe |
|
dependencies. |
|
|
|
--exclude-parents Don't follow parent devices when tracking probe |
|
dependencies. |
|
|
|
EOF |
|
} |
|
|
|
function dev_to_detail() { |
|
local i=0 |
|
while [ $i -lt ${#OUT_LIST[@]} ] |
|
do |
|
local C=${OUT_LIST[i]} |
|
local S=${OUT_LIST[i+1]} |
|
local D="'$(detail_chosen $C $S)'" |
|
if [ ! -z "$D" ] |
|
then |
|
# This weirdness is needed to work with toybox when |
|
# using the -t option. |
|
printf '%05u\t%s\n' ${i} "$D" | tr -d \' |
|
fi |
|
i=$((i+2)) |
|
done |
|
} |
|
|
|
function already_seen() { |
|
local i=0 |
|
while [ $i -lt ${#OUT_LIST[@]} ] |
|
do |
|
if [ "$1" = "${OUT_LIST[$i]}" ] |
|
then |
|
# if-statement treats 0 (no-error) as true |
|
return 0 |
|
fi |
|
i=$(($i+2)) |
|
done |
|
|
|
# if-statement treats 1 (error) as false |
|
return 1 |
|
} |
|
|
|
# Return 0 (no-error/true) if parent was added |
|
function add_parent() { |
|
|
|
if [ ${ALLOW_PARENTS} -eq 0 ] |
|
then |
|
return 1 |
|
fi |
|
|
|
local CON=$1 |
|
# $CON could be a symlink path. So, we need to find the real path and |
|
# then go up one level to find the real parent. |
|
local PARENT=$(realpath $CON/..) |
|
|
|
while [ ! -e ${PARENT}/driver ] |
|
do |
|
if [ "$PARENT" = "/sys/devices" ] |
|
then |
|
return 1 |
|
fi |
|
PARENT=$(realpath $PARENT/..) |
|
done |
|
|
|
CONSUMERS+=($PARENT) |
|
OUT_LIST+=(${CON} ${PARENT}) |
|
return 0 |
|
} |
|
|
|
# Return 0 (no-error/true) if one or more suppliers were added |
|
function add_suppliers() { |
|
local CON=$1 |
|
local RET=1 |
|
|
|
if [ ${ALLOW_DEVLINKS} -eq 0 ] |
|
then |
|
return 1 |
|
fi |
|
|
|
SUPPLIER_LINKS=$(ls -1d $CON/supplier:* 2>/dev/null) |
|
for SL in $SUPPLIER_LINKS; |
|
do |
|
SYNC_STATE=$(cat $SL/sync_state_only) |
|
|
|
# sync_state_only links are proxy dependencies. |
|
# They can also have cycles. So, don't follow them. |
|
if [ "$SYNC_STATE" != '0' ] |
|
then |
|
continue |
|
fi |
|
|
|
SUPPLIER=$(realpath $SL/supplier) |
|
|
|
if [ ! -e $SUPPLIER/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] |
|
then |
|
continue |
|
fi |
|
|
|
CONSUMERS+=($SUPPLIER) |
|
OUT_LIST+=(${CON} ${SUPPLIER}) |
|
RET=0 |
|
done |
|
|
|
return $RET |
|
} |
|
|
|
function detail_compat() { |
|
f=$1/of_node/compatible |
|
if [ -e $f ] |
|
then |
|
echo -n $(cat $f) |
|
else |
|
echo -n $1 |
|
fi |
|
} |
|
|
|
function detail_module() { |
|
f=$1/driver/module |
|
if [ -e $f ] |
|
then |
|
echo -n $(basename $(realpath $f)) |
|
else |
|
echo -n $1 |
|
fi |
|
} |
|
|
|
function detail_driver() { |
|
f=$1/driver |
|
if [ -e $f ] |
|
then |
|
echo -n $(basename $(realpath $f)) |
|
else |
|
echo -n $1 |
|
fi |
|
} |
|
|
|
function detail_fwnode() { |
|
f=$1/firmware_node |
|
if [ ! -e $f ] |
|
then |
|
f=$1/of_node |
|
fi |
|
|
|
if [ -e $f ] |
|
then |
|
echo -n $(realpath $f) |
|
else |
|
echo -n $1 |
|
fi |
|
} |
|
|
|
function detail_graphviz() { |
|
if [ "$2" != "ROOT" ] |
|
then |
|
echo -n "\"$(basename $2)\"->\"$(basename $1)\"" |
|
else |
|
echo -n "\"$(basename $1)\"" |
|
fi |
|
} |
|
|
|
function detail_tsort() { |
|
echo -n "\"$2\" \"$1\"" |
|
} |
|
|
|
function detail_device() { echo -n $1; } |
|
|
|
alias detail=detail_device |
|
ALLOW_NO_DRIVER=0 |
|
ALLOW_DEVLINKS=1 |
|
ALLOW_PARENTS=1 |
|
|
|
while [ $# -gt 0 ] |
|
do |
|
ARG=$1 |
|
case $ARG in |
|
--help) |
|
help |
|
exit 0 |
|
;; |
|
-c) |
|
alias detail=detail_compat |
|
;; |
|
-m) |
|
alias detail=detail_module |
|
;; |
|
-d) |
|
alias detail=detail_driver |
|
;; |
|
-f) |
|
alias detail=detail_fwnode |
|
;; |
|
-g) |
|
alias detail=detail_graphviz |
|
;; |
|
-t) |
|
alias detail=detail_tsort |
|
;; |
|
--allow-no-driver) |
|
ALLOW_NO_DRIVER=1 |
|
;; |
|
--exclude-devlinks) |
|
ALLOW_DEVLINKS=0 |
|
;; |
|
--exclude-parents) |
|
ALLOW_PARENTS=0 |
|
;; |
|
*) |
|
# Stop at the first argument that's not an option. |
|
break |
|
;; |
|
esac |
|
shift |
|
done |
|
|
|
function detail_chosen() { |
|
detail $1 $2 |
|
} |
|
|
|
if [ $# -eq 0 ] |
|
then |
|
help |
|
exit 1 |
|
fi |
|
|
|
CONSUMERS=($@) |
|
OUT_LIST=() |
|
|
|
# Do a breadth first, non-recursive tracking of suppliers. The parent is also |
|
# considered a "supplier" as a device can't probe without its parent. |
|
i=0 |
|
while [ $i -lt ${#CONSUMERS[@]} ] |
|
do |
|
CONSUMER=$(realpath ${CONSUMERS[$i]}) |
|
i=$(($i+1)) |
|
|
|
if already_seen ${CONSUMER} |
|
then |
|
continue |
|
fi |
|
|
|
# If this is not a device with a driver, we don't care about its |
|
# suppliers. |
|
if [ ! -e ${CONSUMER}/driver -a ${ALLOW_NO_DRIVER} -eq 0 ] |
|
then |
|
continue |
|
fi |
|
|
|
ROOT=1 |
|
|
|
# Add suppliers to CONSUMERS list and output the consumer details. |
|
# |
|
# We don't need to worry about a cycle in the dependency chain causing |
|
# infinite loops. That's because the kernel doesn't allow cycles in |
|
# device links unless it's a sync_state_only device link. And we ignore |
|
# sync_state_only device links inside add_suppliers. |
|
if add_suppliers ${CONSUMER} |
|
then |
|
ROOT=0 |
|
fi |
|
|
|
if add_parent ${CONSUMER} |
|
then |
|
ROOT=0 |
|
fi |
|
|
|
if [ $ROOT -eq 1 ] |
|
then |
|
OUT_LIST+=(${CONSUMER} "ROOT") |
|
fi |
|
done |
|
|
|
# Can NOT combine sort and uniq using sort -suk2 because stable sort in toybox |
|
# isn't really stable. |
|
dev_to_detail | sort -k2 -k1 | uniq -f 1 | sort | cut -f2- |
|
|
|
exit 0
|
|
|