WM6108/WM1302 on Ubuntu 24.04 on Pi4b success, finally.

morse_driver 1.16.4 — Ubuntu 24.04 / Raspberry Pi 4B / Kernel 6.8 Build Notes

Troubleshooting Summary — morse_driver 1.16.4 on Ubuntu 24.04 / Pi 4B

Overview

Bringing up the Seeed WM6108 HaLow module on a Raspberry Pi 4B running Ubuntu Server 24.04 LTS required resolving issues across five distinct layers: package management, kernel driver compatibility, device tree configuration, boot sequencing, and userspace tooling. None of these were individually insurmountable, but they had to be resolved in the correct order before the next layer was visible.

1. Package Manager Broken — Missing noble-updates

The first obstacle appeared before any HaLow-specific work began. The Ubuntu 24.04 image flashed by Raspberry Pi Imager ships with a version of libacl1 that does not exist in the configured apt repository. The Suites: line in /etc/apt/sources.list.d/ubuntu.sources only listed noble and noble-security, omitting noble-updates. Attempting to install build-essential failed immediately with a held broken packages error on bzip2.

Fix: adding noble-updates to the Suites line in the sources file resolved the dependency chain and allowed the full build toolchain to install cleanly.


2. Wrong Build Repository

The commonly referenced dustyrobotics/morse-ubuntu repository appeared to be the correct Ubuntu path based on its name and description. On closer inspection it targets an entirely different hardware platform — the Qualcomm QCM6490 Tachyon/Particle board with a USB-connected MM6108. The kernel headers bundled inside it are for 6.8.0-1056-particle, the build system is designed for cross-compilation from an x86_64 host, and the driver it produces is the USB transport variant. None of this applies to a Pi 4B with an SPI-connected WM6108.

Fix: discard the dustyrobotics repo entirely and clone upstream MorseMicro/morse_driver directly, building the SPI driver against the Pi’s own installed kernel headers.


3. Five Kernel Compatibility Errors

Building morse_driver 1.16.4 against the Ubuntu 6.8 raspi kernel produced five successive build failures, each requiring a targeted fix.

Error 1 — ieee80211_is_s1g_short_beacon signature change (dot11ah/ie.c) The kernel 6.8 header updated this function from a single frame_control argument to three arguments, adding variable and variable_len for IE content inspection. The call site in ie.c was building a frame and had no IE data in scope. The driver’s own dot11ah/s1g_ieee80211.h contains a single-argument local version ieee80211_is_s1g_short_beacon_local that does exactly what the call site needs. One sed substitution resolved it.

Error 2 — IEEE80211_CHAN_IGNORE undeclared (dot11ah/s1g_channels.c) This flag is OpenWrt-specific and does not exist in the upstream kernel. The correct upstream equivalent is IEEE80211_CHAN_DISABLED. A #ifndef compat definition appended to compat.h resolved it without touching the source file that uses it.

Error 3 — ieee80211_is_s1g_short_beacon and missing s1g_short_beacon union member (vendor_ie.c) The same function signature mismatch appeared in a second file. Additionally, the kernel removed the s1g_short_beacon member from the ieee80211_ext union — both regular and short S1G beacons now share the s1g_beacon struct. The fix replaced both the function call with the local version and both sides of the ternary operator with s1g_beacon.variable.

Error 4 — GCC 13 enum/int mismatch (debug.c) GCC 13 promotes enum/integer type mismatches to errors under -Werror. The morse_log_is_enabled function is declared with u32 in the header but defined with enum morse_feature_id in the source. Rather than modifying the source, -Wno-enum-int-mismatch was added to EXTRA_CFLAGS at build time.

Error 5 — SPI_CONTROLLER_ENABLE_CS_GPIOD not defined (spi.c) The driver uses a #warning preprocessor directive as a fallback when this OpenWrt-specific SPI controller flag is absent. GCC 13 treats #warning as an error under -Werror. The flag is not needed on the BCM2711 SPI controller. Adding -Wno-cpp to EXTRA_CFLAGS suppressed the warning-as-error without any functional impact.


4. Device Tree Overlay — Three Iterations

Getting the SPI device to enumerate required three overlay revisions.

Iteration 1 included cs-gpios referencing GPIO7 and pinctrl-0 on the mm6108 device node. The SPI controller attempted to look up the GPIO chip pinctrl-bcm2835 to resolve the CS descriptor, but the GPIO chip was not yet available when SPI enumerated at boot. This produced a repeating -517 EPROBE_DEFER error.

Iteration 2 moved pinctrl-0 to the spi0 controller node and kept cs-gpios. The deferral persisted — the problem was in the GPIO descriptor lookup path itself, not the pinctrl placement.

Iteration 3 removed cs-gpios entirely, letting the BCM2711 SPI controller handle CE1 natively through its own hardware CS mechanism. This resolved the deferral. The mm6108@1 device node was created and the driver bound successfully.

