[RFC 2/8] nl80211: introduce freq_offset support

Lachlan Hodges lachlan.hodges at morsemicro.com
Wed May 27 23:38:51 PDT 2026


Introduce the ability to handle S1G channels by introducing the
`freq_offset` parameter to the nl80211 driver to handle S1G channels.

Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
 src/drivers/driver_nl80211.c       | 106 ++++++++++++++++++++---------
 src/drivers/driver_nl80211.h       |   3 +
 src/drivers/driver_nl80211_capa.c  |  30 +++++---
 src/drivers/driver_nl80211_event.c |  26 ++++---
 src/drivers/driver_nl80211_scan.c  |  34 ++++++---
 5 files changed, 141 insertions(+), 58 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index f79558911feb..df82c1b670e3 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -169,7 +169,8 @@ wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss,
 				   const char *driver_params,
 				   enum wpa_p2p_mode p2p_mode);
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
-				  unsigned int freq, unsigned int wait,
+				  unsigned int freq, unsigned int freq_offset,
+				  unsigned int wait,
 				  const u8 *buf, size_t buf_len,
 				  int save_cookie, int no_cck, int no_ack,
 				  int offchanok, const u16 *csa_offs,
@@ -1715,6 +1716,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
 struct nl80211_get_assoc_freq_arg {
 	struct wpa_driver_nl80211_data *drv;
 	unsigned int assoc_freq;
+	unsigned int assoc_freq_offset;
 	unsigned int ibss_freq;
 	u8 assoc_bssid[ETH_ALEN];
 	u8 assoc_ssid[SSID_MAX_LEN];
@@ -1766,8 +1768,12 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
 		if (!drv->sta_mlo_info.valid_links ||
 		    drv->sta_mlo_info.assoc_link_id == link_id) {
 			ctx->assoc_freq = freq;
-			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
-				   ctx->assoc_freq);
+			if (bss[NL80211_BSS_FREQUENCY_OFFSET])
+				ctx->assoc_freq_offset =
+					nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]);
+
+			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u.%u MHz",
+				   ctx->assoc_freq, ctx->assoc_freq_offset);
 		}
 	}
 	if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
@@ -1879,10 +1885,13 @@ try_again:
 	if (ret == 0) {
 		unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
 			arg.ibss_freq : arg.assoc_freq;
+		unsigned int freq_offset = arg.assoc_freq_offset;
 		wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
-			   "associated BSS from scan results: %u MHz", freq);
-		if (freq)
+			   "associated BSS from scan results: %u MHz offset: %d KHz", freq, freq_offset);
+		if (freq) {
 			drv->assoc_freq = freq;
+			drv->assoc_freq_offset = freq_offset;
+		}
 
 		if (drv->sta_mlo_info.valid_links) {
 			int i;
@@ -4673,11 +4682,17 @@ retry:
 		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
 			goto fail;
 	}
-	if (params->freq) {
-		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+	if(params->freq){
+		wpa_printf(MSG_DEBUG, "  * freq=%d MHz", params->freq);
 		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
 			goto fail;
 	}
+	if (params->freq_offset) {
+		wpa_printf(MSG_DEBUG, "  * freq_offset=%d KHz", params->freq_offset);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_OFFSET,
+				params->freq_offset))
+			goto fail;
+	}
 	if (params->ssid) {
 		wpa_printf(MSG_DEBUG, "  * SSID=%s",
 			   wpa_ssid_txt(params->ssid, params->ssid_len));
@@ -4873,14 +4888,14 @@ u8 nl80211_get_link_id_from_link(struct i802_bss *bss, struct i802_link *link)
 }
 
 
-static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq)
+static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq, int freq_offset)
 {
 	struct i802_link *link = nl80211_get_link(bss, link_id);
 
 	link->freq = freq;
+	link->freq_offset = freq_offset;
 }
 
