1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 244d414dbSJohannes Berg /* 344d414dbSJohannes Berg * HT handling 444d414dbSJohannes Berg * 544d414dbSJohannes Berg * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 6bacac545SJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc. 7bacac545SJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc. 844d414dbSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 944d414dbSJohannes Berg * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 10cfcdbde3SJohannes Berg * Copyright 2007-2010, Intel Corporation 117a7c0a64SJohannes Berg * Copyright 2017 Intel Deutschland GmbH 12*bfd8403aSJohannes Berg * Copyright(c) 2020-2022 Intel Corporation 1344d414dbSJohannes Berg */ 1444d414dbSJohannes Berg 1544d414dbSJohannes Berg #include <linux/ieee80211.h> 16bc3b2d7fSPaul Gortmaker #include <linux/export.h> 1744d414dbSJohannes Berg #include <net/mac80211.h> 1844d414dbSJohannes Berg #include "ieee80211_i.h" 1981cb7623SSujith #include "rate.h" 2044d414dbSJohannes Berg 21822854b0SSimon Wunderlich static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa, 22822854b0SSimon Wunderlich struct ieee80211_ht_cap *ht_capa_mask, 23ef96a842SBen Greear struct ieee80211_sta_ht_cap *ht_cap, 24ef96a842SBen Greear u16 flag) 25ef96a842SBen Greear { 26ef96a842SBen Greear __le16 le_flag = cpu_to_le16(flag); 27822854b0SSimon Wunderlich if (ht_capa_mask->cap_info & le_flag) { 28822854b0SSimon Wunderlich if (!(ht_capa->cap_info & le_flag)) 29ef96a842SBen Greear ht_cap->cap &= ~flag; 30ef96a842SBen Greear } 31ef96a842SBen Greear } 32ef96a842SBen Greear 339a07bf50SJouni Malinen static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, 349a07bf50SJouni Malinen struct ieee80211_ht_cap *ht_capa_mask, 359a07bf50SJouni Malinen struct ieee80211_sta_ht_cap *ht_cap, 369a07bf50SJouni Malinen u16 flag) 379a07bf50SJouni Malinen { 389a07bf50SJouni Malinen __le16 le_flag = cpu_to_le16(flag); 399a07bf50SJouni Malinen 409a07bf50SJouni Malinen if ((ht_capa_mask->cap_info & le_flag) && 419a07bf50SJouni Malinen (ht_capa->cap_info & le_flag)) 429a07bf50SJouni Malinen ht_cap->cap |= flag; 439a07bf50SJouni Malinen } 449a07bf50SJouni Malinen 45ef96a842SBen Greear void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 46ef96a842SBen Greear struct ieee80211_sta_ht_cap *ht_cap) 47ef96a842SBen Greear { 48822854b0SSimon Wunderlich struct ieee80211_ht_cap *ht_capa, *ht_capa_mask; 49822854b0SSimon Wunderlich u8 *scaps, *smask; 50ef96a842SBen Greear int i; 51ef96a842SBen Greear 52e1a0c6b3SJohannes Berg if (!ht_cap->ht_supported) 53e1a0c6b3SJohannes Berg return; 54e1a0c6b3SJohannes Berg 55822854b0SSimon Wunderlich switch (sdata->vif.type) { 56822854b0SSimon Wunderlich case NL80211_IFTYPE_STATION: 57822854b0SSimon Wunderlich ht_capa = &sdata->u.mgd.ht_capa; 58822854b0SSimon Wunderlich ht_capa_mask = &sdata->u.mgd.ht_capa_mask; 59822854b0SSimon Wunderlich break; 60822854b0SSimon Wunderlich case NL80211_IFTYPE_ADHOC: 61822854b0SSimon Wunderlich ht_capa = &sdata->u.ibss.ht_capa; 62822854b0SSimon Wunderlich ht_capa_mask = &sdata->u.ibss.ht_capa_mask; 63822854b0SSimon Wunderlich break; 64822854b0SSimon Wunderlich default: 65822854b0SSimon Wunderlich WARN_ON_ONCE(1); 66822854b0SSimon Wunderlich return; 67822854b0SSimon Wunderlich } 68822854b0SSimon Wunderlich 69822854b0SSimon Wunderlich scaps = (u8 *)(&ht_capa->mcs.rx_mask); 70822854b0SSimon Wunderlich smask = (u8 *)(&ht_capa_mask->mcs.rx_mask); 71822854b0SSimon Wunderlich 72ef96a842SBen Greear /* NOTE: If you add more over-rides here, update register_hw 739a07bf50SJouni Malinen * ht_capa_mod_mask logic in main.c as well. 74ef96a842SBen Greear * And, if this method can ever change ht_cap.ht_supported, fix 75ef96a842SBen Greear * the check in ieee80211_add_ht_ie. 76ef96a842SBen Greear */ 77ef96a842SBen Greear 78ef96a842SBen Greear /* check for HT over-rides, MCS rates first. */ 79ef96a842SBen Greear for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { 80ef96a842SBen Greear u8 m = smask[i]; 81ef96a842SBen Greear ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ 82ef96a842SBen Greear /* Add back rates that are supported */ 83ef96a842SBen Greear ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); 84ef96a842SBen Greear } 85ef96a842SBen Greear 86ef96a842SBen Greear /* Force removal of HT-40 capabilities? */ 87822854b0SSimon Wunderlich __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 88822854b0SSimon Wunderlich IEEE80211_HT_CAP_SUP_WIDTH_20_40); 89822854b0SSimon Wunderlich __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 90822854b0SSimon Wunderlich IEEE80211_HT_CAP_SGI_40); 91ef96a842SBen Greear 92bc0784d9SBen Greear /* Allow user to disable SGI-20 (SGI-40 is handled above) */ 93822854b0SSimon Wunderlich __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 94822854b0SSimon Wunderlich IEEE80211_HT_CAP_SGI_20); 95bc0784d9SBen Greear 96ef96a842SBen Greear /* Allow user to disable the max-AMSDU bit. */ 97822854b0SSimon Wunderlich __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 98822854b0SSimon Wunderlich IEEE80211_HT_CAP_MAX_AMSDU); 99ef96a842SBen Greear 100a2e7495dSPawel Kulakowski /* Allow user to disable LDPC */ 101a2e7495dSPawel Kulakowski __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 102a2e7495dSPawel Kulakowski IEEE80211_HT_CAP_LDPC_CODING); 103a2e7495dSPawel Kulakowski 1049a07bf50SJouni Malinen /* Allow user to enable 40 MHz intolerant bit. */ 1059a07bf50SJouni Malinen __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 1069a07bf50SJouni Malinen IEEE80211_HT_CAP_40MHZ_INTOLERANT); 1079a07bf50SJouni Malinen 108d9bb4108SSergey Matyukevich /* Allow user to enable TX STBC bit */ 109d9bb4108SSergey Matyukevich __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 110d9bb4108SSergey Matyukevich IEEE80211_HT_CAP_TX_STBC); 111d9bb4108SSergey Matyukevich 112d9bb4108SSergey Matyukevich /* Allow user to configure RX STBC bits */ 113e9f33a8fSJohannes Berg if (ht_capa_mask->cap_info & cpu_to_le16(IEEE80211_HT_CAP_RX_STBC)) 114e9f33a8fSJohannes Berg ht_cap->cap |= le16_to_cpu(ht_capa->cap_info) & 115e9f33a8fSJohannes Berg IEEE80211_HT_CAP_RX_STBC; 116d9bb4108SSergey Matyukevich 117ef96a842SBen Greear /* Allow user to decrease AMPDU factor */ 118822854b0SSimon Wunderlich if (ht_capa_mask->ampdu_params_info & 119ef96a842SBen Greear IEEE80211_HT_AMPDU_PARM_FACTOR) { 120822854b0SSimon Wunderlich u8 n = ht_capa->ampdu_params_info & 121822854b0SSimon Wunderlich IEEE80211_HT_AMPDU_PARM_FACTOR; 122ef96a842SBen Greear if (n < ht_cap->ampdu_factor) 123ef96a842SBen Greear ht_cap->ampdu_factor = n; 124ef96a842SBen Greear } 125ef96a842SBen Greear 126ef96a842SBen Greear /* Allow the user to increase AMPDU density. */ 127822854b0SSimon Wunderlich if (ht_capa_mask->ampdu_params_info & 128ef96a842SBen Greear IEEE80211_HT_AMPDU_PARM_DENSITY) { 129822854b0SSimon Wunderlich u8 n = (ht_capa->ampdu_params_info & 130ef96a842SBen Greear IEEE80211_HT_AMPDU_PARM_DENSITY) 131ef96a842SBen Greear >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 132ef96a842SBen Greear if (n > ht_cap->ampdu_density) 133ef96a842SBen Greear ht_cap->ampdu_density = n; 134ef96a842SBen Greear } 135ef96a842SBen Greear } 136ef96a842SBen Greear 137ef96a842SBen Greear 138e1a0c6b3SJohannes Berg bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 139ef96a842SBen Greear struct ieee80211_supported_band *sband, 1404a3cb702SJohannes Berg const struct ieee80211_ht_cap *ht_cap_ie, 141e1a0c6b3SJohannes Berg struct sta_info *sta) 14244d414dbSJohannes Berg { 143c07270b6SJohannes Berg struct ieee80211_sta_ht_cap ht_cap, own_cap; 144ae5eb026SJohannes Berg u8 ampdu_info, tx_mcs_set_cap; 145ae5eb026SJohannes Berg int i, max_tx_streams; 146e1a0c6b3SJohannes Berg bool changed; 147e1a0c6b3SJohannes Berg enum ieee80211_sta_rx_bandwidth bw; 14844d414dbSJohannes Berg 149e1a0c6b3SJohannes Berg memset(&ht_cap, 0, sizeof(ht_cap)); 15044d414dbSJohannes Berg 15115804e3eSChristian Lamparter if (!ht_cap_ie || !sband->ht_cap.ht_supported) 152e1a0c6b3SJohannes Berg goto apply; 15344d414dbSJohannes Berg 154e1a0c6b3SJohannes Berg ht_cap.ht_supported = true; 155ae5eb026SJohannes Berg 156c07270b6SJohannes Berg own_cap = sband->ht_cap; 157c07270b6SJohannes Berg 158c07270b6SJohannes Berg /* 159c07270b6SJohannes Berg * If user has specified capability over-rides, take care 16013cc8a4aSArik Nemtsov * of that if the station we're setting up is the AP or TDLS peer that 161c07270b6SJohannes Berg * we advertised a restricted capability set to. Override 162c07270b6SJohannes Berg * our own capabilities and then use those below. 163c07270b6SJohannes Berg */ 16413cc8a4aSArik Nemtsov if (sdata->vif.type == NL80211_IFTYPE_STATION || 16513cc8a4aSArik Nemtsov sdata->vif.type == NL80211_IFTYPE_ADHOC) 166c07270b6SJohannes Berg ieee80211_apply_htcap_overrides(sdata, &own_cap); 167c07270b6SJohannes Berg 1689a418af5SJohannes Berg /* 1699a418af5SJohannes Berg * The bits listed in this expression should be 1709a418af5SJohannes Berg * the same for the peer and us, if the station 1719a418af5SJohannes Berg * advertises more then we can't use those thus 1729a418af5SJohannes Berg * we mask them out. 1739a418af5SJohannes Berg */ 174e1a0c6b3SJohannes Berg ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & 175c07270b6SJohannes Berg (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | 1769a418af5SJohannes Berg IEEE80211_HT_CAP_SUP_WIDTH_20_40 | 1779a418af5SJohannes Berg IEEE80211_HT_CAP_GRN_FLD | 1789a418af5SJohannes Berg IEEE80211_HT_CAP_SGI_20 | 1799a418af5SJohannes Berg IEEE80211_HT_CAP_SGI_40 | 1809a418af5SJohannes Berg IEEE80211_HT_CAP_DSSSCCK40)); 18121add825SJohannes Berg 1829a418af5SJohannes Berg /* 1839a418af5SJohannes Berg * The STBC bits are asymmetric -- if we don't have 1849a418af5SJohannes Berg * TX then mask out the peer's RX and vice versa. 1859a418af5SJohannes Berg */ 186c07270b6SJohannes Berg if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC)) 187e1a0c6b3SJohannes Berg ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; 188c07270b6SJohannes Berg if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC)) 189e1a0c6b3SJohannes Berg ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; 190ae5eb026SJohannes Berg 191ae5eb026SJohannes Berg ampdu_info = ht_cap_ie->ampdu_params_info; 192e1a0c6b3SJohannes Berg ht_cap.ampdu_factor = 193d9fe60deSJohannes Berg ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 194e1a0c6b3SJohannes Berg ht_cap.ampdu_density = 195d9fe60deSJohannes Berg (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; 196d9fe60deSJohannes Berg 197d9fe60deSJohannes Berg /* own MCS TX capabilities */ 198c07270b6SJohannes Berg tx_mcs_set_cap = own_cap.mcs.tx_params; 199d9fe60deSJohannes Berg 20090b4ca9dSJohannes Berg /* Copy peer MCS TX capabilities, the driver might need them. */ 201e1a0c6b3SJohannes Berg ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; 20290b4ca9dSJohannes Berg 203d9fe60deSJohannes Berg /* can we TX with MCS rates? */ 204d9fe60deSJohannes Berg if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) 205e1a0c6b3SJohannes Berg goto apply; 206d9fe60deSJohannes Berg 207d9fe60deSJohannes Berg /* Counting from 0, therefore +1 */ 208d9fe60deSJohannes Berg if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) 209d9fe60deSJohannes Berg max_tx_streams = 210d9fe60deSJohannes Berg ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 211d9fe60deSJohannes Berg >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; 212d9fe60deSJohannes Berg else 213d9fe60deSJohannes Berg max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; 214d9fe60deSJohannes Berg 215d9fe60deSJohannes Berg /* 21690b4ca9dSJohannes Berg * 802.11n-2009 20.3.5 / 20.6 says: 217d9fe60deSJohannes Berg * - indices 0 to 7 and 32 are single spatial stream 218d9fe60deSJohannes Berg * - 8 to 31 are multiple spatial streams using equal modulation 219d9fe60deSJohannes Berg * [8..15 for two streams, 16..23 for three and 24..31 for four] 220d9fe60deSJohannes Berg * - remainder are multiple spatial streams using unequal modulation 221d9fe60deSJohannes Berg */ 222d9fe60deSJohannes Berg for (i = 0; i < max_tx_streams; i++) 223e1a0c6b3SJohannes Berg ht_cap.mcs.rx_mask[i] = 224c07270b6SJohannes Berg own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; 225d9fe60deSJohannes Berg 226d9fe60deSJohannes Berg if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) 227d9fe60deSJohannes Berg for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; 228d9fe60deSJohannes Berg i < IEEE80211_HT_MCS_MASK_LEN; i++) 229e1a0c6b3SJohannes Berg ht_cap.mcs.rx_mask[i] = 230c07270b6SJohannes Berg own_cap.mcs.rx_mask[i] & 231ae5eb026SJohannes Berg ht_cap_ie->mcs.rx_mask[i]; 232d9fe60deSJohannes Berg 233d9fe60deSJohannes Berg /* handle MCS rate 32 too */ 234c07270b6SJohannes Berg if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) 235e1a0c6b3SJohannes Berg ht_cap.mcs.rx_mask[32/8] |= 1; 236ef96a842SBen Greear 237dc5943d5SArik Nemtsov /* set Rx highest rate */ 238dc5943d5SArik Nemtsov ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; 239dc5943d5SArik Nemtsov 240506bcfa8SEmmanuel Grumbach if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) 241506bcfa8SEmmanuel Grumbach sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; 242506bcfa8SEmmanuel Grumbach else 243506bcfa8SEmmanuel Grumbach sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; 244506bcfa8SEmmanuel Grumbach 245e1a0c6b3SJohannes Berg apply: 246046d2e7cSSriram R changed = memcmp(&sta->sta.deflink.ht_cap, &ht_cap, sizeof(ht_cap)); 247e1a0c6b3SJohannes Berg 248046d2e7cSSriram R memcpy(&sta->sta.deflink.ht_cap, &ht_cap, sizeof(ht_cap)); 249e1a0c6b3SJohannes Berg 250e1a0c6b3SJohannes Berg switch (sdata->vif.bss_conf.chandef.width) { 251e1a0c6b3SJohannes Berg default: 252e1a0c6b3SJohannes Berg WARN_ON_ONCE(1); 253fc0561dcSGustavo A. R. Silva fallthrough; 254e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_20_NOHT: 255e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_20: 256e1a0c6b3SJohannes Berg bw = IEEE80211_STA_RX_BW_20; 257e1a0c6b3SJohannes Berg break; 258e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_40: 259e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_80: 260e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_80P80: 261e1a0c6b3SJohannes Berg case NL80211_CHAN_WIDTH_160: 262e1a0c6b3SJohannes Berg bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 263e1a0c6b3SJohannes Berg IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 264e1a0c6b3SJohannes Berg break; 265e1a0c6b3SJohannes Berg } 266e1a0c6b3SJohannes Berg 267046d2e7cSSriram R sta->sta.deflink.bandwidth = bw; 268e1a0c6b3SJohannes Berg 269046d2e7cSSriram R sta->deflink.cur_max_bandwidth = 2700af83d3dSJohannes Berg ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 2710af83d3dSJohannes Berg IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 2720af83d3dSJohannes Berg 273c4d800dcSIlan Peer if (sta->sdata->vif.type == NL80211_IFTYPE_AP || 274c4d800dcSIlan Peer sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 275c4d800dcSIlan Peer enum ieee80211_smps_mode smps_mode; 276c4d800dcSIlan Peer 277af0ed69bSJohannes Berg switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) 278af0ed69bSJohannes Berg >> IEEE80211_HT_CAP_SM_PS_SHIFT) { 279af0ed69bSJohannes Berg case WLAN_HT_CAP_SM_PS_INVALID: 280af0ed69bSJohannes Berg case WLAN_HT_CAP_SM_PS_STATIC: 281af0ed69bSJohannes Berg smps_mode = IEEE80211_SMPS_STATIC; 282af0ed69bSJohannes Berg break; 283af0ed69bSJohannes Berg case WLAN_HT_CAP_SM_PS_DYNAMIC: 284af0ed69bSJohannes Berg smps_mode = IEEE80211_SMPS_DYNAMIC; 285af0ed69bSJohannes Berg break; 286af0ed69bSJohannes Berg case WLAN_HT_CAP_SM_PS_DISABLED: 287af0ed69bSJohannes Berg smps_mode = IEEE80211_SMPS_OFF; 288af0ed69bSJohannes Berg break; 289af0ed69bSJohannes Berg } 290af0ed69bSJohannes Berg 291af0ed69bSJohannes Berg if (smps_mode != sta->sta.smps_mode) 292af0ed69bSJohannes Berg changed = true; 293af0ed69bSJohannes Berg sta->sta.smps_mode = smps_mode; 294c4d800dcSIlan Peer } else { 295c4d800dcSIlan Peer sta->sta.smps_mode = IEEE80211_SMPS_OFF; 296c4d800dcSIlan Peer } 297e1a0c6b3SJohannes Berg return changed; 298ae5eb026SJohannes Berg } 299d9fe60deSJohannes Berg 300c82c4a80SJohannes Berg void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, 301c82c4a80SJohannes Berg enum ieee80211_agg_stop_reason reason) 30244d414dbSJohannes Berg { 303b8695a8fSJohannes Berg int i; 30444d414dbSJohannes Berg 30512811037SIlan peer mutex_lock(&sta->ampdu_mlme.mtx); 3060afe9d4aSJohannes Berg for (i = 0; i < IEEE80211_NUM_TIDS; i++) 30712811037SIlan peer ___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, 308c82c4a80SJohannes Berg WLAN_REASON_QSTA_LEAVE_QBSS, 309c82c4a80SJohannes Berg reason != AGG_STOP_DESTROY_STA && 310c82c4a80SJohannes Berg reason != AGG_STOP_PEER_REQUEST); 3117a7c0a64SJohannes Berg 31272e2c343SSara Sharon for (i = 0; i < IEEE80211_NUM_TIDS; i++) 31372e2c343SSara Sharon ___ieee80211_stop_tx_ba_session(sta, i, reason); 3140afe9d4aSJohannes Berg mutex_unlock(&sta->ampdu_mlme.mtx); 31572e2c343SSara Sharon 31698e93e96SIlan peer /* 31798e93e96SIlan peer * In case the tear down is part of a reconfigure due to HW restart 31898e93e96SIlan peer * request, it is possible that the low level driver requested to stop 31998e93e96SIlan peer * the BA session, so handle it to properly clean tid_tx data. 32098e93e96SIlan peer */ 32139c1134cSAlexander Wetzel if(reason == AGG_STOP_DESTROY_STA) { 32239c1134cSAlexander Wetzel cancel_work_sync(&sta->ampdu_mlme.work); 32339c1134cSAlexander Wetzel 32498e93e96SIlan peer mutex_lock(&sta->ampdu_mlme.mtx); 32598e93e96SIlan peer for (i = 0; i < IEEE80211_NUM_TIDS; i++) { 32698e93e96SIlan peer struct tid_ampdu_tx *tid_tx = 32798e93e96SIlan peer rcu_dereference_protected_tid_tx(sta, i); 32898e93e96SIlan peer 32998e93e96SIlan peer if (!tid_tx) 33098e93e96SIlan peer continue; 33198e93e96SIlan peer 33298e93e96SIlan peer if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 33398e93e96SIlan peer ieee80211_stop_tx_ba_cb(sta, i, tid_tx); 33498e93e96SIlan peer } 33598e93e96SIlan peer mutex_unlock(&sta->ampdu_mlme.mtx); 33644d414dbSJohannes Berg } 33739c1134cSAlexander Wetzel } 33844d414dbSJohannes Berg 33967c282c0SJohannes Berg void ieee80211_ba_session_work(struct work_struct *work) 34067c282c0SJohannes Berg { 34167c282c0SJohannes Berg struct sta_info *sta = 34267c282c0SJohannes Berg container_of(work, struct sta_info, ampdu_mlme.work); 34367c282c0SJohannes Berg struct tid_ampdu_tx *tid_tx; 34439c1134cSAlexander Wetzel bool blocked; 34567c282c0SJohannes Berg int tid; 34667c282c0SJohannes Berg 34739c1134cSAlexander Wetzel /* When this flag is set, new sessions should be blocked. */ 34839c1134cSAlexander Wetzel blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA); 34967c282c0SJohannes Berg 350a93e3644SJohannes Berg mutex_lock(&sta->ampdu_mlme.mtx); 3515a306f58SJohannes Berg for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { 3527c3b1dd8SJohannes Berg if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) 3537c3b1dd8SJohannes Berg ___ieee80211_stop_rx_ba_session( 3547c3b1dd8SJohannes Berg sta, tid, WLAN_BACK_RECIPIENT, 35553f73c09SJohannes Berg WLAN_REASON_QSTA_TIMEOUT, true); 3567c3b1dd8SJohannes Berg 357f41ccd71SShahar Levi if (test_and_clear_bit(tid, 358f41ccd71SShahar Levi sta->ampdu_mlme.tid_rx_stop_requested)) 359f41ccd71SShahar Levi ___ieee80211_stop_rx_ba_session( 360f41ccd71SShahar Levi sta, tid, WLAN_BACK_RECIPIENT, 361f41ccd71SShahar Levi WLAN_REASON_UNSPECIFIED, true); 362f41ccd71SShahar Levi 36339c1134cSAlexander Wetzel if (!blocked && 36439c1134cSAlexander Wetzel test_and_clear_bit(tid, 365699cb58cSJohannes Berg sta->ampdu_mlme.tid_rx_manage_offl)) 366bde59c47SJohannes Berg ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, 367b8042b3dSJohannes Berg IEEE80211_MAX_AMPDU_BUF_HT, 3682ab45876SJohn Crispin false, true, NULL); 369699cb58cSJohannes Berg 370699cb58cSJohannes Berg if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, 371699cb58cSJohannes Berg sta->ampdu_mlme.tid_rx_manage_offl)) 372699cb58cSJohannes Berg ___ieee80211_stop_rx_ba_session( 373699cb58cSJohannes Berg sta, tid, WLAN_BACK_RECIPIENT, 374699cb58cSJohannes Berg 0, false); 375699cb58cSJohannes Berg 376e562078aSBen Greear spin_lock_bh(&sta->lock); 377e562078aSBen Greear 378ec034b20SJohannes Berg tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; 37939c1134cSAlexander Wetzel if (!blocked && tid_tx) { 380ec034b20SJohannes Berg /* 381ec034b20SJohannes Berg * Assign it over to the normal tid_tx array 382ec034b20SJohannes Berg * where it "goes live". 383ec034b20SJohannes Berg */ 38467c282c0SJohannes Berg 385ec034b20SJohannes Berg sta->ampdu_mlme.tid_start_tx[tid] = NULL; 386ec034b20SJohannes Berg /* could there be a race? */ 387ec034b20SJohannes Berg if (sta->ampdu_mlme.tid_tx[tid]) 388ec034b20SJohannes Berg kfree(tid_tx); 389ec034b20SJohannes Berg else 390ec034b20SJohannes Berg ieee80211_assign_tid_tx(sta, tid, tid_tx); 391ec034b20SJohannes Berg spin_unlock_bh(&sta->lock); 392ec034b20SJohannes Berg 39367c282c0SJohannes Berg ieee80211_tx_ba_session_handle_start(sta, tid); 394ec034b20SJohannes Berg continue; 395ec034b20SJohannes Berg } 396e562078aSBen Greear spin_unlock_bh(&sta->lock); 397ec034b20SJohannes Berg 39840b275b6SJohannes Berg tid_tx = rcu_dereference_protected_tid_tx(sta, tid); 3997a7c0a64SJohannes Berg if (!tid_tx) 4007a7c0a64SJohannes Berg continue; 4017a7c0a64SJohannes Berg 40239c1134cSAlexander Wetzel if (!blocked && 40339c1134cSAlexander Wetzel test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) 4047a7c0a64SJohannes Berg ieee80211_start_tx_ba_cb(sta, tid, tid_tx); 4057a7c0a64SJohannes Berg if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) 40667c282c0SJohannes Berg ___ieee80211_stop_tx_ba_session(sta, tid, 407c82c4a80SJohannes Berg AGG_STOP_LOCAL_REQUEST); 4087a7c0a64SJohannes Berg if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 4097a7c0a64SJohannes Berg ieee80211_stop_tx_ba_cb(sta, tid, tid_tx); 41067c282c0SJohannes Berg } 411cfcdbde3SJohannes Berg mutex_unlock(&sta->ampdu_mlme.mtx); 41267c282c0SJohannes Berg } 41367c282c0SJohannes Berg 414b8695a8fSJohannes Berg void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, 415de1ede7aSJohannes Berg const u8 *da, u16 tid, 41644d414dbSJohannes Berg u16 initiator, u16 reason_code) 41744d414dbSJohannes Berg { 41844d414dbSJohannes Berg struct ieee80211_local *local = sdata->local; 41944d414dbSJohannes Berg struct sk_buff *skb; 42044d414dbSJohannes Berg struct ieee80211_mgmt *mgmt; 42144d414dbSJohannes Berg u16 params; 42244d414dbSJohannes Berg 42344d414dbSJohannes Berg skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); 424d15b8459SJoe Perches if (!skb) 42544d414dbSJohannes Berg return; 42644d414dbSJohannes Berg 42744d414dbSJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 428b080db58SJohannes Berg mgmt = skb_put_zero(skb, 24); 42944d414dbSJohannes Berg memcpy(mgmt->da, da, ETH_ALEN); 43047846c9bSJohannes Berg memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 4318abd3f9bSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP || 432ae2772b3SThomas Pedersen sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 433ae2772b3SThomas Pedersen sdata->vif.type == NL80211_IFTYPE_MESH_POINT) 43447846c9bSJohannes Berg memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 43546900298SJohannes Berg else if (sdata->vif.type == NL80211_IFTYPE_STATION) 436*bfd8403aSJohannes Berg memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN); 43713c40c54SAlexander Simon else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) 43813c40c54SAlexander Simon memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); 43946900298SJohannes Berg 44044d414dbSJohannes Berg mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 44144d414dbSJohannes Berg IEEE80211_STYPE_ACTION); 44244d414dbSJohannes Berg 44344d414dbSJohannes Berg skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); 44444d414dbSJohannes Berg 44544d414dbSJohannes Berg mgmt->u.action.category = WLAN_CATEGORY_BACK; 44644d414dbSJohannes Berg mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; 44744d414dbSJohannes Berg params = (u16)(initiator << 11); /* bit 11 initiator */ 44844d414dbSJohannes Berg params |= (u16)(tid << 12); /* bit 15:12 TID number */ 44944d414dbSJohannes Berg 45044d414dbSJohannes Berg mgmt->u.action.u.delba.params = cpu_to_le16(params); 45144d414dbSJohannes Berg mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); 45244d414dbSJohannes Berg 453c6e13327SKarl Beldan ieee80211_tx_skb(sdata, skb); 45444d414dbSJohannes Berg } 45544d414dbSJohannes Berg 456de1ede7aSJohannes Berg void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, 457de1ede7aSJohannes Berg struct sta_info *sta, 458de1ede7aSJohannes Berg struct ieee80211_mgmt *mgmt, size_t len) 459de1ede7aSJohannes Berg { 460de1ede7aSJohannes Berg u16 tid, params; 461de1ede7aSJohannes Berg u16 initiator; 462de1ede7aSJohannes Berg 463de1ede7aSJohannes Berg params = le16_to_cpu(mgmt->u.action.u.delba.params); 464de1ede7aSJohannes Berg tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; 465de1ede7aSJohannes Berg initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; 466de1ede7aSJohannes Berg 467bdcbd8e0SJohannes Berg ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", 468e87cc472SJoe Perches mgmt->sa, initiator ? "initiator" : "recipient", 469e87cc472SJoe Perches tid, 470372362adSJohannes Berg le16_to_cpu(mgmt->u.action.u.delba.reason_code)); 471de1ede7aSJohannes Berg 472de1ede7aSJohannes Berg if (initiator == WLAN_BACK_INITIATOR) 47353f73c09SJohannes Berg __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, 47453f73c09SJohannes Berg true); 475a622ab72SJohannes Berg else 476c82c4a80SJohannes Berg __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST); 477de1ede7aSJohannes Berg } 4780f78231bSJohannes Berg 47957566b20Stamizhr@codeaurora.org enum nl80211_smps_mode 48057566b20Stamizhr@codeaurora.org ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps) 48157566b20Stamizhr@codeaurora.org { 48257566b20Stamizhr@codeaurora.org switch (smps) { 48357566b20Stamizhr@codeaurora.org case IEEE80211_SMPS_OFF: 48457566b20Stamizhr@codeaurora.org return NL80211_SMPS_OFF; 48557566b20Stamizhr@codeaurora.org case IEEE80211_SMPS_STATIC: 48657566b20Stamizhr@codeaurora.org return NL80211_SMPS_STATIC; 48757566b20Stamizhr@codeaurora.org case IEEE80211_SMPS_DYNAMIC: 48857566b20Stamizhr@codeaurora.org return NL80211_SMPS_DYNAMIC; 48957566b20Stamizhr@codeaurora.org default: 49057566b20Stamizhr@codeaurora.org return NL80211_SMPS_OFF; 49157566b20Stamizhr@codeaurora.org } 49257566b20Stamizhr@codeaurora.org } 49357566b20Stamizhr@codeaurora.org 4940f78231bSJohannes Berg int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, 4950f78231bSJohannes Berg enum ieee80211_smps_mode smps, const u8 *da, 4960f78231bSJohannes Berg const u8 *bssid) 4970f78231bSJohannes Berg { 4980f78231bSJohannes Berg struct ieee80211_local *local = sdata->local; 4990f78231bSJohannes Berg struct sk_buff *skb; 5000f78231bSJohannes Berg struct ieee80211_mgmt *action_frame; 5010f78231bSJohannes Berg 5020f78231bSJohannes Berg /* 27 = header + category + action + smps mode */ 5030f78231bSJohannes Berg skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); 5040f78231bSJohannes Berg if (!skb) 5050f78231bSJohannes Berg return -ENOMEM; 5060f78231bSJohannes Berg 5070f78231bSJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 5084df864c1SJohannes Berg action_frame = skb_put(skb, 27); 5090f78231bSJohannes Berg memcpy(action_frame->da, da, ETH_ALEN); 5100f78231bSJohannes Berg memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN); 5110f78231bSJohannes Berg memcpy(action_frame->bssid, bssid, ETH_ALEN); 5120f78231bSJohannes Berg action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 5130f78231bSJohannes Berg IEEE80211_STYPE_ACTION); 5140f78231bSJohannes Berg action_frame->u.action.category = WLAN_CATEGORY_HT; 5150f78231bSJohannes Berg action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; 5160f78231bSJohannes Berg switch (smps) { 5170f78231bSJohannes Berg case IEEE80211_SMPS_AUTOMATIC: 5180f78231bSJohannes Berg case IEEE80211_SMPS_NUM_MODES: 5190f78231bSJohannes Berg WARN_ON(1); 520fc0561dcSGustavo A. R. Silva fallthrough; 5210f78231bSJohannes Berg case IEEE80211_SMPS_OFF: 5220f78231bSJohannes Berg action_frame->u.action.u.ht_smps.smps_control = 5230f78231bSJohannes Berg WLAN_HT_SMPS_CONTROL_DISABLED; 5240f78231bSJohannes Berg break; 5250f78231bSJohannes Berg case IEEE80211_SMPS_STATIC: 5260f78231bSJohannes Berg action_frame->u.action.u.ht_smps.smps_control = 5270f78231bSJohannes Berg WLAN_HT_SMPS_CONTROL_STATIC; 5280f78231bSJohannes Berg break; 5290f78231bSJohannes Berg case IEEE80211_SMPS_DYNAMIC: 5300f78231bSJohannes Berg action_frame->u.action.u.ht_smps.smps_control = 5310f78231bSJohannes Berg WLAN_HT_SMPS_CONTROL_DYNAMIC; 5320f78231bSJohannes Berg break; 5330f78231bSJohannes Berg } 5340f78231bSJohannes Berg 5350f78231bSJohannes Berg /* we'll do more on status of this frame */ 5360f78231bSJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 5370f78231bSJohannes Berg ieee80211_tx_skb(sdata, skb); 5380f78231bSJohannes Berg 5390f78231bSJohannes Berg return 0; 5400f78231bSJohannes Berg } 541d1f5b7a3SJohannes Berg 542687da132SEmmanuel Grumbach void ieee80211_request_smps_mgd_work(struct work_struct *work) 543d1f5b7a3SJohannes Berg { 544d1f5b7a3SJohannes Berg struct ieee80211_sub_if_data *sdata = 545d1f5b7a3SJohannes Berg container_of(work, struct ieee80211_sub_if_data, 546*bfd8403aSJohannes Berg deflink.u.mgd.request_smps_work); 547d1f5b7a3SJohannes Berg 5488d61ffa5SJohannes Berg sdata_lock(sdata); 549*bfd8403aSJohannes Berg __ieee80211_request_smps_mgd(sdata, 550*bfd8403aSJohannes Berg sdata->deflink.u.mgd.driver_smps_mode); 551687da132SEmmanuel Grumbach sdata_unlock(sdata); 552687da132SEmmanuel Grumbach } 553687da132SEmmanuel Grumbach 554d1f5b7a3SJohannes Berg void ieee80211_request_smps(struct ieee80211_vif *vif, 555d1f5b7a3SJohannes Berg enum ieee80211_smps_mode smps_mode) 556d1f5b7a3SJohannes Berg { 557d1f5b7a3SJohannes Berg struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 558d1f5b7a3SJohannes Berg 55910a35c22SJohannes Berg if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION)) 560d1f5b7a3SJohannes Berg return; 561d1f5b7a3SJohannes Berg 562*bfd8403aSJohannes Berg if (sdata->deflink.u.mgd.driver_smps_mode == smps_mode) 56366d57570SEmmanuel Grumbach return; 56410a35c22SJohannes Berg 565*bfd8403aSJohannes Berg sdata->deflink.u.mgd.driver_smps_mode = smps_mode; 566d1f5b7a3SJohannes Berg ieee80211_queue_work(&sdata->local->hw, 567*bfd8403aSJohannes Berg &sdata->deflink.u.mgd.request_smps_work); 568687da132SEmmanuel Grumbach } 569d1f5b7a3SJohannes Berg /* this might change ... don't want non-open drivers using it */ 570d1f5b7a3SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_request_smps); 571