[PATCH RFC 6/9] AP: add S1G support

Lachlan Hodges lachlan.hodges at morsemicro.com
Thu Oct 8 20:32:03 PDT 2026


Introduce the ability to bring up an S1G AP. Currently only US 2024
channelisation is supported. A sample config is as follows:

```
interface=wlan0
country_code=US
driver=nl80211
ssid=wifi_halow

channel=43
ieee80211ah=1
op_class=71
beacon_int=100
dtim_period=10
s1g_oper_centr_freq_idx=44
s1g_primary_2mhz=1

hw_mode=ah
wpa=2
wpa_key_mgmt=SAE
rsn_pairwise=CCMP
sae_password=12345678
ieee80211w=2
```

Where we are configuring an operating channel of 44, primary 1MHz
channel of 43, whilst utilising a 2MHz primary. There is no need to
include sae_pwe=1 as H2E will automatically be enabled for S1G.
ieee80211w=2 is required, and will error if not set correctly during
config validation.

Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
 hostapd/Makefile                |   5 +
 hostapd/config_file.c           |  12 ++
 src/ap/Makefile                 |   2 +
 src/ap/ap_config.c              |  54 +++++++++
 src/ap/ap_config.h              |  23 ++++
 src/ap/ap_drv_ops.c             |   8 ++
 src/ap/ap_drv_ops.h             |   4 +-
 src/ap/beacon.c                 |  92 +++++++++++++--
 src/ap/ctrl_iface_ap.c          |   8 +-
 src/ap/dfs.c                    |   2 +-
 src/ap/hostapd.c                |   7 +-
 src/ap/hw_features.c            |  31 ++++-
 src/ap/ieee802_11.c             |  55 +++++++--
 src/ap/ieee802_11.h             |   6 +
 src/ap/ieee802_11_s1g.c         | 202 ++++++++++++++++++++++++++++++++
 src/ap/interference.c           |   1 +
 src/ap/sta_info.c               |   3 +-
 src/ap/sta_info.h               |   2 +
 src/common/defs.h               |   6 +
 src/common/hw_features_common.c |  46 +++++++-
 src/common/hw_features_common.h |   7 ++
 src/common/ieee802_11_common.c  |  99 +++++++++++++++-
 src/common/ieee802_11_common.h  |   6 +-
 src/common/ieee802_11_defs.h    |  91 +++++++++++---
 src/drivers/driver.h            |  24 +++-
 wlantest/bip.c                  |  12 +-
 wlantest/bss.c                  |   2 +-
 27 files changed, 752 insertions(+), 58 deletions(-)
 create mode 100644 src/ap/ieee802_11_s1g.c

diff --git a/hostapd/Makefile b/hostapd/Makefile
index 1ecafdc5591e..7fb18f86882d 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -369,6 +369,11 @@ NEED_JSON=y
 endif
 endif
 
+ifdef CONFIG_IEEE80211AH
+CFLAGS += -DCONFIG_IEEE80211AH
+OBJS += ../src/ap/ieee802_11_s1g.o
+endif
+
 ifdef CONFIG_MBO
 CFLAGS += -DCONFIG_MBO
 OBJS += ../src/ap/mbo_ap.o
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 2717adc55bd5..232c415aa1d4 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3345,6 +3345,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 			conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
 		else if (os_strcmp(pos, "ad") == 0)
 			conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+#ifdef CONFIG_IEEE80211AH
+		else if (os_strcmp(pos, "ah") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211AH;
+#endif /* CONFIG_IEEE80211AH */
 		else if (os_strcmp(pos, "any") == 0)
 			conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
 		else {
@@ -4004,6 +4008,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 	} else if (os_strcmp(buf, "mbssid_max") == 0) {
 		conf->mbssid_max = atoi(pos);
 #endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AH
+	} else if (os_strcmp(buf, "ieee80211ah") == 0) {
+		conf->ieee80211ah = atoi(pos);
+	} else if (os_strcmp(buf, "s1g_oper_centr_freq_idx") == 0) {
+		conf->s1g_oper_centr_freq_idx = atoi(pos);
+	} else if (os_strcmp(buf, "s1g_primary_2mhz") == 0) {
+		conf->s1g_primary_2mhz = atoi(pos);
+#endif /* CONFIG_IEEE80211AH */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
 	} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 856c3db5c18a..9f0a4d2452bf 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -7,6 +7,7 @@ CFLAGS += -DCONFIG_IEEE80211AC
 CFLAGS += -DCONFIG_IEEE80211AX
 CFLAGS += -DCONFIG_IEEE80211BE
 CFLAGS += -DCONFIG_IEEE80211BN
+CFLAGS += -DCONFIG_IEEE80211AH
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R_AP
 CFLAGS += -DCONFIG_WPS
@@ -43,6 +44,7 @@ LIB_OBJS= \
 	ieee802_11_shared.o \
 	ieee802_11_uhr.o \
 	ieee802_11_vht.o \
+	ieee802_11_s1g.o \
 	ieee802_1x.o \
 	interference.o \
 	neighbor_db.o \
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 95a982f01c3e..65b8f09a0512 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1230,6 +1230,26 @@ static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
 }
 #endif /* CONFIG_SAE_PK */
 
+#ifdef CONFIG_IEEE80211AH
+static bool hostapd_config_check_bss_s1g(struct hostapd_bss_config *bss)
+{
+	if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) {
+		wpa_printf(MSG_ERROR,
+			   "Management frame protection is required in S1GHz (ieee80211w=2)");
+		return false;
+	}
+
+#ifdef CONFIG_SAE
+	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+	    bss->sae_pwe != SAE_PWE_HASH_TO_ELEMENT) {
+		wpa_printf(MSG_INFO, "SAE: Forcing SAE H2E on S1GHz");
+		bss->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	}
+#endif /* CONFIG_SAE */
+
+	return true;
+}
+#endif /* CONFIG_IEEE80211AH */
 
 bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
 {
@@ -1307,6 +1327,12 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
 	    !hostapd_config_check_bss_6g(bss))
 		return -1;
 
+#ifdef CONFIG_IEEE80211AH
+	if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+	    !hostapd_config_check_bss_s1g(bss))
+		return -1;
+#endif /* CONFIG_IEEE80211AH */
+
 	if (full_config && bss->ieee802_1x && !bss->eap_server &&
 	    !bss->radius->auth_servers) {
 		wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
@@ -1700,6 +1726,34 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config)
 		return -1;
 	}
 