-
 static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
 				 bool bss_freq_debug)
 {
@@ -4906,8 +4921,8 @@ static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
 
 static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
 					size_t data_len, int noack,
-					unsigned int freq, int no_cck,
-					int offchanok,
+					unsigned int freq, unsigned int freq_offset,
+					int no_cck, int offchanok,
 					unsigned int wait_time,
 					const u16 *csa_offs,
 					size_t csa_offs_len, int no_encrypt,
@@ -4953,9 +4968,10 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
 		 * of wpa_supplicant.
 		 */
 		if (freq == 0) {
-			wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
-				   drv->last_mgmt_freq);
+			wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d last_mgmt_freq_offset=%d",
+				   drv->last_mgmt_freq, drv->last_mgmt_freq_offset);
 			freq = drv->last_mgmt_freq;
+			freq_offset = drv->last_mgmt_freq_offset;
 		}
 		wait_time = 0;
 		use_cookie = 0;
@@ -4967,9 +4983,13 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
 		unsigned int link_freq = nl80211_get_link_freq(bss, mgmt->sa,
 							       !freq);
+		/* S1G is exempt from MLO so will always be default link */
+		unsigned int link_freq_offset = bss->flink->freq_offset;
 
-		if (!freq)
+		if (!freq) {
 			freq = link_freq;
+			freq_offset = link_freq_offset;
+		}
 
 		if (freq == link_freq)
 			wait_time = 0;
@@ -4986,9 +5006,10 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
 		    (drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
 		    !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
 			freq = nl80211_get_assoc_freq(drv);
+			freq_offset = drv->assoc_freq_offset;
 			wpa_printf(MSG_DEBUG,
-				   "nl80211: send_mlme - Use assoc_freq=%u for external auth",
-				   freq);
+				   "nl80211: send_mlme - Use assoc_freq=%u assoc_freq_offset=%u for external auth",
+				   freq, freq_offset);
 		}
 
 		/* Allow off channel for PASN authentication */
@@ -5021,9 +5042,10 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
 			   freq);
 	}
 	if (freq == 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
-			   link->freq);
+		wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u bss->freq_offset=%u",
+			   link->freq, link->freq_offset);
 		freq = link->freq;
+		freq_offset = link->freq_offset;
 	}
 
 	if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
@@ -5041,7 +5063,7 @@ send_frame_cmd:
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame_cmd");
-	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+	res = nl80211_send_frame_cmd(bss, freq, freq_offset, wait_time, data, data_len,
 				     use_cookie, no_cck, noack, offchanok,
 				     csa_offs, csa_offs_len, link_id);
 	if (!res)
@@ -5636,6 +5658,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
 	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
 		return -ENOBUFS;
+	wpa_printf(MSG_DEBUG, "  * freq_offset=%d", freq->freq_offset);
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_OFFSET, freq->freq_offset))
+		return -ENOBUFS;
 
 	wpa_printf(MSG_DEBUG, "  * eht_enabled=%d", freq->eht_enabled);
 	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
@@ -5799,7 +5824,8 @@ static int wpa_driver_nl80211_set_ap(void *priv,
 		nl80211_link_set_freq(bss,
 				      params->mld_ap ? params->mld_link_id :
 				      NL80211_DRV_LINK_ID_NA,
-				      params->freq->freq);
+				      params->freq->freq,
+				      params->freq->freq_offset);
 
 	if (params->mld_ap) {
 		wpa_printf(MSG_DEBUG, "nl80211: link_id=%u",
@@ -6167,10 +6193,10 @@ static int nl80211_set_channel(struct i802_bss *bss,
 	int ret;
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
-		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+		   "nl80211: Set freq %d offset %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, , bandwidth=%d MHz, cf1=%d MHz, cf1_offset=%d KHz cf2=%d MHz)",
+		   freq->freq, freq->freq_offset, freq->ht_enabled, freq->vht_enabled,
 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
-		   freq->center_freq1, freq->center_freq2);
+		   freq->center_freq1, freq->center_freq1_offset, freq->center_freq2);
 
 	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
 			      NL80211_CMD_SET_WIPHY);
