dev-needs.sh 6.1 KB

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