This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 9c9fdfc55eb768805b654369d6e4c7b0ffce80ee Author: Alan Carvalho de Assis <[email protected]> AuthorDate: Sat Oct 17 17:54:49 2026 -0300 Documentation: add Morse Micro Wi-Fi HaLow driver documentation Document the Morse Micro MM6108/MM8108 Wi-Fi HaLow driver: architecture, external files downloaded from the MM-IoT-SDK and their licenses, configuration, board interface, usage with wapi and troubleshooting. Describe the WAKE/BUSY power save requirement and the Seeed Studio XIAO HaLow module (Quectel FGH100M-H), where the WAKE and BUSY resistors are not populated and the chip must be kept always awake. Signed-off-by: Alan Carvalho de Assis <[email protected]> Assisted-by: Claude Code Opus 5.5 --- .../os/drivers/special/wireless/morse.rst | 303 +++++++++++++++++++++ 1 file changed, 303 insertions(+) diff --git a/Documentation/os/drivers/special/wireless/morse.rst b/Documentation/os/drivers/special/wireless/morse.rst new file mode 100644 index 00000000000..d2ff232efc5 --- /dev/null +++ b/Documentation/os/drivers/special/wireless/morse.rst @@ -0,0 +1,303 @@ +========================================== +Morse Micro Wi-Fi HaLow (IEEE 802.11ah) +========================================== + +This driver supports the Morse Micro MM6108 and MM8108 Wi-Fi HaLow +transceivers connected to the host through SPI, for example the Quectel +FGH100M module used on the Seeed Studio Wi-Fi HaLow module for XIAO. + +Wi-Fi HaLow (IEEE 802.11ah) works in the sub-1 GHz bands (e.g. 902-928 MHz +in the US, 863-868 MHz in Europe), giving a longer range and a lower power +consumption than the 2.4/5 GHz Wi-Fi. For the network stack it behaves like +any other Wi-Fi interface: the driver registers a ``wlanN`` network device +that is configured with the standard wireless IOCTLs, for example with the +:doc:`wapi </applications/wireless/wapi/index>` tool. + +The driver is located in ``drivers/wireless/ieee80211/morse`` and supports +the station (STA) mode with open, OWE and WPA3-SAE security. + +Architecture +============ + +:: + + +----------------------------------------------+ + | NuttX network stack / wapi / dhcpc | + +----------------------------------------------+ + | morse_netdev.c netdev lower half and | + | wireless operations | + +----------------------------------------------+ + | libmorse 802.11ah UMAC, WPA3-SAE | pre-built + | (Morse Micro) supplicant, rate control, | library + | chip transport (SPI) | + +----------------------+-----------------------+ + | morse_mmosal.c | morse_mmhal.c | + | OS abstraction: | hardware abstraction:| + | kthreads, mutexes, | SPI, GPIOs, IRQs, | + | semaphores, timers | firmware and BCF | + +----------------------+-----------------------+ + | NuttX kernel | board lower half | + | | (morse_lower_s) | + +----------------------+-----------------------+ + +The 802.11ah MAC, the supplicant and the chip transport protocol are +provided by ``libmorse``, the Morse Micro host library that is also used by +the Morse Micro FreeRTOS and Zephyr ports. The NuttX driver implements the +interfaces that ``libmorse`` needs from the platform: + +- ``mmosal`` (``morse_mmosal.c``): tasks, mutexes, semaphores, queues and + timers on top of the NuttX kernel services. The timers run on the low + priority work queue. +- ``mmhal`` (``morse_mmhal.c``): SPI transfers through the NuttX SPI + driver, control lines and interrupts through the board lower half, and + the chip firmware and board configuration file that are embedded in the + image. +- the network interface (``morse_netdev.c``): it uses the network lower + half interface (``netdev_lowerhalf_s``) and its wireless operations. The + ``libmorse`` datapath uses 802.3 frames, so the interface is an + Ethernet-like ``NET_LL_IEEE80211`` link. + +External files +============== + +The driver itself is Apache-2.0 licensed, but it needs a few files from the +`Morse Micro MM-IoT-SDK <https://github.com/MorseMicro/mm-iot-sdk>`_. They +are downloaded at build time (about 3 MB) to +``drivers/wireless/ieee80211/morse/mm-iot-sdk-<version>`` and removed by +``make distclean``: + +.. list-table:: + :header-rows: 1 + :widths: 45 35 20 + + * - File + - Content + - License + * - ``framework/morselib/include/*.h`` + - libmorse API headers + - Apache-2.0 + * - ``framework/morselib/lib/<core>/libmorse.a`` + - pre-built host library + - Morse Micro Binary Distribution License + * - ``framework/morsefirmware/mm6108.mbin`` + - chip firmware (``mm8108b2-rl.mbin`` for the MM8108) + - Morse Micro Binary Distribution License + * - ``framework/src/mmregdb/mmregdb.[ch]`` + - channel lists of each regulatory domain + - Apache-2.0 + * - ``framework/src/mmpktmem/mmpktmem_heap.c`` + - packet memory allocator + - Apache-2.0 + * - ``framework/src/mmutils/mmutils.h`` + - utility macros + - Apache-2.0 + * - ``framework/tools/buildsystem/convert-bin-to-mbin.py`` + - BCF conversion tool + - Apache-2.0 + * - ``LICENSES/*`` + - license texts + - + +``libmorse.a`` is available for Cortex-M4F, Cortex-M7F and Cortex-M33F; the +driver selects the right one from the architecture configuration. With +the Make build the pre-built library is merged into ``libdrivers.a`` +(``PREBUILT_LIBS`` in ``drivers/Makefile``), with CMake it is added with +``nuttx_add_extra_library()``. In both cases only the referenced objects are +linked. + +The chip also needs a *board configuration file* (BCF) with the RF +calibration and the regulatory limits of the module. The default one is the +Quectel FGH100M-H BCF, downloaded from the +`morse-firmware <https://github.com/MorseMicro/morse-firmware>`_ +repository and converted to the mbin format with the SDK tool. This requires +``python3`` with the ``pyelftools`` package (``pip install pyelftools``). + +The files that are already present are not downloaded again. To build +without network access, copy the files listed above (keeping the SDK +directory layout) to ``drivers/wireless/ieee80211/morse/mm-iot-sdk-<version>`` +and use ``CONFIG_IEEE80211_MORSE_BCF_SDK`` or +``CONFIG_IEEE80211_MORSE_BCF_CUSTOM``, or also copy the converted +``bcf_fgh100mhaamd.mbin`` to ``drivers/wireless/ieee80211/morse``. + +Configuration +============= + +.. list-table:: + :header-rows: 1 + :widths: 45 55 + + * - Option + - Description + * - ``CONFIG_IEEE80211_MORSE`` + - Enable the driver. + * - ``CONFIG_IEEE80211_MORSE_MM6108``, ``CONFIG_IEEE80211_MORSE_MM8108`` + - Transceiver. + * - ``CONFIG_IEEE80211_MORSE_SDK_VERSION`` + - SDK version the files are downloaded from (default ``2.13.1``). + * - ``CONFIG_IEEE80211_MORSE_BCF_*`` + - BCF: Quectel FGH100M-H (default), one of the SDK BCFs, or a custom mbin file. + * - ``CONFIG_IEEE80211_MORSE_COUNTRY`` + - Default regulatory domain (default ``US``). + * - ``CONFIG_IEEE80211_MORSE_SPI_FREQUENCY`` + - Default SPI clock, if the board does not set one. + * - ``CONFIG_IEEE80211_MORSE_TASK_PRIORITY`` + - Priority of the driver tasks. + * - ``CONFIG_IEEE80211_MORSE_CONNECT_TIMEOUT`` + - Time the connect request waits for the link (default 30 s). + * - ``CONFIG_IEEE80211_MORSE_LIBLOG`` + - Route the libmorse messages to the syslog. + +The driver requires ``CONFIG_NETDEV_WIRELESS_IOCTL``. ``libmorse`` is built +against the newlib C library headers: the driver selects +``CONFIG_ARCH_SETJMP_H`` and ``CONFIG_LIBC_CTYPE_TABLE`` (the newlib +compatible ``_ctype_`` table), and ``CONFIG_LIBC_LOCALTIME`` is not supported +(its ``mktime()`` writes the NuttX specific ``tm_gmtoff``/``tm_zone`` fields +of ``struct tm``). + +WPA3-SAE needs a good entropy source, so the driver depends on +``CONFIG_DEV_RANDOM`` or ``CONFIG_DEV_URANDOM``: enable the hardware random +number generator (e.g. ``CONFIG_STM32_RNG``) and ``CONFIG_DEV_RANDOM``. On the +STM32H7 the RNG kernel clock is HSI48, so ``CONFIG_STM32_HSI48`` must be +enabled as well, otherwise the RNG reports a clock error. + +The image includes the ~380 KB chip firmware: the driver needs about 1 MB +of flash. On the WeAct STM32H743 ``halow`` configuration the whole system +uses about 57 KB of heap while connected. + +Board interface +=============== + +The board logic describes how the transceiver is connected with a +``struct morse_lower_s`` (``include/nuttx/wireless/morse.h``) and registers +the driver with ``morse_register()``: + +====================== ====================================================== +Signal Description +====================== ====================================================== +SPI (SCK, MOSI, MISO) SPI mode 0. The chip select is driven by the board + ``<spi>select()`` function for ``devid``. +RESET_N Output, active low. Held low until the driver boots + the chip (``reset``). +SPI_IRQ Input, active low, falling edge interrupt + (``irq_asserted``, ``irq_attach``, ``irq_enable``). + ``irq_enable`` is called from interrupt context. +WAKE Output, active high (``wake``). Optional. +BUSY Input, active high, rising edge interrupt (``busy``, + ``busy_attach``, ``busy_enable``). Optional. +====================== ====================================================== + +.. code-block:: c + + #include <nuttx/wireless/morse.h> + + static const struct morse_lower_s g_halow_lower = + { + .devid = SPIDEV_WIRELESS(0), + .frequency = 20000000, + .reset = halow_reset, + .irq_asserted = halow_irq_asserted, + .irq_attach = halow_irq_attach, + .irq_enable = halow_irq_enable, + + /* WAKE and BUSY not connected: the chip is kept always awake */ + }; + + int board_halow_initialize(void) + { + struct spi_dev_s *spi = stm32_spibus_initialize(1); + + return morse_register(spi, &g_halow_lower); + } + +``morse_register()`` boots the chip (downloading its firmware through SPI), +reads its MAC address and registers the network interface. +``boards/arm/stm32h7/weact-stm32h743/src/stm32_morse.c`` is a complete +example. + +WAKE and BUSY lines (power save) +================================ + +``libmorse`` puts the chip in power save when the bus is idle (after a few +milliseconds): it de-asserts WAKE and stops listening to SPI_IRQ. When the +chip has an event for the host (a received packet, the end of a scan, ...) +it asserts BUSY, which wakes up the host. So **the power save only works if +both WAKE and BUSY are connected**. + +If the board lower half does not provide ``wake`` or ``busy`` (``NULL``), +the driver keeps the chip always awake: it reports BUSY as permanently +asserted, which prevents ``libmorse`` from suspending the host interface, +and it disables the 802.11 power save of the station. This costs more +power but works with only the SPI, SPI_IRQ and RESET_N lines. + +.. warning:: + + On the **Seeed Studio Wi-Fi HaLow module for XIAO** (Quectel FGH100M-H) + the WAKE and BUSY pads (D1 and D4) are **not connected to the module**: + the series resistors near those pins are not populated (DNP). If the + board declares these lines anyway, the driver works until the first time + the chip is put to sleep and then it stops receiving events: a scan never + completes, the chip holds SPI_IRQ asserted and BUSY never rises. Leave + ``wake``/``busy`` unset for this module, or populate the two resistors to + use the power save. + +Also note that this module needs the 5V pad connected: it supplies the RF +front end, without it the module enumerates and receives but cannot +transmit. + +Usage +===== + +Wi-Fi HaLow only allows open, OWE and WPA3-SAE networks (no WPA2). A +passphrase selects WPA3-SAE (with PMF required); WPA/WPA2 requests are +mapped to SAE:: + + nsh> ifconfig wlan0 up + nsh> wapi scan wlan0 + bssid / frequency / signal level / encode / ssid + 1c:bc:ec:30:f7:fb 9.035e+08 -14 0800 silex-ap + nsh> wapi psk wlan0 mypassphrase 3 3 + nsh> wapi essid wlan0 silex-ap 1 + nsh> renew wlan0 + nsh> ifconfig + wlan0 Link encap:Ethernet HWaddr 68:24:99:44:6b:6b at RUNNING mtu 1500 + inet addr:10.0.3.245 DRaddr:10.0.0.1 Mask:255.255.240.0 + nsh> wapi sense wlan0 + -6 + +The frequency is reported in Hz. ``wapi sense`` reports the RSSI (dBm) of +the access point. ``wapi disconnect wlan0`` disconnects (the carrier goes +down). + +The regulatory domain selects the channels and the transmit power limits. +It must match the country where the device is used and the access point +configuration, otherwise the access point is not found. It can be changed +while disconnected:: + + nsh> wapi country wlan0 AU + +The supported codes are the ones of the SDK regulatory database (AU, CA, +EU, GB, IN, JP, KR, NZ and US), restricted by the regions of the BCF. + +Troubleshooting +=============== + +- **The SPI data is shifted by one or more bits**: signal integrity. With + jumper wires, use the lowest GPIO slew rate for SCK and MOSI and keep a + ground wire next to the SPI wires. On the WeAct STM32H743 the bus is + reliable up to 18.75 MHz this way. +- **The scan never completes / no events after the boot**: WAKE/BUSY + declared but not connected, see above. +- **The system hangs while connecting**: the SAE handshake reads a lot of + random numbers; check that the RNG clock is configured (HSI48 on the + STM32H7). +- **The access point is not found**: check the country code and the BCF + regions. +- Enable ``CONFIG_DEBUG_WIRELESS_INFO`` and ``CONFIG_IEEE80211_MORSE_LIBLOG`` + for more messages. + +Limitations +=========== + +- Only the station mode is supported (no access point or relay mode). +- The SPI transfers are polled (no DMA). +- Only the SPI host interface is supported (no SDIO).
