[RFC 1/8] AP/STA: introduce support for freq_offset

Lachlan Hodges lachlan.hodges at morsemicro.com
Wed May 27 23:38:50 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.

Additionally, internal channel calculation functions have been
converted to KHz - similarly to cfg80211 - whilst the external
callers such as ieee80211_chan_to_freq() simply convert the internal
KHz result to MHz to reduce API churn.

Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
 src/ap/ap_drv_ops.c               |  10 ++-
 src/ap/ap_drv_ops.h               |   3 +-
 src/ap/beacon.c                   |   1 +
 src/ap/dfs.c                      |   4 +-
 src/ap/drv_callbacks.c            |   6 +-
 src/ap/hostapd.c                  |  29 ++++---
 src/ap/hostapd.h                  |   1 +
 src/ap/hw_features.c              |  43 +++++-----
 src/ap/hw_features.h              |   2 +-
 src/ap/interference.c             |   2 +-
 src/common/defs.h                 |   4 +
 src/common/hw_features_common.c   |  22 +++--
 src/common/hw_features_common.h   |  13 +--
 src/common/ieee802_11_common.c    | 136 ++++++++++++++++++------------
 src/common/ieee802_11_common.h    |   1 +
 src/common/privsep_commands.h     |   1 +
 src/common/proximity_ranging.h    |   3 +-
 src/drivers/driver.h              |  44 +++++++++-
 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_ssid.h      |  12 +++
 wpa_supplicant/ctrl_iface.c       |   1 +
 wpa_supplicant/driver_i.h         |   9 +-
 wpa_supplicant/events.c           |   8 +-
 wpa_supplicant/ibss_rsn.c         |   2 +-
 wpa_supplicant/mesh.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 +
 38 files changed, 280 insertions(+), 144 deletions(-)

diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 61b823abe138..5862bedff238 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -646,7 +646,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,
@@ -655,7 +656,8 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 	struct hostapd_freq_params data;
 	struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
 
-	if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
+	if (hostapd_set_freq_params(&data, mode, freq, freq_offset,
+				    channel, edmg,
 				    edmg_channel, ht_enabled,
 				    vht_enabled, he_enabled, eht_enabled,
 				    sec_channel_offset, oper_chwidth,
@@ -883,7 +885,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);
 }
@@ -1094,7 +1096,7 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 		return -1;
 	}
 
-	if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
+	if (hostapd_set_freq_params(&data, mode, freq, 0, channel, 0, 0,
 				    ht_enabled,
 				    vht_enabled, he_enabled, eht_enabled,
 				    sec_channel_offset,
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 66913ed0fca7..fa84f709de91 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -71,7 +71,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 2521e3ab293e..2610106871a6 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2808,6 +2808,7 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
 
 	if (cmode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+				    iface->freq_offset,
 				    iconf->channel, iconf->enable_edmg,
 				    iconf->edmg_channel, iconf->ieee80211n,
 				    iconf->ieee80211ac, iconf->ieee80211ax,
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index d72c8ddb4464..2758454ff033 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1014,7 +1014,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
 #endif /* CONFIG_MESH */
 	err = hostapd_set_freq_params(&csa_settings.freq_params,
 				      iface->conf->hw_mode,
-				      freq, channel,
+				      freq, 0, channel,
 				      iface->conf->enable_edmg,
 				      iface->conf->edmg_channel,
 				      iface->conf->ieee80211n,
@@ -1138,7 +1138,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 0c1e1c67c105..71e09cacfa56 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1275,7 +1275,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,
@@ -1528,7 +1528,7 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd,
 			if (mode->mode == acs_res->hw_mode) {
 				if (hapd->iface->freq > 0 &&
 				    !hw_get_chan(mode->mode,
-						 hapd->iface->freq,
+						 hapd->iface->freq, 0,
 						 hapd->iface->hw_features,
 						 hapd->iface->num_hw_features))
 					continue;
@@ -1553,7 +1553,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 72a0bf503efe..a15e17919b3f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1976,7 +1976,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;
 			}
@@ -2001,7 +2001,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);
 
@@ -2036,7 +2037,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) {
@@ -2177,20 +2179,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;
 	}
 
@@ -2642,7 +2645,8 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 #endif /* CONFIG_MESH */
 
 		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,
@@ -4511,7 +4515,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;
@@ -4524,7 +4528,7 @@ int hostapd_change_config_freq(struct hostapd_data *hapd,
 	/* if a pointer to old_params is provided we save previous state */
 	if (old_params &&
 	    hostapd_set_freq_params(old_params, conf->hw_mode,
-				    hostapd_hw_get_freq(hapd, conf->channel),
+				    hostapd_hw_get_freq(hapd, conf->channel), 0,
 				    conf->channel, conf->enable_edmg,
 				    conf->edmg_channel, conf->ieee80211n,
 				    conf->ieee80211ac, conf->ieee80211ax,
@@ -4794,7 +4798,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 fb06a5afd935..362924aeff54 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -646,6 +646,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 be8ed53522a8..6ac7cd9d8837 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]) : "");
 		}
@@ -333,11 +334,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);
 