+#ifdef CONFIG_IEEE80211AH
+	if (full_config && conf->ieee80211ah &&
+	    conf->hw_mode != HOSTAPD_MODE_IEEE80211AH) {
+		wpa_printf(MSG_ERROR, "ieee80211ah=1 requires hw_mode=ah");
+		return -1;
+	}
+
+	if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+	    !conf->ieee80211ah) {
+		wpa_printf(MSG_ERROR, "hw_mode=ah requires ieee80211ah=1");
+		return -1;
+	}
+
+	if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+	    !global_op_class_to_s1g_op_class(conf->op_class)) {
+		wpa_printf(MSG_ERROR,
+			   "Unsupported op_class %u for S1G operation",
+			   conf->op_class);
+		return -1;
+	}
+
+	if (full_config && conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+	    conf->acs) {
+		wpa_printf(MSG_ERROR, "ACS is not supported for S1G");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211AH */
+
 	for (i = 0; i < conf->num_bss; i++) {
 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
 			return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index e3dcbfd82427..b79a281567af 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1101,6 +1101,7 @@ struct hostapd_config {
 	u8 channel;
 	int enable_edmg;
 	u8 edmg_channel;
+	bool s1g_primary_2mhz;
 	u8 acs;
 	struct wpa_freq_range_list acs_ch_list;
 	struct wpa_freq_range_list acs_freq_list;
@@ -1231,6 +1232,12 @@ struct hostapd_config {
 	bool require_he;
 #endif /* CONFIG_IEEE80211AX */
 
+	int ieee80211ah;
+#ifdef CONFIG_IEEE80211AH
+	int s1g_oper_centr_freq_idx;
+	enum oper_chan_width s1g_oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
+
 	/* VHT enable/disable config from CHAN_SWITCH */
 #define CH_SWITCH_VHT_ENABLED BIT(0)
 #define CH_SWITCH_VHT_DISABLED BIT(1)
@@ -1346,6 +1353,10 @@ struct hostapd_config {
 static inline enum oper_chan_width
 hostapd_get_oper_chwidth(struct hostapd_config *conf)
 {
+#ifdef CONFIG_IEEE80211AH
+	if (conf->ieee80211ah)
+		return conf->s1g_oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		return conf->eht_oper_chwidth;
@@ -1361,6 +1372,10 @@ static inline void
 hostapd_set_oper_chwidth(struct hostapd_config *conf,
 			 enum oper_chan_width oper_chwidth)
 {
+#ifdef CONFIG_IEEE80211AH
+	if (conf->ieee80211ah)
+		conf->s1g_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AH */
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		conf->eht_oper_chwidth = oper_chwidth;
@@ -1377,6 +1392,10 @@ hostapd_set_oper_chwidth(struct hostapd_config *conf,
 static inline u8
 hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
 {
+#ifdef CONFIG_IEEE80211AH
+	if (conf->ieee80211ah)
+		return conf->s1g_oper_centr_freq_idx;
+#endif /* CONFIG_IEEE80211AH */
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		return conf->eht_oper_centr_freq_seg0_idx;
@@ -1392,6 +1411,10 @@ static inline void
 hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
 				     u8 oper_centr_freq_seg0_idx)
 {
+#ifdef CONFIG_IEEE80211AH
+	if (conf->ieee80211ah)
+		conf->s1g_oper_centr_freq_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AH */
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 90d2909cbe90..2f9a31e6513b 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -506,6 +506,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
 		    const struct ieee80211_uhr_capabilities *uhr_capab,
 		    size_t uhr_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+		    const struct ieee80211_s1g_capabilities *s1g_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set, const u8 *link_addr, bool mld_link_sta,
 		    u16 eml_cap, bool epp_sta)
@@ -533,6 +534,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
 	params.uhr_capab = uhr_capab;
 	params.uhr_capab_len = uhr_capab_len;
 	params.he_6ghz_capab = he_6ghz_capab;
+	params.s1g_capab = s1g_capab;
 	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
@@ -680,6 +682,7 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     int edmg, u8 edmg_channel,
 		     int ht_enabled, int vht_enabled,
 		     int he_enabled, bool eht_enabled,
+		     int s1g_enabled,
 		     int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1)
 {
@@ -704,6 +707,9 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		.he.cap = cmode ? &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
 		.eht.cap = cmode ? &cmode->eht_capab[IEEE80211_MODE_AP] : NULL,
 		.eht.punct_bitmap = hostapd_get_punct_bitmap(hapd),
+		.s1g.enabled = s1g_enabled,
+		.s1g.op_class = hapd->iconf->op_class,
+		.s1g.primary_2mhz = hapd->iconf->s1g_primary_2mhz,
 	};
 
 	if (hostapd_set_freq_params(&data, &info))
@@ -1302,6 +1308,8 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
 
 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
 		mode = &hapd->iface->hw_features[i];
+		if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+			continue;
 		if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
 		    selected_mode != mode->mode)
 			continue;
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 972514d38f72..593ecae7260d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -50,6 +50,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
 		    const struct ieee80211_uhr_capabilities *uhr_capab,
 		    size_t uhr_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+		    const struct ieee80211_s1g_capabilities *s1g_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set, const u8 *link_addr, bool mld_link_sta,
 		    u16 eml_cap, bool epp_sta);
@@ -76,7 +77,8 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     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,
+		     bool eht_enabled, int s1g_enabled,
+		     int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3a886e00544b..2df4cd35809f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -837,6 +837,12 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah)
+		buflen += 2 + sizeof(struct ieee80211_s1g_capabilities) +
+			2 + sizeof(struct ieee80211_s1g_operation);
+#endif /* CONFIG_IEEE80211AH */
+
 	buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP,
 					 NULL,
 					 params->known_bss,
@@ -956,6 +962,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah) {
+		pos = hostapd_eid_s1g_capab(hapd, pos);
+		pos = hostapd_eid_s1g_oper(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AH */
+
 #ifdef CONFIG_IEEE80211AX
 	if (hostapd_is_he_enabled(hapd) &&
 	    is_6ghz_op_class(hapd->iconf->op_class))
@@ -1554,7 +1567,13 @@ void handle_probe_req(struct hostapd_data *hapd,
 		return;
 	}
 
-	if ((!elems.ssid || !elems.supp_rates)) {
+	/*
+	 * S1G does not support legacy rates. The equivalent information is
+	 * advertised in the S1G Capabilities element.
+	 */
+	if (!elems.ssid ||
+	    (!elems.supp_rates &&
+	     !(hapd->iconf->ieee80211ah && elems.s1g_capabilities))) {
 		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
 			   "without SSID or supported rates element",
 			   MAC2STR(mgmt->sa));
@@ -2349,6 +2368,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah) {
+		/* +2 for element type + length respectively */
+		tail_len += 2 + sizeof(struct ieee80211_s1g_operation) +
+			    2 + sizeof(struct ieee80211_s1g_capabilities);
+	}
+#endif /* CONFIG_IEEE80211AH */
+
 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
 	    hapd == hostapd_mbssid_get_tx_bss(hapd))
 		tail_len += 5; /* Multiple BSSID Configuration element */
@@ -2375,20 +2402,54 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	}
 	tailend = tail + tail_len;
 
-	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_BEACON);
-	head->duration = host_to_le16(0);
-	os_memset(head->da, 0xff, ETH_ALEN);
+	if (hapd->iconf->ieee80211ah) {
+		/* Same buffer as @head, just a different frame format */
+		struct ieee80211_ext *ext_head = (struct ieee80211_ext *) head;
+
+		ext_head->frame_control =
+			IEEE80211_FC(WLAN_FC_TYPE_EXT,
+				     WLAN_FC_STYPE_S1G_BEACON) |
+			host_to_le16(WLAN_S1G_FC_BSS_BW(
+					     WLAN_S1G_BSS_BW_MIN_1_MAX_16));
+		if (hostapd_own_capab_info(hapd) & WLAN_CAPABILITY_PRIVACY)
+			ext_head->frame_control |=
+				host_to_le16(WLAN_FC_S1G_BEACON_SECURITY);
+		ext_head->duration = host_to_le16(0);
+		os_memcpy(ext_head->sa, hapd->own_addr, ETH_ALEN);
+		pos = &ext_head->u.beacon.variable[0];
+	} else {
+		head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						   WLAN_FC_STYPE_BEACON);
+		head->duration = host_to_le16(0);
+		os_memset(head->da, 0xff, ETH_ALEN);
 
-	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
-	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
-	head->u.beacon.beacon_int =
-		host_to_le16(hapd->iconf->beacon_int);
+		os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+		head->u.beacon.beacon_int =
+			host_to_le16(hapd->iconf->beacon_int);
 
-	/* hardware or low-level driver will setup seq_ctrl and timestamp */
-	capab_info = hostapd_own_capab_info(hapd);
-	head->u.beacon.capab_info = host_to_le16(capab_info);
-	pos = &head->u.beacon.variable[0];
+		/* hardware or low-level driver will setup seq_ctrl and
+		 * timestamp */
+		capab_info = hostapd_own_capab_info(hapd);
+		head->u.beacon.capab_info = host_to_le16(capab_info);
+
+		pos = &head->u.beacon.variable[0];
+	}
+
+	/*
+	 * As per IEEE80211-2024 11.1.3.10.1 the first element in a beacon
+	 * transmitted at a TBTT that is not a TSBTT (i.e a long beacon) shall
+	 * contain the Beacon Compatibility element as the first element in a
+	 * beacon. Therefore insert this at the head before the SSID element.
+	 * Other elements can be inserted in the tail as normal.
+	 */
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah) {
+		pos = hostapd_eid_s1g_beacon_compat(hapd, pos);
+		tailpos = hostapd_eid_s1g_capab(hapd, tailpos);
+		tailpos = hostapd_eid_s1g_oper(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AH */
 
 	/* SSID */
 	*pos++ = WLAN_EID_SSID;
@@ -2910,6 +2971,9 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
 		.he.cap = cmode ? &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
 		.eht.cap = cmode ? &cmode->eht_capab[IEEE80211_MODE_AP] : NULL,
 		.eht.punct_bitmap = hostapd_get_punct_bitmap(hapd),
+		.s1g.enabled = iconf->ieee80211ah,
+		.s1g.op_class = iconf->op_class,
+		.s1g.primary_2mhz = hapd->iconf->s1g_primary_2mhz,
 #ifdef CONFIG_IEEE80211BN
 		.uhr.enabled = iconf->ieee80211bn,
 		.uhr.dbe_bandwidth = iconf->dbe_bandwidth,
@@ -2930,6 +2994,8 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
 		mode = &hapd->iface->hw_features[i];
 
+		if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+			continue;
 		if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
 		    iconf->hw_mode != mode->mode)
 			continue;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 57ec4f6ec12d..740282ce8bd0 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -224,6 +224,8 @@ static const char * hw_mode_str(enum hostapd_hw_mode mode)
 		return "a";
 	case HOSTAPD_MODE_IEEE80211AD:
 		return "ad";
+	case HOSTAPD_MODE_IEEE80211AH:
+		return "ah";
 	case HOSTAPD_MODE_IEEE80211ANY:
 		return "any";
 	case NUM_HOSTAPD_MODES:
@@ -900,6 +902,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			  "ieee80211ax=%d\n"
 			  "ieee80211be=%d\n"
 			  "ieee80211bn=%d\n"
+			  "ieee80211ah=%d\n"
 			  "beacon_int=%u\n"
 			  "dtim_period=%d\n",
 			  iface->conf->channel,
@@ -912,6 +915,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			  hostapd_is_he_enabled(hapd),
 			  hostapd_is_eht_enabled(hapd),
 			  hostapd_is_uhr_enabled(hapd),
+			  iface->conf->ieee80211ah,
 			  iface->conf->beacon_int,
 			  hapd->conf->dtim_period);
 	if (os_snprintf_error(buflen - len, ret))
