Build Thread: HaLow for Raspberry Pi OS

Was this a mix of OpenMANET and piOS nodes? And was it an OpenMANET or piOS node that was power cycled?

Waiting on the kernel to build with the full morse version of mesh_hwmp.c and will report back.

1 Like

Just 2 pi5s running the same build I laid out in my post.

I switched to the fully-morse version of mesh_hwmp.c and I’m still getting kernel panics whenever a node is rebooted.

What’s the correct way to cleanly disconnect a node?

I came up with this:

ip link set bat0 down
batctl if del wlan1
ip link set wlan1 down
wpa_cli -p /var/run/wpa_supplicant_s1g -i wlan1 terminate

From dmesg I was trying to troubleshoot earlier:

[ 306.842936] batman_adv: bat0: The MTU of interface wlan1 is too small (1500) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to 1532 would solve the problem. 
[ 306.842946] batman_adv: bat0: Interface activated: wlan1 
[ 317.914487] batman_adv: bat0: WiFi driver or ethtool info does not provide information about link speeds on interface wlan1, therefore defaulting to hardcoded throughput values of 1.0 Mbps. Consider overriding the throughput manually or checking your driver.

Kinda stuck here now. Not sure if I’ve missed something super obvious…

Dang! Okay I’ll do my best to review my steps tomorrow but I followed them top to bottom for what I posted here. You’re using all the same hardware such as seeedstudio hats and pi5s?

Thank you! I’m going to take another pass at your steps too but with the desktop version 2025-05-13-raspios-bookworm-arm64.img.xz mentioned in the top post.

I also wanted to ask if you could share the final versions of include/net/mac80211.h, net/mac80211/debugfs.c and net/mac80211/mesh_hwmp.c that you ended up with?

And yes, same hardware.

  1. Pi 5 4 GB
  2. Seeed HaLow module: Wio-WM6108 Wi-Fi HaLow Mini-PCIe Module
  3. Seeed mini PCIe hat: WM1302 Raspberry Pi Hat

I’ve tried with both of these releases of piOS: 2025-11-24-raspios-bookworm-arm64-lite.img.xz and with 2025-05-13-raspios-bookworm-arm64-lite.img.xz.

Is this what you mean by Modify wpa_supplicant/.config to enabled MESH?

# echo "CONFIG_IEEE80211AH=y" >> wpa_supplicant/.config
# echo "CONFIG_MESH=y" >> wpa_supplicant/.config
# echo "CONFIG_SAE=y" >> wpa_supplicant/.config
# echo "CONFIG_NO_WPA_PASSPHRASE=y" >> wpa_supplicant/.config

And for future reference here’s the MM version of that file: rpi-linux/net/mac80211/mesh_hwmp.c at mm/rpi-6.12.21/1.16.x · MorseMicro/rpi-linux · GitHub

I actually wrote it multiple times because I kept forgetting :laughing: . But what you showed should be okay. I actually only changed CONFIG_MESH.

That’s for the link. That’s the file I ended up using after the cherry picking was done to just fully replace.

Still working on getting you answers to your last post for the final versions of the other files. Didn’t ignore ya.

1 Like

I tried with the same 2025-05-13-raspios-bookworm-arm64.img.xz linked in the original post and with only CONFIG_MESH=y for wpa_supplicant and still getting kernel panics.

Always after 12ish seconds. Here’s the last thing shown by watch -n 1 batctl o on the two nodes that KP:

[B.A.T.M.A.N. adv 2024.2, MainIF/MAC: wlan1/a8:dd:9f:4d:33:33 (bat0/b2:8b:19:f2:6c:61 BATMAN_V)]
   Originator        last-seen ( throughput)  Nexthop           [outgoingIF]
   a8:dd:9f:4d:11:11   12.480s (        3.5)  a8:dd:9f:4d:22:22 [     wlan1]
 * a8:dd:9f:4d:11:11   12.480s (        7.1)  a8:dd:9f:4d:11:11 [     wlan1]
   a8:dd:9f:4d:22:22    1.204s (        3.3)  a8:dd:9f:4d:11:11 [     wlan1]
 * a8:dd:9f:4d:22:22    1.204s (        7.1)  a8:dd:9f:4d:22:22 [     wlan1]


 [B.A.T.M.A.N. adv 2024.2, MainIF/MAC: wlan1/a8:dd:9f:4d:22:22 (bat0/82:84:98:10:78:c7 BATMAN_V)]
   Originator        last-seen ( throughput)  Nexthop           [outgoingIF]
 * a8:dd:9f:4d:11:11   12.084s (        7.1)  a8:dd:9f:4d:11:11 [     wlan1]
   a8:dd:9f:4d:11:11   12.084s (        3.5)  a8:dd:9f:4d:33:33 [     wlan1]
   a8:dd:9f:4d:33:33    0.480s (        3.5)  a8:dd:9f:4d:11:11 [     wlan1]
 * a8:dd:9f:4d:33:33    0.480s (        7.1)  a8:dd:9f:4d:33:33 [     wlan1]