@@ -372,10 +373,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);
 
@@ -920,15 +921,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;
 
@@ -965,7 +967,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)
@@ -995,7 +997,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;
 
@@ -1097,7 +1099,8 @@ static 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) {
@@ -1105,7 +1108,8 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 		return 0;
 	}
 
-	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;
@@ -1121,7 +1125,7 @@ static 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))
@@ -1135,14 +1139,14 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 
 	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
 	secondary_freq = iface->freq + 20;
-	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0, 0);
 	if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
 		iface->conf->secondary_channel = 1;
 		return 1;
 	}
 
 	secondary_freq = iface->freq - 20;
-	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+	err2 = hostapd_is_usable_chan(iface, secondary_freq, 0, 0);
 	if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
 		iface->conf->secondary_channel = -1;
 		return 1;
@@ -1157,7 +1161,7 @@ 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 &&
@@ -1427,13 +1431,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)
@@ -1446,7 +1451,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 6f945cc7736b..4d73c3656146 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -20,7 +20,7 @@ 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 c88766e7512a..ec602d660ace 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -585,4 +585,8 @@ enum wpa_p2p_mode {
 #define USEC_TO_TU(m) ((m) / USEC_80211_TU)
 #define TU_TO_USEC(m) ((m) * USEC_80211_TU)
 
+#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 e928738ee568..32471ce36651 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -41,14 +41,15 @@ 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 +61,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 +80,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 +100,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;
 }
@@ -477,7 +481,8 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
 
 int hostapd_set_freq_params(struct hostapd_freq_params *data,
 			    enum hostapd_hw_mode mode,
-			    int freq, int channel, int enable_edmg,
+			    int freq, int freq_offset,
+			    int channel, int enable_edmg,
 			    u8 edmg_channel, int ht_enabled,
 			    int vht_enabled, int he_enabled,
 			    bool eht_enabled, int sec_channel_offset,
@@ -498,6 +503,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 80e33adf2d5c..63c0893e2c2b 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,
@@ -39,7 +41,8 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
 			    enum oper_chan_width *width, u8 *seg0, u8 *seg1);
 int hostapd_set_freq_params(struct hostapd_freq_params *data,
 			    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,
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 32342f7d991c..a3b9592e23bc 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1825,7 +1825,7 @@ static int country_match(const char *const cc[], const char *const country)
 }
 
 
