@@ -2435,7 +2435,9 @@ struct ieee80211_sta_aggregates {
* notifications and capabilities. The value is only valid after
* the station moves to associated state.
* @txpwr: the station tx power configuration
- *
+ * @punctured: Preamble puncturing bitmap. Each bit represents
+ * a 20 MHz channel, lowest bit corresponding to the lowest channel.
+ * Bit set to 1 indicates that the channel is punctured.
*/
struct ieee80211_link_sta {
struct ieee80211_sta *sta;
@@ -2456,6 +2458,7 @@ struct ieee80211_link_sta {
u8 rx_nss;
enum ieee80211_sta_rx_bandwidth bandwidth;
struct ieee80211_sta_txpwr txpwr;
+ u16 punctured;
};
/**
@@ -1948,13 +1948,30 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
(void *)params->he_6ghz_capa,
link_sta);
- if (params->he_capa && params->eht_capa)
+ if (params->he_capa && params->eht_capa) {
ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
(u8 *)params->he_capa,
params->he_capa_len,
params->eht_capa,
params->eht_capa_len,
link_sta);
+ /* 802.11s mesh STA may have different eht puncturing pattern,
+ * update it here so that drivers can use if needed.
+ */
+ if (ieee80211_vif_is_mesh(&sdata->vif) && params->punctured) {
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *chan = link->conf->chanreq.oper.chan;
+
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ chandef.punctured = params->punctured;
+ chandef.width = link->conf->chanreq.oper.width;
+ chandef.width = link->conf->chanreq.oper.width;
+ chandef.center_freq1 = link->conf->chanreq.oper.center_freq1;
+
+ if (cfg80211_chandef_valid(&chandef))
+ link_sta->pub->punctured = params->punctured;
+ }
+ }
ieee80211_sta_init_nss(link_sta);
@@ -112,6 +112,12 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
ie->eht_operation,
&sta_chan_def);
+ /* In order to allow mesh peering when peer advertising different
+ * puncturing bitmaps, update the local mesh punctured bitmap
+ * with the new mesh peer punctured bitmap to ensure compatibility.
+ */
+ sta_chan_def.punctured = sdata->vif.bss_conf.chanreq.oper.punctured;
+
if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chanreq.oper,
&sta_chan_def))
return false;
@@ -672,6 +678,9 @@ int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *sk
len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
offsetof(struct ieee80211_eht_operation_info, optional);
+ if (sdata->vif.bss_conf.chanreq.oper.punctured)
+ len += sizeof(sdata->vif.bss_conf.chanreq.oper.punctured);
+
if (skb_tailroom(skb) < len)
return -ENOMEM;
@@ -430,11 +430,49 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
u32 rates, changed = 0;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.deflink.bandwidth;
+ struct cfg80211_chan_def eht_chandef;
sband = ieee80211_get_sband(sdata);
if (!sband)
return;
+ /* 802.11s mesh peer may have different eht puncturing pattern,
+ * update it here so that drivers can use if needed.
+ */
+ if (elems->eht_operation &&
+ (elems->eht_operation->params & IEEE80211_EHT_OPER_INFO_PRESENT) &&
+ (u8_get_bits(elems->eht_operation->optional[0],
+ IEEE80211_EHT_OPER_CHAN_WIDTH) >=
+ IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ) &&
+ (elems->eht_operation->params &
+ IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) {
+ struct ieee80211_channel *chan = sdata->vif.bss_conf.chanreq.oper.chan;
+
+ cfg80211_chandef_create(&eht_chandef, chan, NL80211_CHAN_NO_HT);
+ eht_chandef.punctured = (elems->eht_operation->optional[4] << 8) |
+ elems->eht_operation->optional[3];
+ /* Validate Peer's Puncturing Bitmap and reset if invalid */
+ switch (u8_get_bits(elems->eht_operation->optional[0],
+ IEEE80211_EHT_OPER_CHAN_WIDTH)) {
+ case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ:
+ eht_chandef.width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ:
+ eht_chandef.width = NL80211_CHAN_WIDTH_160;
+ break;
+ case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ:
+ eht_chandef.width = NL80211_CHAN_WIDTH_320;
+ break;
+ default:
+ eht_chandef.width = NL80211_CHAN_WIDTH_20;
+ }
+ eht_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(elems->eht_operation->optional[1],
+ chan->band);
+ if (cfg80211_chandef_valid(&eht_chandef))
+ sta->sta.deflink.punctured = eht_chandef.punctured;
+ }
+
rates = ieee80211_sta_get_rates(sdata, elems, sband->band, NULL);
spin_lock_bh(&sta->mesh->plink_lock);
@@ -2850,9 +2850,13 @@ u8 *ieee80211_ie_build_eht_oper(u8 *pos, const struct cfg80211_chan_def *chandef
u8 eht_oper_info_len =
offsetof(struct ieee80211_eht_operation_info, optional);
u8 chan_width = 0;
+ u8 ie_len = 0;
+
+ if (chandef->punctured)
+ ie_len += sizeof(chandef->punctured);
*pos++ = WLAN_EID_EXTENSION;
- *pos++ = 1 + eht_oper_len + eht_oper_info_len;
+ *pos++ = 1 + eht_oper_len + eht_oper_info_len + ie_len;
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
eht_oper = (struct ieee80211_eht_operation *)pos;
@@ -2904,7 +2908,14 @@ u8 *ieee80211_ie_build_eht_oper(u8 *pos, const struct cfg80211_chan_def *chandef
eht_oper_info->control = chan_width;
pos += eht_oper_info_len;
- /* TODO: eht_oper_info->optional */
+ if (chandef->punctured) {
+ eht_oper->params |=
+ IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT;
+
+ eht_oper_info->optional[0] = chandef->punctured & 0x00FF;
+ eht_oper_info->optional[1] = chandef->punctured >> 8;
+ pos += sizeof(chandef->punctured);
+ }
return pos;
}