I’m currently going through the top level process with a PCIe MM6108IQ chip from Heltec over PCIe and wondering if anyone has tried this. I see for USB there is no Device Tree setup needed. Hoping that’s the case with PCIe as well

Thanks for sharing the steps!

I have a quick question.
Can we do this using Raspberry Pi 4B?
Is it compatible?
Will that work?

@venumadhav24

Following similar steps should work for a RPi4b as well. However, the device tree overlays will be slightly different than what is described in this thread. Mainly, the SPI pinctrl won’t reference “rp1”

Thanks for the detailed guide!

I am also currently trying this on a Raspberry Pi 4 model B with the SeeedStudio WM6108 module. I haven’t worked with device trees before and was wondering if the wm6108-spi.dts would look like this?

/dts-v1/;
/plugin/;

/ {
        compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709", "brcm,bcm2711";

        fragment@0 {
                target = <&spi0>;
                frag0: __overlay__ {
                        pinctrl-0 = <&spi0_pins &spi0_cs_pins &morse_wake &morse_busy &morse_irq &morse_reset>;
                        cs-gpios = <&gpio 8 1>;
                        #address-cells = <1>;
                        #size-cells = <0>;
                        status = "okay";

                        mm6108: mm6108@0 {
                                compatible = "morse,mm610x-spi";
                                reg = <0>;
                                reset-gpios = <&gpio 17 0>;
                                power-gpios = <&gpio 23 0>,
                                              <&gpio 24 0>;
                                spi-irq-gpios = <&gpio 5 0>;
                                spi-max-frequency = <50000000>;
                                status = "okay";
                        };
                        spidev@0 {
                                reg = <0>;
                                status = "disabled";
                        };
                        spidev@1 {
                                reg = <1>;
                                status = "disabled";
                        };

                };
        };

        fragment@1 {
                target = <&gpio>;
                frag1: __overlay__ {
                        morse_wake: morse_wake {
                                function = "gpio";
                                pins = "gpio23";
                                output-high;
                                bias-disable;
                        };

                        morse_busy: morse_busy {
                                function = "gpio";
                                pins = "gpio24";
                                input;
                                bias-pull-down;
                        };

                        morse_irq: morse_irq {
                                function = "gpio";
                                pins = "gpio5";
                                bias-pull-up;
                                input;
                        };

                        morse_reset: morse_reset {
                                function = "gpio";
                                pins = "gpio17";
                                output-high;
                                bias-disable;
                        };
                };
        };
};

When I compile this into a binary overlay file, I receive a warning (unique_unit_address) / duplicate unit-address; I’m not sure if this is important? Finally, when I try to reboot my RPi4B, it hangs at the “Welcome to the Raspberry Pi Desktop” startup screen.

Any help is much appreciated :folded_hands:

Following this thread’s recipe on a fresh stable_20250428 checkout (Pi 5 + Seeed WM1302 carrier + WM6108, kernel 6.12.25 + the mm/rpi-6.12.21/1.16.x cherry-pick), I hit a few issues that took real digging. Posting the fixes in case they save someone time.

1. IEEE80211_HW_STRICT undeclared during kernel build

Symptom: error: 'IEEE80211_HW_STRICT' undeclared from include/net/mac80211.h (~line 3075).

Cause: the morse 6.12.21 patches call ieee80211_hw_check(hw, STRICT) but the enum value isn’t in the 6.12.25 base yet. It’s only ever checked, never set, so a stub is safe:

sed -i '/IEEE80211_HW_SUPPORTS_NDP_BLOCKACK,/a\        IEEE80211_HW_STRICT,' include/net/mac80211.h

2. morse_spi_find_token failed, r=0x10050002 b=0xffffffff (ret:-71) after a rebuild — check CS polarity in the deployed overlay

If everything used to work and this appears after regenerating the overlay, decompile the deployed .dtbo and read the cs-gpios polarity flag:

dtc -I dtb -O dts /boot/firmware/overlays/wm6108-spi.dtbo 2>/dev/null | grep cs-gpios
# must end in 0x01 (ACTIVE_LOW). If it's 0x00 the kernel drives CS high
# during transactions — the chip is never selected and returns garbage.

Source fix: cs-gpios = <&gpio 8 1>;

3. Pi 5 hostapd “Could not set channel” / EBUSY — the ghost default hostapd

If you’re adding a 2.4 GHz AP alongside HaLow: apt install hostapd auto-enables Debian’s default hostapd.service, which half-starts at boot and holds the radio. Your own hostapd then fails in ways that look exactly like “Pi 5 brcmfmac is just bad at AP mode.” Check hostapd -dd for a PID that’s already alive.

sudo systemctl stop hostapd
sudo systemctl disable hostapd
sudo systemctl mask hostapd    # the mask matters — disable alone allows dependency starts

4. batman-adv gw_mode client does NOT install a default route

batctl gwl shows the gateway selected, but pings die. Gateway selection is link-layer only; the kernel routing table is untouched. Add it explicitly, with a metric that beats your eth0 default:

sudo ip route add default via <GATEWAY-BAT0-IP> dev bat0 metric 50

5. morse_cli must be installed alongside wpa_supplicant_s1g — missing it silently breaks mesh peering

wpa_supplicant_s1g shells out to morse_cli at startup to enforce s1g_prim_1mhz_chan_index. If morse_cli isn’t on $PATH the call fails silently and the chip’s primary index defaults to 0. Result: all nodes on the same operating frequency, but different primary 1 MHz channels — they never see each other’s beacons. Verify on every mesh member:

sudo morse_cli -i wlanN channel    # Primary Channel Index must match everywhere

6. The fallback-state diagnostic

If morse_cli -i wlanN channel shows channel_frequency:904500 after a restart, wpa_supplicant_s1g rejected your config — that’s the chip’s hardware fallback, not a configured channel. Almost always a wrong op_class. The values that actually work with the MM6108 + wpa_supplicant_s1g (US 902–928, verified by testing — note these differ from raw IEEE Table E-4, and Gateworks docs cover a different chipset entirely):

BW op_class s1g_prim_chwidth valid channels freq
1 MHz 68 0 odd 3–39 902 + ch × 0.5 MHz
2 MHz 69 0 6,10,…,46 (every 4) "
4 MHz 70 1 8,16,…,48 (every 8) "
8 MHz 71 1 12, 28, 44 "

7. The mesh_hwmp.c conflict is the BATMAN_V 3-node panic (re: #2022)

A second data point for castironclay’s advice in #21, since that thread never got closed out. I hit the same cherry-pick conflict and never hand-merged it — took the branch’s file wholesale:

git checkout morse/mm/rpi-6.12.21/1.16.x -- net/mac80211/mesh_hwmp.c
git add net/mac80211/mesh_hwmp.c
git cherry-pick --continue

Same recipe for the two conflicts in the NDP-blockack patch (include/net/mac80211.h, net/mac80211/debugfs.c) — wholesale-replace, don’t hand-merge. The morse patch rewrites SN-comparison logic in a way that doesn’t compose with upstream drift, so a hand-merge produces something that builds and then bites you at runtime.

On that build I’ve run a three-node BATMAN_V mesh — two Pi 5 + WM6108, one Pi Zero 2W + XIAO HaLow — all peers ESTAB at L2, all mutually visible in batctl o, traffic routed end-to-end through the gateway node at 0% loss (~31–32 Mbps BATMAN throughput estimates). No panics.

@Zippy — if you hand-merged that conflict, I’d start there.

Hope this saves someone a few nights. The few threads on this forum and about 50 hours of console got it done! This product is a game changer. A genuine thank you to all.

1 Like