-static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
+static int ieee80211_chan_to_freq_khz_us(u8 op_class, u8 chan)
 {
 	switch (op_class) {
 	case 12: /* channels 1..11 */
@@ -1833,7 +1833,7 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
 	case 33: /* channels 5..11; 40 MHz */
 		if (chan < 1 || chan > 11)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 1: /* channels 36,40,44,48 */
 	case 2: /* channels 52,56,60,64; dfs */
 	case 22: /* channels 36,44; 40 MHz */
@@ -1842,12 +1842,12 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
 	case 28: /* channels 56,64; 40 MHz */
 		if (chan < 36 || chan > 64)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 4: /* channels 100-144 */
 	case 24: /* channels 100-140; 40 MHz */
 		if (chan < 100 || chan > 144)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 3: /* channels 149,153,157,161 */
 	case 25: /* channels 149,157; 40 MHz */
 	case 26: /* channels 149,157; 40 MHz */
@@ -1855,11 +1855,11 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
 	case 31: /* channels 153,161; 40 MHz */
 		if (chan < 149 || chan > 161)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 5: /* channels 149,153,157,161,165 */
 		if (chan < 149 || chan > 165)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 34: /* 60 GHz band, channels 1..8 */
 		if (chan < 1 || chan > 8)
 			return -1;
@@ -1867,22 +1867,22 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
 	case 37: /* 60 GHz band, EDMG CB2, channels 9..15 */
 		if (chan < 9 || chan > 15)
 			return -1;
-		return 56160 + 2160 * (chan - 8);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 8));
 	case 38: /* 60 GHz band, EDMG CB3, channels 17..22 */
 		if (chan < 17 || chan > 22)
 			return -1;
-		return 56160 + 2160 * (chan - 16);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 16));
 	case 39: /* 60 GHz band, EDMG CB4, channels 25..29 */
 		if (chan < 25 || chan > 29)
 			return -1;
-		return 56160 + 2160 * (chan - 24);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 24));
 	default:
 		return -1;
 	}
 }
 
 
-static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
+static int ieee80211_chan_to_freq_khz_eu(u8 op_class, u8 chan)
 {
 	switch (op_class) {
 	case 4: /* channels 1..13 */
@@ -1890,7 +1890,7 @@ static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
 	case 12: /* channels 5..13; 40 MHz */
 		if (chan < 1 || chan > 13)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 1: /* channels 36,40,44,48 */
 	case 2: /* channels 52,56,60,64; dfs */
 	case 5: /* channels 36,44; 40 MHz */
@@ -1899,41 +1899,41 @@ static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
 	case 9: /* channels 56,64; 40 MHz */
 		if (chan < 36 || chan > 64)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 3: /* channels 100-140 */
 	case 7: /* channels 100-132; 40 MHz */
 	case 10: /* channels 104-136; 40 MHz */
 	case 16: /* channels 100-140 */
 		if (chan < 100 || chan > 140)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 17: /* channels 149,153,157,161,165,169 */
 		if (chan < 149 || chan > 169)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 18: /* 60 GHz band, channels 1..6 */
 		if (chan < 1 || chan > 6)
 			return -1;
-		return 56160 + 2160 * chan;
+		return MHZ_TO_KHZ(56160 + 2160 * chan);
 	case 21: /* 60 GHz band, EDMG CB2, channels 9..11 */
 		if (chan < 9 || chan > 11)
 			return -1;
-		return 56160 + 2160 * (chan - 8);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 8));
 	case 22: /* 60 GHz band, EDMG CB3, channels 17..18 */
 		if (chan < 17 || chan > 18)
 			return -1;
-		return 56160 + 2160 * (chan - 16);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 16));
 	case 23: /* 60 GHz band, EDMG CB4, channels 25 */
 		if (chan != 25)
 			return -1;
-		return 56160 + 2160 * (chan - 24);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 24));
 	default:
 		return -1;
 	}
 }
 
 
-static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
+static int ieee80211_chan_to_freq_khz_jp(u8 op_class, u8 chan)
 {
 	/* Table E-3 in IEEE Std 802.11-2020 - Operating classes in Japan */
 	switch (op_class) {
@@ -1942,11 +1942,11 @@ static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
 	case 57: /* channels 5..13; 40 MHz */
 		if (chan < 1 || chan > 13)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 31: /* channel 14 */
 		if (chan != 14)
 			return -1;
-		return 2414 + 5 * chan;
+		return MHZ_TO_KHZ(2414 + 5 * chan);
 	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
 	case 32: /* channels 52,56,60,64 */
 	case 33: /* channels 52,56,60,64 */
@@ -1958,7 +1958,7 @@ static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
 	case 43: /* channels 56,64; 40 MHz */
 		if (chan < 34 || chan > 64)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 34: /* channels 100-144 */
 	case 35: /* reserved */
 	case 39: /* channels 100-140; 40 MHz */
@@ -1968,30 +1968,30 @@ static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
 	case 58: /* channels 100-144 */
 		if (chan < 100 || chan > 144)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 59: /* 60 GHz band, channels 1..6 */
 		if (chan < 1 || chan > 6)
 			return -1;
-		return 56160 + 2160 * chan;
+		return MHZ_TO_KHZ(56160 + 2160 * chan);
 	case 62: /* 60 GHz band, EDMG CB2, channels 9..11 */
 		if (chan < 9 || chan > 11)
 			return -1;
-		return 56160 + 2160 * (chan - 8);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 8));
 	case 63: /* 60 GHz band, EDMG CB3, channels 17..18 */
 		if (chan < 17 || chan > 18)
 			return -1;
