~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/sound/dapm-graph

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #!/bin/sh
  2 # SPDX-License-Identifier: GPL-2.0
  3 #
  4 # Generate a graph of the current DAPM state for an audio card
  5 #
  6 # Copyright 2024 Bootlin
  7 # Author: Luca Ceresoli <luca.ceresol@bootlin.com>
  8 
  9 set -eu
 10 
 11 STYLE_COMPONENT_ON="color=dodgerblue;style=bold"
 12 STYLE_COMPONENT_OFF="color=gray40;style=filled;fillcolor=gray90"
 13 STYLE_NODE_ON="shape=box,style=bold,color=green4"
 14 STYLE_NODE_OFF="shape=box,style=filled,color=gray30,fillcolor=gray95"
 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 an audio card.
 28 
 29 The DAPM state can be obtained via debugfs for a card on the local host or
 30 a remote target, or from a local copy of the debugfs tree for the card.
 31 
 32 Usage:
 33     $(basename $0) [options] -c CARD                  - Local sound card
 34     $(basename $0) [options] -c CARD -r REMOTE_TARGET - Card on remote system
 35     $(basename $0) [options] -d STATE_DIR             - Local directory
 36 
 37 Options:
 38     -c CARD             Sound card to get DAPM state of
 39     -r REMOTE_TARGET    Get DAPM state from REMOTE_TARGET via SSH and SCP
 40                         instead of using a local sound card
 41     -d STATE_DIR        Get DAPM state from a local copy of a debugfs tree
 42     -o OUT_FILE         Output file (default: dapm.dot)
 43     -D                  Show verbose debugging info
 44     -h                  Print this help and exit
 45 
 46 The output format is implied by the extension of OUT_FILE:
 47 
 48  * Use the .dot extension to generate a text graph representation in
 49    graphviz dot syntax.
 50  * Any other extension is assumed to be a format supported by graphviz for
 51    rendering, e.g. 'png', 'svg', and will produce both the .dot file and a
 52    picture from it. This requires the 'dot' program from the graphviz
 53    package.
 54 "
 55 
 56     exit ${1}
 57 }
 58 
 59 # Connect to a remote target via SSH, collect all DAPM files from debufs
 60 # into a tarball and get the tarball via SCP into $3/dapm.tar
 61 #
 62 # $1 = target as used by ssh and scp, e.g. "root@192.168.1.1"
 63 # $2 = sound card name
 64 # $3 = temp dir path (present on the host, created on the target)
 65 # $4 = local directory to extract the tarball into
 66 #
 67 # Requires an ssh+scp server, find and tar+gz on the target
 68 #
 69 # Note: the tarball is needed because plain 'scp -r' from debugfs would
 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-tree/{}\" \; &&
 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 description in graphviz dot format
 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_OFF}"
