[PATCH RFC 8/9] nl80211: add S1G support
Lachlan Hodges
lachlan.hodges at morsemicro.com
Thu Oct 8 20:32:05 PDT 2026
Introduce the ability to configure an S1G interface via nl80211.
Signed-off-by: Lachlan Hodges <lachlan.hodges at morsemicro.com>
---
src/drivers/driver_nl80211.c | 54 ++++++++++++++++++++++++++++--
src/drivers/driver_nl80211_capa.c | 38 ++++++++++++++++++---
src/drivers/driver_nl80211_event.c | 24 +++++++------
3 files changed, 99 insertions(+), 17 deletions(-)
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 86e0e2b55b8d..9690fcc5fb5b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5691,6 +5691,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
+ wpa_printf(MSG_DEBUG, " * s1g_enabled=%d", freq->s1g_enabled);
wpa_printf(MSG_DEBUG, " * radar_background=%d",
freq->radar_background);
@@ -5698,7 +5699,46 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
hw_mode == HOSTAPD_MODE_IEEE80211B;
- if (freq->vht_enabled ||
+ if (freq->s1g_enabled) {
+ enum nl80211_chan_width cw;
+
+ wpa_printf(MSG_DEBUG, " * channel_width=%d", freq->bandwidth);
+ wpa_printf(MSG_DEBUG, " * center_freq1=%d", freq->center_freq1);
+ wpa_printf(MSG_DEBUG, " * center_freq1_offset=%d",
+ freq->center_freq1_offset);
+ wpa_printf(MSG_DEBUG, " * s1g_primary_2mhz=%d", freq->s1g_primary_2mhz);
+
+ switch (freq->bandwidth) {
+ case 1:
+ cw = NL80211_CHAN_WIDTH_1;
+ break;
+ case 2:
+ cw = NL80211_CHAN_WIDTH_2;
+ break;
+ case 4:
+ cw = NL80211_CHAN_WIDTH_4;
+ break;
+ case 8:
+ cw = NL80211_CHAN_WIDTH_8;
+ break;
+ case 16:
+ cw = NL80211_CHAN_WIDTH_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1_OFFSET,
+ freq->center_freq1_offset))
+ return -ENOBUFS;
+ if (freq->s1g_primary_2mhz &&
+ nla_put_flag(msg, NL80211_ATTR_S1G_PRIMARY_2MHZ))
+ return -ENOBUFS;
+ } else if (freq->vht_enabled ||
((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
enum nl80211_chan_width cw;
@@ -6234,9 +6274,9 @@ static int nl80211_set_channel(struct i802_bss *bss,
int ret;
wpa_printf(MSG_DEBUG,
- "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)",
+ "nl80211: Set freq %d offset %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, s1g_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->he_enabled, freq->eht_enabled, freq->s1g_enabled, freq->bandwidth,
freq->center_freq1, freq->center_freq1_offset, freq->center_freq2);
msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
@@ -6430,6 +6470,14 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+ if (params->s1g_capab) {
+ wpa_hexdump(MSG_DEBUG, " * s1g_capab",
+ params->s1g_capab, sizeof(*params->s1g_capab));
+ if (nla_put(msg, NL80211_ATTR_S1G_CAPABILITY,
+ sizeof(*params->s1g_capab), params->s1g_capab))
+ goto fail;
+ }
+
if (params->ext_capab) {
wpa_hexdump(MSG_DEBUG, " * ext_capab",
params->ext_capab, params->ext_capab_len);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 7dc33e42544c..3297572ea9ff 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -2150,6 +2150,24 @@ static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
}
+static void phy_info_s1g_capa(struct hostapd_hw_modes *mode,
+ struct nlattr *capability,
+ struct nlattr *mcs_nss_set)
+{
+ if (capability && nla_len(capability) >= 10) {
+ u8 *capa;
+ capa = nla_data(capability);
+ os_memcpy(&mode->s1g_capab.capab_info, capa, 10);
+ }
+
+ if (mcs_nss_set && nla_len(mcs_nss_set) >= 5) {
+ u8 *mcs_nss;
+ mcs_nss = nla_data(mcs_nss_set);
+ os_memcpy(&mode->s1g_capab.supp_mcs_nss, mcs_nss, 5);
+ }
+}
+
+
static int phy_info_edmg_capa(struct hostapd_hw_modes *mode,
struct nlattr *bw_config,
struct nlattr *channels)
@@ -2228,6 +2246,13 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_320MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_320;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_4MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_4;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_8MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_8;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_16MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_16;
+
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
@@ -2632,6 +2657,8 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+ phy_info_s1g_capa(mode, tb_band[NL80211_BAND_ATTR_S1G_CAPA],
+ tb_band[NL80211_BAND_ATTR_S1G_MCS_NSS_SET]);
ret = phy_info_edmg_capa(mode,
tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG],
tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]);
@@ -2701,8 +2728,9 @@ wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
continue;
modes[m].is_6ghz = false;
-
- if (modes[m].channels[0].freq < 2000) {
+ if (is_s1ghz_freq(modes[m].channels[0].freq)) {
+ modes[m].mode = HOSTAPD_MODE_IEEE80211AH;
+ } else if (modes[m].channels[0].freq < 2000) {
modes[m].num_channels = 0;
continue;
} else if (modes[m].channels[0].freq < 4000) {
@@ -3172,6 +3200,8 @@ static const char * modestr(enum hostapd_hw_mode mode)
return "802.11a";
case HOSTAPD_MODE_IEEE80211AD:
return "802.11ad";
+ case HOSTAPD_MODE_IEEE80211AH:
+ return "802.11ah";
default:
return "?";
}
@@ -3199,10 +3229,10 @@ static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
if (is_6ghz_freq(chan->freq))
drv->uses_6ghz = true;
- if (chan->freq >= 900 && chan->freq < 1000)
+ if (is_s1ghz_freq(chan->freq))
drv->uses_s1g = true;
- if (drv->uses_s1g) {
+ if (is_s1ghz_freq(chan->freq)) {
/* For S1G, print frequency in KHz */
res = os_snprintf(pos, end - pos, " %d%s%s",
MHZ_TO_KHZ(chan->freq) + chan->freq_offset,
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index cd08b7193e7e..3ffaa70d4bdc 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -280,6 +280,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
union wpa_event_data event;
u16 status;
int ssid_len;
+ size_t fixed_len;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
drv->force_connect_cmd) {
@@ -294,7 +295,13 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: Associate event");
mgmt = (const struct ieee80211_mgmt *) frame;
- if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+
+ if (is_s1ghz_freq(drv->assoc_freq))
+ fixed_len = 24 + sizeof(mgmt->u.s1g_assoc_resp);
+ else
+ fixed_len = 24 + sizeof(mgmt->u.assoc_resp);
+
+ if (len < fixed_len) {
wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
"frame");
return;
@@ -308,11 +315,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
if (status != WLAN_STATUS_SUCCESS) {
os_memset(&event, 0, sizeof(event));
event.assoc_reject.bssid = mgmt->bssid;
- if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
- event.assoc_reject.resp_ies =
- (u8 *) mgmt->u.assoc_resp.variable;
- event.assoc_reject.resp_ies_len =
- len - 24 - sizeof(mgmt->u.assoc_resp);
+ if (len > fixed_len) {
+ event.assoc_reject.resp_ies = frame + fixed_len;
+ event.assoc_reject.resp_ies_len = len - fixed_len;
}
event.assoc_reject.status_code = status;
@@ -327,10 +332,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
os_memset(&event, 0, sizeof(event));
event.assoc_info.resp_frame = frame;
event.assoc_info.resp_frame_len = len;
- if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
- event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
- event.assoc_info.resp_ies_len =
- len - 24 - sizeof(mgmt->u.assoc_resp);
+ if (len > fixed_len) {
+ event.assoc_info.resp_ies = frame + fixed_len;
+ event.assoc_info.resp_ies_len = len - fixed_len;
}
if (req_ie) {
--
2.43.0
More information about the Hostap
mailing list