-		return 56160 + 2160 * (chan - 16);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 16));
 	case 64: /* 60 GHz band, EDMG CB4, channel 25 */
 		if (chan != 25)
 			return -1;
-		return 56160 + 2160 * (chan - 24);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 24));
 	default:
 		return -1;
 	}
 }
 
 
-static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
+static int ieee80211_chan_to_freq_khz_cn(u8 op_class, u8 chan)
 {
 	switch (op_class) {
 	case 7: /* channels 1..13 */
@@ -1999,44 +1999,56 @@ static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
 	case 9: /* channels 5..13; 40 MHz */
 		if (chan < 1 || chan > 13)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 1: /* channels 36,40,44,48 */
 	case 2: /* channels 52,56,60,64; dfs */
 	case 4: /* channels 36,44; 40 MHz */
 	case 5: /* channels 52,60; 40 MHz */
 		if (chan < 36 || chan > 64)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 3: /* channels 149,153,157,161,165 */
 	case 6: /* channels 149,157; 40 MHz */
 		if (chan < 149 || chan > 165)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	default:
 		return -1;
 	}
 }
 
 
-static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
+static int ieee80211_chan_to_freq_khz_global(u8 op_class, u8 chan)
 {
 	/* Table E-4 in IEEE Std 802.11-2020 - Global operating classes */
 	switch (op_class) {
+	case 50:
+	case 51:
+	case 52:
+	case 53:
+	case 68:
+	case 69:
+	case 70:
+	case 71:
+	case 72:
+		if (chan < 1 || chan > 51)
+			return -1;
+		return 902000 + chan * 500;
 	case 81:
 		/* channels 1..13 */
 		if (chan < 1 || chan > 13)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 82:
 		/* channel 14 */
 		if (chan != 14)
 			return -1;
-		return 2414 + 5 * chan;
+		return MHZ_TO_KHZ(2414 + 5 * chan);
 	case 83: /* channels 1..9; 40 MHz */
 	case 84: /* channels 5..13; 40 MHz */
 		if (chan < 1 || chan > 13)
 			return -1;
-		return 2407 + 5 * chan;
+		return MHZ_TO_KHZ(2407 + 5 * chan);
 	case 115: /* channels 36,40,44,48; indoor only */
 	case 116: /* channels 36,44; 40 MHz; indoor only */
 	case 117: /* channels 40,48; 40 MHz; indoor only */
@@ -2045,32 +2057,32 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
 	case 120: /* channels 56,64; 40 MHz; dfs */
 		if (chan < 36 || chan > 64)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 121: /* channels 100-144 */
 	case 122: /* channels 100-140; 40 MHz */
 	case 123: /* channels 104-144; 40 MHz */
 		if (chan < 100 || chan > 144)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 124: /* channels 149,153,157,161 */
 		if (chan < 149 || chan > 161)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 125: /* channels 149,153,157,161,165,169,173,177 */
 	case 126: /* channels 149,157,165,173; 40 MHz */
 	case 127: /* channels 153,161,169,177; 40 MHz */
 		if (chan < 149 || chan > 177)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
 	case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
 		if (chan < 36 || chan > 177)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 129: /* center freqs 50, 114, 163; 160 MHz */
 		if (chan < 36 || chan > 177)
 			return -1;
-		return 5000 + 5 * chan;
+		return MHZ_TO_KHZ(5000 + 5 * chan);
 	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
 	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
 	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
@@ -2079,7 +2091,7 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
 	case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */
 		if (chan < 1 || chan > 233)
 			return -1;
-		return 5950 + chan * 5;
+		return MHZ_TO_KHZ(5950 + chan * 5);
 	case 136: /* UHB channels, 20 MHz: 2 */
 		if (chan == 2)
 			return 5935;
@@ -2087,60 +2099,76 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
 	case 180: /* 60 GHz band, channels 1..8 */
 		if (chan < 1 || chan > 8)
 			return -1;
-		return 56160 + 2160 * chan;
+		return MHZ_TO_KHZ(56160 + 2160 * chan);
 	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
 		if (chan < 9 || chan > 15)
 			return -1;
-		return 56160 + 2160 * (chan - 8);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 8));
 	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
 		if (chan < 17 || chan > 22)
 			return -1;
