1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2c2d1560aSJohannes Berg /* 3c2d1560aSJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc. 4c2d1560aSJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc. 5c2d1560aSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 6c2d1560aSJohannes Berg * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 7d98ad83eSJohannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH 8da6a4352SJohannes Berg * Copyright (C) 2015-2017 Intel Deutschland GmbH 965be6aa3SJohannes Berg * Copyright (C) 2018-2021 Intel Corporation 10c2d1560aSJohannes Berg * 11c2d1560aSJohannes Berg * utilities for mac80211 12c2d1560aSJohannes Berg */ 13c2d1560aSJohannes Berg 14c2d1560aSJohannes Berg #include <net/mac80211.h> 15c2d1560aSJohannes Berg #include <linux/netdevice.h> 16bc3b2d7fSPaul Gortmaker #include <linux/export.h> 17c2d1560aSJohannes Berg #include <linux/types.h> 18c2d1560aSJohannes Berg #include <linux/slab.h> 19c2d1560aSJohannes Berg #include <linux/skbuff.h> 20c2d1560aSJohannes Berg #include <linux/etherdevice.h> 21c2d1560aSJohannes Berg #include <linux/if_arp.h> 22c2d1560aSJohannes Berg #include <linux/bitmap.h> 23dd76986bSJohannes Berg #include <linux/crc32.h> 24881d966bSEric W. Biederman #include <net/net_namespace.h> 25c2d1560aSJohannes Berg #include <net/cfg80211.h> 26dabeb344SJohannes Berg #include <net/rtnetlink.h> 27c2d1560aSJohannes Berg 28c2d1560aSJohannes Berg #include "ieee80211_i.h" 2924487981SJohannes Berg #include "driver-ops.h" 302c8dccc7SJohannes Berg #include "rate.h" 31ee385855SLuis Carlos Cobo #include "mesh.h" 32c2d1560aSJohannes Berg #include "wme.h" 33f2753ddbSJohannes Berg #include "led.h" 34fffd0934SJohannes Berg #include "wep.h" 35c2d1560aSJohannes Berg 36c2d1560aSJohannes Berg /* privid for wiphys to determine whether they belong to us or not */ 378a47cea7SJohannes Berg const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid; 38c2d1560aSJohannes Berg 399a95371aSLuis R. Rodriguez struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) 409a95371aSLuis R. Rodriguez { 419a95371aSLuis R. Rodriguez struct ieee80211_local *local; 429a95371aSLuis R. Rodriguez 439a95371aSLuis R. Rodriguez local = wiphy_priv(wiphy); 449a95371aSLuis R. Rodriguez return &local->hw; 459a95371aSLuis R. Rodriguez } 469a95371aSLuis R. Rodriguez EXPORT_SYMBOL(wiphy_to_ieee80211_hw); 47c2d1560aSJohannes Berg 4809a740ceSThomas Pedersen u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, 4909a740ceSThomas Pedersen enum nl80211_iftype type) 5009a740ceSThomas Pedersen { 5109a740ceSThomas Pedersen __le16 fc = hdr->frame_control; 5209a740ceSThomas Pedersen 5309a740ceSThomas Pedersen if (ieee80211_is_data(fc)) { 5409a740ceSThomas Pedersen if (len < 24) /* drop incorrect hdr len (data) */ 5509a740ceSThomas Pedersen return NULL; 5609a740ceSThomas Pedersen 5709a740ceSThomas Pedersen if (ieee80211_has_a4(fc)) 5809a740ceSThomas Pedersen return NULL; 5909a740ceSThomas Pedersen if (ieee80211_has_tods(fc)) 6009a740ceSThomas Pedersen return hdr->addr1; 6109a740ceSThomas Pedersen if (ieee80211_has_fromds(fc)) 6209a740ceSThomas Pedersen return hdr->addr2; 6309a740ceSThomas Pedersen 6409a740ceSThomas Pedersen return hdr->addr3; 6509a740ceSThomas Pedersen } 6609a740ceSThomas Pedersen 6709a740ceSThomas Pedersen if (ieee80211_is_s1g_beacon(fc)) { 6809a740ceSThomas Pedersen struct ieee80211_ext *ext = (void *) hdr; 6909a740ceSThomas Pedersen 7009a740ceSThomas Pedersen return ext->u.s1g_beacon.sa; 7109a740ceSThomas Pedersen } 7209a740ceSThomas Pedersen 7309a740ceSThomas Pedersen if (ieee80211_is_mgmt(fc)) { 7409a740ceSThomas Pedersen if (len < 24) /* drop incorrect hdr len (mgmt) */ 7509a740ceSThomas Pedersen return NULL; 7609a740ceSThomas Pedersen return hdr->addr3; 7709a740ceSThomas Pedersen } 7809a740ceSThomas Pedersen 7909a740ceSThomas Pedersen if (ieee80211_is_ctl(fc)) { 8009a740ceSThomas Pedersen if (ieee80211_is_pspoll(fc)) 8109a740ceSThomas Pedersen return hdr->addr1; 8209a740ceSThomas Pedersen 8309a740ceSThomas Pedersen if (ieee80211_is_back_req(fc)) { 8409a740ceSThomas Pedersen switch (type) { 8509a740ceSThomas Pedersen case NL80211_IFTYPE_STATION: 8609a740ceSThomas Pedersen return hdr->addr2; 8709a740ceSThomas Pedersen case NL80211_IFTYPE_AP: 8809a740ceSThomas Pedersen case NL80211_IFTYPE_AP_VLAN: 8909a740ceSThomas Pedersen return hdr->addr1; 9009a740ceSThomas Pedersen default: 9109a740ceSThomas Pedersen break; /* fall through to the return */ 9209a740ceSThomas Pedersen } 9309a740ceSThomas Pedersen } 9409a740ceSThomas Pedersen } 9509a740ceSThomas Pedersen 9609a740ceSThomas Pedersen return NULL; 9709a740ceSThomas Pedersen } 9809a740ceSThomas Pedersen EXPORT_SYMBOL(ieee80211_get_bssid); 9909a740ceSThomas Pedersen 1005cf121c3SJohannes Berg void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) 101c2d1560aSJohannes Berg { 102252b86c4SJohannes Berg struct sk_buff *skb; 1032de8e0d9SJohannes Berg struct ieee80211_hdr *hdr; 104c2d1560aSJohannes Berg 105252b86c4SJohannes Berg skb_queue_walk(&tx->skbs, skb) { 1062de8e0d9SJohannes Berg hdr = (struct ieee80211_hdr *) skb->data; 107c2d1560aSJohannes Berg hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 108252b86c4SJohannes Berg } 109c2d1560aSJohannes Berg } 110c2d1560aSJohannes Berg 11157fbcce3SJohannes Berg int ieee80211_frame_duration(enum nl80211_band band, size_t len, 112438b61b7SSimon Wunderlich int rate, int erp, int short_preamble, 113438b61b7SSimon Wunderlich int shift) 114c2d1560aSJohannes Berg { 115c2d1560aSJohannes Berg int dur; 116c2d1560aSJohannes Berg 117c2d1560aSJohannes Berg /* calculate duration (in microseconds, rounded up to next higher 118c2d1560aSJohannes Berg * integer if it includes a fractional microsecond) to send frame of 119c2d1560aSJohannes Berg * len bytes (does not include FCS) at the given rate. Duration will 120c2d1560aSJohannes Berg * also include SIFS. 121c2d1560aSJohannes Berg * 122c2d1560aSJohannes Berg * rate is in 100 kbps, so divident is multiplied by 10 in the 123c2d1560aSJohannes Berg * DIV_ROUND_UP() operations. 124438b61b7SSimon Wunderlich * 125438b61b7SSimon Wunderlich * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and 126438b61b7SSimon Wunderlich * is assumed to be 0 otherwise. 127c2d1560aSJohannes Berg */ 128c2d1560aSJohannes Berg 12957fbcce3SJohannes Berg if (band == NL80211_BAND_5GHZ || erp) { 130c2d1560aSJohannes Berg /* 131c2d1560aSJohannes Berg * OFDM: 132c2d1560aSJohannes Berg * 133c2d1560aSJohannes Berg * N_DBPS = DATARATE x 4 134c2d1560aSJohannes Berg * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) 135c2d1560aSJohannes Berg * (16 = SIGNAL time, 6 = tail bits) 136c2d1560aSJohannes Berg * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext 137c2d1560aSJohannes Berg * 138c2d1560aSJohannes Berg * T_SYM = 4 usec 139438b61b7SSimon Wunderlich * 802.11a - 18.5.2: aSIFSTime = 16 usec 140c2d1560aSJohannes Berg * 802.11g - 19.8.4: aSIFSTime = 10 usec + 141c2d1560aSJohannes Berg * signal ext = 6 usec 142c2d1560aSJohannes Berg */ 143c2d1560aSJohannes Berg dur = 16; /* SIFS + signal ext */ 144438b61b7SSimon Wunderlich dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ 145438b61b7SSimon Wunderlich dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ 146438b61b7SSimon Wunderlich 147438b61b7SSimon Wunderlich /* IEEE 802.11-2012 18.3.2.4: all values above are: 148438b61b7SSimon Wunderlich * * times 4 for 5 MHz 149438b61b7SSimon Wunderlich * * times 2 for 10 MHz 150438b61b7SSimon Wunderlich */ 151438b61b7SSimon Wunderlich dur *= 1 << shift; 1522103dec1SSimon Wunderlich 1532103dec1SSimon Wunderlich /* rates should already consider the channel bandwidth, 1542103dec1SSimon Wunderlich * don't apply divisor again. 1552103dec1SSimon Wunderlich */ 1562103dec1SSimon Wunderlich dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 1572103dec1SSimon Wunderlich 4 * rate); /* T_SYM x N_SYM */ 158c2d1560aSJohannes Berg } else { 159c2d1560aSJohannes Berg /* 160c2d1560aSJohannes Berg * 802.11b or 802.11g with 802.11b compatibility: 161c2d1560aSJohannes Berg * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + 162c2d1560aSJohannes Berg * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 163c2d1560aSJohannes Berg * 164c2d1560aSJohannes Berg * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 165c2d1560aSJohannes Berg * aSIFSTime = 10 usec 166c2d1560aSJohannes Berg * aPreambleLength = 144 usec or 72 usec with short preamble 167c2d1560aSJohannes Berg * aPLCPHeaderLength = 48 usec or 24 usec with short preamble 168c2d1560aSJohannes Berg */ 169c2d1560aSJohannes Berg dur = 10; /* aSIFSTime = 10 usec */ 170c2d1560aSJohannes Berg dur += short_preamble ? (72 + 24) : (144 + 48); 171c2d1560aSJohannes Berg 172c2d1560aSJohannes Berg dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); 173c2d1560aSJohannes Berg } 174c2d1560aSJohannes Berg 175c2d1560aSJohannes Berg return dur; 176c2d1560aSJohannes Berg } 177c2d1560aSJohannes Berg 178c2d1560aSJohannes Berg /* Exported duration function for driver use */ 17932bfd35dSJohannes Berg __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, 18032bfd35dSJohannes Berg struct ieee80211_vif *vif, 18157fbcce3SJohannes Berg enum nl80211_band band, 1828318d78aSJohannes Berg size_t frame_len, 1838318d78aSJohannes Berg struct ieee80211_rate *rate) 184c2d1560aSJohannes Berg { 18525d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 186c2d1560aSJohannes Berg u16 dur; 187438b61b7SSimon Wunderlich int erp, shift = 0; 18825d834e1SJohannes Berg bool short_preamble = false; 189c2d1560aSJohannes Berg 1908318d78aSJohannes Berg erp = 0; 19125d834e1SJohannes Berg if (vif) { 19225d834e1SJohannes Berg sdata = vif_to_sdata(vif); 193bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 1948318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 1958318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 196438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 19725d834e1SJohannes Berg } 1988318d78aSJohannes Berg 1994ee73f33SMichal Kazior dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, 200438b61b7SSimon Wunderlich short_preamble, shift); 201c2d1560aSJohannes Berg 202c2d1560aSJohannes Berg return cpu_to_le16(dur); 203c2d1560aSJohannes Berg } 204c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_generic_frame_duration); 205c2d1560aSJohannes Berg 20632bfd35dSJohannes Berg __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, 20732bfd35dSJohannes Berg struct ieee80211_vif *vif, size_t frame_len, 208e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 209c2d1560aSJohannes Berg { 210c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 211c2d1560aSJohannes Berg struct ieee80211_rate *rate; 21225d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 213471b3efdSJohannes Berg bool short_preamble; 2142103dec1SSimon Wunderlich int erp, shift = 0, bitrate; 215c2d1560aSJohannes Berg u16 dur; 2162e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2172e92e6f2SJohannes Berg 2184ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 219c2d1560aSJohannes Berg 22025d834e1SJohannes Berg short_preamble = false; 2217e9ed188SDaniel Drake 222e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2238318d78aSJohannes Berg 2248318d78aSJohannes Berg erp = 0; 22525d834e1SJohannes Berg if (vif) { 22625d834e1SJohannes Berg sdata = vif_to_sdata(vif); 227bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2288318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2298318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 230438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 23125d834e1SJohannes Berg } 232c2d1560aSJohannes Berg 2332103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2342103dec1SSimon Wunderlich 235c2d1560aSJohannes Berg /* CTS duration */ 2362103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, 10, bitrate, 237438b61b7SSimon Wunderlich erp, short_preamble, shift); 238c2d1560aSJohannes Berg /* Data frame duration */ 2392103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, 240438b61b7SSimon Wunderlich erp, short_preamble, shift); 241c2d1560aSJohannes Berg /* ACK duration */ 2422103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate, 243438b61b7SSimon Wunderlich erp, short_preamble, shift); 244c2d1560aSJohannes Berg 245c2d1560aSJohannes Berg return cpu_to_le16(dur); 246c2d1560aSJohannes Berg } 247c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_rts_duration); 248c2d1560aSJohannes Berg 24932bfd35dSJohannes Berg __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, 25032bfd35dSJohannes Berg struct ieee80211_vif *vif, 251c2d1560aSJohannes Berg size_t frame_len, 252e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 253c2d1560aSJohannes Berg { 254c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 255c2d1560aSJohannes Berg struct ieee80211_rate *rate; 25625d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 257471b3efdSJohannes Berg bool short_preamble; 2582103dec1SSimon Wunderlich int erp, shift = 0, bitrate; 259c2d1560aSJohannes Berg u16 dur; 2602e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2612e92e6f2SJohannes Berg 2624ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 263c2d1560aSJohannes Berg 26425d834e1SJohannes Berg short_preamble = false; 2657e9ed188SDaniel Drake 266e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2678318d78aSJohannes Berg erp = 0; 26825d834e1SJohannes Berg if (vif) { 26925d834e1SJohannes Berg sdata = vif_to_sdata(vif); 270bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2718318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2728318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 273438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 27425d834e1SJohannes Berg } 275c2d1560aSJohannes Berg 2762103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2772103dec1SSimon Wunderlich 278c2d1560aSJohannes Berg /* Data frame duration */ 2792103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, 280438b61b7SSimon Wunderlich erp, short_preamble, shift); 281e039fa4aSJohannes Berg if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { 282c2d1560aSJohannes Berg /* ACK duration */ 2832103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate, 284438b61b7SSimon Wunderlich erp, short_preamble, shift); 285c2d1560aSJohannes Berg } 286c2d1560aSJohannes Berg 287c2d1560aSJohannes Berg return cpu_to_le16(dur); 288c2d1560aSJohannes Berg } 289c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_ctstoself_duration); 290c2d1560aSJohannes Berg 29121a5d4c3SManikanta Pubbisetty static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) 29221a5d4c3SManikanta Pubbisetty { 29321a5d4c3SManikanta Pubbisetty struct ieee80211_local *local = sdata->local; 29421a5d4c3SManikanta Pubbisetty struct ieee80211_vif *vif = &sdata->vif; 29521a5d4c3SManikanta Pubbisetty struct fq *fq = &local->fq; 29621a5d4c3SManikanta Pubbisetty struct ps_data *ps = NULL; 29721a5d4c3SManikanta Pubbisetty struct txq_info *txqi; 29821a5d4c3SManikanta Pubbisetty struct sta_info *sta; 29921a5d4c3SManikanta Pubbisetty int i; 30021a5d4c3SManikanta Pubbisetty 301d8dec42bSJohannes Berg local_bh_disable(); 302d8dec42bSJohannes Berg spin_lock(&fq->lock); 30321a5d4c3SManikanta Pubbisetty 30421a5d4c3SManikanta Pubbisetty if (sdata->vif.type == NL80211_IFTYPE_AP) 30521a5d4c3SManikanta Pubbisetty ps = &sdata->bss->ps; 30621a5d4c3SManikanta Pubbisetty 30721a5d4c3SManikanta Pubbisetty sdata->vif.txqs_stopped[ac] = false; 30821a5d4c3SManikanta Pubbisetty 30921a5d4c3SManikanta Pubbisetty list_for_each_entry_rcu(sta, &local->sta_list, list) { 31021a5d4c3SManikanta Pubbisetty if (sdata != sta->sdata) 31121a5d4c3SManikanta Pubbisetty continue; 31221a5d4c3SManikanta Pubbisetty 31321a5d4c3SManikanta Pubbisetty for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { 31421a5d4c3SManikanta Pubbisetty struct ieee80211_txq *txq = sta->sta.txq[i]; 31521a5d4c3SManikanta Pubbisetty 316a5ae3264SErik Stromdahl if (!txq) 317a5ae3264SErik Stromdahl continue; 318a5ae3264SErik Stromdahl 31921a5d4c3SManikanta Pubbisetty txqi = to_txq_info(txq); 32021a5d4c3SManikanta Pubbisetty 32121a5d4c3SManikanta Pubbisetty if (ac != txq->ac) 32221a5d4c3SManikanta Pubbisetty continue; 32321a5d4c3SManikanta Pubbisetty 32421a5d4c3SManikanta Pubbisetty if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, 32521a5d4c3SManikanta Pubbisetty &txqi->flags)) 32621a5d4c3SManikanta Pubbisetty continue; 32721a5d4c3SManikanta Pubbisetty 328d8dec42bSJohannes Berg spin_unlock(&fq->lock); 32921a5d4c3SManikanta Pubbisetty drv_wake_tx_queue(local, txqi); 330d8dec42bSJohannes Berg spin_lock(&fq->lock); 33121a5d4c3SManikanta Pubbisetty } 33221a5d4c3SManikanta Pubbisetty } 33321a5d4c3SManikanta Pubbisetty 33421a5d4c3SManikanta Pubbisetty if (!vif->txq) 33521a5d4c3SManikanta Pubbisetty goto out; 33621a5d4c3SManikanta Pubbisetty 33721a5d4c3SManikanta Pubbisetty txqi = to_txq_info(vif->txq); 33821a5d4c3SManikanta Pubbisetty 33921a5d4c3SManikanta Pubbisetty if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) || 34021a5d4c3SManikanta Pubbisetty (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) 34121a5d4c3SManikanta Pubbisetty goto out; 34221a5d4c3SManikanta Pubbisetty 343d8dec42bSJohannes Berg spin_unlock(&fq->lock); 34421a5d4c3SManikanta Pubbisetty 34521a5d4c3SManikanta Pubbisetty drv_wake_tx_queue(local, txqi); 346d8dec42bSJohannes Berg local_bh_enable(); 34721a5d4c3SManikanta Pubbisetty return; 34821a5d4c3SManikanta Pubbisetty out: 349d8dec42bSJohannes Berg spin_unlock(&fq->lock); 350d8dec42bSJohannes Berg local_bh_enable(); 35121a5d4c3SManikanta Pubbisetty } 35221a5d4c3SManikanta Pubbisetty 353f6c7f03fSEmmanuel Grumbach static void 354f6c7f03fSEmmanuel Grumbach __releases(&local->queue_stop_reason_lock) 355f6c7f03fSEmmanuel Grumbach __acquires(&local->queue_stop_reason_lock) 356f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags) 35721a5d4c3SManikanta Pubbisetty { 35821a5d4c3SManikanta Pubbisetty struct ieee80211_sub_if_data *sdata; 35921a5d4c3SManikanta Pubbisetty int n_acs = IEEE80211_NUM_ACS; 36021a5d4c3SManikanta Pubbisetty int i; 36121a5d4c3SManikanta Pubbisetty 36221a5d4c3SManikanta Pubbisetty rcu_read_lock(); 36321a5d4c3SManikanta Pubbisetty 36421a5d4c3SManikanta Pubbisetty if (local->hw.queues < IEEE80211_NUM_ACS) 36521a5d4c3SManikanta Pubbisetty n_acs = 1; 36621a5d4c3SManikanta Pubbisetty 36721a5d4c3SManikanta Pubbisetty for (i = 0; i < local->hw.queues; i++) { 36821a5d4c3SManikanta Pubbisetty if (local->queue_stop_reasons[i]) 36921a5d4c3SManikanta Pubbisetty continue; 37021a5d4c3SManikanta Pubbisetty 371f6c7f03fSEmmanuel Grumbach spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags); 37221a5d4c3SManikanta Pubbisetty list_for_each_entry_rcu(sdata, &local->interfaces, list) { 37321a5d4c3SManikanta Pubbisetty int ac; 37421a5d4c3SManikanta Pubbisetty 37521a5d4c3SManikanta Pubbisetty for (ac = 0; ac < n_acs; ac++) { 37621a5d4c3SManikanta Pubbisetty int ac_queue = sdata->vif.hw_queue[ac]; 37721a5d4c3SManikanta Pubbisetty 37821a5d4c3SManikanta Pubbisetty if (ac_queue == i || 37921a5d4c3SManikanta Pubbisetty sdata->vif.cab_queue == i) 38021a5d4c3SManikanta Pubbisetty __ieee80211_wake_txqs(sdata, ac); 38121a5d4c3SManikanta Pubbisetty } 38221a5d4c3SManikanta Pubbisetty } 383f6c7f03fSEmmanuel Grumbach spin_lock_irqsave(&local->queue_stop_reason_lock, *flags); 38421a5d4c3SManikanta Pubbisetty } 38521a5d4c3SManikanta Pubbisetty 38621a5d4c3SManikanta Pubbisetty rcu_read_unlock(); 38721a5d4c3SManikanta Pubbisetty } 38821a5d4c3SManikanta Pubbisetty 389da1cad73SAllen Pais void ieee80211_wake_txqs(struct tasklet_struct *t) 390f6c7f03fSEmmanuel Grumbach { 391da1cad73SAllen Pais struct ieee80211_local *local = from_tasklet(local, t, 392da1cad73SAllen Pais wake_txqs_tasklet); 393f6c7f03fSEmmanuel Grumbach unsigned long flags; 394f6c7f03fSEmmanuel Grumbach 395f6c7f03fSEmmanuel Grumbach spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 396f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(local, &flags); 397f6c7f03fSEmmanuel Grumbach spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 398f6c7f03fSEmmanuel Grumbach } 399f6c7f03fSEmmanuel Grumbach 4003a25a8c8SJohannes Berg void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) 4013a25a8c8SJohannes Berg { 4023a25a8c8SJohannes Berg struct ieee80211_sub_if_data *sdata; 403a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 404a6f38ac3SJohannes Berg 40580a83cfcSMichal Kazior if (local->ops->wake_tx_queue) 40680a83cfcSMichal Kazior return; 40780a83cfcSMichal Kazior 408a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 409a6f38ac3SJohannes Berg n_acs = 1; 4103a25a8c8SJohannes Berg 4113a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 4123a25a8c8SJohannes Berg int ac; 4133a25a8c8SJohannes Berg 414f142c6b9SJohannes Berg if (!sdata->dev) 415f142c6b9SJohannes Berg continue; 416f142c6b9SJohannes Berg 4173a25a8c8SJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && 4183a25a8c8SJohannes Berg local->queue_stop_reasons[sdata->vif.cab_queue] != 0) 4193a25a8c8SJohannes Berg continue; 4203a25a8c8SJohannes Berg 421a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 4223a25a8c8SJohannes Berg int ac_queue = sdata->vif.hw_queue[ac]; 4233a25a8c8SJohannes Berg 4243a25a8c8SJohannes Berg if (ac_queue == queue || 4253a25a8c8SJohannes Berg (sdata->vif.cab_queue == queue && 4263a25a8c8SJohannes Berg local->queue_stop_reasons[ac_queue] == 0 && 4273a25a8c8SJohannes Berg skb_queue_empty(&local->pending[ac_queue]))) 4283a25a8c8SJohannes Berg netif_wake_subqueue(sdata->dev, ac); 4293a25a8c8SJohannes Berg } 4303a25a8c8SJohannes Berg } 4313a25a8c8SJohannes Berg } 4323a25a8c8SJohannes Berg 433ce7c9111SKalle Valo static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, 434cca07b00SLuciano Coelho enum queue_stop_reason reason, 435f6c7f03fSEmmanuel Grumbach bool refcounted, 436f6c7f03fSEmmanuel Grumbach unsigned long *flags) 437c2d1560aSJohannes Berg { 438c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 439c2d1560aSJohannes Berg 440b5878a2dSJohannes Berg trace_wake_queue(local, queue, reason); 441b5878a2dSJohannes Berg 442e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 44396f5e66eSJohannes Berg return; 44496f5e66eSJohannes Berg 445ada15125SJohannes Berg if (!test_bit(reason, &local->queue_stop_reasons[queue])) 446ada15125SJohannes Berg return; 447ada15125SJohannes Berg 448856142cdSJohannes Berg if (!refcounted) { 449cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason] = 0; 450856142cdSJohannes Berg } else { 451cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason]--; 452856142cdSJohannes Berg if (WARN_ON(local->q_stop_reasons[queue][reason] < 0)) 453856142cdSJohannes Berg local->q_stop_reasons[queue][reason] = 0; 454856142cdSJohannes Berg } 455cca07b00SLuciano Coelho 456cca07b00SLuciano Coelho if (local->q_stop_reasons[queue][reason] == 0) 457ce7c9111SKalle Valo __clear_bit(reason, &local->queue_stop_reasons[queue]); 458ce7c9111SKalle Valo 459ce7c9111SKalle Valo if (local->queue_stop_reasons[queue] != 0) 460ce7c9111SKalle Valo /* someone still has this queue stopped */ 461ce7c9111SKalle Valo return; 462ce7c9111SKalle Valo 4637236fe29SJohannes Berg if (skb_queue_empty(&local->pending[queue])) { 464cf0277e7SJohannes Berg rcu_read_lock(); 4653a25a8c8SJohannes Berg ieee80211_propagate_queue_wake(local, queue); 466cf0277e7SJohannes Berg rcu_read_unlock(); 4677236fe29SJohannes Berg } else 4687236fe29SJohannes Berg tasklet_schedule(&local->tx_pending_tasklet); 46921a5d4c3SManikanta Pubbisetty 470f6c7f03fSEmmanuel Grumbach /* 471f6c7f03fSEmmanuel Grumbach * Calling _ieee80211_wake_txqs here can be a problem because it may 472f6c7f03fSEmmanuel Grumbach * release queue_stop_reason_lock which has been taken by 473f6c7f03fSEmmanuel Grumbach * __ieee80211_wake_queue's caller. It is certainly not very nice to 474f6c7f03fSEmmanuel Grumbach * release someone's lock, but it is fine because all the callers of 475f6c7f03fSEmmanuel Grumbach * __ieee80211_wake_queue call it right before releasing the lock. 476f6c7f03fSEmmanuel Grumbach */ 477f6c7f03fSEmmanuel Grumbach if (local->ops->wake_tx_queue) { 478f6c7f03fSEmmanuel Grumbach if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) 47921a5d4c3SManikanta Pubbisetty tasklet_schedule(&local->wake_txqs_tasklet); 480f6c7f03fSEmmanuel Grumbach else 481f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(local, flags); 482f6c7f03fSEmmanuel Grumbach } 483c2d1560aSJohannes Berg } 484ce7c9111SKalle Valo 48596f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, 486cca07b00SLuciano Coelho enum queue_stop_reason reason, 487cca07b00SLuciano Coelho bool refcounted) 488ce7c9111SKalle Valo { 489ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 490ce7c9111SKalle Valo unsigned long flags; 491ce7c9111SKalle Valo 492ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 493f6c7f03fSEmmanuel Grumbach __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags); 494ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 495ce7c9111SKalle Valo } 496ce7c9111SKalle Valo 497ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) 498ce7c9111SKalle Valo { 499ce7c9111SKalle Valo ieee80211_wake_queue_by_reason(hw, queue, 500cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER, 501cca07b00SLuciano Coelho false); 502ce7c9111SKalle Valo } 503c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue); 504c2d1560aSJohannes Berg 505ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, 506cca07b00SLuciano Coelho enum queue_stop_reason reason, 507cca07b00SLuciano Coelho bool refcounted) 508c2d1560aSJohannes Berg { 509c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 510cf0277e7SJohannes Berg struct ieee80211_sub_if_data *sdata; 511a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 512c2d1560aSJohannes Berg 513b5878a2dSJohannes Berg trace_stop_queue(local, queue, reason); 514b5878a2dSJohannes Berg 515e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 51696f5e66eSJohannes Berg return; 51796f5e66eSJohannes Berg 518cca07b00SLuciano Coelho if (!refcounted) 519cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason] = 1; 520cca07b00SLuciano Coelho else 521cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason]++; 522ada15125SJohannes Berg 523cca07b00SLuciano Coelho if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue])) 524cca07b00SLuciano Coelho return; 525cf0277e7SJohannes Berg 526a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 527a6f38ac3SJohannes Berg n_acs = 1; 528a6f38ac3SJohannes Berg 529cf0277e7SJohannes Berg rcu_read_lock(); 5303a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5313a25a8c8SJohannes Berg int ac; 5323a25a8c8SJohannes Berg 533f142c6b9SJohannes Berg if (!sdata->dev) 534f142c6b9SJohannes Berg continue; 535f142c6b9SJohannes Berg 536a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 5373a25a8c8SJohannes Berg if (sdata->vif.hw_queue[ac] == queue || 53821a5d4c3SManikanta Pubbisetty sdata->vif.cab_queue == queue) { 53921a5d4c3SManikanta Pubbisetty if (!local->ops->wake_tx_queue) { 5403a25a8c8SJohannes Berg netif_stop_subqueue(sdata->dev, ac); 54121a5d4c3SManikanta Pubbisetty continue; 54221a5d4c3SManikanta Pubbisetty } 54321a5d4c3SManikanta Pubbisetty spin_lock(&local->fq.lock); 54421a5d4c3SManikanta Pubbisetty sdata->vif.txqs_stopped[ac] = true; 54521a5d4c3SManikanta Pubbisetty spin_unlock(&local->fq.lock); 54621a5d4c3SManikanta Pubbisetty } 5473a25a8c8SJohannes Berg } 5483a25a8c8SJohannes Berg } 549cf0277e7SJohannes Berg rcu_read_unlock(); 550c2d1560aSJohannes Berg } 551ce7c9111SKalle Valo 55296f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, 553cca07b00SLuciano Coelho enum queue_stop_reason reason, 554cca07b00SLuciano Coelho bool refcounted) 555ce7c9111SKalle Valo { 556ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 557ce7c9111SKalle Valo unsigned long flags; 558ce7c9111SKalle Valo 559ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 560cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, queue, reason, refcounted); 561ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 562ce7c9111SKalle Valo } 563ce7c9111SKalle Valo 564ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) 565ce7c9111SKalle Valo { 566ce7c9111SKalle Valo ieee80211_stop_queue_by_reason(hw, queue, 567cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER, 568cca07b00SLuciano Coelho false); 569ce7c9111SKalle Valo } 570c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue); 571c2d1560aSJohannes Berg 5728f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local, 5738f77f384SJohannes Berg struct sk_buff *skb) 5748f77f384SJohannes Berg { 5758f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 5768f77f384SJohannes Berg unsigned long flags; 577a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 5783a25a8c8SJohannes Berg int queue = info->hw_queue; 579a7bc376cSJohannes Berg 580a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 581d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 582a7bc376cSJohannes Berg return; 583a7bc376cSJohannes Berg } 5848f77f384SJohannes Berg 5858f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 586cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 587cca07b00SLuciano Coelho false); 5883b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 589cca07b00SLuciano Coelho __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 590f6c7f03fSEmmanuel Grumbach false, &flags); 5918f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 5928f77f384SJohannes Berg } 5938f77f384SJohannes Berg 594e3685e03SJohannes Berg void ieee80211_add_pending_skbs(struct ieee80211_local *local, 595e3685e03SJohannes Berg struct sk_buff_head *skbs) 5968f77f384SJohannes Berg { 5978f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 5988f77f384SJohannes Berg struct sk_buff *skb; 5998f77f384SJohannes Berg unsigned long flags; 600b0b97a8aSJohannes Berg int queue, i; 6018f77f384SJohannes Berg 6028f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6038f77f384SJohannes Berg while ((skb = skb_dequeue(skbs))) { 604a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 605a7bc376cSJohannes Berg 606a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 607d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 608a7bc376cSJohannes Berg continue; 609a7bc376cSJohannes Berg } 610a7bc376cSJohannes Berg 6113a25a8c8SJohannes Berg queue = info->hw_queue; 6124644ae89SJohannes Berg 6134644ae89SJohannes Berg __ieee80211_stop_queue(hw, queue, 614cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 615cca07b00SLuciano Coelho false); 6164644ae89SJohannes Berg 6173b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 6188f77f384SJohannes Berg } 6198f77f384SJohannes Berg 6203b8d81e0SJohannes Berg for (i = 0; i < hw->queues; i++) 6218f77f384SJohannes Berg __ieee80211_wake_queue(hw, i, 622cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 623f6c7f03fSEmmanuel Grumbach false, &flags); 6248f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6258f77f384SJohannes Berg } 6268f77f384SJohannes Berg 627ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 628445ea4e8SJohannes Berg unsigned long queues, 629cca07b00SLuciano Coelho enum queue_stop_reason reason, 630cca07b00SLuciano Coelho bool refcounted) 631c2d1560aSJohannes Berg { 632ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 633ce7c9111SKalle Valo unsigned long flags; 634c2d1560aSJohannes Berg int i; 635c2d1560aSJohannes Berg 636ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 637ce7c9111SKalle Valo 638445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 639cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, i, reason, refcounted); 640ce7c9111SKalle Valo 641ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 642ce7c9111SKalle Valo } 643ce7c9111SKalle Valo 644ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw) 645ce7c9111SKalle Valo { 646445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 647cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER, 648cca07b00SLuciano Coelho false); 649c2d1560aSJohannes Berg } 650c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues); 651c2d1560aSJohannes Berg 65292ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) 65392ab8535STomas Winkler { 65492ab8535STomas Winkler struct ieee80211_local *local = hw_to_local(hw); 6553b8d81e0SJohannes Berg unsigned long flags; 6563b8d81e0SJohannes Berg int ret; 65796f5e66eSJohannes Berg 658e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 65996f5e66eSJohannes Berg return true; 66096f5e66eSJohannes Berg 6613b8d81e0SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6622419ea14SThomas Pedersen ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, 6632419ea14SThomas Pedersen &local->queue_stop_reasons[queue]); 6643b8d81e0SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6653b8d81e0SJohannes Berg return ret; 66692ab8535STomas Winkler } 66792ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped); 66892ab8535STomas Winkler 669ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, 670445ea4e8SJohannes Berg unsigned long queues, 671cca07b00SLuciano Coelho enum queue_stop_reason reason, 672cca07b00SLuciano Coelho bool refcounted) 673c2d1560aSJohannes Berg { 674ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 675ce7c9111SKalle Valo unsigned long flags; 676c2d1560aSJohannes Berg int i; 677c2d1560aSJohannes Berg 678ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 679ce7c9111SKalle Valo 680445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 681f6c7f03fSEmmanuel Grumbach __ieee80211_wake_queue(hw, i, reason, refcounted, &flags); 682ce7c9111SKalle Valo 683ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 684ce7c9111SKalle Valo } 685ce7c9111SKalle Valo 686ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw) 687ce7c9111SKalle Valo { 688445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 689cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER, 690cca07b00SLuciano Coelho false); 691c2d1560aSJohannes Berg } 692c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues); 693dabeb344SJohannes Berg 69426da23b6SLuciano Coelho static unsigned int 69526da23b6SLuciano Coelho ieee80211_get_vif_queues(struct ieee80211_local *local, 69639ecc01dSJohannes Berg struct ieee80211_sub_if_data *sdata) 69739ecc01dSJohannes Berg { 69826da23b6SLuciano Coelho unsigned int queues; 69939ecc01dSJohannes Berg 70030686bf7SJohannes Berg if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) { 70139ecc01dSJohannes Berg int ac; 70239ecc01dSJohannes Berg 70339ecc01dSJohannes Berg queues = 0; 70439ecc01dSJohannes Berg 70539ecc01dSJohannes Berg for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 70639ecc01dSJohannes Berg queues |= BIT(sdata->vif.hw_queue[ac]); 70739ecc01dSJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) 70839ecc01dSJohannes Berg queues |= BIT(sdata->vif.cab_queue); 70939ecc01dSJohannes Berg } else { 71039ecc01dSJohannes Berg /* all queues */ 71139ecc01dSJohannes Berg queues = BIT(local->hw.queues) - 1; 71239ecc01dSJohannes Berg } 71339ecc01dSJohannes Berg 71426da23b6SLuciano Coelho return queues; 71526da23b6SLuciano Coelho } 71626da23b6SLuciano Coelho 7174f9610d5SLiad Kaufman void __ieee80211_flush_queues(struct ieee80211_local *local, 7184f9610d5SLiad Kaufman struct ieee80211_sub_if_data *sdata, 7193b24f4c6SEmmanuel Grumbach unsigned int queues, bool drop) 72026da23b6SLuciano Coelho { 72126da23b6SLuciano Coelho if (!local->ops->flush) 72226da23b6SLuciano Coelho return; 72326da23b6SLuciano Coelho 7244f9610d5SLiad Kaufman /* 7254f9610d5SLiad Kaufman * If no queue was set, or if the HW doesn't support 7264f9610d5SLiad Kaufman * IEEE80211_HW_QUEUE_CONTROL - flush all queues 7274f9610d5SLiad Kaufman */ 72830686bf7SJohannes Berg if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) 72926da23b6SLuciano Coelho queues = ieee80211_get_vif_queues(local, sdata); 73026da23b6SLuciano Coelho 73159f48fe2SLuciano Coelho ieee80211_stop_queues_by_reason(&local->hw, queues, 732cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_FLUSH, 733cca07b00SLuciano Coelho false); 734445ea4e8SJohannes Berg 7353b24f4c6SEmmanuel Grumbach drv_flush(local, sdata, queues, drop); 736445ea4e8SJohannes Berg 73759f48fe2SLuciano Coelho ieee80211_wake_queues_by_reason(&local->hw, queues, 738cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_FLUSH, 739cca07b00SLuciano Coelho false); 74039ecc01dSJohannes Berg } 74139ecc01dSJohannes Berg 7424f9610d5SLiad Kaufman void ieee80211_flush_queues(struct ieee80211_local *local, 7433b24f4c6SEmmanuel Grumbach struct ieee80211_sub_if_data *sdata, bool drop) 7444f9610d5SLiad Kaufman { 7453b24f4c6SEmmanuel Grumbach __ieee80211_flush_queues(local, sdata, 0, drop); 7464f9610d5SLiad Kaufman } 7474f9610d5SLiad Kaufman 74826da23b6SLuciano Coelho void ieee80211_stop_vif_queues(struct ieee80211_local *local, 74926da23b6SLuciano Coelho struct ieee80211_sub_if_data *sdata, 75026da23b6SLuciano Coelho enum queue_stop_reason reason) 75126da23b6SLuciano Coelho { 75226da23b6SLuciano Coelho ieee80211_stop_queues_by_reason(&local->hw, 75326da23b6SLuciano Coelho ieee80211_get_vif_queues(local, sdata), 75426da23b6SLuciano Coelho reason, true); 75526da23b6SLuciano Coelho } 75626da23b6SLuciano Coelho 75726da23b6SLuciano Coelho void ieee80211_wake_vif_queues(struct ieee80211_local *local, 75826da23b6SLuciano Coelho struct ieee80211_sub_if_data *sdata, 75926da23b6SLuciano Coelho enum queue_stop_reason reason) 76026da23b6SLuciano Coelho { 76126da23b6SLuciano Coelho ieee80211_wake_queues_by_reason(&local->hw, 76226da23b6SLuciano Coelho ieee80211_get_vif_queues(local, sdata), 76326da23b6SLuciano Coelho reason, true); 76426da23b6SLuciano Coelho } 76526da23b6SLuciano Coelho 7663384d757SArik Nemtsov static void __iterate_interfaces(struct ieee80211_local *local, 767c7c71066SJohannes Berg u32 iter_flags, 768dabeb344SJohannes Berg void (*iterator)(void *data, u8 *mac, 76932bfd35dSJohannes Berg struct ieee80211_vif *vif), 770dabeb344SJohannes Berg void *data) 771dabeb344SJohannes Berg { 772dabeb344SJohannes Berg struct ieee80211_sub_if_data *sdata; 7733384d757SArik Nemtsov bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE; 774dabeb344SJohannes Berg 775e38bad47SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 77651fb61e7SJohannes Berg switch (sdata->vif.type) { 77705c914feSJohannes Berg case NL80211_IFTYPE_MONITOR: 778d8212184SAviya Erenfeld if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) 77931eba5bcSFelix Fietkau continue; 78031eba5bcSFelix Fietkau break; 78105c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 782dabeb344SJohannes Berg continue; 7832ca27bcfSJohannes Berg default: 784dabeb344SJohannes Berg break; 785dabeb344SJohannes Berg } 7868b2c9824SJohannes Berg if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 7873384d757SArik Nemtsov active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 7888b2c9824SJohannes Berg continue; 789265a0708SBen Greear if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) && 790265a0708SBen Greear !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 791265a0708SBen Greear continue; 7923384d757SArik Nemtsov if (ieee80211_sdata_running(sdata) || !active_only) 79347846c9bSJohannes Berg iterator(data, sdata->vif.addr, 79432bfd35dSJohannes Berg &sdata->vif); 795dabeb344SJohannes Berg } 796e38bad47SJohannes Berg 797c7c71066SJohannes Berg sdata = rcu_dereference_check(local->monitor_sdata, 798c7c71066SJohannes Berg lockdep_is_held(&local->iflist_mtx) || 799c7c71066SJohannes Berg lockdep_rtnl_is_held()); 8008b2c9824SJohannes Berg if (sdata && 8013384d757SArik Nemtsov (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only || 8028b2c9824SJohannes Berg sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 803685fb72bSJohannes Berg iterator(data, sdata->vif.addr, &sdata->vif); 804c7c71066SJohannes Berg } 805685fb72bSJohannes Berg 8063384d757SArik Nemtsov void ieee80211_iterate_interfaces( 807c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 808c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 809c7c71066SJohannes Berg struct ieee80211_vif *vif), 810c7c71066SJohannes Berg void *data) 811c7c71066SJohannes Berg { 812c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 813c7c71066SJohannes Berg 814c7c71066SJohannes Berg mutex_lock(&local->iflist_mtx); 8153384d757SArik Nemtsov __iterate_interfaces(local, iter_flags, iterator, data); 816c7c71066SJohannes Berg mutex_unlock(&local->iflist_mtx); 817c7c71066SJohannes Berg } 8183384d757SArik Nemtsov EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces); 819c7c71066SJohannes Berg 820c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic( 821c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 822c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 823c7c71066SJohannes Berg struct ieee80211_vif *vif), 824c7c71066SJohannes Berg void *data) 825c7c71066SJohannes Berg { 826c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 827c7c71066SJohannes Berg 828c7c71066SJohannes Berg rcu_read_lock(); 8293384d757SArik Nemtsov __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 8303384d757SArik Nemtsov iterator, data); 831e38bad47SJohannes Berg rcu_read_unlock(); 832dabeb344SJohannes Berg } 8332f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 83437ffc8daSJohannes Berg 835a05829a7SJohannes Berg void ieee80211_iterate_active_interfaces_mtx( 836c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 837c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 838c7c71066SJohannes Berg struct ieee80211_vif *vif), 839c7c71066SJohannes Berg void *data) 840c7c71066SJohannes Berg { 841c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 842c7c71066SJohannes Berg 843a05829a7SJohannes Berg lockdep_assert_wiphy(hw->wiphy); 844c7c71066SJohannes Berg 8453384d757SArik Nemtsov __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 8463384d757SArik Nemtsov iterator, data); 847c7c71066SJohannes Berg } 848a05829a7SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx); 849c7c71066SJohannes Berg 8500fc1e049SArik Nemtsov static void __iterate_stations(struct ieee80211_local *local, 8510fc1e049SArik Nemtsov void (*iterator)(void *data, 8520fc1e049SArik Nemtsov struct ieee80211_sta *sta), 8530fc1e049SArik Nemtsov void *data) 8540fc1e049SArik Nemtsov { 8550fc1e049SArik Nemtsov struct sta_info *sta; 8560fc1e049SArik Nemtsov 8570fc1e049SArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 8580fc1e049SArik Nemtsov if (!sta->uploaded) 8590fc1e049SArik Nemtsov continue; 8600fc1e049SArik Nemtsov 8610fc1e049SArik Nemtsov iterator(data, &sta->sta); 8620fc1e049SArik Nemtsov } 8630fc1e049SArik Nemtsov } 8640fc1e049SArik Nemtsov 8650fc1e049SArik Nemtsov void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, 8660fc1e049SArik Nemtsov void (*iterator)(void *data, 8670fc1e049SArik Nemtsov struct ieee80211_sta *sta), 8680fc1e049SArik Nemtsov void *data) 8690fc1e049SArik Nemtsov { 8700fc1e049SArik Nemtsov struct ieee80211_local *local = hw_to_local(hw); 8710fc1e049SArik Nemtsov 8720fc1e049SArik Nemtsov rcu_read_lock(); 8730fc1e049SArik Nemtsov __iterate_stations(local, iterator, data); 8740fc1e049SArik Nemtsov rcu_read_unlock(); 8750fc1e049SArik Nemtsov } 8760fc1e049SArik Nemtsov EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic); 8770fc1e049SArik Nemtsov 878ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) 879ad7e718cSJohannes Berg { 880ad7e718cSJohannes Berg struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 881ad7e718cSJohannes Berg 882ad7e718cSJohannes Berg if (!ieee80211_sdata_running(sdata) || 883ad7e718cSJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 884ad7e718cSJohannes Berg return NULL; 885ad7e718cSJohannes Berg return &sdata->vif; 886ad7e718cSJohannes Berg } 887ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); 888ad7e718cSJohannes Berg 889dc5a1ad7SEmmanuel Grumbach struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) 890dc5a1ad7SEmmanuel Grumbach { 8916513e98eSJohannes Berg if (!vif) 8926513e98eSJohannes Berg return NULL; 8936513e98eSJohannes Berg 894f30386a8SEmmanuel Grumbach return &vif_to_sdata(vif)->wdev; 895dc5a1ad7SEmmanuel Grumbach } 896dc5a1ad7SEmmanuel Grumbach EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); 897dc5a1ad7SEmmanuel Grumbach 89842935ecaSLuis R. Rodriguez /* 89942935ecaSLuis R. Rodriguez * Nothing should have been stuffed into the workqueue during 9004afaff17SEmmanuel Grumbach * the suspend->resume cycle. Since we can't check each caller 9014afaff17SEmmanuel Grumbach * of this function if we are already quiescing / suspended, 9024afaff17SEmmanuel Grumbach * check here and don't WARN since this can actually happen when 9034afaff17SEmmanuel Grumbach * the rx path (for example) is racing against __ieee80211_suspend 9044afaff17SEmmanuel Grumbach * and suspending / quiescing was set after the rx path checked 9054afaff17SEmmanuel Grumbach * them. 90642935ecaSLuis R. Rodriguez */ 90742935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local) 90842935ecaSLuis R. Rodriguez { 9094afaff17SEmmanuel Grumbach if (local->quiescing || (local->suspended && !local->resuming)) { 9104afaff17SEmmanuel Grumbach pr_warn("queueing ieee80211 work while going to suspend\n"); 91142935ecaSLuis R. Rodriguez return false; 9124afaff17SEmmanuel Grumbach } 91342935ecaSLuis R. Rodriguez 91442935ecaSLuis R. Rodriguez return true; 91542935ecaSLuis R. Rodriguez } 91642935ecaSLuis R. Rodriguez 91742935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) 91842935ecaSLuis R. Rodriguez { 91942935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 92042935ecaSLuis R. Rodriguez 92142935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 92242935ecaSLuis R. Rodriguez return; 92342935ecaSLuis R. Rodriguez 92442935ecaSLuis R. Rodriguez queue_work(local->workqueue, work); 92542935ecaSLuis R. Rodriguez } 92642935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work); 92742935ecaSLuis R. Rodriguez 92842935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, 92942935ecaSLuis R. Rodriguez struct delayed_work *dwork, 93042935ecaSLuis R. Rodriguez unsigned long delay) 93142935ecaSLuis R. Rodriguez { 93242935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 93342935ecaSLuis R. Rodriguez 93442935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 93542935ecaSLuis R. Rodriguez return; 93642935ecaSLuis R. Rodriguez 93742935ecaSLuis R. Rodriguez queue_delayed_work(local->workqueue, dwork, delay); 93842935ecaSLuis R. Rodriguez } 93942935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work); 94042935ecaSLuis R. Rodriguez 941e4d005b8SJohannes Berg static void ieee80211_parse_extension_element(u32 *crc, 942e4d005b8SJohannes Berg const struct element *elem, 943e4d005b8SJohannes Berg struct ieee802_11_elems *elems) 944e4d005b8SJohannes Berg { 945e4d005b8SJohannes Berg const void *data = elem->data + 1; 946e4d005b8SJohannes Berg u8 len = elem->datalen - 1; 947e4d005b8SJohannes Berg 948e4d005b8SJohannes Berg switch (elem->data[0]) { 949e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_MU_EDCA: 950652e8363SJohannes Berg if (len >= sizeof(*elems->mu_edca_param_set)) { 951e4d005b8SJohannes Berg elems->mu_edca_param_set = data; 952e4d005b8SJohannes Berg if (crc) 953e4d005b8SJohannes Berg *crc = crc32_be(*crc, (void *)elem, 954e4d005b8SJohannes Berg elem->datalen + 2); 955e4d005b8SJohannes Berg } 956e4d005b8SJohannes Berg break; 957e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_CAPABILITY: 958e4d005b8SJohannes Berg elems->he_cap = data; 959e4d005b8SJohannes Berg elems->he_cap_len = len; 960e4d005b8SJohannes Berg break; 961e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_OPERATION: 962e4d005b8SJohannes Berg if (len >= sizeof(*elems->he_operation) && 9630f7e90faSBrian Norris len >= ieee80211_he_oper_size(data) - 1) { 964660d81daSJohannes Berg if (crc) 965660d81daSJohannes Berg *crc = crc32_be(*crc, (void *)elem, 966660d81daSJohannes Berg elem->datalen + 2); 967e4d005b8SJohannes Berg elems->he_operation = data; 968660d81daSJohannes Berg } 969e4d005b8SJohannes Berg break; 970e4d005b8SJohannes Berg case WLAN_EID_EXT_UORA: 971652e8363SJohannes Berg if (len >= 1) 972e4d005b8SJohannes Berg elems->uora_element = data; 973e4d005b8SJohannes Berg break; 974e4d005b8SJohannes Berg case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: 975e4d005b8SJohannes Berg if (len == 3) 976e4d005b8SJohannes Berg elems->max_channel_switch_time = data; 977e4d005b8SJohannes Berg break; 978e4d005b8SJohannes Berg case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: 979652e8363SJohannes Berg if (len >= sizeof(*elems->mbssid_config_ie)) 980e4d005b8SJohannes Berg elems->mbssid_config_ie = data; 981e4d005b8SJohannes Berg break; 982e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_SPR: 983e4d005b8SJohannes Berg if (len >= sizeof(*elems->he_spr) && 984e4d005b8SJohannes Berg len >= ieee80211_he_spr_size(data)) 985e4d005b8SJohannes Berg elems->he_spr = data; 986e4d005b8SJohannes Berg break; 987a6cf28e0SRajkumar Manoharan case WLAN_EID_EXT_HE_6GHZ_CAPA: 988652e8363SJohannes Berg if (len >= sizeof(*elems->he_6ghz_capa)) 989a6cf28e0SRajkumar Manoharan elems->he_6ghz_capa = data; 990a6cf28e0SRajkumar Manoharan break; 991e4d005b8SJohannes Berg } 992e4d005b8SJohannes Berg } 993e4d005b8SJohannes Berg 99478ac51f8SSara Sharon static u32 99578ac51f8SSara Sharon _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, 996dd76986bSJohannes Berg struct ieee802_11_elems *elems, 997671042a4SSara Sharon u64 filter, u32 crc, 998671042a4SSara Sharon const struct element *check_inherit) 999dd76986bSJohannes Berg { 1000671042a4SSara Sharon const struct element *elem; 1001dd76986bSJohannes Berg bool calc_crc = filter != 0; 1002fcff4f10SPaul Stewart DECLARE_BITMAP(seen_elems, 256); 1003b2e506bfSJohannes Berg const u8 *ie; 1004dd76986bSJohannes Berg 1005fcff4f10SPaul Stewart bitmap_zero(seen_elems, 256); 1006dd76986bSJohannes Berg 1007c17e28d1SJohannes Berg for_each_element(elem, start, len) { 1008fcff4f10SPaul Stewart bool elem_parse_failed; 1009c17e28d1SJohannes Berg u8 id = elem->id; 1010c17e28d1SJohannes Berg u8 elen = elem->datalen; 1011c17e28d1SJohannes Berg const u8 *pos = elem->data; 1012fcff4f10SPaul Stewart 1013671042a4SSara Sharon if (check_inherit && 1014671042a4SSara Sharon !cfg80211_is_element_inherited(elem, 1015671042a4SSara Sharon check_inherit)) 1016671042a4SSara Sharon continue; 1017671042a4SSara Sharon 10189690fb16SJohannes Berg switch (id) { 10199690fb16SJohannes Berg case WLAN_EID_SSID: 10209690fb16SJohannes Berg case WLAN_EID_SUPP_RATES: 10219690fb16SJohannes Berg case WLAN_EID_FH_PARAMS: 10229690fb16SJohannes Berg case WLAN_EID_DS_PARAMS: 10239690fb16SJohannes Berg case WLAN_EID_CF_PARAMS: 10249690fb16SJohannes Berg case WLAN_EID_TIM: 10259690fb16SJohannes Berg case WLAN_EID_IBSS_PARAMS: 10269690fb16SJohannes Berg case WLAN_EID_CHALLENGE: 10279690fb16SJohannes Berg case WLAN_EID_RSN: 10289690fb16SJohannes Berg case WLAN_EID_ERP_INFO: 10299690fb16SJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 10309690fb16SJohannes Berg case WLAN_EID_HT_CAPABILITY: 10319690fb16SJohannes Berg case WLAN_EID_HT_OPERATION: 10329690fb16SJohannes Berg case WLAN_EID_VHT_CAPABILITY: 10339690fb16SJohannes Berg case WLAN_EID_VHT_OPERATION: 10349690fb16SJohannes Berg case WLAN_EID_MESH_ID: 10359690fb16SJohannes Berg case WLAN_EID_MESH_CONFIG: 10369690fb16SJohannes Berg case WLAN_EID_PEER_MGMT: 10379690fb16SJohannes Berg case WLAN_EID_PREQ: 10389690fb16SJohannes Berg case WLAN_EID_PREP: 10399690fb16SJohannes Berg case WLAN_EID_PERR: 10409690fb16SJohannes Berg case WLAN_EID_RANN: 10419690fb16SJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 10429690fb16SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 10439690fb16SJohannes Berg case WLAN_EID_COUNTRY: 10449690fb16SJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 10459690fb16SJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 104685220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 1047b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 10488f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM: 10499041c1faSArik Nemtsov case WLAN_EID_EXT_CAPABILITY: 105053837584SArik Nemtsov case WLAN_EID_CHAN_SWITCH_TIMING: 105153837584SArik Nemtsov case WLAN_EID_LINK_ID: 1052e38a017bSAvraham Stern case WLAN_EID_BSS_MAX_IDLE_PERIOD: 1053c0058df7SShaul Triebitz case WLAN_EID_RSNX: 1054cd418ba6SThomas Pedersen case WLAN_EID_S1G_BCN_COMPAT: 1055cd418ba6SThomas Pedersen case WLAN_EID_S1G_CAPABILITIES: 1056cd418ba6SThomas Pedersen case WLAN_EID_S1G_OPERATION: 10571d00ce80SThomas Pedersen case WLAN_EID_AID_RESPONSE: 1058cd418ba6SThomas Pedersen case WLAN_EID_S1G_SHORT_BCN_INTERVAL: 1059b2e506bfSJohannes Berg /* 1060b2e506bfSJohannes Berg * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 1061b2e506bfSJohannes Berg * that if the content gets bigger it might be needed more than once 1062b2e506bfSJohannes Berg */ 10639690fb16SJohannes Berg if (test_bit(id, seen_elems)) { 1064fcff4f10SPaul Stewart elems->parse_error = true; 1065fcff4f10SPaul Stewart continue; 1066fcff4f10SPaul Stewart } 10679690fb16SJohannes Berg break; 10689690fb16SJohannes Berg } 1069dd76986bSJohannes Berg 1070dd76986bSJohannes Berg if (calc_crc && id < 64 && (filter & (1ULL << id))) 1071dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 1072dd76986bSJohannes Berg 1073fcff4f10SPaul Stewart elem_parse_failed = false; 1074fcff4f10SPaul Stewart 1075dd76986bSJohannes Berg switch (id) { 107653837584SArik Nemtsov case WLAN_EID_LINK_ID: 1077652e8363SJohannes Berg if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { 107853837584SArik Nemtsov elem_parse_failed = true; 107953837584SArik Nemtsov break; 108053837584SArik Nemtsov } 108153837584SArik Nemtsov elems->lnk_id = (void *)(pos - 2); 108253837584SArik Nemtsov break; 108353837584SArik Nemtsov case WLAN_EID_CHAN_SWITCH_TIMING: 1084652e8363SJohannes Berg if (elen < sizeof(struct ieee80211_ch_switch_timing)) { 108553837584SArik Nemtsov elem_parse_failed = true; 108653837584SArik Nemtsov break; 108753837584SArik Nemtsov } 108853837584SArik Nemtsov elems->ch_sw_timing = (void *)pos; 108953837584SArik Nemtsov break; 10909041c1faSArik Nemtsov case WLAN_EID_EXT_CAPABILITY: 10919041c1faSArik Nemtsov elems->ext_capab = pos; 10929041c1faSArik Nemtsov elems->ext_capab_len = elen; 10939041c1faSArik Nemtsov break; 1094dd76986bSJohannes Berg case WLAN_EID_SSID: 1095dd76986bSJohannes Berg elems->ssid = pos; 1096dd76986bSJohannes Berg elems->ssid_len = elen; 1097dd76986bSJohannes Berg break; 1098dd76986bSJohannes Berg case WLAN_EID_SUPP_RATES: 1099dd76986bSJohannes Berg elems->supp_rates = pos; 1100dd76986bSJohannes Berg elems->supp_rates_len = elen; 1101dd76986bSJohannes Berg break; 1102dd76986bSJohannes Berg case WLAN_EID_DS_PARAMS: 11031cd8e88eSJohannes Berg if (elen >= 1) 1104dd76986bSJohannes Berg elems->ds_params = pos; 11051cd8e88eSJohannes Berg else 11061cd8e88eSJohannes Berg elem_parse_failed = true; 1107dd76986bSJohannes Berg break; 1108dd76986bSJohannes Berg case WLAN_EID_TIM: 1109dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_tim_ie)) { 1110dd76986bSJohannes Berg elems->tim = (void *)pos; 1111dd76986bSJohannes Berg elems->tim_len = elen; 1112fcff4f10SPaul Stewart } else 1113fcff4f10SPaul Stewart elem_parse_failed = true; 1114dd76986bSJohannes Berg break; 1115dd76986bSJohannes Berg case WLAN_EID_CHALLENGE: 1116dd76986bSJohannes Berg elems->challenge = pos; 1117dd76986bSJohannes Berg elems->challenge_len = elen; 1118dd76986bSJohannes Berg break; 1119dd76986bSJohannes Berg case WLAN_EID_VENDOR_SPECIFIC: 1120dd76986bSJohannes Berg if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && 1121dd76986bSJohannes Berg pos[2] == 0xf2) { 1122dd76986bSJohannes Berg /* Microsoft OUI (00:50:F2) */ 1123dd76986bSJohannes Berg 1124dd76986bSJohannes Berg if (calc_crc) 1125dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 1126dd76986bSJohannes Berg 1127441a33baSJohannes Berg if (elen >= 5 && pos[3] == 2) { 1128dd76986bSJohannes Berg /* OUI Type 2 - WMM IE */ 1129dd76986bSJohannes Berg if (pos[4] == 0) { 1130dd76986bSJohannes Berg elems->wmm_info = pos; 1131dd76986bSJohannes Berg elems->wmm_info_len = elen; 1132dd76986bSJohannes Berg } else if (pos[4] == 1) { 1133dd76986bSJohannes Berg elems->wmm_param = pos; 1134dd76986bSJohannes Berg elems->wmm_param_len = elen; 1135dd76986bSJohannes Berg } 1136dd76986bSJohannes Berg } 1137dd76986bSJohannes Berg } 1138dd76986bSJohannes Berg break; 1139dd76986bSJohannes Berg case WLAN_EID_RSN: 1140dd76986bSJohannes Berg elems->rsn = pos; 1141dd76986bSJohannes Berg elems->rsn_len = elen; 1142dd76986bSJohannes Berg break; 1143dd76986bSJohannes Berg case WLAN_EID_ERP_INFO: 11441946bed9SJohannes Berg if (elen >= 1) 1145dd76986bSJohannes Berg elems->erp_info = pos; 11461946bed9SJohannes Berg else 11471946bed9SJohannes Berg elem_parse_failed = true; 1148dd76986bSJohannes Berg break; 1149dd76986bSJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 1150dd76986bSJohannes Berg elems->ext_supp_rates = pos; 1151dd76986bSJohannes Berg elems->ext_supp_rates_len = elen; 1152dd76986bSJohannes Berg break; 1153dd76986bSJohannes Berg case WLAN_EID_HT_CAPABILITY: 1154dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_ht_cap)) 1155dd76986bSJohannes Berg elems->ht_cap_elem = (void *)pos; 1156fcff4f10SPaul Stewart else 1157fcff4f10SPaul Stewart elem_parse_failed = true; 1158dd76986bSJohannes Berg break; 1159074d46d1SJohannes Berg case WLAN_EID_HT_OPERATION: 1160074d46d1SJohannes Berg if (elen >= sizeof(struct ieee80211_ht_operation)) 1161074d46d1SJohannes Berg elems->ht_operation = (void *)pos; 1162fcff4f10SPaul Stewart else 1163fcff4f10SPaul Stewart elem_parse_failed = true; 1164dd76986bSJohannes Berg break; 1165818255eaSMahesh Palivela case WLAN_EID_VHT_CAPABILITY: 1166818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_cap)) 1167818255eaSMahesh Palivela elems->vht_cap_elem = (void *)pos; 1168818255eaSMahesh Palivela else 1169818255eaSMahesh Palivela elem_parse_failed = true; 1170818255eaSMahesh Palivela break; 1171818255eaSMahesh Palivela case WLAN_EID_VHT_OPERATION: 1172a04564c9SJohannes Berg if (elen >= sizeof(struct ieee80211_vht_operation)) { 1173818255eaSMahesh Palivela elems->vht_operation = (void *)pos; 1174a04564c9SJohannes Berg if (calc_crc) 1175a04564c9SJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 1176a04564c9SJohannes Berg break; 1177a04564c9SJohannes Berg } 1178818255eaSMahesh Palivela elem_parse_failed = true; 1179818255eaSMahesh Palivela break; 1180bee7f586SJohannes Berg case WLAN_EID_OPMODE_NOTIF: 1181a04564c9SJohannes Berg if (elen > 0) { 1182bee7f586SJohannes Berg elems->opmode_notif = pos; 1183a04564c9SJohannes Berg if (calc_crc) 1184a04564c9SJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 1185a04564c9SJohannes Berg break; 1186a04564c9SJohannes Berg } 1187bee7f586SJohannes Berg elem_parse_failed = true; 1188bee7f586SJohannes Berg break; 1189dd76986bSJohannes Berg case WLAN_EID_MESH_ID: 1190dd76986bSJohannes Berg elems->mesh_id = pos; 1191dd76986bSJohannes Berg elems->mesh_id_len = elen; 1192dd76986bSJohannes Berg break; 1193dd76986bSJohannes Berg case WLAN_EID_MESH_CONFIG: 1194dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_meshconf_ie)) 1195dd76986bSJohannes Berg elems->mesh_config = (void *)pos; 1196fcff4f10SPaul Stewart else 1197fcff4f10SPaul Stewart elem_parse_failed = true; 1198dd76986bSJohannes Berg break; 1199dd76986bSJohannes Berg case WLAN_EID_PEER_MGMT: 1200dd76986bSJohannes Berg elems->peering = pos; 1201dd76986bSJohannes Berg elems->peering_len = elen; 1202dd76986bSJohannes Berg break; 12033f52b7e3SMarco Porsch case WLAN_EID_MESH_AWAKE_WINDOW: 12043f52b7e3SMarco Porsch if (elen >= 2) 12053f52b7e3SMarco Porsch elems->awake_window = (void *)pos; 12063f52b7e3SMarco Porsch break; 1207dd76986bSJohannes Berg case WLAN_EID_PREQ: 1208dd76986bSJohannes Berg elems->preq = pos; 1209dd76986bSJohannes Berg elems->preq_len = elen; 1210dd76986bSJohannes Berg break; 1211dd76986bSJohannes Berg case WLAN_EID_PREP: 1212dd76986bSJohannes Berg elems->prep = pos; 1213dd76986bSJohannes Berg elems->prep_len = elen; 1214dd76986bSJohannes Berg break; 1215dd76986bSJohannes Berg case WLAN_EID_PERR: 1216dd76986bSJohannes Berg elems->perr = pos; 1217dd76986bSJohannes Berg elems->perr_len = elen; 1218dd76986bSJohannes Berg break; 1219dd76986bSJohannes Berg case WLAN_EID_RANN: 1220dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_rann_ie)) 1221dd76986bSJohannes Berg elems->rann = (void *)pos; 1222fcff4f10SPaul Stewart else 1223fcff4f10SPaul Stewart elem_parse_failed = true; 1224dd76986bSJohannes Berg break; 1225dd76986bSJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 12265bc1420bSJohannes Berg if (elen != sizeof(struct ieee80211_channel_sw_ie)) { 12275bc1420bSJohannes Berg elem_parse_failed = true; 12285bc1420bSJohannes Berg break; 12295bc1420bSJohannes Berg } 12305bc1420bSJohannes Berg elems->ch_switch_ie = (void *)pos; 1231dd76986bSJohannes Berg break; 1232b4f286a1SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 1233b4f286a1SJohannes Berg if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { 1234b4f286a1SJohannes Berg elem_parse_failed = true; 1235b4f286a1SJohannes Berg break; 1236b4f286a1SJohannes Berg } 1237b4f286a1SJohannes Berg elems->ext_chansw_ie = (void *)pos; 1238b4f286a1SJohannes Berg break; 123985220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 124085220d71SJohannes Berg if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { 124185220d71SJohannes Berg elem_parse_failed = true; 124285220d71SJohannes Berg break; 124385220d71SJohannes Berg } 124485220d71SJohannes Berg elems->sec_chan_offs = (void *)pos; 124585220d71SJohannes Berg break; 12468f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM: 1247652e8363SJohannes Berg if (elen < 12488f2535b9SChun-Yeow Yeoh sizeof(*elems->mesh_chansw_params_ie)) { 12498f2535b9SChun-Yeow Yeoh elem_parse_failed = true; 12508f2535b9SChun-Yeow Yeoh break; 12518f2535b9SChun-Yeow Yeoh } 12528f2535b9SChun-Yeow Yeoh elems->mesh_chansw_params_ie = (void *)pos; 12538f2535b9SChun-Yeow Yeoh break; 1254b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 1255b2e506bfSJohannes Berg if (!action || 1256652e8363SJohannes Berg elen < sizeof(*elems->wide_bw_chansw_ie)) { 1257b2e506bfSJohannes Berg elem_parse_failed = true; 1258b2e506bfSJohannes Berg break; 1259b2e506bfSJohannes Berg } 1260b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = (void *)pos; 1261b2e506bfSJohannes Berg break; 1262b2e506bfSJohannes Berg case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 1263b2e506bfSJohannes Berg if (action) { 1264b2e506bfSJohannes Berg elem_parse_failed = true; 1265b2e506bfSJohannes Berg break; 1266b2e506bfSJohannes Berg } 1267b2e506bfSJohannes Berg /* 1268b2e506bfSJohannes Berg * This is a bit tricky, but as we only care about 1269b2e506bfSJohannes Berg * the wide bandwidth channel switch element, so 1270b2e506bfSJohannes Berg * just parse it out manually. 1271b2e506bfSJohannes Berg */ 1272b2e506bfSJohannes Berg ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 1273b2e506bfSJohannes Berg pos, elen); 1274b2e506bfSJohannes Berg if (ie) { 1275652e8363SJohannes Berg if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) 1276b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = 1277b2e506bfSJohannes Berg (void *)(ie + 2); 1278b2e506bfSJohannes Berg else 1279b2e506bfSJohannes Berg elem_parse_failed = true; 1280b2e506bfSJohannes Berg } 1281b2e506bfSJohannes Berg break; 1282dd76986bSJohannes Berg case WLAN_EID_COUNTRY: 1283dd76986bSJohannes Berg elems->country_elem = pos; 1284dd76986bSJohannes Berg elems->country_elem_len = elen; 1285dd76986bSJohannes Berg break; 1286dd76986bSJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 1287761a48d2SJohannes Berg if (elen != 1) { 1288761a48d2SJohannes Berg elem_parse_failed = true; 1289761a48d2SJohannes Berg break; 1290761a48d2SJohannes Berg } 1291dd76986bSJohannes Berg elems->pwr_constr_elem = pos; 1292dd76986bSJohannes Berg break; 1293c8d65917SSteinar H. Gunderson case WLAN_EID_CISCO_VENDOR_SPECIFIC: 1294c8d65917SSteinar H. Gunderson /* Lots of different options exist, but we only care 1295c8d65917SSteinar H. Gunderson * about the Dynamic Transmit Power Control element. 1296c8d65917SSteinar H. Gunderson * First check for the Cisco OUI, then for the DTPC 1297c8d65917SSteinar H. Gunderson * tag (0x00). 1298c8d65917SSteinar H. Gunderson */ 1299c8d65917SSteinar H. Gunderson if (elen < 4) { 1300c8d65917SSteinar H. Gunderson elem_parse_failed = true; 1301c8d65917SSteinar H. Gunderson break; 1302c8d65917SSteinar H. Gunderson } 1303c8d65917SSteinar H. Gunderson 1304c8d65917SSteinar H. Gunderson if (pos[0] != 0x00 || pos[1] != 0x40 || 1305c8d65917SSteinar H. Gunderson pos[2] != 0x96 || pos[3] != 0x00) 1306c8d65917SSteinar H. Gunderson break; 1307c8d65917SSteinar H. Gunderson 1308c8d65917SSteinar H. Gunderson if (elen != 6) { 1309c8d65917SSteinar H. Gunderson elem_parse_failed = true; 1310c8d65917SSteinar H. Gunderson break; 1311c8d65917SSteinar H. Gunderson } 1312c8d65917SSteinar H. Gunderson 1313c8d65917SSteinar H. Gunderson if (calc_crc) 1314c8d65917SSteinar H. Gunderson crc = crc32_be(crc, pos - 2, elen + 2); 1315c8d65917SSteinar H. Gunderson 1316c8d65917SSteinar H. Gunderson elems->cisco_dtpc_elem = pos; 1317c8d65917SSteinar H. Gunderson break; 13182aa485e1SJohn Crispin case WLAN_EID_ADDBA_EXT: 1319652e8363SJohannes Berg if (elen < sizeof(struct ieee80211_addba_ext_ie)) { 13202aa485e1SJohn Crispin elem_parse_failed = true; 13212aa485e1SJohn Crispin break; 13222aa485e1SJohn Crispin } 13232aa485e1SJohn Crispin elems->addba_ext_ie = (void *)pos; 13242aa485e1SJohn Crispin break; 1325dd76986bSJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 132679ba1d89SJohannes Berg if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) 132779ba1d89SJohannes Berg elems->timeout_int = (void *)pos; 132879ba1d89SJohannes Berg else 132979ba1d89SJohannes Berg elem_parse_failed = true; 1330dd76986bSJohannes Berg break; 1331e38a017bSAvraham Stern case WLAN_EID_BSS_MAX_IDLE_PERIOD: 1332e38a017bSAvraham Stern if (elen >= sizeof(*elems->max_idle_period_ie)) 1333e38a017bSAvraham Stern elems->max_idle_period_ie = (void *)pos; 1334e38a017bSAvraham Stern break; 1335c0058df7SShaul Triebitz case WLAN_EID_RSNX: 1336c0058df7SShaul Triebitz elems->rsnx = pos; 1337c0058df7SShaul Triebitz elems->rsnx_len = elen; 1338c0058df7SShaul Triebitz break; 1339b0345850SWen Gong case WLAN_EID_TX_POWER_ENVELOPE: 1340b0345850SWen Gong if (elen < 1 || 1341b0345850SWen Gong elen > sizeof(struct ieee80211_tx_pwr_env)) 1342b0345850SWen Gong break; 1343b0345850SWen Gong 1344b0345850SWen Gong if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env)) 1345b0345850SWen Gong break; 1346b0345850SWen Gong 1347b0345850SWen Gong elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos; 1348b0345850SWen Gong elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen; 1349b0345850SWen Gong elems->tx_pwr_env_num++; 1350b0345850SWen Gong break; 135141cbb0f5SLuca Coelho case WLAN_EID_EXTENSION: 1352e4d005b8SJohannes Berg ieee80211_parse_extension_element(calc_crc ? 1353e4d005b8SJohannes Berg &crc : NULL, 1354e4d005b8SJohannes Berg elem, elems); 135541cbb0f5SLuca Coelho break; 1356cd418ba6SThomas Pedersen case WLAN_EID_S1G_CAPABILITIES: 1357652e8363SJohannes Berg if (elen >= sizeof(*elems->s1g_capab)) 1358cd418ba6SThomas Pedersen elems->s1g_capab = (void *)pos; 1359cd418ba6SThomas Pedersen else 1360cd418ba6SThomas Pedersen elem_parse_failed = true; 1361cd418ba6SThomas Pedersen break; 1362cd418ba6SThomas Pedersen case WLAN_EID_S1G_OPERATION: 1363cd418ba6SThomas Pedersen if (elen == sizeof(*elems->s1g_oper)) 1364cd418ba6SThomas Pedersen elems->s1g_oper = (void *)pos; 1365cd418ba6SThomas Pedersen else 1366cd418ba6SThomas Pedersen elem_parse_failed = true; 1367cd418ba6SThomas Pedersen break; 1368cd418ba6SThomas Pedersen case WLAN_EID_S1G_BCN_COMPAT: 1369cd418ba6SThomas Pedersen if (elen == sizeof(*elems->s1g_bcn_compat)) 1370cd418ba6SThomas Pedersen elems->s1g_bcn_compat = (void *)pos; 1371cd418ba6SThomas Pedersen else 1372cd418ba6SThomas Pedersen elem_parse_failed = true; 1373cd418ba6SThomas Pedersen break; 13741d00ce80SThomas Pedersen case WLAN_EID_AID_RESPONSE: 13751d00ce80SThomas Pedersen if (elen == sizeof(struct ieee80211_aid_response_ie)) 13761d00ce80SThomas Pedersen elems->aid_resp = (void *)pos; 13771d00ce80SThomas Pedersen else 13781d00ce80SThomas Pedersen elem_parse_failed = true; 13791d00ce80SThomas Pedersen break; 1380dd76986bSJohannes Berg default: 1381dd76986bSJohannes Berg break; 1382dd76986bSJohannes Berg } 1383dd76986bSJohannes Berg 1384fcff4f10SPaul Stewart if (elem_parse_failed) 1385fcff4f10SPaul Stewart elems->parse_error = true; 1386fcff4f10SPaul Stewart else 13875df45690SJohannes Berg __set_bit(id, seen_elems); 1388dd76986bSJohannes Berg } 1389dd76986bSJohannes Berg 1390c17e28d1SJohannes Berg if (!for_each_element_completed(elem, start, len)) 1391fcff4f10SPaul Stewart elems->parse_error = true; 1392fcff4f10SPaul Stewart 1393dd76986bSJohannes Berg return crc; 1394dd76986bSJohannes Berg } 1395dd76986bSJohannes Berg 13965023b14cSSara Sharon static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, 1397671042a4SSara Sharon struct ieee802_11_elems *elems, 1398671042a4SSara Sharon u8 *transmitter_bssid, 13995023b14cSSara Sharon u8 *bss_bssid, 14005809a5d5SDan Carpenter u8 *nontransmitted_profile) 1401671042a4SSara Sharon { 1402671042a4SSara Sharon const struct element *elem, *sub; 14035023b14cSSara Sharon size_t profile_len = 0; 14045023b14cSSara Sharon bool found = false; 1405671042a4SSara Sharon 1406671042a4SSara Sharon if (!bss_bssid || !transmitter_bssid) 14075023b14cSSara Sharon return profile_len; 1408671042a4SSara Sharon 1409671042a4SSara Sharon for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { 1410671042a4SSara Sharon if (elem->datalen < 2) 1411671042a4SSara Sharon continue; 1412671042a4SSara Sharon 1413671042a4SSara Sharon for_each_element(sub, elem->data + 1, elem->datalen - 1) { 1414671042a4SSara Sharon u8 new_bssid[ETH_ALEN]; 1415671042a4SSara Sharon const u8 *index; 1416671042a4SSara Sharon 1417671042a4SSara Sharon if (sub->id != 0 || sub->datalen < 4) { 1418671042a4SSara Sharon /* not a valid BSS profile */ 1419671042a4SSara Sharon continue; 1420671042a4SSara Sharon } 1421671042a4SSara Sharon 1422671042a4SSara Sharon if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || 1423671042a4SSara Sharon sub->data[1] != 2) { 1424671042a4SSara Sharon /* The first element of the 1425671042a4SSara Sharon * Nontransmitted BSSID Profile is not 1426671042a4SSara Sharon * the Nontransmitted BSSID Capability 1427671042a4SSara Sharon * element. 1428671042a4SSara Sharon */ 1429671042a4SSara Sharon continue; 1430671042a4SSara Sharon } 1431671042a4SSara Sharon 14325809a5d5SDan Carpenter memset(nontransmitted_profile, 0, len); 14335023b14cSSara Sharon profile_len = cfg80211_merge_profile(start, len, 14345023b14cSSara Sharon elem, 14355023b14cSSara Sharon sub, 14365023b14cSSara Sharon nontransmitted_profile, 14375023b14cSSara Sharon len); 14385023b14cSSara Sharon 1439671042a4SSara Sharon /* found a Nontransmitted BSSID Profile */ 1440671042a4SSara Sharon index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, 14415809a5d5SDan Carpenter nontransmitted_profile, 14425023b14cSSara Sharon profile_len); 1443671042a4SSara Sharon if (!index || index[1] < 1 || index[2] == 0) { 1444671042a4SSara Sharon /* Invalid MBSSID Index element */ 1445671042a4SSara Sharon continue; 1446671042a4SSara Sharon } 1447671042a4SSara Sharon 1448671042a4SSara Sharon cfg80211_gen_new_bssid(transmitter_bssid, 1449671042a4SSara Sharon elem->data[0], 1450671042a4SSara Sharon index[2], 1451671042a4SSara Sharon new_bssid); 1452671042a4SSara Sharon if (ether_addr_equal(new_bssid, bss_bssid)) { 14535023b14cSSara Sharon found = true; 1454671042a4SSara Sharon elems->bssid_index_len = index[1]; 1455671042a4SSara Sharon elems->bssid_index = (void *)&index[2]; 1456671042a4SSara Sharon break; 1457671042a4SSara Sharon } 1458671042a4SSara Sharon } 1459671042a4SSara Sharon } 14605023b14cSSara Sharon 14615023b14cSSara Sharon return found ? profile_len : 0; 1462671042a4SSara Sharon } 1463671042a4SSara Sharon 1464*c6e37ed4SJohannes Berg void ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, 146578ac51f8SSara Sharon struct ieee802_11_elems *elems, 146678ac51f8SSara Sharon u64 filter, u32 crc, u8 *transmitter_bssid, 146778ac51f8SSara Sharon u8 *bss_bssid) 146878ac51f8SSara Sharon { 1469671042a4SSara Sharon const struct element *non_inherit = NULL; 14705023b14cSSara Sharon u8 *nontransmitted_profile; 14715023b14cSSara Sharon int nontransmitted_profile_len = 0; 1472671042a4SSara Sharon 147378ac51f8SSara Sharon memset(elems, 0, sizeof(*elems)); 147478ac51f8SSara Sharon elems->ie_start = start; 147578ac51f8SSara Sharon elems->total_len = len; 147678ac51f8SSara Sharon 14775023b14cSSara Sharon nontransmitted_profile = kmalloc(len, GFP_ATOMIC); 14785023b14cSSara Sharon if (nontransmitted_profile) { 14795023b14cSSara Sharon nontransmitted_profile_len = 14805023b14cSSara Sharon ieee802_11_find_bssid_profile(start, len, elems, 14815023b14cSSara Sharon transmitter_bssid, 14825023b14cSSara Sharon bss_bssid, 14835809a5d5SDan Carpenter nontransmitted_profile); 1484671042a4SSara Sharon non_inherit = 1485671042a4SSara Sharon cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, 14865023b14cSSara Sharon nontransmitted_profile, 14875023b14cSSara Sharon nontransmitted_profile_len); 14885023b14cSSara Sharon } 1489671042a4SSara Sharon 149078ac51f8SSara Sharon crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter, 1491671042a4SSara Sharon crc, non_inherit); 149278ac51f8SSara Sharon 149378ac51f8SSara Sharon /* Override with nontransmitted profile, if found */ 14945023b14cSSara Sharon if (nontransmitted_profile_len) 14955023b14cSSara Sharon _ieee802_11_parse_elems_crc(nontransmitted_profile, 14965023b14cSSara Sharon nontransmitted_profile_len, 1497671042a4SSara Sharon action, elems, 0, 0, NULL); 149878ac51f8SSara Sharon 149978ac51f8SSara Sharon if (elems->tim && !elems->parse_error) { 150078ac51f8SSara Sharon const struct ieee80211_tim_ie *tim_ie = elems->tim; 150178ac51f8SSara Sharon 150278ac51f8SSara Sharon elems->dtim_period = tim_ie->dtim_period; 150378ac51f8SSara Sharon elems->dtim_count = tim_ie->dtim_count; 150478ac51f8SSara Sharon } 150578ac51f8SSara Sharon 150678ac51f8SSara Sharon /* Override DTIM period and count if needed */ 150778ac51f8SSara Sharon if (elems->bssid_index && 150878ac51f8SSara Sharon elems->bssid_index_len >= 150978ac51f8SSara Sharon offsetofend(struct ieee80211_bssid_index, dtim_period)) 151078ac51f8SSara Sharon elems->dtim_period = elems->bssid_index->dtim_period; 151178ac51f8SSara Sharon 151278ac51f8SSara Sharon if (elems->bssid_index && 151378ac51f8SSara Sharon elems->bssid_index_len >= 151478ac51f8SSara Sharon offsetofend(struct ieee80211_bssid_index, dtim_count)) 151578ac51f8SSara Sharon elems->dtim_count = elems->bssid_index->dtim_count; 151678ac51f8SSara Sharon 15175023b14cSSara Sharon kfree(nontransmitted_profile); 15185023b14cSSara Sharon 1519*c6e37ed4SJohannes Berg elems->crc = crc; 152078ac51f8SSara Sharon } 152178ac51f8SSara Sharon 1522e552af05SHaim Dreyfuss void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, 1523e552af05SHaim Dreyfuss struct ieee80211_tx_queue_params 1524e552af05SHaim Dreyfuss *qparam, int ac) 1525e552af05SHaim Dreyfuss { 1526e552af05SHaim Dreyfuss struct ieee80211_chanctx_conf *chanctx_conf; 1527e552af05SHaim Dreyfuss const struct ieee80211_reg_rule *rrule; 152838cb87eeSStanislaw Gruszka const struct ieee80211_wmm_ac *wmm_ac; 1529e552af05SHaim Dreyfuss u16 center_freq = 0; 1530e552af05SHaim Dreyfuss 1531e552af05SHaim Dreyfuss if (sdata->vif.type != NL80211_IFTYPE_AP && 1532e552af05SHaim Dreyfuss sdata->vif.type != NL80211_IFTYPE_STATION) 1533e552af05SHaim Dreyfuss return; 1534e552af05SHaim Dreyfuss 1535e552af05SHaim Dreyfuss rcu_read_lock(); 1536e552af05SHaim Dreyfuss chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 1537e552af05SHaim Dreyfuss if (chanctx_conf) 1538e552af05SHaim Dreyfuss center_freq = chanctx_conf->def.chan->center_freq; 1539e552af05SHaim Dreyfuss 1540e552af05SHaim Dreyfuss if (!center_freq) { 1541e552af05SHaim Dreyfuss rcu_read_unlock(); 1542e552af05SHaim Dreyfuss return; 1543e552af05SHaim Dreyfuss } 1544e552af05SHaim Dreyfuss 1545e552af05SHaim Dreyfuss rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq)); 1546e552af05SHaim Dreyfuss 154738cb87eeSStanislaw Gruszka if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) { 1548e552af05SHaim Dreyfuss rcu_read_unlock(); 1549e552af05SHaim Dreyfuss return; 1550e552af05SHaim Dreyfuss } 1551e552af05SHaim Dreyfuss 1552e552af05SHaim Dreyfuss if (sdata->vif.type == NL80211_IFTYPE_AP) 155338cb87eeSStanislaw Gruszka wmm_ac = &rrule->wmm_rule.ap[ac]; 1554e552af05SHaim Dreyfuss else 155538cb87eeSStanislaw Gruszka wmm_ac = &rrule->wmm_rule.client[ac]; 1556e552af05SHaim Dreyfuss qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min); 1557e552af05SHaim Dreyfuss qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max); 1558e552af05SHaim Dreyfuss qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn); 1559abd76d25SDreyfuss, Haim qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32); 1560e552af05SHaim Dreyfuss rcu_read_unlock(); 1561e552af05SHaim Dreyfuss } 1562e552af05SHaim Dreyfuss 15633abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, 1564cec66283SJohannes Berg bool bss_notify, bool enable_qos) 15655825fe10SJohannes Berg { 15665825fe10SJohannes Berg struct ieee80211_local *local = sdata->local; 15675825fe10SJohannes Berg struct ieee80211_tx_queue_params qparam; 156855de908aSJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 156954bcbc69SJohannes Berg int ac; 1570cec66283SJohannes Berg bool use_11b; 1571239281f8SRostislav Lisovy bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ 1572aa837e1dSJohannes Berg int aCWmin, aCWmax; 15735825fe10SJohannes Berg 15745825fe10SJohannes Berg if (!local->ops->conf_tx) 15755825fe10SJohannes Berg return; 15765825fe10SJohannes Berg 157754bcbc69SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 157854bcbc69SJohannes Berg return; 157954bcbc69SJohannes Berg 15805825fe10SJohannes Berg memset(&qparam, 0, sizeof(qparam)); 15815825fe10SJohannes Berg 158255de908aSJohannes Berg rcu_read_lock(); 158355de908aSJohannes Berg chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 158455de908aSJohannes Berg use_11b = (chanctx_conf && 158557fbcce3SJohannes Berg chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) && 1586aa837e1dSJohannes Berg !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); 158755de908aSJohannes Berg rcu_read_unlock(); 15885825fe10SJohannes Berg 1589239281f8SRostislav Lisovy is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); 1590239281f8SRostislav Lisovy 1591aa837e1dSJohannes Berg /* Set defaults according to 802.11-2007 Table 7-37 */ 1592aa837e1dSJohannes Berg aCWmax = 1023; 1593aa837e1dSJohannes Berg if (use_11b) 1594aa837e1dSJohannes Berg aCWmin = 31; 15955825fe10SJohannes Berg else 1596aa837e1dSJohannes Berg aCWmin = 15; 15975825fe10SJohannes Berg 15981f4ffde8SFred Zhou /* Confiure old 802.11b/g medium access rules. */ 15991f4ffde8SFred Zhou qparam.cw_max = aCWmax; 16001f4ffde8SFred Zhou qparam.cw_min = aCWmin; 16011f4ffde8SFred Zhou qparam.txop = 0; 16021f4ffde8SFred Zhou qparam.aifs = 2; 16031f4ffde8SFred Zhou 16041f4ffde8SFred Zhou for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 16051f4ffde8SFred Zhou /* Update if QoS is enabled. */ 1606a8ce8544SStanislaw Gruszka if (enable_qos) { 160754bcbc69SJohannes Berg switch (ac) { 16081d98fb12SJohannes Berg case IEEE80211_AC_BK: 16097ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 16107ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 16115825fe10SJohannes Berg qparam.txop = 0; 1612239281f8SRostislav Lisovy if (is_ocb) 1613239281f8SRostislav Lisovy qparam.aifs = 9; 1614239281f8SRostislav Lisovy else 1615aa837e1dSJohannes Berg qparam.aifs = 7; 1616aa837e1dSJohannes Berg break; 1617a8ce8544SStanislaw Gruszka /* never happens but let's not leave undefined */ 1618a8ce8544SStanislaw Gruszka default: 16191d98fb12SJohannes Berg case IEEE80211_AC_BE: 16207ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 16217ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 1622aa837e1dSJohannes Berg qparam.txop = 0; 1623239281f8SRostislav Lisovy if (is_ocb) 1624239281f8SRostislav Lisovy qparam.aifs = 6; 1625239281f8SRostislav Lisovy else 1626aa837e1dSJohannes Berg qparam.aifs = 3; 1627aa837e1dSJohannes Berg break; 16281d98fb12SJohannes Berg case IEEE80211_AC_VI: 1629aa837e1dSJohannes Berg qparam.cw_max = aCWmin; 1630aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 2 - 1; 1631239281f8SRostislav Lisovy if (is_ocb) 1632239281f8SRostislav Lisovy qparam.txop = 0; 1633239281f8SRostislav Lisovy else if (use_11b) 1634aa837e1dSJohannes Berg qparam.txop = 6016/32; 1635aa837e1dSJohannes Berg else 1636aa837e1dSJohannes Berg qparam.txop = 3008/32; 1637239281f8SRostislav Lisovy 1638239281f8SRostislav Lisovy if (is_ocb) 1639239281f8SRostislav Lisovy qparam.aifs = 3; 1640239281f8SRostislav Lisovy else 1641aa837e1dSJohannes Berg qparam.aifs = 2; 1642aa837e1dSJohannes Berg break; 16431d98fb12SJohannes Berg case IEEE80211_AC_VO: 1644aa837e1dSJohannes Berg qparam.cw_max = (aCWmin + 1) / 2 - 1; 1645aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 4 - 1; 1646239281f8SRostislav Lisovy if (is_ocb) 1647239281f8SRostislav Lisovy qparam.txop = 0; 1648239281f8SRostislav Lisovy else if (use_11b) 1649aa837e1dSJohannes Berg qparam.txop = 3264/32; 1650aa837e1dSJohannes Berg else 1651aa837e1dSJohannes Berg qparam.txop = 1504/32; 1652aa837e1dSJohannes Berg qparam.aifs = 2; 1653aa837e1dSJohannes Berg break; 1654aa837e1dSJohannes Berg } 1655a8ce8544SStanislaw Gruszka } 1656e552af05SHaim Dreyfuss ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac); 16575825fe10SJohannes Berg 1658ab13315aSKalle Valo qparam.uapsd = false; 1659ab13315aSKalle Valo 166054bcbc69SJohannes Berg sdata->tx_conf[ac] = qparam; 166154bcbc69SJohannes Berg drv_conf_tx(local, sdata, ac, &qparam); 1662aa837e1dSJohannes Berg } 1663e1b3ec1aSStanislaw Gruszka 1664f142c6b9SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_MONITOR && 1665708d50edSAyala Beker sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 1666708d50edSAyala Beker sdata->vif.type != NL80211_IFTYPE_NAN) { 1667a8ce8544SStanislaw Gruszka sdata->vif.bss_conf.qos = enable_qos; 16683abead59SJohannes Berg if (bss_notify) 16693abead59SJohannes Berg ieee80211_bss_info_change_notify(sdata, 16703abead59SJohannes Berg BSS_CHANGED_QOS); 16715825fe10SJohannes Berg } 1672d9734979SSujith } 1673e50db65cSJohannes Berg 167446900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 1675700e8ea6SJouni Malinen u16 transaction, u16 auth_alg, u16 status, 16764a3cb702SJohannes Berg const u8 *extra, size_t extra_len, const u8 *da, 16771672c0e3SJohannes Berg const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, 16781672c0e3SJohannes Berg u32 tx_flags) 167946900298SJohannes Berg { 168046900298SJohannes Berg struct ieee80211_local *local = sdata->local; 168146900298SJohannes Berg struct sk_buff *skb; 168246900298SJohannes Berg struct ieee80211_mgmt *mgmt; 1683fffd0934SJohannes Berg int err; 168446900298SJohannes Berg 168515e230abSFred Zhou /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ 1686744462a9SMax Stepanov skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + 1687744462a9SMax Stepanov 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN); 1688d15b8459SJoe Perches if (!skb) 168946900298SJohannes Berg return; 1690d15b8459SJoe Perches 1691744462a9SMax Stepanov skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN); 169246900298SJohannes Berg 1693b080db58SJohannes Berg mgmt = skb_put_zero(skb, 24 + 6); 169446900298SJohannes Berg mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 169546900298SJohannes Berg IEEE80211_STYPE_AUTH); 1696efa6a09dSAntonio Quartulli memcpy(mgmt->da, da, ETH_ALEN); 169747846c9bSJohannes Berg memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 169846900298SJohannes Berg memcpy(mgmt->bssid, bssid, ETH_ALEN); 169946900298SJohannes Berg mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); 170046900298SJohannes Berg mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); 1701700e8ea6SJouni Malinen mgmt->u.auth.status_code = cpu_to_le16(status); 170246900298SJohannes Berg if (extra) 170359ae1d12SJohannes Berg skb_put_data(skb, extra, extra_len); 170446900298SJohannes Berg 1705fffd0934SJohannes Berg if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { 1706fffd0934SJohannes Berg mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 1707fffd0934SJohannes Berg err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); 17087d7b0075SJohannes Berg if (WARN_ON(err)) { 17097d7b0075SJohannes Berg kfree_skb(skb); 17107d7b0075SJohannes Berg return; 17117d7b0075SJohannes Berg } 1712fffd0934SJohannes Berg } 1713fffd0934SJohannes Berg 17141672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 17151672c0e3SJohannes Berg tx_flags; 171662ae67beSJohannes Berg ieee80211_tx_skb(sdata, skb); 171746900298SJohannes Berg } 171846900298SJohannes Berg 17196ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, 17204b08d1b6SJohannes Berg const u8 *da, const u8 *bssid, 17214b08d1b6SJohannes Berg u16 stype, u16 reason, 17226ae16775SAntonio Quartulli bool send_frame, u8 *frame_buf) 17236ae16775SAntonio Quartulli { 17246ae16775SAntonio Quartulli struct ieee80211_local *local = sdata->local; 17256ae16775SAntonio Quartulli struct sk_buff *skb; 17266ae16775SAntonio Quartulli struct ieee80211_mgmt *mgmt = (void *)frame_buf; 17276ae16775SAntonio Quartulli 17286ae16775SAntonio Quartulli /* build frame */ 17296ae16775SAntonio Quartulli mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 17306ae16775SAntonio Quartulli mgmt->duration = 0; /* initialize only */ 17316ae16775SAntonio Quartulli mgmt->seq_ctrl = 0; /* initialize only */ 17324b08d1b6SJohannes Berg memcpy(mgmt->da, da, ETH_ALEN); 17336ae16775SAntonio Quartulli memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 17346ae16775SAntonio Quartulli memcpy(mgmt->bssid, bssid, ETH_ALEN); 17356ae16775SAntonio Quartulli /* u.deauth.reason_code == u.disassoc.reason_code */ 17366ae16775SAntonio Quartulli mgmt->u.deauth.reason_code = cpu_to_le16(reason); 17376ae16775SAntonio Quartulli 17386ae16775SAntonio Quartulli if (send_frame) { 17396ae16775SAntonio Quartulli skb = dev_alloc_skb(local->hw.extra_tx_headroom + 17406ae16775SAntonio Quartulli IEEE80211_DEAUTH_FRAME_LEN); 17416ae16775SAntonio Quartulli if (!skb) 17426ae16775SAntonio Quartulli return; 17436ae16775SAntonio Quartulli 17446ae16775SAntonio Quartulli skb_reserve(skb, local->hw.extra_tx_headroom); 17456ae16775SAntonio Quartulli 17466ae16775SAntonio Quartulli /* copy in frame */ 174759ae1d12SJohannes Berg skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN); 17486ae16775SAntonio Quartulli 17496ae16775SAntonio Quartulli if (sdata->vif.type != NL80211_IFTYPE_STATION || 17506ae16775SAntonio Quartulli !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) 17516ae16775SAntonio Quartulli IEEE80211_SKB_CB(skb)->flags |= 17526ae16775SAntonio Quartulli IEEE80211_TX_INTFL_DONT_ENCRYPT; 17536ae16775SAntonio Quartulli 17546ae16775SAntonio Quartulli ieee80211_tx_skb(sdata, skb); 17556ae16775SAntonio Quartulli } 17566ae16775SAntonio Quartulli } 17576ae16775SAntonio Quartulli 17582ad2274cSIlan Peer static u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) 17592ad2274cSIlan Peer { 17602ad2274cSIlan Peer if ((end - pos) < 5) 17612ad2274cSIlan Peer return pos; 17622ad2274cSIlan Peer 17632ad2274cSIlan Peer *pos++ = WLAN_EID_EXTENSION; 17642ad2274cSIlan Peer *pos++ = 1 + sizeof(cap); 17652ad2274cSIlan Peer *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA; 17662ad2274cSIlan Peer memcpy(pos, &cap, sizeof(cap)); 17672ad2274cSIlan Peer 17682ad2274cSIlan Peer return pos + 2; 17692ad2274cSIlan Peer } 17702ad2274cSIlan Peer 17712ad2274cSIlan Peer static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, 1772c56ef672SDavid Spinadel u8 *buffer, size_t buffer_len, 1773c56ef672SDavid Spinadel const u8 *ie, size_t ie_len, 177457fbcce3SJohannes Berg enum nl80211_band band, 1775c56ef672SDavid Spinadel u32 rate_mask, 1776c56ef672SDavid Spinadel struct cfg80211_chan_def *chandef, 177700387f32SJohannes Berg size_t *offset, u32 flags) 1778de95a54bSJohannes Berg { 17792ad2274cSIlan Peer struct ieee80211_local *local = sdata->local; 1780de95a54bSJohannes Berg struct ieee80211_supported_band *sband; 178141cbb0f5SLuca Coelho const struct ieee80211_sta_he_cap *he_cap; 1782c604b9f2SJohannes Berg u8 *pos = buffer, *end = buffer + buffer_len; 1783c56ef672SDavid Spinadel size_t noffset; 17848e664fb3SJohannes Berg int supp_rates_len, i; 17858dcb2003SJouni Malinen u8 rates[32]; 17868dcb2003SJouni Malinen int num_rates; 17878dcb2003SJouni Malinen int ext_rates_len; 17882103dec1SSimon Wunderlich int shift; 17892103dec1SSimon Wunderlich u32 rate_flags; 179040a11ca8SJohannes Berg bool have_80mhz = false; 1791de95a54bSJohannes Berg 1792c56ef672SDavid Spinadel *offset = 0; 1793c56ef672SDavid Spinadel 17944d36ec58SJohannes Berg sband = local->hw.wiphy->bands[band]; 1795d811b3d5SArik Nemtsov if (WARN_ON_ONCE(!sband)) 1796d811b3d5SArik Nemtsov return 0; 1797de95a54bSJohannes Berg 17982103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(chandef); 17992103dec1SSimon Wunderlich shift = ieee80211_chandef_get_shift(chandef); 18002103dec1SSimon Wunderlich 18018dcb2003SJouni Malinen num_rates = 0; 18028dcb2003SJouni Malinen for (i = 0; i < sband->n_bitrates; i++) { 18038dcb2003SJouni Malinen if ((BIT(i) & rate_mask) == 0) 18048dcb2003SJouni Malinen continue; /* skip rate */ 18052103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 18062103dec1SSimon Wunderlich continue; 18072103dec1SSimon Wunderlich 18082103dec1SSimon Wunderlich rates[num_rates++] = 18092103dec1SSimon Wunderlich (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 18102103dec1SSimon Wunderlich (1 << shift) * 5); 18118dcb2003SJouni Malinen } 18128dcb2003SJouni Malinen 18138dcb2003SJouni Malinen supp_rates_len = min_t(int, num_rates, 8); 18148e664fb3SJohannes Berg 1815c604b9f2SJohannes Berg if (end - pos < 2 + supp_rates_len) 1816c604b9f2SJohannes Berg goto out_err; 1817de95a54bSJohannes Berg *pos++ = WLAN_EID_SUPP_RATES; 18188e664fb3SJohannes Berg *pos++ = supp_rates_len; 18198dcb2003SJouni Malinen memcpy(pos, rates, supp_rates_len); 18208dcb2003SJouni Malinen pos += supp_rates_len; 1821de95a54bSJohannes Berg 18228e664fb3SJohannes Berg /* insert "request information" if in custom IEs */ 18238e664fb3SJohannes Berg if (ie && ie_len) { 18248e664fb3SJohannes Berg static const u8 before_extrates[] = { 18258e664fb3SJohannes Berg WLAN_EID_SSID, 18268e664fb3SJohannes Berg WLAN_EID_SUPP_RATES, 18278e664fb3SJohannes Berg WLAN_EID_REQUEST, 18288e664fb3SJohannes Berg }; 18298e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 18308e664fb3SJohannes Berg before_extrates, 18318e664fb3SJohannes Berg ARRAY_SIZE(before_extrates), 1832c56ef672SDavid Spinadel *offset); 1833c56ef672SDavid Spinadel if (end - pos < noffset - *offset) 1834c604b9f2SJohannes Berg goto out_err; 1835c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset); 1836c56ef672SDavid Spinadel pos += noffset - *offset; 1837c56ef672SDavid Spinadel *offset = noffset; 18388e664fb3SJohannes Berg } 18398e664fb3SJohannes Berg 18408dcb2003SJouni Malinen ext_rates_len = num_rates - supp_rates_len; 18418dcb2003SJouni Malinen if (ext_rates_len > 0) { 1842c604b9f2SJohannes Berg if (end - pos < 2 + ext_rates_len) 1843c604b9f2SJohannes Berg goto out_err; 1844de95a54bSJohannes Berg *pos++ = WLAN_EID_EXT_SUPP_RATES; 18458dcb2003SJouni Malinen *pos++ = ext_rates_len; 18468dcb2003SJouni Malinen memcpy(pos, rates + supp_rates_len, ext_rates_len); 18478dcb2003SJouni Malinen pos += ext_rates_len; 18488e664fb3SJohannes Berg } 18498e664fb3SJohannes Berg 185057fbcce3SJohannes Berg if (chandef->chan && sband->band == NL80211_BAND_2GHZ) { 1851c604b9f2SJohannes Berg if (end - pos < 3) 1852c604b9f2SJohannes Berg goto out_err; 1853651b5225SJouni Malinen *pos++ = WLAN_EID_DS_PARAMS; 1854651b5225SJouni Malinen *pos++ = 1; 18552103dec1SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel( 18562103dec1SSimon Wunderlich chandef->chan->center_freq); 1857651b5225SJouni Malinen } 1858651b5225SJouni Malinen 1859b9771d41SJohannes Berg if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT) 1860b9771d41SJohannes Berg goto done; 1861b9771d41SJohannes Berg 18628e664fb3SJohannes Berg /* insert custom IEs that go before HT */ 18638e664fb3SJohannes Berg if (ie && ie_len) { 18648e664fb3SJohannes Berg static const u8 before_ht[] = { 1865a7f26d80SJohannes Berg /* 1866a7f26d80SJohannes Berg * no need to list the ones split off already 1867a7f26d80SJohannes Berg * (or generated here) 1868a7f26d80SJohannes Berg */ 18698e664fb3SJohannes Berg WLAN_EID_DS_PARAMS, 18708e664fb3SJohannes Berg WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 18718e664fb3SJohannes Berg }; 18728e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 18738e664fb3SJohannes Berg before_ht, ARRAY_SIZE(before_ht), 1874c56ef672SDavid Spinadel *offset); 1875c56ef672SDavid Spinadel if (end - pos < noffset - *offset) 1876c604b9f2SJohannes Berg goto out_err; 1877c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset); 1878c56ef672SDavid Spinadel pos += noffset - *offset; 1879c56ef672SDavid Spinadel *offset = noffset; 1880de95a54bSJohannes Berg } 1881de95a54bSJohannes Berg 1882c604b9f2SJohannes Berg if (sband->ht_cap.ht_supported) { 1883c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) 1884c604b9f2SJohannes Berg goto out_err; 1885ef96a842SBen Greear pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, 1886ef96a842SBen Greear sband->ht_cap.cap); 1887c604b9f2SJohannes Berg } 18885ef2d41aSJohannes Berg 18894d952300SJohannes Berg /* insert custom IEs that go before VHT */ 18908e664fb3SJohannes Berg if (ie && ie_len) { 18914d952300SJohannes Berg static const u8 before_vht[] = { 1892a7f26d80SJohannes Berg /* 1893a7f26d80SJohannes Berg * no need to list the ones split off already 1894a7f26d80SJohannes Berg * (or generated here) 1895a7f26d80SJohannes Berg */ 18964d952300SJohannes Berg WLAN_EID_BSS_COEX_2040, 18974d952300SJohannes Berg WLAN_EID_EXT_CAPABILITY, 18984d952300SJohannes Berg WLAN_EID_SSID_LIST, 18994d952300SJohannes Berg WLAN_EID_CHANNEL_USAGE, 19004d952300SJohannes Berg WLAN_EID_INTERWORKING, 1901b44eebeaSLiad Kaufman WLAN_EID_MESH_ID, 1902a7f26d80SJohannes Berg /* 60 GHz (Multi-band, DMG, MMS) can't happen */ 19034d952300SJohannes Berg }; 19044d952300SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 19054d952300SJohannes Berg before_vht, ARRAY_SIZE(before_vht), 1906c56ef672SDavid Spinadel *offset); 1907c56ef672SDavid Spinadel if (end - pos < noffset - *offset) 1908c604b9f2SJohannes Berg goto out_err; 1909c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset); 1910c56ef672SDavid Spinadel pos += noffset - *offset; 1911c56ef672SDavid Spinadel *offset = noffset; 1912de95a54bSJohannes Berg } 1913de95a54bSJohannes Berg 191440a11ca8SJohannes Berg /* Check if any channel in this sband supports at least 80 MHz */ 191540a11ca8SJohannes Berg for (i = 0; i < sband->n_channels; i++) { 1916fa44b988SArik Nemtsov if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 1917fa44b988SArik Nemtsov IEEE80211_CHAN_NO_80MHZ)) 1918fa44b988SArik Nemtsov continue; 1919fa44b988SArik Nemtsov 192040a11ca8SJohannes Berg have_80mhz = true; 192140a11ca8SJohannes Berg break; 192240a11ca8SJohannes Berg } 192340a11ca8SJohannes Berg 192440a11ca8SJohannes Berg if (sband->vht_cap.vht_supported && have_80mhz) { 1925c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) 1926c604b9f2SJohannes Berg goto out_err; 1927ba0afa2fSMahesh Palivela pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, 1928ba0afa2fSMahesh Palivela sband->vht_cap.cap); 1929c604b9f2SJohannes Berg } 1930ba0afa2fSMahesh Palivela 193141cbb0f5SLuca Coelho /* insert custom IEs that go before HE */ 193241cbb0f5SLuca Coelho if (ie && ie_len) { 193341cbb0f5SLuca Coelho static const u8 before_he[] = { 193441cbb0f5SLuca Coelho /* 193541cbb0f5SLuca Coelho * no need to list the ones split off before VHT 193641cbb0f5SLuca Coelho * or generated here 193741cbb0f5SLuca Coelho */ 193841cbb0f5SLuca Coelho WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS, 193941cbb0f5SLuca Coelho WLAN_EID_AP_CSN, 194041cbb0f5SLuca Coelho /* TODO: add 11ah/11aj/11ak elements */ 194141cbb0f5SLuca Coelho }; 194241cbb0f5SLuca Coelho noffset = ieee80211_ie_split(ie, ie_len, 194341cbb0f5SLuca Coelho before_he, ARRAY_SIZE(before_he), 194441cbb0f5SLuca Coelho *offset); 194541cbb0f5SLuca Coelho if (end - pos < noffset - *offset) 194641cbb0f5SLuca Coelho goto out_err; 194741cbb0f5SLuca Coelho memcpy(pos, ie + *offset, noffset - *offset); 194841cbb0f5SLuca Coelho pos += noffset - *offset; 194941cbb0f5SLuca Coelho *offset = noffset; 195041cbb0f5SLuca Coelho } 195141cbb0f5SLuca Coelho 1952bac2fd3dSJohannes Berg he_cap = ieee80211_get_he_iftype_cap(sband, 1953bac2fd3dSJohannes Berg ieee80211_vif_type_p2p(&sdata->vif)); 19540bc47057SJohannes Berg if (he_cap && 19550bc47057SJohannes Berg cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), 19560bc47057SJohannes Berg IEEE80211_CHAN_NO_HE)) { 195741cbb0f5SLuca Coelho pos = ieee80211_ie_build_he_cap(pos, he_cap, end); 195841cbb0f5SLuca Coelho if (!pos) 195941cbb0f5SLuca Coelho goto out_err; 19607d29bc50SJohannes Berg } 19612ad2274cSIlan Peer 19627d29bc50SJohannes Berg if (cfg80211_any_usable_channels(local->hw.wiphy, 19637d29bc50SJohannes Berg BIT(NL80211_BAND_6GHZ), 19647d29bc50SJohannes Berg IEEE80211_CHAN_NO_HE)) { 19657d29bc50SJohannes Berg struct ieee80211_supported_band *sband6; 19667d29bc50SJohannes Berg 19677d29bc50SJohannes Berg sband6 = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 19687d29bc50SJohannes Berg he_cap = ieee80211_get_he_iftype_cap(sband6, 19697d29bc50SJohannes Berg ieee80211_vif_type_p2p(&sdata->vif)); 19707d29bc50SJohannes Berg 19717d29bc50SJohannes Berg if (he_cap) { 19722ad2274cSIlan Peer enum nl80211_iftype iftype = 19732ad2274cSIlan Peer ieee80211_vif_type_p2p(&sdata->vif); 19742ad2274cSIlan Peer __le16 cap = ieee80211_get_he_6ghz_capa(sband, iftype); 19752ad2274cSIlan Peer 19762ad2274cSIlan Peer pos = ieee80211_write_he_6ghz_cap(pos, cap, end); 19772ad2274cSIlan Peer } 197841cbb0f5SLuca Coelho } 197941cbb0f5SLuca Coelho 198041cbb0f5SLuca Coelho /* 198141cbb0f5SLuca Coelho * If adding more here, adjust code in main.c 198241cbb0f5SLuca Coelho * that calculates local->scan_ies_len. 198341cbb0f5SLuca Coelho */ 198441cbb0f5SLuca Coelho 1985de95a54bSJohannes Berg return pos - buffer; 1986c604b9f2SJohannes Berg out_err: 1987c604b9f2SJohannes Berg WARN_ONCE(1, "not enough space for preq IEs\n"); 1988b9771d41SJohannes Berg done: 1989c604b9f2SJohannes Berg return pos - buffer; 1990de95a54bSJohannes Berg } 1991de95a54bSJohannes Berg 19922ad2274cSIlan Peer int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer, 1993c56ef672SDavid Spinadel size_t buffer_len, 1994c56ef672SDavid Spinadel struct ieee80211_scan_ies *ie_desc, 1995c56ef672SDavid Spinadel const u8 *ie, size_t ie_len, 1996c56ef672SDavid Spinadel u8 bands_used, u32 *rate_masks, 199700387f32SJohannes Berg struct cfg80211_chan_def *chandef, 199800387f32SJohannes Berg u32 flags) 1999c56ef672SDavid Spinadel { 2000c56ef672SDavid Spinadel size_t pos = 0, old_pos = 0, custom_ie_offset = 0; 2001c56ef672SDavid Spinadel int i; 2002c56ef672SDavid Spinadel 2003c56ef672SDavid Spinadel memset(ie_desc, 0, sizeof(*ie_desc)); 2004c56ef672SDavid Spinadel 200557fbcce3SJohannes Berg for (i = 0; i < NUM_NL80211_BANDS; i++) { 2006c56ef672SDavid Spinadel if (bands_used & BIT(i)) { 20072ad2274cSIlan Peer pos += ieee80211_build_preq_ies_band(sdata, 2008c56ef672SDavid Spinadel buffer + pos, 2009c56ef672SDavid Spinadel buffer_len - pos, 2010c56ef672SDavid Spinadel ie, ie_len, i, 2011c56ef672SDavid Spinadel rate_masks[i], 2012c56ef672SDavid Spinadel chandef, 201300387f32SJohannes Berg &custom_ie_offset, 201400387f32SJohannes Berg flags); 2015c56ef672SDavid Spinadel ie_desc->ies[i] = buffer + old_pos; 2016c56ef672SDavid Spinadel ie_desc->len[i] = pos - old_pos; 2017c56ef672SDavid Spinadel old_pos = pos; 2018c56ef672SDavid Spinadel } 2019c56ef672SDavid Spinadel } 2020c56ef672SDavid Spinadel 2021c56ef672SDavid Spinadel /* add any remaining custom IEs */ 2022c56ef672SDavid Spinadel if (ie && ie_len) { 2023c56ef672SDavid Spinadel if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, 2024c56ef672SDavid Spinadel "not enough space for preq custom IEs\n")) 2025c56ef672SDavid Spinadel return pos; 2026c56ef672SDavid Spinadel memcpy(buffer + pos, ie + custom_ie_offset, 2027c56ef672SDavid Spinadel ie_len - custom_ie_offset); 2028c56ef672SDavid Spinadel ie_desc->common_ies = buffer + pos; 2029c56ef672SDavid Spinadel ie_desc->common_ie_len = ie_len - custom_ie_offset; 2030c56ef672SDavid Spinadel pos += ie_len - custom_ie_offset; 2031c56ef672SDavid Spinadel } 2032c56ef672SDavid Spinadel 2033c56ef672SDavid Spinadel return pos; 2034c56ef672SDavid Spinadel }; 2035c56ef672SDavid Spinadel 2036a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, 2037a344d677SJohannes Berg const u8 *src, const u8 *dst, 2038a344d677SJohannes Berg u32 ratemask, 20396b77863bSJohannes Berg struct ieee80211_channel *chan, 2040de95a54bSJohannes Berg const u8 *ssid, size_t ssid_len, 2041a806c558SPaul Stewart const u8 *ie, size_t ie_len, 204200387f32SJohannes Berg u32 flags) 204346900298SJohannes Berg { 204446900298SJohannes Berg struct ieee80211_local *local = sdata->local; 20452103dec1SSimon Wunderlich struct cfg80211_chan_def chandef; 204646900298SJohannes Berg struct sk_buff *skb; 204746900298SJohannes Berg struct ieee80211_mgmt *mgmt; 2048b9a9ada1SJohannes Berg int ies_len; 204957fbcce3SJohannes Berg u32 rate_masks[NUM_NL80211_BANDS] = {}; 2050c56ef672SDavid Spinadel struct ieee80211_scan_ies dummy_ie_desc; 205146900298SJohannes Berg 2052a806c558SPaul Stewart /* 2053a806c558SPaul Stewart * Do not send DS Channel parameter for directed probe requests 2054a806c558SPaul Stewart * in order to maximize the chance that we get a response. Some 2055a806c558SPaul Stewart * badly-behaved APs don't respond when this parameter is included. 2056a806c558SPaul Stewart */ 20572103dec1SSimon Wunderlich chandef.width = sdata->vif.bss_conf.chandef.width; 205800387f32SJohannes Berg if (flags & IEEE80211_PROBE_FLAG_DIRECTED) 20592103dec1SSimon Wunderlich chandef.chan = NULL; 2060a806c558SPaul Stewart else 20612103dec1SSimon Wunderlich chandef.chan = chan; 2062651b5225SJouni Malinen 2063a344d677SJohannes Berg skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len, 2064a344d677SJohannes Berg 100 + ie_len); 20655b2bbf75SJohannes Berg if (!skb) 2066b9a9ada1SJohannes Berg return NULL; 2067b9a9ada1SJohannes Berg 2068c56ef672SDavid Spinadel rate_masks[chan->band] = ratemask; 20692ad2274cSIlan Peer ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb), 2070c56ef672SDavid Spinadel skb_tailroom(skb), &dummy_ie_desc, 2071c56ef672SDavid Spinadel ie, ie_len, BIT(chan->band), 207200387f32SJohannes Berg rate_masks, &chandef, flags); 2073b9a9ada1SJohannes Berg skb_put(skb, ies_len); 20747c12ce8bSKalle Valo 207546900298SJohannes Berg if (dst) { 20767c12ce8bSKalle Valo mgmt = (struct ieee80211_mgmt *) skb->data; 207746900298SJohannes Berg memcpy(mgmt->da, dst, ETH_ALEN); 207846900298SJohannes Berg memcpy(mgmt->bssid, dst, ETH_ALEN); 207946900298SJohannes Berg } 208046900298SJohannes Berg 208162ae67beSJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 20825b2bbf75SJohannes Berg 2083a619a4c0SJuuso Oikarinen return skb; 2084a619a4c0SJuuso Oikarinen } 2085a619a4c0SJuuso Oikarinen 20862103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 208746900298SJohannes Berg struct ieee802_11_elems *elems, 208857fbcce3SJohannes Berg enum nl80211_band band, u32 *basic_rates) 208946900298SJohannes Berg { 209046900298SJohannes Berg struct ieee80211_supported_band *sband; 209146900298SJohannes Berg size_t num_rates; 20922103dec1SSimon Wunderlich u32 supp_rates, rate_flags; 20932103dec1SSimon Wunderlich int i, j, shift; 209421a8e9ddSMohammed Shafi Shajakhan 20952103dec1SSimon Wunderlich sband = sdata->local->hw.wiphy->bands[band]; 209621a8e9ddSMohammed Shafi Shajakhan if (WARN_ON(!sband)) 209721a8e9ddSMohammed Shafi Shajakhan return 1; 20982103dec1SSimon Wunderlich 20992103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 21002103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 210146900298SJohannes Berg 210246900298SJohannes Berg num_rates = sband->n_bitrates; 210346900298SJohannes Berg supp_rates = 0; 210446900298SJohannes Berg for (i = 0; i < elems->supp_rates_len + 210546900298SJohannes Berg elems->ext_supp_rates_len; i++) { 210646900298SJohannes Berg u8 rate = 0; 210746900298SJohannes Berg int own_rate; 21089ebb61a2SAshok Nagarajan bool is_basic; 210946900298SJohannes Berg if (i < elems->supp_rates_len) 211046900298SJohannes Berg rate = elems->supp_rates[i]; 211146900298SJohannes Berg else if (elems->ext_supp_rates) 211246900298SJohannes Berg rate = elems->ext_supp_rates 211346900298SJohannes Berg [i - elems->supp_rates_len]; 211446900298SJohannes Berg own_rate = 5 * (rate & 0x7f); 21159ebb61a2SAshok Nagarajan is_basic = !!(rate & 0x80); 21169ebb61a2SAshok Nagarajan 21179ebb61a2SAshok Nagarajan if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) 21189ebb61a2SAshok Nagarajan continue; 21199ebb61a2SAshok Nagarajan 21209ebb61a2SAshok Nagarajan for (j = 0; j < num_rates; j++) { 21212103dec1SSimon Wunderlich int brate; 21222103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[j].flags) 21232103dec1SSimon Wunderlich != rate_flags) 21242103dec1SSimon Wunderlich continue; 21252103dec1SSimon Wunderlich 21262103dec1SSimon Wunderlich brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 21272103dec1SSimon Wunderlich 1 << shift); 21282103dec1SSimon Wunderlich 21292103dec1SSimon Wunderlich if (brate == own_rate) { 213046900298SJohannes Berg supp_rates |= BIT(j); 21319ebb61a2SAshok Nagarajan if (basic_rates && is_basic) 21329ebb61a2SAshok Nagarajan *basic_rates |= BIT(j); 21339ebb61a2SAshok Nagarajan } 21349ebb61a2SAshok Nagarajan } 213546900298SJohannes Berg } 213646900298SJohannes Berg return supp_rates; 213746900298SJohannes Berg } 2138f2753ddbSJohannes Berg 213984f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local) 214084f6a01cSJohannes Berg { 214184f6a01cSJohannes Berg ieee80211_led_radio(local, false); 214267408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); 214384f6a01cSJohannes Berg 214484f6a01cSJohannes Berg cancel_work_sync(&local->reconfig_filter); 214584f6a01cSJohannes Berg 214684f6a01cSJohannes Berg flush_workqueue(local->workqueue); 2147678f415fSLennert Buytenhek drv_stop(local); 214884f6a01cSJohannes Berg } 214984f6a01cSJohannes Berg 215074430f94SJohannes Berg static void ieee80211_flush_completed_scan(struct ieee80211_local *local, 215174430f94SJohannes Berg bool aborted) 215274430f94SJohannes Berg { 215374430f94SJohannes Berg /* It's possible that we don't handle the scan completion in 215474430f94SJohannes Berg * time during suspend, so if it's still marked as completed 215574430f94SJohannes Berg * here, queue the work and flush it to clean things up. 215674430f94SJohannes Berg * Instead of calling the worker function directly here, we 215774430f94SJohannes Berg * really queue it to avoid potential races with other flows 215874430f94SJohannes Berg * scheduling the same work. 215974430f94SJohannes Berg */ 216074430f94SJohannes Berg if (test_bit(SCAN_COMPLETED, &local->scanning)) { 216174430f94SJohannes Berg /* If coming from reconfiguration failure, abort the scan so 216274430f94SJohannes Berg * we don't attempt to continue a partial HW scan - which is 216374430f94SJohannes Berg * possible otherwise if (e.g.) the 2.4 GHz portion was the 216474430f94SJohannes Berg * completed scan, and a 5 GHz portion is still pending. 216574430f94SJohannes Berg */ 216674430f94SJohannes Berg if (aborted) 216774430f94SJohannes Berg set_bit(SCAN_ABORTED, &local->scanning); 216874430f94SJohannes Berg ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); 216974430f94SJohannes Berg flush_delayed_work(&local->scan_work); 217074430f94SJohannes Berg } 217174430f94SJohannes Berg } 217274430f94SJohannes Berg 2173f6837ba8SJohannes Berg static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) 2174f6837ba8SJohannes Berg { 2175f6837ba8SJohannes Berg struct ieee80211_sub_if_data *sdata; 2176f6837ba8SJohannes Berg struct ieee80211_chanctx *ctx; 2177f6837ba8SJohannes Berg 2178f6837ba8SJohannes Berg /* 2179f6837ba8SJohannes Berg * We get here if during resume the device can't be restarted properly. 2180f6837ba8SJohannes Berg * We might also get here if this happens during HW reset, which is a 2181f6837ba8SJohannes Berg * slightly different situation and we need to drop all connections in 2182f6837ba8SJohannes Berg * the latter case. 2183f6837ba8SJohannes Berg * 2184f6837ba8SJohannes Berg * Ask cfg80211 to turn off all interfaces, this will result in more 2185f6837ba8SJohannes Berg * warnings but at least we'll then get into a clean stopped state. 2186f6837ba8SJohannes Berg */ 2187f6837ba8SJohannes Berg 2188f6837ba8SJohannes Berg local->resuming = false; 2189f6837ba8SJohannes Berg local->suspended = false; 21907584f88fSEliad Peller local->in_reconfig = false; 2191f6837ba8SJohannes Berg 219274430f94SJohannes Berg ieee80211_flush_completed_scan(local, true); 219374430f94SJohannes Berg 2194f6837ba8SJohannes Berg /* scheduled scan clearly can't be running any more, but tell 2195f6837ba8SJohannes Berg * cfg80211 and clear local state 2196f6837ba8SJohannes Berg */ 2197f6837ba8SJohannes Berg ieee80211_sched_scan_end(local); 2198f6837ba8SJohannes Berg 2199f6837ba8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) 2200f6837ba8SJohannes Berg sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; 2201f6837ba8SJohannes Berg 2202f6837ba8SJohannes Berg /* Mark channel contexts as not being in the driver any more to avoid 2203f6837ba8SJohannes Berg * removing them from the driver during the shutdown process... 2204f6837ba8SJohannes Berg */ 2205f6837ba8SJohannes Berg mutex_lock(&local->chanctx_mtx); 2206f6837ba8SJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list) 2207f6837ba8SJohannes Berg ctx->driver_present = false; 2208f6837ba8SJohannes Berg mutex_unlock(&local->chanctx_mtx); 2209f6837ba8SJohannes Berg } 2210f6837ba8SJohannes Berg 2211153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local, 2212153a5fc4SStanislaw Gruszka struct ieee80211_sub_if_data *sdata) 2213153a5fc4SStanislaw Gruszka { 2214153a5fc4SStanislaw Gruszka struct ieee80211_chanctx_conf *conf; 2215153a5fc4SStanislaw Gruszka struct ieee80211_chanctx *ctx; 2216153a5fc4SStanislaw Gruszka 2217153a5fc4SStanislaw Gruszka if (!local->use_chanctx) 2218153a5fc4SStanislaw Gruszka return; 2219153a5fc4SStanislaw Gruszka 2220153a5fc4SStanislaw Gruszka mutex_lock(&local->chanctx_mtx); 2221153a5fc4SStanislaw Gruszka conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 2222153a5fc4SStanislaw Gruszka lockdep_is_held(&local->chanctx_mtx)); 2223153a5fc4SStanislaw Gruszka if (conf) { 2224153a5fc4SStanislaw Gruszka ctx = container_of(conf, struct ieee80211_chanctx, conf); 2225153a5fc4SStanislaw Gruszka drv_assign_vif_chanctx(local, sdata, ctx); 2226153a5fc4SStanislaw Gruszka } 2227153a5fc4SStanislaw Gruszka mutex_unlock(&local->chanctx_mtx); 2228153a5fc4SStanislaw Gruszka } 2229153a5fc4SStanislaw Gruszka 22301ea2c864SJohannes Berg static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) 22311ea2c864SJohannes Berg { 22321ea2c864SJohannes Berg struct ieee80211_local *local = sdata->local; 22331ea2c864SJohannes Berg struct sta_info *sta; 22341ea2c864SJohannes Berg 22351ea2c864SJohannes Berg /* add STAs back */ 22361ea2c864SJohannes Berg mutex_lock(&local->sta_mtx); 22371ea2c864SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 22381ea2c864SJohannes Berg enum ieee80211_sta_state state; 22391ea2c864SJohannes Berg 22401ea2c864SJohannes Berg if (!sta->uploaded || sta->sdata != sdata) 22411ea2c864SJohannes Berg continue; 22421ea2c864SJohannes Berg 22431ea2c864SJohannes Berg for (state = IEEE80211_STA_NOTEXIST; 22441ea2c864SJohannes Berg state < sta->sta_state; state++) 22451ea2c864SJohannes Berg WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 22461ea2c864SJohannes Berg state + 1)); 22471ea2c864SJohannes Berg } 22481ea2c864SJohannes Berg mutex_unlock(&local->sta_mtx); 22491ea2c864SJohannes Berg } 22501ea2c864SJohannes Berg 2251167e33f4SAyala Beker static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) 2252167e33f4SAyala Beker { 2253167e33f4SAyala Beker struct cfg80211_nan_func *func, **funcs; 2254167e33f4SAyala Beker int res, id, i = 0; 2255167e33f4SAyala Beker 2256167e33f4SAyala Beker res = drv_start_nan(sdata->local, sdata, 2257167e33f4SAyala Beker &sdata->u.nan.conf); 2258167e33f4SAyala Beker if (WARN_ON(res)) 2259167e33f4SAyala Beker return res; 2260167e33f4SAyala Beker 22616396bb22SKees Cook funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1, 22626396bb22SKees Cook sizeof(*funcs), 22636396bb22SKees Cook GFP_KERNEL); 2264167e33f4SAyala Beker if (!funcs) 2265167e33f4SAyala Beker return -ENOMEM; 2266167e33f4SAyala Beker 2267167e33f4SAyala Beker /* Add all the functions: 2268167e33f4SAyala Beker * This is a little bit ugly. We need to call a potentially sleeping 2269167e33f4SAyala Beker * callback for each NAN function, so we can't hold the spinlock. 2270167e33f4SAyala Beker */ 2271167e33f4SAyala Beker spin_lock_bh(&sdata->u.nan.func_lock); 2272167e33f4SAyala Beker 2273167e33f4SAyala Beker idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) 2274167e33f4SAyala Beker funcs[i++] = func; 2275167e33f4SAyala Beker 2276167e33f4SAyala Beker spin_unlock_bh(&sdata->u.nan.func_lock); 2277167e33f4SAyala Beker 2278167e33f4SAyala Beker for (i = 0; funcs[i]; i++) { 2279167e33f4SAyala Beker res = drv_add_nan_func(sdata->local, sdata, funcs[i]); 2280167e33f4SAyala Beker if (WARN_ON(res)) 2281167e33f4SAyala Beker ieee80211_nan_func_terminated(&sdata->vif, 2282167e33f4SAyala Beker funcs[i]->instance_id, 2283167e33f4SAyala Beker NL80211_NAN_FUNC_TERM_REASON_ERROR, 2284167e33f4SAyala Beker GFP_KERNEL); 2285167e33f4SAyala Beker } 2286167e33f4SAyala Beker 2287167e33f4SAyala Beker kfree(funcs); 2288167e33f4SAyala Beker 2289167e33f4SAyala Beker return 0; 2290167e33f4SAyala Beker } 2291167e33f4SAyala Beker 2292f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local) 2293f2753ddbSJohannes Berg { 2294f2753ddbSJohannes Berg struct ieee80211_hw *hw = &local->hw; 2295f2753ddbSJohannes Berg struct ieee80211_sub_if_data *sdata; 229655de908aSJohannes Berg struct ieee80211_chanctx *ctx; 2297f2753ddbSJohannes Berg struct sta_info *sta; 22982683d65bSEliad Peller int res, i; 2299d888130aSJohannes Berg bool reconfig_due_to_wowlan = false; 2300d43c6b6eSDavid Spinadel struct ieee80211_sub_if_data *sched_scan_sdata; 23016ea0a69cSJohannes Berg struct cfg80211_sched_scan_request *sched_scan_req; 2302d43c6b6eSDavid Spinadel bool sched_scan_stopped = false; 2303b0485e9fSEliad Peller bool suspended = local->suspended; 2304d888130aSJohannes Berg 23050f8b8245SEliad Peller /* nothing to do if HW shouldn't run */ 23060f8b8245SEliad Peller if (!local->open_count) 23070f8b8245SEliad Peller goto wake_up; 23080f8b8245SEliad Peller 23098f21b0adSJohannes Berg #ifdef CONFIG_PM 2310b0485e9fSEliad Peller if (suspended) 2311ceb99fe0SJohannes Berg local->resuming = true; 2312f2753ddbSJohannes Berg 2313eecc4800SJohannes Berg if (local->wowlan) { 2314b0485e9fSEliad Peller /* 2315b0485e9fSEliad Peller * In the wowlan case, both mac80211 and the device 2316b0485e9fSEliad Peller * are functional when the resume op is called, so 2317b0485e9fSEliad Peller * clear local->suspended so the device could operate 2318b0485e9fSEliad Peller * normally (e.g. pass rx frames). 2319b0485e9fSEliad Peller */ 2320b0485e9fSEliad Peller local->suspended = false; 2321eecc4800SJohannes Berg res = drv_resume(local); 232227b3eb9cSJohannes Berg local->wowlan = false; 2323eecc4800SJohannes Berg if (res < 0) { 2324eecc4800SJohannes Berg local->resuming = false; 2325eecc4800SJohannes Berg return res; 2326eecc4800SJohannes Berg } 2327eecc4800SJohannes Berg if (res == 0) 2328eecc4800SJohannes Berg goto wake_up; 2329eecc4800SJohannes Berg WARN_ON(res > 1); 2330eecc4800SJohannes Berg /* 2331eecc4800SJohannes Berg * res is 1, which means the driver requested 2332eecc4800SJohannes Berg * to go through a regular reset on wakeup. 2333b0485e9fSEliad Peller * restore local->suspended in this case. 2334eecc4800SJohannes Berg */ 2335d888130aSJohannes Berg reconfig_due_to_wowlan = true; 2336b0485e9fSEliad Peller local->suspended = true; 2337eecc4800SJohannes Berg } 2338eecc4800SJohannes Berg #endif 233994f9b97bSJohannes Berg 234024feda00SLuis R. Rodriguez /* 234143d6df00SEliad Peller * In case of hw_restart during suspend (without wowlan), 234243d6df00SEliad Peller * cancel restart work, as we are reconfiguring the device 234343d6df00SEliad Peller * anyway. 234443d6df00SEliad Peller * Note that restart_work is scheduled on a frozen workqueue, 234543d6df00SEliad Peller * so we can't deadlock in this case. 234643d6df00SEliad Peller */ 234743d6df00SEliad Peller if (suspended && local->in_reconfig && !reconfig_due_to_wowlan) 234843d6df00SEliad Peller cancel_work_sync(&local->restart_work); 234943d6df00SEliad Peller 2350968a76ceSEliad Peller local->started = false; 2351968a76ceSEliad Peller 235243d6df00SEliad Peller /* 235324feda00SLuis R. Rodriguez * Upon resume hardware can sometimes be goofy due to 235424feda00SLuis R. Rodriguez * various platform / driver / bus issues, so restarting 235524feda00SLuis R. Rodriguez * the device may at times not work immediately. Propagate 235624feda00SLuis R. Rodriguez * the error. 235724feda00SLuis R. Rodriguez */ 235824487981SJohannes Berg res = drv_start(local); 235924feda00SLuis R. Rodriguez if (res) { 2360b0485e9fSEliad Peller if (suspended) 2361f6837ba8SJohannes Berg WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); 2362f6837ba8SJohannes Berg else 2363f6837ba8SJohannes Berg WARN(1, "Hardware became unavailable during restart.\n"); 2364f6837ba8SJohannes Berg ieee80211_handle_reconfig_failure(local); 236524feda00SLuis R. Rodriguez return res; 236624feda00SLuis R. Rodriguez } 2367f2753ddbSJohannes Berg 23687f281975SYogesh Ashok Powar /* setup fragmentation threshold */ 23697f281975SYogesh Ashok Powar drv_set_frag_threshold(local, hw->wiphy->frag_threshold); 23707f281975SYogesh Ashok Powar 23717f281975SYogesh Ashok Powar /* setup RTS threshold */ 23727f281975SYogesh Ashok Powar drv_set_rts_threshold(local, hw->wiphy->rts_threshold); 23737f281975SYogesh Ashok Powar 23747f281975SYogesh Ashok Powar /* reset coverage class */ 23757f281975SYogesh Ashok Powar drv_set_coverage_class(local, hw->wiphy->coverage_class); 23767f281975SYogesh Ashok Powar 23771f87f7d3SJohannes Berg ieee80211_led_radio(local, true); 237867408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 237967408c8cSJohannes Berg IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); 2380f2753ddbSJohannes Berg 2381f2753ddbSJohannes Berg /* add interfaces */ 23824b6f1dd6SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 23834b6f1dd6SJohannes Berg if (sdata) { 23843c3e21e7SJohannes Berg /* in HW restart it exists already */ 23853c3e21e7SJohannes Berg WARN_ON(local->resuming); 23864b6f1dd6SJohannes Berg res = drv_add_interface(local, sdata); 23874b6f1dd6SJohannes Berg if (WARN_ON(res)) { 23880c2bef46SMonam Agarwal RCU_INIT_POINTER(local->monitor_sdata, NULL); 23894b6f1dd6SJohannes Berg synchronize_net(); 23904b6f1dd6SJohannes Berg kfree(sdata); 23914b6f1dd6SJohannes Berg } 23924b6f1dd6SJohannes Berg } 23934b6f1dd6SJohannes Berg 2394f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 2395f2753ddbSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 2396f2753ddbSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR && 2397c8fff3dcSLuciano Coelho ieee80211_sdata_running(sdata)) { 23987b7eab6fSJohannes Berg res = drv_add_interface(local, sdata); 2399c8fff3dcSLuciano Coelho if (WARN_ON(res)) 2400c8fff3dcSLuciano Coelho break; 2401c8fff3dcSLuciano Coelho } 2402c8fff3dcSLuciano Coelho } 2403c8fff3dcSLuciano Coelho 2404c8fff3dcSLuciano Coelho /* If adding any of the interfaces failed above, roll back and 2405c8fff3dcSLuciano Coelho * report failure. 2406c8fff3dcSLuciano Coelho */ 2407c8fff3dcSLuciano Coelho if (res) { 2408c8fff3dcSLuciano Coelho list_for_each_entry_continue_reverse(sdata, &local->interfaces, 2409c8fff3dcSLuciano Coelho list) 2410c8fff3dcSLuciano Coelho if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 2411c8fff3dcSLuciano Coelho sdata->vif.type != NL80211_IFTYPE_MONITOR && 2412c8fff3dcSLuciano Coelho ieee80211_sdata_running(sdata)) 2413c8fff3dcSLuciano Coelho drv_remove_interface(local, sdata); 2414c8fff3dcSLuciano Coelho ieee80211_handle_reconfig_failure(local); 2415c8fff3dcSLuciano Coelho return res; 2416f2753ddbSJohannes Berg } 2417f2753ddbSJohannes Berg 241855de908aSJohannes Berg /* add channel contexts */ 2419f0dea9c7SArend van Spriel if (local->use_chanctx) { 242055de908aSJohannes Berg mutex_lock(&local->chanctx_mtx); 242155de908aSJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list) 24225bcae31dSMichal Kazior if (ctx->replace_state != 24235bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 242455de908aSJohannes Berg WARN_ON(drv_add_chanctx(local, ctx)); 242555de908aSJohannes Berg mutex_unlock(&local->chanctx_mtx); 242655de908aSJohannes Berg 2427fe5f2559SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 2428153a5fc4SStanislaw Gruszka if (sdata && ieee80211_sdata_running(sdata)) 2429153a5fc4SStanislaw Gruszka ieee80211_assign_chanctx(local, sdata); 24307df180f7SZhao, Gang } 2431fe5f2559SJohannes Berg 2432f2753ddbSJohannes Berg /* reconfigure hardware */ 2433f2753ddbSJohannes Berg ieee80211_hw_config(local, ~0); 2434f2753ddbSJohannes Berg 2435f2753ddbSJohannes Berg ieee80211_configure_filter(local); 2436f2753ddbSJohannes Berg 2437f2753ddbSJohannes Berg /* Finally also reconfigure all the BSS information */ 2438f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 2439ac8dd506SJohannes Berg u32 changed; 2440ac8dd506SJohannes Berg 24419607e6b6SJohannes Berg if (!ieee80211_sdata_running(sdata)) 2442f2753ddbSJohannes Berg continue; 2443ac8dd506SJohannes Berg 24441ea2c864SJohannes Berg ieee80211_assign_chanctx(local, sdata); 24451ea2c864SJohannes Berg 24461ea2c864SJohannes Berg switch (sdata->vif.type) { 24471ea2c864SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 24481ea2c864SJohannes Berg case NL80211_IFTYPE_MONITOR: 24491ea2c864SJohannes Berg break; 24504926b51bSJohannes Berg case NL80211_IFTYPE_ADHOC: 24514926b51bSJohannes Berg if (sdata->vif.bss_conf.ibss_joined) 24524926b51bSJohannes Berg WARN_ON(drv_join_ibss(local, sdata)); 2453fc0561dcSGustavo A. R. Silva fallthrough; 24541ea2c864SJohannes Berg default: 24551ea2c864SJohannes Berg ieee80211_reconfig_stations(sdata); 2456fc0561dcSGustavo A. R. Silva fallthrough; 24571ea2c864SJohannes Berg case NL80211_IFTYPE_AP: /* AP stations are handled later */ 24581ea2c864SJohannes Berg for (i = 0; i < IEEE80211_NUM_ACS; i++) 24591ea2c864SJohannes Berg drv_conf_tx(local, sdata, i, 24601ea2c864SJohannes Berg &sdata->tx_conf[i]); 24611ea2c864SJohannes Berg break; 24621ea2c864SJohannes Berg } 24631ea2c864SJohannes Berg 2464ac8dd506SJohannes Berg /* common change flags for all interface types */ 2465ac8dd506SJohannes Berg changed = BSS_CHANGED_ERP_CTS_PROT | 2466ac8dd506SJohannes Berg BSS_CHANGED_ERP_PREAMBLE | 2467ac8dd506SJohannes Berg BSS_CHANGED_ERP_SLOT | 2468ac8dd506SJohannes Berg BSS_CHANGED_HT | 2469ac8dd506SJohannes Berg BSS_CHANGED_BASIC_RATES | 2470ac8dd506SJohannes Berg BSS_CHANGED_BEACON_INT | 2471ac8dd506SJohannes Berg BSS_CHANGED_BSSID | 24724ced3f74SJohannes Berg BSS_CHANGED_CQM | 247355de47f6SEliad Peller BSS_CHANGED_QOS | 24741ea6f9c0SJohannes Berg BSS_CHANGED_IDLE | 2475dcbe73caSPradeep Kumar Chitrapu BSS_CHANGED_TXPOWER | 2476dcbe73caSPradeep Kumar Chitrapu BSS_CHANGED_MCAST_RATE; 2477ac8dd506SJohannes Berg 2478b5a33d52SSara Sharon if (sdata->vif.mu_mimo_owner) 247923a1f8d4SSara Sharon changed |= BSS_CHANGED_MU_GROUPS; 248023a1f8d4SSara Sharon 2481f2753ddbSJohannes Berg switch (sdata->vif.type) { 2482f2753ddbSJohannes Berg case NL80211_IFTYPE_STATION: 24830d392e93SEliad Peller changed |= BSS_CHANGED_ASSOC | 2484ab095877SEliad Peller BSS_CHANGED_ARP_FILTER | 2485ab095877SEliad Peller BSS_CHANGED_PS; 2486c65dd147SEmmanuel Grumbach 2487989c6505SAlexander Bondar /* Re-send beacon info report to the driver */ 2488989c6505SAlexander Bondar if (sdata->u.mgd.have_beacon) 2489989c6505SAlexander Bondar changed |= BSS_CHANGED_BEACON_INFO; 2490c65dd147SEmmanuel Grumbach 2491e38a017bSAvraham Stern if (sdata->vif.bss_conf.max_idle_period || 2492e38a017bSAvraham Stern sdata->vif.bss_conf.protected_keep_alive) 2493e38a017bSAvraham Stern changed |= BSS_CHANGED_KEEP_ALIVE; 2494e38a017bSAvraham Stern 24958d61ffa5SJohannes Berg sdata_lock(sdata); 2496ac8dd506SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 24978d61ffa5SJohannes Berg sdata_unlock(sdata); 2498ac8dd506SJohannes Berg break; 24996e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 2500239281f8SRostislav Lisovy changed |= BSS_CHANGED_OCB; 2501239281f8SRostislav Lisovy ieee80211_bss_info_change_notify(sdata, changed); 25026e0bd6c3SRostislav Lisovy break; 2503f2753ddbSJohannes Berg case NL80211_IFTYPE_ADHOC: 2504ac8dd506SJohannes Berg changed |= BSS_CHANGED_IBSS; 2505fc0561dcSGustavo A. R. Silva fallthrough; 2506f2753ddbSJohannes Berg case NL80211_IFTYPE_AP: 2507339afbf4SJohannes Berg changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; 2508e7979ac7SArik Nemtsov 2509bc847970SPradeep Kumar Chitrapu if (sdata->vif.bss_conf.ftm_responder == 1 && 2510bc847970SPradeep Kumar Chitrapu wiphy_ext_feature_isset(sdata->local->hw.wiphy, 2511bc847970SPradeep Kumar Chitrapu NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) 2512bc847970SPradeep Kumar Chitrapu changed |= BSS_CHANGED_FTM_RESPONDER; 2513bc847970SPradeep Kumar Chitrapu 25141041638fSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP) { 2515e7979ac7SArik Nemtsov changed |= BSS_CHANGED_AP_PROBE_RESP; 2516e7979ac7SArik Nemtsov 25171041638fSJohannes Berg if (rcu_access_pointer(sdata->u.ap.beacon)) 25181041638fSJohannes Berg drv_start_ap(local, sdata); 25191041638fSJohannes Berg } 2520fc0561dcSGustavo A. R. Silva fallthrough; 2521f2753ddbSJohannes Berg case NL80211_IFTYPE_MESH_POINT: 25228da34932SJohannes Berg if (sdata->vif.bss_conf.enable_beacon) { 2523ac8dd506SJohannes Berg changed |= BSS_CHANGED_BEACON | 2524ac8dd506SJohannes Berg BSS_CHANGED_BEACON_ENABLED; 25252d0ddec5SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 25268da34932SJohannes Berg } 2527f2753ddbSJohannes Berg break; 2528167e33f4SAyala Beker case NL80211_IFTYPE_NAN: 2529167e33f4SAyala Beker res = ieee80211_reconfig_nan(sdata); 2530167e33f4SAyala Beker if (res < 0) { 2531167e33f4SAyala Beker ieee80211_handle_reconfig_failure(local); 2532167e33f4SAyala Beker return res; 2533167e33f4SAyala Beker } 2534167e33f4SAyala Beker break; 2535f2753ddbSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 2536f2753ddbSJohannes Berg case NL80211_IFTYPE_MONITOR: 253798104fdeSJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 2538b205786eSZhao, Gang /* nothing to do */ 2539f142c6b9SJohannes Berg break; 2540f2753ddbSJohannes Berg case NL80211_IFTYPE_UNSPECIFIED: 25412e161f78SJohannes Berg case NUM_NL80211_IFTYPES: 25422ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_CLIENT: 25432ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_GO: 254470d9c599SJohannes Berg case NL80211_IFTYPE_WDS: 2545f2753ddbSJohannes Berg WARN_ON(1); 2546f2753ddbSJohannes Berg break; 2547f2753ddbSJohannes Berg } 2548f2753ddbSJohannes Berg } 2549f2753ddbSJohannes Berg 25504a733ef1SJohannes Berg ieee80211_recalc_ps(local); 25518e1b23b9SEyal Shapira 25522a419056SJohannes Berg /* 25536e1b1b24SEliad Peller * The sta might be in psm against the ap (e.g. because 25546e1b1b24SEliad Peller * this was the state before a hw restart), so we 25556e1b1b24SEliad Peller * explicitly send a null packet in order to make sure 25566e1b1b24SEliad Peller * it'll sync against the ap (and get out of psm). 25576e1b1b24SEliad Peller */ 25586e1b1b24SEliad Peller if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { 25596e1b1b24SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) { 25606e1b1b24SEliad Peller if (sdata->vif.type != NL80211_IFTYPE_STATION) 25616e1b1b24SEliad Peller continue; 256220f544eeSJohannes Berg if (!sdata->u.mgd.associated) 256320f544eeSJohannes Berg continue; 25646e1b1b24SEliad Peller 2565076cdcb1SJohannes Berg ieee80211_send_nullfunc(local, sdata, false); 25666e1b1b24SEliad Peller } 25676e1b1b24SEliad Peller } 25686e1b1b24SEliad Peller 25692e8d397eSArik Nemtsov /* APs are now beaconing, add back stations */ 25702e8d397eSArik Nemtsov mutex_lock(&local->sta_mtx); 25712e8d397eSArik Nemtsov list_for_each_entry(sta, &local->sta_list, list) { 25722e8d397eSArik Nemtsov enum ieee80211_sta_state state; 25732e8d397eSArik Nemtsov 25742e8d397eSArik Nemtsov if (!sta->uploaded) 25752e8d397eSArik Nemtsov continue; 25762e8d397eSArik Nemtsov 257719103a4bSmpubbise@codeaurora.org if (sta->sdata->vif.type != NL80211_IFTYPE_AP && 257819103a4bSmpubbise@codeaurora.org sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN) 25792e8d397eSArik Nemtsov continue; 25802e8d397eSArik Nemtsov 25812e8d397eSArik Nemtsov for (state = IEEE80211_STA_NOTEXIST; 25822e8d397eSArik Nemtsov state < sta->sta_state; state++) 25832e8d397eSArik Nemtsov WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 25842e8d397eSArik Nemtsov state + 1)); 25852e8d397eSArik Nemtsov } 25862e8d397eSArik Nemtsov mutex_unlock(&local->sta_mtx); 25872e8d397eSArik Nemtsov 25887b21aea0SEyal Shapira /* add back keys */ 25897b21aea0SEyal Shapira list_for_each_entry(sdata, &local->interfaces, list) 2590624ff4b2SLior Cohen ieee80211_reenable_keys(sdata); 25917b21aea0SEyal Shapira 25920d440ea2SEliad Peller /* Reconfigure sched scan if it was interrupted by FW restart */ 25930d440ea2SEliad Peller mutex_lock(&local->mtx); 25940d440ea2SEliad Peller sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, 25950d440ea2SEliad Peller lockdep_is_held(&local->mtx)); 25960d440ea2SEliad Peller sched_scan_req = rcu_dereference_protected(local->sched_scan_req, 25970d440ea2SEliad Peller lockdep_is_held(&local->mtx)); 25980d440ea2SEliad Peller if (sched_scan_sdata && sched_scan_req) 25990d440ea2SEliad Peller /* 26000d440ea2SEliad Peller * Sched scan stopped, but we don't want to report it. Instead, 26010d440ea2SEliad Peller * we're trying to reschedule. However, if more than one scan 26020d440ea2SEliad Peller * plan was set, we cannot reschedule since we don't know which 26030d440ea2SEliad Peller * scan plan was currently running (and some scan plans may have 26040d440ea2SEliad Peller * already finished). 26050d440ea2SEliad Peller */ 26060d440ea2SEliad Peller if (sched_scan_req->n_scan_plans > 1 || 26070d440ea2SEliad Peller __ieee80211_request_sched_scan_start(sched_scan_sdata, 2608b9f628fcSEliad Peller sched_scan_req)) { 2609b9f628fcSEliad Peller RCU_INIT_POINTER(local->sched_scan_sdata, NULL); 2610b9f628fcSEliad Peller RCU_INIT_POINTER(local->sched_scan_req, NULL); 26110d440ea2SEliad Peller sched_scan_stopped = true; 2612b9f628fcSEliad Peller } 26130d440ea2SEliad Peller mutex_unlock(&local->mtx); 26140d440ea2SEliad Peller 26150d440ea2SEliad Peller if (sched_scan_stopped) 2616a05829a7SJohannes Berg cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); 26170d440ea2SEliad Peller 2618c6209488SEliad Peller wake_up: 2619470f4d61SEliad Peller 26203c3e21e7SJohannes Berg if (local->monitors == local->open_count && local->monitors > 0) 26213c3e21e7SJohannes Berg ieee80211_add_virtual_monitor(local); 26223c3e21e7SJohannes Berg 26236e1b1b24SEliad Peller /* 26242a419056SJohannes Berg * Clear the WLAN_STA_BLOCK_BA flag so new aggregation 26252a419056SJohannes Berg * sessions can be established after a resume. 26262a419056SJohannes Berg * 26272a419056SJohannes Berg * Also tear down aggregation sessions since reconfiguring 26282a419056SJohannes Berg * them in a hardware restart scenario is not easily done 26292a419056SJohannes Berg * right now, and the hardware will have lost information 26302a419056SJohannes Berg * about the sessions, but we and the AP still think they 26312a419056SJohannes Berg * are active. This is really a workaround though. 26322a419056SJohannes Berg */ 263330686bf7SJohannes Berg if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { 26342a419056SJohannes Berg mutex_lock(&local->sta_mtx); 26352a419056SJohannes Berg 26362a419056SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 263727392719SEliad Peller if (!local->resuming) 2638c82c4a80SJohannes Berg ieee80211_sta_tear_down_BA_sessions( 2639c82c4a80SJohannes Berg sta, AGG_STOP_LOCAL_REQUEST); 2640c2c98fdeSJohannes Berg clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 264174e2bd1fSWey-Yi Guy } 26422a419056SJohannes Berg 26432a419056SJohannes Berg mutex_unlock(&local->sta_mtx); 264474e2bd1fSWey-Yi Guy } 264574e2bd1fSWey-Yi Guy 26462316380fSSara Sharon if (local->in_reconfig) { 26472316380fSSara Sharon local->in_reconfig = false; 26482316380fSSara Sharon barrier(); 26492316380fSSara Sharon 26502316380fSSara Sharon /* Restart deferred ROCs */ 26512316380fSSara Sharon mutex_lock(&local->mtx); 26522316380fSSara Sharon ieee80211_start_next_roc(local); 26532316380fSSara Sharon mutex_unlock(&local->mtx); 2654f8891461SNaftali Goldstein 2655f8891461SNaftali Goldstein /* Requeue all works */ 2656f8891461SNaftali Goldstein list_for_each_entry(sdata, &local->interfaces, list) 2657f8891461SNaftali Goldstein ieee80211_queue_work(&local->hw, &sdata->work); 26582316380fSSara Sharon } 26592316380fSSara Sharon 2660445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 2661cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SUSPEND, 2662cca07b00SLuciano Coelho false); 2663f2753ddbSJohannes Berg 26645bb644a0SJohannes Berg /* 26655bb644a0SJohannes Berg * If this is for hw restart things are still running. 26665bb644a0SJohannes Berg * We may want to change that later, however. 26675bb644a0SJohannes Berg */ 2668b0485e9fSEliad Peller if (local->open_count && (!suspended || reconfig_due_to_wowlan)) 2669cf2c92d8SEliad Peller drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); 26708f21b0adSJohannes Berg 2671b0485e9fSEliad Peller if (!suspended) 26725bb644a0SJohannes Berg return 0; 26735bb644a0SJohannes Berg 26745bb644a0SJohannes Berg #ifdef CONFIG_PM 2675ceb99fe0SJohannes Berg /* first set suspended false, then resuming */ 26765bb644a0SJohannes Berg local->suspended = false; 2677ceb99fe0SJohannes Berg mb(); 2678ceb99fe0SJohannes Berg local->resuming = false; 26795bb644a0SJohannes Berg 268074430f94SJohannes Berg ieee80211_flush_completed_scan(local, false); 26819120d94eSLuciano Coelho 26820f8b8245SEliad Peller if (local->open_count && !reconfig_due_to_wowlan) 2683cf2c92d8SEliad Peller drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); 2684cf2c92d8SEliad Peller 2685b8360ab8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 2686b8360ab8SJohannes Berg if (!ieee80211_sdata_running(sdata)) 2687b8360ab8SJohannes Berg continue; 2688b8360ab8SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION) 2689b8360ab8SJohannes Berg ieee80211_sta_restart(sdata); 2690b8360ab8SJohannes Berg } 2691b8360ab8SJohannes Berg 269226d59535SJohannes Berg mod_timer(&local->sta_cleanup, jiffies + 1); 26935bb644a0SJohannes Berg #else 26945bb644a0SJohannes Berg WARN_ON(1); 26955bb644a0SJohannes Berg #endif 2696d43c6b6eSDavid Spinadel 2697f2753ddbSJohannes Berg return 0; 2698f2753ddbSJohannes Berg } 269942935ecaSLuis R. Rodriguez 270095acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif) 270195acac61SJohannes Berg { 270295acac61SJohannes Berg struct ieee80211_sub_if_data *sdata; 270395acac61SJohannes Berg struct ieee80211_local *local; 270495acac61SJohannes Berg struct ieee80211_key *key; 270595acac61SJohannes Berg 270695acac61SJohannes Berg if (WARN_ON(!vif)) 270795acac61SJohannes Berg return; 270895acac61SJohannes Berg 270995acac61SJohannes Berg sdata = vif_to_sdata(vif); 271095acac61SJohannes Berg local = sdata->local; 271195acac61SJohannes Berg 271295acac61SJohannes Berg if (WARN_ON(!local->resuming)) 271395acac61SJohannes Berg return; 271495acac61SJohannes Berg 271595acac61SJohannes Berg if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 271695acac61SJohannes Berg return; 271795acac61SJohannes Berg 271895acac61SJohannes Berg sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; 271995acac61SJohannes Berg 272095acac61SJohannes Berg mutex_lock(&local->key_mtx); 272195acac61SJohannes Berg list_for_each_entry(key, &sdata->key_list, list) 272295acac61SJohannes Berg key->flags |= KEY_FLAG_TAINTED; 272395acac61SJohannes Berg mutex_unlock(&local->key_mtx); 272495acac61SJohannes Berg } 272595acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 272695acac61SJohannes Berg 272704ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) 27280f78231bSJohannes Berg { 272904ecd257SJohannes Berg struct ieee80211_local *local = sdata->local; 273004ecd257SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 273104ecd257SJohannes Berg struct ieee80211_chanctx *chanctx; 27320f78231bSJohannes Berg 273304ecd257SJohannes Berg mutex_lock(&local->chanctx_mtx); 27340f78231bSJohannes Berg 273504ecd257SJohannes Berg chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 273604ecd257SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 27370f78231bSJohannes Berg 2738c189a685SAndrei Otcheretianski /* 2739c189a685SAndrei Otcheretianski * This function can be called from a work, thus it may be possible 2740c189a685SAndrei Otcheretianski * that the chanctx_conf is removed (due to a disconnection, for 2741c189a685SAndrei Otcheretianski * example). 2742c189a685SAndrei Otcheretianski * So nothing should be done in such case. 2743c189a685SAndrei Otcheretianski */ 2744c189a685SAndrei Otcheretianski if (!chanctx_conf) 27455d8e4237SJohannes Berg goto unlock; 27460f78231bSJohannes Berg 274704ecd257SJohannes Berg chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 274804ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, chanctx); 27495d8e4237SJohannes Berg unlock: 275004ecd257SJohannes Berg mutex_unlock(&local->chanctx_mtx); 27510f78231bSJohannes Berg } 27528e664fb3SJohannes Berg 275321f659bfSEliad Peller void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) 275421f659bfSEliad Peller { 275521f659bfSEliad Peller struct ieee80211_local *local = sdata->local; 275621f659bfSEliad Peller struct ieee80211_chanctx_conf *chanctx_conf; 275721f659bfSEliad Peller struct ieee80211_chanctx *chanctx; 275821f659bfSEliad Peller 275921f659bfSEliad Peller mutex_lock(&local->chanctx_mtx); 276021f659bfSEliad Peller 276121f659bfSEliad Peller chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 276221f659bfSEliad Peller lockdep_is_held(&local->chanctx_mtx)); 276321f659bfSEliad Peller 276421f659bfSEliad Peller if (WARN_ON_ONCE(!chanctx_conf)) 276521f659bfSEliad Peller goto unlock; 276621f659bfSEliad Peller 276721f659bfSEliad Peller chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 276821f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, chanctx); 276921f659bfSEliad Peller unlock: 277021f659bfSEliad Peller mutex_unlock(&local->chanctx_mtx); 277121f659bfSEliad Peller } 277221f659bfSEliad Peller 27738e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) 27748e664fb3SJohannes Berg { 27758e664fb3SJohannes Berg size_t pos = offset; 27768e664fb3SJohannes Berg 27778e664fb3SJohannes Berg while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) 27788e664fb3SJohannes Berg pos += 2 + ies[pos + 1]; 27798e664fb3SJohannes Berg 27808e664fb3SJohannes Berg return pos; 27818e664fb3SJohannes Berg } 2782615f7b9bSMeenakshi Venkataraman 2783615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, 2784615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 2785615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 2786615f7b9bSMeenakshi Venkataraman { 2787615f7b9bSMeenakshi Venkataraman trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); 2788615f7b9bSMeenakshi Venkataraman 2789615f7b9bSMeenakshi Venkataraman if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 2790615f7b9bSMeenakshi Venkataraman return; 2791615f7b9bSMeenakshi Venkataraman 2792615f7b9bSMeenakshi Venkataraman /* 2793615f7b9bSMeenakshi Venkataraman * Scale up threshold values before storing it, as the RSSI averaging 2794615f7b9bSMeenakshi Venkataraman * algorithm uses a scaled up value as well. Change this scaling 2795615f7b9bSMeenakshi Venkataraman * factor if the RSSI averaging algorithm changes. 2796615f7b9bSMeenakshi Venkataraman */ 2797615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; 2798615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; 2799615f7b9bSMeenakshi Venkataraman } 2800615f7b9bSMeenakshi Venkataraman 2801615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, 2802615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 2803615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 2804615f7b9bSMeenakshi Venkataraman { 2805615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 2806615f7b9bSMeenakshi Venkataraman 2807615f7b9bSMeenakshi Venkataraman WARN_ON(rssi_min_thold == rssi_max_thold || 2808615f7b9bSMeenakshi Venkataraman rssi_min_thold > rssi_max_thold); 2809615f7b9bSMeenakshi Venkataraman 2810615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, 2811615f7b9bSMeenakshi Venkataraman rssi_max_thold); 2812615f7b9bSMeenakshi Venkataraman } 2813615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports); 2814615f7b9bSMeenakshi Venkataraman 2815615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) 2816615f7b9bSMeenakshi Venkataraman { 2817615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 2818615f7b9bSMeenakshi Venkataraman 2819615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, 0, 0); 2820615f7b9bSMeenakshi Venkataraman } 2821615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports); 2822768db343SArik Nemtsov 2823ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 282442e7aa77SAlexander Simon u16 cap) 282542e7aa77SAlexander Simon { 282642e7aa77SAlexander Simon __le16 tmp; 282742e7aa77SAlexander Simon 282842e7aa77SAlexander Simon *pos++ = WLAN_EID_HT_CAPABILITY; 282942e7aa77SAlexander Simon *pos++ = sizeof(struct ieee80211_ht_cap); 283042e7aa77SAlexander Simon memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 283142e7aa77SAlexander Simon 283242e7aa77SAlexander Simon /* capability flags */ 283342e7aa77SAlexander Simon tmp = cpu_to_le16(cap); 283442e7aa77SAlexander Simon memcpy(pos, &tmp, sizeof(u16)); 283542e7aa77SAlexander Simon pos += sizeof(u16); 283642e7aa77SAlexander Simon 283742e7aa77SAlexander Simon /* AMPDU parameters */ 2838ef96a842SBen Greear *pos++ = ht_cap->ampdu_factor | 2839ef96a842SBen Greear (ht_cap->ampdu_density << 284042e7aa77SAlexander Simon IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); 284142e7aa77SAlexander Simon 284242e7aa77SAlexander Simon /* MCS set */ 2843ef96a842SBen Greear memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); 2844ef96a842SBen Greear pos += sizeof(ht_cap->mcs); 284542e7aa77SAlexander Simon 284642e7aa77SAlexander Simon /* extended capabilities */ 284742e7aa77SAlexander Simon pos += sizeof(__le16); 284842e7aa77SAlexander Simon 284942e7aa77SAlexander Simon /* BF capabilities */ 285042e7aa77SAlexander Simon pos += sizeof(__le32); 285142e7aa77SAlexander Simon 285242e7aa77SAlexander Simon /* antenna selection */ 285342e7aa77SAlexander Simon pos += sizeof(u8); 285442e7aa77SAlexander Simon 285542e7aa77SAlexander Simon return pos; 285642e7aa77SAlexander Simon } 285742e7aa77SAlexander Simon 2858ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 2859ba0afa2fSMahesh Palivela u32 cap) 2860ba0afa2fSMahesh Palivela { 2861ba0afa2fSMahesh Palivela __le32 tmp; 2862ba0afa2fSMahesh Palivela 2863ba0afa2fSMahesh Palivela *pos++ = WLAN_EID_VHT_CAPABILITY; 2864d4950281SMahesh Palivela *pos++ = sizeof(struct ieee80211_vht_cap); 2865d4950281SMahesh Palivela memset(pos, 0, sizeof(struct ieee80211_vht_cap)); 2866ba0afa2fSMahesh Palivela 2867ba0afa2fSMahesh Palivela /* capability flags */ 2868ba0afa2fSMahesh Palivela tmp = cpu_to_le32(cap); 2869ba0afa2fSMahesh Palivela memcpy(pos, &tmp, sizeof(u32)); 2870ba0afa2fSMahesh Palivela pos += sizeof(u32); 2871ba0afa2fSMahesh Palivela 2872ba0afa2fSMahesh Palivela /* VHT MCS set */ 2873ba0afa2fSMahesh Palivela memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); 2874ba0afa2fSMahesh Palivela pos += sizeof(vht_cap->vht_mcs); 2875ba0afa2fSMahesh Palivela 2876ba0afa2fSMahesh Palivela return pos; 2877ba0afa2fSMahesh Palivela } 2878ba0afa2fSMahesh Palivela 287960ad72daSSven Eckelmann u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) 288060ad72daSSven Eckelmann { 288160ad72daSSven Eckelmann const struct ieee80211_sta_he_cap *he_cap; 288260ad72daSSven Eckelmann struct ieee80211_supported_band *sband; 288360ad72daSSven Eckelmann u8 n; 288460ad72daSSven Eckelmann 288560ad72daSSven Eckelmann sband = ieee80211_get_sband(sdata); 288660ad72daSSven Eckelmann if (!sband) 288760ad72daSSven Eckelmann return 0; 288860ad72daSSven Eckelmann 288960ad72daSSven Eckelmann he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 289060ad72daSSven Eckelmann if (!he_cap) 289160ad72daSSven Eckelmann return 0; 289260ad72daSSven Eckelmann 289360ad72daSSven Eckelmann n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); 289460ad72daSSven Eckelmann return 2 + 1 + 289560ad72daSSven Eckelmann sizeof(he_cap->he_cap_elem) + n + 289660ad72daSSven Eckelmann ieee80211_he_ppe_size(he_cap->ppe_thres[0], 289760ad72daSSven Eckelmann he_cap->he_cap_elem.phy_cap_info); 289860ad72daSSven Eckelmann } 289960ad72daSSven Eckelmann 290041cbb0f5SLuca Coelho u8 *ieee80211_ie_build_he_cap(u8 *pos, 290141cbb0f5SLuca Coelho const struct ieee80211_sta_he_cap *he_cap, 290241cbb0f5SLuca Coelho u8 *end) 290341cbb0f5SLuca Coelho { 290441cbb0f5SLuca Coelho u8 n; 290541cbb0f5SLuca Coelho u8 ie_len; 290641cbb0f5SLuca Coelho u8 *orig_pos = pos; 290741cbb0f5SLuca Coelho 290841cbb0f5SLuca Coelho /* Make sure we have place for the IE */ 290941cbb0f5SLuca Coelho /* 291041cbb0f5SLuca Coelho * TODO: the 1 added is because this temporarily is under the EXTENSION 291141cbb0f5SLuca Coelho * IE. Get rid of it when it moves. 291241cbb0f5SLuca Coelho */ 291341cbb0f5SLuca Coelho if (!he_cap) 291441cbb0f5SLuca Coelho return orig_pos; 291541cbb0f5SLuca Coelho 291641cbb0f5SLuca Coelho n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); 291741cbb0f5SLuca Coelho ie_len = 2 + 1 + 291841cbb0f5SLuca Coelho sizeof(he_cap->he_cap_elem) + n + 291941cbb0f5SLuca Coelho ieee80211_he_ppe_size(he_cap->ppe_thres[0], 292041cbb0f5SLuca Coelho he_cap->he_cap_elem.phy_cap_info); 292141cbb0f5SLuca Coelho 292241cbb0f5SLuca Coelho if ((end - pos) < ie_len) 292341cbb0f5SLuca Coelho return orig_pos; 292441cbb0f5SLuca Coelho 292541cbb0f5SLuca Coelho *pos++ = WLAN_EID_EXTENSION; 292641cbb0f5SLuca Coelho pos++; /* We'll set the size later below */ 292741cbb0f5SLuca Coelho *pos++ = WLAN_EID_EXT_HE_CAPABILITY; 292841cbb0f5SLuca Coelho 292941cbb0f5SLuca Coelho /* Fixed data */ 293041cbb0f5SLuca Coelho memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem)); 293141cbb0f5SLuca Coelho pos += sizeof(he_cap->he_cap_elem); 293241cbb0f5SLuca Coelho 293341cbb0f5SLuca Coelho memcpy(pos, &he_cap->he_mcs_nss_supp, n); 293441cbb0f5SLuca Coelho pos += n; 293541cbb0f5SLuca Coelho 293641cbb0f5SLuca Coelho /* Check if PPE Threshold should be present */ 293741cbb0f5SLuca Coelho if ((he_cap->he_cap_elem.phy_cap_info[6] & 293841cbb0f5SLuca Coelho IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) 293941cbb0f5SLuca Coelho goto end; 294041cbb0f5SLuca Coelho 294141cbb0f5SLuca Coelho /* 294241cbb0f5SLuca Coelho * Calculate how many PPET16/PPET8 pairs are to come. Algorithm: 294341cbb0f5SLuca Coelho * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK) 294441cbb0f5SLuca Coelho */ 294541cbb0f5SLuca Coelho n = hweight8(he_cap->ppe_thres[0] & 294641cbb0f5SLuca Coelho IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); 294741cbb0f5SLuca Coelho n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >> 294841cbb0f5SLuca Coelho IEEE80211_PPE_THRES_NSS_POS)); 294941cbb0f5SLuca Coelho 295041cbb0f5SLuca Coelho /* 295141cbb0f5SLuca Coelho * Each pair is 6 bits, and we need to add the 7 "header" bits to the 295241cbb0f5SLuca Coelho * total size. 295341cbb0f5SLuca Coelho */ 295441cbb0f5SLuca Coelho n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7; 295541cbb0f5SLuca Coelho n = DIV_ROUND_UP(n, 8); 295641cbb0f5SLuca Coelho 295741cbb0f5SLuca Coelho /* Copy PPE Thresholds */ 295841cbb0f5SLuca Coelho memcpy(pos, &he_cap->ppe_thres, n); 295941cbb0f5SLuca Coelho pos += n; 296041cbb0f5SLuca Coelho 296141cbb0f5SLuca Coelho end: 296241cbb0f5SLuca Coelho orig_pos[1] = (pos - orig_pos) - 2; 296341cbb0f5SLuca Coelho return pos; 296441cbb0f5SLuca Coelho } 296541cbb0f5SLuca Coelho 296624a2042cSRajkumar Manoharan void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, 296724a2042cSRajkumar Manoharan struct sk_buff *skb) 296824a2042cSRajkumar Manoharan { 296924a2042cSRajkumar Manoharan struct ieee80211_supported_band *sband; 297024a2042cSRajkumar Manoharan const struct ieee80211_sband_iftype_data *iftd; 297124a2042cSRajkumar Manoharan enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 297224a2042cSRajkumar Manoharan u8 *pos; 297324a2042cSRajkumar Manoharan u16 cap; 297424a2042cSRajkumar Manoharan 297565be6aa3SJohannes Berg if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy, 297665be6aa3SJohannes Berg BIT(NL80211_BAND_6GHZ), 297765be6aa3SJohannes Berg IEEE80211_CHAN_NO_HE)) 297824a2042cSRajkumar Manoharan return; 297924a2042cSRajkumar Manoharan 298065be6aa3SJohannes Berg sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 298165be6aa3SJohannes Berg 298224a2042cSRajkumar Manoharan iftd = ieee80211_get_sband_iftype_data(sband, iftype); 298365be6aa3SJohannes Berg if (!iftd) 298424a2042cSRajkumar Manoharan return; 298524a2042cSRajkumar Manoharan 298665ad3ef9SRajkumar Manoharan /* Check for device HE 6 GHz capability before adding element */ 298765ad3ef9SRajkumar Manoharan if (!iftd->he_6ghz_capa.capa) 298865ad3ef9SRajkumar Manoharan return; 298965ad3ef9SRajkumar Manoharan 299024a2042cSRajkumar Manoharan cap = le16_to_cpu(iftd->he_6ghz_capa.capa); 299124a2042cSRajkumar Manoharan cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS; 299224a2042cSRajkumar Manoharan 299324a2042cSRajkumar Manoharan switch (sdata->smps_mode) { 299424a2042cSRajkumar Manoharan case IEEE80211_SMPS_AUTOMATIC: 299524a2042cSRajkumar Manoharan case IEEE80211_SMPS_NUM_MODES: 299624a2042cSRajkumar Manoharan WARN_ON(1); 2997fc0561dcSGustavo A. R. Silva fallthrough; 299824a2042cSRajkumar Manoharan case IEEE80211_SMPS_OFF: 299924a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED, 300024a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS); 300124a2042cSRajkumar Manoharan break; 300224a2042cSRajkumar Manoharan case IEEE80211_SMPS_STATIC: 300324a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC, 300424a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS); 300524a2042cSRajkumar Manoharan break; 300624a2042cSRajkumar Manoharan case IEEE80211_SMPS_DYNAMIC: 300724a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC, 300824a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS); 300924a2042cSRajkumar Manoharan break; 301024a2042cSRajkumar Manoharan } 301124a2042cSRajkumar Manoharan 301224a2042cSRajkumar Manoharan pos = skb_put(skb, 2 + 1 + sizeof(cap)); 30132ad2274cSIlan Peer ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap), 30142ad2274cSIlan Peer pos + 2 + 1 + sizeof(cap)); 301524a2042cSRajkumar Manoharan } 301624a2042cSRajkumar Manoharan 3017074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 30184bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 301957f255f5SArik Nemtsov u16 prot_mode, bool rifs_mode) 302042e7aa77SAlexander Simon { 3021074d46d1SJohannes Berg struct ieee80211_ht_operation *ht_oper; 302242e7aa77SAlexander Simon /* Build HT Information */ 3023074d46d1SJohannes Berg *pos++ = WLAN_EID_HT_OPERATION; 3024074d46d1SJohannes Berg *pos++ = sizeof(struct ieee80211_ht_operation); 3025074d46d1SJohannes Berg ht_oper = (struct ieee80211_ht_operation *)pos; 30264bf88530SJohannes Berg ht_oper->primary_chan = ieee80211_frequency_to_channel( 30274bf88530SJohannes Berg chandef->chan->center_freq); 30284bf88530SJohannes Berg switch (chandef->width) { 30294bf88530SJohannes Berg case NL80211_CHAN_WIDTH_160: 30304bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80P80: 30314bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80: 30324bf88530SJohannes Berg case NL80211_CHAN_WIDTH_40: 30334bf88530SJohannes Berg if (chandef->center_freq1 > chandef->chan->center_freq) 30344bf88530SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 30354bf88530SJohannes Berg else 3036074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 303742e7aa77SAlexander Simon break; 303842e7aa77SAlexander Simon default: 3039074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; 304042e7aa77SAlexander Simon break; 304142e7aa77SAlexander Simon } 3042aee286c2SThomas Pedersen if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && 30434bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20_NOHT && 30444bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20) 3045074d46d1SJohannes Berg ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; 3046ff3cc5f4SSimon Wunderlich 304757f255f5SArik Nemtsov if (rifs_mode) 304857f255f5SArik Nemtsov ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE; 304957f255f5SArik Nemtsov 3050431e3154SAshok Nagarajan ht_oper->operation_mode = cpu_to_le16(prot_mode); 3051074d46d1SJohannes Berg ht_oper->stbc_param = 0x0000; 305242e7aa77SAlexander Simon 305342e7aa77SAlexander Simon /* It seems that Basic MCS set and Supported MCS set 305442e7aa77SAlexander Simon are identical for the first 10 bytes */ 3055074d46d1SJohannes Berg memset(&ht_oper->basic_set, 0, 16); 3056074d46d1SJohannes Berg memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); 305742e7aa77SAlexander Simon 3058074d46d1SJohannes Berg return pos + sizeof(struct ieee80211_ht_operation); 305942e7aa77SAlexander Simon } 306042e7aa77SAlexander Simon 306175d627d5SSimon Wunderlich void ieee80211_ie_build_wide_bw_cs(u8 *pos, 306275d627d5SSimon Wunderlich const struct cfg80211_chan_def *chandef) 306375d627d5SSimon Wunderlich { 306475d627d5SSimon Wunderlich *pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH; /* EID */ 306575d627d5SSimon Wunderlich *pos++ = 3; /* IE length */ 306675d627d5SSimon Wunderlich /* New channel width */ 306775d627d5SSimon Wunderlich switch (chandef->width) { 306875d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_80: 306975d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ; 307075d627d5SSimon Wunderlich break; 307175d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_160: 307275d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ; 307375d627d5SSimon Wunderlich break; 307475d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_80P80: 307575d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ; 307675d627d5SSimon Wunderlich break; 307775d627d5SSimon Wunderlich default: 307875d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT; 307975d627d5SSimon Wunderlich } 308075d627d5SSimon Wunderlich 308175d627d5SSimon Wunderlich /* new center frequency segment 0 */ 308275d627d5SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel(chandef->center_freq1); 308375d627d5SSimon Wunderlich /* new center frequency segment 1 */ 308475d627d5SSimon Wunderlich if (chandef->center_freq2) 308575d627d5SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel(chandef->center_freq2); 308675d627d5SSimon Wunderlich else 308775d627d5SSimon Wunderlich *pos++ = 0; 308875d627d5SSimon Wunderlich } 308975d627d5SSimon Wunderlich 3090fb28ec0cSArik Nemtsov u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 3091fb28ec0cSArik Nemtsov const struct cfg80211_chan_def *chandef) 3092fb28ec0cSArik Nemtsov { 3093fb28ec0cSArik Nemtsov struct ieee80211_vht_operation *vht_oper; 3094fb28ec0cSArik Nemtsov 3095fb28ec0cSArik Nemtsov *pos++ = WLAN_EID_VHT_OPERATION; 3096fb28ec0cSArik Nemtsov *pos++ = sizeof(struct ieee80211_vht_operation); 3097fb28ec0cSArik Nemtsov vht_oper = (struct ieee80211_vht_operation *)pos; 30982fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel( 3099fb28ec0cSArik Nemtsov chandef->center_freq1); 3100fb28ec0cSArik Nemtsov if (chandef->center_freq2) 31012fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx = 3102fb28ec0cSArik Nemtsov ieee80211_frequency_to_channel(chandef->center_freq2); 3103f020ae40SChun-Yeow Yeoh else 31042fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx = 0x00; 3105fb28ec0cSArik Nemtsov 3106fb28ec0cSArik Nemtsov switch (chandef->width) { 3107fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_160: 310823665aafSJouni Malinen /* 310923665aafSJouni Malinen * Convert 160 MHz channel width to new style as interop 311023665aafSJouni Malinen * workaround. 311123665aafSJouni Malinen */ 311223665aafSJouni Malinen vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 31132fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; 311423665aafSJouni Malinen if (chandef->chan->center_freq < chandef->center_freq1) 31152fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx -= 8; 311623665aafSJouni Malinen else 31172fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx += 8; 3118fb28ec0cSArik Nemtsov break; 3119fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_80P80: 312023665aafSJouni Malinen /* 312123665aafSJouni Malinen * Convert 80+80 MHz channel width to new style as interop 312223665aafSJouni Malinen * workaround. 312323665aafSJouni Malinen */ 312423665aafSJouni Malinen vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 3125fb28ec0cSArik Nemtsov break; 3126fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_80: 3127fb28ec0cSArik Nemtsov vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 3128fb28ec0cSArik Nemtsov break; 3129fb28ec0cSArik Nemtsov default: 3130fb28ec0cSArik Nemtsov vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; 3131fb28ec0cSArik Nemtsov break; 3132fb28ec0cSArik Nemtsov } 3133fb28ec0cSArik Nemtsov 3134fb28ec0cSArik Nemtsov /* don't require special VHT peer rates */ 3135fb28ec0cSArik Nemtsov vht_oper->basic_mcs_set = cpu_to_le16(0xffff); 3136fb28ec0cSArik Nemtsov 3137fb28ec0cSArik Nemtsov return pos + sizeof(struct ieee80211_vht_operation); 3138fb28ec0cSArik Nemtsov } 3139fb28ec0cSArik Nemtsov 3140d1b7524bSRajkumar Manoharan u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef) 314160ad72daSSven Eckelmann { 314260ad72daSSven Eckelmann struct ieee80211_he_operation *he_oper; 3143d1b7524bSRajkumar Manoharan struct ieee80211_he_6ghz_oper *he_6ghz_op; 314460ad72daSSven Eckelmann u32 he_oper_params; 3145d1b7524bSRajkumar Manoharan u8 ie_len = 1 + sizeof(struct ieee80211_he_operation); 3146d1b7524bSRajkumar Manoharan 3147d1b7524bSRajkumar Manoharan if (chandef->chan->band == NL80211_BAND_6GHZ) 3148d1b7524bSRajkumar Manoharan ie_len += sizeof(struct ieee80211_he_6ghz_oper); 314960ad72daSSven Eckelmann 315060ad72daSSven Eckelmann *pos++ = WLAN_EID_EXTENSION; 3151d1b7524bSRajkumar Manoharan *pos++ = ie_len; 315260ad72daSSven Eckelmann *pos++ = WLAN_EID_EXT_HE_OPERATION; 315360ad72daSSven Eckelmann 315460ad72daSSven Eckelmann he_oper_params = 0; 315560ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1023, /* disabled */ 315660ad72daSSven Eckelmann IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); 315760ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1, 315860ad72daSSven Eckelmann IEEE80211_HE_OPERATION_ER_SU_DISABLE); 315960ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1, 316060ad72daSSven Eckelmann IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); 3161d1b7524bSRajkumar Manoharan if (chandef->chan->band == NL80211_BAND_6GHZ) 3162d1b7524bSRajkumar Manoharan he_oper_params |= u32_encode_bits(1, 3163d1b7524bSRajkumar Manoharan IEEE80211_HE_OPERATION_6GHZ_OP_INFO); 316460ad72daSSven Eckelmann 316560ad72daSSven Eckelmann he_oper = (struct ieee80211_he_operation *)pos; 316660ad72daSSven Eckelmann he_oper->he_oper_params = cpu_to_le32(he_oper_params); 316760ad72daSSven Eckelmann 316860ad72daSSven Eckelmann /* don't require special HE peer rates */ 316960ad72daSSven Eckelmann he_oper->he_mcs_nss_set = cpu_to_le16(0xffff); 3170d1b7524bSRajkumar Manoharan pos += sizeof(struct ieee80211_he_operation); 317160ad72daSSven Eckelmann 3172d1b7524bSRajkumar Manoharan if (chandef->chan->band != NL80211_BAND_6GHZ) 3173d1b7524bSRajkumar Manoharan goto out; 317460ad72daSSven Eckelmann 3175d1b7524bSRajkumar Manoharan /* TODO add VHT operational */ 3176d1b7524bSRajkumar Manoharan he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos; 3177d1b7524bSRajkumar Manoharan he_6ghz_op->minrate = 6; /* 6 Mbps */ 3178d1b7524bSRajkumar Manoharan he_6ghz_op->primary = 3179d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->chan->center_freq); 3180d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 = 3181d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->center_freq1); 3182d1b7524bSRajkumar Manoharan if (chandef->center_freq2) 3183d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 = 3184d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->center_freq2); 3185d1b7524bSRajkumar Manoharan else 3186d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 = 0; 3187d1b7524bSRajkumar Manoharan 3188d1b7524bSRajkumar Manoharan switch (chandef->width) { 3189d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_160: 3190d1b7524bSRajkumar Manoharan /* Convert 160 MHz channel width to new style as interop 3191d1b7524bSRajkumar Manoharan * workaround. 3192d1b7524bSRajkumar Manoharan */ 3193d1b7524bSRajkumar Manoharan he_6ghz_op->control = 3194d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 3195d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0; 3196d1b7524bSRajkumar Manoharan if (chandef->chan->center_freq < chandef->center_freq1) 3197d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 -= 8; 3198d1b7524bSRajkumar Manoharan else 3199d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 += 8; 3200d1b7524bSRajkumar Manoharan fallthrough; 3201d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_80P80: 3202d1b7524bSRajkumar Manoharan he_6ghz_op->control = 3203d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 3204d1b7524bSRajkumar Manoharan break; 3205d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_80: 3206d1b7524bSRajkumar Manoharan he_6ghz_op->control = 3207d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ; 3208d1b7524bSRajkumar Manoharan break; 3209d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_40: 3210d1b7524bSRajkumar Manoharan he_6ghz_op->control = 3211d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ; 3212d1b7524bSRajkumar Manoharan break; 3213d1b7524bSRajkumar Manoharan default: 3214d1b7524bSRajkumar Manoharan he_6ghz_op->control = 3215d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ; 3216d1b7524bSRajkumar Manoharan break; 3217d1b7524bSRajkumar Manoharan } 3218d1b7524bSRajkumar Manoharan 3219d1b7524bSRajkumar Manoharan pos += sizeof(struct ieee80211_he_6ghz_oper); 3220d1b7524bSRajkumar Manoharan 3221d1b7524bSRajkumar Manoharan out: 3222d1b7524bSRajkumar Manoharan return pos; 322360ad72daSSven Eckelmann } 322460ad72daSSven Eckelmann 32258ac3c704SJohannes Berg bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, 32264bf88530SJohannes Berg struct cfg80211_chan_def *chandef) 322742e7aa77SAlexander Simon { 322842e7aa77SAlexander Simon enum nl80211_channel_type channel_type; 322942e7aa77SAlexander Simon 32308ac3c704SJohannes Berg if (!ht_oper) 32318ac3c704SJohannes Berg return false; 323242e7aa77SAlexander Simon 3233074d46d1SJohannes Berg switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 323442e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_NONE: 323542e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT20; 323642e7aa77SAlexander Simon break; 323742e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 323842e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40PLUS; 323942e7aa77SAlexander Simon break; 324042e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 324142e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40MINUS; 324242e7aa77SAlexander Simon break; 324342e7aa77SAlexander Simon default: 324442e7aa77SAlexander Simon channel_type = NL80211_CHAN_NO_HT; 32458ac3c704SJohannes Berg return false; 324642e7aa77SAlexander Simon } 324742e7aa77SAlexander Simon 32488ac3c704SJohannes Berg cfg80211_chandef_create(chandef, chandef->chan, channel_type); 32498ac3c704SJohannes Berg return true; 325042e7aa77SAlexander Simon } 325142e7aa77SAlexander Simon 32522a333a0dSJohannes Berg bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, 32537eb26df2SJohannes Berg const struct ieee80211_vht_operation *oper, 32547eb26df2SJohannes Berg const struct ieee80211_ht_operation *htop, 3255abcff6efSJanusz.Dziedzic@tieto.com struct cfg80211_chan_def *chandef) 3256abcff6efSJanusz.Dziedzic@tieto.com { 32578ac3c704SJohannes Berg struct cfg80211_chan_def new = *chandef; 32587eb26df2SJohannes Berg int cf0, cf1; 32597eb26df2SJohannes Berg int ccfs0, ccfs1, ccfs2; 32607eb26df2SJohannes Berg int ccf0, ccf1; 326133181ea7SShay Bar u32 vht_cap; 326233181ea7SShay Bar bool support_80_80 = false; 326333181ea7SShay Bar bool support_160 = false; 32642a333a0dSJohannes Berg u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info, 32652a333a0dSJohannes Berg IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 32662a333a0dSJohannes Berg u8 supp_chwidth = u32_get_bits(vht_cap_info, 32672a333a0dSJohannes Berg IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); 3268abcff6efSJanusz.Dziedzic@tieto.com 32697eb26df2SJohannes Berg if (!oper || !htop) 32708ac3c704SJohannes Berg return false; 32718ac3c704SJohannes Berg 327233181ea7SShay Bar vht_cap = hw->wiphy->bands[chandef->chan->band]->vht_cap.cap; 327333181ea7SShay Bar support_160 = (vht_cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK | 327433181ea7SShay Bar IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)); 327533181ea7SShay Bar support_80_80 = ((vht_cap & 327633181ea7SShay Bar IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) || 327733181ea7SShay Bar (vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && 327833181ea7SShay Bar vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) || 327933181ea7SShay Bar ((vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >> 328033181ea7SShay Bar IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT > 1)); 32817eb26df2SJohannes Berg ccfs0 = oper->center_freq_seg0_idx; 32827eb26df2SJohannes Berg ccfs1 = oper->center_freq_seg1_idx; 32837eb26df2SJohannes Berg ccfs2 = (le16_to_cpu(htop->operation_mode) & 32847eb26df2SJohannes Berg IEEE80211_HT_OP_MODE_CCFS2_MASK) 32857eb26df2SJohannes Berg >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT; 32867eb26df2SJohannes Berg 32877eb26df2SJohannes Berg ccf0 = ccfs0; 32882a333a0dSJohannes Berg 32892a333a0dSJohannes Berg /* if not supported, parse as though we didn't understand it */ 32902a333a0dSJohannes Berg if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW)) 32912a333a0dSJohannes Berg ext_nss_bw_supp = 0; 32922a333a0dSJohannes Berg 32932a333a0dSJohannes Berg /* 32942a333a0dSJohannes Berg * Cf. IEEE 802.11 Table 9-250 32952a333a0dSJohannes Berg * 32962a333a0dSJohannes Berg * We really just consider that because it's inefficient to connect 32972a333a0dSJohannes Berg * at a higher bandwidth than we'll actually be able to use. 32982a333a0dSJohannes Berg */ 32992a333a0dSJohannes Berg switch ((supp_chwidth << 4) | ext_nss_bw_supp) { 33002a333a0dSJohannes Berg default: 33012a333a0dSJohannes Berg case 0x00: 33022a333a0dSJohannes Berg ccf1 = 0; 33032a333a0dSJohannes Berg support_160 = false; 33042a333a0dSJohannes Berg support_80_80 = false; 33052a333a0dSJohannes Berg break; 33062a333a0dSJohannes Berg case 0x01: 33072a333a0dSJohannes Berg support_80_80 = false; 3308fc0561dcSGustavo A. R. Silva fallthrough; 33092a333a0dSJohannes Berg case 0x02: 33102a333a0dSJohannes Berg case 0x03: 33117eb26df2SJohannes Berg ccf1 = ccfs2; 33122a333a0dSJohannes Berg break; 33132a333a0dSJohannes Berg case 0x10: 33142a333a0dSJohannes Berg ccf1 = ccfs1; 33152a333a0dSJohannes Berg break; 33162a333a0dSJohannes Berg case 0x11: 33172a333a0dSJohannes Berg case 0x12: 33182a333a0dSJohannes Berg if (!ccfs1) 33192a333a0dSJohannes Berg ccf1 = ccfs2; 33202a333a0dSJohannes Berg else 33212a333a0dSJohannes Berg ccf1 = ccfs1; 33222a333a0dSJohannes Berg break; 33232a333a0dSJohannes Berg case 0x13: 33242a333a0dSJohannes Berg case 0x20: 33252a333a0dSJohannes Berg case 0x23: 33262a333a0dSJohannes Berg ccf1 = ccfs1; 33272a333a0dSJohannes Berg break; 33282a333a0dSJohannes Berg } 33297eb26df2SJohannes Berg 33307eb26df2SJohannes Berg cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band); 33317eb26df2SJohannes Berg cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band); 3332abcff6efSJanusz.Dziedzic@tieto.com 3333abcff6efSJanusz.Dziedzic@tieto.com switch (oper->chan_width) { 3334abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_USE_HT: 33357eb26df2SJohannes Berg /* just use HT information directly */ 3336abcff6efSJanusz.Dziedzic@tieto.com break; 3337abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_80MHZ: 33388ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_80; 33397eb26df2SJohannes Berg new.center_freq1 = cf0; 334023665aafSJouni Malinen /* If needed, adjust based on the newer interop workaround. */ 33417eb26df2SJohannes Berg if (ccf1) { 334223665aafSJouni Malinen unsigned int diff; 334323665aafSJouni Malinen 33447eb26df2SJohannes Berg diff = abs(ccf1 - ccf0); 334533181ea7SShay Bar if ((diff == 8) && support_160) { 334623665aafSJouni Malinen new.width = NL80211_CHAN_WIDTH_160; 33477eb26df2SJohannes Berg new.center_freq1 = cf1; 334833181ea7SShay Bar } else if ((diff > 8) && support_80_80) { 334923665aafSJouni Malinen new.width = NL80211_CHAN_WIDTH_80P80; 33507eb26df2SJohannes Berg new.center_freq2 = cf1; 335123665aafSJouni Malinen } 335223665aafSJouni Malinen } 3353abcff6efSJanusz.Dziedzic@tieto.com break; 3354abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_160MHZ: 33557eb26df2SJohannes Berg /* deprecated encoding */ 33568ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_160; 33577eb26df2SJohannes Berg new.center_freq1 = cf0; 3358abcff6efSJanusz.Dziedzic@tieto.com break; 3359abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_80P80MHZ: 33607eb26df2SJohannes Berg /* deprecated encoding */ 33618ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_80P80; 33627eb26df2SJohannes Berg new.center_freq1 = cf0; 33637eb26df2SJohannes Berg new.center_freq2 = cf1; 3364abcff6efSJanusz.Dziedzic@tieto.com break; 3365abcff6efSJanusz.Dziedzic@tieto.com default: 33668ac3c704SJohannes Berg return false; 3367abcff6efSJanusz.Dziedzic@tieto.com } 3368abcff6efSJanusz.Dziedzic@tieto.com 33698ac3c704SJohannes Berg if (!cfg80211_chandef_valid(&new)) 33708ac3c704SJohannes Berg return false; 33718ac3c704SJohannes Berg 33728ac3c704SJohannes Berg *chandef = new; 33738ac3c704SJohannes Berg return true; 3374abcff6efSJanusz.Dziedzic@tieto.com } 3375abcff6efSJanusz.Dziedzic@tieto.com 337657fa5e85SJohannes Berg bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, 337757fa5e85SJohannes Berg const struct ieee80211_he_operation *he_oper, 337857fa5e85SJohannes Berg struct cfg80211_chan_def *chandef) 337957fa5e85SJohannes Berg { 338057fa5e85SJohannes Berg struct ieee80211_local *local = sdata->local; 338157fa5e85SJohannes Berg struct ieee80211_supported_band *sband; 338257fa5e85SJohannes Berg enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 338357fa5e85SJohannes Berg const struct ieee80211_sta_he_cap *he_cap; 338457fa5e85SJohannes Berg struct cfg80211_chan_def he_chandef = *chandef; 338557fa5e85SJohannes Berg const struct ieee80211_he_6ghz_oper *he_6ghz_oper; 338657fa5e85SJohannes Berg bool support_80_80, support_160; 338757fa5e85SJohannes Berg u8 he_phy_cap; 338857fa5e85SJohannes Berg u32 freq; 338957fa5e85SJohannes Berg 339057fa5e85SJohannes Berg if (chandef->chan->band != NL80211_BAND_6GHZ) 339157fa5e85SJohannes Berg return true; 339257fa5e85SJohannes Berg 339357fa5e85SJohannes Berg sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 339457fa5e85SJohannes Berg 339557fa5e85SJohannes Berg he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 339657fa5e85SJohannes Berg if (!he_cap) { 339757fa5e85SJohannes Berg sdata_info(sdata, "Missing iftype sband data/HE cap"); 339857fa5e85SJohannes Berg return false; 339957fa5e85SJohannes Berg } 340057fa5e85SJohannes Berg 340157fa5e85SJohannes Berg he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0]; 340257fa5e85SJohannes Berg support_160 = 340357fa5e85SJohannes Berg he_phy_cap & 340457fa5e85SJohannes Berg IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 340557fa5e85SJohannes Berg support_80_80 = 340657fa5e85SJohannes Berg he_phy_cap & 340757fa5e85SJohannes Berg IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; 340857fa5e85SJohannes Berg 340957fa5e85SJohannes Berg if (!he_oper) { 341057fa5e85SJohannes Berg sdata_info(sdata, 341157fa5e85SJohannes Berg "HE is not advertised on (on %d MHz), expect issues\n", 341257fa5e85SJohannes Berg chandef->chan->center_freq); 341357fa5e85SJohannes Berg return false; 341457fa5e85SJohannes Berg } 341557fa5e85SJohannes Berg 341657fa5e85SJohannes Berg he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); 341757fa5e85SJohannes Berg 341857fa5e85SJohannes Berg if (!he_6ghz_oper) { 341957fa5e85SJohannes Berg sdata_info(sdata, 342057fa5e85SJohannes Berg "HE 6GHz operation missing (on %d MHz), expect issues\n", 342157fa5e85SJohannes Berg chandef->chan->center_freq); 342257fa5e85SJohannes Berg return false; 342357fa5e85SJohannes Berg } 342457fa5e85SJohannes Berg 342557fa5e85SJohannes Berg freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary, 342657fa5e85SJohannes Berg NL80211_BAND_6GHZ); 342757fa5e85SJohannes Berg he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); 342857fa5e85SJohannes Berg 342957fa5e85SJohannes Berg switch (u8_get_bits(he_6ghz_oper->control, 343057fa5e85SJohannes Berg IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { 343157fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: 343257fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_20; 343357fa5e85SJohannes Berg break; 343457fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: 343557fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_40; 343657fa5e85SJohannes Berg break; 343757fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: 343857fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80; 343957fa5e85SJohannes Berg break; 344057fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: 344157fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80; 344257fa5e85SJohannes Berg if (!he_6ghz_oper->ccfs1) 344357fa5e85SJohannes Berg break; 344457fa5e85SJohannes Berg if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { 344557fa5e85SJohannes Berg if (support_160) 344657fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_160; 344757fa5e85SJohannes Berg } else { 344857fa5e85SJohannes Berg if (support_80_80) 344957fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80P80; 345057fa5e85SJohannes Berg } 345157fa5e85SJohannes Berg break; 345257fa5e85SJohannes Berg } 345357fa5e85SJohannes Berg 345457fa5e85SJohannes Berg if (he_chandef.width == NL80211_CHAN_WIDTH_160) { 345557fa5e85SJohannes Berg he_chandef.center_freq1 = 345657fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 345757fa5e85SJohannes Berg NL80211_BAND_6GHZ); 345857fa5e85SJohannes Berg } else { 345957fa5e85SJohannes Berg he_chandef.center_freq1 = 346057fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, 346157fa5e85SJohannes Berg NL80211_BAND_6GHZ); 346275bcbd69SJohn Crispin if (support_80_80 || support_160) 346357fa5e85SJohannes Berg he_chandef.center_freq2 = 346457fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 346557fa5e85SJohannes Berg NL80211_BAND_6GHZ); 346657fa5e85SJohannes Berg } 346757fa5e85SJohannes Berg 346857fa5e85SJohannes Berg if (!cfg80211_chandef_valid(&he_chandef)) { 346957fa5e85SJohannes Berg sdata_info(sdata, 347057fa5e85SJohannes Berg "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n", 347157fa5e85SJohannes Berg he_chandef.chan ? he_chandef.chan->center_freq : 0, 347257fa5e85SJohannes Berg he_chandef.width, 347357fa5e85SJohannes Berg he_chandef.center_freq1, 347457fa5e85SJohannes Berg he_chandef.center_freq2); 347557fa5e85SJohannes Berg return false; 347657fa5e85SJohannes Berg } 347757fa5e85SJohannes Berg 347857fa5e85SJohannes Berg *chandef = he_chandef; 347957fa5e85SJohannes Berg 34808fca2b87SWen Gong return true; 34811d00ce80SThomas Pedersen } 34821d00ce80SThomas Pedersen 34831d00ce80SThomas Pedersen bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, 34841d00ce80SThomas Pedersen struct cfg80211_chan_def *chandef) 34851d00ce80SThomas Pedersen { 34861d00ce80SThomas Pedersen u32 oper_freq; 34871d00ce80SThomas Pedersen 34881d00ce80SThomas Pedersen if (!oper) 34891d00ce80SThomas Pedersen return false; 34901d00ce80SThomas Pedersen 34911d00ce80SThomas Pedersen switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) { 34921d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_1MHZ: 34931d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_1; 34941d00ce80SThomas Pedersen break; 34951d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_2MHZ: 34961d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_2; 34971d00ce80SThomas Pedersen break; 34981d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_4MHZ: 34991d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_4; 35001d00ce80SThomas Pedersen break; 35011d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_8MHZ: 35021d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_8; 35031d00ce80SThomas Pedersen break; 35041d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_16MHZ: 35051d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_16; 35061d00ce80SThomas Pedersen break; 35071d00ce80SThomas Pedersen default: 35081d00ce80SThomas Pedersen return false; 35091d00ce80SThomas Pedersen } 35101d00ce80SThomas Pedersen 35111d00ce80SThomas Pedersen oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch, 35121d00ce80SThomas Pedersen NL80211_BAND_S1GHZ); 35131d00ce80SThomas Pedersen chandef->center_freq1 = KHZ_TO_MHZ(oper_freq); 35141d00ce80SThomas Pedersen chandef->freq1_offset = oper_freq % 1000; 35151d00ce80SThomas Pedersen 351657fa5e85SJohannes Berg return true; 351757fa5e85SJohannes Berg } 351857fa5e85SJohannes Berg 35192103dec1SSimon Wunderlich int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, 35202103dec1SSimon Wunderlich const struct ieee80211_supported_band *sband, 35212103dec1SSimon Wunderlich const u8 *srates, int srates_len, u32 *rates) 35222103dec1SSimon Wunderlich { 35232103dec1SSimon Wunderlich u32 rate_flags = ieee80211_chandef_rate_flags(chandef); 35242103dec1SSimon Wunderlich int shift = ieee80211_chandef_get_shift(chandef); 35252103dec1SSimon Wunderlich struct ieee80211_rate *br; 35262103dec1SSimon Wunderlich int brate, rate, i, j, count = 0; 35272103dec1SSimon Wunderlich 35282103dec1SSimon Wunderlich *rates = 0; 35292103dec1SSimon Wunderlich 35302103dec1SSimon Wunderlich for (i = 0; i < srates_len; i++) { 35312103dec1SSimon Wunderlich rate = srates[i] & 0x7f; 35322103dec1SSimon Wunderlich 35332103dec1SSimon Wunderlich for (j = 0; j < sband->n_bitrates; j++) { 35342103dec1SSimon Wunderlich br = &sband->bitrates[j]; 35352103dec1SSimon Wunderlich if ((rate_flags & br->flags) != rate_flags) 35362103dec1SSimon Wunderlich continue; 35372103dec1SSimon Wunderlich 35382103dec1SSimon Wunderlich brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); 35392103dec1SSimon Wunderlich if (brate == rate) { 35402103dec1SSimon Wunderlich *rates |= BIT(j); 35412103dec1SSimon Wunderlich count++; 35422103dec1SSimon Wunderlich break; 35432103dec1SSimon Wunderlich } 35442103dec1SSimon Wunderlich } 35452103dec1SSimon Wunderlich } 35462103dec1SSimon Wunderlich return count; 35472103dec1SSimon Wunderlich } 35482103dec1SSimon Wunderlich 3549fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, 35506b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 355157fbcce3SJohannes Berg enum nl80211_band band) 3552768db343SArik Nemtsov { 3553768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 3554768db343SArik Nemtsov struct ieee80211_supported_band *sband; 35552103dec1SSimon Wunderlich int rate, shift; 3556768db343SArik Nemtsov u8 i, rates, *pos; 3557fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 35582103dec1SSimon Wunderlich u32 rate_flags; 3559768db343SArik Nemtsov 35602103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 35612103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 35626b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 35632103dec1SSimon Wunderlich rates = 0; 35642103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) { 35652103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 35662103dec1SSimon Wunderlich continue; 35672103dec1SSimon Wunderlich rates++; 35682103dec1SSimon Wunderlich } 3569768db343SArik Nemtsov if (rates > 8) 3570768db343SArik Nemtsov rates = 8; 3571768db343SArik Nemtsov 3572768db343SArik Nemtsov if (skb_tailroom(skb) < rates + 2) 3573768db343SArik Nemtsov return -ENOMEM; 3574768db343SArik Nemtsov 3575768db343SArik Nemtsov pos = skb_put(skb, rates + 2); 3576768db343SArik Nemtsov *pos++ = WLAN_EID_SUPP_RATES; 3577768db343SArik Nemtsov *pos++ = rates; 3578768db343SArik Nemtsov for (i = 0; i < rates; i++) { 3579657c3e0cSAshok Nagarajan u8 basic = 0; 35802103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 35812103dec1SSimon Wunderlich continue; 35822103dec1SSimon Wunderlich 3583657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 3584657c3e0cSAshok Nagarajan basic = 0x80; 35852103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 35862103dec1SSimon Wunderlich 5 * (1 << shift)); 35872103dec1SSimon Wunderlich *pos++ = basic | (u8) rate; 3588768db343SArik Nemtsov } 3589768db343SArik Nemtsov 3590768db343SArik Nemtsov return 0; 3591768db343SArik Nemtsov } 3592768db343SArik Nemtsov 3593fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, 35946b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 359557fbcce3SJohannes Berg enum nl80211_band band) 3596768db343SArik Nemtsov { 3597768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 3598768db343SArik Nemtsov struct ieee80211_supported_band *sband; 3599cc63ec76SChun-Yeow Yeoh int rate, shift; 3600768db343SArik Nemtsov u8 i, exrates, *pos; 3601fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 36022103dec1SSimon Wunderlich u32 rate_flags; 36032103dec1SSimon Wunderlich 36042103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 36052103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 3606768db343SArik Nemtsov 36076b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 36082103dec1SSimon Wunderlich exrates = 0; 36092103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) { 36102103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 36112103dec1SSimon Wunderlich continue; 36122103dec1SSimon Wunderlich exrates++; 36132103dec1SSimon Wunderlich } 36142103dec1SSimon Wunderlich 3615768db343SArik Nemtsov if (exrates > 8) 3616768db343SArik Nemtsov exrates -= 8; 3617768db343SArik Nemtsov else 3618768db343SArik Nemtsov exrates = 0; 3619768db343SArik Nemtsov 3620768db343SArik Nemtsov if (skb_tailroom(skb) < exrates + 2) 3621768db343SArik Nemtsov return -ENOMEM; 3622768db343SArik Nemtsov 3623768db343SArik Nemtsov if (exrates) { 3624768db343SArik Nemtsov pos = skb_put(skb, exrates + 2); 3625768db343SArik Nemtsov *pos++ = WLAN_EID_EXT_SUPP_RATES; 3626768db343SArik Nemtsov *pos++ = exrates; 3627768db343SArik Nemtsov for (i = 8; i < sband->n_bitrates; i++) { 3628657c3e0cSAshok Nagarajan u8 basic = 0; 36292103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) 36302103dec1SSimon Wunderlich != rate_flags) 36312103dec1SSimon Wunderlich continue; 3632657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 3633657c3e0cSAshok Nagarajan basic = 0x80; 36342103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 36352103dec1SSimon Wunderlich 5 * (1 << shift)); 36362103dec1SSimon Wunderlich *pos++ = basic | (u8) rate; 3637768db343SArik Nemtsov } 3638768db343SArik Nemtsov } 3639768db343SArik Nemtsov return 0; 3640768db343SArik Nemtsov } 36411dae27f8SWey-Yi Guy 36421dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif) 36431dae27f8SWey-Yi Guy { 36441dae27f8SWey-Yi Guy struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 36451dae27f8SWey-Yi Guy struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 36461dae27f8SWey-Yi Guy 3647be6bcabcSWey-Yi Guy if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { 3648be6bcabcSWey-Yi Guy /* non-managed type inferfaces */ 3649be6bcabcSWey-Yi Guy return 0; 3650be6bcabcSWey-Yi Guy } 3651338c17aeSJohannes Berg return -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); 36521dae27f8SWey-Yi Guy } 36530d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 365404ecd257SJohannes Berg 365504ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) 365604ecd257SJohannes Berg { 365704ecd257SJohannes Berg if (!mcs) 365804ecd257SJohannes Berg return 1; 365904ecd257SJohannes Berg 366004ecd257SJohannes Berg /* TODO: consider rx_highest */ 366104ecd257SJohannes Berg 366204ecd257SJohannes Berg if (mcs->rx_mask[3]) 366304ecd257SJohannes Berg return 4; 366404ecd257SJohannes Berg if (mcs->rx_mask[2]) 366504ecd257SJohannes Berg return 3; 366604ecd257SJohannes Berg if (mcs->rx_mask[1]) 366704ecd257SJohannes Berg return 2; 366804ecd257SJohannes Berg return 1; 366904ecd257SJohannes Berg } 3670f4bda337SThomas Pedersen 3671f4bda337SThomas Pedersen /** 3672f4bda337SThomas Pedersen * ieee80211_calculate_rx_timestamp - calculate timestamp in frame 3673f4bda337SThomas Pedersen * @local: mac80211 hw info struct 3674f4bda337SThomas Pedersen * @status: RX status 3675f4bda337SThomas Pedersen * @mpdu_len: total MPDU length (including FCS) 3676f4bda337SThomas Pedersen * @mpdu_offset: offset into MPDU to calculate timestamp at 3677f4bda337SThomas Pedersen * 3678f4bda337SThomas Pedersen * This function calculates the RX timestamp at the given MPDU offset, taking 3679f4bda337SThomas Pedersen * into account what the RX timestamp was. An offset of 0 will just normalize 3680f4bda337SThomas Pedersen * the timestamp to TSF at beginning of MPDU reception. 3681f4bda337SThomas Pedersen */ 3682f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, 3683f4bda337SThomas Pedersen struct ieee80211_rx_status *status, 3684f4bda337SThomas Pedersen unsigned int mpdu_len, 3685f4bda337SThomas Pedersen unsigned int mpdu_offset) 3686f4bda337SThomas Pedersen { 3687f4bda337SThomas Pedersen u64 ts = status->mactime; 3688f4bda337SThomas Pedersen struct rate_info ri; 3689f4bda337SThomas Pedersen u16 rate; 3690da388233SAvraham Stern u8 n_ltf; 3691f4bda337SThomas Pedersen 3692f4bda337SThomas Pedersen if (WARN_ON(!ieee80211_have_rx_timestamp(status))) 3693f4bda337SThomas Pedersen return 0; 3694f4bda337SThomas Pedersen 3695f4bda337SThomas Pedersen memset(&ri, 0, sizeof(ri)); 3696f4bda337SThomas Pedersen 369735f4962cSJohannes Berg ri.bw = status->bw; 369835f4962cSJohannes Berg 3699f4bda337SThomas Pedersen /* Fill cfg80211 rate info */ 3700da6a4352SJohannes Berg switch (status->encoding) { 3701da388233SAvraham Stern case RX_ENC_HE: 3702da388233SAvraham Stern ri.flags |= RATE_INFO_FLAGS_HE_MCS; 3703da388233SAvraham Stern ri.mcs = status->rate_idx; 3704da388233SAvraham Stern ri.nss = status->nss; 3705da388233SAvraham Stern ri.he_ru_alloc = status->he_ru; 3706da388233SAvraham Stern if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 3707da388233SAvraham Stern ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 3708da388233SAvraham Stern 3709da388233SAvraham Stern /* 3710da388233SAvraham Stern * See P802.11ax_D6.0, section 27.3.4 for 3711da388233SAvraham Stern * VHT PPDU format. 3712da388233SAvraham Stern */ 3713da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 3714da388233SAvraham Stern mpdu_offset += 2; 3715da388233SAvraham Stern ts += 36; 3716da388233SAvraham Stern 3717da388233SAvraham Stern /* 3718da388233SAvraham Stern * TODO: 3719da388233SAvraham Stern * For HE MU PPDU, add the HE-SIG-B. 3720da388233SAvraham Stern * For HE ER PPDU, add 8us for the HE-SIG-A. 3721da388233SAvraham Stern * For HE TB PPDU, add 4us for the HE-STF. 3722da388233SAvraham Stern * Add the HE-LTF durations - variable. 3723da388233SAvraham Stern */ 3724da388233SAvraham Stern } 3725da388233SAvraham Stern 3726da388233SAvraham Stern break; 3727da6a4352SJohannes Berg case RX_ENC_HT: 3728f4bda337SThomas Pedersen ri.mcs = status->rate_idx; 3729f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_MCS; 37307fdd69c5SJohannes Berg if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 3731f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 3732da388233SAvraham Stern 3733da388233SAvraham Stern /* 3734da388233SAvraham Stern * See P802.11REVmd_D3.0, section 19.3.2 for 3735da388233SAvraham Stern * HT PPDU format. 3736da388233SAvraham Stern */ 3737da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 3738da388233SAvraham Stern mpdu_offset += 2; 3739da388233SAvraham Stern if (status->enc_flags & RX_ENC_FLAG_HT_GF) 3740da388233SAvraham Stern ts += 24; 3741da388233SAvraham Stern else 3742da388233SAvraham Stern ts += 32; 3743da388233SAvraham Stern 3744da388233SAvraham Stern /* 3745da388233SAvraham Stern * Add Data HT-LTFs per streams 3746da388233SAvraham Stern * TODO: add Extension HT-LTFs, 4us per LTF 3747da388233SAvraham Stern */ 3748da388233SAvraham Stern n_ltf = ((ri.mcs >> 3) & 3) + 1; 3749da388233SAvraham Stern n_ltf = n_ltf == 3 ? 4 : n_ltf; 3750da388233SAvraham Stern ts += n_ltf * 4; 3751da388233SAvraham Stern } 3752da388233SAvraham Stern 3753da6a4352SJohannes Berg break; 3754da6a4352SJohannes Berg case RX_ENC_VHT: 37555614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_VHT_MCS; 37565614618eSJohannes Berg ri.mcs = status->rate_idx; 37578613c948SJohannes Berg ri.nss = status->nss; 37587fdd69c5SJohannes Berg if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 37595614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 3760da388233SAvraham Stern 3761da388233SAvraham Stern /* 3762da388233SAvraham Stern * See P802.11REVmd_D3.0, section 21.3.2 for 3763da388233SAvraham Stern * VHT PPDU format. 3764da388233SAvraham Stern */ 3765da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 3766da388233SAvraham Stern mpdu_offset += 2; 3767da388233SAvraham Stern ts += 36; 3768da388233SAvraham Stern 3769da388233SAvraham Stern /* 3770da388233SAvraham Stern * Add VHT-LTFs per streams 3771da388233SAvraham Stern */ 3772da388233SAvraham Stern n_ltf = (ri.nss != 1) && (ri.nss % 2) ? 3773da388233SAvraham Stern ri.nss + 1 : ri.nss; 3774da388233SAvraham Stern ts += 4 * n_ltf; 3775da388233SAvraham Stern } 3776da388233SAvraham Stern 3777da6a4352SJohannes Berg break; 3778da6a4352SJohannes Berg default: 3779da6a4352SJohannes Berg WARN_ON(1); 3780fc0561dcSGustavo A. R. Silva fallthrough; 3781da6a4352SJohannes Berg case RX_ENC_LEGACY: { 3782f4bda337SThomas Pedersen struct ieee80211_supported_band *sband; 37832103dec1SSimon Wunderlich int shift = 0; 37842103dec1SSimon Wunderlich int bitrate; 37852103dec1SSimon Wunderlich 3786da6a4352SJohannes Berg switch (status->bw) { 3787da6a4352SJohannes Berg case RATE_INFO_BW_10: 37882103dec1SSimon Wunderlich shift = 1; 3789da6a4352SJohannes Berg break; 3790da6a4352SJohannes Berg case RATE_INFO_BW_5: 37912103dec1SSimon Wunderlich shift = 2; 3792da6a4352SJohannes Berg break; 3793b51f3beeSJohannes Berg } 3794f4bda337SThomas Pedersen 3795f4bda337SThomas Pedersen sband = local->hw.wiphy->bands[status->band]; 37962103dec1SSimon Wunderlich bitrate = sband->bitrates[status->rate_idx].bitrate; 37972103dec1SSimon Wunderlich ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); 3798f4a0f0c5SJohannes Berg 3799f4a0f0c5SJohannes Berg if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 380057fbcce3SJohannes Berg if (status->band == NL80211_BAND_5GHZ) { 3801f4a0f0c5SJohannes Berg ts += 20 << shift; 3802f4a0f0c5SJohannes Berg mpdu_offset += 2; 38037fdd69c5SJohannes Berg } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) { 3804f4a0f0c5SJohannes Berg ts += 96; 3805f4a0f0c5SJohannes Berg } else { 3806f4a0f0c5SJohannes Berg ts += 192; 3807f4a0f0c5SJohannes Berg } 3808f4a0f0c5SJohannes Berg } 3809da6a4352SJohannes Berg break; 3810da6a4352SJohannes Berg } 3811f4bda337SThomas Pedersen } 3812f4bda337SThomas Pedersen 3813f4bda337SThomas Pedersen rate = cfg80211_calculate_bitrate(&ri); 3814d86aa4f8SJohannes Berg if (WARN_ONCE(!rate, 3815f980ebc0SSara Sharon "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n", 3816f980ebc0SSara Sharon (unsigned long long)status->flag, status->rate_idx, 38178613c948SJohannes Berg status->nss)) 3818d86aa4f8SJohannes Berg return 0; 3819f4bda337SThomas Pedersen 3820f4bda337SThomas Pedersen /* rewind from end of MPDU */ 3821f4bda337SThomas Pedersen if (status->flag & RX_FLAG_MACTIME_END) 3822f4bda337SThomas Pedersen ts -= mpdu_len * 8 * 10 / rate; 3823f4bda337SThomas Pedersen 3824f4bda337SThomas Pedersen ts += mpdu_offset * 8 * 10 / rate; 3825f4bda337SThomas Pedersen 3826f4bda337SThomas Pedersen return ts; 3827f4bda337SThomas Pedersen } 3828164eb02dSSimon Wunderlich 3829164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) 3830164eb02dSSimon Wunderlich { 3831164eb02dSSimon Wunderlich struct ieee80211_sub_if_data *sdata; 3832d2859df5SJanusz Dziedzic struct cfg80211_chan_def chandef; 3833164eb02dSSimon Wunderlich 38344a199068SJohannes Berg /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */ 3835a05829a7SJohannes Berg lockdep_assert_wiphy(local->hw.wiphy); 38364a199068SJohannes Berg 383734a3740dSJohannes Berg mutex_lock(&local->mtx); 3838164eb02dSSimon Wunderlich list_for_each_entry(sdata, &local->interfaces, list) { 383934a3740dSJohannes Berg /* it might be waiting for the local->mtx, but then 384034a3740dSJohannes Berg * by the time it gets it, sdata->wdev.cac_started 384134a3740dSJohannes Berg * will no longer be true 384234a3740dSJohannes Berg */ 384334a3740dSJohannes Berg cancel_delayed_work(&sdata->dfs_cac_timer_work); 3844164eb02dSSimon Wunderlich 3845164eb02dSSimon Wunderlich if (sdata->wdev.cac_started) { 3846d2859df5SJanusz Dziedzic chandef = sdata->vif.bss_conf.chandef; 3847164eb02dSSimon Wunderlich ieee80211_vif_release_channel(sdata); 3848164eb02dSSimon Wunderlich cfg80211_cac_event(sdata->dev, 3849d2859df5SJanusz Dziedzic &chandef, 3850164eb02dSSimon Wunderlich NL80211_RADAR_CAC_ABORTED, 3851164eb02dSSimon Wunderlich GFP_KERNEL); 3852164eb02dSSimon Wunderlich } 3853164eb02dSSimon Wunderlich } 385434a3740dSJohannes Berg mutex_unlock(&local->mtx); 3855164eb02dSSimon Wunderlich } 3856164eb02dSSimon Wunderlich 3857164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work) 3858164eb02dSSimon Wunderlich { 3859164eb02dSSimon Wunderlich struct ieee80211_local *local = 3860164eb02dSSimon Wunderlich container_of(work, struct ieee80211_local, radar_detected_work); 386184a3d1c9SJanusz Dziedzic struct cfg80211_chan_def chandef = local->hw.conf.chandef; 3862486cf4c0SMichal Kazior struct ieee80211_chanctx *ctx; 3863486cf4c0SMichal Kazior int num_chanctx = 0; 3864486cf4c0SMichal Kazior 3865486cf4c0SMichal Kazior mutex_lock(&local->chanctx_mtx); 3866486cf4c0SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 3867486cf4c0SMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) 3868486cf4c0SMichal Kazior continue; 3869486cf4c0SMichal Kazior 3870486cf4c0SMichal Kazior num_chanctx++; 3871486cf4c0SMichal Kazior chandef = ctx->conf.def; 3872486cf4c0SMichal Kazior } 3873486cf4c0SMichal Kazior mutex_unlock(&local->chanctx_mtx); 3874164eb02dSSimon Wunderlich 3875a05829a7SJohannes Berg wiphy_lock(local->hw.wiphy); 3876164eb02dSSimon Wunderlich ieee80211_dfs_cac_cancel(local); 3877a05829a7SJohannes Berg wiphy_unlock(local->hw.wiphy); 3878164eb02dSSimon Wunderlich 3879486cf4c0SMichal Kazior if (num_chanctx > 1) 3880486cf4c0SMichal Kazior /* XXX: multi-channel is not supported yet */ 3881164eb02dSSimon Wunderlich WARN_ON(1); 388284a3d1c9SJanusz Dziedzic else 3883164eb02dSSimon Wunderlich cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); 3884164eb02dSSimon Wunderlich } 3885164eb02dSSimon Wunderlich 3886164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw) 3887164eb02dSSimon Wunderlich { 3888164eb02dSSimon Wunderlich struct ieee80211_local *local = hw_to_local(hw); 3889164eb02dSSimon Wunderlich 3890164eb02dSSimon Wunderlich trace_api_radar_detected(local); 3891164eb02dSSimon Wunderlich 38924a199068SJohannes Berg schedule_work(&local->radar_detected_work); 3893164eb02dSSimon Wunderlich } 3894164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected); 3895e6b7cde4SSimon Wunderlich 3896e6b7cde4SSimon Wunderlich u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) 3897e6b7cde4SSimon Wunderlich { 3898e6b7cde4SSimon Wunderlich u32 ret; 3899e6b7cde4SSimon Wunderlich int tmp; 3900e6b7cde4SSimon Wunderlich 3901e6b7cde4SSimon Wunderlich switch (c->width) { 3902e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20: 3903e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT; 3904e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 3905e6b7cde4SSimon Wunderlich break; 3906e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_40: 3907e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20; 3908e6b7cde4SSimon Wunderlich c->center_freq1 = c->chan->center_freq; 3909e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_40MHZ | 3910e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_VHT; 3911e6b7cde4SSimon Wunderlich break; 3912e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80: 3913e6b7cde4SSimon Wunderlich tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 3914e6b7cde4SSimon Wunderlich /* n_P40 */ 3915e6b7cde4SSimon Wunderlich tmp /= 2; 3916e6b7cde4SSimon Wunderlich /* freq_P40 */ 3917e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; 3918e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_40; 3919e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_VHT; 3920e6b7cde4SSimon Wunderlich break; 3921e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80P80: 3922e6b7cde4SSimon Wunderlich c->center_freq2 = 0; 3923e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80; 3924e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_80P80MHZ | 3925e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_160MHZ; 3926e6b7cde4SSimon Wunderlich break; 3927e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_160: 3928e6b7cde4SSimon Wunderlich /* n_P20 */ 3929e6b7cde4SSimon Wunderlich tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 3930e6b7cde4SSimon Wunderlich /* n_P80 */ 3931e6b7cde4SSimon Wunderlich tmp /= 4; 3932e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; 3933e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80; 3934e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_80P80MHZ | 3935e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_160MHZ; 3936e6b7cde4SSimon Wunderlich break; 3937e6b7cde4SSimon Wunderlich default: 3938e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20_NOHT: 3939e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1); 3940e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT; 3941e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 3942e6b7cde4SSimon Wunderlich break; 3943df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_1: 3944df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_2: 3945df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_4: 3946df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_8: 3947df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_16: 3948e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_5: 3949e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_10: 3950e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1); 3951e6b7cde4SSimon Wunderlich /* keep c->width */ 3952e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 3953e6b7cde4SSimon Wunderlich break; 3954e6b7cde4SSimon Wunderlich } 3955e6b7cde4SSimon Wunderlich 3956e6b7cde4SSimon Wunderlich WARN_ON_ONCE(!cfg80211_chandef_valid(c)); 3957e6b7cde4SSimon Wunderlich 3958e6b7cde4SSimon Wunderlich return ret; 3959e6b7cde4SSimon Wunderlich } 3960687da132SEmmanuel Grumbach 3961687da132SEmmanuel Grumbach /* 3962687da132SEmmanuel Grumbach * Returns true if smps_mode_new is strictly more restrictive than 3963687da132SEmmanuel Grumbach * smps_mode_old. 3964687da132SEmmanuel Grumbach */ 3965687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, 3966687da132SEmmanuel Grumbach enum ieee80211_smps_mode smps_mode_new) 3967687da132SEmmanuel Grumbach { 3968687da132SEmmanuel Grumbach if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || 3969687da132SEmmanuel Grumbach smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) 3970687da132SEmmanuel Grumbach return false; 3971687da132SEmmanuel Grumbach 3972687da132SEmmanuel Grumbach switch (smps_mode_old) { 3973687da132SEmmanuel Grumbach case IEEE80211_SMPS_STATIC: 3974687da132SEmmanuel Grumbach return false; 3975687da132SEmmanuel Grumbach case IEEE80211_SMPS_DYNAMIC: 3976687da132SEmmanuel Grumbach return smps_mode_new == IEEE80211_SMPS_STATIC; 3977687da132SEmmanuel Grumbach case IEEE80211_SMPS_OFF: 3978687da132SEmmanuel Grumbach return smps_mode_new != IEEE80211_SMPS_OFF; 3979687da132SEmmanuel Grumbach default: 3980687da132SEmmanuel Grumbach WARN_ON(1); 3981687da132SEmmanuel Grumbach } 3982687da132SEmmanuel Grumbach 3983687da132SEmmanuel Grumbach return false; 3984687da132SEmmanuel Grumbach } 3985c6da674aSChun-Yeow Yeoh 3986c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, 3987c6da674aSChun-Yeow Yeoh struct cfg80211_csa_settings *csa_settings) 3988c6da674aSChun-Yeow Yeoh { 3989c6da674aSChun-Yeow Yeoh struct sk_buff *skb; 3990c6da674aSChun-Yeow Yeoh struct ieee80211_mgmt *mgmt; 3991c6da674aSChun-Yeow Yeoh struct ieee80211_local *local = sdata->local; 3992c6da674aSChun-Yeow Yeoh int freq; 39934c121fd6SJohannes Berg int hdr_len = offsetofend(struct ieee80211_mgmt, 39944c121fd6SJohannes Berg u.action.u.chan_switch); 3995c6da674aSChun-Yeow Yeoh u8 *pos; 3996c6da674aSChun-Yeow Yeoh 3997c6da674aSChun-Yeow Yeoh if (sdata->vif.type != NL80211_IFTYPE_ADHOC && 3998c6da674aSChun-Yeow Yeoh sdata->vif.type != NL80211_IFTYPE_MESH_POINT) 3999c6da674aSChun-Yeow Yeoh return -EOPNOTSUPP; 4000c6da674aSChun-Yeow Yeoh 4001c6da674aSChun-Yeow Yeoh skb = dev_alloc_skb(local->tx_headroom + hdr_len + 4002c6da674aSChun-Yeow Yeoh 5 + /* channel switch announcement element */ 4003c6da674aSChun-Yeow Yeoh 3 + /* secondary channel offset element */ 400475d627d5SSimon Wunderlich 5 + /* wide bandwidth channel switch announcement */ 4005c6da674aSChun-Yeow Yeoh 8); /* mesh channel switch parameters element */ 4006c6da674aSChun-Yeow Yeoh if (!skb) 4007c6da674aSChun-Yeow Yeoh return -ENOMEM; 4008c6da674aSChun-Yeow Yeoh 4009c6da674aSChun-Yeow Yeoh skb_reserve(skb, local->tx_headroom); 4010b080db58SJohannes Berg mgmt = skb_put_zero(skb, hdr_len); 4011c6da674aSChun-Yeow Yeoh mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 4012c6da674aSChun-Yeow Yeoh IEEE80211_STYPE_ACTION); 4013c6da674aSChun-Yeow Yeoh 4014c6da674aSChun-Yeow Yeoh eth_broadcast_addr(mgmt->da); 4015c6da674aSChun-Yeow Yeoh memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 4016c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) { 4017c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 4018c6da674aSChun-Yeow Yeoh } else { 4019c6da674aSChun-Yeow Yeoh struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 4020c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); 4021c6da674aSChun-Yeow Yeoh } 4022c6da674aSChun-Yeow Yeoh mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 4023c6da674aSChun-Yeow Yeoh mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; 4024c6da674aSChun-Yeow Yeoh pos = skb_put(skb, 5); 4025c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ 4026c6da674aSChun-Yeow Yeoh *pos++ = 3; /* IE length */ 4027c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ 4028c6da674aSChun-Yeow Yeoh freq = csa_settings->chandef.chan->center_freq; 4029c6da674aSChun-Yeow Yeoh *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ 4030c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->count; /* count */ 4031c6da674aSChun-Yeow Yeoh 4032c6da674aSChun-Yeow Yeoh if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { 4033c6da674aSChun-Yeow Yeoh enum nl80211_channel_type ch_type; 4034c6da674aSChun-Yeow Yeoh 4035c6da674aSChun-Yeow Yeoh skb_put(skb, 3); 4036c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ 4037c6da674aSChun-Yeow Yeoh *pos++ = 1; /* IE length */ 4038c6da674aSChun-Yeow Yeoh ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); 4039c6da674aSChun-Yeow Yeoh if (ch_type == NL80211_CHAN_HT40PLUS) 4040c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 4041c6da674aSChun-Yeow Yeoh else 4042c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 4043c6da674aSChun-Yeow Yeoh } 4044c6da674aSChun-Yeow Yeoh 4045c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) { 4046c6da674aSChun-Yeow Yeoh struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 4047c6da674aSChun-Yeow Yeoh 4048c6da674aSChun-Yeow Yeoh skb_put(skb, 8); 4049c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ 4050c6da674aSChun-Yeow Yeoh *pos++ = 6; /* IE length */ 4051c6da674aSChun-Yeow Yeoh *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ 4052c6da674aSChun-Yeow Yeoh *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ 4053c6da674aSChun-Yeow Yeoh *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; 4054c6da674aSChun-Yeow Yeoh *pos++ |= csa_settings->block_tx ? 4055c6da674aSChun-Yeow Yeoh WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; 4056c6da674aSChun-Yeow Yeoh put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ 4057c6da674aSChun-Yeow Yeoh pos += 2; 4058ca91dc97SChun-Yeow Yeoh put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ 4059c6da674aSChun-Yeow Yeoh pos += 2; 4060c6da674aSChun-Yeow Yeoh } 4061c6da674aSChun-Yeow Yeoh 406275d627d5SSimon Wunderlich if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 || 406375d627d5SSimon Wunderlich csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 || 406475d627d5SSimon Wunderlich csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) { 406575d627d5SSimon Wunderlich skb_put(skb, 5); 406675d627d5SSimon Wunderlich ieee80211_ie_build_wide_bw_cs(pos, &csa_settings->chandef); 406775d627d5SSimon Wunderlich } 406875d627d5SSimon Wunderlich 4069c6da674aSChun-Yeow Yeoh ieee80211_tx_skb(sdata, skb); 4070c6da674aSChun-Yeow Yeoh return 0; 4071c6da674aSChun-Yeow Yeoh } 40722475b1ccSMax Stepanov 40732475b1ccSMax Stepanov bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) 40742475b1ccSMax Stepanov { 40752475b1ccSMax Stepanov return !(cs == NULL || cs->cipher == 0 || 40762475b1ccSMax Stepanov cs->hdr_len < cs->pn_len + cs->pn_off || 40772475b1ccSMax Stepanov cs->hdr_len <= cs->key_idx_off || 40782475b1ccSMax Stepanov cs->key_idx_shift > 7 || 40792475b1ccSMax Stepanov cs->key_idx_mask == 0); 40802475b1ccSMax Stepanov } 40812475b1ccSMax Stepanov 40822475b1ccSMax Stepanov bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) 40832475b1ccSMax Stepanov { 40842475b1ccSMax Stepanov int i; 40852475b1ccSMax Stepanov 40862475b1ccSMax Stepanov /* Ensure we have enough iftype bitmap space for all iftype values */ 40872475b1ccSMax Stepanov WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); 40882475b1ccSMax Stepanov 40892475b1ccSMax Stepanov for (i = 0; i < n; i++) 40902475b1ccSMax Stepanov if (!ieee80211_cs_valid(&cs[i])) 40912475b1ccSMax Stepanov return false; 40922475b1ccSMax Stepanov 40932475b1ccSMax Stepanov return true; 40942475b1ccSMax Stepanov } 40952475b1ccSMax Stepanov 40962475b1ccSMax Stepanov const struct ieee80211_cipher_scheme * 40972475b1ccSMax Stepanov ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, 40982475b1ccSMax Stepanov enum nl80211_iftype iftype) 40992475b1ccSMax Stepanov { 41002475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; 41012475b1ccSMax Stepanov int n = local->hw.n_cipher_schemes; 41022475b1ccSMax Stepanov int i; 41032475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *cs = NULL; 41042475b1ccSMax Stepanov 41052475b1ccSMax Stepanov for (i = 0; i < n; i++) { 41062475b1ccSMax Stepanov if (l[i].cipher == cipher) { 41072475b1ccSMax Stepanov cs = &l[i]; 41082475b1ccSMax Stepanov break; 41092475b1ccSMax Stepanov } 41102475b1ccSMax Stepanov } 41112475b1ccSMax Stepanov 41122475b1ccSMax Stepanov if (!cs || !(cs->iftype & BIT(iftype))) 41132475b1ccSMax Stepanov return NULL; 41142475b1ccSMax Stepanov 41152475b1ccSMax Stepanov return cs; 41162475b1ccSMax Stepanov } 41172475b1ccSMax Stepanov 41182475b1ccSMax Stepanov int ieee80211_cs_headroom(struct ieee80211_local *local, 41192475b1ccSMax Stepanov struct cfg80211_crypto_settings *crypto, 41202475b1ccSMax Stepanov enum nl80211_iftype iftype) 41212475b1ccSMax Stepanov { 41222475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *cs; 41232475b1ccSMax Stepanov int headroom = IEEE80211_ENCRYPT_HEADROOM; 41242475b1ccSMax Stepanov int i; 41252475b1ccSMax Stepanov 41262475b1ccSMax Stepanov for (i = 0; i < crypto->n_ciphers_pairwise; i++) { 41272475b1ccSMax Stepanov cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], 41282475b1ccSMax Stepanov iftype); 41292475b1ccSMax Stepanov 41302475b1ccSMax Stepanov if (cs && headroom < cs->hdr_len) 41312475b1ccSMax Stepanov headroom = cs->hdr_len; 41322475b1ccSMax Stepanov } 41332475b1ccSMax Stepanov 41342475b1ccSMax Stepanov cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); 41352475b1ccSMax Stepanov if (cs && headroom < cs->hdr_len) 41362475b1ccSMax Stepanov headroom = cs->hdr_len; 41372475b1ccSMax Stepanov 41382475b1ccSMax Stepanov return headroom; 41392475b1ccSMax Stepanov } 4140a7022e65SFelix Fietkau 4141a7022e65SFelix Fietkau static bool 4142a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) 4143a7022e65SFelix Fietkau { 4144a7022e65SFelix Fietkau s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); 4145a7022e65SFelix Fietkau int skip; 4146a7022e65SFelix Fietkau 4147a7022e65SFelix Fietkau if (end > 0) 4148a7022e65SFelix Fietkau return false; 4149a7022e65SFelix Fietkau 4150519ee691SJanusz.Dziedzic@tieto.com /* One shot NOA */ 4151519ee691SJanusz.Dziedzic@tieto.com if (data->count[i] == 1) 4152519ee691SJanusz.Dziedzic@tieto.com return false; 4153519ee691SJanusz.Dziedzic@tieto.com 4154519ee691SJanusz.Dziedzic@tieto.com if (data->desc[i].interval == 0) 4155519ee691SJanusz.Dziedzic@tieto.com return false; 4156519ee691SJanusz.Dziedzic@tieto.com 4157a7022e65SFelix Fietkau /* End time is in the past, check for repetitions */ 4158a7022e65SFelix Fietkau skip = DIV_ROUND_UP(-end, data->desc[i].interval); 4159a7022e65SFelix Fietkau if (data->count[i] < 255) { 4160a7022e65SFelix Fietkau if (data->count[i] <= skip) { 4161a7022e65SFelix Fietkau data->count[i] = 0; 4162a7022e65SFelix Fietkau return false; 4163a7022e65SFelix Fietkau } 4164a7022e65SFelix Fietkau 4165a7022e65SFelix Fietkau data->count[i] -= skip; 4166a7022e65SFelix Fietkau } 4167a7022e65SFelix Fietkau 4168a7022e65SFelix Fietkau data->desc[i].start += skip * data->desc[i].interval; 4169a7022e65SFelix Fietkau 4170a7022e65SFelix Fietkau return true; 4171a7022e65SFelix Fietkau } 4172a7022e65SFelix Fietkau 4173a7022e65SFelix Fietkau static bool 4174a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, 4175a7022e65SFelix Fietkau s32 *offset) 4176a7022e65SFelix Fietkau { 4177a7022e65SFelix Fietkau bool ret = false; 4178a7022e65SFelix Fietkau int i; 4179a7022e65SFelix Fietkau 4180a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 4181a7022e65SFelix Fietkau s32 cur; 4182a7022e65SFelix Fietkau 4183a7022e65SFelix Fietkau if (!data->count[i]) 4184a7022e65SFelix Fietkau continue; 4185a7022e65SFelix Fietkau 4186a7022e65SFelix Fietkau if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) 4187a7022e65SFelix Fietkau ret = true; 4188a7022e65SFelix Fietkau 4189a7022e65SFelix Fietkau cur = data->desc[i].start - tsf; 4190a7022e65SFelix Fietkau if (cur > *offset) 4191a7022e65SFelix Fietkau continue; 4192a7022e65SFelix Fietkau 4193a7022e65SFelix Fietkau cur = data->desc[i].start + data->desc[i].duration - tsf; 4194a7022e65SFelix Fietkau if (cur > *offset) 4195a7022e65SFelix Fietkau *offset = cur; 4196a7022e65SFelix Fietkau } 4197a7022e65SFelix Fietkau 4198a7022e65SFelix Fietkau return ret; 4199a7022e65SFelix Fietkau } 4200a7022e65SFelix Fietkau 4201a7022e65SFelix Fietkau static u32 4202a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) 4203a7022e65SFelix Fietkau { 4204a7022e65SFelix Fietkau s32 offset = 0; 4205a7022e65SFelix Fietkau int tries = 0; 4206a7022e65SFelix Fietkau /* 4207a7022e65SFelix Fietkau * arbitrary limit, used to avoid infinite loops when combined NoA 4208a7022e65SFelix Fietkau * descriptors cover the full time period. 4209a7022e65SFelix Fietkau */ 4210a7022e65SFelix Fietkau int max_tries = 5; 4211a7022e65SFelix Fietkau 4212a7022e65SFelix Fietkau ieee80211_extend_absent_time(data, tsf, &offset); 4213a7022e65SFelix Fietkau do { 4214a7022e65SFelix Fietkau if (!ieee80211_extend_absent_time(data, tsf, &offset)) 4215a7022e65SFelix Fietkau break; 4216a7022e65SFelix Fietkau 4217a7022e65SFelix Fietkau tries++; 4218a7022e65SFelix Fietkau } while (tries < max_tries); 4219a7022e65SFelix Fietkau 4220a7022e65SFelix Fietkau return offset; 4221a7022e65SFelix Fietkau } 4222a7022e65SFelix Fietkau 4223a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) 4224a7022e65SFelix Fietkau { 4225a7022e65SFelix Fietkau u32 next_offset = BIT(31) - 1; 4226a7022e65SFelix Fietkau int i; 4227a7022e65SFelix Fietkau 4228a7022e65SFelix Fietkau data->absent = 0; 4229a7022e65SFelix Fietkau data->has_next_tsf = false; 4230a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 4231a7022e65SFelix Fietkau s32 start; 4232a7022e65SFelix Fietkau 4233a7022e65SFelix Fietkau if (!data->count[i]) 4234a7022e65SFelix Fietkau continue; 4235a7022e65SFelix Fietkau 4236a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i); 4237a7022e65SFelix Fietkau start = data->desc[i].start - tsf; 4238a7022e65SFelix Fietkau if (start <= 0) 4239a7022e65SFelix Fietkau data->absent |= BIT(i); 4240a7022e65SFelix Fietkau 4241a7022e65SFelix Fietkau if (next_offset > start) 4242a7022e65SFelix Fietkau next_offset = start; 4243a7022e65SFelix Fietkau 4244a7022e65SFelix Fietkau data->has_next_tsf = true; 4245a7022e65SFelix Fietkau } 4246a7022e65SFelix Fietkau 4247a7022e65SFelix Fietkau if (data->absent) 4248a7022e65SFelix Fietkau next_offset = ieee80211_get_noa_absent_time(data, tsf); 4249a7022e65SFelix Fietkau 4250a7022e65SFelix Fietkau data->next_tsf = tsf + next_offset; 4251a7022e65SFelix Fietkau } 4252a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa); 4253a7022e65SFelix Fietkau 4254a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, 4255a7022e65SFelix Fietkau struct ieee80211_noa_data *data, u32 tsf) 4256a7022e65SFelix Fietkau { 4257a7022e65SFelix Fietkau int ret = 0; 4258a7022e65SFelix Fietkau int i; 4259a7022e65SFelix Fietkau 4260a7022e65SFelix Fietkau memset(data, 0, sizeof(*data)); 4261a7022e65SFelix Fietkau 4262a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 4263a7022e65SFelix Fietkau const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; 4264a7022e65SFelix Fietkau 4265a7022e65SFelix Fietkau if (!desc->count || !desc->duration) 4266a7022e65SFelix Fietkau continue; 4267a7022e65SFelix Fietkau 4268a7022e65SFelix Fietkau data->count[i] = desc->count; 4269a7022e65SFelix Fietkau data->desc[i].start = le32_to_cpu(desc->start_time); 4270a7022e65SFelix Fietkau data->desc[i].duration = le32_to_cpu(desc->duration); 4271a7022e65SFelix Fietkau data->desc[i].interval = le32_to_cpu(desc->interval); 4272a7022e65SFelix Fietkau 4273a7022e65SFelix Fietkau if (data->count[i] > 1 && 4274a7022e65SFelix Fietkau data->desc[i].interval < data->desc[i].duration) 4275a7022e65SFelix Fietkau continue; 4276a7022e65SFelix Fietkau 4277a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i); 4278a7022e65SFelix Fietkau ret++; 4279a7022e65SFelix Fietkau } 4280a7022e65SFelix Fietkau 4281a7022e65SFelix Fietkau if (ret) 4282a7022e65SFelix Fietkau ieee80211_update_p2p_noa(data, tsf); 4283a7022e65SFelix Fietkau 4284a7022e65SFelix Fietkau return ret; 4285a7022e65SFelix Fietkau } 4286a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa); 4287057d5f4bSThomas Pedersen 4288057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local, 4289057d5f4bSThomas Pedersen struct ieee80211_sub_if_data *sdata) 4290057d5f4bSThomas Pedersen { 4291057d5f4bSThomas Pedersen u64 tsf = drv_get_tsf(local, sdata); 4292057d5f4bSThomas Pedersen u64 dtim_count = 0; 4293057d5f4bSThomas Pedersen u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; 4294057d5f4bSThomas Pedersen u8 dtim_period = sdata->vif.bss_conf.dtim_period; 4295057d5f4bSThomas Pedersen struct ps_data *ps; 4296057d5f4bSThomas Pedersen u8 bcns_from_dtim; 4297057d5f4bSThomas Pedersen 4298057d5f4bSThomas Pedersen if (tsf == -1ULL || !beacon_int || !dtim_period) 4299057d5f4bSThomas Pedersen return; 4300057d5f4bSThomas Pedersen 4301057d5f4bSThomas Pedersen if (sdata->vif.type == NL80211_IFTYPE_AP || 4302057d5f4bSThomas Pedersen sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 4303057d5f4bSThomas Pedersen if (!sdata->bss) 4304057d5f4bSThomas Pedersen return; 4305057d5f4bSThomas Pedersen 4306057d5f4bSThomas Pedersen ps = &sdata->bss->ps; 4307057d5f4bSThomas Pedersen } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 4308057d5f4bSThomas Pedersen ps = &sdata->u.mesh.ps; 4309057d5f4bSThomas Pedersen } else { 4310057d5f4bSThomas Pedersen return; 4311057d5f4bSThomas Pedersen } 4312057d5f4bSThomas Pedersen 4313057d5f4bSThomas Pedersen /* 4314057d5f4bSThomas Pedersen * actually finds last dtim_count, mac80211 will update in 4315057d5f4bSThomas Pedersen * __beacon_add_tim(). 4316057d5f4bSThomas Pedersen * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period 4317057d5f4bSThomas Pedersen */ 4318057d5f4bSThomas Pedersen do_div(tsf, beacon_int); 4319057d5f4bSThomas Pedersen bcns_from_dtim = do_div(tsf, dtim_period); 4320057d5f4bSThomas Pedersen /* just had a DTIM */ 4321057d5f4bSThomas Pedersen if (!bcns_from_dtim) 4322057d5f4bSThomas Pedersen dtim_count = 0; 4323057d5f4bSThomas Pedersen else 4324057d5f4bSThomas Pedersen dtim_count = dtim_period - bcns_from_dtim; 4325057d5f4bSThomas Pedersen 4326057d5f4bSThomas Pedersen ps->dtim_count = dtim_count; 4327057d5f4bSThomas Pedersen } 432873de86a3SLuciano Coelho 432971e6195eSMichal Kazior static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, 433071e6195eSMichal Kazior struct ieee80211_chanctx *ctx) 433171e6195eSMichal Kazior { 433271e6195eSMichal Kazior struct ieee80211_sub_if_data *sdata; 433371e6195eSMichal Kazior u8 radar_detect = 0; 433471e6195eSMichal Kazior 433571e6195eSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 433671e6195eSMichal Kazior 433771e6195eSMichal Kazior if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) 433871e6195eSMichal Kazior return 0; 433971e6195eSMichal Kazior 434071e6195eSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) 434171e6195eSMichal Kazior if (sdata->reserved_radar_required) 434271e6195eSMichal Kazior radar_detect |= BIT(sdata->reserved_chandef.width); 434371e6195eSMichal Kazior 434471e6195eSMichal Kazior /* 434571e6195eSMichal Kazior * An in-place reservation context should not have any assigned vifs 434671e6195eSMichal Kazior * until it replaces the other context. 434771e6195eSMichal Kazior */ 434871e6195eSMichal Kazior WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && 434971e6195eSMichal Kazior !list_empty(&ctx->assigned_vifs)); 435071e6195eSMichal Kazior 435171e6195eSMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) 435271e6195eSMichal Kazior if (sdata->radar_required) 435371e6195eSMichal Kazior radar_detect |= BIT(sdata->vif.bss_conf.chandef.width); 435471e6195eSMichal Kazior 435571e6195eSMichal Kazior return radar_detect; 435671e6195eSMichal Kazior } 435771e6195eSMichal Kazior 435873de86a3SLuciano Coelho int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, 435973de86a3SLuciano Coelho const struct cfg80211_chan_def *chandef, 436073de86a3SLuciano Coelho enum ieee80211_chanctx_mode chanmode, 436173de86a3SLuciano Coelho u8 radar_detect) 436273de86a3SLuciano Coelho { 436373de86a3SLuciano Coelho struct ieee80211_local *local = sdata->local; 436473de86a3SLuciano Coelho struct ieee80211_sub_if_data *sdata_iter; 436573de86a3SLuciano Coelho enum nl80211_iftype iftype = sdata->wdev.iftype; 436673de86a3SLuciano Coelho struct ieee80211_chanctx *ctx; 436773de86a3SLuciano Coelho int total = 1; 4368e227300cSPurushottam Kushwaha struct iface_combination_params params = { 4369e227300cSPurushottam Kushwaha .radar_detect = radar_detect, 4370e227300cSPurushottam Kushwaha }; 437173de86a3SLuciano Coelho 437273de86a3SLuciano Coelho lockdep_assert_held(&local->chanctx_mtx); 437373de86a3SLuciano Coelho 437473de86a3SLuciano Coelho if (WARN_ON(hweight32(radar_detect) > 1)) 437573de86a3SLuciano Coelho return -EINVAL; 437673de86a3SLuciano Coelho 4377b6a55015SLuciano Coelho if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && 4378b6a55015SLuciano Coelho !chandef->chan)) 437973de86a3SLuciano Coelho return -EINVAL; 438073de86a3SLuciano Coelho 438173de86a3SLuciano Coelho if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) 438273de86a3SLuciano Coelho return -EINVAL; 438373de86a3SLuciano Coelho 4384ac668afeSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP || 4385ac668afeSJohannes Berg sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { 4386ac668afeSJohannes Berg /* 4387ac668afeSJohannes Berg * always passing this is harmless, since it'll be the 4388ac668afeSJohannes Berg * same value that cfg80211 finds if it finds the same 4389ac668afeSJohannes Berg * interface ... and that's always allowed 4390ac668afeSJohannes Berg */ 4391ac668afeSJohannes Berg params.new_beacon_int = sdata->vif.bss_conf.beacon_int; 4392ac668afeSJohannes Berg } 4393ac668afeSJohannes Berg 439473de86a3SLuciano Coelho /* Always allow software iftypes */ 4395e6f40511SManikanta Pubbisetty if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) { 439673de86a3SLuciano Coelho if (radar_detect) 439773de86a3SLuciano Coelho return -EINVAL; 439873de86a3SLuciano Coelho return 0; 439973de86a3SLuciano Coelho } 440073de86a3SLuciano Coelho 4401e227300cSPurushottam Kushwaha if (chandef) 4402e227300cSPurushottam Kushwaha params.num_different_channels = 1; 440373de86a3SLuciano Coelho 440473de86a3SLuciano Coelho if (iftype != NL80211_IFTYPE_UNSPECIFIED) 4405e227300cSPurushottam Kushwaha params.iftype_num[iftype] = 1; 440673de86a3SLuciano Coelho 440773de86a3SLuciano Coelho list_for_each_entry(ctx, &local->chanctx_list, list) { 44085bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 44095bcae31dSMichal Kazior continue; 4410e227300cSPurushottam Kushwaha params.radar_detect |= 4411e227300cSPurushottam Kushwaha ieee80211_chanctx_radar_detect(local, ctx); 441273de86a3SLuciano Coelho if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { 4413e227300cSPurushottam Kushwaha params.num_different_channels++; 441473de86a3SLuciano Coelho continue; 441573de86a3SLuciano Coelho } 4416b6a55015SLuciano Coelho if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && 441773de86a3SLuciano Coelho cfg80211_chandef_compatible(chandef, 441873de86a3SLuciano Coelho &ctx->conf.def)) 441973de86a3SLuciano Coelho continue; 4420e227300cSPurushottam Kushwaha params.num_different_channels++; 442173de86a3SLuciano Coelho } 442273de86a3SLuciano Coelho 442373de86a3SLuciano Coelho list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { 442473de86a3SLuciano Coelho struct wireless_dev *wdev_iter; 442573de86a3SLuciano Coelho 442673de86a3SLuciano Coelho wdev_iter = &sdata_iter->wdev; 442773de86a3SLuciano Coelho 442873de86a3SLuciano Coelho if (sdata_iter == sdata || 44290f611d28SAndrei Otcheretianski !ieee80211_sdata_running(sdata_iter) || 4430e6f40511SManikanta Pubbisetty cfg80211_iftype_allowed(local->hw.wiphy, 4431e6f40511SManikanta Pubbisetty wdev_iter->iftype, 0, 1)) 443273de86a3SLuciano Coelho continue; 443373de86a3SLuciano Coelho 4434e227300cSPurushottam Kushwaha params.iftype_num[wdev_iter->iftype]++; 443573de86a3SLuciano Coelho total++; 443673de86a3SLuciano Coelho } 443773de86a3SLuciano Coelho 4438e227300cSPurushottam Kushwaha if (total == 1 && !params.radar_detect) 443973de86a3SLuciano Coelho return 0; 444073de86a3SLuciano Coelho 4441e227300cSPurushottam Kushwaha return cfg80211_check_combinations(local->hw.wiphy, ¶ms); 444273de86a3SLuciano Coelho } 44436fa001bcSMichal Kazior 44446fa001bcSMichal Kazior static void 44456fa001bcSMichal Kazior ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, 44466fa001bcSMichal Kazior void *data) 44476fa001bcSMichal Kazior { 44486fa001bcSMichal Kazior u32 *max_num_different_channels = data; 44496fa001bcSMichal Kazior 44506fa001bcSMichal Kazior *max_num_different_channels = max(*max_num_different_channels, 44516fa001bcSMichal Kazior c->num_different_channels); 44526fa001bcSMichal Kazior } 44536fa001bcSMichal Kazior 44546fa001bcSMichal Kazior int ieee80211_max_num_channels(struct ieee80211_local *local) 44556fa001bcSMichal Kazior { 44566fa001bcSMichal Kazior struct ieee80211_sub_if_data *sdata; 44576fa001bcSMichal Kazior struct ieee80211_chanctx *ctx; 44586fa001bcSMichal Kazior u32 max_num_different_channels = 1; 44596fa001bcSMichal Kazior int err; 4460e227300cSPurushottam Kushwaha struct iface_combination_params params = {0}; 44616fa001bcSMichal Kazior 44626fa001bcSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 44636fa001bcSMichal Kazior 44646fa001bcSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 44655bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 44665bcae31dSMichal Kazior continue; 44675bcae31dSMichal Kazior 4468e227300cSPurushottam Kushwaha params.num_different_channels++; 44696fa001bcSMichal Kazior 4470e227300cSPurushottam Kushwaha params.radar_detect |= 4471e227300cSPurushottam Kushwaha ieee80211_chanctx_radar_detect(local, ctx); 44726fa001bcSMichal Kazior } 44736fa001bcSMichal Kazior 44746fa001bcSMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) 4475e227300cSPurushottam Kushwaha params.iftype_num[sdata->wdev.iftype]++; 44766fa001bcSMichal Kazior 4477e227300cSPurushottam Kushwaha err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms, 4478e227300cSPurushottam Kushwaha ieee80211_iter_max_chans, 44796fa001bcSMichal Kazior &max_num_different_channels); 44806fa001bcSMichal Kazior if (err < 0) 44816fa001bcSMichal Kazior return err; 44826fa001bcSMichal Kazior 44836fa001bcSMichal Kazior return max_num_different_channels; 44846fa001bcSMichal Kazior } 448540b861a0SArik Nemtsov 44867957c6c8SThomas Pedersen void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, 44877957c6c8SThomas Pedersen struct ieee80211_sta_s1g_cap *caps, 44887957c6c8SThomas Pedersen struct sk_buff *skb) 44897957c6c8SThomas Pedersen { 44907957c6c8SThomas Pedersen struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 44917957c6c8SThomas Pedersen struct ieee80211_s1g_cap s1g_capab; 44927957c6c8SThomas Pedersen u8 *pos; 44937957c6c8SThomas Pedersen int i; 44947957c6c8SThomas Pedersen 44957957c6c8SThomas Pedersen if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 44967957c6c8SThomas Pedersen return; 44977957c6c8SThomas Pedersen 44987957c6c8SThomas Pedersen if (!caps->s1g) 44997957c6c8SThomas Pedersen return; 45007957c6c8SThomas Pedersen 45017957c6c8SThomas Pedersen memcpy(s1g_capab.capab_info, caps->cap, sizeof(caps->cap)); 45027957c6c8SThomas Pedersen memcpy(s1g_capab.supp_mcs_nss, caps->nss_mcs, sizeof(caps->nss_mcs)); 45037957c6c8SThomas Pedersen 45047957c6c8SThomas Pedersen /* override the capability info */ 45057957c6c8SThomas Pedersen for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) { 45067957c6c8SThomas Pedersen u8 mask = ifmgd->s1g_capa_mask.capab_info[i]; 45077957c6c8SThomas Pedersen 45087957c6c8SThomas Pedersen s1g_capab.capab_info[i] &= ~mask; 45097957c6c8SThomas Pedersen s1g_capab.capab_info[i] |= ifmgd->s1g_capa.capab_info[i] & mask; 45107957c6c8SThomas Pedersen } 45117957c6c8SThomas Pedersen 45127957c6c8SThomas Pedersen /* then MCS and NSS set */ 45137957c6c8SThomas Pedersen for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) { 45147957c6c8SThomas Pedersen u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i]; 45157957c6c8SThomas Pedersen 45167957c6c8SThomas Pedersen s1g_capab.supp_mcs_nss[i] &= ~mask; 45177957c6c8SThomas Pedersen s1g_capab.supp_mcs_nss[i] |= 45187957c6c8SThomas Pedersen ifmgd->s1g_capa.supp_mcs_nss[i] & mask; 45197957c6c8SThomas Pedersen } 45207957c6c8SThomas Pedersen 45217957c6c8SThomas Pedersen pos = skb_put(skb, 2 + sizeof(s1g_capab)); 45227957c6c8SThomas Pedersen *pos++ = WLAN_EID_S1G_CAPABILITIES; 45237957c6c8SThomas Pedersen *pos++ = sizeof(s1g_capab); 45247957c6c8SThomas Pedersen 45257957c6c8SThomas Pedersen memcpy(pos, &s1g_capab, sizeof(s1g_capab)); 45267957c6c8SThomas Pedersen } 45277957c6c8SThomas Pedersen 45281d00ce80SThomas Pedersen void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, 45291d00ce80SThomas Pedersen struct sk_buff *skb) 45301d00ce80SThomas Pedersen { 45311d00ce80SThomas Pedersen u8 *pos = skb_put(skb, 3); 45321d00ce80SThomas Pedersen 45331d00ce80SThomas Pedersen *pos++ = WLAN_EID_AID_REQUEST; 45341d00ce80SThomas Pedersen *pos++ = 1; 45351d00ce80SThomas Pedersen *pos++ = 0; 45361d00ce80SThomas Pedersen } 45371d00ce80SThomas Pedersen 453840b861a0SArik Nemtsov u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) 453940b861a0SArik Nemtsov { 454040b861a0SArik Nemtsov *buf++ = WLAN_EID_VENDOR_SPECIFIC; 454140b861a0SArik Nemtsov *buf++ = 7; /* len */ 454240b861a0SArik Nemtsov *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ 454340b861a0SArik Nemtsov *buf++ = 0x50; 454440b861a0SArik Nemtsov *buf++ = 0xf2; 454540b861a0SArik Nemtsov *buf++ = 2; /* WME */ 454640b861a0SArik Nemtsov *buf++ = 0; /* WME info */ 454740b861a0SArik Nemtsov *buf++ = 1; /* WME ver */ 454840b861a0SArik Nemtsov *buf++ = qosinfo; /* U-APSD no in use */ 454940b861a0SArik Nemtsov 455040b861a0SArik Nemtsov return buf; 455140b861a0SArik Nemtsov } 4552ba8c3d6fSFelix Fietkau 4553f2ac7e30SMichal Kazior void ieee80211_txq_get_depth(struct ieee80211_txq *txq, 4554f2ac7e30SMichal Kazior unsigned long *frame_cnt, 4555f2ac7e30SMichal Kazior unsigned long *byte_cnt) 4556f2ac7e30SMichal Kazior { 4557f2ac7e30SMichal Kazior struct txq_info *txqi = to_txq_info(txq); 4558bb42f2d1SToke Høiland-Jørgensen u32 frag_cnt = 0, frag_bytes = 0; 4559bb42f2d1SToke Høiland-Jørgensen struct sk_buff *skb; 4560bb42f2d1SToke Høiland-Jørgensen 4561bb42f2d1SToke Høiland-Jørgensen skb_queue_walk(&txqi->frags, skb) { 4562bb42f2d1SToke Høiland-Jørgensen frag_cnt++; 4563bb42f2d1SToke Høiland-Jørgensen frag_bytes += skb->len; 4564bb42f2d1SToke Høiland-Jørgensen } 4565f2ac7e30SMichal Kazior 4566f2ac7e30SMichal Kazior if (frame_cnt) 4567bb42f2d1SToke Høiland-Jørgensen *frame_cnt = txqi->tin.backlog_packets + frag_cnt; 4568f2ac7e30SMichal Kazior 4569f2ac7e30SMichal Kazior if (byte_cnt) 4570bb42f2d1SToke Høiland-Jørgensen *byte_cnt = txqi->tin.backlog_bytes + frag_bytes; 4571f2ac7e30SMichal Kazior } 4572f2ac7e30SMichal Kazior EXPORT_SYMBOL(ieee80211_txq_get_depth); 4573f438ceb8SEmmanuel Grumbach 4574f438ceb8SEmmanuel Grumbach const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = { 4575f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, 4576f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_VI, 4577f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_BE, 4578f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_BK 4579f438ceb8SEmmanuel Grumbach }; 458005d10957SThomas Pedersen 458105d10957SThomas Pedersen u16 ieee80211_encode_usf(int listen_interval) 458205d10957SThomas Pedersen { 458305d10957SThomas Pedersen static const int listen_int_usf[] = { 1, 10, 1000, 10000 }; 458405d10957SThomas Pedersen u16 ui, usf = 0; 458505d10957SThomas Pedersen 458605d10957SThomas Pedersen /* find greatest USF */ 458705d10957SThomas Pedersen while (usf < IEEE80211_MAX_USF) { 458805d10957SThomas Pedersen if (listen_interval % listen_int_usf[usf + 1]) 458905d10957SThomas Pedersen break; 459005d10957SThomas Pedersen usf += 1; 459105d10957SThomas Pedersen } 459205d10957SThomas Pedersen ui = listen_interval / listen_int_usf[usf]; 459305d10957SThomas Pedersen 459405d10957SThomas Pedersen /* error if there is a remainder. Should've been checked by user */ 459505d10957SThomas Pedersen WARN_ON_ONCE(ui > IEEE80211_MAX_UI); 459605d10957SThomas Pedersen listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) | 459705d10957SThomas Pedersen FIELD_PREP(LISTEN_INT_UI, ui); 459805d10957SThomas Pedersen 459905d10957SThomas Pedersen return (u16) listen_interval; 460005d10957SThomas Pedersen } 4601