162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HT handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 662306a36Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc. 762306a36Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 862306a36Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 962306a36Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 1062306a36Sopenharmony_ci * Copyright 2007-2010, Intel Corporation 1162306a36Sopenharmony_ci * Copyright 2017 Intel Deutschland GmbH 1262306a36Sopenharmony_ci * Copyright(c) 2020-2023 Intel Corporation 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/ieee80211.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <net/mac80211.h> 1862306a36Sopenharmony_ci#include "ieee80211_i.h" 1962306a36Sopenharmony_ci#include "rate.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa, 2262306a36Sopenharmony_ci struct ieee80211_ht_cap *ht_capa_mask, 2362306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 2462306a36Sopenharmony_ci u16 flag) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci __le16 le_flag = cpu_to_le16(flag); 2762306a36Sopenharmony_ci if (ht_capa_mask->cap_info & le_flag) { 2862306a36Sopenharmony_ci if (!(ht_capa->cap_info & le_flag)) 2962306a36Sopenharmony_ci ht_cap->cap &= ~flag; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, 3462306a36Sopenharmony_ci struct ieee80211_ht_cap *ht_capa_mask, 3562306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 3662306a36Sopenharmony_ci u16 flag) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci __le16 le_flag = cpu_to_le16(flag); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if ((ht_capa_mask->cap_info & le_flag) && 4162306a36Sopenharmony_ci (ht_capa->cap_info & le_flag)) 4262306a36Sopenharmony_ci ht_cap->cap |= flag; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_civoid ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 4662306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct ieee80211_ht_cap *ht_capa, *ht_capa_mask; 4962306a36Sopenharmony_ci u8 *scaps, *smask; 5062306a36Sopenharmony_ci int i; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!ht_cap->ht_supported) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci switch (sdata->vif.type) { 5662306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 5762306a36Sopenharmony_ci ht_capa = &sdata->u.mgd.ht_capa; 5862306a36Sopenharmony_ci ht_capa_mask = &sdata->u.mgd.ht_capa_mask; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 6162306a36Sopenharmony_ci ht_capa = &sdata->u.ibss.ht_capa; 6262306a36Sopenharmony_ci ht_capa_mask = &sdata->u.ibss.ht_capa_mask; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci default: 6562306a36Sopenharmony_ci WARN_ON_ONCE(1); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci scaps = (u8 *)(&ht_capa->mcs.rx_mask); 7062306a36Sopenharmony_ci smask = (u8 *)(&ht_capa_mask->mcs.rx_mask); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* NOTE: If you add more over-rides here, update register_hw 7362306a36Sopenharmony_ci * ht_capa_mod_mask logic in main.c as well. 7462306a36Sopenharmony_ci * And, if this method can ever change ht_cap.ht_supported, fix 7562306a36Sopenharmony_ci * the check in ieee80211_add_ht_ie. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* check for HT over-rides, MCS rates first. */ 7962306a36Sopenharmony_ci for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { 8062306a36Sopenharmony_ci u8 m = smask[i]; 8162306a36Sopenharmony_ci ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ 8262306a36Sopenharmony_ci /* Add back rates that are supported */ 8362306a36Sopenharmony_ci ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Force removal of HT-40 capabilities? */ 8762306a36Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 8862306a36Sopenharmony_ci IEEE80211_HT_CAP_SUP_WIDTH_20_40); 8962306a36Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 9062306a36Sopenharmony_ci IEEE80211_HT_CAP_SGI_40); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Allow user to disable SGI-20 (SGI-40 is handled above) */ 9362306a36Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 9462306a36Sopenharmony_ci IEEE80211_HT_CAP_SGI_20); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Allow user to disable the max-AMSDU bit. */ 9762306a36Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 9862306a36Sopenharmony_ci IEEE80211_HT_CAP_MAX_AMSDU); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Allow user to disable LDPC */ 10162306a36Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 10262306a36Sopenharmony_ci IEEE80211_HT_CAP_LDPC_CODING); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Allow user to enable 40 MHz intolerant bit. */ 10562306a36Sopenharmony_ci __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 10662306a36Sopenharmony_ci IEEE80211_HT_CAP_40MHZ_INTOLERANT); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Allow user to enable TX STBC bit */ 10962306a36Sopenharmony_ci __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 11062306a36Sopenharmony_ci IEEE80211_HT_CAP_TX_STBC); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Allow user to configure RX STBC bits */ 11362306a36Sopenharmony_ci if (ht_capa_mask->cap_info & cpu_to_le16(IEEE80211_HT_CAP_RX_STBC)) 11462306a36Sopenharmony_ci ht_cap->cap |= le16_to_cpu(ht_capa->cap_info) & 11562306a36Sopenharmony_ci IEEE80211_HT_CAP_RX_STBC; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Allow user to decrease AMPDU factor */ 11862306a36Sopenharmony_ci if (ht_capa_mask->ampdu_params_info & 11962306a36Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_FACTOR) { 12062306a36Sopenharmony_ci u8 n = ht_capa->ampdu_params_info & 12162306a36Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_FACTOR; 12262306a36Sopenharmony_ci if (n < ht_cap->ampdu_factor) 12362306a36Sopenharmony_ci ht_cap->ampdu_factor = n; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Allow the user to increase AMPDU density. */ 12762306a36Sopenharmony_ci if (ht_capa_mask->ampdu_params_info & 12862306a36Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY) { 12962306a36Sopenharmony_ci u8 n = (ht_capa->ampdu_params_info & 13062306a36Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY) 13162306a36Sopenharmony_ci >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 13262306a36Sopenharmony_ci if (n > ht_cap->ampdu_density) 13362306a36Sopenharmony_ci ht_cap->ampdu_density = n; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cibool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 13962306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 14062306a36Sopenharmony_ci const struct ieee80211_ht_cap *ht_cap_ie, 14162306a36Sopenharmony_ci struct link_sta_info *link_sta) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf; 14462306a36Sopenharmony_ci struct sta_info *sta = link_sta->sta; 14562306a36Sopenharmony_ci struct ieee80211_sta_ht_cap ht_cap, own_cap; 14662306a36Sopenharmony_ci u8 ampdu_info, tx_mcs_set_cap; 14762306a36Sopenharmony_ci int i, max_tx_streams; 14862306a36Sopenharmony_ci bool changed; 14962306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw; 15062306a36Sopenharmony_ci enum nl80211_chan_width width; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci memset(&ht_cap, 0, sizeof(ht_cap)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!ht_cap_ie || !sband->ht_cap.ht_supported) 15562306a36Sopenharmony_ci goto apply; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ht_cap.ht_supported = true; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci own_cap = sband->ht_cap; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * If user has specified capability over-rides, take care 16362306a36Sopenharmony_ci * of that if the station we're setting up is the AP or TDLS peer that 16462306a36Sopenharmony_ci * we advertised a restricted capability set to. Override 16562306a36Sopenharmony_ci * our own capabilities and then use those below. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION || 16862306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_ADHOC) 16962306a36Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &own_cap); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * The bits listed in this expression should be 17362306a36Sopenharmony_ci * the same for the peer and us, if the station 17462306a36Sopenharmony_ci * advertises more then we can't use those thus 17562306a36Sopenharmony_ci * we mask them out. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & 17862306a36Sopenharmony_ci (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | 17962306a36Sopenharmony_ci IEEE80211_HT_CAP_SUP_WIDTH_20_40 | 18062306a36Sopenharmony_ci IEEE80211_HT_CAP_GRN_FLD | 18162306a36Sopenharmony_ci IEEE80211_HT_CAP_SGI_20 | 18262306a36Sopenharmony_ci IEEE80211_HT_CAP_SGI_40 | 18362306a36Sopenharmony_ci IEEE80211_HT_CAP_DSSSCCK40)); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * The STBC bits are asymmetric -- if we don't have 18762306a36Sopenharmony_ci * TX then mask out the peer's RX and vice versa. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC)) 19062306a36Sopenharmony_ci ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; 19162306a36Sopenharmony_ci if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC)) 19262306a36Sopenharmony_ci ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ampdu_info = ht_cap_ie->ampdu_params_info; 19562306a36Sopenharmony_ci ht_cap.ampdu_factor = 19662306a36Sopenharmony_ci ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 19762306a36Sopenharmony_ci ht_cap.ampdu_density = 19862306a36Sopenharmony_ci (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* own MCS TX capabilities */ 20162306a36Sopenharmony_ci tx_mcs_set_cap = own_cap.mcs.tx_params; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Copy peer MCS TX capabilities, the driver might need them. */ 20462306a36Sopenharmony_ci ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* can we TX with MCS rates? */ 20762306a36Sopenharmony_ci if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) 20862306a36Sopenharmony_ci goto apply; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Counting from 0, therefore +1 */ 21162306a36Sopenharmony_ci if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) 21262306a36Sopenharmony_ci max_tx_streams = 21362306a36Sopenharmony_ci ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 21462306a36Sopenharmony_ci >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * 802.11n-2009 20.3.5 / 20.6 says: 22062306a36Sopenharmony_ci * - indices 0 to 7 and 32 are single spatial stream 22162306a36Sopenharmony_ci * - 8 to 31 are multiple spatial streams using equal modulation 22262306a36Sopenharmony_ci * [8..15 for two streams, 16..23 for three and 24..31 for four] 22362306a36Sopenharmony_ci * - remainder are multiple spatial streams using unequal modulation 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci for (i = 0; i < max_tx_streams; i++) 22662306a36Sopenharmony_ci ht_cap.mcs.rx_mask[i] = 22762306a36Sopenharmony_ci own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) 23062306a36Sopenharmony_ci for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; 23162306a36Sopenharmony_ci i < IEEE80211_HT_MCS_MASK_LEN; i++) 23262306a36Sopenharmony_ci ht_cap.mcs.rx_mask[i] = 23362306a36Sopenharmony_ci own_cap.mcs.rx_mask[i] & 23462306a36Sopenharmony_ci ht_cap_ie->mcs.rx_mask[i]; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* handle MCS rate 32 too */ 23762306a36Sopenharmony_ci if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) 23862306a36Sopenharmony_ci ht_cap.mcs.rx_mask[32/8] |= 1; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* set Rx highest rate */ 24162306a36Sopenharmony_ci ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) 24462306a36Sopenharmony_ci link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ieee80211_sta_recalc_aggregates(&sta->sta); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci apply: 25162306a36Sopenharmony_ci changed = memcmp(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci memcpy(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci rcu_read_lock(); 25662306a36Sopenharmony_ci link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]); 25762306a36Sopenharmony_ci if (WARN_ON(!link_conf)) 25862306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_20_NOHT; 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci width = link_conf->chandef.width; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci switch (width) { 26362306a36Sopenharmony_ci default: 26462306a36Sopenharmony_ci WARN_ON_ONCE(1); 26562306a36Sopenharmony_ci fallthrough; 26662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 26762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 26862306a36Sopenharmony_ci bw = IEEE80211_STA_RX_BW_20; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 27162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 27262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 27362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 27462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 27562306a36Sopenharmony_ci bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 27662306a36Sopenharmony_ci IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci rcu_read_unlock(); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci link_sta->pub->bandwidth = bw; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci link_sta->cur_max_bandwidth = 28462306a36Sopenharmony_ci ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 28562306a36Sopenharmony_ci IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (sta->sdata->vif.type == NL80211_IFTYPE_AP || 28862306a36Sopenharmony_ci sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 28962306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) 29262306a36Sopenharmony_ci >> IEEE80211_HT_CAP_SM_PS_SHIFT) { 29362306a36Sopenharmony_ci case WLAN_HT_CAP_SM_PS_INVALID: 29462306a36Sopenharmony_ci case WLAN_HT_CAP_SM_PS_STATIC: 29562306a36Sopenharmony_ci smps_mode = IEEE80211_SMPS_STATIC; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case WLAN_HT_CAP_SM_PS_DYNAMIC: 29862306a36Sopenharmony_ci smps_mode = IEEE80211_SMPS_DYNAMIC; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case WLAN_HT_CAP_SM_PS_DISABLED: 30162306a36Sopenharmony_ci smps_mode = IEEE80211_SMPS_OFF; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (smps_mode != link_sta->pub->smps_mode) 30662306a36Sopenharmony_ci changed = true; 30762306a36Sopenharmony_ci link_sta->pub->smps_mode = smps_mode; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci link_sta->pub->smps_mode = IEEE80211_SMPS_OFF; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return changed; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_civoid ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, 31662306a36Sopenharmony_ci enum ieee80211_agg_stop_reason reason) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci int i; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 32162306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) 32262306a36Sopenharmony_ci ___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, 32362306a36Sopenharmony_ci WLAN_REASON_QSTA_LEAVE_QBSS, 32462306a36Sopenharmony_ci reason != AGG_STOP_DESTROY_STA && 32562306a36Sopenharmony_ci reason != AGG_STOP_PEER_REQUEST); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) 32862306a36Sopenharmony_ci ___ieee80211_stop_tx_ba_session(sta, i, reason); 32962306a36Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * In case the tear down is part of a reconfigure due to HW restart 33362306a36Sopenharmony_ci * request, it is possible that the low level driver requested to stop 33462306a36Sopenharmony_ci * the BA session, so handle it to properly clean tid_tx data. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci if(reason == AGG_STOP_DESTROY_STA) { 33762306a36Sopenharmony_ci cancel_work_sync(&sta->ampdu_mlme.work); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 34062306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) { 34162306a36Sopenharmony_ci struct tid_ampdu_tx *tid_tx = 34262306a36Sopenharmony_ci rcu_dereference_protected_tid_tx(sta, i); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!tid_tx) 34562306a36Sopenharmony_ci continue; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 34862306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb(sta, i, tid_tx); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_civoid ieee80211_ba_session_work(struct work_struct *work) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct sta_info *sta = 35762306a36Sopenharmony_ci container_of(work, struct sta_info, ampdu_mlme.work); 35862306a36Sopenharmony_ci struct tid_ampdu_tx *tid_tx; 35962306a36Sopenharmony_ci bool blocked; 36062306a36Sopenharmony_ci int tid; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* When this flag is set, new sessions should be blocked. */ 36362306a36Sopenharmony_ci blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 36662306a36Sopenharmony_ci for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { 36762306a36Sopenharmony_ci if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) 36862306a36Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 36962306a36Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 37062306a36Sopenharmony_ci WLAN_REASON_QSTA_TIMEOUT, true); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (test_and_clear_bit(tid, 37362306a36Sopenharmony_ci sta->ampdu_mlme.tid_rx_stop_requested)) 37462306a36Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 37562306a36Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 37662306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, true); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (!blocked && 37962306a36Sopenharmony_ci test_and_clear_bit(tid, 38062306a36Sopenharmony_ci sta->ampdu_mlme.tid_rx_manage_offl)) 38162306a36Sopenharmony_ci ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, 38262306a36Sopenharmony_ci IEEE80211_MAX_AMPDU_BUF_HT, 38362306a36Sopenharmony_ci false, true, NULL); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, 38662306a36Sopenharmony_ci sta->ampdu_mlme.tid_rx_manage_offl)) 38762306a36Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 38862306a36Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 38962306a36Sopenharmony_ci 0, false); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci spin_lock_bh(&sta->lock); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; 39462306a36Sopenharmony_ci if (!blocked && tid_tx) { 39562306a36Sopenharmony_ci struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); 39662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 39762306a36Sopenharmony_ci vif_to_sdata(txqi->txq.vif); 39862306a36Sopenharmony_ci struct fq *fq = &sdata->local->fq; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci spin_lock_bh(&fq->lock); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Allow only frags to be dequeued */ 40362306a36Sopenharmony_ci set_bit(IEEE80211_TXQ_STOP, &txqi->flags); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!skb_queue_empty(&txqi->frags)) { 40662306a36Sopenharmony_ci /* Fragmented Tx is ongoing, wait for it to 40762306a36Sopenharmony_ci * finish. Reschedule worker to retry later. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci spin_unlock_bh(&fq->lock); 41162306a36Sopenharmony_ci spin_unlock_bh(&sta->lock); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Give the task working on the txq a chance 41462306a36Sopenharmony_ci * to send out the queued frags 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci synchronize_net(); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ieee80211_queue_work(&sdata->local->hw, work); 42162306a36Sopenharmony_ci return; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_unlock_bh(&fq->lock); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * Assign it over to the normal tid_tx array 42862306a36Sopenharmony_ci * where it "goes live". 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci sta->ampdu_mlme.tid_start_tx[tid] = NULL; 43262306a36Sopenharmony_ci /* could there be a race? */ 43362306a36Sopenharmony_ci if (sta->ampdu_mlme.tid_tx[tid]) 43462306a36Sopenharmony_ci kfree(tid_tx); 43562306a36Sopenharmony_ci else 43662306a36Sopenharmony_ci ieee80211_assign_tid_tx(sta, tid, tid_tx); 43762306a36Sopenharmony_ci spin_unlock_bh(&sta->lock); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ieee80211_tx_ba_session_handle_start(sta, tid); 44062306a36Sopenharmony_ci continue; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci spin_unlock_bh(&sta->lock); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci tid_tx = rcu_dereference_protected_tid_tx(sta, tid); 44562306a36Sopenharmony_ci if (!tid_tx) 44662306a36Sopenharmony_ci continue; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!blocked && 44962306a36Sopenharmony_ci test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) 45062306a36Sopenharmony_ci ieee80211_start_tx_ba_cb(sta, tid, tid_tx); 45162306a36Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) 45262306a36Sopenharmony_ci ___ieee80211_stop_tx_ba_session(sta, tid, 45362306a36Sopenharmony_ci AGG_STOP_LOCAL_REQUEST); 45462306a36Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 45562306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb(sta, tid, tid_tx); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_civoid ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, 46162306a36Sopenharmony_ci const u8 *da, u16 tid, 46262306a36Sopenharmony_ci u16 initiator, u16 reason_code) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 46562306a36Sopenharmony_ci struct sk_buff *skb; 46662306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 46762306a36Sopenharmony_ci u16 params; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); 47062306a36Sopenharmony_ci if (!skb) 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 47462306a36Sopenharmony_ci mgmt = skb_put_zero(skb, 24); 47562306a36Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 47662306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 47762306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 47862306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 47962306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_MESH_POINT) 48062306a36Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 48162306a36Sopenharmony_ci else if (sdata->vif.type == NL80211_IFTYPE_STATION) 48262306a36Sopenharmony_ci memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN); 48362306a36Sopenharmony_ci else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) 48462306a36Sopenharmony_ci memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 48762306a36Sopenharmony_ci IEEE80211_STYPE_ACTION); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_BACK; 49262306a36Sopenharmony_ci mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; 49362306a36Sopenharmony_ci params = (u16)(initiator << 11); /* bit 11 initiator */ 49462306a36Sopenharmony_ci params |= (u16)(tid << 12); /* bit 15:12 TID number */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci mgmt->u.action.u.delba.params = cpu_to_le16(params); 49762306a36Sopenharmony_ci mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_civoid ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, 50362306a36Sopenharmony_ci struct sta_info *sta, 50462306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci u16 tid, params; 50762306a36Sopenharmony_ci u16 initiator; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci params = le16_to_cpu(mgmt->u.action.u.delba.params); 51062306a36Sopenharmony_ci tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; 51162306a36Sopenharmony_ci initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", 51462306a36Sopenharmony_ci mgmt->sa, initiator ? "initiator" : "recipient", 51562306a36Sopenharmony_ci tid, 51662306a36Sopenharmony_ci le16_to_cpu(mgmt->u.action.u.delba.reason_code)); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (initiator == WLAN_BACK_INITIATOR) 51962306a36Sopenharmony_ci __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, 52062306a36Sopenharmony_ci true); 52162306a36Sopenharmony_ci else 52262306a36Sopenharmony_ci __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cienum nl80211_smps_mode 52662306a36Sopenharmony_ciieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci switch (smps) { 52962306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 53062306a36Sopenharmony_ci return NL80211_SMPS_OFF; 53162306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 53262306a36Sopenharmony_ci return NL80211_SMPS_STATIC; 53362306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 53462306a36Sopenharmony_ci return NL80211_SMPS_DYNAMIC; 53562306a36Sopenharmony_ci default: 53662306a36Sopenharmony_ci return NL80211_SMPS_OFF; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciint ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, 54162306a36Sopenharmony_ci enum ieee80211_smps_mode smps, const u8 *da, 54262306a36Sopenharmony_ci const u8 *bssid) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 54562306a36Sopenharmony_ci struct sk_buff *skb; 54662306a36Sopenharmony_ci struct ieee80211_mgmt *action_frame; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 27 = header + category + action + smps mode */ 54962306a36Sopenharmony_ci skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); 55062306a36Sopenharmony_ci if (!skb) 55162306a36Sopenharmony_ci return -ENOMEM; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 55462306a36Sopenharmony_ci action_frame = skb_put(skb, 27); 55562306a36Sopenharmony_ci memcpy(action_frame->da, da, ETH_ALEN); 55662306a36Sopenharmony_ci memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN); 55762306a36Sopenharmony_ci memcpy(action_frame->bssid, bssid, ETH_ALEN); 55862306a36Sopenharmony_ci action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 55962306a36Sopenharmony_ci IEEE80211_STYPE_ACTION); 56062306a36Sopenharmony_ci action_frame->u.action.category = WLAN_CATEGORY_HT; 56162306a36Sopenharmony_ci action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; 56262306a36Sopenharmony_ci switch (smps) { 56362306a36Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 56462306a36Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 56562306a36Sopenharmony_ci WARN_ON(1); 56662306a36Sopenharmony_ci fallthrough; 56762306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 56862306a36Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 56962306a36Sopenharmony_ci WLAN_HT_SMPS_CONTROL_DISABLED; 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 57262306a36Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 57362306a36Sopenharmony_ci WLAN_HT_SMPS_CONTROL_STATIC; 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 57662306a36Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 57762306a36Sopenharmony_ci WLAN_HT_SMPS_CONTROL_DYNAMIC; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* we'll do more on status of this frame */ 58262306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 58362306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_civoid ieee80211_request_smps(struct ieee80211_vif *vif, unsigned int link_id, 58962306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 59262306a36Sopenharmony_ci struct ieee80211_link_data *link; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION)) 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci rcu_read_lock(); 59862306a36Sopenharmony_ci link = rcu_dereference(sdata->link[link_id]); 59962306a36Sopenharmony_ci if (WARN_ON(!link)) 60062306a36Sopenharmony_ci goto out; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (link->u.mgd.driver_smps_mode == smps_mode) 60362306a36Sopenharmony_ci goto out; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci link->u.mgd.driver_smps_mode = smps_mode; 60662306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 60762306a36Sopenharmony_ci &link->u.mgd.request_smps_work); 60862306a36Sopenharmony_ciout: 60962306a36Sopenharmony_ci rcu_read_unlock(); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci/* this might change ... don't want non-open drivers using it */ 61262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_request_smps); 613