-		return 56160 + 2160 * (chan - 16);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 16));
 	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
 		if (chan < 25 || chan > 29)
 			return -1;
-		return 56160 + 2160 * (chan - 24);
+		return MHZ_TO_KHZ(56160 + 2160 * (chan - 24));
 	default:
 		return -1;
 	}
 }
 
 /**
- * ieee80211_chan_to_freq - Convert channel info to frequency
+ * ieee80211_chan_to_freq_khz - Convert channel info to frequency KHz
  * @country: Country code, if known; otherwise, global operating class is used
  * @op_class: Operating class
  * @chan: Channel number
- * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ * Returns: Frequency in KHz or -1 if the specified channel is unknown
  */
-int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+int ieee80211_chan_to_freq_khz(const char *country, u8 op_class, u8 chan)
 {
 	int freq;
 
 	if (country_match(us_op_class_cc, country)) {
-		freq = ieee80211_chan_to_freq_us(op_class, chan);
+		freq = ieee80211_chan_to_freq_khz_us(op_class, chan);
 		if (freq > 0)
 			return freq;
 	}
 
 	if (country_match(eu_op_class_cc, country)) {
-		freq = ieee80211_chan_to_freq_eu(op_class, chan);
+		freq = ieee80211_chan_to_freq_khz_eu(op_class, chan);
 		if (freq > 0)
 			return freq;
 	}
 
 	if (country_match(jp_op_class_cc, country)) {
-		freq = ieee80211_chan_to_freq_jp(op_class, chan);
+		freq = ieee80211_chan_to_freq_khz_jp(op_class, chan);
 		if (freq > 0)
 			return freq;
 	}
 
 	if (country_match(cn_op_class_cc, country)) {
-		freq = ieee80211_chan_to_freq_cn(op_class, chan);
+		freq = ieee80211_chan_to_freq_khz_cn(op_class, chan);
 		if (freq > 0)
 			return freq;
 	}
 
-	return ieee80211_chan_to_freq_global(op_class, chan);
+	return ieee80211_chan_to_freq_khz_global(op_class, chan);
+}
+
+/**
+ * ieee80211_chan_to_freq - Convert channel info to frequency
+ * @country: Country code, if known; otherwise, global operating class is used
+ * @op_class: Operating class
+ * @chan: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+{
+	int freq_khz = ieee80211_chan_to_freq_khz(country, op_class, chan);
+	if (freq_khz < 0)
+		return -1;
+
+	return KHZ_TO_MHZ(freq_khz);
 }
 
 
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 5d9c840cee76..ed0147812fde 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -264,6 +264,7 @@ int hostapd_config_tx_queue(struct hostapd_tx_queue_params queue[],
 			    const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
 int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+int ieee80211_chan_to_freq_khz(const char *country, u8 op_class, u8 chan);
 enum hostapd_hw_mode
 ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
 			      enum oper_chan_width chanwidth,
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/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 902ced541040..dbcd04a0ae9e 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -563,7 +563,8 @@ struct pr_config {
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
-			      int noack, unsigned int freq, unsigned int wait);
+			      int noack, unsigned int freq, unsigned int freq_offset,
+			      unsigned int wait);
 
 	/**
 	 * negotiation_started - Called when PASN negotiation begins
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6450c66b5d3a..c382bf90ccf6 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_*)
 	 */