A separate conflict was also discovered: the dtoverlay=spi0-1cs line in config.txt was adding its own cs-gpios definition targeting GPIO8/CE0, fighting with the overlay. Removing that line was required before the overlay could function correctly.


5. Warm Boot CMD63 Failure

With the overlay working, the driver loaded and attempted to probe the MM6108 via SPI. On warm boot the probe failed with morse_spi_probe: failed to init SPI with CMD63 (ret:-61). This is a known issue documented in the Morse Micro community forum — the MM6108 RESET line must be toggled before the driver probes, and on warm boot this does not happen automatically.

The fix involved three components working together. First, both morse and dot11ah were blacklisted from autoloading so the kernel would not attempt to probe the device before the reset sequence ran. Second, a shell script using gpioset (from the gpiod package) was written to assert GPIO17 low, pause, assert it high, then load the modules in the correct order. The sysfs GPIO path could not be used because GPIO17 is claimed by the device tree overlay. Third, a systemd oneshot service was created to run the script at boot after systemd-modules-load.service.

Cold power cycle is still required on first boot after flashing. Subsequent warm reboots are handled by the service.


6. Standard wpa_supplicant Conflict

After the driver loaded successfully, the standard wpa_supplicant service running on the system had already claimed wlan0 and associated it with the local WiFi network. It had to be stopped and disabled before wpa_supplicant_s1g could take control of the interface.


7. wpa_supplicant_s1g Build

The Morse hostap repository branch is named v1.16, not 1.16 — cloning with the wrong branch name produces a silent failure with no repository created. The build required two fixes: suppressing OpenSSL 3.0 deprecation warnings via CFLAGS, and installing the missing libnl-route-3-dev package which is not pulled in by the standard libnl dependencies.


Result

After resolving all of the above, wlan0 comes up cleanly on every boot, the morse and dot11ah modules load via the reset service, and wpa_supplicant_s1g runs in station mode scanning for a HaLow AP. The entire software stack — driver, firmware, BCF, overlay, reset service, and userspace tools — is operational on an unmodified Ubuntu 24.04 LTS kernel without any kernel patching.

Hardware

  • Raspberry Pi 4B
  • Seeed WM1302 Pi HAT (PCIe carrier only — no onboard silicon)
  • Seeed WM6108 mini PCIe module (Quectel FGH100MHAAMD / MM6108)

Software

Component Version
OS Ubuntu Server 24.04.4 LTS ARM64
Kernel 6.8.0-1048-raspi
morse_driver 1.16.4
morse-firmware 1.16 branch
hostap (wpa_supplicant_s1g) v1.16 branch

Prerequisites

# Fix missing noble-updates repo (Ubuntu 24.04 raspi image ships without it)
sudo nano /etc/apt/sources.list.d/ubuntu.sources
# Change: Suites: noble
# To:     Suites: noble noble-updates

sudo apt update
sudo apt install -y build-essential dkms linux-headers-$(uname -r) bc git \
  libnl-3-dev libnl-genl-3-dev libnl-route-3-dev libssl-dev pkg-config gpiod iw

Driver Build

git clone https://github.com/MorseMicro/morse_driver.git
cd morse_driver
git submodule update --init --recursive

Patch 1 — ieee80211_is_s1g_short_beacon signature mismatch (dot11ah/ie.c)

Kernel 6.8 updated this function to three arguments. The call site in ie.c only has frame_control in scope. Use the driver’s own single-argument local version instead.

sed -i 's/ieee80211_is_s1g_short_beacon(frame_control)/ieee80211_is_s1g_short_beacon_local(frame_control)/' dot11ah/ie.c

Patch 2 — IEEE80211_CHAN_IGNORE undeclared (dot11ah/s1g_channels.c)

OpenWrt-specific flag not present in upstream kernel. Map to IEEE80211_CHAN_DISABLED.

cat >> compat.h << 'EOF'
#ifndef IEEE80211_CHAN_IGNORE
#define IEEE80211_CHAN_IGNORE IEEE80211_CHAN_DISABLED
#endif
EOF

Patch 3 — ieee80211_is_s1g_short_beacon and missing s1g_short_beacon union member (vendor_ie.c)

Same signature issue plus kernel removed s1g_short_beacon from the ieee80211_ext union — both beacon types now share s1g_beacon.

sed -i 's/ieee80211_is_s1g_short_beacon(bcn->frame_control) ?/ieee80211_is_s1g_short_beacon_local(bcn->frame_control) ?/' vendor_ie.c
sed -i 's/bcn->u\.s1g_short_beacon\.variable/bcn->u.s1g_beacon.variable/' vendor_ie.c

Build

