Software Development Workflow¶
This page covers the software side of openwifi development: rebuilding and deploying the driver, sdrctl, and the other user_space/ tools. Building the FPGA bitstream and turning it into a loadable image are on the FPGA Development page. This page starts where the FPGA pages stop: getting built software onto the board.
The prebuilt SD image may be older than the current repo, so copy the repo's user_space/ files onto the board before doing serious work, and rebuild the driver against the matching kernel.
If you are in the middle of editing code and want the steps, start with the quick reference below and follow its links for detail.
Environment setup¶
Set up the shared host toolchain first: see Environment Setup for the Vivado/Vitis install and the Ubuntu build packages. The software builds use all four variables:
export XILINX_DIR=/opt/Xilinx # dir containing Vitis/, Vivado/, etc.
export OPENWIFI_HW_IMG_DIR=/path/to/openwifi-hw-img
export BOARD_NAME=zed_fmcs2 # your board
export ARCH_BIT=32 # 32 for Zynq-7000, 64 for Zynq UltraScale+ (e.g. ZCU102)
For the exact toolchain, kernel, and image versions these builds expect, see Versions this wiki targets.
Quick reference: from code change to running board¶
Find the row that matches what you changed and follow its link to the full instructions. The commands assume the usual setup: sources on your PC, the board reachable at 192.168.10.122 (the default address from Getting Started), and the environment variables set.
| You changed | Rebuild & deploy | Take effect |
|---|---|---|
Driver C code (driver/, including driver/side_ch/) |
On the PC: cd driver && ./make_all.sh $XILINX_DIR $ARCH_BIT, then scp the .ko files to the board's openwifi/ directory |
./wgd.sh on the board, no reboot needed (details) |
sdrctl source (user_space/sdrctl_src/) |
scp the source to the board, then compile on the board with make |
The new binary replaces openwifi/sdrctl immediately (details) |
side_ch_ctl source (user_space/side_ch_ctl_src/) |
scp the source to the board, then on the board: gcc -o side_ch_ctl side_ch_ctl.c |
Run it. If you also changed side_ch.ko, reload that like any other driver module (details) |
inject_80211 / analyze_80211 (user_space/inject_80211/) |
scp the source to the board, then on the board: make |
Run it (usage) |
Helper scripts (user_space/*.sh, *.py) |
Nothing to compile. Copy them to the board with scp. The Python display scripts (side_info_display.py, iq_capture.py, and others) run on the PC instead |
Run them |
| FPGA Verilog / IP cores | On the PC: rebuild the bitstream (FPGA Development), then boot_bin_gen.sh and scp system_top.bit.bin to the board |
./wgd.sh, no reboot needed (details) |
| Kernel config or device tree | Rebuild on the PC, then transfer and populate on the board. Full step-by-step in Updating a board to a newly built kernel, short form under bulk helpers | Reboot. A kernel version change also needs the driver rebuilt and the populate script run twice |
The driver iteration loop¶
The most common cycle is changing driver code and testing it on the board. Once set up, each round takes less than a minute.
One-time setup (per PC / per kernel version):
- Set the environment variables and install the build packages.
- Prepare the kernel source the driver builds against:
cd openwifi/user_space && ./prepare_kernel.sh $XILINX_DIR $ARCH_BIT.
Each round:
- Edit the driver code on the PC.
- Compile:
cd openwifi/driver && ./make_all.sh $XILINX_DIR $ARCH_BIT(add extra args for conditional-compile macros). - Copy to the board:
scp `find ./ -name \*.ko` root@192.168.10.122:openwifi/. - On the board:
./wgd.shreloads the modules live. Make suresystem_top.bit.binis not in the directory unless you also want the FPGA image reloaded (details). - Check it loaded:
dmesg | tail -20should show the driver initializing without symbol/version errors (if it does show them, see the note on kernel mismatch), andip ashould listsdr0. - Reloading recreates
sdr0from scratch, so restart whatever mode you were in: AP (hostapd/fosdem.sh), client (wpa_supplicant), or your monitor-mode setup (Operating Modes).
For print-style debugging, add printk calls in the driver and watch them live on the board with dmesg -w in a second ssh session.
Rebuilding the driver¶
- Prepare the Analog Devices kernel source once (this is what the driver builds against):
cd openwifi/user_space
./prepare_kernel.sh $XILINX_DIR $ARCH_BIT
- Compile the driver:
cd openwifi/driver
./make_all.sh $XILINX_DIR $ARCH_BIT
# Extra args beyond these two become "#define" macros in pre_def.h
# for conditional compilation (see below).
- Copy the
.kofiles to the board:
cd openwifi/driver
scp `find ./ -name \*.ko` root@192.168.10.122:openwifi/
- On the board,
./wgd.shloads the new driver (and reloads the FPGA image ifsystem_top.bit.binis present in the same directory).
Symbol/version errors on load mean a kernel mismatch
The kernel in the SD image is usually older than the one your driver was built against. Fix it by putting the freshly built kernel image into the BOOT partition: adi-linux/arch/arm/boot/uImage (32-bit) or adi-linux-64/arch/arm64/boot/Image (64-bit). The full procedure is in Updating a board to a newly built kernel.
Conditional compilation¶
Passing extra arguments to make_all.sh turns them into #define macros in pre_def.h, so you can gate driver code blocks per build. Combined with the FPGA's equivalent Verilog-macro mechanism, this is how you produce feature variants. See dynamic reloading for a clean way to keep several variants side by side.
Reloading driver and FPGA without rebooting¶
wgd.sh can reload the driver and/or FPGA live and switch between different builds with no reboot and no power cycle. Keep your on-board files current with user_space/ to use it.
Driver only. Ensure system_top.bit.bin is not in the directory. wgd.sh then loads just the .ko files.
Driver + FPGA. Generate the reloadable bitstream and put it beside the driver files (the .xsa input comes from the FPGA build, see FPGA Development):
cd openwifi/user_space
./drv_and_fpga_package_gen.sh $OPENWIFI_HW_IMG_DIR $XILINX_DIR $BOARD_NAME
# produces system_top.bit.bin AND drv_and_fpga.tar.gz
Then run ./wgd.sh on the board as usual.
From a single package file (recommended). drv_and_fpga_package_gen.sh also bundles everything into drv_and_fpga.tar.gz (driver .kos, FPGA image, and related source). Rename it meaningfully per branch/variant and load it directly:
./wgd.sh ./drv_and_fpga_myvariant.tar.gz
This makes it easy to keep, share, and switch between variants. To build a variant, either work on a separate branch, or use conditional-compile arguments (driver make_all.sh extra args, FPGA Verilog macros) and rename the package to record which options are on. Note: drv_and_fpga_package_gen.sh calls make_all.sh without extra args by default, so if you rely on conditional-compile flags, add them there too.
From a target directory. Put a driver+FPGA set in its own directory and load it explicitly, so different versions live in different directories:
./wgd.sh $TARGET_DIR
Full wgd.sh usage (also shown by ./wgd.sh -h):
- no argument: load the driver
.kofiles and the FPGA image (ifsystem_top.bit.binexists) from the current directory, withtest_mode=0 - a numeric first argument is assigned to
test_mode(loads everything from the current directory) remotedownloads the files and then loads them. An optional second argument names the target directory, an optional third setstest_mode- any other first argument that is not a
.tar.gzfile is treated as a directory to load from. An optional second argument setstest_mode - a
.tar.gzfile is unpacked and loaded from the unpacked directory. An optional second argument setstest_mode
test_mode¶
insmod sdr.ko test_mode=<value> (or passing the value to wgd.sh/fosdem.sh) toggles experimental features via the test_mode global in sdr.c. Two bits are in use: bit0 = A-MPDU aggregation on/off (default off), which is why ./wgd.sh 1 gives you aggregation, and bit1 = advertise short guard interval, which only the driver source documents. See Wi-Fi 4 & Wi-Fi 6 Features for both in practice.
Rebuilding sdrctl¶
sdrctl is compiled on the board:
# from host, push the source:
cd openwifi/user_space/sdrctl_src
scp `find ./ -name \*` root@192.168.10.122:openwifi/sdrctl_src/
# on the board:
cd ~/openwifi/sdrctl_src/ && make clean && make && cp sdrctl ../ && cd ..
A successful build finishes without errors, and the new binary lands at ~/openwifi/sdrctl.
Bulk update helpers¶
For larger updates (kernel, modules, device tree, rootfs) there are paired host/board scripts:
- Kernel + modules + device tree: on the host,
prepare_kernel.sh,boot_bin_gen.sh, andtransfer_kernel_image_module_to_board.sh. On the board,populate_kernel_image_module_reboot.sh(run it again after the first reboot if the kernel version changed, so symlinks point at the new version). The whole flow, with the verification steps that get you back to a workingsdr0, is written out in Updating a board to a newly built kernel. - Driver + user space: on the host,
make_all.shandtransfer_driver_userspace_to_board.sh. On the board,populate_driver_userspace.sh. - Over FTP (optional): set up an anonymous FTP server on the PC rooted at your
openwifidirectory, then on the board./sdcard_boot_update.sh $BOARD_NAME(pullsuImage,BOOT.BIN,devicetree.dtbinto the boot partition, then power-cycle) and./wgd.sh remote(pulls driver files and brings upsdr0). Anonymous FTP has no authentication, so use it on trusted lab networks only. - rootfs as a disk: on the PC, File manager → Connect to Server →
sftp://root@192.168.10.122/root(passwordopenwifi). - Refreshing the ADI rootfs tools is also worthwhile: on the board, clone
linux_image_ADI-scripts,apt update, then runadi_update_tools.sh(see the ADI Kuiper update guide).
Building a full SD image from scratch¶
Two base operating systems are supported, ADI Kuiper (Debian/Ubuntu-like) and OpenWrt (router-style with LuCI). The full step-by-step for both (flashing the base image, the rootfs edits, update_sdcard.sh, and the OpenWrt Docker build) is on the dedicated Building SD Images page. Kuiper builds need Vivado 2022.2 (with Vitis) and the flex bison libssl-dev device-tree-compiler u-boot-tools packages. The OpenWrt build only needs Docker.