@@ -6191,7 +6217,7 @@ static int nl80211_set_channel(struct i802_bss *bss,
 
 	ret = send_and_recv_cmd(drv, msg);
 	if (ret == 0) {
-		nl80211_link_set_freq(bss, freq->link_id, freq->freq);
+		nl80211_link_set_freq(bss, freq->link_id, freq->freq, freq->freq_offset);
 		return 0;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
@@ -7534,6 +7560,16 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 		}
 
 		drv->assoc_freq = params->freq.freq;
+
+		if (params->freq.freq_offset && !params->mld_params.mld_addr) {
+			wpa_printf(MSG_DEBUG, "  * freq_offset=%d",
+				params->freq.freq_offset);
+			if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_OFFSET,
+					params->freq.freq_offset))
+				return -1;
+
+			drv->assoc_freq_offset = params->freq.freq_offset;
+		}
 	}
 
 	if (params->freq_hint) {
@@ -9141,7 +9177,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
-					    0, NULL, 0, 0, link_id);
+					    0, 0, NULL, 0, 0, link_id);
 }
 
 
@@ -9168,7 +9204,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
-					    0, NULL, 0, 0, link_id);
+					    0, 0, NULL, 0, 0, link_id);
 }
 
 
@@ -9927,7 +9963,8 @@ static int cookie_handler(struct nl_msg *msg, void *arg)
 
 
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
-				  unsigned int freq, unsigned int wait,
+				  unsigned int freq, unsigned int freq_offset,
+				  unsigned int wait,
 				  const u8 *buf, size_t buf_len,
 				  int save_cookie, int no_cck, int no_ack,
 				  int offchanok, const u16 *csa_offs,
@@ -9938,15 +9975,16 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
 	u64 cookie;
 	int ret = -1;
 
-	wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+	wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u freq_offset=%u wait=%u no_cck=%d "
 		   "no_ack=%d offchanok=%d",
-		   freq, wait, no_cck, no_ack, offchanok);
+		   freq, freq_offset, wait, no_cck, no_ack, offchanok);
 	wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
 
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
 	    ((link_id != NL80211_DRV_LINK_ID_NA) &&
 	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
 	    (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+	    (freq_offset && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_OFFSET, freq_offset)) ||
 	    (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
 	    (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
 			   drv->test_use_roc_tx) &&
@@ -10079,11 +10117,11 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
 
 	if (is_ap_interface(drv->nlmode))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
-						   0, freq, no_cck, offchanok,
+						   0, freq, 0, no_cck, offchanok,
 						   wait_time, NULL, 0, 0,
 						   link_id);
 	else
-		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+		ret = nl80211_send_frame_cmd(bss, freq, 0, wait_time, buf,
 					     24 + data_len, 1, no_cck, 0,
 					     offchanok, NULL, 0, link_id);
 
@@ -11353,7 +11391,7 @@ static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
 	os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
 
 	if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
-					 0, 0, NULL, 0, 0, -1) < 0)
+					 0, 0, 0, NULL, 0, 0, -1) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
 			   "send poll frame");
 }