make KERNEL_SRC=/usr/src/linux-headers-$(uname -r) \
  CONFIG_WLAN_VENDOR_MORSE=m \
  CONFIG_MORSE_SPI=y \
  CONFIG_MORSE_USER_ACCESS=y \
  CONFIG_MORSE_VENDOR_COMMAND=y \
  EXTRA_CFLAGS="-Wno-enum-int-mismatch -Wno-deprecated-declarations -Wno-cpp"

The EXTRA_CFLAGS suppress three GCC 13 issues:

  • -Wno-enum-int-mismatch — enum/int type mismatch in debug.c
  • -Wno-deprecated-declarations — deprecated OpenSSL usage
  • -Wno-cppSPI_CONTROLLER_ENABLE_CS_GPIOD not defined (OpenWrt-specific, not needed on BCM2711)
sudo make KERNEL_SRC=/usr/src/linux-headers-$(uname -r) modules_install
sudo depmod -a

Firmware and BCF

git clone https://github.com/MorseMicro/morse-firmware.git --branch 1.16
cd morse-firmware
sudo make install

# FGH100MHAAMD OTP is not factory programmed — specify BCF explicitly
echo "options morse bcf=bcf_fgh100mhaamd.bin" | sudo tee /etc/modprobe.d/morse.conf

Device Tree Overlay — BCM2711 / Pi 4B / CE1 / GPIO7

GPIO mapping for WM6108 on WM1302 Pi HAT (from Seeed schematics):

Signal GPIO
MOSI GPIO10
MISO GPIO9
SCLK GPIO11
CS GPIO7 (CE1)
IRQ GPIO5
RESET GPIO17
WAKE GPIO23
BUSY GPIO24

Save as /tmp/morse-spi.dts:

/dts-v1/;
/plugin/;

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

    fragment@0 {
        target = <&spi0>;
        __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";

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

    fragment@1 {
        target = <&gpio>;
        __overlay__ {
            spi0_pins: spi0_pins {
                brcm,pins     = <9 10 11>;
                brcm,function = <4  4  4>;
                brcm,pull     = <2  2  2>;
            };
            spi0_cs_pins: spi0_cs_pins {
                brcm,pins     = <7>;
                brcm,function = <1>;
                brcm,pull     = <2>;
            };
            mm6108_pins: mm6108_pins {
                brcm,pins     = <5 17 23 24>;
                brcm,function = <0  1  1  0>;
                brcm,pull     = <2  0  0  1>;
            };
        };
    };
};
dtc -I dts -O dtb /tmp/morse-spi.dts -o /tmp/morse-spi.dtbo
sudo cp /tmp/morse-spi.dtbo /boot/firmware/overlays/morse-spi.dtbo

Add to /boot/firmware/config.txt — do not add spi0-1cs, it conflicts:

dtoverlay=morse-spi

Warm Boot CMD63 Fix

The MM6108 requires a hardware reset line toggle before driver probe. On warm boot the reset line is not toggled and the driver fails with morse_spi_probe: failed to init SPI with CMD63 (ret:-61). Cold power cycle resolves it on first boot. For subsequent warm boots, use a systemd service with gpioset.

Blacklist autoload — the reset service is the sole load path:

cat << 'EOF' | sudo tee /etc/modprobe.d/morse-blacklist.conf
blacklist morse
blacklist dot11ah
EOF

Reset script /usr/local/bin/morse-reset.sh:

#!/bin/bash
gpioset gpiochip0 17=0
sleep 0.1
gpioset gpiochip0 17=1
sleep 0.1
modprobe dot11ah
modprobe morse
sudo chmod +x /usr/local/bin/morse-reset.sh

Systemd service /etc/systemd/system/morse-reset.service:

[Unit]
Description=Morse Micro MM6108 reset and driver load
After=systemd-modules-load.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/morse-reset.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl enable morse-reset.service

wpa_supplicant_s1g Build

git clone https://github.com/MorseMicro/hostap.git --branch v1.16
cd hostap/wpa_supplicant
cp defconfig .config

cat << 'EOF' >> .config
CONFIG_DRIVER_NL80211=y
CONFIG_LIBNL32=y
CONFIG_IEEE80211AH=y
CONFIG_SAE=y
CONFIG_WPA3=y
CFLAGS += -Wno-deprecated-declarations
EOF

make -j4
sudo cp wpa_supplicant_s1g wpa_cli_s1g wpa_passphrase_s1g /usr/local/bin/

Verify

After cold power cycle:

sudo dmesg | grep -i morse
iw dev

Expected: wlan0 interface present, driver registered. Station mode scan:

sudo iw reg set US
sudo wpa_supplicant_s1g -i wlan0 -c /etc/wpa_supplicant_s1g.conf -B -D nl80211
sudo wpa_cli_s1g -p /var/run/wpa_supplicant_s1g -i wlan0 status
2 Likes