[PATCH 6/6] tests: add S1G association coverage

James Ewing james at teledatics.com
Tue Sep 30 12:13:38 PDT 2025


Introduce the S1G association hwsim test that verifies the
freq_khz/freq_offset plumbing end-to-end. The test integrates with the
existing run-tests.py harness and gracefully skips when the running
kernel does not expose NL80211_BAND_S1GHZ.

Tested: ./tests/hwsim/run-tests.py -q test_s1g_assoc (SKIP: iw phy did 
not report an S1G channel index)
Signed-off-by: James Ewing <james at teledatics.com>
---
  tests/hwsim/test_s1g_assoc.py | 101 ++++++++++++++++++++++++++++++++++
  1 file changed, 101 insertions(+)
  create mode 100644 tests/hwsim/test_s1g_assoc.py

diff --git a/tests/hwsim/test_s1g_assoc.py b/tests/hwsim/test_s1g_assoc.py
new file mode 100644
index 000000000..80ca3be31
--- /dev/null
+++ b/tests/hwsim/test_s1g_assoc.py
@@ -0,0 +1,101 @@
+# S1G association path coverage
+#
+# Validates that the supplicant can associate with an S1G-capable AP while
+# preserving kHz-level channel metadata inside the BSS cache.
+
+import logging
+
+import hostapd
+
+from utils import HwsimSkip, list_s1g_capable_channels
+
+
+logger = logging.getLogger()
+
+
+def _pick_s1g_channel():
+    channels = list_s1g_capable_channels()
+    if not channels:
+        raise HwsimSkip("mac80211_hwsim does not expose any S1G channels")
+
+    for entry in channels:
+        if entry.get("channel"):
+            return entry
+
+    # Fall back to the first entry even if iw does not report a channel 
number.
+    return channels[0]
+
+
+def test_sta_assoc_s1g_channel(dev, apdev):
+    """Exercise STA association on an S1G channel and observe freq_khz 
plumbing"""
+
+    channel_info = _pick_s1g_channel()
+    channel = channel_info.get("channel")
+    if channel is None:
+        raise HwsimSkip("iw phy did not report an S1G channel index")
+
+    freq_khz = int(round(channel_info["freq_mhz"] * 1000))
+    primary_freq_mhz = freq_khz // 1000
+    freq_offset = freq_khz - primary_freq_mhz * 1000
+
+    logger.info(
+        "Using S1G channel %d at %.3f MHz (primary %d MHz, offset %d kHz)",
+        channel,
+        channel_info["freq_mhz"],
+        primary_freq_mhz,
+        freq_offset,
+    )
+
+    params = {
+        "ssid": "s1g-assoc",
+        "hw_mode": "ah",
+        "channel": str(channel),
+        "ieee80211n": "0",
+        "ieee80211ac": "0",
+        "ieee80211ax": "0",
+    }
+
+    hapd = hostapd.add_ap(apdev[0], params, set_channel=False)
+
+    dev[0].connect(
+        ssid="s1g-assoc",
+        key_mgmt="NONE",
+        scan_freq=str(primary_freq_mhz),
+    )
+
+    status = dev[0].get_status()
+    if status.get("freq") != str(primary_freq_mhz):
+        raise Exception(
+            "Supplicant reported freq %s, expected %d MHz" %
+            (status.get("freq"), primary_freq_mhz))
+
+    bss = dev[0].get_bss(hapd.own_addr())
+    if not bss:
+        raise Exception("Failed to fetch BSS entry for associated S1G AP")
+
+    if bss.get("freq") != str(primary_freq_mhz):
+        raise Exception(
+            "BSS table freq %s does not match primary %d MHz" %
+            (bss.get("freq"), primary_freq_mhz))
+
+    freq_khz_str = bss.get("freq_khz")
+    if freq_khz_str is None:
+        raise Exception("freq_khz missing from BSS table entry")
+    if int(freq_khz_str) != freq_khz:
+        raise Exception(
+            "freq_khz %s did not match expected %d" %
+            (freq_khz_str, freq_khz))
+
+    freq_offset_str = bss.get("freq_offset")
+    if freq_offset:
+        if freq_offset_str is None:
+            raise Exception("freq_offset missing from BSS table entry")
+        if int(freq_offset_str) != freq_offset:
+            raise Exception(
+                "freq_offset %s did not match expected %d" %
+                (freq_offset_str, freq_offset))
+    elif freq_offset_str is not None and int(freq_offset_str) != 0:
+        raise Exception("Expected zero freq_offset, saw %s" % 
freq_offset_str)
+
+    dev[0].request("DISCONNECT")
+
-- 
2.43.0





More information about the Hostap mailing list