Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

wifi: mac80211: correctly initialise S1G chandef for STA

When moving to the APs channel, ensure we correctly initialise the chandef
and perform the required validation. Additionally, if the AP is beaconing on a
2MHz primary, calculate the 2MHz primary center frequency by extracting
the sibling 1MHz primary and averaging the frequencies to find the 2MHz
primary center frequency.

Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
Link: https://patch.msgid.link/20250918051913.500781-3-lachlan.hodges@morsemicro.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Lachlan Hodges and committed by
Johannes Berg
31e7681d d0688dc2

+109 -23
+17 -1
include/linux/ieee80211.h
··· 1182 1182 IEEE80211_S1G_CHANWIDTH_16MHZ = 15, 1183 1183 }; 1184 1184 1185 + /** 1186 + * enum ieee80211_s1g_pri_chanwidth - S1G primary channel widths 1187 + * described in IEEE80211-2024 Table 10-39. 1188 + * 1189 + * @IEEE80211_S1G_PRI_CHANWIDTH_2MHZ: 2MHz primary channel 1190 + * @IEEE80211_S1G_PRI_CHANWIDTH_1MHZ: 1MHz primary channel 1191 + */ 1192 + enum ieee80211_s1g_pri_chanwidth { 1193 + IEEE80211_S1G_PRI_CHANWIDTH_2MHZ = 0, 1194 + IEEE80211_S1G_PRI_CHANWIDTH_1MHZ = 1, 1195 + }; 1196 + 1185 1197 #define WLAN_SA_QUERY_TR_ID_LEN 2 1186 1198 #define WLAN_MEMBERSHIP_LEN 8 1187 1199 #define WLAN_USER_POSITION_LEN 16 ··· 3182 3170 3183 3171 #define S1G_CAP9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0) 3184 3172 3185 - #define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ BIT(0) 3173 + #define S1G_OPER_CH_WIDTH_PRIMARY BIT(0) 3186 3174 #define S1G_OPER_CH_WIDTH_OPER GENMASK(4, 1) 3175 + #define S1G_OPER_CH_PRIMARY_LOCATION BIT(5) 3176 + 3177 + #define S1G_2M_PRIMARY_LOCATION_LOWER 0 3178 + #define S1G_2M_PRIMARY_LOCATION_UPPER 1 3187 3179 3188 3180 /* EHT MAC capabilities as defined in P802.11be_D2.0 section 9.4.2.313.2 */ 3189 3181 #define IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS 0x01
+2 -1
net/mac80211/ieee80211_i.h
··· 2710 2710 const struct ieee80211_he_operation *he_oper, 2711 2711 const struct ieee80211_eht_operation *eht_oper, 2712 2712 struct cfg80211_chan_def *chandef); 2713 - bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, 2713 + bool ieee80211_chandef_s1g_oper(struct ieee80211_local *local, 2714 + const struct ieee80211_s1g_oper_ie *oper, 2714 2715 struct cfg80211_chan_def *chandef); 2715 2716 void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef, 2716 2717 struct ieee80211_conn_settings *conn);
+4 -2
net/mac80211/main.c
··· 1249 1249 if (!dflt_chandef.chan) { 1250 1250 /* 1251 1251 * Assign the first enabled channel to dflt_chandef 1252 - * from the list of channels 1252 + * from the list of channels. For S1G interfaces 1253 + * ensure it can be used as a primary. 1253 1254 */ 1254 1255 for (i = 0; i < sband->n_channels; i++) 1255 1256 if (!(sband->channels[i].flags & 1256 - IEEE80211_CHAN_DISABLED)) 1257 + (IEEE80211_CHAN_DISABLED | 1258 + IEEE80211_CHAN_S1G_NO_PRIMARY))) 1257 1259 break; 1258 1260 /* if none found then use the first anyway */ 1259 1261 if (i == sband->n_channels)
+47 -6
net/mac80211/mlme.c
··· 180 180 181 181 /* get special S1G case out of the way */ 182 182 if (sband->band == NL80211_BAND_S1GHZ) { 183 - if (!ieee80211_chandef_s1g_oper(elems->s1g_oper, chandef)) { 184 - sdata_info(sdata, 185 - "Missing S1G Operation Element? Trying operating == primary\n"); 186 - chandef->width = ieee80211_s1g_channel_width(channel); 183 + if (!ieee80211_chandef_s1g_oper(sdata->local, elems->s1g_oper, 184 + chandef)) { 185 + /* Fallback to default 1MHz */ 186 + chandef->width = NL80211_CHAN_WIDTH_1; 187 + chandef->s1g_primary_2mhz = false; 187 188 } 188 189 189 190 return IEEE80211_CONN_MODE_S1G; ··· 1047 1046 ret = -EINVAL; 1048 1047 goto free; 1049 1048 } 1049 + 1050 + chanreq->oper = *ap_chandef; 1051 + if (!cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper, 1052 + IEEE80211_CHAN_DISABLED)) { 1053 + ret = -EINVAL; 1054 + goto free; 1055 + } 1056 + 1050 1057 return elems; 1051 1058 case NL80211_BAND_6GHZ: 1052 1059 if (ap_mode < IEEE80211_CONN_MODE_HE) { ··· 7301 7292 return memcmp(elems->ssid, cfg->ssid, cfg->ssid_len); 7302 7293 } 7303 7294 7295 + static bool 7296 + ieee80211_rx_beacon_freq_valid(struct ieee80211_local *local, 7297 + struct ieee80211_mgmt *mgmt, 7298 + struct ieee80211_rx_status *rx_status, 7299 + struct ieee80211_chanctx_conf *chanctx) 7300 + { 7301 + u32 pri_2mhz_khz; 7302 + struct ieee80211_channel *s1g_sibling_1mhz; 7303 + u32 pri_khz = ieee80211_channel_to_khz(chanctx->def.chan); 7304 + u32 rx_khz = ieee80211_rx_status_to_khz(rx_status); 7305 + 7306 + if (rx_khz == pri_khz) 7307 + return true; 7308 + 7309 + if (!chanctx->def.s1g_primary_2mhz) 7310 + return false; 7311 + 7312 + /* 7313 + * If we have an S1G interface with a 2MHz primary, beacons are 7314 + * sent on the center frequency of the 2MHz primary. Find the sibling 7315 + * 1MHz channel and calculate the 2MHz primary center frequency. 7316 + */ 7317 + s1g_sibling_1mhz = cfg80211_s1g_get_primary_sibling(local->hw.wiphy, 7318 + &chanctx->def); 7319 + if (!s1g_sibling_1mhz) 7320 + return false; 7321 + 7322 + pri_2mhz_khz = 7323 + (pri_khz + ieee80211_channel_to_khz(s1g_sibling_1mhz)) / 2; 7324 + return rx_khz == pri_2mhz_khz; 7325 + } 7326 + 7304 7327 static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, 7305 7328 struct ieee80211_hdr *hdr, size_t len, 7306 7329 struct ieee80211_rx_status *rx_status) ··· 7387 7346 return; 7388 7347 } 7389 7348 7390 - if (ieee80211_rx_status_to_khz(rx_status) != 7391 - ieee80211_channel_to_khz(chanctx_conf->def.chan)) { 7349 + if (!ieee80211_rx_beacon_freq_valid(local, mgmt, rx_status, 7350 + chanctx_conf)) { 7392 7351 rcu_read_unlock(); 7393 7352 return; 7394 7353 }
+7 -6
net/mac80211/scan.c
··· 996 996 local->scan_chandef.freq1_offset = chan->freq_offset; 997 997 local->scan_chandef.center_freq2 = 0; 998 998 999 - /* For scanning on the S1G band, detect the channel width according to 1000 - * the channel being scanned. 1001 - */ 999 + /* For S1G, only scan the 1MHz primaries. */ 1002 1000 if (chan->band == NL80211_BAND_S1GHZ) { 1003 - local->scan_chandef.width = ieee80211_s1g_channel_width(chan); 1001 + local->scan_chandef.width = NL80211_CHAN_WIDTH_1; 1002 + local->scan_chandef.s1g_primary_2mhz = false; 1004 1003 goto set_channel; 1005 1004 } 1006 1005 1007 - /* If scanning on oper channel, use whatever channel-type 1006 + /* 1007 + * If scanning on oper channel, use whatever channel-type 1008 1008 * is currently in use. 1009 1009 */ 1010 1010 if (chan == local->hw.conf.chandef.chan) ··· 1213 1213 1214 1214 for (band = 0; band < NUM_NL80211_BANDS; band++) { 1215 1215 if (!local->hw.wiphy->bands[band] || 1216 - band == NL80211_BAND_6GHZ) 1216 + band == NL80211_BAND_6GHZ || 1217 + band == NL80211_BAND_S1GHZ) 1217 1218 continue; 1218 1219 1219 1220 max_n = local->hw.wiphy->bands[band]->n_channels;
+32 -7
net/mac80211/util.c
··· 3199 3199 return true; 3200 3200 } 3201 3201 3202 - bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, 3202 + bool ieee80211_chandef_s1g_oper(struct ieee80211_local *local, 3203 + const struct ieee80211_s1g_oper_ie *oper, 3203 3204 struct cfg80211_chan_def *chandef) 3204 3205 { 3205 - u32 oper_freq; 3206 + u32 oper_khz, pri_1mhz_khz, pri_2mhz_khz; 3206 3207 3207 3208 if (!oper) 3208 3209 return false; ··· 3228 3227 return false; 3229 3228 } 3230 3229 3231 - oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch, 3232 - NL80211_BAND_S1GHZ); 3233 - chandef->center_freq1 = KHZ_TO_MHZ(oper_freq); 3234 - chandef->freq1_offset = oper_freq % 1000; 3230 + chandef->s1g_primary_2mhz = false; 3235 3231 3236 - return true; 3232 + switch (u8_get_bits(oper->ch_width, S1G_OPER_CH_WIDTH_PRIMARY)) { 3233 + case IEEE80211_S1G_PRI_CHANWIDTH_1MHZ: 3234 + pri_1mhz_khz = ieee80211_channel_to_freq_khz( 3235 + oper->primary_ch, NL80211_BAND_S1GHZ); 3236 + break; 3237 + case IEEE80211_S1G_PRI_CHANWIDTH_2MHZ: 3238 + chandef->s1g_primary_2mhz = true; 3239 + pri_2mhz_khz = ieee80211_channel_to_freq_khz( 3240 + oper->primary_ch, NL80211_BAND_S1GHZ); 3241 + 3242 + if (u8_get_bits(oper->ch_width, S1G_OPER_CH_PRIMARY_LOCATION) == 3243 + S1G_2M_PRIMARY_LOCATION_LOWER) 3244 + pri_1mhz_khz = pri_2mhz_khz - 500; 3245 + else 3246 + pri_1mhz_khz = pri_2mhz_khz + 500; 3247 + break; 3248 + default: 3249 + return false; 3250 + } 3251 + 3252 + oper_khz = ieee80211_channel_to_freq_khz(oper->oper_ch, 3253 + NL80211_BAND_S1GHZ); 3254 + chandef->center_freq1 = KHZ_TO_MHZ(oper_khz); 3255 + chandef->freq1_offset = oper_khz % 1000; 3256 + chandef->chan = 3257 + ieee80211_get_channel_khz(local->hw.wiphy, pri_1mhz_khz); 3258 + 3259 + return chandef->chan; 3237 3260 } 3238 3261 3239 3262 int ieee80211_put_srates_elem(struct sk_buff *skb,