Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: add sloppy logic analyzer using polling

This is a sloppy logic analyzer using GPIOs. It comes with a script to
isolate a CPU for polling. While this is definitely not a production
level analyzer, it can be a helpful first view when remote debugging.
Read the documentation for details.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20240620094159.6785-2-wsa+renesas@sang-engineering.com
[Bartosz: moved the Kconfig entry into a different category]
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Wolfram Sang and committed by
Bartosz Golaszewski
7828b7bb 6a9c1508

+704
+93
Documentation/dev-tools/gpio-sloppy-logic-analyzer.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ============================================= 4 + Linux Kernel GPIO based sloppy logic analyzer 5 + ============================================= 6 + 7 + :Author: Wolfram Sang 8 + 9 + Introduction 10 + ============ 11 + 12 + This document briefly describes how to run the GPIO based in-kernel sloppy 13 + logic analyzer running on an isolated CPU. 14 + 15 + The sloppy logic analyzer will utilize a few GPIO lines in input mode on a 16 + system to rapidly sample these digital lines, which will, if the Nyquist 17 + criteria is met, result in a time series log with approximate waveforms as they 18 + appeared on these lines. One way to use it is to analyze external traffic 19 + connected to these GPIO lines with wires (i.e. digital probes), acting as a 20 + common logic analyzer. 21 + 22 + Another feature is to snoop on on-chip peripherals if the I/O cells of these 23 + peripherals can be used in GPIO input mode at the same time as they are being 24 + used as inputs or outputs for the peripheral. That means you could e.g. snoop 25 + I2C traffic without any wiring (if your hardware supports it). In the pin 26 + control subsystem such pin controllers are called "non-strict": a certain pin 27 + can be used with a certain peripheral and as a GPIO input line at the same 28 + time. 29 + 30 + Note that this is a last resort analyzer which can be affected by latencies, 31 + non-deterministic code paths and non-maskable interrupts. It is called 'sloppy' 32 + for a reason. However, for e.g. remote development, it may be useful to get a 33 + first view and aid further debugging. 34 + 35 + Setup 36 + ===== 37 + 38 + Your kernel must have CONFIG_DEBUG_FS and CONFIG_CPUSETS enabled. Ideally, your 39 + runtime environment does not utilize cpusets otherwise, then isolation of a CPU 40 + core is easiest. If you do need cpusets, check that helper script for the 41 + sloppy logic analyzer does not interfere with your other settings. 42 + 43 + Tell the kernel which GPIOs are used as probes. For a Device Tree based system, 44 + you need to use the following bindings. Because these bindings are only for 45 + debugging, there is no official schema:: 46 + 47 + i2c-analyzer { 48 + compatible = "gpio-sloppy-logic-analyzer"; 49 + probe-gpios = <&gpio6 21 GPIO_OPEN_DRAIN>, <&gpio6 4 GPIO_OPEN_DRAIN>; 50 + probe-names = "SCL", "SDA"; 51 + }; 52 + 53 + Note that you must provide a name for every GPIO specified. Currently a 54 + maximum of 8 probes are supported. 32 are likely possible but are not 55 + implemented yet. 56 + 57 + Usage 58 + ===== 59 + 60 + The logic analyzer is configurable via files in debugfs. However, it is 61 + strongly recommended to not use them directly, but to use the script 62 + ``tools/gpio/gpio-sloppy-logic-analyzer``. Besides checking parameters more 63 + extensively, it will isolate the CPU core so you will have the least 64 + disturbance while measuring. 65 + 66 + The script has a help option explaining the parameters. For the above DT 67 + snippet which analyzes an I2C bus at 400kHz on a Renesas Salvator-XS board, the 68 + following settings are used: The isolated CPU shall be CPU1 because it is a big 69 + core in a big.LITTLE setup. Because CPU1 is the default, we don't need a 70 + parameter. The bus speed is 400kHz. So, the sampling theorem says we need to 71 + sample at least at 800kHz. However, falling edges of both signals in an I2C 72 + start condition happen faster, so we need a higher sampling frequency, e.g. 73 + ``-s 1500000`` for 1.5MHz. Also, we don't want to sample right away but wait 74 + for a start condition on an idle bus. So, we need to set a trigger to a falling 75 + edge on SDA while SCL stays high, i.e. ``-t 1H+2F``. Last is the duration, let 76 + us assume 15ms here which results in the parameter ``-d 15000``. So, 77 + altogether:: 78 + 79 + gpio-sloppy-logic-analyzer -s 1500000 -t 1H+2F -d 15000 80 + 81 + Note that the process will return you back to the prompt but a sub-process is 82 + still sampling in the background. Unless this has finished, you will not find a 83 + result file in the current or specified directory. For the above example, we 84 + will then need to trigger I2C communication:: 85 + 86 + i2cdetect -y -r <your bus number> 87 + 88 + Result is a .sr file to be consumed with PulseView or sigrok-cli from the free 89 + `sigrok`_ project. It is a zip file which also contains the binary sample data 90 + which may be consumed by other software. The filename is the logic analyzer 91 + instance name plus a since-epoch timestamp. 92 + 93 + .. _sigrok: https://sigrok.org/
+1
Documentation/dev-tools/index.rst
··· 32 32 kunit/index 33 33 ktap 34 34 checkuapi 35 + gpio-sloppy-logic-analyzer 35 36 36 37 37 38 .. only:: subproject and html
+19
drivers/gpio/Kconfig
··· 1891 1891 1892 1892 endmenu 1893 1893 1894 + menu "GPIO Debugging utilities" 1895 + 1896 + config GPIO_SLOPPY_LOGIC_ANALYZER 1897 + tristate "Sloppy GPIO logic analyzer" 1898 + depends on (GPIOLIB || COMPILE_TEST) && CPUSETS && DEBUG_FS && EXPERT 1899 + help 1900 + This option enables support for a sloppy logic analyzer using polled 1901 + GPIOs. Use the 'tools/gpio/gpio-sloppy-logic-analyzer' script with 1902 + this driver. The script will make it easier to use and will also 1903 + isolate a CPU for the polling task. Note that this is a last resort 1904 + analyzer which can be affected by latencies, non-deterministic code 1905 + paths, or NMIs. However, for e.g. remote development, it may be useful 1906 + to get a first view and aid further debugging. 1907 + 1908 + If this driver is built as a module it will be called 1909 + 'gpio-sloppy-logic-analyzer'. 1910 + 1911 + endmenu 1912 + 1894 1913 endif
+1
drivers/gpio/Makefile
··· 150 150 obj-$(CONFIG_GPIO_SIM) += gpio-sim.o 151 151 obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o 152 152 obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o 153 + obj-$(CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER) += gpio-sloppy-logic-analyzer.o 153 154 obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o 154 155 obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o 155 156 obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
+344
drivers/gpio/gpio-sloppy-logic-analyzer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Sloppy logic analyzer using GPIOs (to be run on an isolated CPU) 4 + * 5 + * Use the 'gpio-sloppy-logic-analyzer' script in the 'tools/gpio' folder for 6 + * easier usage and further documentation. Note that this is a last resort 7 + * analyzer which can be affected by latencies and non-deterministic code 8 + * paths. However, for e.g. remote development, it may be useful to get a first 9 + * view and aid further debugging. 10 + * 11 + * Copyright (C) Wolfram Sang <wsa@sang-engineering.com> 12 + * Copyright (C) Renesas Electronics Corporation 13 + */ 14 + 15 + #include <linux/ctype.h> 16 + #include <linux/debugfs.h> 17 + #include <linux/delay.h> 18 + #include <linux/device.h> 19 + #include <linux/err.h> 20 + #include <linux/gpio/consumer.h> 21 + #include <linux/init.h> 22 + #include <linux/ktime.h> 23 + #include <linux/mod_devicetable.h> 24 + #include <linux/module.h> 25 + #include <linux/mutex.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/property.h> 28 + #include <linux/slab.h> 29 + #include <linux/sizes.h> 30 + #include <linux/timekeeping.h> 31 + #include <linux/types.h> 32 + #include <linux/vmalloc.h> 33 + 34 + #define GPIO_LA_NAME "gpio-sloppy-logic-analyzer" 35 + #define GPIO_LA_DEFAULT_BUF_SIZE SZ_256K 36 + /* can be increased but then we need to extend the u8 buffers */ 37 + #define GPIO_LA_MAX_PROBES 8 38 + #define GPIO_LA_NUM_TESTS 1024 39 + 40 + struct gpio_la_poll_priv { 41 + struct mutex blob_lock; /* serialize access to the blob (data) */ 42 + u32 buf_idx; 43 + struct gpio_descs *descs; 44 + unsigned long delay_ns; 45 + unsigned long acq_delay; 46 + struct debugfs_blob_wrapper blob; 47 + struct dentry *debug_dir; 48 + struct dentry *blob_dent; 49 + struct debugfs_blob_wrapper meta; 50 + struct device *dev; 51 + unsigned int trig_len; 52 + u8 *trig_data; 53 + }; 54 + 55 + static struct dentry *gpio_la_poll_debug_dir; 56 + 57 + static __always_inline int gpio_la_get_array(struct gpio_descs *d, unsigned long *sptr) 58 + { 59 + int ret; 60 + 61 + ret = gpiod_get_array_value(d->ndescs, d->desc, d->info, sptr); 62 + if (ret == 0 && fatal_signal_pending(current)) 63 + ret = -EINTR; 64 + 65 + return ret; 66 + } 67 + 68 + static int fops_capture_set(void *data, u64 val) 69 + { 70 + struct gpio_la_poll_priv *priv = data; 71 + u8 *la_buf = priv->blob.data; 72 + unsigned long state = 0; /* zeroed because GPIO arrays are bitfields */ 73 + unsigned long delay; 74 + ktime_t start_time; 75 + unsigned int i; 76 + int ret; 77 + 78 + if (!val) 79 + return 0; 80 + 81 + if (!la_buf) 82 + return -ENOMEM; 83 + 84 + if (!priv->delay_ns) 85 + return -EINVAL; 86 + 87 + mutex_lock(&priv->blob_lock); 88 + if (priv->blob_dent) { 89 + debugfs_remove(priv->blob_dent); 90 + priv->blob_dent = NULL; 91 + } 92 + 93 + priv->buf_idx = 0; 94 + 95 + local_irq_disable(); 96 + preempt_disable_notrace(); 97 + 98 + /* Measure delay of reading GPIOs */ 99 + start_time = ktime_get(); 100 + for (i = 0; i < GPIO_LA_NUM_TESTS; i++) { 101 + ret = gpio_la_get_array(priv->descs, &state); 102 + if (ret) 103 + goto out; 104 + } 105 + 106 + priv->acq_delay = ktime_sub(ktime_get(), start_time) / GPIO_LA_NUM_TESTS; 107 + if (priv->delay_ns < priv->acq_delay) { 108 + ret = -ERANGE; 109 + goto out; 110 + } 111 + 112 + delay = priv->delay_ns - priv->acq_delay; 113 + 114 + /* Wait for triggers */ 115 + for (i = 0; i < priv->trig_len; i += 2) { 116 + do { 117 + ret = gpio_la_get_array(priv->descs, &state); 118 + if (ret) 119 + goto out; 120 + 121 + ndelay(delay); 122 + } while ((state & priv->trig_data[i]) != priv->trig_data[i + 1]); 123 + } 124 + 125 + /* With triggers, final state is also the first sample */ 126 + if (priv->trig_len) 127 + la_buf[priv->buf_idx++] = state; 128 + 129 + /* Sample */ 130 + while (priv->buf_idx < priv->blob.size) { 131 + ret = gpio_la_get_array(priv->descs, &state); 132 + if (ret) 133 + goto out; 134 + 135 + la_buf[priv->buf_idx++] = state; 136 + ndelay(delay); 137 + } 138 + out: 139 + preempt_enable_notrace(); 140 + local_irq_enable(); 141 + if (ret) 142 + dev_err(priv->dev, "couldn't read GPIOs: %d\n", ret); 143 + 144 + kfree(priv->trig_data); 145 + priv->trig_data = NULL; 146 + priv->trig_len = 0; 147 + 148 + priv->blob_dent = debugfs_create_blob("sample_data", 0400, priv->debug_dir, &priv->blob); 149 + mutex_unlock(&priv->blob_lock); 150 + 151 + return ret; 152 + } 153 + DEFINE_DEBUGFS_ATTRIBUTE(fops_capture, NULL, fops_capture_set, "%llu\n"); 154 + 155 + static int fops_buf_size_get(void *data, u64 *val) 156 + { 157 + struct gpio_la_poll_priv *priv = data; 158 + 159 + *val = priv->blob.size; 160 + 161 + return 0; 162 + } 163 + 164 + static int fops_buf_size_set(void *data, u64 val) 165 + { 166 + struct gpio_la_poll_priv *priv = data; 167 + int ret = 0; 168 + void *p; 169 + 170 + if (!val) 171 + return -EINVAL; 172 + 173 + mutex_lock(&priv->blob_lock); 174 + 175 + vfree(priv->blob.data); 176 + p = vzalloc(val); 177 + if (!p) { 178 + val = 0; 179 + ret = -ENOMEM; 180 + } 181 + 182 + priv->blob.data = p; 183 + priv->blob.size = val; 184 + 185 + mutex_unlock(&priv->blob_lock); 186 + return ret; 187 + } 188 + DEFINE_DEBUGFS_ATTRIBUTE(fops_buf_size, fops_buf_size_get, fops_buf_size_set, "%llu\n"); 189 + 190 + static int trigger_open(struct inode *inode, struct file *file) 191 + { 192 + return single_open(file, NULL, inode->i_private); 193 + } 194 + 195 + static ssize_t trigger_write(struct file *file, const char __user *ubuf, 196 + size_t count, loff_t *offset) 197 + { 198 + struct seq_file *m = file->private_data; 199 + struct gpio_la_poll_priv *priv = m->private; 200 + char *buf; 201 + 202 + /* upper limit is arbitrary but should be less than PAGE_SIZE */ 203 + if (count > 2048 || count & 1) 204 + return -EINVAL; 205 + 206 + buf = memdup_user(ubuf, count); 207 + if (IS_ERR(buf)) 208 + return PTR_ERR(buf); 209 + 210 + priv->trig_data = buf; 211 + priv->trig_len = count; 212 + 213 + return count; 214 + } 215 + 216 + static const struct file_operations fops_trigger = { 217 + .owner = THIS_MODULE, 218 + .open = trigger_open, 219 + .write = trigger_write, 220 + .llseek = no_llseek, 221 + .release = single_release, 222 + }; 223 + 224 + static int gpio_la_poll_probe(struct platform_device *pdev) 225 + { 226 + struct gpio_la_poll_priv *priv; 227 + struct device *dev = &pdev->dev; 228 + const char *devname = dev_name(dev); 229 + const char *gpio_names[GPIO_LA_MAX_PROBES]; 230 + char *meta = NULL; 231 + unsigned int i, meta_len = 0; 232 + int ret; 233 + 234 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 235 + if (!priv) 236 + return -ENOMEM; 237 + 238 + devm_mutex_init(dev, &priv->blob_lock); 239 + 240 + fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); 241 + 242 + priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); 243 + if (IS_ERR(priv->descs)) 244 + return PTR_ERR(priv->descs); 245 + 246 + /* artificial limit to keep 1 byte per sample for now */ 247 + if (priv->descs->ndescs > GPIO_LA_MAX_PROBES) 248 + return -EFBIG; 249 + 250 + ret = device_property_read_string_array(dev, "probe-names", gpio_names, 251 + priv->descs->ndescs); 252 + if (ret >= 0 && ret != priv->descs->ndescs) 253 + ret = -EBADR; 254 + if (ret < 0) 255 + return dev_err_probe(dev, ret, "error naming the GPIOs"); 256 + 257 + for (i = 0; i < priv->descs->ndescs; i++) { 258 + unsigned int add_len; 259 + char *new_meta, *consumer_name; 260 + 261 + if (gpiod_cansleep(priv->descs->desc[i])) 262 + return -EREMOTE; 263 + 264 + consumer_name = kasprintf(GFP_KERNEL, "%s: %s", devname, gpio_names[i]); 265 + if (!consumer_name) 266 + return -ENOMEM; 267 + gpiod_set_consumer_name(priv->descs->desc[i], consumer_name); 268 + kfree(consumer_name); 269 + 270 + /* '10' is length of 'probe00=\n\0' */ 271 + add_len = strlen(gpio_names[i]) + 10; 272 + 273 + new_meta = devm_krealloc(dev, meta, meta_len + add_len, GFP_KERNEL); 274 + if (!new_meta) 275 + return -ENOMEM; 276 + 277 + meta = new_meta; 278 + meta_len += snprintf(meta + meta_len, add_len, "probe%02u=%s\n", 279 + i + 1, gpio_names[i]); 280 + } 281 + 282 + platform_set_drvdata(pdev, priv); 283 + priv->dev = dev; 284 + 285 + priv->meta.data = meta; 286 + priv->meta.size = meta_len; 287 + priv->debug_dir = debugfs_create_dir(devname, gpio_la_poll_debug_dir); 288 + debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta); 289 + debugfs_create_ulong("delay_ns", 0600, priv->debug_dir, &priv->delay_ns); 290 + debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay); 291 + debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); 292 + debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture); 293 + debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger); 294 + 295 + return 0; 296 + } 297 + 298 + static void gpio_la_poll_remove(struct platform_device *pdev) 299 + { 300 + struct gpio_la_poll_priv *priv = platform_get_drvdata(pdev); 301 + 302 + mutex_lock(&priv->blob_lock); 303 + debugfs_remove_recursive(priv->debug_dir); 304 + mutex_unlock(&priv->blob_lock); 305 + } 306 + 307 + static const struct of_device_id gpio_la_poll_of_match[] = { 308 + { .compatible = GPIO_LA_NAME }, 309 + { } 310 + }; 311 + MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match); 312 + 313 + static struct platform_driver gpio_la_poll_device_driver = { 314 + .probe = gpio_la_poll_probe, 315 + .remove_new = gpio_la_poll_remove, 316 + .driver = { 317 + .name = GPIO_LA_NAME, 318 + .of_match_table = gpio_la_poll_of_match, 319 + } 320 + }; 321 + 322 + static int __init gpio_la_poll_init(void) 323 + { 324 + gpio_la_poll_debug_dir = debugfs_create_dir(GPIO_LA_NAME, NULL); 325 + 326 + return platform_driver_register(&gpio_la_poll_device_driver); 327 + } 328 + /* 329 + * Non-strict pin controllers can read GPIOs while being muxed to something else. 330 + * To support that, we need to claim GPIOs before further pinmuxing happens. So, 331 + * we probe early using 'late_initcall' 332 + */ 333 + late_initcall(gpio_la_poll_init); 334 + 335 + static void __exit gpio_la_poll_exit(void) 336 + { 337 + platform_driver_unregister(&gpio_la_poll_device_driver); 338 + debugfs_remove_recursive(gpio_la_poll_debug_dir); 339 + } 340 + module_exit(gpio_la_poll_exit); 341 + 342 + MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); 343 + MODULE_DESCRIPTION("Sloppy logic analyzer using GPIOs"); 344 + MODULE_LICENSE("GPL");
+246
tools/gpio/gpio-sloppy-logic-analyzer.sh
··· 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" &