[PATCH 5/6] wpa_supplicant: preserve freq_khz metadata

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


Keep the kHz-level channel information that the driver now reports by
storing freq_khz/freq_offset in the BSS cache, IBSS frequency selection,
and association parameters. Expose the new fields via the CTRL interface
so downstream tooling can verify S1G chandefs end-to-end.

Tested: tests/hwsim/run_s1g_smoke.sh
Signed-off-by: James Ewing <james at teledatics.com>
---
  wpa_supplicant/bss.c              |  4 ++++
  wpa_supplicant/bss.h              |  4 ++++
  wpa_supplicant/ctrl_iface.c       | 14 ++++++++++++++
  wpa_supplicant/scan.c             | 32 +++++++++++++++++++------------
  wpa_supplicant/wpa_supplicant.c   |  9 +++++++++
  wpa_supplicant/wpa_supplicant_i.h |  2 ++
  6 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 58adaf744..8937dc516 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -384,6 +384,10 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, 
struct wpa_scan_res *src,
      dst->flags = src->flags;
      os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
      dst->freq = src->freq;
+    dst->freq_khz = src->freq_khz;
+    if (!dst->freq_khz && src->freq > 0)
+        dst->freq_khz = src->freq * 1000;
+    dst->freq_offset = src->freq_offset;
      dst->max_cw = src->max_cw;
      dst->beacon_int = src->beacon_int;
      dst->caps = src->caps;
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 74ef3c858..b78759942 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -93,6 +93,10 @@ struct wpa_bss {
      size_t ssid_len;
      /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
      int freq;
+    /** Frequency in kHz when provided by the driver (0 if unknown) */
+    unsigned int freq_khz;
+    /** Frequency offset in kHz relative to @freq */
+    unsigned int freq_offset;
      /** The max channel width supported by both the AP and the STA */
      enum chan_width max_cw;
      /** Beacon interval in TUs (host byte order) */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index ebcc5d7ee..d27f9cac0 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5387,6 +5387,20 @@ static int print_bss_info(struct wpa_supplicant 
*wpa_s, struct wpa_bss *bss,
          if (os_snprintf_error(end - pos, ret))
              return 0;
          pos += ret;
+        if (bss->freq_khz) {
+            ret = os_snprintf(pos, end - pos, "freq_khz=%u\n",
+                      bss->freq_khz);
+            if (os_snprintf_error(end - pos, ret))
+                return 0;
+            pos += ret;
+        }
+        if (bss->freq_offset) {
+            ret = os_snprintf(pos, end - pos, "freq_offset=%u\n",
+                      bss->freq_offset);
+            if (os_snprintf_error(end - pos, ret))
+                return 0;
+            pos += ret;
+        }
      }

      if (mask & WPA_BSS_MASK_BEACON_INT) {
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index a4824678d..d1132163c 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -3303,9 +3303,11 @@ unsigned int wpas_get_est_tpt(const struct 
wpa_supplicant *wpa_s,
               (IS_2P4GHZ(freq) ?
                HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
                HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) && ht40) {
-            if (*max_cw == CHAN_WIDTH_UNKNOWN ||
-                *max_cw < CHAN_WIDTH_40)
-                *max_cw = CHAN_WIDTH_40;
+        if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+            channel_width_to_int(*max_cw) <
+            channel_width_to_int(CHAN_WIDTH_40)) {
+            *max_cw = CHAN_WIDTH_40;
+        }
              adjusted_snr = snr + wpas_channel_width_rssi_bump(
                  ies, ies_len, CHAN_WIDTH_40);
              tmp = max_he_eht_rate(he40_table, adjusted_snr,
@@ -3317,9 +3319,11 @@ unsigned int wpas_get_est_tpt(const struct 
wpa_supplicant *wpa_s,
          if (!IS_2P4GHZ(freq) &&
              (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) &&
              (!IS_5GHZ(freq) || vht80)) {
-            if (*max_cw == CHAN_WIDTH_UNKNOWN ||
-                *max_cw < CHAN_WIDTH_80)
-                *max_cw = CHAN_WIDTH_80;
+        if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+            channel_width_to_int(*max_cw) <
+            channel_width_to_int(CHAN_WIDTH_80)) {
+            *max_cw = CHAN_WIDTH_80;
+        }
              adjusted_snr = snr + wpas_channel_width_rssi_bump(
                  ies, ies_len, CHAN_WIDTH_80);

@@ -3341,9 +3345,11 @@ unsigned int wpas_get_est_tpt(const struct 
wpa_supplicant *wpa_s,
              (cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
                 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) &&
              (!IS_5GHZ(freq) || vht160)) {
-            if (*max_cw == CHAN_WIDTH_UNKNOWN ||
-                *max_cw < CHAN_WIDTH_160)
-                *max_cw = CHAN_WIDTH_160;
+        if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+            channel_width_to_int(*max_cw) <
+            channel_width_to_int(CHAN_WIDTH_160)) {
+            *max_cw = CHAN_WIDTH_160;
+        }
              adjusted_snr = snr + wpas_channel_width_rssi_bump(
                  ies, ies_len, CHAN_WIDTH_160);

@@ -3368,9 +3374,11 @@ unsigned int wpas_get_est_tpt(const struct 
wpa_supplicant *wpa_s,
          if (is_6ghz_freq(freq) &&
              (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
               EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
-            if (*max_cw == CHAN_WIDTH_UNKNOWN ||
-                *max_cw < CHAN_WIDTH_320)
-                *max_cw = CHAN_WIDTH_320;
+        if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+            channel_width_to_int(*max_cw) <
+            channel_width_to_int(CHAN_WIDTH_320)) {
+            *max_cw = CHAN_WIDTH_320;
+        }
              adjusted_snr = snr + wpas_channel_width_rssi_bump(
                  ies, ies_len, CHAN_WIDTH_320);

diff --git a/wpa_supplicant/wpa_supplicant.c 
b/wpa_supplicant/wpa_supplicant.c
index d45002fd9..1928c476e 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -3469,6 +3469,8 @@ void ibss_mesh_setup_freq(struct wpa_supplicant 
*wpa_s,
      bool is_6ghz, is_24ghz;

      freq->freq = ssid->frequency;
+    freq->freq_khz = freq->freq > 0 ? freq->freq * 1000 : 0;
+    freq->freq_offset = 0;

      if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
          struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
@@ -3478,6 +3480,9 @@ void ibss_mesh_setup_freq(struct wpa_supplicant 
*wpa_s,
                     "IBSS already found in scan results, adjust control 
freq: %d",
                     bss->freq);
              freq->freq = bss->freq;
+            freq->freq_khz = bss->freq_khz ? bss->freq_khz :
+                (bss->freq > 0 ? bss->freq * 1000 : 0);
+            freq->freq_offset = bss->freq_offset;
              obss_scan = 0;
          }
      }
@@ -4640,6 +4645,10 @@ static void wpas_start_assoc_cb(struct 
wpa_radio_work *work, int deinit)
                     wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
              params.bssid = bss->bssid;
              params.freq.freq = bss->freq;
+            params.freq.freq_khz = bss->freq_khz;
+            if (!params.freq.freq_khz && bss->freq > 0)
+                params.freq.freq_khz = bss->freq * 1000;
+            params.freq.freq_offset = bss->freq_offset;
          }
          params.bssid_hint = bss->bssid;
          params.freq_hint = bss->freq;
diff --git a/wpa_supplicant/wpa_supplicant_i.h 
b/wpa_supplicant/wpa_supplicant_i.h
index e7675a4ab..d1c41fdcc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1003,6 +1003,8 @@ struct wpa_supplicant {
          u8 ssid[SSID_MAX_LEN];
          size_t ssid_len;
          int freq;
+        unsigned int freq_khz;
+        unsigned int freq_offset;
          u8 assoc_req_ie[1500];
          size_t assoc_req_ie_len;
          int mfp;
-- 
2.43.0





More information about the Hostap mailing list