1 #!/bin/sh 2 # SPDX-License-Identifier: GPL-2.0 3 # 4 # Generate a graph of the current DAPM state f 5 # 6 # Copyright 2024 Bootlin 7 # Author: Luca Ceresoli <luca.ceresol@bootlin.c 8 9 set -eu 10 11 STYLE_COMPONENT_ON="color=dodgerblue;style=bol 12 STYLE_COMPONENT_OFF="color=gray40;style=filled 13 STYLE_NODE_ON="shape=box,style=bold,color=gree 14 STYLE_NODE_OFF="shape=box,style=filled,color=g 15 16 # Print usage and exit 17 # 18 # $1 = exit return value 19 # $2 = error string (required if $1 != 0) 20 usage() 21 { 22 if [ "${1}" -ne 0 ]; then 23 echo "${2}" >&2 24 fi 25 26 echo " 27 Generate a graph of the current DAPM state for 28 29 The DAPM state can be obtained via debugfs for 30 a remote target, or from a local copy of the d 31 32 Usage: 33 $(basename $0) [options] -c CARD 34 $(basename $0) [options] -c CARD -r REMOTE 35 $(basename $0) [options] -d STATE_DIR 36 37 Options: 38 -c CARD Sound card to get DAPM 39 -r REMOTE_TARGET Get DAPM state from RE 40 instead of using a loc 41 -d STATE_DIR Get DAPM state from a 42 -o OUT_FILE Output file (default: 43 -D Show verbose debugging 44 -h Print this help and ex 45 46 The output format is implied by the extension 47 48 * Use the .dot extension to generate a text g 49 graphviz dot syntax. 50 * Any other extension is assumed to be a form 51 rendering, e.g. 'png', 'svg', and will prod 52 picture from it. This requires the 'dot' pr 53 package. 54 " 55 56 exit ${1} 57 } 58 59 # Connect to a remote target via SSH, collect 60 # into a tarball and get the tarball via SCP i 61 # 62 # $1 = target as used by ssh and scp, e.g. "ro 63 # $2 = sound card name 64 # $3 = temp dir path (present on the host, cre 65 # $4 = local directory to extract the tarball 66 # 67 # Requires an ssh+scp server, find and tar+gz 68 # 69 # Note: the tarball is needed because plain 's 70 # copy only empty files 71 grab_remote_files() 72 { 73 echo "Collecting DAPM state from ${1}" 74 dbg_echo "Collected DAPM state in ${3}" 75 76 ssh "${1}" " 77 set -eu && 78 cd \"/sys/kernel/debug/asoc/${2}\" && 79 find * -type d -exec mkdir -p ${3}/dapm-tree/{ 80 find * -type f -exec cp \"{}\" \"${3}/dapm-tre 81 cd ${3}/dapm-tree && 82 tar cf ${3}/dapm.tar ." 83 scp -q "${1}:${3}/dapm.tar" "${3}" 84 85 mkdir -p "${4}" 86 tar xf "${tmp_dir}/dapm.tar" -C "${4}" 87 } 88 89 # Parse a widget file and generate graph descr 90 # 91 # Skips any file named "bias_level". 92 # 93 # $1 = temporary work dir 94 # $2 = component name 95 # $3 = widget filename 96 process_dapm_widget() 97 { 98 local tmp_dir="${1}" 99 local c_name="${2}" 100 local w_file="${3}" 101 local dot_file="${tmp_dir}/main.dot" 102 local links_file="${tmp_dir}/links.dot" 103 104 local w_name="$(basename "${w_file}")" 105 local w_tag="${c_name}_${w_name}" 106 107 if [ "${w_name}" = "bias_level" ]; then 108 return 0 109 fi 110 111 dbg_echo " + Widget: ${w_name}" 112 113 cat "${w_file}" | ( 114 read line 115 116 if echo "${line}" | grep -q ': On ' 117 then local node_style="${STYLE_NODE_ON 118 else local node_style="${STYLE_NODE_OF 119 fi 120 121 local w_type="" 122 while read line; do 123 # Collect widget type if present 124 if echo "${line}" | grep -q '^widg 125 local w_type_raw="$(echo "$lin 126 dbg_echo " - Widget type: 127 128 # Note: escaping '\n' is trick 129 # bash and busybox ash, so use 130 # later 131 local w_type="%n[${w_type_raw} 132 fi 133 134 # Collect any links. We could use 135 # let's use "in" links 136 if echo "${line}" | grep -q '^in ' 137 local w_route=$(echo "$line" | 138 local w_src=$(echo "$line" | 139 awk -F\" '{p 140 sed 's/^(nu 141 dbg_echo " - Input route f 142 dbg_echo " - Route: ${w_ro 143 local w_edge_attrs="" 144 if [ "${w_route}" != "static" 145 w_edge_attrs=" [label=\"${ 146 fi 147 echo " \"${w_src}\" -> \"$w_t 148 fi 149 done 150 151 echo " \"${w_tag}\" [label=\"${w_na 152 tr '%' '\\' >> "${dot_file}" 153 ) 154 } 155 156 # Parse the DAPM tree for a sound card compone 157 # description in graphviz dot format 158 # 159 # $1 = temporary work dir 160 # $2 = component directory 161 # $3 = "ROOT" for the root card directory, emp 162 process_dapm_component() 163 { 164 local tmp_dir="${1}" 165 local c_dir="${2}" 166 local c_name="${3}" 167 local is_component=0 168 local dot_file="${tmp_dir}/main.dot" 169 local links_file="${tmp_dir}/links.dot" 170 local c_attribs="" 171 172 if [ -z "${c_name}" ]; then 173 is_component=1 174 175 # Extract directory name into componen 176 # "./cs42l51.0-004a/dapm" -> "cs42l5 177 c_name="$(basename $(dirname "${c_dir} 178 fi 179 180 dbg_echo " * Component: ${c_name}" 181 182 if [ ${is_component} = 1 ]; then 183 if [ -f "${c_dir}/bias_level" ]; then 184 c_onoff=$(sed -n -e 1p "${c_dir}/b 185 dbg_echo " - bias_level: ${c_ono 186 if [ "$c_onoff" = "On" ]; then 187 c_attribs="${STYLE_COMPONENT_O 188 elif [ "$c_onoff" = "Off" ]; then 189 c_attribs="${STYLE_COMPONENT_O 190 fi 191 fi 192 193 echo "" >> " 194 echo " subgraph \"${c_name}\" {" >> " 195 echo " cluster = true" >> " 196 echo " label = \"${c_name}\"" >> " 197 echo " ${c_attribs}" >> " 198 fi 199 200 # Create empty file to ensure it will exis 201 >"${links_file}" 202 203 # Iterate over widgets in the component di 204 for w_file in ${c_dir}/*; do 205 process_dapm_widget "${tmp_dir}" "${c_ 206 done 207 208 if [ ${is_component} = 1 ]; then 209 echo " }" >> "${dot_file}" 210 fi 211 212 cat "${links_file}" >> "${dot_file}" 213 } 214 215 # Parse the DAPM tree for a sound card and gen 216 # graphviz dot format 217 # 218 # $1 = temporary work dir 219 # $2 = directory tree with DAPM state (either 220 process_dapm_tree() 221 { 222 local tmp_dir="${1}" 223 local dapm_dir="${2}" 224 local dot_file="${tmp_dir}/main.dot" 225 226 echo "digraph G {" > "${dot_file}" 227 echo " fontname=\"sans-serif\"" >> "${dot 228 echo " node [fontname=\"sans-serif\"]" >> 229 echo " edge [fontname=\"sans-serif\"]" >> 230 231 # Process root directory (no component) 232 process_dapm_component "${tmp_dir}" "${dap 233 234 # Iterate over components 235 for c_dir in "${dapm_dir}"/*/dapm 236 do 237 process_dapm_component "${tmp_dir}" "$ 238 done 239 240 echo "}" >> "${dot_file}" 241 } 242 243 main() 244 { 245 # Parse command line 246 local out_file="dapm.dot" 247 local card_name="" 248 local remote_target="" 249 local dapm_tree="" 250 local dbg_on="" 251 while getopts "c:r:d:o:Dh" arg; do 252 case $arg in 253 c) card_name="${OPTARG}" ;; 254 r) remote_target="${OPTARG}" ;; 255 d) dapm_tree="${OPTARG}" ;; 256 o) out_file="${OPTARG}" ;; 257 D) dbg_on="1" ;; 258 h) usage 0 ;; 259 *) usage 1 ;; 260 esac 261 done 262 shift $(($OPTIND - 1)) 263 264 if [ -n "${dapm_tree}" ]; then 265 if [ -n "${card_name}${remote_target}" 266 usage 1 "Cannot use -c and -r with 267 fi 268 echo "Using local tree: ${dapm_tree}" 269 elif [ -n "${remote_target}" ]; then 270 if [ -z "${card_name}" ]; then 271 usage 1 "-r requires -c" 272 fi 273 echo "Using card ${card_name} from rem 274 elif [ -n "${card_name}" ]; then 275 echo "Using local card: ${card_name}" 276 else 277 usage 1 "Please choose mode using -c, 278 fi 279 280 # Define logging function 281 if [ "${dbg_on}" ]; then 282 dbg_echo() { 283 echo "$*" >&2 284 } 285 else 286 dbg_echo() { 287 : 288 } 289 fi 290 291 # Filename must have a dot in order the in 292 # extension 293 if ! echo "${out_file}" | grep -qE '\.'; t 294 echo "Missing extension in output file 295 usage 296 exit 1 297 fi 298 299 local out_fmt="${out_file##*.}" 300 local dot_file="${out_file%.*}.dot" 301 302 dbg_echo "dot file: $dot_file" 303 dbg_echo "Output file: $out_file" 304 dbg_echo "Output format: $out_fmt" 305 306 tmp_dir="$(mktemp -d /tmp/$(basename $0).X 307 trap "{ rm -fr ${tmp_dir}; }" INT TERM EXI 308 309 if [ -z "${dapm_tree}" ] 310 then 311 dapm_tree="/sys/kernel/debug/asoc/${ca 312 fi 313 if [ -n "${remote_target}" ]; then 314 dapm_tree="${tmp_dir}/dapm-tree" 315 grab_remote_files "${remote_target}" " 316 fi 317 # In all cases now ${dapm_tree} contains t 318 319 process_dapm_tree "${tmp_dir}" "${dapm_tre 320 cp "${tmp_dir}/main.dot" "${dot_file}" 321 322 if [ "${out_file}" != "${dot_file}" ]; the 323 dot -T"${out_fmt}" "${dot_file}" -o "$ 324 fi 325 326 echo "Generated file ${out_file}" 327 } 328 329 main "${@}"
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.