@@ -374,6 +379,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)
@@ -414,6 +420,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;
@@ -521,6 +528,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
 	 *
@@ -759,6 +773,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;
@@ -839,6 +854,11 @@ struct hostapd_freq_params {
 	 */
 	int freq;
 
+	/**
+	 * freq_offset - Frequency offset in KHz from @freq
+	 */
+	int freq_offset;
+
 	/**
 	 * channel - Channel number
 	 */
@@ -875,6 +895,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
 	 *
@@ -3891,6 +3918,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
@@ -3900,8 +3928,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);
 
 	/**
@@ -6822,6 +6850,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.
 		 */
@@ -7215,6 +7248,11 @@ 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)
 		 */
@@ -7418,6 +7456,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
@@ -7428,6 +7467,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 033e4eb46e8b..f79558911feb 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -11850,7 +11850,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 d0a1a96fd5b4..8a3f86c2e0ff 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;
 
 			/*
@@ -1182,6 +1183,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 2cd9cc8c73f5..304486f4b650 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 e99036366681..6bb57ae7dc19 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2959,6 +2959,7 @@ static const struct parse_data ssid_fields[] = {
 #ifdef CONFIG_PASN
 	{ FUNC(pasn_groups) },
 #endif /* CONFIG_PASN */
+	{ INT_RANGE(scan_freq_offset, 0, 999)},
 };
 
 #undef OFFSET
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 7c60d4e38cab..aa3c1a166819 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 ce9bc3277a32..93ceeb223cd9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -10582,6 +10582,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 13ac06bfd8b2..930946ebcd7a 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -334,12 +334,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 1d203a8004b7..f9c51994cae4 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1659,11 +1659,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) ||
@@ -5557,8 +5557,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 06228d0ef6c9..3d26bc1368a4 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -533,7 +533,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/mesh.c b/wpa_supplicant/mesh.c
index 8f7143f6ea63..30547b19d108 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -198,7 +198,7 @@ static int wpas_mesh_update_freq_params(struct wpa_supplicant *wpa_s)
 	if (hostapd_set_freq_params(
 		    &params->freq,
 		    ifmsh->conf->hw_mode,
-		    ifmsh->freq,
+		    ifmsh->freq, 0,
 		    ifmsh->conf->channel,
 		    ifmsh->conf->enable_edmg,
 		    ifmsh->conf->edmg_channel,
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index b842293dfccc..4ad92a7993ab 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -1044,7 +1044,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, 0, 0);
+	return wpa_drv_send_mlme(wpa_s, data, data_len, 0, 0, 0, 0);
 }
 
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 7ae584a5a9d9..66d396826d56 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3349,7 +3349,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);
 }
 
 
@@ -5739,7 +5739,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 e2862c6fab0c..b3939ed24d04 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -76,7 +76,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);
 }
 
 
@@ -1637,7 +1637,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 b7050585651f..92c1143700f6 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1327,6 +1327,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);
@@ -2553,20 +2554,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);
 		}
@@ -3872,6 +3873,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 b0b77fe60335..1dd1f79601b3 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1257,6 +1257,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;
@@ -1267,6 +1268,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;
 
@@ -1892,8 +1894,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);
@@ -2153,7 +2156,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);
 
@@ -2272,7 +2275,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);
 }
@@ -2476,7 +2479,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;
@@ -4509,6 +4512,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 367448eaf70b..be9db6d646fd 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2953,8 +2953,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");
@@ -3525,6 +3527,7 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
 
 skip_80mhz:
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+				    freq->freq_offset,
 				    freq->channel, ssid->enable_edmg,
 				    ssid->edmg_channel, freq->ht_enabled,
 				    freq->vht_enabled, freq->he_enabled,
@@ -3555,6 +3558,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);
@@ -4737,6 +4741,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 fd327cdb37dc..e446f683a002 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -798,6 +798,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 */
@@ -1077,6 +1078,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