@@ -11477,7 +11515,7 @@ static int nl80211_start_radar_detection(void *priv,
 
 	ret = send_and_recv_cmd(drv, msg);
 	if (ret == 0) {
-		nl80211_link_set_freq(bss, freq->link_id, freq->freq);
+		nl80211_link_set_freq(bss, freq->link_id, freq->freq, 0);
 		return 0;
 	}
 
@@ -11857,7 +11895,7 @@ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-					    freq, 0, 0, wait, csa_offs,
+					    freq, freq_offset, 0, 0, wait, csa_offs,
 					    csa_offs_len, no_encrypt, link_id);
 }
 
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 9c871ce18e63..14c3a542ecae 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -75,6 +75,7 @@ struct i802_link {
 	unsigned int beacon_set:1;
 
 	int freq;
+	int freq_offset;
 	int bandwidth;
 	u8 addr[ETH_ALEN];
 	void *ctx;
@@ -173,6 +174,7 @@ struct wpa_driver_nl80211_data {
 	enum nl80211_iftype nlmode;
 	enum nl80211_iftype ap_scan_as_station;
 	unsigned int assoc_freq;
+	unsigned int assoc_freq_offset;
 
 	unsigned int disabled_11b_rates:1;
 	unsigned int pending_remain_on_chan:1;
@@ -236,6 +238,7 @@ struct wpa_driver_nl80211_data {
 	int eapol_tx_link_id;
 
 	unsigned int last_mgmt_freq;
+	unsigned int last_mgmt_freq_offset;
 
 	struct wpa_driver_scan_filter *filter_ssids;
 	size_t num_filter_ssids;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 66ccbbb3719c..e423eeeb4528 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -2171,6 +2171,7 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
 
 	os_memset(chan, 0, sizeof(*chan));
 	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+	chan->freq_offset = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_OFFSET]);
 	chan->flag = 0;
 	chan->allowed_bw = ~0;
 	chan->dfs_cac_ms = 0;
@@ -3152,14 +3153,27 @@ static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
 				drv->uses_6ghz = true;
 			if (chan->freq >= 900 && chan->freq < 1000)
 				drv->uses_s1g = true;
-			res = os_snprintf(pos, end - pos, " %d%s%s%s",
-					  chan->freq,
-					  (chan->flag & HOSTAPD_CHAN_DISABLED) ?
-					  "[DISABLED]" : "",
-					  (chan->flag & HOSTAPD_CHAN_NO_IR) ?
-					  "[NO_IR]" : "",
-					  (chan->flag & HOSTAPD_CHAN_RADAR) ?
-					  "[RADAR]" : "");
+
+			if (drv->uses_s1g) {
+				/* For S1G, print frequency in KHz */
+				res = os_snprintf(pos, end - pos, " %d%s%s",
+						MHZ_TO_KHZ(chan->freq) + chan->freq_offset,
+						(chan->flag & HOSTAPD_CHAN_DISABLED) ?
+						"[DISABLED]" : "",
+						(chan->flag & HOSTAPD_CHAN_NO_IR) ?
+						"[NO_IR]" : "");
+			} else {
+				/* For normal operation, print frequency in MHz */
+				res = os_snprintf(pos, end - pos, " %d%s%s%s",
+						chan->freq,
+						(chan->flag & HOSTAPD_CHAN_DISABLED) ?
+						"[DISABLED]" : "",
+						(chan->flag & HOSTAPD_CHAN_NO_IR) ?
+						"[NO_IR]" : "",
+						(chan->flag & HOSTAPD_CHAN_RADAR) ?
+						"[RADAR]" : "");
+			}
+
 			if (os_snprintf_error(end - pos, res))
 				break;
 			pos += res;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 1097c08b4e90..28e8a7123af6 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -1114,7 +1114,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 	}
 
 	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+	event.assoc_info.freq_offset = drv->assoc_freq_offset;
 	drv->first_bss->flink->freq = drv->assoc_freq;