119         fi
120 
121         local w_type=""
122         while read line; do
123             # Collect widget type if present
124             if echo "${line}" | grep -q '^widget-type '; then
125                 local w_type_raw="$(echo "$line" | cut -d ' ' -f 2)"
126                 dbg_echo "     - Widget type: ${w_type_raw}"
127 
128                 # Note: escaping '\n' is tricky to get working with both
129                 # bash and busybox ash, so use a '%' here and replace it
130                 # later
131                 local w_type="%n[${w_type_raw}]"
132             fi
133 
134             # Collect any links. We could use "in" links or "out" links,
135             # let's use "in" links
136             if echo "${line}" | grep -q '^in '; then
137                 local w_route=$(echo "$line" | awk -F\" '{print $2}')
138                 local w_src=$(echo "$line" |
139                                   awk -F\" '{print $6 "_" $4}' |
140                                   sed  's/^(null)_/ROOT_/')
141                 dbg_echo "     - Input route from: ${w_src}"
142                 dbg_echo "     - Route: ${w_route}"
143                 local w_edge_attrs=""
144                 if [ "${w_route}" != "static" ]; then
145                     w_edge_attrs=" [label=\"${w_route}\"]"
146                 fi
147                 echo "  \"${w_src}\" -> \"$w_tag\"${w_edge_attrs}" >> "${links_file}"
148             fi
149         done
150 
151         echo "    \"${w_tag}\" [label=\"${w_name}${w_type}\",${node_style}]" |
152             tr '%' '\\' >> "${dot_file}"
153    )
154 }
155 
156 # Parse the DAPM tree for a sound card component and generate graph
157 # description in graphviz dot format
158 #
159 # $1 = temporary work dir
160 # $2 = component directory
161 # $3 = "ROOT" for the root card directory, empty otherwise
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 component name:
176         #   "./cs42l51.0-004a/dapm" -> "cs42l51.0-004a"
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}/bias_level" | awk '{print $1}')
185             dbg_echo "   - bias_level: ${c_onoff}"
186             if [ "$c_onoff" = "On" ]; then
187                 c_attribs="${STYLE_COMPONENT_ON}"
188             elif [ "$c_onoff" = "Off" ]; then
189                 c_attribs="${STYLE_COMPONENT_OFF}"
190             fi
191         fi
192 
193         echo ""                           >> "${dot_file}"
194         echo "  subgraph \"${c_name}\" {" >> "${dot_file}"
195         echo "    cluster = true"         >> "${dot_file}"
196         echo "    label = \"${c_name}\""  >> "${dot_file}"
197         echo "    ${c_attribs}"           >> "${dot_file}"
198     fi
199 
200     # Create empty file to ensure it will exist in all cases
201     >"${links_file}"
202 
203     # Iterate over widgets in the component dir
204     for w_file in ${c_dir}/*; do
205         process_dapm_widget "${tmp_dir}" "${c_name}" "${w_file}"
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 generate graph description in
216 # graphviz dot format
217 #
218 # $1 = temporary work dir
219 # $2 = directory tree with DAPM state (either in debugfs or a mirror)
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_file}"
228     echo "  node [fontname=\"sans-serif\"]" >> "${dot_file}"
229     echo "  edge [fontname=\"sans-serif\"]" >> "${dot_file}"
230 
231     # Process root directory (no component)
232     process_dapm_component "${tmp_dir}" "${dapm_dir}/dapm" "ROOT"
233 
234     # Iterate over components
235     for c_dir in "${dapm_dir}"/*/dapm
236     do
237         process_dapm_component "${tmp_dir}" "${c_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}" ]; then
266             usage 1 "Cannot use -c and -r with -d"
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 remote target ${remote_target}"
274     elif [ -n "${card_name}" ]; then
275         echo "Using local card: ${card_name}"
276     else
277         usage 1 "Please choose mode using -c, -r or -d"
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 infer the format from the
292     # extension
293     if ! echo "${out_file}" | grep -qE '\.'; then
294         echo "Missing extension in output filename ${out_file}" >&2
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).XXXXXX)"
307     trap "{ rm -fr ${tmp_dir}; }" INT TERM EXIT
308 
309     if [ -z "${dapm_tree}" ]
310     then
311         dapm_tree="/sys/kernel/debug/asoc/${card_name}"
312     fi
313     if [ -n "${remote_target}" ]; then
314         dapm_tree="${tmp_dir}/dapm-tree"
315         grab_remote_files "${remote_target}" "${card_name}" "${tmp_dir}" "${dapm_tree}"
316     fi
317     # In all cases now ${dapm_tree} contains the DAPM state
318 
319     process_dapm_tree "${tmp_dir}" "${dapm_tree}"
320     cp "${tmp_dir}/main.dot" "${dot_file}"
321 
322     if [ "${out_file}" != "${dot_file}" ]; then
323         dot -T"${out_fmt}" "${dot_file}" -o "${out_file}"
324     fi
325 
326     echo "Generated file ${out_file}"
327 }
328 
329 main "${@}"

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php