[PATCH RFC 2/9] AP/STA: introduce support for freq_offset

Lachlan Hodges lachlan.hodges at morsemicro.com
Thu Oct 8 20:31:59 PDT 2026


80211ah or S1G sits below 1GHz with channel granularity of 500KHz,
meaning MHz alone is not enough to correctly represent a channels
center frequency. This requires the introduction of a 'freq_offset'
parameter that sits alongside the 'freq' parameter. Where `freq_offset`
exists as a KHz component to be added to `freq`. This parameter is
introduced anywhere we pass `freq` such that an S1G channel
can be correctly defined.

Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
 src/ap/ap_drv_ops.c               |  6 ++--
 src/ap/ap_drv_ops.h               |  3 +-
 src/ap/beacon.c                   |  1 +
 src/ap/dfs.c                      |  2 +-
 src/ap/drv_callbacks.c            |  6 ++--
 src/ap/hostapd.c                  | 27 ++++++++++--------
 src/ap/hostapd.h                  |  1 +
 src/ap/hw_features.c              | 46 +++++++++++++++++--------------
 src/ap/hw_features.h              |  3 +-
 src/ap/interference.c             |  2 +-
 src/common/defs.h                 |  1 +
 src/common/hw_features_common.c   | 21 +++++++++-----
 src/common/hw_features_common.h   | 11 +++++---
 src/common/privsep_commands.h     |  1 +
 src/drivers/driver.h              | 45 ++++++++++++++++++++++++++++--
 src/drivers/driver_nl80211.c      |  2 +-
 src/drivers/driver_privsep.c      |  7 +++--
 wpa_supplicant/ap.c               |  4 ++-
 wpa_supplicant/bss.c              |  5 ++--
 wpa_supplicant/bss.h              |  2 ++
 wpa_supplicant/config.c           |  1 +
 wpa_supplicant/config_file.c      |  1 +
 wpa_supplicant/config_ssid.h      | 12 ++++++++
 wpa_supplicant/ctrl_iface.c       |  1 +
 wpa_supplicant/driver_i.h         |  9 +++---
 wpa_supplicant/events.c           |  9 ++++--
 wpa_supplicant/ibss_rsn.c         |  2 +-
 wpa_supplicant/nan_supplicant.c   |  2 +-
 wpa_supplicant/p2p_supplicant.c   |  4 +--
 wpa_supplicant/pasn_supplicant.c  |  4 +--
 wpa_supplicant/scan.c             | 10 ++++---
 wpa_supplicant/sme.c              | 14 ++++++----
 wpa_supplicant/wpa_priv.c         |  1 +
 wpa_supplicant/wpa_supplicant.c   |  7 ++++-
 wpa_supplicant/wpa_supplicant_i.h |  2 ++
 35 files changed, 192 insertions(+), 83 deletions(-)

diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index dd2f5c542785..90d2909cbe90 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -676,7 +676,8 @@ int hostapd_flush(struct hostapd_data *hapd)
 
 
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
-		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int freq, int freq_offset, int channel,
+		     int edmg, u8 edmg_channel,
 		     int ht_enabled, int vht_enabled,
 		     int he_enabled, bool eht_enabled,
 		     int sec_channel_offset, int oper_chwidth,
@@ -687,6 +688,7 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 	struct hostapd_channel_info info = {
 		.mode = mode,
 		.freq = freq,
+		.freq_offset = freq_offset,
 		.channel = channel,
 		.edmg.enabled = edmg,
 		.edmg.channel = edmg_channel,
@@ -922,7 +924,7 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 
 	if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
 		return 0;
-	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, 0,
 				       csa_offs, csa_offs_len, no_encrypt, 0,
 				       link_id);
 }
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index cd86fb762315..972514d38f72 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -73,7 +73,8 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
 		       const u8 *addr, int idx, int link_id, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