+	drv->first_bss->flink->freq_offset = drv->assoc_freq_offset;
 
 	if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
 	    (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
@@ -1424,8 +1426,8 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
 
 
 static void mlme_event_mgmt(struct i802_bss *bss,
-			    struct nlattr *freq, struct nlattr *sig,
-			    const u8 *frame, size_t len,
+			    struct nlattr *freq, struct nlattr *freq_offset,
+			    struct nlattr *sig, const u8 *frame, size_t len,
 			    int link_id)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -1434,6 +1436,7 @@ static void mlme_event_mgmt(struct i802_bss *bss,
 	u16 fc, stype;
 	int ssi_signal = 0;
 	int rx_freq = 0;
+	int rx_freq_offset = 0;
 
 	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
 	mgmt = (const struct ieee80211_mgmt *) frame;
@@ -1451,13 +1454,15 @@ static void mlme_event_mgmt(struct i802_bss *bss,
 	os_memset(&event, 0, sizeof(event));
 	if (freq) {
 		event.rx_mgmt.freq = nla_get_u32(freq);
+		event.rx_mgmt.freq_offset = nla_get_u32(freq_offset);
 		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+		rx_freq_offset = drv->last_mgmt_freq_offset = event.rx_mgmt.freq_offset;
 	}
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
-		   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+		   " freq=%d freq_offset=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
 		   MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
-		   rx_freq, ssi_signal, fc,
+		   rx_freq, rx_freq_offset, ssi_signal, fc,
 		   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
 		   (unsigned int) len);
 	event.rx_mgmt.frame = frame;
@@ -1842,7 +1847,8 @@ nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
 static void mlme_event(struct i802_bss *bss,
 		       enum nl80211_commands cmd, struct nlattr *frame,
 		       struct nlattr *addr, struct nlattr *timed_out,
-		       struct nlattr *freq, struct nlattr *ack,
+		       struct nlattr *freq, struct nlattr *freq_offset,
+		       struct nlattr *ack,
 		       struct nlattr *cookie, struct nlattr *sig,
 		       struct nlattr *wmm, struct nlattr *req_ie,
 		       struct nlattr *link)
@@ -1959,7 +1965,7 @@ static void mlme_event(struct i802_bss *bss,
 					   nla_data(frame), nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+		mlme_event_mgmt(bss, freq, freq_offset, sig, nla_data(frame),
 				nla_len(frame), link_id);
 		break;
 	case NL80211_CMD_FRAME_TX_STATUS:
@@ -4933,7 +4939,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
 	case NL80211_CMD_UNPROT_DISASSOCIATE:
 		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_WIPHY_FREQ],
+			   tb[NL80211_ATTR_WIPHY_FREQ_OFFSET],
+			   tb[NL80211_ATTR_ACK],
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
 			   tb[NL80211_ATTR_STA_WME],
@@ -5326,7 +5334,9 @@ int process_bss_event(struct nl_msg *msg, void *arg)
 	case NL80211_CMD_FRAME_TX_STATUS:
 		mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_WIPHY_FREQ],
+			   tb[NL80211_ATTR_WIPHY_FREQ_OFFSET],
+			   tb[NL80211_ATTR_ACK],
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
 			   tb[NL80211_ATTR_STA_WME], NULL,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index b44f0a1d5b5a..6328d33dbfbb 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -251,16 +251,31 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
 
 	if (params->freqs) {
 		struct nlattr *freqs;
-		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
-		if (freqs == NULL)
-			goto fail;
-		for (i = 0; params->freqs[i]; i++) {
-			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
-				   "MHz", params->freqs[i]);
-			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+
+		i = 0;
+		if (params->freq_offset) {
+			freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQ_KHZ);
+			if (freqs == NULL)
 				goto fail;
+			for (; params->freqs[i]; i++) {
+				wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+					"KHz", MHZ_TO_KHZ(params->freqs[i]) + params->freq_offset);
+				if (nla_put_u32(msg, i + 1, MHZ_TO_KHZ(params->freqs[i]) + params->freq_offset))
+					goto fail;
+			}
+			nla_nest_end(msg, freqs);
+		} else {
+			freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+			if (freqs == NULL)
+				goto fail;
+			for (; params->freqs[i]; i++) {
+				wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+					"MHz", params->freqs[i]);
+				if (nla_put_u32(msg, i + 1, params->freqs[i]))
+					goto fail;
+			}
+			nla_nest_end(msg, freqs);
 		}
-		nla_nest_end(msg, freqs);
 	}
 
 	os_free(drv->filter_ssids);
@@ -788,6 +803,7 @@ nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
 		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
 		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
+		[NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
 	};
 	struct wpa_scan_res *r;
 	const u8 *ie, *beacon_ie;
@@ -831,6 +847,8 @@ nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
 			  ETH_ALEN);
 	if (bss[NL80211_BSS_FREQUENCY])
 		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+	if (bss[NL80211_BSS_FREQUENCY_OFFSET])
+		r->freq_offset = nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]);
 	if (bss[NL80211_BSS_BEACON_INTERVAL])
 		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
 	if (bss[NL80211_BSS_CAPABILITY])
-- 
2.43.0




More information about the Hostap mailing list