| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * HT handling | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> | 
|---|
| 6 | * Copyright 2002-2005, Instant802 Networks, Inc. | 
|---|
| 7 | * Copyright 2005-2006, Devicescape Software, Inc. | 
|---|
| 8 | * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz> | 
|---|
| 9 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 
|---|
| 10 | * Copyright 2007-2010, Intel Corporation | 
|---|
| 11 | * Copyright 2017	Intel Deutschland GmbH | 
|---|
| 12 | * Copyright(c) 2020-2025 Intel Corporation | 
|---|
| 13 | */ | 
|---|
| 14 |  | 
|---|
| 15 | #include <linux/ieee80211.h> | 
|---|
| 16 | #include <linux/export.h> | 
|---|
| 17 | #include <net/mac80211.h> | 
|---|
| 18 | #include "ieee80211_i.h" | 
|---|
| 19 | #include "rate.h" | 
|---|
| 20 |  | 
|---|
| 21 | static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa, | 
|---|
| 22 | struct ieee80211_ht_cap *ht_capa_mask, | 
|---|
| 23 | struct ieee80211_sta_ht_cap *ht_cap, | 
|---|
| 24 | u16 flag) | 
|---|
| 25 | { | 
|---|
| 26 | __le16 le_flag = cpu_to_le16(flag); | 
|---|
| 27 | if (ht_capa_mask->cap_info & le_flag) { | 
|---|
| 28 | if (!(ht_capa->cap_info & le_flag)) | 
|---|
| 29 | ht_cap->cap &= ~flag; | 
|---|
| 30 | } | 
|---|
| 31 | } | 
|---|
| 32 |  | 
|---|
| 33 | static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, | 
|---|
| 34 | struct ieee80211_ht_cap *ht_capa_mask, | 
|---|
| 35 | struct ieee80211_sta_ht_cap *ht_cap, | 
|---|
| 36 | u16 flag) | 
|---|
| 37 | { | 
|---|
| 38 | __le16 le_flag = cpu_to_le16(flag); | 
|---|
| 39 |  | 
|---|
| 40 | if ((ht_capa_mask->cap_info & le_flag) && | 
|---|
| 41 | (ht_capa->cap_info & le_flag)) | 
|---|
| 42 | ht_cap->cap |= flag; | 
|---|
| 43 | } | 
|---|
| 44 |  | 
|---|
| 45 | void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | 
|---|
| 46 | struct ieee80211_sta_ht_cap *ht_cap) | 
|---|
| 47 | { | 
|---|
| 48 | struct ieee80211_ht_cap *ht_capa, *ht_capa_mask; | 
|---|
| 49 | u8 *scaps, *smask; | 
|---|
| 50 | int i; | 
|---|
| 51 |  | 
|---|
| 52 | if (!ht_cap->ht_supported) | 
|---|
| 53 | return; | 
|---|
| 54 |  | 
|---|
| 55 | switch (sdata->vif.type) { | 
|---|
| 56 | case NL80211_IFTYPE_STATION: | 
|---|
| 57 | ht_capa = &sdata->u.mgd.ht_capa; | 
|---|
| 58 | ht_capa_mask = &sdata->u.mgd.ht_capa_mask; | 
|---|
| 59 | break; | 
|---|
| 60 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 61 | ht_capa = &sdata->u.ibss.ht_capa; | 
|---|
| 62 | ht_capa_mask = &sdata->u.ibss.ht_capa_mask; | 
|---|
| 63 | break; | 
|---|
| 64 | default: | 
|---|
| 65 | WARN_ON_ONCE(1); | 
|---|
| 66 | return; | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | scaps = (u8 *)(&ht_capa->mcs.rx_mask); | 
|---|
| 70 | smask = (u8 *)(&ht_capa_mask->mcs.rx_mask); | 
|---|
| 71 |  | 
|---|
| 72 | /* NOTE:  If you add more over-rides here, update register_hw | 
|---|
| 73 | * ht_capa_mod_mask logic in main.c as well. | 
|---|
| 74 | * And, if this method can ever change ht_cap.ht_supported, fix | 
|---|
| 75 | * the check in ieee80211_add_ht_ie. | 
|---|
| 76 | */ | 
|---|
| 77 |  | 
|---|
| 78 | /* check for HT over-rides, MCS rates first. */ | 
|---|
| 79 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { | 
|---|
| 80 | u8 m = smask[i]; | 
|---|
| 81 | ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ | 
|---|
| 82 | /* Add back rates that are supported */ | 
|---|
| 83 | ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | /* Force removal of HT-40 capabilities? */ | 
|---|
| 87 | __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 88 | IEEE80211_HT_CAP_SUP_WIDTH_20_40); | 
|---|
| 89 | __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 90 | IEEE80211_HT_CAP_SGI_40); | 
|---|
| 91 |  | 
|---|
| 92 | /* Allow user to disable SGI-20 (SGI-40 is handled above) */ | 
|---|
| 93 | __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 94 | IEEE80211_HT_CAP_SGI_20); | 
|---|
| 95 |  | 
|---|
| 96 | /* Allow user to disable the max-AMSDU bit. */ | 
|---|
| 97 | __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 98 | IEEE80211_HT_CAP_MAX_AMSDU); | 
|---|
| 99 |  | 
|---|
| 100 | /* Allow user to disable LDPC */ | 
|---|
| 101 | __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 102 | IEEE80211_HT_CAP_LDPC_CODING); | 
|---|
| 103 |  | 
|---|
| 104 | /* Allow user to enable 40 MHz intolerant bit. */ | 
|---|
| 105 | __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 106 | IEEE80211_HT_CAP_40MHZ_INTOLERANT); | 
|---|
| 107 |  | 
|---|
| 108 | /* Allow user to enable TX STBC bit  */ | 
|---|
| 109 | __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, | 
|---|
| 110 | IEEE80211_HT_CAP_TX_STBC); | 
|---|
| 111 |  | 
|---|
| 112 | /* Allow user to configure RX STBC bits */ | 
|---|
| 113 | if (ht_capa_mask->cap_info & cpu_to_le16(IEEE80211_HT_CAP_RX_STBC)) | 
|---|
| 114 | ht_cap->cap |= le16_to_cpu(ht_capa->cap_info) & | 
|---|
| 115 | IEEE80211_HT_CAP_RX_STBC; | 
|---|
| 116 |  | 
|---|
| 117 | /* Allow user to decrease AMPDU factor */ | 
|---|
| 118 | if (ht_capa_mask->ampdu_params_info & | 
|---|
| 119 | IEEE80211_HT_AMPDU_PARM_FACTOR) { | 
|---|
| 120 | u8 n = ht_capa->ampdu_params_info & | 
|---|
| 121 | IEEE80211_HT_AMPDU_PARM_FACTOR; | 
|---|
| 122 | if (n < ht_cap->ampdu_factor) | 
|---|
| 123 | ht_cap->ampdu_factor = n; | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|
| 126 | /* Allow the user to increase AMPDU density. */ | 
|---|
| 127 | if (ht_capa_mask->ampdu_params_info & | 
|---|
| 128 | IEEE80211_HT_AMPDU_PARM_DENSITY) { | 
|---|
| 129 | u8 n = (ht_capa->ampdu_params_info & | 
|---|
| 130 | IEEE80211_HT_AMPDU_PARM_DENSITY) | 
|---|
| 131 | >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; | 
|---|
| 132 | if (n > ht_cap->ampdu_density) | 
|---|
| 133 | ht_cap->ampdu_density = n; | 
|---|
| 134 | } | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 |  | 
|---|
| 138 | bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | 
|---|
| 139 | struct ieee80211_supported_band *sband, | 
|---|
| 140 | const struct ieee80211_ht_cap *ht_cap_ie, | 
|---|
| 141 | struct link_sta_info *link_sta) | 
|---|
| 142 | { | 
|---|
| 143 | struct ieee80211_bss_conf *link_conf; | 
|---|
| 144 | struct sta_info *sta = link_sta->sta; | 
|---|
| 145 | struct ieee80211_sta_ht_cap ht_cap, own_cap; | 
|---|
| 146 | u8 ampdu_info, tx_mcs_set_cap; | 
|---|
| 147 | int i, max_tx_streams; | 
|---|
| 148 | bool changed; | 
|---|
| 149 | enum ieee80211_sta_rx_bandwidth bw; | 
|---|
| 150 | enum nl80211_chan_width width; | 
|---|
| 151 |  | 
|---|
| 152 | memset(s: &ht_cap, c: 0, n: sizeof(ht_cap)); | 
|---|
| 153 |  | 
|---|
| 154 | if (!ht_cap_ie || !sband->ht_cap.ht_supported) | 
|---|
| 155 | goto apply; | 
|---|
| 156 |  | 
|---|
| 157 | ht_cap.ht_supported = true; | 
|---|
| 158 |  | 
|---|
| 159 | own_cap = sband->ht_cap; | 
|---|
| 160 |  | 
|---|
| 161 | /* | 
|---|
| 162 | * If user has specified capability over-rides, take care | 
|---|
| 163 | * of that if the station we're setting up is the AP or TDLS peer that | 
|---|
| 164 | * we advertised a restricted capability set to. Override | 
|---|
| 165 | * our own capabilities and then use those below. | 
|---|
| 166 | */ | 
|---|
| 167 | if (sdata->vif.type == NL80211_IFTYPE_STATION || | 
|---|
| 168 | sdata->vif.type == NL80211_IFTYPE_ADHOC) | 
|---|
| 169 | ieee80211_apply_htcap_overrides(sdata, ht_cap: &own_cap); | 
|---|
| 170 |  | 
|---|
| 171 | /* | 
|---|
| 172 | * The bits listed in this expression should be | 
|---|
| 173 | * the same for the peer and us, if the station | 
|---|
| 174 | * advertises more then we can't use those thus | 
|---|
| 175 | * we mask them out. | 
|---|
| 176 | */ | 
|---|
| 177 | ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & | 
|---|
| 178 | (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | | 
|---|
| 179 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | 
|---|
| 180 | IEEE80211_HT_CAP_GRN_FLD | | 
|---|
| 181 | IEEE80211_HT_CAP_SGI_20 | | 
|---|
| 182 | IEEE80211_HT_CAP_SGI_40 | | 
|---|
| 183 | IEEE80211_HT_CAP_DSSSCCK40)); | 
|---|
| 184 |  | 
|---|
| 185 | /* | 
|---|
| 186 | * The STBC bits are asymmetric -- if we don't have | 
|---|
| 187 | * TX then mask out the peer's RX and vice versa. | 
|---|
| 188 | */ | 
|---|
| 189 | if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC)) | 
|---|
| 190 | ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; | 
|---|
| 191 | if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC)) | 
|---|
| 192 | ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; | 
|---|
| 193 |  | 
|---|
| 194 | ampdu_info = ht_cap_ie->ampdu_params_info; | 
|---|
| 195 | ht_cap.ampdu_factor = | 
|---|
| 196 | ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | 
|---|
| 197 | ht_cap.ampdu_density = | 
|---|
| 198 | (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | 
|---|
| 199 |  | 
|---|
| 200 | /* own MCS TX capabilities */ | 
|---|
| 201 | tx_mcs_set_cap = own_cap.mcs.tx_params; | 
|---|
| 202 |  | 
|---|
| 203 | /* Copy peer MCS TX capabilities, the driver might need them. */ | 
|---|
| 204 | ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; | 
|---|
| 205 |  | 
|---|
| 206 | /* can we TX with MCS rates? */ | 
|---|
| 207 | if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) | 
|---|
| 208 | goto apply; | 
|---|
| 209 |  | 
|---|
| 210 | /* Counting from 0, therefore +1 */ | 
|---|
| 211 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) | 
|---|
| 212 | max_tx_streams = | 
|---|
| 213 | ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) | 
|---|
| 214 | >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; | 
|---|
| 215 | else | 
|---|
| 216 | max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; | 
|---|
| 217 |  | 
|---|
| 218 | /* | 
|---|
| 219 | * 802.11n-2009 20.3.5 / 20.6 says: | 
|---|
| 220 | * - indices 0 to 7 and 32 are single spatial stream | 
|---|
| 221 | * - 8 to 31 are multiple spatial streams using equal modulation | 
|---|
| 222 | *   [8..15 for two streams, 16..23 for three and 24..31 for four] | 
|---|
| 223 | * - remainder are multiple spatial streams using unequal modulation | 
|---|
| 224 | */ | 
|---|
| 225 | for (i = 0; i < max_tx_streams; i++) | 
|---|
| 226 | ht_cap.mcs.rx_mask[i] = | 
|---|
| 227 | own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; | 
|---|
| 228 |  | 
|---|
| 229 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) | 
|---|
| 230 | for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; | 
|---|
| 231 | i < IEEE80211_HT_MCS_MASK_LEN; i++) | 
|---|
| 232 | ht_cap.mcs.rx_mask[i] = | 
|---|
| 233 | own_cap.mcs.rx_mask[i] & | 
|---|
| 234 | ht_cap_ie->mcs.rx_mask[i]; | 
|---|
| 235 |  | 
|---|
| 236 | /* handle MCS rate 32 too */ | 
|---|
| 237 | if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) | 
|---|
| 238 | ht_cap.mcs.rx_mask[32/8] |= 1; | 
|---|
| 239 |  | 
|---|
| 240 | /* set Rx highest rate */ | 
|---|
| 241 | ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; | 
|---|
| 242 |  | 
|---|
| 243 | if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) | 
|---|
| 244 | link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; | 
|---|
| 245 | else | 
|---|
| 246 | link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; | 
|---|
| 247 |  | 
|---|
| 248 | ieee80211_sta_recalc_aggregates(pubsta: &sta->sta); | 
|---|
| 249 |  | 
|---|
| 250 | apply: | 
|---|
| 251 | changed = memcmp(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap)); | 
|---|
| 252 |  | 
|---|
| 253 | memcpy(to: &link_sta->pub->ht_cap, from: &ht_cap, len: sizeof(ht_cap)); | 
|---|
| 254 |  | 
|---|
| 255 | rcu_read_lock(); | 
|---|
| 256 | link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]); | 
|---|
| 257 | if (WARN_ON(!link_conf)) | 
|---|
| 258 | width = NL80211_CHAN_WIDTH_20_NOHT; | 
|---|
| 259 | else | 
|---|
| 260 | width = link_conf->chanreq.oper.width; | 
|---|
| 261 |  | 
|---|
| 262 | switch (width) { | 
|---|
| 263 | default: | 
|---|
| 264 | WARN_ON_ONCE(1); | 
|---|
| 265 | fallthrough; | 
|---|
| 266 | case NL80211_CHAN_WIDTH_20_NOHT: | 
|---|
| 267 | case NL80211_CHAN_WIDTH_20: | 
|---|
| 268 | bw = IEEE80211_STA_RX_BW_20; | 
|---|
| 269 | break; | 
|---|
| 270 | case NL80211_CHAN_WIDTH_40: | 
|---|
| 271 | case NL80211_CHAN_WIDTH_80: | 
|---|
| 272 | case NL80211_CHAN_WIDTH_80P80: | 
|---|
| 273 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 274 | case NL80211_CHAN_WIDTH_320: | 
|---|
| 275 | bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | 
|---|
| 276 | IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | 
|---|
| 277 | break; | 
|---|
| 278 | } | 
|---|
| 279 | rcu_read_unlock(); | 
|---|
| 280 |  | 
|---|
| 281 | link_sta->pub->bandwidth = bw; | 
|---|
| 282 |  | 
|---|
| 283 | link_sta->cur_max_bandwidth = | 
|---|
| 284 | ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | 
|---|
| 285 | IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | 
|---|
| 286 |  | 
|---|
| 287 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP || | 
|---|
| 288 | sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | 
|---|
| 289 | enum ieee80211_smps_mode smps_mode; | 
|---|
| 290 |  | 
|---|
| 291 | switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) | 
|---|
| 292 | >> IEEE80211_HT_CAP_SM_PS_SHIFT) { | 
|---|
| 293 | case WLAN_HT_CAP_SM_PS_INVALID: | 
|---|
| 294 | case WLAN_HT_CAP_SM_PS_STATIC: | 
|---|
| 295 | smps_mode = IEEE80211_SMPS_STATIC; | 
|---|
| 296 | break; | 
|---|
| 297 | case WLAN_HT_CAP_SM_PS_DYNAMIC: | 
|---|
| 298 | smps_mode = IEEE80211_SMPS_DYNAMIC; | 
|---|
| 299 | break; | 
|---|
| 300 | case WLAN_HT_CAP_SM_PS_DISABLED: | 
|---|
| 301 | smps_mode = IEEE80211_SMPS_OFF; | 
|---|
| 302 | break; | 
|---|
| 303 | } | 
|---|
| 304 |  | 
|---|
| 305 | if (smps_mode != link_sta->pub->smps_mode) | 
|---|
| 306 | changed = true; | 
|---|
| 307 | link_sta->pub->smps_mode = smps_mode; | 
|---|
| 308 | } else { | 
|---|
| 309 | link_sta->pub->smps_mode = IEEE80211_SMPS_OFF; | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | return changed; | 
|---|
| 313 | } | 
|---|
| 314 |  | 
|---|
| 315 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | 
|---|
| 316 | enum ieee80211_agg_stop_reason reason) | 
|---|
| 317 | { | 
|---|
| 318 | int i; | 
|---|
| 319 |  | 
|---|
| 320 | lockdep_assert_wiphy(sta->local->hw.wiphy); | 
|---|
| 321 |  | 
|---|
| 322 | for (i = 0; i <  IEEE80211_NUM_TIDS; i++) | 
|---|
| 323 | __ieee80211_stop_rx_ba_session(sta, tid: i, initiator: WLAN_BACK_RECIPIENT, | 
|---|
| 324 | reason: WLAN_REASON_QSTA_LEAVE_QBSS, | 
|---|
| 325 | stop: reason != AGG_STOP_DESTROY_STA && | 
|---|
| 326 | reason != AGG_STOP_PEER_REQUEST); | 
|---|
| 327 |  | 
|---|
| 328 | for (i = 0; i <  IEEE80211_NUM_TIDS; i++) | 
|---|
| 329 | __ieee80211_stop_tx_ba_session(sta, tid: i, reason); | 
|---|
| 330 |  | 
|---|
| 331 | /* | 
|---|
| 332 | * In case the tear down is part of a reconfigure due to HW restart | 
|---|
| 333 | * request, it is possible that the low level driver requested to stop | 
|---|
| 334 | * the BA session, so handle it to properly clean tid_tx data. | 
|---|
| 335 | */ | 
|---|
| 336 | if(reason == AGG_STOP_DESTROY_STA) { | 
|---|
| 337 | wiphy_work_cancel(wiphy: sta->local->hw.wiphy, work: &sta->ampdu_mlme.work); | 
|---|
| 338 |  | 
|---|
| 339 | for (i = 0; i < IEEE80211_NUM_TIDS; i++) { | 
|---|
| 340 | struct tid_ampdu_tx *tid_tx = | 
|---|
| 341 | rcu_dereference_protected_tid_tx(sta, i); | 
|---|
| 342 |  | 
|---|
| 343 | if (!tid_tx) | 
|---|
| 344 | continue; | 
|---|
| 345 |  | 
|---|
| 346 | if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, addr: &tid_tx->state)) | 
|---|
| 347 | ieee80211_stop_tx_ba_cb(sta, tid: i, tid_tx); | 
|---|
| 348 | } | 
|---|
| 349 | } | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work) | 
|---|
| 353 | { | 
|---|
| 354 | struct sta_info *sta = | 
|---|
| 355 | container_of(work, struct sta_info, ampdu_mlme.work); | 
|---|
| 356 | struct tid_ampdu_tx *tid_tx; | 
|---|
| 357 | bool blocked; | 
|---|
| 358 | int tid; | 
|---|
| 359 |  | 
|---|
| 360 | lockdep_assert_wiphy(sta->local->hw.wiphy); | 
|---|
| 361 |  | 
|---|
| 362 | /* When this flag is set, new sessions should be blocked. */ | 
|---|
| 363 | blocked = test_sta_flag(sta, flag: WLAN_STA_BLOCK_BA); | 
|---|
| 364 |  | 
|---|
| 365 | for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { | 
|---|
| 366 | if (test_and_clear_bit(nr: tid, addr: sta->ampdu_mlme.tid_rx_timer_expired)) | 
|---|
| 367 | __ieee80211_stop_rx_ba_session( | 
|---|
| 368 | sta, tid, initiator: WLAN_BACK_RECIPIENT, | 
|---|
| 369 | reason: WLAN_REASON_QSTA_TIMEOUT, stop: true); | 
|---|
| 370 |  | 
|---|
| 371 | if (test_and_clear_bit(nr: tid, | 
|---|
| 372 | addr: sta->ampdu_mlme.tid_rx_stop_requested)) | 
|---|
| 373 | __ieee80211_stop_rx_ba_session( | 
|---|
| 374 | sta, tid, initiator: WLAN_BACK_RECIPIENT, | 
|---|
| 375 | reason: WLAN_REASON_UNSPECIFIED, stop: true); | 
|---|
| 376 |  | 
|---|
| 377 | if (!blocked && | 
|---|
| 378 | test_and_clear_bit(nr: tid, | 
|---|
| 379 | addr: sta->ampdu_mlme.tid_rx_manage_offl)) | 
|---|
| 380 | __ieee80211_start_rx_ba_session(sta, dialog_token: 0, timeout: 0, start_seq_num: 0, ba_policy: 1, tid, | 
|---|
| 381 | IEEE80211_MAX_AMPDU_BUF_HT, | 
|---|
| 382 | tx: false, auto_seq: true, addba_ext_data: 0); | 
|---|
| 383 |  | 
|---|
| 384 | if (test_and_clear_bit(nr: tid + IEEE80211_NUM_TIDS, | 
|---|
| 385 | addr: sta->ampdu_mlme.tid_rx_manage_offl)) | 
|---|
| 386 | __ieee80211_stop_rx_ba_session( | 
|---|
| 387 | sta, tid, initiator: WLAN_BACK_RECIPIENT, | 
|---|
| 388 | reason: 0, stop: false); | 
|---|
| 389 |  | 
|---|
| 390 | spin_lock_bh(lock: &sta->lock); | 
|---|
| 391 |  | 
|---|
| 392 | tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; | 
|---|
| 393 | if (!blocked && tid_tx) { | 
|---|
| 394 | struct txq_info *txqi = to_txq_info(txq: sta->sta.txq[tid]); | 
|---|
| 395 | struct ieee80211_sub_if_data *sdata = | 
|---|
| 396 | vif_to_sdata(p: txqi->txq.vif); | 
|---|
| 397 | struct fq *fq = &sdata->local->fq; | 
|---|
| 398 |  | 
|---|
| 399 | spin_lock_bh(lock: &fq->lock); | 
|---|
| 400 |  | 
|---|
| 401 | /* Allow only frags to be dequeued */ | 
|---|
| 402 | set_bit(nr: IEEE80211_TXQ_STOP, addr: &txqi->flags); | 
|---|
| 403 |  | 
|---|
| 404 | if (!skb_queue_empty(list: &txqi->frags)) { | 
|---|
| 405 | /* Fragmented Tx is ongoing, wait for it to | 
|---|
| 406 | * finish. Reschedule worker to retry later. | 
|---|
| 407 | */ | 
|---|
| 408 |  | 
|---|
| 409 | spin_unlock_bh(lock: &fq->lock); | 
|---|
| 410 | spin_unlock_bh(lock: &sta->lock); | 
|---|
| 411 |  | 
|---|
| 412 | /* Give the task working on the txq a chance | 
|---|
| 413 | * to send out the queued frags | 
|---|
| 414 | */ | 
|---|
| 415 | synchronize_net(); | 
|---|
| 416 |  | 
|---|
| 417 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, work); | 
|---|
| 418 | return; | 
|---|
| 419 | } | 
|---|
| 420 |  | 
|---|
| 421 | spin_unlock_bh(lock: &fq->lock); | 
|---|
| 422 |  | 
|---|
| 423 | /* | 
|---|
| 424 | * Assign it over to the normal tid_tx array | 
|---|
| 425 | * where it "goes live". | 
|---|
| 426 | */ | 
|---|
| 427 |  | 
|---|
| 428 | sta->ampdu_mlme.tid_start_tx[tid] = NULL; | 
|---|
| 429 | /* could there be a race? */ | 
|---|
| 430 | if (sta->ampdu_mlme.tid_tx[tid]) | 
|---|
| 431 | kfree(objp: tid_tx); | 
|---|
| 432 | else | 
|---|
| 433 | ieee80211_assign_tid_tx(sta, tid, tid_tx); | 
|---|
| 434 | spin_unlock_bh(lock: &sta->lock); | 
|---|
| 435 |  | 
|---|
| 436 | ieee80211_tx_ba_session_handle_start(sta, tid); | 
|---|
| 437 | continue; | 
|---|
| 438 | } | 
|---|
| 439 | spin_unlock_bh(lock: &sta->lock); | 
|---|
| 440 |  | 
|---|
| 441 | tid_tx = rcu_dereference_protected_tid_tx(sta, tid); | 
|---|
| 442 | if (!tid_tx) | 
|---|
| 443 | continue; | 
|---|
| 444 |  | 
|---|
| 445 | if (!blocked && | 
|---|
| 446 | test_and_clear_bit(HT_AGG_STATE_START_CB, addr: &tid_tx->state)) | 
|---|
| 447 | ieee80211_start_tx_ba_cb(sta, tid, tid_tx); | 
|---|
| 448 | if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, addr: &tid_tx->state)) | 
|---|
| 449 | __ieee80211_stop_tx_ba_session(sta, tid, | 
|---|
| 450 | reason: AGG_STOP_LOCAL_REQUEST); | 
|---|
| 451 | if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, addr: &tid_tx->state)) | 
|---|
| 452 | ieee80211_stop_tx_ba_cb(sta, tid, tid_tx); | 
|---|
| 453 | } | 
|---|
| 454 | } | 
|---|
| 455 |  | 
|---|
| 456 | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | 
|---|
| 457 | const u8 *da, u16 tid, | 
|---|
| 458 | u16 initiator, u16 reason_code) | 
|---|
| 459 | { | 
|---|
| 460 | struct ieee80211_local *local = sdata->local; | 
|---|
| 461 | struct sk_buff *skb; | 
|---|
| 462 | struct ieee80211_mgmt *mgmt; | 
|---|
| 463 | u16 params; | 
|---|
| 464 |  | 
|---|
| 465 | skb = dev_alloc_skb(length: sizeof(*mgmt) + local->hw.extra_tx_headroom); | 
|---|
| 466 | if (!skb) | 
|---|
| 467 | return; | 
|---|
| 468 |  | 
|---|
| 469 | skb_reserve(skb, len: local->hw.extra_tx_headroom); | 
|---|
| 470 | mgmt = ieee80211_mgmt_ba(skb, da, sdata); | 
|---|
| 471 |  | 
|---|
| 472 | skb_put(skb, len: 1 + sizeof(mgmt->u.action.u.delba)); | 
|---|
| 473 |  | 
|---|
| 474 | mgmt->u.action.category = WLAN_CATEGORY_BACK; | 
|---|
| 475 | mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; | 
|---|
| 476 | params = (u16)(initiator << 11); 	/* bit 11 initiator */ | 
|---|
| 477 | params |= (u16)(tid << 12); 		/* bit 15:12 TID number */ | 
|---|
| 478 |  | 
|---|
| 479 | mgmt->u.action.u.delba.params = cpu_to_le16(params); | 
|---|
| 480 | mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); | 
|---|
| 481 |  | 
|---|
| 482 | ieee80211_tx_skb(sdata, skb); | 
|---|
| 483 | } | 
|---|
| 484 |  | 
|---|
| 485 | void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | 
|---|
| 486 | struct sta_info *sta, | 
|---|
| 487 | struct ieee80211_mgmt *mgmt, size_t len) | 
|---|
| 488 | { | 
|---|
| 489 | u16 tid, params; | 
|---|
| 490 | u16 initiator; | 
|---|
| 491 |  | 
|---|
| 492 | params = le16_to_cpu(mgmt->u.action.u.delba.params); | 
|---|
| 493 | tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; | 
|---|
| 494 | initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; | 
|---|
| 495 |  | 
|---|
| 496 | ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", | 
|---|
| 497 | mgmt->sa, initiator ? "initiator": "recipient", | 
|---|
| 498 | tid, | 
|---|
| 499 | le16_to_cpu(mgmt->u.action.u.delba.reason_code)); | 
|---|
| 500 |  | 
|---|
| 501 | if (initiator == WLAN_BACK_INITIATOR) | 
|---|
| 502 | __ieee80211_stop_rx_ba_session(sta, tid, initiator: WLAN_BACK_INITIATOR, reason: 0, | 
|---|
| 503 | stop: true); | 
|---|
| 504 | else | 
|---|
| 505 | __ieee80211_stop_tx_ba_session(sta, tid, reason: AGG_STOP_PEER_REQUEST); | 
|---|
| 506 | } | 
|---|
| 507 |  | 
|---|
| 508 | enum nl80211_smps_mode | 
|---|
| 509 | ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps) | 
|---|
| 510 | { | 
|---|
| 511 | switch (smps) { | 
|---|
| 512 | case IEEE80211_SMPS_OFF: | 
|---|
| 513 | return NL80211_SMPS_OFF; | 
|---|
| 514 | case IEEE80211_SMPS_STATIC: | 
|---|
| 515 | return NL80211_SMPS_STATIC; | 
|---|
| 516 | case IEEE80211_SMPS_DYNAMIC: | 
|---|
| 517 | return NL80211_SMPS_DYNAMIC; | 
|---|
| 518 | default: | 
|---|
| 519 | return NL80211_SMPS_OFF; | 
|---|
| 520 | } | 
|---|
| 521 | } | 
|---|
| 522 |  | 
|---|
| 523 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | 
|---|
| 524 | enum ieee80211_smps_mode smps, const u8 *da, | 
|---|
| 525 | const u8 *bssid, int link_id) | 
|---|
| 526 | { | 
|---|
| 527 | struct ieee80211_local *local = sdata->local; | 
|---|
| 528 | struct sk_buff *skb; | 
|---|
| 529 | struct ieee80211_mgmt *action_frame; | 
|---|
| 530 | struct ieee80211_tx_info *info; | 
|---|
| 531 | u8 status_link_id = link_id < 0 ? 0 : link_id; | 
|---|
| 532 |  | 
|---|
| 533 | /* 27 = header + category + action + smps mode */ | 
|---|
| 534 | skb = dev_alloc_skb(length: 27 + local->hw.extra_tx_headroom); | 
|---|
| 535 | if (!skb) | 
|---|
| 536 | return -ENOMEM; | 
|---|
| 537 |  | 
|---|
| 538 | skb_reserve(skb, len: local->hw.extra_tx_headroom); | 
|---|
| 539 | action_frame = skb_put(skb, len: 27); | 
|---|
| 540 | memcpy(to: action_frame->da, from: da, ETH_ALEN); | 
|---|
| 541 | memcpy(to: action_frame->sa, from: sdata->dev->dev_addr, ETH_ALEN); | 
|---|
| 542 | memcpy(to: action_frame->bssid, from: bssid, ETH_ALEN); | 
|---|
| 543 | action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 
|---|
| 544 | IEEE80211_STYPE_ACTION); | 
|---|
| 545 | action_frame->u.action.category = WLAN_CATEGORY_HT; | 
|---|
| 546 | action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; | 
|---|
| 547 | switch (smps) { | 
|---|
| 548 | case IEEE80211_SMPS_AUTOMATIC: | 
|---|
| 549 | case IEEE80211_SMPS_NUM_MODES: | 
|---|
| 550 | WARN_ON(1); | 
|---|
| 551 | smps = IEEE80211_SMPS_OFF; | 
|---|
| 552 | fallthrough; | 
|---|
| 553 | case IEEE80211_SMPS_OFF: | 
|---|
| 554 | action_frame->u.action.u.ht_smps.smps_control = | 
|---|
| 555 | WLAN_HT_SMPS_CONTROL_DISABLED; | 
|---|
| 556 | break; | 
|---|
| 557 | case IEEE80211_SMPS_STATIC: | 
|---|
| 558 | action_frame->u.action.u.ht_smps.smps_control = | 
|---|
| 559 | WLAN_HT_SMPS_CONTROL_STATIC; | 
|---|
| 560 | break; | 
|---|
| 561 | case IEEE80211_SMPS_DYNAMIC: | 
|---|
| 562 | action_frame->u.action.u.ht_smps.smps_control = | 
|---|
| 563 | WLAN_HT_SMPS_CONTROL_DYNAMIC; | 
|---|
| 564 | break; | 
|---|
| 565 | } | 
|---|
| 566 |  | 
|---|
| 567 | /* we'll do more on status of this frame */ | 
|---|
| 568 | info = IEEE80211_SKB_CB(skb); | 
|---|
| 569 | info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; | 
|---|
| 570 | /* we have 13 bits, and need 6: link_id 4, smps 2 */ | 
|---|
| 571 | info->status_data = IEEE80211_STATUS_TYPE_SMPS | | 
|---|
| 572 | u16_encode_bits(v: status_link_id << 2 | smps, | 
|---|
| 573 | field: IEEE80211_STATUS_SUBDATA_MASK); | 
|---|
| 574 | ieee80211_tx_skb_tid(sdata, skb, tid: 7, link_id); | 
|---|
| 575 |  | 
|---|
| 576 | return 0; | 
|---|
| 577 | } | 
|---|
| 578 |  | 
|---|
| 579 | void ieee80211_request_smps(struct ieee80211_vif *vif, unsigned int link_id, | 
|---|
| 580 | enum ieee80211_smps_mode smps_mode) | 
|---|
| 581 | { | 
|---|
| 582 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); | 
|---|
| 583 | struct ieee80211_link_data *link; | 
|---|
| 584 |  | 
|---|
| 585 | if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION)) | 
|---|
| 586 | return; | 
|---|
| 587 |  | 
|---|
| 588 | rcu_read_lock(); | 
|---|
| 589 | link = rcu_dereference(sdata->link[link_id]); | 
|---|
| 590 | if (WARN_ON(!link)) | 
|---|
| 591 | goto out; | 
|---|
| 592 |  | 
|---|
| 593 | trace_api_request_smps(local: sdata->local, sdata, link, smps_mode); | 
|---|
| 594 |  | 
|---|
| 595 | if (link->u.mgd.driver_smps_mode == smps_mode) | 
|---|
| 596 | goto out; | 
|---|
| 597 |  | 
|---|
| 598 | link->u.mgd.driver_smps_mode = smps_mode; | 
|---|
| 599 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, | 
|---|
| 600 | work: &link->u.mgd.request_smps_work); | 
|---|
| 601 | out: | 
|---|
| 602 | rcu_read_unlock(); | 
|---|
| 603 | } | 
|---|
| 604 | /* this might change ... don't want non-open drivers using it */ | 
|---|
| 605 | EXPORT_SYMBOL_GPL(ieee80211_request_smps); | 
|---|
| 606 |  | 
|---|
| 607 | void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local, | 
|---|
| 608 | struct ieee80211_sub_if_data *sdata, | 
|---|
| 609 | struct sta_info *sta, | 
|---|
| 610 | struct link_sta_info *link_sta, | 
|---|
| 611 | u8 chanwidth, enum nl80211_band band) | 
|---|
| 612 | { | 
|---|
| 613 | enum ieee80211_sta_rx_bandwidth max_bw, new_bw; | 
|---|
| 614 | struct ieee80211_supported_band *sband; | 
|---|
| 615 | struct sta_opmode_info sta_opmode = {}; | 
|---|
| 616 |  | 
|---|
| 617 | lockdep_assert_wiphy(local->hw.wiphy); | 
|---|
| 618 |  | 
|---|
| 619 | if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) | 
|---|
| 620 | max_bw = IEEE80211_STA_RX_BW_20; | 
|---|
| 621 | else | 
|---|
| 622 | max_bw = ieee80211_sta_cap_rx_bw(link_sta); | 
|---|
| 623 |  | 
|---|
| 624 | /* set cur_max_bandwidth and recalc sta bw */ | 
|---|
| 625 | link_sta->cur_max_bandwidth = max_bw; | 
|---|
| 626 | new_bw = ieee80211_sta_cur_vht_bw(link_sta); | 
|---|
| 627 |  | 
|---|
| 628 | if (link_sta->pub->bandwidth == new_bw) | 
|---|
| 629 | return; | 
|---|
| 630 |  | 
|---|
| 631 | link_sta->pub->bandwidth = new_bw; | 
|---|
| 632 | sband = local->hw.wiphy->bands[band]; | 
|---|
| 633 | sta_opmode.bw = | 
|---|
| 634 | ieee80211_sta_rx_bw_to_chan_width(sta: link_sta); | 
|---|
| 635 | sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED; | 
|---|
| 636 |  | 
|---|
| 637 | rate_control_rate_update(local, sband, link_sta, | 
|---|
| 638 | changed: IEEE80211_RC_BW_CHANGED); | 
|---|
| 639 | cfg80211_sta_opmode_change_notify(dev: sdata->dev, | 
|---|
| 640 | mac: sta->addr, | 
|---|
| 641 | sta_opmode: &sta_opmode, | 
|---|
| 642 | GFP_KERNEL); | 
|---|
| 643 | } | 
|---|
| 644 |  | 
|---|