-		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int freq, int freq_offset,
+		     int channel, int edmg, u8 edmg_channel,
 		     int ht_enabled, int vht_enabled, int he_enabled,
 		     bool eht_enabled, int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index be8e1153d6d0..3a886e00544b 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2894,6 +2894,7 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
 	info = (struct hostapd_channel_info) {
 		.mode = iconf->hw_mode,
 		.freq = iface->freq,
+		.freq_offset = iface->freq_offset,
 		.channel = iconf->channel,
 		.edmg.enabled = iconf->enable_edmg,
 		.edmg.channel = iconf->edmg_channel,
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 8b85a25558b4..caba71537b5b 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1143,7 +1143,7 @@ hostapd_is_freq_in_current_hw_info(struct hostapd_iface *iface, int freq)
 	if (!iface->current_mode)
 		return false;
 
-	chan = hw_mode_get_channel(iface->current_mode, freq, NULL);
+	chan = hw_mode_get_channel(iface->current_mode, freq, 0, NULL);
 
 	/* If channel data is not found for the given frequency, consider it is
 	 * out of the current hardware info. */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 7f2ef4b0d021..04da5da0ea75 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1283,7 +1283,7 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 	is_dfs0 = hostapd_is_dfs_required(hapd->iface);
 	hapd->iface->freq = freq;
 
-	channel = hostapd_hw_get_channel(hapd, freq);
+	channel = hostapd_hw_get_channel(hapd, freq, 0);
 	if (!channel) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
@@ -1536,7 +1536,7 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd,
 			if (mode->mode == acs_res->hw_mode) {
 				if (hapd->iface->freq > 0 &&
 				    !hw_mode_get_channel(mode,
-							 hapd->iface->freq,
+							 hapd->iface->freq, 0,
 							 NULL))
 					continue;
 				hapd->iface->current_mode = mode;
@@ -1560,7 +1560,7 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd,
 		goto out;
 	}
 	pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
-				       acs_res->pri_freq, NULL,
+				       acs_res->pri_freq, 0, NULL,
 				       hapd->iface->hw_features,
 				       hapd->iface->num_hw_features);
 	if (!pri_chan) {
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index a4394f73d94f..0503322d5d88 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2021,7 +2021,7 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
 
 		if (mode->mode == iface->conf->hw_mode) {
 			if (iface->freq > 0 &&
-			    !hw_mode_get_channel(mode, iface->freq, NULL)) {
+			    !hw_mode_get_channel(mode, iface->freq, iface->freq_offset, NULL)) {
 				mode = NULL;
 				continue;
 			}
@@ -2046,7 +2046,8 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
 			struct hostapd_channel_data *chan;
 
 			chan = hw_get_channel_freq(mode->mode,
-						   iface->freq, NULL,
+						   iface->freq,
+						   iface->freq_offset, NULL,
 						   hw_features,
 						   num_hw_features);
 
@@ -2081,7 +2082,8 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
 			struct hostapd_channel_data *chan;
 
 			chan = hw_get_channel_freq(mode->mode,
-						   iface->freq, NULL,
+						   iface->freq,
+						   iface->freq_offset, NULL,
 						   hw_features,
 						   num_hw_features);
 			if (!chan) {
@@ -2222,20 +2224,21 @@ static int setup_interface(struct hostapd_iface *iface)
 
 static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
 {
-	int freq, i, j;
+	int freq_khz, i, j;
 
 	if (!iface->conf->channel)
 		return 0;
 	if (iface->conf->op_class) {
-		freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
-					      iface->conf->channel);
-		if (freq < 0) {
+		freq_khz = ieee80211_chan_to_freq_khz(NULL, iface->conf->op_class,
+						      iface->conf->channel);
+		if (freq_khz < 0) {
 			wpa_printf(MSG_INFO,
 				   "Could not convert op_class %u channel %u to operating frequency",
 				   iface->conf->op_class, iface->conf->channel);
 			return -1;
 		}
-		iface->freq = freq;
+		iface->freq = KHZ_TO_MHZ(freq_khz);
+		iface->freq_offset = KHZ_TO_S1G_OFFSET(freq_khz);
 		return 0;
 	}
 
@@ -2702,7 +2705,8 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 #endif /* CONFIG_IEEE80211AX */
 
 		if (!delay_apply_cfg &&
-		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+		    hostapd_set_freq(hapd, hapd->iconf->hw_mode,
+				     iface->freq, iface->freq_offset,
 				     hapd->iconf->channel,
 				     hapd->iconf->enable_edmg,
 				     hapd->iconf->edmg_channel,
@@ -4565,7 +4569,7 @@ int hostapd_change_config_freq(struct hostapd_data *hapd,
 
 	if (!params->channel) {
 		/* check if the new channel is supported by hw */
-		params->channel = hostapd_hw_get_channel(hapd, params->freq);
+		params->channel = hostapd_hw_get_channel(hapd, params->freq, 0);
 	}
 
 	channel = params->channel;
@@ -4911,7 +4915,8 @@ int hostapd_force_channel_switch(struct hostapd_iface *iface,
 	if (!settings->freq_params.channel) {
 		/* Check if the new channel is supported */
 		settings->freq_params.channel = hostapd_hw_get_channel(
-			iface->bss[0], settings->freq_params.freq);
+			iface->bss[0], settings->freq_params.freq,
+			settings->freq_params.freq_offset);
 		if (!settings->freq_params.channel)
 			return -1;
 	}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 6ce29653b2ad..8383b164d32b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -645,6 +645,7 @@ struct hostapd_iface {
 	int num_hw_features;
 	struct hostapd_hw_modes *current_mode;
 	int freq;
+	int freq_offset;
 
 	bool radar_detected;
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 9fe84f70add2..d7715206ce9e 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -197,10 +197,11 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
 				continue;
 
 			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
-				   "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
+				   "chan=%d freq=%d MHz freq_offset=%d KHz max_tx_power=%d dBm%s",
 				   feature->mode,
 				   feature->channels[j].chan,
 				   feature->channels[j].freq,
+				   feature->channels[j].freq_offset,
 				   feature->channels[j].max_tx_power,
 				   dfs ? dfs_info(&feature->channels[j]) : "");
 		}
@@ -335,11 +336,11 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 	if (!iface->current_mode)
 		return 0;
 
-	p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
+	p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, 0, NULL,
 				     iface->hw_features,
 				     iface->num_hw_features);
 
-	s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
+	s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, 0, NULL,
 				     iface->hw_features,
 				     iface->num_hw_features);
 
@@ -374,10 +375,10 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 
 	if (!iface->current_mode)
 		return 0;
-	pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, 0,
 				       NULL, iface->hw_features,
 				       iface->num_hw_features);
-	sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
+	sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, 0,
 				       NULL, iface->hw_features,
 				       iface->num_hw_features);
 
@@ -922,15 +923,16 @@ int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
  * -1 = not currently usable due to 6 GHz NO-IR
  */
 static int hostapd_is_usable_chan(struct hostapd_iface *iface,
-				  int frequency, int primary)
+				  int frequency, int freq_offset, int primary)
 {
 	struct hostapd_channel_data *chan;
 
 	if (!iface->current_mode)
 		return 0;
 
-	chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
-				   iface->hw_features, iface->num_hw_features);
+	chan = hw_get_channel_freq(iface->current_mode->mode, frequency,
+				   freq_offset, NULL, iface->hw_features,
+				   iface->num_hw_features);
 	if (!chan)
 		return 0;
 
@@ -967,7 +969,7 @@ static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
 	if (!iface->current_mode)
 		return 0;
 	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
-				       iface->freq, NULL,
+				       iface->freq, 0, NULL,
 				       iface->hw_features,
 				       iface->num_hw_features);
 	if (!pri_chan)
