18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HT handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 68c2ecf20Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc. 78c2ecf20Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 88c2ecf20Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 98c2ecf20Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 108c2ecf20Sopenharmony_ci * Copyright 2007-2010, Intel Corporation 118c2ecf20Sopenharmony_ci * Copyright 2017 Intel Deutschland GmbH 128c2ecf20Sopenharmony_ci * Copyright(c) 2020 Intel Corporation 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include <net/mac80211.h> 188c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 198c2ecf20Sopenharmony_ci#include "rate.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa, 228c2ecf20Sopenharmony_ci struct ieee80211_ht_cap *ht_capa_mask, 238c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 248c2ecf20Sopenharmony_ci u16 flag) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci __le16 le_flag = cpu_to_le16(flag); 278c2ecf20Sopenharmony_ci if (ht_capa_mask->cap_info & le_flag) { 288c2ecf20Sopenharmony_ci if (!(ht_capa->cap_info & le_flag)) 298c2ecf20Sopenharmony_ci ht_cap->cap &= ~flag; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, 348c2ecf20Sopenharmony_ci struct ieee80211_ht_cap *ht_capa_mask, 358c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap, 368c2ecf20Sopenharmony_ci u16 flag) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci __le16 le_flag = cpu_to_le16(flag); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if ((ht_capa_mask->cap_info & le_flag) && 418c2ecf20Sopenharmony_ci (ht_capa->cap_info & le_flag)) 428c2ecf20Sopenharmony_ci ht_cap->cap |= flag; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_civoid ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 468c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct ieee80211_ht_cap *ht_capa, *ht_capa_mask; 498c2ecf20Sopenharmony_ci u8 *scaps, *smask; 508c2ecf20Sopenharmony_ci int i; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!ht_cap->ht_supported) 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci switch (sdata->vif.type) { 568c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 578c2ecf20Sopenharmony_ci ht_capa = &sdata->u.mgd.ht_capa; 588c2ecf20Sopenharmony_ci ht_capa_mask = &sdata->u.mgd.ht_capa_mask; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 618c2ecf20Sopenharmony_ci ht_capa = &sdata->u.ibss.ht_capa; 628c2ecf20Sopenharmony_ci ht_capa_mask = &sdata->u.ibss.ht_capa_mask; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci default: 658c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 668c2ecf20Sopenharmony_ci return; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci scaps = (u8 *)(&ht_capa->mcs.rx_mask); 708c2ecf20Sopenharmony_ci smask = (u8 *)(&ht_capa_mask->mcs.rx_mask); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* NOTE: If you add more over-rides here, update register_hw 738c2ecf20Sopenharmony_ci * ht_capa_mod_mask logic in main.c as well. 748c2ecf20Sopenharmony_ci * And, if this method can ever change ht_cap.ht_supported, fix 758c2ecf20Sopenharmony_ci * the check in ieee80211_add_ht_ie. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* check for HT over-rides, MCS rates first. */ 798c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { 808c2ecf20Sopenharmony_ci u8 m = smask[i]; 818c2ecf20Sopenharmony_ci ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ 828c2ecf20Sopenharmony_ci /* Add back rates that are supported */ 838c2ecf20Sopenharmony_ci ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Force removal of HT-40 capabilities? */ 878c2ecf20Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 888c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SUP_WIDTH_20_40); 898c2ecf20Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 908c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Allow user to disable SGI-20 (SGI-40 is handled above) */ 938c2ecf20Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 948c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Allow user to disable the max-AMSDU bit. */ 978c2ecf20Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 988c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_MAX_AMSDU); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Allow user to disable LDPC */ 1018c2ecf20Sopenharmony_ci __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, 1028c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_LDPC_CODING); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Allow user to enable 40 MHz intolerant bit. */ 1058c2ecf20Sopenharmony_ci __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 1068c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_40MHZ_INTOLERANT); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Allow user to enable TX STBC bit */ 1098c2ecf20Sopenharmony_ci __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, 1108c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_TX_STBC); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Allow user to configure RX STBC bits */ 1138c2ecf20Sopenharmony_ci if (ht_capa_mask->cap_info & cpu_to_le16(IEEE80211_HT_CAP_RX_STBC)) 1148c2ecf20Sopenharmony_ci ht_cap->cap |= le16_to_cpu(ht_capa->cap_info) & 1158c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_RX_STBC; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Allow user to decrease AMPDU factor */ 1188c2ecf20Sopenharmony_ci if (ht_capa_mask->ampdu_params_info & 1198c2ecf20Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_FACTOR) { 1208c2ecf20Sopenharmony_ci u8 n = ht_capa->ampdu_params_info & 1218c2ecf20Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_FACTOR; 1228c2ecf20Sopenharmony_ci if (n < ht_cap->ampdu_factor) 1238c2ecf20Sopenharmony_ci ht_cap->ampdu_factor = n; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Allow the user to increase AMPDU density. */ 1278c2ecf20Sopenharmony_ci if (ht_capa_mask->ampdu_params_info & 1288c2ecf20Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY) { 1298c2ecf20Sopenharmony_ci u8 n = (ht_capa->ampdu_params_info & 1308c2ecf20Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY) 1318c2ecf20Sopenharmony_ci >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 1328c2ecf20Sopenharmony_ci if (n > ht_cap->ampdu_density) 1338c2ecf20Sopenharmony_ci ht_cap->ampdu_density = n; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cibool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 1398c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 1408c2ecf20Sopenharmony_ci const struct ieee80211_ht_cap *ht_cap_ie, 1418c2ecf20Sopenharmony_ci struct sta_info *sta) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap ht_cap, own_cap; 1448c2ecf20Sopenharmony_ci u8 ampdu_info, tx_mcs_set_cap; 1458c2ecf20Sopenharmony_ci int i, max_tx_streams; 1468c2ecf20Sopenharmony_ci bool changed; 1478c2ecf20Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci memset(&ht_cap, 0, sizeof(ht_cap)); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (!ht_cap_ie || !sband->ht_cap.ht_supported) 1528c2ecf20Sopenharmony_ci goto apply; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ht_cap.ht_supported = true; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci own_cap = sband->ht_cap; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * If user has specified capability over-rides, take care 1608c2ecf20Sopenharmony_ci * of that if the station we're setting up is the AP or TDLS peer that 1618c2ecf20Sopenharmony_ci * we advertised a restricted capability set to. Override 1628c2ecf20Sopenharmony_ci * our own capabilities and then use those below. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION || 1658c2ecf20Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_ADHOC) 1668c2ecf20Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &own_cap); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * The bits listed in this expression should be 1708c2ecf20Sopenharmony_ci * the same for the peer and us, if the station 1718c2ecf20Sopenharmony_ci * advertises more then we can't use those thus 1728c2ecf20Sopenharmony_ci * we mask them out. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & 1758c2ecf20Sopenharmony_ci (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | 1768c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SUP_WIDTH_20_40 | 1778c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_GRN_FLD | 1788c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20 | 1798c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40 | 1808c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_DSSSCCK40)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * The STBC bits are asymmetric -- if we don't have 1848c2ecf20Sopenharmony_ci * TX then mask out the peer's RX and vice versa. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC)) 1878c2ecf20Sopenharmony_ci ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; 1888c2ecf20Sopenharmony_ci if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC)) 1898c2ecf20Sopenharmony_ci ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ampdu_info = ht_cap_ie->ampdu_params_info; 1928c2ecf20Sopenharmony_ci ht_cap.ampdu_factor = 1938c2ecf20Sopenharmony_ci ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 1948c2ecf20Sopenharmony_ci ht_cap.ampdu_density = 1958c2ecf20Sopenharmony_ci (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* own MCS TX capabilities */ 1988c2ecf20Sopenharmony_ci tx_mcs_set_cap = own_cap.mcs.tx_params; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Copy peer MCS TX capabilities, the driver might need them. */ 2018c2ecf20Sopenharmony_ci ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* can we TX with MCS rates? */ 2048c2ecf20Sopenharmony_ci if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) 2058c2ecf20Sopenharmony_ci goto apply; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Counting from 0, therefore +1 */ 2088c2ecf20Sopenharmony_ci if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) 2098c2ecf20Sopenharmony_ci max_tx_streams = 2108c2ecf20Sopenharmony_ci ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 2118c2ecf20Sopenharmony_ci >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * 802.11n-2009 20.3.5 / 20.6 says: 2178c2ecf20Sopenharmony_ci * - indices 0 to 7 and 32 are single spatial stream 2188c2ecf20Sopenharmony_ci * - 8 to 31 are multiple spatial streams using equal modulation 2198c2ecf20Sopenharmony_ci * [8..15 for two streams, 16..23 for three and 24..31 for four] 2208c2ecf20Sopenharmony_ci * - remainder are multiple spatial streams using unequal modulation 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci for (i = 0; i < max_tx_streams; i++) 2238c2ecf20Sopenharmony_ci ht_cap.mcs.rx_mask[i] = 2248c2ecf20Sopenharmony_ci own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) 2278c2ecf20Sopenharmony_ci for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; 2288c2ecf20Sopenharmony_ci i < IEEE80211_HT_MCS_MASK_LEN; i++) 2298c2ecf20Sopenharmony_ci ht_cap.mcs.rx_mask[i] = 2308c2ecf20Sopenharmony_ci own_cap.mcs.rx_mask[i] & 2318c2ecf20Sopenharmony_ci ht_cap_ie->mcs.rx_mask[i]; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* handle MCS rate 32 too */ 2348c2ecf20Sopenharmony_ci if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) 2358c2ecf20Sopenharmony_ci ht_cap.mcs.rx_mask[32/8] |= 1; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* set Rx highest rate */ 2388c2ecf20Sopenharmony_ci ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) 2418c2ecf20Sopenharmony_ci sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; 2428c2ecf20Sopenharmony_ci else 2438c2ecf20Sopenharmony_ci sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci apply: 2468c2ecf20Sopenharmony_ci changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci switch (sdata->vif.bss_conf.chandef.width) { 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 2538c2ecf20Sopenharmony_ci fallthrough; 2548c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 2558c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 2568c2ecf20Sopenharmony_ci bw = IEEE80211_STA_RX_BW_20; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 2598c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 2608c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 2618c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 2628c2ecf20Sopenharmony_ci bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 2638c2ecf20Sopenharmony_ci IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci sta->sta.bandwidth = bw; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = 2708c2ecf20Sopenharmony_ci ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 2718c2ecf20Sopenharmony_ci IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (sta->sdata->vif.type == NL80211_IFTYPE_AP || 2748c2ecf20Sopenharmony_ci sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 2758c2ecf20Sopenharmony_ci enum ieee80211_smps_mode smps_mode; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) 2788c2ecf20Sopenharmony_ci >> IEEE80211_HT_CAP_SM_PS_SHIFT) { 2798c2ecf20Sopenharmony_ci case WLAN_HT_CAP_SM_PS_INVALID: 2808c2ecf20Sopenharmony_ci case WLAN_HT_CAP_SM_PS_STATIC: 2818c2ecf20Sopenharmony_ci smps_mode = IEEE80211_SMPS_STATIC; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case WLAN_HT_CAP_SM_PS_DYNAMIC: 2848c2ecf20Sopenharmony_ci smps_mode = IEEE80211_SMPS_DYNAMIC; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case WLAN_HT_CAP_SM_PS_DISABLED: 2878c2ecf20Sopenharmony_ci smps_mode = IEEE80211_SMPS_OFF; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (smps_mode != sta->sta.smps_mode) 2928c2ecf20Sopenharmony_ci changed = true; 2938c2ecf20Sopenharmony_ci sta->sta.smps_mode = smps_mode; 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci sta->sta.smps_mode = IEEE80211_SMPS_OFF; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci return changed; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_civoid ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, 3018c2ecf20Sopenharmony_ci enum ieee80211_agg_stop_reason reason) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int i; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 3068c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) 3078c2ecf20Sopenharmony_ci ___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, 3088c2ecf20Sopenharmony_ci WLAN_REASON_QSTA_LEAVE_QBSS, 3098c2ecf20Sopenharmony_ci reason != AGG_STOP_DESTROY_STA && 3108c2ecf20Sopenharmony_ci reason != AGG_STOP_PEER_REQUEST); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) 3138c2ecf20Sopenharmony_ci ___ieee80211_stop_tx_ba_session(sta, i, reason); 3148c2ecf20Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * In case the tear down is part of a reconfigure due to HW restart 3188c2ecf20Sopenharmony_ci * request, it is possible that the low level driver requested to stop 3198c2ecf20Sopenharmony_ci * the BA session, so handle it to properly clean tid_tx data. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci if(reason == AGG_STOP_DESTROY_STA) { 3228c2ecf20Sopenharmony_ci cancel_work_sync(&sta->ampdu_mlme.work); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 3258c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_TIDS; i++) { 3268c2ecf20Sopenharmony_ci struct tid_ampdu_tx *tid_tx = 3278c2ecf20Sopenharmony_ci rcu_dereference_protected_tid_tx(sta, i); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!tid_tx) 3308c2ecf20Sopenharmony_ci continue; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 3338c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb(sta, i, tid_tx); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_civoid ieee80211_ba_session_work(struct work_struct *work) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct sta_info *sta = 3428c2ecf20Sopenharmony_ci container_of(work, struct sta_info, ampdu_mlme.work); 3438c2ecf20Sopenharmony_ci struct tid_ampdu_tx *tid_tx; 3448c2ecf20Sopenharmony_ci bool blocked; 3458c2ecf20Sopenharmony_ci int tid; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* When this flag is set, new sessions should be blocked. */ 3488c2ecf20Sopenharmony_ci blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&sta->ampdu_mlme.mtx); 3518c2ecf20Sopenharmony_ci for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { 3528c2ecf20Sopenharmony_ci if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) 3538c2ecf20Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 3548c2ecf20Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 3558c2ecf20Sopenharmony_ci WLAN_REASON_QSTA_TIMEOUT, true); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (test_and_clear_bit(tid, 3588c2ecf20Sopenharmony_ci sta->ampdu_mlme.tid_rx_stop_requested)) 3598c2ecf20Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 3608c2ecf20Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 3618c2ecf20Sopenharmony_ci WLAN_REASON_UNSPECIFIED, true); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!blocked && 3648c2ecf20Sopenharmony_ci test_and_clear_bit(tid, 3658c2ecf20Sopenharmony_ci sta->ampdu_mlme.tid_rx_manage_offl)) 3668c2ecf20Sopenharmony_ci ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, 3678c2ecf20Sopenharmony_ci IEEE80211_MAX_AMPDU_BUF_HT, 3688c2ecf20Sopenharmony_ci false, true, NULL); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, 3718c2ecf20Sopenharmony_ci sta->ampdu_mlme.tid_rx_manage_offl)) 3728c2ecf20Sopenharmony_ci ___ieee80211_stop_rx_ba_session( 3738c2ecf20Sopenharmony_ci sta, tid, WLAN_BACK_RECIPIENT, 3748c2ecf20Sopenharmony_ci 0, false); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci spin_lock_bh(&sta->lock); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; 3798c2ecf20Sopenharmony_ci if (!blocked && tid_tx) { 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * Assign it over to the normal tid_tx array 3828c2ecf20Sopenharmony_ci * where it "goes live". 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci sta->ampdu_mlme.tid_start_tx[tid] = NULL; 3868c2ecf20Sopenharmony_ci /* could there be a race? */ 3878c2ecf20Sopenharmony_ci if (sta->ampdu_mlme.tid_tx[tid]) 3888c2ecf20Sopenharmony_ci kfree(tid_tx); 3898c2ecf20Sopenharmony_ci else 3908c2ecf20Sopenharmony_ci ieee80211_assign_tid_tx(sta, tid, tid_tx); 3918c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->lock); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ieee80211_tx_ba_session_handle_start(sta, tid); 3948c2ecf20Sopenharmony_ci continue; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->lock); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci tid_tx = rcu_dereference_protected_tid_tx(sta, tid); 3998c2ecf20Sopenharmony_ci if (!tid_tx) 4008c2ecf20Sopenharmony_ci continue; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!blocked && 4038c2ecf20Sopenharmony_ci test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) 4048c2ecf20Sopenharmony_ci ieee80211_start_tx_ba_cb(sta, tid, tid_tx); 4058c2ecf20Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) 4068c2ecf20Sopenharmony_ci ___ieee80211_stop_tx_ba_session(sta, tid, 4078c2ecf20Sopenharmony_ci AGG_STOP_LOCAL_REQUEST); 4088c2ecf20Sopenharmony_ci if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) 4098c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb(sta, tid, tid_tx); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci mutex_unlock(&sta->ampdu_mlme.mtx); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_civoid ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, 4158c2ecf20Sopenharmony_ci const u8 *da, u16 tid, 4168c2ecf20Sopenharmony_ci u16 initiator, u16 reason_code) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 4198c2ecf20Sopenharmony_ci struct sk_buff *skb; 4208c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 4218c2ecf20Sopenharmony_ci u16 params; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); 4248c2ecf20Sopenharmony_ci if (!skb) 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 4288c2ecf20Sopenharmony_ci mgmt = skb_put_zero(skb, 24); 4298c2ecf20Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 4308c2ecf20Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 4318c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 4328c2ecf20Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 4338c2ecf20Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_MESH_POINT) 4348c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 4358c2ecf20Sopenharmony_ci else if (sdata->vif.type == NL80211_IFTYPE_STATION) 4368c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); 4378c2ecf20Sopenharmony_ci else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) 4388c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 4418c2ecf20Sopenharmony_ci IEEE80211_STYPE_ACTION); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_BACK; 4468c2ecf20Sopenharmony_ci mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; 4478c2ecf20Sopenharmony_ci params = (u16)(initiator << 11); /* bit 11 initiator */ 4488c2ecf20Sopenharmony_ci params |= (u16)(tid << 12); /* bit 15:12 TID number */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci mgmt->u.action.u.delba.params = cpu_to_le16(params); 4518c2ecf20Sopenharmony_ci mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_civoid ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, 4578c2ecf20Sopenharmony_ci struct sta_info *sta, 4588c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci u16 tid, params; 4618c2ecf20Sopenharmony_ci u16 initiator; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci params = le16_to_cpu(mgmt->u.action.u.delba.params); 4648c2ecf20Sopenharmony_ci tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; 4658c2ecf20Sopenharmony_ci initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", 4688c2ecf20Sopenharmony_ci mgmt->sa, initiator ? "initiator" : "recipient", 4698c2ecf20Sopenharmony_ci tid, 4708c2ecf20Sopenharmony_ci le16_to_cpu(mgmt->u.action.u.delba.reason_code)); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (initiator == WLAN_BACK_INITIATOR) 4738c2ecf20Sopenharmony_ci __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, 4748c2ecf20Sopenharmony_ci true); 4758c2ecf20Sopenharmony_ci else 4768c2ecf20Sopenharmony_ci __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cienum nl80211_smps_mode 4808c2ecf20Sopenharmony_ciieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci switch (smps) { 4838c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 4848c2ecf20Sopenharmony_ci return NL80211_SMPS_OFF; 4858c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 4868c2ecf20Sopenharmony_ci return NL80211_SMPS_STATIC; 4878c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 4888c2ecf20Sopenharmony_ci return NL80211_SMPS_DYNAMIC; 4898c2ecf20Sopenharmony_ci default: 4908c2ecf20Sopenharmony_ci return NL80211_SMPS_OFF; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ciint ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, 4958c2ecf20Sopenharmony_ci enum ieee80211_smps_mode smps, const u8 *da, 4968c2ecf20Sopenharmony_ci const u8 *bssid) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 4998c2ecf20Sopenharmony_ci struct sk_buff *skb; 5008c2ecf20Sopenharmony_ci struct ieee80211_mgmt *action_frame; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 27 = header + category + action + smps mode */ 5038c2ecf20Sopenharmony_ci skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); 5048c2ecf20Sopenharmony_ci if (!skb) 5058c2ecf20Sopenharmony_ci return -ENOMEM; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 5088c2ecf20Sopenharmony_ci action_frame = skb_put(skb, 27); 5098c2ecf20Sopenharmony_ci memcpy(action_frame->da, da, ETH_ALEN); 5108c2ecf20Sopenharmony_ci memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN); 5118c2ecf20Sopenharmony_ci memcpy(action_frame->bssid, bssid, ETH_ALEN); 5128c2ecf20Sopenharmony_ci action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 5138c2ecf20Sopenharmony_ci IEEE80211_STYPE_ACTION); 5148c2ecf20Sopenharmony_ci action_frame->u.action.category = WLAN_CATEGORY_HT; 5158c2ecf20Sopenharmony_ci action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; 5168c2ecf20Sopenharmony_ci switch (smps) { 5178c2ecf20Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 5188c2ecf20Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 5198c2ecf20Sopenharmony_ci WARN_ON(1); 5208c2ecf20Sopenharmony_ci fallthrough; 5218c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 5228c2ecf20Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 5238c2ecf20Sopenharmony_ci WLAN_HT_SMPS_CONTROL_DISABLED; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 5268c2ecf20Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 5278c2ecf20Sopenharmony_ci WLAN_HT_SMPS_CONTROL_STATIC; 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 5308c2ecf20Sopenharmony_ci action_frame->u.action.u.ht_smps.smps_control = 5318c2ecf20Sopenharmony_ci WLAN_HT_SMPS_CONTROL_DYNAMIC; 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* we'll do more on status of this frame */ 5368c2ecf20Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 5378c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_civoid ieee80211_request_smps_mgd_work(struct work_struct *work) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 5458c2ecf20Sopenharmony_ci container_of(work, struct ieee80211_sub_if_data, 5468c2ecf20Sopenharmony_ci u.mgd.request_smps_work); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci sdata_lock(sdata); 5498c2ecf20Sopenharmony_ci __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); 5508c2ecf20Sopenharmony_ci sdata_unlock(sdata); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_civoid ieee80211_request_smps(struct ieee80211_vif *vif, 5548c2ecf20Sopenharmony_ci enum ieee80211_smps_mode smps_mode) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && 5598c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_AP)) 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION) { 5638c2ecf20Sopenharmony_ci if (sdata->u.mgd.driver_smps_mode == smps_mode) 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci sdata->u.mgd.driver_smps_mode = smps_mode; 5668c2ecf20Sopenharmony_ci ieee80211_queue_work(&sdata->local->hw, 5678c2ecf20Sopenharmony_ci &sdata->u.mgd.request_smps_work); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci/* this might change ... don't want non-open drivers using it */ 5718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_request_smps); 572