@@ -1189,7 +1193,9 @@ static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface,
 	enum hostapd_hw_mode target_mode;
 	bool is_6ghz = is_6ghz_freq(freq);
 
-	if (freq < 4000)
+	if (freq > 0 && freq < 1000)
+		target_mode = HOSTAPD_MODE_IEEE80211AH;
+	else if (freq < 4000)
 		target_mode = HOSTAPD_MODE_IEEE80211G;
 	else if (freq > 50000)
 		target_mode = HOSTAPD_MODE_IEEE80211AD;
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index caba71537b5b..909af3c281f9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -839,7 +839,7 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
 	int skip_radar = 0;
 
-	if (is_6ghz_freq(iface->freq))
+	if (is_6ghz_freq(iface->freq) || is_s1ghz_freq(iface->freq))
 		return 1;
 
 	if (!iface->current_mode) {
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0503322d5d88..b493c40bd7bf 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -134,7 +134,8 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
 
 	if (hapd->conf->wmm_enabled < 0)
 		hapd->conf->wmm_enabled = hapd->iconf->ieee80211n |
-			hapd->iconf->ieee80211ax;
+					  hapd->iconf->ieee80211ax |
+					  hapd->iconf->ieee80211ah;
 
 #ifndef CONFIG_NO_RADIUS
 	radius_client_reconfig(hapd->radius, hapd->conf->radius);
@@ -1586,7 +1587,8 @@ setup_mld:
 
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n |
-			hapd->iconf->ieee80211ax;
+				    hapd->iconf->ieee80211ax |
+				    hapd->iconf->ieee80211ah;
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (is_zero_ether_addr(conf->r1_key_holder))
@@ -2714,6 +2716,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 				     hapd->iconf->ieee80211ac,
 				     hapd->iconf->ieee80211ax,
 				     hapd->iconf->ieee80211be,
+				     hapd->iconf->ieee80211ah,
 				     hapd->iconf->secondary_channel,
 				     hostapd_get_oper_chwidth(hapd->iconf),
 				     hostapd_get_oper_centr_freq_seg0_idx(
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index d7715206ce9e..d64fc545783b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -263,6 +263,12 @@ int hostapd_prepare_rates(struct hostapd_data *hapd,
 	int basic_rates_g[] = { 10, 20, 55, 110, 0 };
 	const int *basic_rates;
 
+#ifdef CONFIG_IEEE80211AH
+	/* S1G does not support basic rates */
+	if (mode->mode == HOSTAPD_MODE_IEEE80211AH)
+		return 0;
+#endif /* CONFIG_IEEE80211AH */
+
 	if (conf->basic_rates)
 		basic_rates = conf->basic_rates;
 	else switch (mode->mode) {
@@ -1207,6 +1213,11 @@ int hostapd_is_usable_chans(struct hostapd_iface *iface)
 		return err;
 	}
 
+#ifdef CONFIG_IEEE80211AH
+	if (iface->conf->ieee80211ah)
+		return 1;
+#endif /* CONFIG_IEEE80211AH */
+
 	if (!iface->conf->secondary_channel)
 		return 1;
 
@@ -1269,7 +1280,9 @@ int hostapd_determine_mode(struct hostapd_iface *iface)
 	    iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
 		return 0;
 
-	if (iface->freq < 4000)
+	if (iface->freq > 0 && iface->freq < 1000)
+		target_mode = HOSTAPD_MODE_IEEE80211AH;
+	else if (iface->freq < 4000)
 		target_mode = HOSTAPD_MODE_IEEE80211G;
 	else if (iface->freq > 50000)
 		target_mode = HOSTAPD_MODE_IEEE80211AD;
@@ -1445,6 +1458,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
 		iface->conf->ieee80211be = 0;
 	}
 
+	if (iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211AH &&
+	    (iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+	     iface->conf->ieee80211ax || iface->conf->ieee80211be ||
+	     iface->conf->ieee80211bn)) {
+		wpa_printf(MSG_INFO, "Disable HT/VHT/HE/EHT/UHR for S1G mode");
+		iface->conf->ieee80211n = 0;
+		iface->conf->ieee80211ac = 0;
+		iface->conf->ieee80211ax = 0;
+		iface->conf->ieee80211be = 0;
+		iface->conf->ieee80211bn = 0;
+	}
+
 	iface->current_mode = NULL;
 	for (i = 0; i < iface->num_hw_features; i++) {
 		struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -1507,6 +1532,8 @@ const char * hostapd_hw_mode_txt(int mode)
 		return "IEEE 802.11g";
 	case HOSTAPD_MODE_IEEE80211AD:
 		return "IEEE 802.11ad";
+	case HOSTAPD_MODE_IEEE80211AH:
+		return "IEEE 802.11ah";
 	default:
 		return "UNKNOWN";
 	}
@@ -1554,6 +1581,8 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
 {
 	int i;
 
+	if (mode->mode == HOSTAPD_MODE_IEEE80211AH && !iface->current_mode)
+		return 1;
 	if (iface->current_mode)
 		return mode != iface->current_mode;
 	if (mode->mode != HOSTAPD_MODE_IEEE80211B)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 219a94b0f9f7..ffa232f11d6b 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -522,6 +522,12 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
 	u8 buf[100];
 	size_t len;
 
+#ifdef CONFIG_IEEE80211AH
+	/* No supp rates for S1G */
+	if (hapd->iconf->ieee80211ah)
+		return eid;
+#endif /* CONFIG_IEEE80211AH */
+
 	len = hostapd_supp_rates(hapd, buf);
 	if (len == 0)
 		return eid;
@@ -5522,9 +5528,10 @@ static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
 static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
 			   struct ieee802_11_elems *elems)
 {
-	/* Supported rates not used in IEEE 802.11ad/DMG */
-	if (hapd->iface->current_mode &&
-	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+	/* Supported rates not used in IEEE 802.11ad/DMG/ah */
+	if ((hapd->iface->current_mode &&
+	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) ||
+	    hapd->iconf->ieee80211ah)
 		return WLAN_STATUS_SUCCESS;
 
 	if (!elems->supp_rates) {
@@ -6113,6 +6120,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 	}
 #endif /* CONFIG_IEEE80211BN */
 
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah) {
+		resp = copy_sta_s1g_capab(hapd, sta, IEEE80211_MODE_AP,
+					  elems->s1g_capabilities);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211AH */
+
 #ifdef CONFIG_P2P
 	if (elems->p2p && ies && ies_len) {
 		wpabuf_free(sta->p2p_ie);
@@ -7274,7 +7290,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
 			    sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
 			    sta->flags & WLAN_STA_UHR ? &uhr_cap : NULL,
 			    sta->flags & WLAN_STA_UHR ? sta->uhr_capab_len : 0,
-			    sta->he_6ghz_capab,
+			    sta->he_6ghz_capab, sta->s1g_capab,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
 			    set, mld_link_addr, mld_link_sta, eml_cap,
@@ -7309,6 +7325,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 	struct ieee80211_mgmt *reply;
 	u8 *p;
 	u16 res = WLAN_STATUS_SUCCESS;
+	u16 aid;
 
 	buflen = sizeof(struct ieee80211_mgmt) + 1024;
 #ifdef CONFIG_FILS
@@ -7365,15 +7382,23 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 	os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
 
 	send_len = IEEE80211_HDRLEN;
-	send_len += sizeof(reply->u.assoc_resp);
 	reply->u.assoc_resp.capab_info =
 		host_to_le16(hostapd_own_capab_info(hapd));
 	reply->u.assoc_resp.status_code = host_to_le16(status_code);
 
-	reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
-					       BIT(14) | BIT(15));
+	aid = sta ? sta->aid : 0;
+	if (hapd->iconf->ieee80211ah) {
+		/* S1G AID is carried in the AID Response element */
+		send_len += sizeof(reply->u.s1g_assoc_resp);
+		p = reply->u.s1g_assoc_resp.variable;
+	} else {
+		send_len += sizeof(reply->u.assoc_resp);
+		reply->u.assoc_resp.aid = host_to_le16(aid | BIT(14) | BIT(15));
+		p = reply->u.assoc_resp.variable;
+	}
+
 	/* Supported rates */
-	p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+	p = hostapd_eid_supp_rates(hapd, p);
 	/* Extended supported rates */
 	p = hostapd_eid_ext_supp_rates(hapd, p);
 
@@ -7464,6 +7489,14 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+#ifdef CONFIG_IEEE80211AH
+	if (hapd->iconf->ieee80211ah) {
+		p = hostapd_eid_s1g_capab(hapd, p);
+		p = hostapd_eid_s1g_oper(hapd, p);
+		p = hostapd_eid_aid_response(hapd, p, aid);
+	}
+#endif /* CONFIG_IEEE80211AH */
+
 	p = hostapd_eid_ext_capab(hapd, p, false);
 	p = hostapd_eid_bss_max_idle_period(hapd, p,
 					    sta ? sta->max_idle_period : 0);
@@ -7714,7 +7747,11 @@ rsnxe_done:
 		p += wpabuf_len(hapd->conf->assocresp_elements);
 	}
 
-	send_len += p - reply->u.assoc_resp.variable;
+	if (hapd->iconf->ieee80211ah) {
+		send_len += p - reply->u.s1g_assoc_resp.variable;
+	} else {
+		send_len += p - reply->u.assoc_resp.variable;
+	}
 
 #ifdef CONFIG_FILS
 	if (sta &&
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index da87f0b29959..01e101313f15 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -103,6 +103,10 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_oper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_s1g_beacon_compat(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_aid_response(struct hostapd_data *hapd, u8 *eid, u16 aid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -192,6 +196,8 @@ int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
 				     u16 value);
+u16 copy_sta_s1g_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode, const u8 *s1g_capab);
 
 int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
 #ifdef CONFIG_SAE
diff --git a/src/ap/ieee802_11_s1g.c b/src/ap/ieee802_11_s1g.c
new file mode 100644
index 000000000000..75b55ba22054
--- /dev/null
+++ b/src/ap/ieee802_11_s1g.c
@@ -0,0 +1,202 @@
+/*
+ * hostapd / IEEE 802.11ah S1G
+ * Copyright (c) 2022, Morse Micro
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+/* 1SS, MCS9. TODO set via config */
+#define S1G_BASIC_MCS_NSS 0xCCC4
+
+static u8 hostapd_s1g_calc_pri_1mhz_loc(struct hostapd_data *hapd)
+{
+	u8 low_chan;
+	u8 pri_1mhz_chan = hapd->iconf->channel;
+	u8 oper_ch = hapd->iconf->s1g_oper_centr_freq_idx ?
+		     hapd->iconf->s1g_oper_centr_freq_idx :
+		     hapd->iconf->channel;
+
+	switch (hapd->iconf->s1g_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_2MHZ:
+		low_chan = oper_ch - 1;
+		break;
+	case CONF_OPER_CHWIDTH_4MHZ:
+		low_chan = oper_ch - 3;
+		break;
+	case CONF_OPER_CHWIDTH_8MHZ:
+		low_chan = oper_ch - 7;
+		break;
+	case CONF_OPER_CHWIDTH_16MHZ:
+		low_chan = oper_ch - 15;
+		break;
+	default:
+		return 0;
+	}
+
+	return ((pri_1mhz_chan - low_chan) / 2) & 1;
+}
+
+
+u8 * hostapd_eid_s1g_oper(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_s1g_operation *cap;
+	u8 *pos = eid;
+	u8 ch_width = 0;
+	u8 bss_width;
+	u8 s1g_pri_loc = hostapd_s1g_calc_pri_1mhz_loc(hapd);
+
+	/* IEEE80211-2024 Table 10-39 */
+	switch (hapd->iconf->s1g_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_1MHZ:
+		bss_width = 0x0;
+		break;
+	case CONF_OPER_CHWIDTH_2MHZ:
+		bss_width = 0x1;
+		break;
+	case CONF_OPER_CHWIDTH_4MHZ:
+		bss_width = 0x3;
+		break;
+	case CONF_OPER_CHWIDTH_8MHZ:
+		bss_width = 0x7;
+		break;
+	case CONF_OPER_CHWIDTH_16MHZ:
+		bss_width = 0xF;
+		break;
+	default:
+		return eid;
+	}
+
+	*pos++ = WLAN_EID_S1G_OPERATION;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_s1g_operation *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+
+	if (!hapd->iconf->s1g_primary_2mhz)
+		ch_width |= S1G_OPER_IE_CHANWIDTH_PRIM_CH_MASK;
+
+	ch_width |= (bss_width << 1) & S1G_OPER_IE_CHANWIDTH_OPER_CH_MASK;
+
+	if (hapd->iconf->s1g_primary_2mhz && s1g_pri_loc)
+		ch_width |= S1G_OPER_IE_CHANWIDTH_PRIM_OFFSET;
+
+	cap->ch_width = ch_width;
+	cap->oper_class =
+		global_op_class_to_s1g_op_class(hapd->iconf->op_class);
+	cap->basic_mcs_nss = host_to_le16(S1G_BASIC_MCS_NSS);
+
+	if (hapd->iconf->s1g_primary_2mhz) {
+		if (s1g_pri_loc)
+			cap->primary_ch = hapd->iconf->channel - 1;
+		else
+			cap->primary_ch = hapd->iconf->channel + 1;
+	} else {
+		cap->primary_ch = hapd->iconf->channel;
+	}
+
+	cap->oper_ch = hapd->iconf->s1g_oper_centr_freq_idx ?
+		       hapd->iconf->s1g_oper_centr_freq_idx :
+		       cap->primary_ch;
+
+	pos += sizeof(*cap);
+	return pos;
+}
+
+
+u8 * hostapd_eid_s1g_capab(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_s1g_capabilities *cap;
+	u8 *pos = eid;
+
+	*pos++ = WLAN_EID_S1G_CAPABILITIES;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_s1g_capabilities *) pos;
+	os_memcpy(cap, &hapd->iface->current_mode->s1g_capab, sizeof(*cap));
+
+	pos += sizeof(*cap);
+	return pos;
+}
+
+
+u8 * hostapd_eid_s1g_beacon_compat(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u16 beacon_int;
+
+	beacon_int = hapd->iconf->beacon_int;
+
+	*pos++ = WLAN_EID_S1G_BCN_COMPAT;
+	*pos++ = 8;
+	WPA_PUT_LE16(pos, hostapd_own_capab_info(hapd));
+	pos += 2;
+
+	WPA_PUT_LE16(pos, beacon_int);
+	pos += 2;
+
+	/*
+	 * Last 4 octets will be filled by the driver/fw to contain the 4
+	 * MSB of the TSF timer at time of generation.
+	 */
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	return pos;
+}
+
+
+u8 * hostapd_eid_aid_response(struct hostapd_data *hapd, u8 *eid, u16 aid)
+{
+	u8 *pos = eid;
+
+	*pos++ = WLAN_EID_AID_RESPONSE;
+	*pos++ = 5;
+
+	/* AID/MID */
+	WPA_PUT_LE16(pos, aid);
+	pos += 2;
+	/* AID Switch Count */
+	*pos++ = 0;
+	/* AID Response Interval */
+	WPA_PUT_LE16(pos, 0);
+	pos += 2;
+
+	return pos;
+}
+
+
+u16 copy_sta_s1g_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode, const u8 *s1g_capab)
+{
+	if (!s1g_capab) {
+		os_free(sta->s1g_capab);
+		sta->s1g_capab = NULL;
+		sta->flags &= ~WLAN_STA_S1G;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (!sta->s1g_capab) {
+		sta->s1g_capab =
+			os_zalloc(sizeof(struct ieee80211_s1g_capabilities));
+		if (!sta->s1g_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	os_memcpy(sta->s1g_capab, s1g_capab,
+		  sizeof(struct ieee80211_s1g_capabilities));
+	sta->flags |= WLAN_STA_S1G;
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/interference.c b/src/ap/interference.c
index 44aa6c0019ef..12ab133c32d6 100644
--- a/src/ap/interference.c
+++ b/src/ap/interference.c
@@ -558,6 +558,7 @@ int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
 			    iface->conf->ieee80211ac,
 			    iface->conf->ieee80211ax,
 			    iface->conf->ieee80211be,
+			    iface->conf->ieee80211ah,
 			    iface->conf->secondary_channel,
 			    hostapd_get_oper_chwidth(iface->conf),
 			    hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 4326f1344277..cf34c7644dfe 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -515,6 +515,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	os_free(sta->he_6ghz_capab);
 	os_free(sta->eht_capab);
 	os_free(sta->uhr_capab);
+	os_free(sta->s1g_capab);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
@@ -2155,7 +2156,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
 	if (hostapd_sta_add(hapd, sta->addr, 0, 0,
 			    sta->supported_rates,
 			    sta->supported_rates_len,
-			    0, NULL, NULL, NULL, 0, NULL, 0, NULL, 0, NULL,
+			    0, NULL, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, NULL,
 			    sta->flags, 0, 0, 0, 0,
 			    mld_link_addr, mld_link_sta, eml_cap, epp_sta)) {
 		hostapd_logger(hapd, sta->addr,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6dd3ce4ae929..39ac0b43d848 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -50,6 +50,7 @@
 #define WLAN_STA_PENDING_DEAUTH_CB BIT_ULL(30)
 #define WLAN_STA_NONERP BIT_ULL(31)
 #define WLAN_STA_UHR BIT_ULL(32)
+#define WLAN_STA_S1G BIT_ULL(33)
 
 /* Maximum number of supported rates (from both Supported Rates and Extended
  * Supported Rates IEs). */
@@ -228,6 +229,7 @@ struct sta_info {
 	size_t eht_capab_len;
 	struct ieee80211_uhr_capabilities *uhr_capab;
 	size_t uhr_capab_len;
+	struct ieee80211_s1g_capabilities *s1g_capab;
 
 	int sa_query_count; /* number of pending SA Query requests;
 			     * 0 = no SA Query in progress */
diff --git a/src/common/defs.h b/src/common/defs.h
index d6bd21b38baf..86e6893ed9b7 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -423,6 +423,7 @@ enum hostapd_hw_mode {
 	HOSTAPD_MODE_IEEE80211A,
 	HOSTAPD_MODE_IEEE80211AD,
 	HOSTAPD_MODE_IEEE80211ANY,
+	HOSTAPD_MODE_IEEE80211AH,
 	NUM_HOSTAPD_MODES
 };
 
@@ -514,6 +515,11 @@ enum oper_chan_width {
 	CONF_OPER_CHWIDTH_8640MHZ,
 	CONF_OPER_CHWIDTH_40MHZ_6GHZ,
 	CONF_OPER_CHWIDTH_320MHZ,
+	CONF_OPER_CHWIDTH_1MHZ,
+	CONF_OPER_CHWIDTH_2MHZ,
+	CONF_OPER_CHWIDTH_4MHZ,
+	CONF_OPER_CHWIDTH_8MHZ,
+	CONF_OPER_CHWIDTH_16MHZ,
 };
 
 enum key_flag {
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 0d4f47cf796d..24607fa4a71d 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -575,6 +575,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
 	const struct eht_capabilities *eht_cap = info->eht.cap;
 	u16 punct_bitmap = info->eht.punct_bitmap;
 	bool uhr_enabled = info->uhr.enabled;
+	int s1g_enabled = info->s1g.enabled;
+	bool s1g_primary_2mhz = info->s1g.primary_2mhz;
 	enum oper_chan_width oper_chwidth_legacy;
 	u8 seg0_legacy, seg1_legacy;
 
@@ -594,10 +596,12 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
 	data->vht_enabled = vht_enabled;
 	data->he_enabled = he_enabled;
 	data->eht_enabled = eht_enabled;
+	data->s1g_enabled = s1g_enabled;
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
 	data->punct_bitmap = punct_bitmap;
+	data->s1g_primary_2mhz = s1g_primary_2mhz;
 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 		data->bandwidth = 80;
 	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
@@ -614,7 +618,41 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
 	hostapd_encode_edmg_chan(enable_edmg, edmg_channel, channel,
 				 &data->edmg);
 
-	if (is_6ghz_freq(freq)) {
+	if (is_s1ghz_freq(freq)) {
+		if (oper_chwidth == CONF_OPER_CHWIDTH_16MHZ)
+			data->bandwidth = 16;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_8MHZ)
+			data->bandwidth = 8;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_4MHZ)
+			data->bandwidth = 4;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_2MHZ)
+			data->bandwidth = 2;
+		else
+			data->bandwidth = 1;
+
+		if (!center_segment0) {
+			data->center_freq1 = data->freq;
+			data->center_freq1_offset = data->freq_offset;
+		} else {
+			int freq1 = ieee80211_chan_to_freq_khz(
+				NULL, info->s1g.op_class, center_segment0);
+			if (freq1 < 0) {
+				wpa_printf(MSG_ERROR,
+					   "Invalid segment 0 center frequency for S1G");
+				return -1;
+			}
+
+			data->center_freq1 = KHZ_TO_MHZ(freq1);
+			data->center_freq1_offset = KHZ_TO_S1G_OFFSET(freq1);
+		}
+
+		data->ht_enabled = 0;
+		data->vht_enabled = 0;
+		data->he_enabled = 0;
+		data->eht_enabled = 0;
+		data->center_freq2 = 0;
+		return 0;
+	} else if (is_6ghz_freq(freq)) {
 		if (!data->he_enabled && !data->eht_enabled) {
 			wpa_printf(MSG_ERROR,
 				   "Can't set 6 GHz mode - HE or EHT aren't enabled");
@@ -1060,6 +1098,12 @@ int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
 	u32 bw_mask;
 
 	switch (bw) {
+	case 4:
+		return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_4;
+	case 8:
+		return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_8;
+	case 16:
+		return chan->allowed_bw & HOSTAPD_CHAN_WIDTH_16;
 	case 20:
 		bw_mask = HOSTAPD_CHAN_WIDTH_20;
 		break;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 47333a7bd7de..088b9ebb4a9d 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -87,6 +87,13 @@ struct hostapd_channel_info {
 		u8 dbe_bandwidth;
 		u16 dbe_punct_bitmap;
 	} uhr;
+
+	struct {
+		int enabled;
+		/* Global operating class */
+		u8 op_class;
+		bool primary_2mhz;
+	} s1g;
 };
 
 int hostapd_set_freq_params(struct hostapd_freq_params *data,
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index eaf6310911a9..fc2fc09f505f 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -761,7 +761,7 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
 		case WLAN_EID_S1G_CAPABILITIES:
 			if (elen < 15)
 				break;
-			elems->s1g_capab = pos;
+			elems->s1g_capabilities = pos;
 			break;
 		case WLAN_EID_FRAGMENT:
 			wpa_printf(MSG_MSGDUMP,
@@ -773,6 +773,16 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
 						       len, show_errors))
 				unknown++;
 			break;
+		case WLAN_EID_S1G_OPERATION:
+			if (elen < sizeof(struct ieee80211_s1g_operation))
+				break;
+			elems->s1g_operation = pos;
+			break;
+		case WLAN_EID_AID_RESPONSE:
+			if (elen < sizeof(struct ieee80211_aid_response))
+				break;
+			elems->aid_response = pos;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -976,7 +986,10 @@ void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems,
 			elems->dils_len = 0;
 			break;
 		case WLAN_EID_S1G_CAPABILITIES:
-			elems->s1g_capab = NULL;
+			elems->s1g_capabilities = NULL;
+			break;
+		case WLAN_EID_S1G_OPERATION:
+			elems->s1g_operation = NULL;
 			break;
 		}
 	}
@@ -1586,6 +1599,36 @@ ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
 
 	/* TODO: more operating classes */
 
+	if (is_s1ghz_freq(freq)) {
+		if (freq < 902 || freq > 927)
+			return NUM_HOSTAPD_MODES;
+
+		*channel = (MHZ_TO_KHZ(freq) + 500 - MHZ_TO_KHZ(902)) / 500;
+
+		switch (chanwidth) {
+		case CONF_OPER_CHWIDTH_1MHZ:
+			*op_class = 68;
+			break;
+		case CONF_OPER_CHWIDTH_2MHZ:
+			*op_class = 69;
+			break;
+		case CONF_OPER_CHWIDTH_4MHZ:
+			*op_class = 70;
+			break;
+		case CONF_OPER_CHWIDTH_8MHZ:
+			*op_class = 71;
+			break;
+		case CONF_OPER_CHWIDTH_16MHZ:
+			*op_class = 72;
+			break;
+		default:
+			*op_class = 0;
+			break;
+		}
+
+		return HOSTAPD_MODE_IEEE80211AH;
+	}
+
 	if (sec_channel > 1 || sec_channel < -1)
 		return NUM_HOSTAPD_MODES;
 
@@ -2093,6 +2136,14 @@ 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 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)
@@ -3224,6 +3275,12 @@ bool is_same_band(int freq1, int freq2)
 }
 
 
+bool is_s1ghz_freq(int freq)
+{
+	return freq >= 750 && freq < 1000;
+}
+
+
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len)
 {
@@ -3512,6 +3569,16 @@ int op_class_to_bandwidth(u8 op_class)
 enum oper_chan_width op_class_to_ch_width(u8 op_class)
 {
 	switch (op_class) {
+	case 68: /* S1G 2024 US 1MHz */
+		return CONF_OPER_CHWIDTH_1MHZ;
+	case 69: /* S1G 2024 US 2MHz */
+		return CONF_OPER_CHWIDTH_2MHZ;
+	case 70: /* S1G 2024 US 4MHz */
+		return CONF_OPER_CHWIDTH_4MHZ;
+	case 71: /* S1G 2024 US 8MHz */
+		return CONF_OPER_CHWIDTH_8MHZ;
+	case 72: /* S1G 2024 US 16MHz */
+		return CONF_OPER_CHWIDTH_16MHZ;
 	case 81:
 	case 82:
 		return CONF_OPER_CHWIDTH_USE_HT;
@@ -4407,3 +4474,31 @@ int ieee80211_get_center_freq(int ctrl_freq, u32 bw)
 		return -1;
 	}
 }
+
+
+/**
+ * global_op_class_to_s1g_op_class - Convert a global operating class to S1G
+ * @op_class: Global operating class (IEEE Std 802.11-2024, Table E-4)
+ * Returns: The matching S1G operating class (IEEE Std 802.11-2024, Table E-5)
+ * or 0 if the operating class is not a supported S1G operating class
+ *
+ * Only the S1G operating classes for the United States are currently
+ * supported.
+ */
+u8 global_op_class_to_s1g_op_class(u8 op_class)
+{
+	switch (op_class) {
+	case 68:
+		return 1;
+	case 69:
+		return 2;
+	case 70:
+		return 3;
+	case 71:
+		return 4;
+	case 72:
+		return 5;
+	default:
+		return 0;
+	}
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ab0031cebe47..bd07530c8c1a 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -146,7 +146,7 @@ struct ieee802_11_elems {
 	const u8 *short_ssid_list;
 	const u8 *he_6ghz_band_cap;
 	const u8 *sae_pk;
-	const u8 *s1g_capab;
+	const u8 *s1g_capabilities;
 	const u8 *pasn_params;
 	const u8 *eht_capabilities;
 	const u8 *eht_operation;
@@ -170,6 +170,8 @@ struct ieee802_11_elems {
 	const u8 *uhr_operation;
 	const u8 *security_profile;
 	const u8 *pqc_parameter;
+	const u8 *s1g_operation;
+	const u8 *aid_response;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -374,6 +376,8 @@ int get_6ghz_sec_channel(int channel);
 bool is_same_band(int freq1, int freq2);
 #define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
 #define IS_5GHZ(n) (n > 4000 && n < 5895)
+bool is_s1ghz_freq(int freq);
+u8 global_op_class_to_s1g_op_class(u8 op_class);
 
 u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx);
 int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 20edcae0b8d9..1b0cba393a24 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -89,6 +89,21 @@
 #define WLAN_FC_STYPE_DMG_BEACON	0
 #define WLAN_FC_STYPE_S1G_BEACON	1
 
+/* IEEE Std 802.11-2024, Table 9-8 (Frame Control field BSS BW setting) */
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_2	0
+#define WLAN_S1G_BSS_BW_EQUAL_TO_PPDU	1
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_4	2
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_4	3
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_8	4
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_8	5
+#define WLAN_S1G_BSS_BW_MIN_1_MAX_16	6
+#define WLAN_S1G_BSS_BW_MIN_2_MAX_16	7
+
+#define WLAN_S1G_FC_BSS_BW_SHIFT	11
+#define WLAN_S1G_FC_BSS_BW_MASK		(0x7 << WLAN_S1G_FC_BSS_BW_SHIFT)
+#define WLAN_S1G_FC_BSS_BW(bw)		(((bw) & 0x7) << \
+					 WLAN_S1G_FC_BSS_BW_SHIFT)
+
 /* Authentication algorithms */
 #define WLAN_AUTH_OPEN			0
 #define WLAN_AUTH_SHARED_KEY		1
@@ -490,7 +505,10 @@
 #define WLAN_EID_DEVICE_LOCATION 204
 #define WLAN_EID_WHITE_SPACE_MAP 205
 #define WLAN_EID_FTM_PARAMETERS 206
+#define WLAN_EID_AID_REQUEST 210
+#define WLAN_EID_AID_RESPONSE 211
 #define WLAN_EID_S1G_BCN_COMPAT 213
+#define WLAN_EID_S1G_SHORT_BCN_INTERVAL 214
 #define WLAN_EID_TWT 216
 #define WLAN_EID_S1G_CAPABILITIES 217
 #define WLAN_EID_VENDOR_SPECIFIC 221
@@ -1085,23 +1103,6 @@ struct ieee80211_hdr {
 
 #define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
 
-struct ieee80211_hdr_s1g_beacon {
-	le16 frame_control;
-	le16 duration_id;
-	u8 sa[6];
-	u8 timestamp[4];
-	u8 change_seq[1];
-	/* followed by:
-	 *   'u8 next_tbtt[3];' if the Next TBTT Present field in the
-	 *			Frame Control field is 1
-	 *   'u8 compressed_ssid[4];' if the Compressed SSID Present field in
-	 *			the Frame Control is 1
-	 *   'u8 ano[1];' if the ANO field in the Frame Control field is 1
-	 */
-} STRUCT_PACKED;
-
-#define IEEE80211_HDRLEN_S1G_BEACON (sizeof(struct ieee80211_hdr_s1g_beacon))
-
 #define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
 
 struct ieee80211_mgmt {
@@ -1136,6 +1137,11 @@ struct ieee80211_mgmt {
 			/* followed by Supported rates */
 			u8 variable[];
 		} STRUCT_PACKED assoc_resp, reassoc_resp;
+		struct {
+			le16 capab_info;
+			le16 status_code;
+			u8 variable[];
+		} STRUCT_PACKED s1g_assoc_resp, s1g_reassoc_resp;
 		struct {
 			le16 capab_info;
 			le16 listen_interval;
@@ -1307,6 +1313,29 @@ struct ieee80211_mgmt {
 	} u;
 } STRUCT_PACKED;
 
+struct ieee80211_ext {
+	le16 frame_control;
+	le16 duration;
+	u8 sa[6];
+	union {
+		struct {
+			u8 timestamp[4];
+			u8 change_seq;
+			/* followed by:
+			 *   'u8 next_tbtt[3];' if the Next TBTT Present field
+			 *			in the Frame Control field is 1
+			 *   'u8 compressed_ssid[4];' if the Compressed SSID
+			 *			Present field in the Frame
+			 *			Control is 1
+			 *   'u8 ano[1];' if the ANO field in the Frame Control
+			 *		  field is 1
+			 */
+			u8 variable[];
+		} STRUCT_PACKED beacon;
+	} u;
+} STRUCT_PACKED;
+
+#define IEEE80211_HDRLEN_S1G_BEACON (sizeof(struct ieee80211_ext))
 
 #define IEEE80211_MIN_ACTION_LEN(type)	\
 	(offsetof(struct ieee80211_mgmt, u.action.u.type) + \
@@ -2768,6 +2797,34 @@ struct ieee80211_he_mu_edca_parameter_set {
 /* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
 #define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
 
+struct ieee80211_s1g_capabilities {
+	u8 capab_info[10];
+	u8 supp_mcs_nss[5];
+} STRUCT_PACKED;
+
+struct ieee80211_s1g_operation {
+	u8 ch_width;
+	u8 oper_class;
+	u8 primary_ch;
+	u8 oper_ch;
+	le16 basic_mcs_nss;
+} STRUCT_PACKED;
+
+struct ieee80211_aid_response {
+	le16 aid;
+	u8 switch_count;
+	le16 response_interval;
+} STRUCT_PACKED;
+
+#define S1G_OPER_IE_CHANWIDTH_PRIM_CH_MASK     (BIT(0))
+#define S1G_OPER_IE_CHANWIDTH_OPER_CH_MASK     (BIT(1) | BIT(2) | \
+						BIT(3) | BIT(4))
+#define S1G_OPER_IE_CHANWIDTH_PRIM_OFFSET      (BIT(5))
+#define S1G_OPER_IE_MAX_MCS_1_NSS_SHIFT        (2)
+#define S1G_OPER_IE_MAX_MCS_2_NSS_SHIFT        (6)
+#define S1G_OPER_IE_MAX_MCS_3_NSS_SHIFT        (10)
+#define S1G_OPER_IE_MAX_MCS_4_NSS_SHIFT        (14)
+
 /*
  * IEEE Std 802.11-2020 and IEEE Std 802.11ax-2021
  * 9.4.2.170 Reduced Neighbor Report element
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f8c854227011..7131923de327 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -63,6 +63,9 @@ enum hostapd_chan_width_attr {
 	HOSTAPD_CHAN_WIDTH_80   = BIT(4),
 	HOSTAPD_CHAN_WIDTH_160  = BIT(5),
 	HOSTAPD_CHAN_WIDTH_320  = BIT(6),
+	HOSTAPD_CHAN_WIDTH_4    = BIT(9),
+	HOSTAPD_CHAN_WIDTH_8    = BIT(10),
+	HOSTAPD_CHAN_WIDTH_16   = BIT(11),
 };
 
 /* Filter gratuitous ARP */
@@ -344,6 +347,11 @@ struct hostapd_hw_modes {
 	 * uhr_capab - UHR (IEEE 802.11bn) capabilities
 	 */
 	struct uhr_capabilities uhr_capab[IEEE80211_MODE_NUM];
+
+	/**
+	 * s1g_capab - S1G (IEEE 802.11ah) capabilities
+	 */
+	struct ieee80211_s1g_capabilities s1g_capab;
 };
 
 
@@ -900,6 +908,11 @@ struct hostapd_freq_params {
 	 */
 	int he_enabled;
 
+	/**
+	 * s1g_enabled - Whether S1G is enabled
+	 */
+	int s1g_enabled;
+
 	/**
 	 * center_freq1 - Segment 0 center frequency in MHz
 	 *
@@ -922,7 +935,7 @@ struct hostapd_freq_params {
 	int center_freq2;
 
 	/**
-	 * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+	 * bandwidth - Channel bandwidth in MHz (1, 2, 4, 8, 16, 20, 40, 80, 160)
 	 */
 	int bandwidth;
 
@@ -954,6 +967,13 @@ struct hostapd_freq_params {
 	 * link_id: If >=0 indicates the link of the AP MLD to configure
 	 */
 	int link_id;
+
+	/**
+	 * s1g_primary_2mhz: Whether the S1G channel uses a 2 MHz primary
+	 *
+	 * S1G primary channels can either be 1MHz or 2MHz.
+	 */
+	bool s1g_primary_2mhz;
 };
 
 /**
@@ -2941,6 +2961,8 @@ struct hostapd_sta_add_params {
 	const struct ieee80211_uhr_capabilities *uhr_capab;
 	size_t uhr_capab_len;
 
+	const struct ieee80211_s1g_capabilities *s1g_capab;
+
 #ifdef CONFIG_NAN
 	/*
 	 * For a station added to a NAN Data Interface (NDI) indicate the
diff --git a/wlantest/bip.c b/wlantest/bip.c
index 8fbf43ed6fad..ddfbdca7aacb 100644
--- a/wlantest/bip.c
+++ b/wlantest/bip.c
@@ -77,7 +77,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
 	u8 *prot, *pos, *buf;
 	u8 mic[16];
 	u16 fc;
-	struct ieee80211_hdr_s1g_beacon *hdr;
+	const struct ieee80211_ext *hdr;
 	struct ieee80211_s1g_beacon_compat *bc;
 	size_t plen, mic_len, element_len, buf_len;
 	size_t hdr_add_len = 0, bce_add_len = 0;
@@ -121,7 +121,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
 
 	/* BIP AAD: FC SA ChangeSeq NextTBTT(if present)
 	 * CompressedSSID(if present) ANO(if present), BIPN(if using BCE) */
-	hdr = (struct ieee80211_hdr_s1g_beacon *) frame;
+	hdr = (const struct ieee80211_ext *) frame;
 	fc = le_to_host16(hdr->frame_control);
 	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_EXT ||
 	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_S1G_BEACON) {
@@ -133,7 +133,7 @@ u8 * bip_protect_s1g_beacon(const u8 *igtk, size_t igtk_len, const u8 *frame,
 
 	WPA_PUT_LE16(buf, fc);
 	os_memcpy(buf + 2, hdr->sa, ETH_ALEN);
-	os_memcpy(buf + 8, hdr->change_seq, 1);
+	buf[8] = hdr->u.beacon.change_seq;
 	if (fc & WLAN_FC_S1G_BEACON_NEXT_TBTT)
 		hdr_add_len += 3;
 	if (fc & WLAN_FC_S1G_BEACON_COMP_SSID)
@@ -257,7 +257,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
 {
 	u8 *prot, *pos, *buf;
 	u16 fc;
-	struct ieee80211_hdr_s1g_beacon *hdr;
+	const struct ieee80211_ext *hdr;
 	struct ieee80211_s1g_beacon_compat *bc;
 	size_t plen, mic_len, element_len, buf_len;
 	size_t hdr_add_len = 0, bce_add_len = 0;
@@ -301,7 +301,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
 
 	/* BIP AAD: FC SA ChangeSeq NextTBTT(if present)
 	 * CompressedSSID(if present) ANO(if present) */
-	hdr = (struct ieee80211_hdr_s1g_beacon *) frame;
+	hdr = (const struct ieee80211_ext *) frame;
 	fc = le_to_host16(hdr->frame_control);
 	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_EXT ||
 	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_S1G_BEACON) {
@@ -313,7 +313,7 @@ u8 * bip_gmac_protect_s1g_beacon(const u8 *igtk, size_t igtk_len,
 
 	WPA_PUT_LE16(buf, fc);
 	os_memcpy(buf + 2, hdr->sa, ETH_ALEN);
-	os_memcpy(buf + 8, hdr->change_seq, 1);
+	buf[8] = hdr->u.beacon.change_seq;
 	if (fc & WLAN_FC_S1G_BEACON_NEXT_TBTT)
 		hdr_add_len += 3;
 	if (fc & WLAN_FC_S1G_BEACON_COMP_SSID)
diff --git a/wlantest/bss.c b/wlantest/bss.c
index aed678ba6127..34a5f8f64a17 100644
--- a/wlantest/bss.c
+++ b/wlantest/bss.c
@@ -178,7 +178,7 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
 	 * Probe Response frames. Note this assumes short beacons were dropped
 	 * due to missing SSID above.
 	 */
-	if (!elems->rsn_ie && (!elems->s1g_capab || beacon != 1)) {
+	if (!elems->rsn_ie && (!elems->s1g_capabilities || beacon != 1)) {
 		if (bss->rsnie[0]) {
 			add_note(wt, MSG_INFO, "BSS " MACSTR
 				 " - RSN IE removed", MAC2STR(bss->bssid));
-- 
2.43.0




More information about the Hostap mailing list