@@ -997,7 +999,7 @@ static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
 		if (num_of_enabled > 4)
 			return 0;
 
-		err = hostapd_is_usable_chan(iface, freq, 1);
+		err = hostapd_is_usable_chan(iface, freq, 0, 1);
 		if (err <= 0)
 			return err;
 
@@ -1118,7 +1120,7 @@ static int hostapd_is_usable_chan_seg(struct hostapd_iface *iface)
 	for (i = 0; i < chan_num; i++) {
 		int freq = start_freq + i * 20;
 
-		err = hostapd_is_usable_chan(iface, freq, freq == iface->freq);
+		err = hostapd_is_usable_chan(iface, freq, 0, freq == iface->freq);
 		if (err <= 0) {
 			wpa_printf(MSG_ERROR,
 				   "Frequency %d is not allowed (seg0)",
@@ -1142,7 +1144,7 @@ static int hostapd_is_usable_chan_seg(struct hostapd_iface *iface)
 	for (i = 0; i < chan_num; i++) {
 		int freq = start_freq + i * 20;
 
-		err = hostapd_is_usable_chan(iface, freq, freq == iface->freq);
+		err = hostapd_is_usable_chan(iface, freq, 0, freq == iface->freq);
 		if (err <= 0) {
 			wpa_printf(MSG_ERROR,
 				   "Frequency %d is not allowed (seg1)",
@@ -1170,7 +1172,8 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
 	if (!iface->current_mode)
 		return 0;
 	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
-				       iface->freq, NULL,
+				       iface->freq,
+				       iface->freq_offset, NULL,
 				       iface->hw_features,
 				       iface->num_hw_features);
 	if (!pri_chan) {
@@ -1197,7 +1200,8 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
 	    oper_chwidth != CONF_OPER_CHWIDTH_USE_HT && central)
 		return hostapd_is_usable_chan_seg(iface);
 
-	err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
+	err = hostapd_is_usable_chan(iface, pri_chan->freq,
+				     pri_chan->freq_offset, 1);
 	if (err <= 0) {
 		wpa_printf(MSG_ERROR, "Primary frequency not allowed");
 		return err;
@@ -1207,7 +1211,7 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
 		return 1;
 
 	err = hostapd_is_usable_chan(iface, iface->freq +
-				     iface->conf->secondary_channel * 20, 0);
+				     iface->conf->secondary_channel * 20, 0, 0);
 	if (err > 0) {
 		if (iface->conf->secondary_channel == 1 &&
 		    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))
@@ -1226,7 +1230,7 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
 		HOSTAPD_CHAN_WIDTH_40P : HOSTAPD_CHAN_WIDTH_40M;
 
 	secondary_freq = iface->freq + new_sec * 20;
-	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0, 0);
 	if (err2 <= 0)
 		return err;
 
@@ -1244,7 +1248,8 @@ static bool skip_mode(struct hostapd_iface *iface,
 {
 	int chan;
 
-	if (iface->freq > 0 && !hw_mode_get_channel(mode, iface->freq, &chan))
+	if (iface->freq > 0 &&
+	    !hw_mode_get_channel(mode, iface->freq, iface->freq_offset, &chan))
 		return true;
 
 	if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 &&
@@ -1514,13 +1519,14 @@ int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 }
 
 
-int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq, int freq_offset)
 {
 	int i, channel;
 	struct hostapd_hw_modes *mode;
 
 	if (hapd->iface->current_mode) {
-		channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
+		channel = hw_get_chan(hapd->iface->current_mode->mode,
+				      freq, freq_offset,
 				      hapd->iface->hw_features,
 				      hapd->iface->num_hw_features);
 		if (channel)
@@ -1533,7 +1539,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 		return 0;
 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
 		mode = &hapd->iface->hw_features[i];
-		channel = hw_get_chan(mode->mode, freq,
+		channel = hw_get_chan(mode->mode, freq, freq_offset,
 				      hapd->iface->hw_features,
 				      hapd->iface->num_hw_features);
 		if (channel)
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index 4993e68afd62..d49f532b5cad 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -20,7 +20,8 @@ int hostapd_acs_completed(struct hostapd_iface *iface, int err);
 int hostapd_select_hw_mode(struct hostapd_iface *iface);
 const char * hostapd_hw_mode_txt(int mode);
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
-int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq,
+			   int freq_offset);
 int hostapd_check_ht_capab(struct hostapd_iface *iface);
 int hostapd_check_edmg_capab(struct hostapd_iface *iface);
 int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface);
diff --git a/src/ap/interference.c b/src/ap/interference.c
index c93cdc75f13f..44aa6c0019ef 100644
--- a/src/ap/interference.c
+++ b/src/ap/interference.c
@@ -552,7 +552,7 @@ int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
 			goto exit;
 		}
 		if (hostapd_set_freq(
-			    iface->bss[0], iface->conf->hw_mode, iface->freq,
+			    iface->bss[0], iface->conf->hw_mode, iface->freq, 0,
 			    iface->conf->channel, iface->conf->enable_edmg,
 			    iface->conf->edmg_channel, iface->conf->ieee80211n,
 			    iface->conf->ieee80211ac,
diff --git a/src/common/defs.h b/src/common/defs.h
index 9f7ea34a2e5a..d6bd21b38baf 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -591,5 +591,6 @@ enum wpa_p2p_mode {
 
 #define MHZ_TO_KHZ(x) ((x) * 1000)
 #define KHZ_TO_MHZ(x) ((x) / 1000)
+#define KHZ_TO_S1G_OFFSET(x) ((x) % 1000)
 
 #endif /* DEFS_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index ce0074d10311..0d4f47cf796d 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -41,14 +41,16 @@ struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
 
 
 struct hostapd_channel_data *
-hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan)
+hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq,
+		    int freq_offset, int *chan)
 {
 	int i;
 
 	for (i = 0; i < mode->num_channels; i++) {
 		struct hostapd_channel_data *ch = &mode->channels[i];
 
-		if (ch->freq == freq) {
+		if (MHZ_TO_KHZ(ch->freq) + ch->freq_offset ==
+		    MHZ_TO_KHZ(freq) + freq_offset) {
 			if (chan)
 				*chan = ch->chan;
 			return ch;
@@ -60,8 +62,9 @@ hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan)
 
 
 struct hostapd_channel_data *
-hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
-		    struct hostapd_hw_modes *hw_features, int num_hw_features)
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int freq_offset,
+		    int *chan, struct hostapd_hw_modes *hw_features,
+		    int num_hw_features)
 {
 	struct hostapd_channel_data *chan_data;
 	int i;
@@ -78,7 +81,8 @@ hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
 		if (curr_mode->mode != mode)
 			continue;
 
-		chan_data = hw_mode_get_channel(curr_mode, freq, chan);
+		chan_data = hw_mode_get_channel(curr_mode, freq,
+						freq_offset, chan);
 		if (chan_data)
 			return chan_data;
 	}
@@ -97,12 +101,13 @@ int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
 }
 
 
-int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+int hw_get_chan(enum hostapd_hw_mode mode, int freq, int freq_offset,
 		struct hostapd_hw_modes *hw_features, int num_hw_features)
 {
 	int chan;
 
-	hw_get_channel_freq(mode, freq, &chan, hw_features, num_hw_features);
+	hw_get_channel_freq(mode, freq, freq_offset, &chan, hw_features,
+			    num_hw_features);
 
 	return chan;
 }
@@ -553,6 +558,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
 {
 	enum hostapd_hw_mode mode = info->mode;
 	int freq = info->freq;
+	int freq_offset = info->freq_offset;
 	int channel = info->channel;
 	int enable_edmg = info->edmg.enabled;
 	u8 edmg_channel = info->edmg.channel;
@@ -582,6 +588,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
 	os_memset(data, 0, sizeof(*data));
 	data->mode = mode;
 	data->freq = freq;
+	data->freq_offset = freq_offset;
 	data->channel = channel;
 	data->ht_enabled = ht_enabled;
 	data->vht_enabled = vht_enabled;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 605739939e3d..47333a7bd7de 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -15,14 +15,16 @@
 struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
 						  int chan, int *freq);
 struct hostapd_channel_data *
-hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan);
+hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq,
+		    int freq_offset, int *chan);
 
 struct hostapd_channel_data *
-hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
-		    struct hostapd_hw_modes *hw_features, int num_hw_features);
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int freq_offset,
+		    int *chan, struct hostapd_hw_modes *hw_features,
+		    int num_hw_features);
 
 int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
-int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+int hw_get_chan(enum hostapd_hw_mode mode, int freq, int freq_offset,
 		struct hostapd_hw_modes *hw_features, int num_hw_features);
 
 int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
@@ -43,6 +45,7 @@ struct hostapd_channel_info {
 	enum hostapd_hw_mode mode;
 
 	int freq;
+	int freq_offset;
 	int channel;
 	enum oper_chan_width oper_chwidth;
 
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index d2c4bbd5e8b6..85e1e89977b2 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -63,6 +63,7 @@ struct privsep_cmd_associate {
 	size_t ssid_len;
 	int hwmode;
 	int freq;
+	int freq_offset;
 	int channel;
 	int pairwise_suite;
 	int group_suite;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e957a56ae093..f8c854227011 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -126,6 +126,11 @@ struct hostapd_channel_data {
 	 */
 	int freq;
 
+	/**
+	 * freq_offset - Frequency offset from @freq in KHz
+	 */
+	int freq_offset;
+
 	/**
 	 * flag - Channel flags (HOSTAPD_CHAN_*)
 	 */
@@ -386,6 +391,7 @@ struct hostapd_multi_hw_info {
  * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
  * @bssid: BSSID
  * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @freq_offset: frequency offset of the channel from @freq in KHz
  * @max_cw: the max channel width of the connection (calculated during scan
  * result processing)
  * @beacon_int: beacon interval in TUs (host byte order)
@@ -426,6 +432,7 @@ struct wpa_scan_res {
 	unsigned int flags;
 	u8 bssid[ETH_ALEN];
 	int freq;
+	int freq_offset;
 	enum chan_width max_cw;
 	u16 beacon_int;
 	u16 caps;
@@ -533,6 +540,13 @@ struct wpa_driver_scan_params {
 	 */
 	int *freqs;
 
+	/**
+	 * freq_offset - Frequency offset in KHz
+	 *
+	 * Each @freqs value will have this frequency offset applied.
+	 */
+	int freq_offset;
+
 	/**
 	 * filter_ssids - Filter for reporting SSIDs
 	 *
@@ -771,6 +785,7 @@ struct wpa_driver_scan_params {
  */
 struct wpa_driver_auth_params {
 	int freq;
+	int freq_offset;
 	const u8 *bssid;
 	const u8 *ssid;
 	size_t ssid_len;
@@ -851,6 +866,11 @@ struct hostapd_freq_params {
 	 */
 	int freq;
 
+	/**
+	 * freq_offset - Frequency offset in KHz from @freq
+	 */
+	int freq_offset;
+
 	/**
 	 * channel - Channel number
 	 */
@@ -887,6 +907,13 @@ struct hostapd_freq_params {
 	 */
 	int center_freq1;
 
+	/**
+	 * center_freq1_offset - Segment 0 center frequency offset in KHz
+	 *
+	 * Valid for S1G.
+	 */
+	int center_freq1_offset;
+
 	/**
 	 * center_freq2 - Segment 1 center frequency in MHz
 	 *
@@ -3957,6 +3984,7 @@ struct wpa_driver_ops {
 	 * @noack: Do not wait for this frame to be acked (disable retries)
 	 * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
 	 * driver decide
+	 * @freq_offset: Frequency offset (in KHz) from @freq to send the frame on
 	 * @csa_offs: Array of CSA offsets or %NULL
 	 * @csa_offs_len: Number of elements in csa_offs
 	 * @no_encrypt: Do not encrypt frame even if appropriate key exists
@@ -3966,8 +3994,8 @@ struct wpa_driver_ops {
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
-			 int noack, unsigned int freq, const u16 *csa_offs,
-			 size_t csa_offs_len, int no_encrypt,
+			 int noack, unsigned int freq, unsigned int freq_offset,
+			 const u16 *csa_offs, size_t csa_offs_len, int no_encrypt,
 			 unsigned int wait, int link_id);
 
 	/**
@@ -6937,6 +6965,11 @@ union wpa_event_data {
 		 */
 		unsigned int freq;
 
+		/**
+		 * freq_offset - Frequency offset from @freq in KHz
+		 */
+		unsigned int freq_offset;
+
 		/**
 		 * wmm_params - WMM parameters used in this association.
 		 */
@@ -7330,6 +7363,12 @@ union wpa_event_data {
 		 */
 		int freq;
 
+		/**
+		 * freq_offset - Frequency offset (in KHz) from @freq on which
+		 * the frame was received
+		 */
+		int freq_offset;
+
 		/**
 		 * ssi_signal - Signal strength in dBm (or 0 if not available)
 		 */
@@ -7533,6 +7572,7 @@ union wpa_event_data {
 	/**
 	 * struct ch_switch
 	 * @freq: Frequency of new channel in MHz
+	 * @freq_offset: Frequency offset from @freq in KHz
 	 * @ht_enabled: Whether this is an HT channel
 	 * @ch_offset: Secondary channel offset
 	 * @ch_width: Channel width
@@ -7543,6 +7583,7 @@ union wpa_event_data {
 	 */
 	struct ch_switch {
 		int freq;
+		int freq_offset;
 		int ht_enabled;
 		int ch_offset;
 		enum chan_width ch_width;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 84540d6c0063..5839a8f9be08 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -12015,7 +12015,7 @@ static bool nl80211_is_drv_shared(void *priv, int link_id)
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
 				    size_t data_len, int noack,
-				    unsigned int freq,
+				    unsigned int freq, unsigned int freq_offset,
 				    const u16 *csa_offs, size_t csa_offs_len,
 				    int no_encrypt, unsigned int wait,
 				    int link_id)
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index d7c6b01a317e..e53421179d01 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -306,10 +306,10 @@ static int wpa_driver_privsep_associate(
 	int res;
 	size_t buflen;
 
-	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d freq_offset=%d pairwise_suite=%d "
 		   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
-		   __func__, priv, params->freq.freq, params->pairwise_suite,
-		   params->group_suite, params->key_mgmt_suite,
+		   __func__, priv, params->freq.freq, params->freq.freq_offset,
+		   params->pairwise_suite, params->group_suite, params->key_mgmt_suite,
 		   params->auth_alg, params->mode);
 
 	buflen = sizeof(*data) + params->wpa_ie_len;
@@ -323,6 +323,7 @@ static int wpa_driver_privsep_associate(
 	data->ssid_len = params->ssid_len;
 	data->hwmode = params->freq.mode;
 	data->freq = params->freq.freq;
+	data->freq_offset = params->freq.freq_offset;
 	data->channel = params->freq.channel;
 	data->pairwise_suite = params->pairwise_suite;
 	data->group_suite = params->group_suite;
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index aa3eef9b68a8..460b44ced891 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -457,7 +457,8 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
 		{
 			if (iface == wpa_s ||
 			    iface->wpa_state < WPA_AUTHENTICATING ||
-			    (int) iface->assoc_freq != ssid->frequency)
+			    MHZ_TO_KHZ(iface->assoc_freq) + iface->assoc_freq_offset !=
+			    MHZ_TO_KHZ(ssid->frequency) + ssid->freq_offset)
 				continue;
 
 			/*
@@ -1196,6 +1197,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
 	wpa_s->assoc_freq = ssid->frequency;
+	wpa_s->assoc_freq_offset = ssid->freq_offset;
 	wpa_s->ap_iface->conf->enable_edmg = ssid->enable_edmg;
 	wpa_s->ap_iface->conf->edmg_channel = ssid->edmg_channel;
 
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index b534cab1cd88..987297dd3146 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -384,6 +384,7 @@ 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_offset = src->freq_offset;
 	dst->max_cw = src->max_cw;
 	dst->beacon_int = src->beacon_int;
 	dst->caps = src->caps;
@@ -635,9 +636,9 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
 	}
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
-		" SSID '%s' freq %d%s",
+		" SSID '%s' freq %d freq_offset %d%s",
 		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
-		bss->freq, extra);
+		bss->freq, bss->freq_offset, extra);
 	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
 	return bss;
 }
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index d9d0401cef32..5580fc0d32c4 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -93,6 +93,8 @@ struct wpa_bss {
 	size_t ssid_len;
 	/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
 	int freq;
+	/** Frequency offset from @freq of the channel in KHz */
+	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/config.c b/wpa_supplicant/config.c
index afe770b584a5..dce75ae074a2 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -3018,6 +3018,7 @@ static const struct parse_data ssid_fields[] = {
 #endif /* CONFIG_PASN */
 	{ FUNC(security_profiles) },
 	{ BOOL(disable_pmksa_caching) },
+	{ INT_RANGE(scan_freq_offset, 0, 999)},
 };
 
 #undef OFFSET
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index b01134945e13..7e4328410fe3 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -826,6 +826,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid,
 	STR(bgscan);
 	STR(autoscan);
 	STR(scan_freq);
+	INT(scan_freq_offset);
 	STR(freq_list);
 #ifdef IEEE8021X_EAPOL
 	write_eap(f, ssid);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 86bd8d6172c4..a6932c2dcd98 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -547,6 +547,11 @@ struct wpa_ssid {
 	 */
 	int frequency;
 
+	/**
+	 * freq_offset - Channel frequency offset from @frequency in KHz
+	 */
+	int freq_offset;
+
 	/**
 	 * enable_edmg - Enable EDMG feature in STA/AP mode
 	 *
@@ -653,6 +658,13 @@ struct wpa_ssid {
 	 */
 	int *scan_freq;
 
+	/**
+	 * scan_freq_offset - Frequency offset in KHz
+	 *
+	 * To be applied to each entry in @scan_freq array.
+	 */
+	int scan_freq_offset;
+
 	/**
 	 * bgscan - Background scan and roaming parameters or %NULL if none
 	 *
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 0d995bb60e9f..027baa25de90 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -10714,6 +10714,7 @@ static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
 	params.freq.freq = wpa_s->sme.freq;
+	params.freq.freq_offset = wpa_s->sme.freq_offset;
 	if (wpa_s->last_assoc_req_wpa_ie) {
 		params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
 		params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 9122b9353b25..a2d30f87975e 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -336,12 +336,13 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
 
 static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
 				    const u8 *data, size_t data_len, int noack,
-				    unsigned int freq, unsigned int wait)
+				    unsigned int freq, unsigned int freq_offset,
+				    unsigned int wait)
 {
 	if (wpa_s->driver->send_mlme)
-		return wpa_s->driver->send_mlme(wpa_s->drv_priv,
-						data, data_len, noack,
-						freq, NULL, 0, 0, wait, -1);
+		return wpa_s->driver->send_mlme(wpa_s->drv_priv, data, data_len,
+						noack, freq, freq_offset, NULL,
+						0, 0, wait, -1);
 	return -1;
 }
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index b2d9558aa183..4f92fe039f42 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1864,11 +1864,11 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 
 	if (debug_print) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR
-			" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s",
+			" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d freq_offset=%d %s%s",
 			i, MAC2STR(bss->bssid),
 			wpa_ssid_txt(bss->ssid, bss->ssid_len),
 			wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
-			bss->freq,
+			bss->freq, bss->freq_offset,
 			wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ?
 			" wps" : "",
 			(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
@@ -4334,6 +4334,7 @@ no_pfs:
 	}
 
 	wpa_s->assoc_freq = data->assoc_info.freq;
+	wpa_s->assoc_freq_offset = data->assoc_info.freq_offset;
 
 #ifdef CONFIG_P2P
 	if (wpa_s->allow_p2p_assisted_dfs &&
@@ -5815,8 +5816,10 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
 	{
 		struct wpa_bss *bss;
 		bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
-		if (bss)
+		if (bss) {
 			wpa_s->sme.freq = bss->freq;
+			wpa_s->sme.freq_offset = bss->freq_offset;
+		}
 		wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
 		sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
 			      WLAN_AUTH_FT);
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index e4f592399f15..16518706e8c3 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -534,7 +534,7 @@ static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
 	wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR,
 		   seq, MAC2STR(da));
 
-	return wpa_drv_send_mlme(wpa_s, (u8 *) &auth, auth_length, 0, 0, 0);
+	return wpa_drv_send_mlme(wpa_s, (u8 *) &auth, auth_length, 0, 0, 0, 0);
 }
 
 
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 173d2a0cedd7..ffdaa874fd9f 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -1079,7 +1079,7 @@ static int wpas_nan_pasn_send_cb(void *ctx, const u8 *data, size_t data_len,
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	return wpa_drv_send_mlme(wpa_s, data, data_len, 0, freq, 0);
+	return wpa_drv_send_mlme(wpa_s, data, data_len, 0, freq, 0, 0);
 }
 
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 648b08bab945..6a9fc41eb0eb 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3410,7 +3410,7 @@ static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
-				 freq, 0);
+				 freq, 0, 0);
 }
 
 
@@ -5800,7 +5800,7 @@ static int wpas_p2p_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len,
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, 0, wait);
 }
 
 
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 76415fdbeed4..0d12dc99a3f6 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -77,7 +77,7 @@ static int wpas_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, 0, wait);
 }
 
 
@@ -1789,7 +1789,7 @@ int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
 	 * without a radio work.
 	 */
 	ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
-				bss->freq, 0);
+				bss->freq, 0, 0);
 
 	wpabuf_free(buf);
 	wpa_printf(MSG_DEBUG, "PASN: deauth: send_mlme ret=%d", ret);
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index f02d8cbcd60b..376761417acf 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1338,6 +1338,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 				os_free(params.freqs);
 				params.freqs = NULL;
 			}
+			params.freq_offset = tssid->scan_freq_offset;
 			freqs_set = 1;
 		}
 		int_array_sort_unique(params.freqs);
@@ -2563,20 +2564,20 @@ static void dump_scan_res(struct wpa_scan_results *scan_res)
 			int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
 
 			wpa_printf(MSG_EXCESSIVE, MACSTR
-				   " ssid=%s freq=%d qual=%d noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
+				   " ssid=%s freq=%d freq_offset=%d qual=%d noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
 				   MAC2STR(r->bssid),
 				   wpa_ssid_txt(ssid, ssid_len),
-				   r->freq, r->qual,
+				   r->freq, r->freq_offset, r->qual,
 				   r->noise, noise_valid ? "" : "~", r->level,
 				   r->snr, r->snr >= GREAT_SNR ? "*" : "",
 				   r->flags,
 				   r->age, r->est_throughput);
 		} else {
 			wpa_printf(MSG_EXCESSIVE, MACSTR
-				   " ssid=%s freq=%d qual=%d noise=%d level=%d flags=0x%x age=%u est=%u",
+				   " ssid=%s freq=%d freq_offset=%d qual=%d noise=%d level=%d flags=0x%x age=%u est=%u",
 				   MAC2STR(r->bssid),
 				   wpa_ssid_txt(ssid, ssid_len),
-				   r->freq, r->qual,
+				   r->freq, r->freq_offset, r->qual,
 				   r->noise, r->level, r->flags, r->age,
 				   r->est_throughput);
 		}
@@ -3882,6 +3883,7 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
 	params->p2p_include_6ghz = src->p2p_include_6ghz;
 	params->non_coloc_6ghz = src->non_coloc_6ghz;
 	params->min_probe_req_content = src->min_probe_req_content;
+	params->freq_offset = src->freq_offset;
 	return params;
 
 failed:
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 1279a29af915..8070b8b826fb 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1589,6 +1589,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 	wpa_s->reassociate = 0;
 
 	params.freq = bss->freq;
+	params.freq_offset = bss->freq_offset;
 	params.bssid = bss->bssid;
 	params.ssid = bss->ssid;
 	params.ssid_len = bss->ssid_len;
@@ -1599,6 +1600,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 		wpa_s->sme.prev_bssid_set = 0;
 
 	wpa_s->sme.freq = params.freq;
+	wpa_s->sme.freq_offset = params.freq_offset;
 	os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
 	wpa_s->sme.ssid_len = params.ssid_len;
 
@@ -2300,8 +2302,9 @@ no_fils:
 	wpa_supplicant_cancel_scan(wpa_s);
 
 	wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
-		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
-		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
+		" (SSID='%s' freq=%d freq_offset=%d)", MAC2STR(params.bssid),
+		wpa_ssid_txt(params.ssid, params.ssid_len),
+		params.freq, params.freq_offset);
 
 	eapol_sm_notify_portValid(wpa_s->eapol, false);
 	wpa_clear_keys(wpa_s, bss->bssid);
@@ -2561,7 +2564,7 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
 				    wpa_s->sme.ext_ml_auth ?
 				    wpa_s->own_addr : NULL);
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
-			  wpa_s->sme.ext_auth_freq, 0);
+			  wpa_s->sme.ext_auth_freq, 0, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
 
@@ -2680,7 +2683,7 @@ static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
 				    wpa_s->own_addr : NULL);
 
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
-			  wpa_s->sme.ext_auth_freq, 0);
+			  wpa_s->sme.ext_auth_freq, 0, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
 }
@@ -2884,7 +2887,7 @@ static int sme_external_auth_send_802_1x(struct wpa_supplicant *wpa_s,
 					   wpa_s->own_addr : NULL);
 
 	if (wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf),
-			      0, 0, 0) < 0) {
+			      0, 0, 0, 0) < 0) {
 		wpa_printf(MSG_INFO,
 			   "IEEE 802.1X: Failed to send Authentication frame");
 		goto fail;
@@ -4980,6 +4983,7 @@ mscs_fail:
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
 	params.freq.freq = wpa_s->sme.freq;
+	params.freq.freq_offset = wpa_s->sme.freq_offset;
 	params.bg_scan_period = ssid ? ssid->bg_scan_period : -1;
 	params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
 		wpa_s->sme.assoc_req_ie : NULL;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 88f3f2a52356..b49da9f299d1 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -325,6 +325,7 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
 	params.ssid_len = assoc->ssid_len;
 	params.freq.mode = assoc->hwmode;
 	params.freq.freq = assoc->freq;
+	params.freq.freq_offset = assoc->freq_offset;
 	params.freq.channel = assoc->channel;
 	if (assoc->wpa_ie_len) {
 		params.wpa_ie = (u8 *) (assoc + 1);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index aa455c2041f8..71a099f9a4ed 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -3557,8 +3557,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 				"Driver does not support mesh mode");
 			return;
 		}
