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

TOMOYO Linux Cross Reference
Linux/tools/gpio/gpio-sloppy-logic-analyzer.sh

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ 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.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #!/bin/sh -eu
  2 # SPDX-License-Identifier: GPL-2.0
  3 #
  4 # Helper script for the Linux Kernel GPIO sloppy logic analyzer
  5 #
  6 # Copyright (C) Wolfram Sang <wsa@sang-engineering.com>
  7 # Copyright (C) Renesas Electronics Corporation
  8 
  9 samplefreq=1000000
 10 numsamples=250000
 11 cpusetdefaultdir='/sys/fs/cgroup'
 12 cpusetprefix='cpuset.'
 13 debugdir='/sys/kernel/debug'
 14 ladirname='gpio-sloppy-logic-analyzer'
 15 outputdir="$PWD"
 16 neededcmds='taskset zip'
 17 max_chans=8
 18 duration=
 19 initcpu=
 20 listinstances=0
 21 lainstance=
 22 lasysfsdir=
 23 triggerdat=
 24 trigger_bindat=
 25 progname="${0##*/}"
 26 print_help()
 27 {
 28         cat << EOF
 29 $progname - helper script for the Linux Kernel Sloppy GPIO Logic Analyzer
 30 Available options:
 31         -c|--cpu <n>: which CPU to isolate for sampling. Only needed once. Default <1>.
 32                       Remember that a more powerful CPU gives you higher sampling speeds.
 33                       Also CPU0 is not recommended as it usually does extra bookkeeping.
 34         -d|--duration-us <SI-n>: number of microseconds to sample. Overrides -n, no default value.
 35         -h|--help: print this help
 36         -i|--instance <str>: name of the logic analyzer in case you have multiple instances. Default
 37                              to first instance found
 38         -k|--kernel-debug-dir <str>: path to the kernel debugfs mountpoint. Default: <$debugdir>
 39         -l|--list-instances: list all available instances
 40         -n|--num_samples <SI-n>: number of samples to acquire. Default <$numsamples>
 41         -o|--output-dir <str>: directory to put the result files. Default: current dir
 42         -s|--sample_freq <SI-n>: desired sampling frequency. Might be capped if too large.
 43                                  Default: <1000000>
 44         -t|--trigger <str>: pattern to use as trigger. <str> consists of two-char pairs. First
 45                             char is channel number starting at "1". Second char is trigger level:
 46                             "L" - low; "H" - high; "R" - rising; "F" - falling
 47                             These pairs can be combined with "+", so "1H+2F" triggers when probe 1
 48                             is high while probe 2 has a falling edge. You can have multiple triggers
 49                             combined with ",". So, "1H+2F,1H+2R" is like the example before but it
 50                             waits for a rising edge on probe 2 while probe 1 is still high after the
 51                             first trigger has been met.
 52                             Trigger data will only be used for the next capture and then be erased.
 53 
 54 <SI-n> is an integer value where SI units "T", "G", "M", "K" are recognized, e.g. '1M500K' is 1500000.
 55 
 56 Examples:
 57 Samples $numsamples values at 1MHz with an already prepared CPU or automatically prepares CPU1 if needed,
 58 use the first logic analyzer instance found:
 59         '$progname'
 60 Samples 50us at 2MHz waiting for a falling edge on channel 2. CPU and instance as above:
 61         '$progname -d 50 -s 2M -t "2F"'
 62 
 63 Note that the process exits after checking all parameters but a sub-process still works in
 64 the background. The result is only available once the sub-process finishes.
 65 
 66 Result is a .sr file to be consumed with PulseView from the free Sigrok project. It is
 67 a zip file which also contains the binary sample data which may be consumed by others.
 68 The filename is the logic analyzer instance name plus a since-epoch timestamp.
 69 EOF
 70 }
 71 
 72 fail()
 73 {
 74         echo "$1"
 75         exit 1
 76 }
 77 
 78 parse_si()
 79 {
 80         conv_si="$(printf $1 | sed 's/[tT]+\?/*1000G+/g; s/[gG]+\?/*1000M+/g; s/[mM]+\?/*1000K+/g; s/[kK]+\?/*1000+/g; s/+$//')"
 81         si_val="$((conv_si))"
 82 }
 83 set_newmask()
 84 {
 85         for f in $(find "$1" -iname "$2"); do echo "$newmask" > "$f" 2>/dev/null || true; done
 86 }
 87 
 88 init_cpu()
 89 {
 90         isol_cpu="$1"
 91 
 92         [ -d "$lacpusetdir" ] || mkdir "$lacpusetdir"
 93 
 94         cur_cpu=$(cat "${lacpusetfile}cpus")
 95         [ "$cur_cpu" = "$isol_cpu" ] && return
 96         [ -z "$cur_cpu" ] || fail "CPU$isol_cpu requested but CPU$cur_cpu already isolated"
 97 
 98         echo "$isol_cpu" > "${lacpusetfile}cpus" || fail "Could not isolate CPU$isol_cpu. Does it exist?"
 99         echo 1 > "${lacpusetfile}cpu_exclusive"
100         echo 0 > "${lacpusetfile}mems"
101 
102         oldmask=$(cat /proc/irq/default_smp_affinity)
103         newmask=$(printf "%x" $((0x$oldmask & ~(1 << isol_cpu))))
104 
105         set_newmask '/proc/irq' '*smp_affinity'
106         set_newmask '/sys/devices/virtual/workqueue/' 'cpumask'
107 
108         # Move tasks away from isolated CPU
109         for p in $(ps -o pid | tail -n +2); do
110                 mask=$(taskset -p "$p") || continue
111                 # Ignore tasks with a custom mask, i.e. not equal $oldmask
112                 [ "${mask##*: }" = "$oldmask" ] || continue
113                 taskset -p "$newmask" "$p" || continue
114         done 2>/dev/null >/dev/null
115 
116         # Big hammer! Working with 'rcu_momentary_dyntick_idle()' for a more fine-grained solution
117         # still printed warnings. Same for re-enabling the stall detector after sampling.
118         echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
119 
120         cpufreqgov="/sys/devices/system/cpu/cpu$isol_cpu/cpufreq/scaling_governor"
121         [ -w "$cpufreqgov" ] && echo 'performance' > "$cpufreqgov" || true
122 }
123 
124 parse_triggerdat()
125 {
126         oldifs="$IFS"
127         IFS=','; for trig in $1; do
128                 mask=0; val1=0; val2=0
129                 IFS='+'; for elem in $trig; do
130                         chan=${elem%[lhfrLHFR]}
131                         mode=${elem#$chan}
132                         # Check if we could parse something and the channel number fits
133                         [ "$chan" != "$elem" ] && [ "$chan" -le $max_chans ] || fail "Trigger syntax error: $elem"
134                         bit=$((1 << (chan - 1)))
135                         mask=$((mask | bit))
136                         case $mode in
137                                 [hH]) val1=$((val1 | bit)); val2=$((val2 | bit));;
138                                 [fF]) val1=$((val1 | bit));;
139                                 [rR]) val2=$((val2 | bit));;
140                         esac
141                 done
142                 trigger_bindat="$trigger_bindat$(printf '\\%o\\%o' $mask $val1)"
143                 [ $val1 -ne $val2 ] && trigger_bindat="$trigger_bindat$(printf '\\%o\\%o' $mask $val2)"
144         done
145         IFS="$oldifs"
146 }
147 
148 do_capture()
149 {
150         taskset "$1" echo 1 > "$lasysfsdir"/capture || fail "Capture error! Check kernel log"
151 
152         srtmp=$(mktemp -d)
153         echo 1 > "$srtmp"/version
154         cp "$lasysfsdir"/sample_data "$srtmp"/logic-1-1
155         cat > "$srtmp"/metadata << EOF
156 [global]
157 sigrok version=0.2.0
158 
159 [device 1]
160 capturefile=logic-1
161 total probes=$(wc -l < "$lasysfsdir"/meta_data)
162 samplerate=${samplefreq}Hz
163 unitsize=1
164 EOF
165         cat "$lasysfsdir"/meta_data >> "$srtmp"/metadata
166 
167         zipname="$outputdir/${lasysfsdir##*/}-$(date +%s).sr"
168         zip -jq "$zipname" "$srtmp"/*
169         rm -rf "$srtmp"
170         delay_ack=$(cat "$lasysfsdir"/delay_ns_acquisition)
171         [ "$delay_ack" -eq 0 ] && delay_ack=1
172         echo "Logic analyzer done. Saved '$zipname'"
173         echo "Max sample frequency this time: $((1000000000 / delay_ack))Hz."
174 }
175 
176 rep=$(getopt -a -l cpu:,duration-us:,help,instance:,list-instances,kernel-debug-dir:,num_samples:,output-dir:,sample_freq:,trigger: -o c:d:hi:k:ln:o:s:t: -- "$@") || exit 1
177 eval set -- "$rep"
178 while true; do
179         case "$1" in
180         -c|--cpu) initcpu="$2"; shift;;
181         -d|--duration-us) parse_si $2; duration=$si_val; shift;;
182         -h|--help) print_help; exit 0;;
183         -i|--instance) lainstance="$2"; shift;;
184         -k|--kernel-debug-dir) debugdir="$2"; shift;;
185         -l|--list-instances) listinstances=1;;
186         -n|--num_samples) parse_si $2; numsamples=$si_val; shift;;
187         -o|--output-dir) outputdir="$2"; shift;;
188         -s|--sample_freq) parse_si $2; samplefreq=$si_val; shift;;
189         -t|--trigger) triggerdat="$2"; shift;;
190         --) break;;
191         *) fail "error parsing command line: $*";;
192         esac
193         shift
194 done
195 
196 for f in $neededcmds; do
197         command -v "$f" >/dev/null || fail "Command '$f' not found"
198 done
199 
200 # print cpuset mountpoint if any, errorcode > 0 if noprefix option was found
201 cpusetdir=$(awk '$3 == "cgroup" && $4 ~ /cpuset/ { print $2; exit (match($4, /noprefix/) > 0) }' /proc/self/mounts) || cpusetprefix=''
202 if [ -z "$cpusetdir" ]; then
203         cpusetdir="$cpusetdefaultdir"
204         [ -d $cpusetdir ] || mkdir $cpusetdir
205         mount -t cgroup -o cpuset none $cpusetdir || fail "Couldn't mount cpusets. Not in kernel or already in use?"
206 fi
207 
208 lacpusetdir="$cpusetdir/$ladirname"
209 lacpusetfile="$lacpusetdir/$cpusetprefix"
210 sysfsdir="$debugdir/$ladirname"
211 
212 [ "$samplefreq" -ne 0 ] || fail "Invalid sample frequency"
213 
214 [ -d "$sysfsdir" ] || fail "Could not find logic analyzer root dir '$sysfsdir'. Module loaded?"
215 [ -x "$sysfsdir" ] || fail "Could not access logic analyzer root dir '$sysfsdir'. Need root?"
216 
217 [ $listinstances -gt 0 ] && find "$sysfsdir" -mindepth 1 -type d | sed 's|.*/||' && exit 0
218 
219 if [ -n "$lainstance" ]; then
220         lasysfsdir="$sysfsdir/$lainstance"
221 else
222         lasysfsdir=$(find "$sysfsdir" -mindepth 1 -type d -print -quit)
223 fi
224 [ -d "$lasysfsdir" ] || fail "Logic analyzer directory '$lasysfsdir' not found!"
225 [ -d "$outputdir" ] || fail "Output directory '$outputdir' not found!"
226 
227 [ -n "$initcpu" ] && init_cpu "$initcpu"
228 [ -d "$lacpusetdir" ] || { echo "Auto-Isolating CPU1"; init_cpu 1; }
229 
230 ndelay=$((1000000000 / samplefreq))
231 echo "$ndelay" > "$lasysfsdir"/delay_ns
232 
233 [ -n "$duration" ] && numsamples=$((samplefreq * duration / 1000000))
234 echo $numsamples > "$lasysfsdir"/buf_size
235 
236 if [ -n "$triggerdat" ]; then
237         parse_triggerdat "$triggerdat"
238         printf "$trigger_bindat" > "$lasysfsdir"/trigger 2>/dev/null || fail "Trigger data '$triggerdat' rejected"
239 fi
240 
241 workcpu=$(cat "${lacpusetfile}effective_cpus")
242 [ -n "$workcpu" ] || fail "No isolated CPU found"
243 cpumask=$(printf '%x' $((1 << workcpu)))
244 instance=${lasysfsdir##*/}
245 echo "Setting up '$instance': $numsamples samples at ${samplefreq}Hz with ${triggerdat:-no} trigger using CPU$workcpu"
246 do_capture "$cpumask" &

~ [ 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