-		if (bss)
+		if (bss) {
 			ssid->frequency = bss->freq;
+			ssid->freq_offset = bss->freq_offset;
+		}
 		if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
 			wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
 			wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
@@ -4132,6 +4134,7 @@ skip_80mhz:
 	info = (struct hostapd_channel_info) {
 		.mode = mode->mode,
 		.freq = freq->freq,
+		.freq_offset = freq->freq_offset,
 		.channel = freq->channel,
 		.edmg.enabled = ssid->enable_edmg,
 		.edmg.channel = ssid->edmg_channel,
@@ -4170,6 +4173,7 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
 	bool is_6ghz, is_24ghz;
 
 	freq->freq = ssid->frequency;
+	freq->freq_offset = ssid->freq_offset;
 
 	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
 		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
@@ -5587,6 +5591,7 @@ 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_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 4f30cff1c921..47522e33601e 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -805,6 +805,7 @@ struct wpa_supplicant {
 	struct wpa_bss *current_bss;
 	int ap_ies_from_associnfo;
 	unsigned int assoc_freq;
+	unsigned int assoc_freq_offset;
 	u8 ap_mld_addr[ETH_ALEN];
 	u8 mlo_assoc_link_id;
 	u16 valid_links; /* bitmap of valid MLO link IDs */
@@ -1110,6 +1111,7 @@ struct wpa_supplicant {
 		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 		int freq;
+		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