1c2d1560aSJohannes Berg /* 2c2d1560aSJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc. 3c2d1560aSJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc. 4c2d1560aSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 5c2d1560aSJohannes Berg * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 6c2d1560aSJohannes Berg * 7c2d1560aSJohannes Berg * This program is free software; you can redistribute it and/or modify 8c2d1560aSJohannes Berg * it under the terms of the GNU General Public License version 2 as 9c2d1560aSJohannes Berg * published by the Free Software Foundation. 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 */ 37c2d1560aSJohannes Berg void *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 BUG_ON(!wiphy); 439a95371aSLuis R. Rodriguez 449a95371aSLuis R. Rodriguez local = wiphy_priv(wiphy); 459a95371aSLuis R. Rodriguez return &local->hw; 469a95371aSLuis R. Rodriguez } 479a95371aSLuis R. Rodriguez EXPORT_SYMBOL(wiphy_to_ieee80211_hw); 48c2d1560aSJohannes Berg 4971364716SRon Rindjunsky u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, 5005c914feSJohannes Berg enum nl80211_iftype type) 51c2d1560aSJohannes Berg { 52a494bb1cSHarvey Harrison __le16 fc = hdr->frame_control; 53c2d1560aSJohannes Berg 5498f0b0a3SRon Rindjunsky /* drop ACK/CTS frames and incorrect hdr len (ctrl) */ 5598f0b0a3SRon Rindjunsky if (len < 16) 56c2d1560aSJohannes Berg return NULL; 57c2d1560aSJohannes Berg 58a494bb1cSHarvey Harrison if (ieee80211_is_data(fc)) { 5998f0b0a3SRon Rindjunsky if (len < 24) /* drop incorrect hdr len (data) */ 6098f0b0a3SRon Rindjunsky return NULL; 61a494bb1cSHarvey Harrison 62a494bb1cSHarvey Harrison if (ieee80211_has_a4(fc)) 63c2d1560aSJohannes Berg return NULL; 64a494bb1cSHarvey Harrison if (ieee80211_has_tods(fc)) 65a494bb1cSHarvey Harrison return hdr->addr1; 66a494bb1cSHarvey Harrison if (ieee80211_has_fromds(fc)) 67c2d1560aSJohannes Berg return hdr->addr2; 68a494bb1cSHarvey Harrison 69c2d1560aSJohannes Berg return hdr->addr3; 70c2d1560aSJohannes Berg } 71a494bb1cSHarvey Harrison 72a494bb1cSHarvey Harrison if (ieee80211_is_mgmt(fc)) { 7398f0b0a3SRon Rindjunsky if (len < 24) /* drop incorrect hdr len (mgmt) */ 7498f0b0a3SRon Rindjunsky return NULL; 75c2d1560aSJohannes Berg return hdr->addr3; 76a494bb1cSHarvey Harrison } 77a494bb1cSHarvey Harrison 78a494bb1cSHarvey Harrison if (ieee80211_is_ctl(fc)) { 79a494bb1cSHarvey Harrison if (ieee80211_is_pspoll(fc)) 80c2d1560aSJohannes Berg return hdr->addr1; 81a494bb1cSHarvey Harrison 82a494bb1cSHarvey Harrison if (ieee80211_is_back_req(fc)) { 8371364716SRon Rindjunsky switch (type) { 8405c914feSJohannes Berg case NL80211_IFTYPE_STATION: 8571364716SRon Rindjunsky return hdr->addr2; 8605c914feSJohannes Berg case NL80211_IFTYPE_AP: 8705c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 8871364716SRon Rindjunsky return hdr->addr1; 8971364716SRon Rindjunsky default: 90a494bb1cSHarvey Harrison break; /* fall through to the return */ 9171364716SRon Rindjunsky } 9271364716SRon Rindjunsky } 93c2d1560aSJohannes Berg } 94c2d1560aSJohannes Berg 95c2d1560aSJohannes Berg return NULL; 96c2d1560aSJohannes Berg } 97c2d1560aSJohannes Berg 985cf121c3SJohannes Berg void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) 99c2d1560aSJohannes Berg { 100252b86c4SJohannes Berg struct sk_buff *skb; 1012de8e0d9SJohannes Berg struct ieee80211_hdr *hdr; 102c2d1560aSJohannes Berg 103252b86c4SJohannes Berg skb_queue_walk(&tx->skbs, skb) { 1042de8e0d9SJohannes Berg hdr = (struct ieee80211_hdr *) skb->data; 105c2d1560aSJohannes Berg hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 106252b86c4SJohannes Berg } 107c2d1560aSJohannes Berg } 108c2d1560aSJohannes Berg 1094ee73f33SMichal Kazior int ieee80211_frame_duration(enum ieee80211_band band, size_t len, 110438b61b7SSimon Wunderlich int rate, int erp, int short_preamble, 111438b61b7SSimon Wunderlich int shift) 112c2d1560aSJohannes Berg { 113c2d1560aSJohannes Berg int dur; 114c2d1560aSJohannes Berg 115c2d1560aSJohannes Berg /* calculate duration (in microseconds, rounded up to next higher 116c2d1560aSJohannes Berg * integer if it includes a fractional microsecond) to send frame of 117c2d1560aSJohannes Berg * len bytes (does not include FCS) at the given rate. Duration will 118c2d1560aSJohannes Berg * also include SIFS. 119c2d1560aSJohannes Berg * 120c2d1560aSJohannes Berg * rate is in 100 kbps, so divident is multiplied by 10 in the 121c2d1560aSJohannes Berg * DIV_ROUND_UP() operations. 122438b61b7SSimon Wunderlich * 123438b61b7SSimon Wunderlich * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and 124438b61b7SSimon Wunderlich * is assumed to be 0 otherwise. 125c2d1560aSJohannes Berg */ 126c2d1560aSJohannes Berg 1274ee73f33SMichal Kazior if (band == IEEE80211_BAND_5GHZ || erp) { 128c2d1560aSJohannes Berg /* 129c2d1560aSJohannes Berg * OFDM: 130c2d1560aSJohannes Berg * 131c2d1560aSJohannes Berg * N_DBPS = DATARATE x 4 132c2d1560aSJohannes Berg * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) 133c2d1560aSJohannes Berg * (16 = SIGNAL time, 6 = tail bits) 134c2d1560aSJohannes Berg * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext 135c2d1560aSJohannes Berg * 136c2d1560aSJohannes Berg * T_SYM = 4 usec 137438b61b7SSimon Wunderlich * 802.11a - 18.5.2: aSIFSTime = 16 usec 138c2d1560aSJohannes Berg * 802.11g - 19.8.4: aSIFSTime = 10 usec + 139c2d1560aSJohannes Berg * signal ext = 6 usec 140c2d1560aSJohannes Berg */ 141c2d1560aSJohannes Berg dur = 16; /* SIFS + signal ext */ 142438b61b7SSimon Wunderlich dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ 143438b61b7SSimon Wunderlich dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ 144438b61b7SSimon Wunderlich 145438b61b7SSimon Wunderlich /* IEEE 802.11-2012 18.3.2.4: all values above are: 146438b61b7SSimon Wunderlich * * times 4 for 5 MHz 147438b61b7SSimon Wunderlich * * times 2 for 10 MHz 148438b61b7SSimon Wunderlich */ 149438b61b7SSimon Wunderlich dur *= 1 << shift; 1502103dec1SSimon Wunderlich 1512103dec1SSimon Wunderlich /* rates should already consider the channel bandwidth, 1522103dec1SSimon Wunderlich * don't apply divisor again. 1532103dec1SSimon Wunderlich */ 1542103dec1SSimon Wunderlich dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 1552103dec1SSimon Wunderlich 4 * rate); /* T_SYM x N_SYM */ 156c2d1560aSJohannes Berg } else { 157c2d1560aSJohannes Berg /* 158c2d1560aSJohannes Berg * 802.11b or 802.11g with 802.11b compatibility: 159c2d1560aSJohannes Berg * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + 160c2d1560aSJohannes Berg * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 161c2d1560aSJohannes Berg * 162c2d1560aSJohannes Berg * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 163c2d1560aSJohannes Berg * aSIFSTime = 10 usec 164c2d1560aSJohannes Berg * aPreambleLength = 144 usec or 72 usec with short preamble 165c2d1560aSJohannes Berg * aPLCPHeaderLength = 48 usec or 24 usec with short preamble 166c2d1560aSJohannes Berg */ 167c2d1560aSJohannes Berg dur = 10; /* aSIFSTime = 10 usec */ 168c2d1560aSJohannes Berg dur += short_preamble ? (72 + 24) : (144 + 48); 169c2d1560aSJohannes Berg 170c2d1560aSJohannes Berg dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); 171c2d1560aSJohannes Berg } 172c2d1560aSJohannes Berg 173c2d1560aSJohannes Berg return dur; 174c2d1560aSJohannes Berg } 175c2d1560aSJohannes Berg 176c2d1560aSJohannes Berg /* Exported duration function for driver use */ 17732bfd35dSJohannes Berg __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, 17832bfd35dSJohannes Berg struct ieee80211_vif *vif, 1794ee73f33SMichal Kazior enum ieee80211_band band, 1808318d78aSJohannes Berg size_t frame_len, 1818318d78aSJohannes Berg struct ieee80211_rate *rate) 182c2d1560aSJohannes Berg { 18325d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 184c2d1560aSJohannes Berg u16 dur; 185438b61b7SSimon Wunderlich int erp, shift = 0; 18625d834e1SJohannes Berg bool short_preamble = false; 187c2d1560aSJohannes Berg 1888318d78aSJohannes Berg erp = 0; 18925d834e1SJohannes Berg if (vif) { 19025d834e1SJohannes Berg sdata = vif_to_sdata(vif); 191bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 1928318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 1938318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 194438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 19525d834e1SJohannes Berg } 1968318d78aSJohannes Berg 1974ee73f33SMichal Kazior dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, 198438b61b7SSimon Wunderlich short_preamble, shift); 199c2d1560aSJohannes Berg 200c2d1560aSJohannes Berg return cpu_to_le16(dur); 201c2d1560aSJohannes Berg } 202c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_generic_frame_duration); 203c2d1560aSJohannes Berg 20432bfd35dSJohannes Berg __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, 20532bfd35dSJohannes Berg struct ieee80211_vif *vif, size_t frame_len, 206e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 207c2d1560aSJohannes Berg { 208c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 209c2d1560aSJohannes Berg struct ieee80211_rate *rate; 21025d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 211471b3efdSJohannes Berg bool short_preamble; 2122103dec1SSimon Wunderlich int erp, shift = 0, bitrate; 213c2d1560aSJohannes Berg u16 dur; 2142e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2152e92e6f2SJohannes Berg 2164ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 217c2d1560aSJohannes Berg 21825d834e1SJohannes Berg short_preamble = false; 2197e9ed188SDaniel Drake 220e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2218318d78aSJohannes Berg 2228318d78aSJohannes Berg erp = 0; 22325d834e1SJohannes Berg if (vif) { 22425d834e1SJohannes Berg sdata = vif_to_sdata(vif); 225bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2268318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2278318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 228438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 22925d834e1SJohannes Berg } 230c2d1560aSJohannes Berg 2312103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2322103dec1SSimon Wunderlich 233c2d1560aSJohannes Berg /* CTS duration */ 2342103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, 10, bitrate, 235438b61b7SSimon Wunderlich erp, short_preamble, shift); 236c2d1560aSJohannes Berg /* Data frame duration */ 2372103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, 238438b61b7SSimon Wunderlich erp, short_preamble, shift); 239c2d1560aSJohannes Berg /* ACK duration */ 2402103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate, 241438b61b7SSimon Wunderlich erp, short_preamble, shift); 242c2d1560aSJohannes Berg 243c2d1560aSJohannes Berg return cpu_to_le16(dur); 244c2d1560aSJohannes Berg } 245c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_rts_duration); 246c2d1560aSJohannes Berg 24732bfd35dSJohannes Berg __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, 24832bfd35dSJohannes Berg struct ieee80211_vif *vif, 249c2d1560aSJohannes Berg size_t frame_len, 250e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 251c2d1560aSJohannes Berg { 252c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 253c2d1560aSJohannes Berg struct ieee80211_rate *rate; 25425d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 255471b3efdSJohannes Berg bool short_preamble; 2562103dec1SSimon Wunderlich int erp, shift = 0, bitrate; 257c2d1560aSJohannes Berg u16 dur; 2582e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2592e92e6f2SJohannes Berg 2604ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 261c2d1560aSJohannes Berg 26225d834e1SJohannes Berg short_preamble = false; 2637e9ed188SDaniel Drake 264e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2658318d78aSJohannes Berg erp = 0; 26625d834e1SJohannes Berg if (vif) { 26725d834e1SJohannes Berg sdata = vif_to_sdata(vif); 268bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2698318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2708318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 271438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif); 27225d834e1SJohannes Berg } 273c2d1560aSJohannes Berg 2742103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2752103dec1SSimon Wunderlich 276c2d1560aSJohannes Berg /* Data frame duration */ 2772103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, 278438b61b7SSimon Wunderlich erp, short_preamble, shift); 279e039fa4aSJohannes Berg if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { 280c2d1560aSJohannes Berg /* ACK duration */ 2812103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate, 282438b61b7SSimon Wunderlich erp, short_preamble, shift); 283c2d1560aSJohannes Berg } 284c2d1560aSJohannes Berg 285c2d1560aSJohannes Berg return cpu_to_le16(dur); 286c2d1560aSJohannes Berg } 287c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_ctstoself_duration); 288c2d1560aSJohannes Berg 2893a25a8c8SJohannes Berg void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) 2903a25a8c8SJohannes Berg { 2913a25a8c8SJohannes Berg struct ieee80211_sub_if_data *sdata; 292a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 293a6f38ac3SJohannes Berg 294a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 295a6f38ac3SJohannes Berg n_acs = 1; 2963a25a8c8SJohannes Berg 2973a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 2983a25a8c8SJohannes Berg int ac; 2993a25a8c8SJohannes Berg 300f142c6b9SJohannes Berg if (!sdata->dev) 301f142c6b9SJohannes Berg continue; 302f142c6b9SJohannes Berg 3033a25a8c8SJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && 3043a25a8c8SJohannes Berg local->queue_stop_reasons[sdata->vif.cab_queue] != 0) 3053a25a8c8SJohannes Berg continue; 3063a25a8c8SJohannes Berg 307a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 3083a25a8c8SJohannes Berg int ac_queue = sdata->vif.hw_queue[ac]; 3093a25a8c8SJohannes Berg 3103a25a8c8SJohannes Berg if (ac_queue == queue || 3113a25a8c8SJohannes Berg (sdata->vif.cab_queue == queue && 3123a25a8c8SJohannes Berg local->queue_stop_reasons[ac_queue] == 0 && 3133a25a8c8SJohannes Berg skb_queue_empty(&local->pending[ac_queue]))) 3143a25a8c8SJohannes Berg netif_wake_subqueue(sdata->dev, ac); 3153a25a8c8SJohannes Berg } 3163a25a8c8SJohannes Berg } 3173a25a8c8SJohannes Berg } 3183a25a8c8SJohannes Berg 319ce7c9111SKalle Valo static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, 320ce7c9111SKalle Valo enum queue_stop_reason reason) 321c2d1560aSJohannes Berg { 322c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 323c2d1560aSJohannes Berg 324b5878a2dSJohannes Berg trace_wake_queue(local, queue, reason); 325b5878a2dSJohannes Berg 326e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 32796f5e66eSJohannes Berg return; 32896f5e66eSJohannes Berg 329ada15125SJohannes Berg if (!test_bit(reason, &local->queue_stop_reasons[queue])) 330ada15125SJohannes Berg return; 331ada15125SJohannes Berg 332ce7c9111SKalle Valo __clear_bit(reason, &local->queue_stop_reasons[queue]); 333ce7c9111SKalle Valo 334ce7c9111SKalle Valo if (local->queue_stop_reasons[queue] != 0) 335ce7c9111SKalle Valo /* someone still has this queue stopped */ 336ce7c9111SKalle Valo return; 337ce7c9111SKalle Valo 3387236fe29SJohannes Berg if (skb_queue_empty(&local->pending[queue])) { 339cf0277e7SJohannes Berg rcu_read_lock(); 3403a25a8c8SJohannes Berg ieee80211_propagate_queue_wake(local, queue); 341cf0277e7SJohannes Berg rcu_read_unlock(); 3427236fe29SJohannes Berg } else 3437236fe29SJohannes Berg tasklet_schedule(&local->tx_pending_tasklet); 344c2d1560aSJohannes Berg } 345ce7c9111SKalle Valo 34696f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, 347ce7c9111SKalle Valo enum queue_stop_reason reason) 348ce7c9111SKalle Valo { 349ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 350ce7c9111SKalle Valo unsigned long flags; 351ce7c9111SKalle Valo 352ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 353ce7c9111SKalle Valo __ieee80211_wake_queue(hw, queue, reason); 354ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 355ce7c9111SKalle Valo } 356ce7c9111SKalle Valo 357ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) 358ce7c9111SKalle Valo { 359ce7c9111SKalle Valo ieee80211_wake_queue_by_reason(hw, queue, 360ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 361ce7c9111SKalle Valo } 362c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue); 363c2d1560aSJohannes Berg 364ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, 365ce7c9111SKalle Valo enum queue_stop_reason reason) 366c2d1560aSJohannes Berg { 367c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 368cf0277e7SJohannes Berg struct ieee80211_sub_if_data *sdata; 369a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 370c2d1560aSJohannes Berg 371b5878a2dSJohannes Berg trace_stop_queue(local, queue, reason); 372b5878a2dSJohannes Berg 373e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 37496f5e66eSJohannes Berg return; 37596f5e66eSJohannes Berg 376ada15125SJohannes Berg if (test_bit(reason, &local->queue_stop_reasons[queue])) 377ada15125SJohannes Berg return; 378ada15125SJohannes Berg 3792a577d98SJohannes Berg __set_bit(reason, &local->queue_stop_reasons[queue]); 380cf0277e7SJohannes Berg 381a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 382a6f38ac3SJohannes Berg n_acs = 1; 383a6f38ac3SJohannes Berg 384cf0277e7SJohannes Berg rcu_read_lock(); 3853a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 3863a25a8c8SJohannes Berg int ac; 3873a25a8c8SJohannes Berg 388f142c6b9SJohannes Berg if (!sdata->dev) 389f142c6b9SJohannes Berg continue; 390f142c6b9SJohannes Berg 391a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 3923a25a8c8SJohannes Berg if (sdata->vif.hw_queue[ac] == queue || 3933a25a8c8SJohannes Berg sdata->vif.cab_queue == queue) 3943a25a8c8SJohannes Berg netif_stop_subqueue(sdata->dev, ac); 3953a25a8c8SJohannes Berg } 3963a25a8c8SJohannes Berg } 397cf0277e7SJohannes Berg rcu_read_unlock(); 398c2d1560aSJohannes Berg } 399ce7c9111SKalle Valo 40096f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, 401ce7c9111SKalle Valo enum queue_stop_reason reason) 402ce7c9111SKalle Valo { 403ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 404ce7c9111SKalle Valo unsigned long flags; 405ce7c9111SKalle Valo 406ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 407ce7c9111SKalle Valo __ieee80211_stop_queue(hw, queue, reason); 408ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 409ce7c9111SKalle Valo } 410ce7c9111SKalle Valo 411ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) 412ce7c9111SKalle Valo { 413ce7c9111SKalle Valo ieee80211_stop_queue_by_reason(hw, queue, 414ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 415ce7c9111SKalle Valo } 416c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue); 417c2d1560aSJohannes Berg 4188f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local, 4198f77f384SJohannes Berg struct sk_buff *skb) 4208f77f384SJohannes Berg { 4218f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 4228f77f384SJohannes Berg unsigned long flags; 423a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 4243a25a8c8SJohannes Berg int queue = info->hw_queue; 425a7bc376cSJohannes Berg 426a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 427d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 428a7bc376cSJohannes Berg return; 429a7bc376cSJohannes Berg } 4308f77f384SJohannes Berg 4318f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4328f77f384SJohannes Berg __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4333b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 4348f77f384SJohannes Berg __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4358f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4368f77f384SJohannes Berg } 4378f77f384SJohannes Berg 438b0b97a8aSJohannes Berg void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, 43950a9432dSJohannes Berg struct sk_buff_head *skbs, 44050a9432dSJohannes Berg void (*fn)(void *data), void *data) 4418f77f384SJohannes Berg { 4428f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 4438f77f384SJohannes Berg struct sk_buff *skb; 4448f77f384SJohannes Berg unsigned long flags; 445b0b97a8aSJohannes Berg int queue, i; 4468f77f384SJohannes Berg 4478f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4488f77f384SJohannes Berg while ((skb = skb_dequeue(skbs))) { 449a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 450a7bc376cSJohannes Berg 451a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 452d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 453a7bc376cSJohannes Berg continue; 454a7bc376cSJohannes Berg } 455a7bc376cSJohannes Berg 4563a25a8c8SJohannes Berg queue = info->hw_queue; 4574644ae89SJohannes Berg 4584644ae89SJohannes Berg __ieee80211_stop_queue(hw, queue, 4594644ae89SJohannes Berg IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4604644ae89SJohannes Berg 4613b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 4628f77f384SJohannes Berg } 4638f77f384SJohannes Berg 46450a9432dSJohannes Berg if (fn) 46550a9432dSJohannes Berg fn(data); 46650a9432dSJohannes Berg 4673b8d81e0SJohannes Berg for (i = 0; i < hw->queues; i++) 4688f77f384SJohannes Berg __ieee80211_wake_queue(hw, i, 4698f77f384SJohannes Berg IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4708f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4718f77f384SJohannes Berg } 4728f77f384SJohannes Berg 473ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 474445ea4e8SJohannes Berg unsigned long queues, 475ce7c9111SKalle Valo enum queue_stop_reason reason) 476c2d1560aSJohannes Berg { 477ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 478ce7c9111SKalle Valo unsigned long flags; 479c2d1560aSJohannes Berg int i; 480c2d1560aSJohannes Berg 481ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 482ce7c9111SKalle Valo 483445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 484ce7c9111SKalle Valo __ieee80211_stop_queue(hw, i, reason); 485ce7c9111SKalle Valo 486ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 487ce7c9111SKalle Valo } 488ce7c9111SKalle Valo 489ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw) 490ce7c9111SKalle Valo { 491445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 492ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 493c2d1560aSJohannes Berg } 494c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues); 495c2d1560aSJohannes Berg 49692ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) 49792ab8535STomas Winkler { 49892ab8535STomas Winkler struct ieee80211_local *local = hw_to_local(hw); 4993b8d81e0SJohannes Berg unsigned long flags; 5003b8d81e0SJohannes Berg int ret; 50196f5e66eSJohannes Berg 502e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 50396f5e66eSJohannes Berg return true; 50496f5e66eSJohannes Berg 5053b8d81e0SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 5062419ea14SThomas Pedersen ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, 5072419ea14SThomas Pedersen &local->queue_stop_reasons[queue]); 5083b8d81e0SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 5093b8d81e0SJohannes Berg return ret; 51092ab8535STomas Winkler } 51192ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped); 51292ab8535STomas Winkler 513ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, 514445ea4e8SJohannes Berg unsigned long queues, 515ce7c9111SKalle Valo enum queue_stop_reason reason) 516c2d1560aSJohannes Berg { 517ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 518ce7c9111SKalle Valo unsigned long flags; 519c2d1560aSJohannes Berg int i; 520c2d1560aSJohannes Berg 521ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 522ce7c9111SKalle Valo 523445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 524ce7c9111SKalle Valo __ieee80211_wake_queue(hw, i, reason); 525ce7c9111SKalle Valo 526ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 527ce7c9111SKalle Valo } 528ce7c9111SKalle Valo 529ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw) 530ce7c9111SKalle Valo { 531445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 532445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_DRIVER); 533c2d1560aSJohannes Berg } 534c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues); 535dabeb344SJohannes Berg 53639ecc01dSJohannes Berg void ieee80211_flush_queues(struct ieee80211_local *local, 53739ecc01dSJohannes Berg struct ieee80211_sub_if_data *sdata) 53839ecc01dSJohannes Berg { 53939ecc01dSJohannes Berg u32 queues; 54039ecc01dSJohannes Berg 54139ecc01dSJohannes Berg if (!local->ops->flush) 54239ecc01dSJohannes Berg return; 54339ecc01dSJohannes Berg 54439ecc01dSJohannes Berg if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { 54539ecc01dSJohannes Berg int ac; 54639ecc01dSJohannes Berg 54739ecc01dSJohannes Berg queues = 0; 54839ecc01dSJohannes Berg 54939ecc01dSJohannes Berg for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 55039ecc01dSJohannes Berg queues |= BIT(sdata->vif.hw_queue[ac]); 55139ecc01dSJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) 55239ecc01dSJohannes Berg queues |= BIT(sdata->vif.cab_queue); 55339ecc01dSJohannes Berg } else { 55439ecc01dSJohannes Berg /* all queues */ 55539ecc01dSJohannes Berg queues = BIT(local->hw.queues) - 1; 55639ecc01dSJohannes Berg } 55739ecc01dSJohannes Berg 558445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, 559445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_FLUSH); 560445ea4e8SJohannes Berg 56139ecc01dSJohannes Berg drv_flush(local, queues, false); 562445ea4e8SJohannes Berg 563445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, 564445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_FLUSH); 56539ecc01dSJohannes Berg } 56639ecc01dSJohannes Berg 567c7c71066SJohannes Berg static void __iterate_active_interfaces(struct ieee80211_local *local, 568c7c71066SJohannes Berg u32 iter_flags, 569dabeb344SJohannes Berg void (*iterator)(void *data, u8 *mac, 57032bfd35dSJohannes Berg struct ieee80211_vif *vif), 571dabeb344SJohannes Berg void *data) 572dabeb344SJohannes Berg { 573dabeb344SJohannes Berg struct ieee80211_sub_if_data *sdata; 574dabeb344SJohannes Berg 575e38bad47SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 57651fb61e7SJohannes Berg switch (sdata->vif.type) { 57705c914feSJohannes Berg case NL80211_IFTYPE_MONITOR: 57831eba5bcSFelix Fietkau if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) 57931eba5bcSFelix Fietkau continue; 58031eba5bcSFelix Fietkau break; 58105c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 582dabeb344SJohannes Berg continue; 5832ca27bcfSJohannes Berg default: 584dabeb344SJohannes Berg break; 585dabeb344SJohannes Berg } 5868b2c9824SJohannes Berg if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 5878b2c9824SJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 5888b2c9824SJohannes Berg continue; 5899607e6b6SJohannes Berg if (ieee80211_sdata_running(sdata)) 59047846c9bSJohannes Berg iterator(data, sdata->vif.addr, 59132bfd35dSJohannes Berg &sdata->vif); 592dabeb344SJohannes Berg } 593e38bad47SJohannes Berg 594c7c71066SJohannes Berg sdata = rcu_dereference_check(local->monitor_sdata, 595c7c71066SJohannes Berg lockdep_is_held(&local->iflist_mtx) || 596c7c71066SJohannes Berg lockdep_rtnl_is_held()); 5978b2c9824SJohannes Berg if (sdata && 5988b2c9824SJohannes Berg (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || 5998b2c9824SJohannes Berg sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 600685fb72bSJohannes Berg iterator(data, sdata->vif.addr, &sdata->vif); 601c7c71066SJohannes Berg } 602685fb72bSJohannes Berg 603c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces( 604c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 605c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 606c7c71066SJohannes Berg struct ieee80211_vif *vif), 607c7c71066SJohannes Berg void *data) 608c7c71066SJohannes Berg { 609c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 610c7c71066SJohannes Berg 611c7c71066SJohannes Berg mutex_lock(&local->iflist_mtx); 612c7c71066SJohannes Berg __iterate_active_interfaces(local, iter_flags, iterator, data); 613c7c71066SJohannes Berg mutex_unlock(&local->iflist_mtx); 614c7c71066SJohannes Berg } 615c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); 616c7c71066SJohannes Berg 617c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic( 618c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 619c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 620c7c71066SJohannes Berg struct ieee80211_vif *vif), 621c7c71066SJohannes Berg void *data) 622c7c71066SJohannes Berg { 623c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 624c7c71066SJohannes Berg 625c7c71066SJohannes Berg rcu_read_lock(); 626c7c71066SJohannes Berg __iterate_active_interfaces(local, iter_flags, iterator, data); 627e38bad47SJohannes Berg rcu_read_unlock(); 628dabeb344SJohannes Berg } 6292f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 63037ffc8daSJohannes Berg 631c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_rtnl( 632c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 633c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac, 634c7c71066SJohannes Berg struct ieee80211_vif *vif), 635c7c71066SJohannes Berg void *data) 636c7c71066SJohannes Berg { 637c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 638c7c71066SJohannes Berg 639c7c71066SJohannes Berg ASSERT_RTNL(); 640c7c71066SJohannes Berg 641c7c71066SJohannes Berg __iterate_active_interfaces(local, iter_flags, iterator, data); 642c7c71066SJohannes Berg } 643c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); 644c7c71066SJohannes Berg 645ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) 646ad7e718cSJohannes Berg { 647ad7e718cSJohannes Berg struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 648ad7e718cSJohannes Berg 649ad7e718cSJohannes Berg if (!ieee80211_sdata_running(sdata) || 650ad7e718cSJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 651ad7e718cSJohannes Berg return NULL; 652ad7e718cSJohannes Berg return &sdata->vif; 653ad7e718cSJohannes Berg } 654ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); 655ad7e718cSJohannes Berg 65642935ecaSLuis R. Rodriguez /* 65742935ecaSLuis R. Rodriguez * Nothing should have been stuffed into the workqueue during 65842935ecaSLuis R. Rodriguez * the suspend->resume cycle. If this WARN is seen then there 65942935ecaSLuis R. Rodriguez * is a bug with either the driver suspend or something in 66042935ecaSLuis R. Rodriguez * mac80211 stuffing into the workqueue which we haven't yet 66142935ecaSLuis R. Rodriguez * cleared during mac80211's suspend cycle. 66242935ecaSLuis R. Rodriguez */ 66342935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local) 66442935ecaSLuis R. Rodriguez { 665ceb99fe0SJohannes Berg if (WARN(local->suspended && !local->resuming, 666ceb99fe0SJohannes Berg "queueing ieee80211 work while going to suspend\n")) 66742935ecaSLuis R. Rodriguez return false; 66842935ecaSLuis R. Rodriguez 66942935ecaSLuis R. Rodriguez return true; 67042935ecaSLuis R. Rodriguez } 67142935ecaSLuis R. Rodriguez 67242935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) 67342935ecaSLuis R. Rodriguez { 67442935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 67542935ecaSLuis R. Rodriguez 67642935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 67742935ecaSLuis R. Rodriguez return; 67842935ecaSLuis R. Rodriguez 67942935ecaSLuis R. Rodriguez queue_work(local->workqueue, work); 68042935ecaSLuis R. Rodriguez } 68142935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work); 68242935ecaSLuis R. Rodriguez 68342935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, 68442935ecaSLuis R. Rodriguez struct delayed_work *dwork, 68542935ecaSLuis R. Rodriguez unsigned long delay) 68642935ecaSLuis R. Rodriguez { 68742935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 68842935ecaSLuis R. Rodriguez 68942935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 69042935ecaSLuis R. Rodriguez return; 69142935ecaSLuis R. Rodriguez 69242935ecaSLuis R. Rodriguez queue_delayed_work(local->workqueue, dwork, delay); 69342935ecaSLuis R. Rodriguez } 69442935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work); 69542935ecaSLuis R. Rodriguez 69635d865afSJohannes Berg u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, 697dd76986bSJohannes Berg struct ieee802_11_elems *elems, 698dd76986bSJohannes Berg u64 filter, u32 crc) 699dd76986bSJohannes Berg { 700dd76986bSJohannes Berg size_t left = len; 70135d865afSJohannes Berg const u8 *pos = start; 702dd76986bSJohannes Berg bool calc_crc = filter != 0; 703fcff4f10SPaul Stewart DECLARE_BITMAP(seen_elems, 256); 704b2e506bfSJohannes Berg const u8 *ie; 705dd76986bSJohannes Berg 706fcff4f10SPaul Stewart bitmap_zero(seen_elems, 256); 707dd76986bSJohannes Berg memset(elems, 0, sizeof(*elems)); 708dd76986bSJohannes Berg elems->ie_start = start; 709dd76986bSJohannes Berg elems->total_len = len; 710dd76986bSJohannes Berg 711dd76986bSJohannes Berg while (left >= 2) { 712dd76986bSJohannes Berg u8 id, elen; 713fcff4f10SPaul Stewart bool elem_parse_failed; 714dd76986bSJohannes Berg 715dd76986bSJohannes Berg id = *pos++; 716dd76986bSJohannes Berg elen = *pos++; 717dd76986bSJohannes Berg left -= 2; 718dd76986bSJohannes Berg 719fcff4f10SPaul Stewart if (elen > left) { 720fcff4f10SPaul Stewart elems->parse_error = true; 721dd76986bSJohannes Berg break; 722fcff4f10SPaul Stewart } 723fcff4f10SPaul Stewart 7249690fb16SJohannes Berg switch (id) { 7259690fb16SJohannes Berg case WLAN_EID_SSID: 7269690fb16SJohannes Berg case WLAN_EID_SUPP_RATES: 7279690fb16SJohannes Berg case WLAN_EID_FH_PARAMS: 7289690fb16SJohannes Berg case WLAN_EID_DS_PARAMS: 7299690fb16SJohannes Berg case WLAN_EID_CF_PARAMS: 7309690fb16SJohannes Berg case WLAN_EID_TIM: 7319690fb16SJohannes Berg case WLAN_EID_IBSS_PARAMS: 7329690fb16SJohannes Berg case WLAN_EID_CHALLENGE: 7339690fb16SJohannes Berg case WLAN_EID_RSN: 7349690fb16SJohannes Berg case WLAN_EID_ERP_INFO: 7359690fb16SJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 7369690fb16SJohannes Berg case WLAN_EID_HT_CAPABILITY: 7379690fb16SJohannes Berg case WLAN_EID_HT_OPERATION: 7389690fb16SJohannes Berg case WLAN_EID_VHT_CAPABILITY: 7399690fb16SJohannes Berg case WLAN_EID_VHT_OPERATION: 7409690fb16SJohannes Berg case WLAN_EID_MESH_ID: 7419690fb16SJohannes Berg case WLAN_EID_MESH_CONFIG: 7429690fb16SJohannes Berg case WLAN_EID_PEER_MGMT: 7439690fb16SJohannes Berg case WLAN_EID_PREQ: 7449690fb16SJohannes Berg case WLAN_EID_PREP: 7459690fb16SJohannes Berg case WLAN_EID_PERR: 7469690fb16SJohannes Berg case WLAN_EID_RANN: 7479690fb16SJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 7489690fb16SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 7499690fb16SJohannes Berg case WLAN_EID_COUNTRY: 7509690fb16SJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 7519690fb16SJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 75285220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 753b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 7548f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM: 755b2e506bfSJohannes Berg /* 756b2e506bfSJohannes Berg * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 757b2e506bfSJohannes Berg * that if the content gets bigger it might be needed more than once 758b2e506bfSJohannes Berg */ 7599690fb16SJohannes Berg if (test_bit(id, seen_elems)) { 760fcff4f10SPaul Stewart elems->parse_error = true; 761fcff4f10SPaul Stewart left -= elen; 762fcff4f10SPaul Stewart pos += elen; 763fcff4f10SPaul Stewart continue; 764fcff4f10SPaul Stewart } 7659690fb16SJohannes Berg break; 7669690fb16SJohannes Berg } 767dd76986bSJohannes Berg 768dd76986bSJohannes Berg if (calc_crc && id < 64 && (filter & (1ULL << id))) 769dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 770dd76986bSJohannes Berg 771fcff4f10SPaul Stewart elem_parse_failed = false; 772fcff4f10SPaul Stewart 773dd76986bSJohannes Berg switch (id) { 774dd76986bSJohannes Berg case WLAN_EID_SSID: 775dd76986bSJohannes Berg elems->ssid = pos; 776dd76986bSJohannes Berg elems->ssid_len = elen; 777dd76986bSJohannes Berg break; 778dd76986bSJohannes Berg case WLAN_EID_SUPP_RATES: 779dd76986bSJohannes Berg elems->supp_rates = pos; 780dd76986bSJohannes Berg elems->supp_rates_len = elen; 781dd76986bSJohannes Berg break; 782dd76986bSJohannes Berg case WLAN_EID_DS_PARAMS: 7831cd8e88eSJohannes Berg if (elen >= 1) 784dd76986bSJohannes Berg elems->ds_params = pos; 7851cd8e88eSJohannes Berg else 7861cd8e88eSJohannes Berg elem_parse_failed = true; 787dd76986bSJohannes Berg break; 788dd76986bSJohannes Berg case WLAN_EID_TIM: 789dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_tim_ie)) { 790dd76986bSJohannes Berg elems->tim = (void *)pos; 791dd76986bSJohannes Berg elems->tim_len = elen; 792fcff4f10SPaul Stewart } else 793fcff4f10SPaul Stewart elem_parse_failed = true; 794dd76986bSJohannes Berg break; 795dd76986bSJohannes Berg case WLAN_EID_CHALLENGE: 796dd76986bSJohannes Berg elems->challenge = pos; 797dd76986bSJohannes Berg elems->challenge_len = elen; 798dd76986bSJohannes Berg break; 799dd76986bSJohannes Berg case WLAN_EID_VENDOR_SPECIFIC: 800dd76986bSJohannes Berg if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && 801dd76986bSJohannes Berg pos[2] == 0xf2) { 802dd76986bSJohannes Berg /* Microsoft OUI (00:50:F2) */ 803dd76986bSJohannes Berg 804dd76986bSJohannes Berg if (calc_crc) 805dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 806dd76986bSJohannes Berg 807441a33baSJohannes Berg if (elen >= 5 && pos[3] == 2) { 808dd76986bSJohannes Berg /* OUI Type 2 - WMM IE */ 809dd76986bSJohannes Berg if (pos[4] == 0) { 810dd76986bSJohannes Berg elems->wmm_info = pos; 811dd76986bSJohannes Berg elems->wmm_info_len = elen; 812dd76986bSJohannes Berg } else if (pos[4] == 1) { 813dd76986bSJohannes Berg elems->wmm_param = pos; 814dd76986bSJohannes Berg elems->wmm_param_len = elen; 815dd76986bSJohannes Berg } 816dd76986bSJohannes Berg } 817dd76986bSJohannes Berg } 818dd76986bSJohannes Berg break; 819dd76986bSJohannes Berg case WLAN_EID_RSN: 820dd76986bSJohannes Berg elems->rsn = pos; 821dd76986bSJohannes Berg elems->rsn_len = elen; 822dd76986bSJohannes Berg break; 823dd76986bSJohannes Berg case WLAN_EID_ERP_INFO: 8241946bed9SJohannes Berg if (elen >= 1) 825dd76986bSJohannes Berg elems->erp_info = pos; 8261946bed9SJohannes Berg else 8271946bed9SJohannes Berg elem_parse_failed = true; 828dd76986bSJohannes Berg break; 829dd76986bSJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 830dd76986bSJohannes Berg elems->ext_supp_rates = pos; 831dd76986bSJohannes Berg elems->ext_supp_rates_len = elen; 832dd76986bSJohannes Berg break; 833dd76986bSJohannes Berg case WLAN_EID_HT_CAPABILITY: 834dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_ht_cap)) 835dd76986bSJohannes Berg elems->ht_cap_elem = (void *)pos; 836fcff4f10SPaul Stewart else 837fcff4f10SPaul Stewart elem_parse_failed = true; 838dd76986bSJohannes Berg break; 839074d46d1SJohannes Berg case WLAN_EID_HT_OPERATION: 840074d46d1SJohannes Berg if (elen >= sizeof(struct ieee80211_ht_operation)) 841074d46d1SJohannes Berg elems->ht_operation = (void *)pos; 842fcff4f10SPaul Stewart else 843fcff4f10SPaul Stewart elem_parse_failed = true; 844dd76986bSJohannes Berg break; 845818255eaSMahesh Palivela case WLAN_EID_VHT_CAPABILITY: 846818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_cap)) 847818255eaSMahesh Palivela elems->vht_cap_elem = (void *)pos; 848818255eaSMahesh Palivela else 849818255eaSMahesh Palivela elem_parse_failed = true; 850818255eaSMahesh Palivela break; 851818255eaSMahesh Palivela case WLAN_EID_VHT_OPERATION: 852818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_operation)) 853818255eaSMahesh Palivela elems->vht_operation = (void *)pos; 854818255eaSMahesh Palivela else 855818255eaSMahesh Palivela elem_parse_failed = true; 856818255eaSMahesh Palivela break; 857bee7f586SJohannes Berg case WLAN_EID_OPMODE_NOTIF: 858bee7f586SJohannes Berg if (elen > 0) 859bee7f586SJohannes Berg elems->opmode_notif = pos; 860bee7f586SJohannes Berg else 861bee7f586SJohannes Berg elem_parse_failed = true; 862bee7f586SJohannes Berg break; 863dd76986bSJohannes Berg case WLAN_EID_MESH_ID: 864dd76986bSJohannes Berg elems->mesh_id = pos; 865dd76986bSJohannes Berg elems->mesh_id_len = elen; 866dd76986bSJohannes Berg break; 867dd76986bSJohannes Berg case WLAN_EID_MESH_CONFIG: 868dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_meshconf_ie)) 869dd76986bSJohannes Berg elems->mesh_config = (void *)pos; 870fcff4f10SPaul Stewart else 871fcff4f10SPaul Stewart elem_parse_failed = true; 872dd76986bSJohannes Berg break; 873dd76986bSJohannes Berg case WLAN_EID_PEER_MGMT: 874dd76986bSJohannes Berg elems->peering = pos; 875dd76986bSJohannes Berg elems->peering_len = elen; 876dd76986bSJohannes Berg break; 8773f52b7e3SMarco Porsch case WLAN_EID_MESH_AWAKE_WINDOW: 8783f52b7e3SMarco Porsch if (elen >= 2) 8793f52b7e3SMarco Porsch elems->awake_window = (void *)pos; 8803f52b7e3SMarco Porsch break; 881dd76986bSJohannes Berg case WLAN_EID_PREQ: 882dd76986bSJohannes Berg elems->preq = pos; 883dd76986bSJohannes Berg elems->preq_len = elen; 884dd76986bSJohannes Berg break; 885dd76986bSJohannes Berg case WLAN_EID_PREP: 886dd76986bSJohannes Berg elems->prep = pos; 887dd76986bSJohannes Berg elems->prep_len = elen; 888dd76986bSJohannes Berg break; 889dd76986bSJohannes Berg case WLAN_EID_PERR: 890dd76986bSJohannes Berg elems->perr = pos; 891dd76986bSJohannes Berg elems->perr_len = elen; 892dd76986bSJohannes Berg break; 893dd76986bSJohannes Berg case WLAN_EID_RANN: 894dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_rann_ie)) 895dd76986bSJohannes Berg elems->rann = (void *)pos; 896fcff4f10SPaul Stewart else 897fcff4f10SPaul Stewart elem_parse_failed = true; 898dd76986bSJohannes Berg break; 899dd76986bSJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 9005bc1420bSJohannes Berg if (elen != sizeof(struct ieee80211_channel_sw_ie)) { 9015bc1420bSJohannes Berg elem_parse_failed = true; 9025bc1420bSJohannes Berg break; 9035bc1420bSJohannes Berg } 9045bc1420bSJohannes Berg elems->ch_switch_ie = (void *)pos; 905dd76986bSJohannes Berg break; 906b4f286a1SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 907b4f286a1SJohannes Berg if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { 908b4f286a1SJohannes Berg elem_parse_failed = true; 909b4f286a1SJohannes Berg break; 910b4f286a1SJohannes Berg } 911b4f286a1SJohannes Berg elems->ext_chansw_ie = (void *)pos; 912b4f286a1SJohannes Berg break; 91385220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 91485220d71SJohannes Berg if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { 91585220d71SJohannes Berg elem_parse_failed = true; 91685220d71SJohannes Berg break; 91785220d71SJohannes Berg } 91885220d71SJohannes Berg elems->sec_chan_offs = (void *)pos; 91985220d71SJohannes Berg break; 9208f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM: 9218f2535b9SChun-Yeow Yeoh if (elen != 9228f2535b9SChun-Yeow Yeoh sizeof(*elems->mesh_chansw_params_ie)) { 9238f2535b9SChun-Yeow Yeoh elem_parse_failed = true; 9248f2535b9SChun-Yeow Yeoh break; 9258f2535b9SChun-Yeow Yeoh } 9268f2535b9SChun-Yeow Yeoh elems->mesh_chansw_params_ie = (void *)pos; 9278f2535b9SChun-Yeow Yeoh break; 928b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 929b2e506bfSJohannes Berg if (!action || 930b2e506bfSJohannes Berg elen != sizeof(*elems->wide_bw_chansw_ie)) { 931b2e506bfSJohannes Berg elem_parse_failed = true; 932b2e506bfSJohannes Berg break; 933b2e506bfSJohannes Berg } 934b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = (void *)pos; 935b2e506bfSJohannes Berg break; 936b2e506bfSJohannes Berg case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 937b2e506bfSJohannes Berg if (action) { 938b2e506bfSJohannes Berg elem_parse_failed = true; 939b2e506bfSJohannes Berg break; 940b2e506bfSJohannes Berg } 941b2e506bfSJohannes Berg /* 942b2e506bfSJohannes Berg * This is a bit tricky, but as we only care about 943b2e506bfSJohannes Berg * the wide bandwidth channel switch element, so 944b2e506bfSJohannes Berg * just parse it out manually. 945b2e506bfSJohannes Berg */ 946b2e506bfSJohannes Berg ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 947b2e506bfSJohannes Berg pos, elen); 948b2e506bfSJohannes Berg if (ie) { 949b2e506bfSJohannes Berg if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) 950b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = 951b2e506bfSJohannes Berg (void *)(ie + 2); 952b2e506bfSJohannes Berg else 953b2e506bfSJohannes Berg elem_parse_failed = true; 954b2e506bfSJohannes Berg } 955b2e506bfSJohannes Berg break; 956dd76986bSJohannes Berg case WLAN_EID_COUNTRY: 957dd76986bSJohannes Berg elems->country_elem = pos; 958dd76986bSJohannes Berg elems->country_elem_len = elen; 959dd76986bSJohannes Berg break; 960dd76986bSJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 961761a48d2SJohannes Berg if (elen != 1) { 962761a48d2SJohannes Berg elem_parse_failed = true; 963761a48d2SJohannes Berg break; 964761a48d2SJohannes Berg } 965dd76986bSJohannes Berg elems->pwr_constr_elem = pos; 966dd76986bSJohannes Berg break; 967dd76986bSJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 96879ba1d89SJohannes Berg if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) 96979ba1d89SJohannes Berg elems->timeout_int = (void *)pos; 97079ba1d89SJohannes Berg else 97179ba1d89SJohannes Berg elem_parse_failed = true; 972dd76986bSJohannes Berg break; 973dd76986bSJohannes Berg default: 974dd76986bSJohannes Berg break; 975dd76986bSJohannes Berg } 976dd76986bSJohannes Berg 977fcff4f10SPaul Stewart if (elem_parse_failed) 978fcff4f10SPaul Stewart elems->parse_error = true; 979fcff4f10SPaul Stewart else 9805df45690SJohannes Berg __set_bit(id, seen_elems); 981fcff4f10SPaul Stewart 982dd76986bSJohannes Berg left -= elen; 983dd76986bSJohannes Berg pos += elen; 984dd76986bSJohannes Berg } 985dd76986bSJohannes Berg 986fcff4f10SPaul Stewart if (left != 0) 987fcff4f10SPaul Stewart elems->parse_error = true; 988fcff4f10SPaul Stewart 989dd76986bSJohannes Berg return crc; 990dd76986bSJohannes Berg } 991dd76986bSJohannes Berg 9923abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, 9933abead59SJohannes Berg bool bss_notify) 9945825fe10SJohannes Berg { 9955825fe10SJohannes Berg struct ieee80211_local *local = sdata->local; 9965825fe10SJohannes Berg struct ieee80211_tx_queue_params qparam; 99755de908aSJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 99854bcbc69SJohannes Berg int ac; 999a8ce8544SStanislaw Gruszka bool use_11b, enable_qos; 1000aa837e1dSJohannes Berg int aCWmin, aCWmax; 10015825fe10SJohannes Berg 10025825fe10SJohannes Berg if (!local->ops->conf_tx) 10035825fe10SJohannes Berg return; 10045825fe10SJohannes Berg 100554bcbc69SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 100654bcbc69SJohannes Berg return; 100754bcbc69SJohannes Berg 10085825fe10SJohannes Berg memset(&qparam, 0, sizeof(qparam)); 10095825fe10SJohannes Berg 101055de908aSJohannes Berg rcu_read_lock(); 101155de908aSJohannes Berg chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 101255de908aSJohannes Berg use_11b = (chanctx_conf && 10134bf88530SJohannes Berg chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) && 1014aa837e1dSJohannes Berg !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); 101555de908aSJohannes Berg rcu_read_unlock(); 10165825fe10SJohannes Berg 1017a8ce8544SStanislaw Gruszka /* 1018a8ce8544SStanislaw Gruszka * By default disable QoS in STA mode for old access points, which do 1019a8ce8544SStanislaw Gruszka * not support 802.11e. New APs will provide proper queue parameters, 1020a8ce8544SStanislaw Gruszka * that we will configure later. 1021a8ce8544SStanislaw Gruszka */ 1022a8ce8544SStanislaw Gruszka enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); 1023a8ce8544SStanislaw Gruszka 1024aa837e1dSJohannes Berg /* Set defaults according to 802.11-2007 Table 7-37 */ 1025aa837e1dSJohannes Berg aCWmax = 1023; 1026aa837e1dSJohannes Berg if (use_11b) 1027aa837e1dSJohannes Berg aCWmin = 31; 10285825fe10SJohannes Berg else 1029aa837e1dSJohannes Berg aCWmin = 15; 10305825fe10SJohannes Berg 10311f4ffde8SFred Zhou /* Confiure old 802.11b/g medium access rules. */ 10321f4ffde8SFred Zhou qparam.cw_max = aCWmax; 10331f4ffde8SFred Zhou qparam.cw_min = aCWmin; 10341f4ffde8SFred Zhou qparam.txop = 0; 10351f4ffde8SFred Zhou qparam.aifs = 2; 10361f4ffde8SFred Zhou 10371f4ffde8SFred Zhou for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 10381f4ffde8SFred Zhou /* Update if QoS is enabled. */ 1039a8ce8544SStanislaw Gruszka if (enable_qos) { 104054bcbc69SJohannes Berg switch (ac) { 10411d98fb12SJohannes Berg case IEEE80211_AC_BK: 10427ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 10437ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 10445825fe10SJohannes Berg qparam.txop = 0; 1045aa837e1dSJohannes Berg qparam.aifs = 7; 1046aa837e1dSJohannes Berg break; 1047a8ce8544SStanislaw Gruszka /* never happens but let's not leave undefined */ 1048a8ce8544SStanislaw Gruszka default: 10491d98fb12SJohannes Berg case IEEE80211_AC_BE: 10507ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 10517ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 1052aa837e1dSJohannes Berg qparam.txop = 0; 1053aa837e1dSJohannes Berg qparam.aifs = 3; 1054aa837e1dSJohannes Berg break; 10551d98fb12SJohannes Berg case IEEE80211_AC_VI: 1056aa837e1dSJohannes Berg qparam.cw_max = aCWmin; 1057aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 2 - 1; 1058aa837e1dSJohannes Berg if (use_11b) 1059aa837e1dSJohannes Berg qparam.txop = 6016/32; 1060aa837e1dSJohannes Berg else 1061aa837e1dSJohannes Berg qparam.txop = 3008/32; 1062aa837e1dSJohannes Berg qparam.aifs = 2; 1063aa837e1dSJohannes Berg break; 10641d98fb12SJohannes Berg case IEEE80211_AC_VO: 1065aa837e1dSJohannes Berg qparam.cw_max = (aCWmin + 1) / 2 - 1; 1066aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 4 - 1; 1067aa837e1dSJohannes Berg if (use_11b) 1068aa837e1dSJohannes Berg qparam.txop = 3264/32; 1069aa837e1dSJohannes Berg else 1070aa837e1dSJohannes Berg qparam.txop = 1504/32; 1071aa837e1dSJohannes Berg qparam.aifs = 2; 1072aa837e1dSJohannes Berg break; 1073aa837e1dSJohannes Berg } 1074a8ce8544SStanislaw Gruszka } 10755825fe10SJohannes Berg 1076ab13315aSKalle Valo qparam.uapsd = false; 1077ab13315aSKalle Valo 107854bcbc69SJohannes Berg sdata->tx_conf[ac] = qparam; 107954bcbc69SJohannes Berg drv_conf_tx(local, sdata, ac, &qparam); 1080aa837e1dSJohannes Berg } 1081e1b3ec1aSStanislaw Gruszka 1082f142c6b9SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_MONITOR && 1083f142c6b9SJohannes Berg sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { 1084a8ce8544SStanislaw Gruszka sdata->vif.bss_conf.qos = enable_qos; 10853abead59SJohannes Berg if (bss_notify) 10863abead59SJohannes Berg ieee80211_bss_info_change_notify(sdata, 10873abead59SJohannes Berg BSS_CHANGED_QOS); 10885825fe10SJohannes Berg } 1089d9734979SSujith } 1090e50db65cSJohannes Berg 109146900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 1092700e8ea6SJouni Malinen u16 transaction, u16 auth_alg, u16 status, 10934a3cb702SJohannes Berg const u8 *extra, size_t extra_len, const u8 *da, 10941672c0e3SJohannes Berg const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, 10951672c0e3SJohannes Berg u32 tx_flags) 109646900298SJohannes Berg { 109746900298SJohannes Berg struct ieee80211_local *local = sdata->local; 109846900298SJohannes Berg struct sk_buff *skb; 109946900298SJohannes Berg struct ieee80211_mgmt *mgmt; 1100fffd0934SJohannes Berg int err; 110146900298SJohannes Berg 110215e230abSFred Zhou /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ 110315e230abSFred Zhou skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len); 1104d15b8459SJoe Perches if (!skb) 110546900298SJohannes Berg return; 1106d15b8459SJoe Perches 110746900298SJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 110846900298SJohannes Berg 110946900298SJohannes Berg mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); 111046900298SJohannes Berg memset(mgmt, 0, 24 + 6); 111146900298SJohannes Berg mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 111246900298SJohannes Berg IEEE80211_STYPE_AUTH); 1113efa6a09dSAntonio Quartulli memcpy(mgmt->da, da, ETH_ALEN); 111447846c9bSJohannes Berg memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 111546900298SJohannes Berg memcpy(mgmt->bssid, bssid, ETH_ALEN); 111646900298SJohannes Berg mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); 111746900298SJohannes Berg mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); 1118700e8ea6SJouni Malinen mgmt->u.auth.status_code = cpu_to_le16(status); 111946900298SJohannes Berg if (extra) 112046900298SJohannes Berg memcpy(skb_put(skb, extra_len), extra, extra_len); 112146900298SJohannes Berg 1122fffd0934SJohannes Berg if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { 1123fffd0934SJohannes Berg mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 1124fffd0934SJohannes Berg err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); 1125fffd0934SJohannes Berg WARN_ON(err); 1126fffd0934SJohannes Berg } 1127fffd0934SJohannes Berg 11281672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 11291672c0e3SJohannes Berg tx_flags; 113062ae67beSJohannes Berg ieee80211_tx_skb(sdata, skb); 113146900298SJohannes Berg } 113246900298SJohannes Berg 11336ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, 11346ae16775SAntonio Quartulli const u8 *bssid, u16 stype, u16 reason, 11356ae16775SAntonio Quartulli bool send_frame, u8 *frame_buf) 11366ae16775SAntonio Quartulli { 11376ae16775SAntonio Quartulli struct ieee80211_local *local = sdata->local; 11386ae16775SAntonio Quartulli struct sk_buff *skb; 11396ae16775SAntonio Quartulli struct ieee80211_mgmt *mgmt = (void *)frame_buf; 11406ae16775SAntonio Quartulli 11416ae16775SAntonio Quartulli /* build frame */ 11426ae16775SAntonio Quartulli mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 11436ae16775SAntonio Quartulli mgmt->duration = 0; /* initialize only */ 11446ae16775SAntonio Quartulli mgmt->seq_ctrl = 0; /* initialize only */ 11456ae16775SAntonio Quartulli memcpy(mgmt->da, bssid, ETH_ALEN); 11466ae16775SAntonio Quartulli memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 11476ae16775SAntonio Quartulli memcpy(mgmt->bssid, bssid, ETH_ALEN); 11486ae16775SAntonio Quartulli /* u.deauth.reason_code == u.disassoc.reason_code */ 11496ae16775SAntonio Quartulli mgmt->u.deauth.reason_code = cpu_to_le16(reason); 11506ae16775SAntonio Quartulli 11516ae16775SAntonio Quartulli if (send_frame) { 11526ae16775SAntonio Quartulli skb = dev_alloc_skb(local->hw.extra_tx_headroom + 11536ae16775SAntonio Quartulli IEEE80211_DEAUTH_FRAME_LEN); 11546ae16775SAntonio Quartulli if (!skb) 11556ae16775SAntonio Quartulli return; 11566ae16775SAntonio Quartulli 11576ae16775SAntonio Quartulli skb_reserve(skb, local->hw.extra_tx_headroom); 11586ae16775SAntonio Quartulli 11596ae16775SAntonio Quartulli /* copy in frame */ 11606ae16775SAntonio Quartulli memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN), 11616ae16775SAntonio Quartulli mgmt, IEEE80211_DEAUTH_FRAME_LEN); 11626ae16775SAntonio Quartulli 11636ae16775SAntonio Quartulli if (sdata->vif.type != NL80211_IFTYPE_STATION || 11646ae16775SAntonio Quartulli !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) 11656ae16775SAntonio Quartulli IEEE80211_SKB_CB(skb)->flags |= 11666ae16775SAntonio Quartulli IEEE80211_TX_INTFL_DONT_ENCRYPT; 11676ae16775SAntonio Quartulli 11686ae16775SAntonio Quartulli ieee80211_tx_skb(sdata, skb); 11696ae16775SAntonio Quartulli } 11706ae16775SAntonio Quartulli } 11716ae16775SAntonio Quartulli 1172de95a54bSJohannes Berg int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, 1173c604b9f2SJohannes Berg size_t buffer_len, const u8 *ie, size_t ie_len, 1174651b5225SJouni Malinen enum ieee80211_band band, u32 rate_mask, 11752103dec1SSimon Wunderlich struct cfg80211_chan_def *chandef) 1176de95a54bSJohannes Berg { 1177de95a54bSJohannes Berg struct ieee80211_supported_band *sband; 1178c604b9f2SJohannes Berg u8 *pos = buffer, *end = buffer + buffer_len; 11798e664fb3SJohannes Berg size_t offset = 0, noffset; 11808e664fb3SJohannes Berg int supp_rates_len, i; 11818dcb2003SJouni Malinen u8 rates[32]; 11828dcb2003SJouni Malinen int num_rates; 11838dcb2003SJouni Malinen int ext_rates_len; 11842103dec1SSimon Wunderlich int shift; 11852103dec1SSimon Wunderlich u32 rate_flags; 1186de95a54bSJohannes Berg 11874d36ec58SJohannes Berg sband = local->hw.wiphy->bands[band]; 1188d811b3d5SArik Nemtsov if (WARN_ON_ONCE(!sband)) 1189d811b3d5SArik Nemtsov return 0; 1190de95a54bSJohannes Berg 11912103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(chandef); 11922103dec1SSimon Wunderlich shift = ieee80211_chandef_get_shift(chandef); 11932103dec1SSimon Wunderlich 11948dcb2003SJouni Malinen num_rates = 0; 11958dcb2003SJouni Malinen for (i = 0; i < sband->n_bitrates; i++) { 11968dcb2003SJouni Malinen if ((BIT(i) & rate_mask) == 0) 11978dcb2003SJouni Malinen continue; /* skip rate */ 11982103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 11992103dec1SSimon Wunderlich continue; 12002103dec1SSimon Wunderlich 12012103dec1SSimon Wunderlich rates[num_rates++] = 12022103dec1SSimon Wunderlich (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 12032103dec1SSimon Wunderlich (1 << shift) * 5); 12048dcb2003SJouni Malinen } 12058dcb2003SJouni Malinen 12068dcb2003SJouni Malinen supp_rates_len = min_t(int, num_rates, 8); 12078e664fb3SJohannes Berg 1208c604b9f2SJohannes Berg if (end - pos < 2 + supp_rates_len) 1209c604b9f2SJohannes Berg goto out_err; 1210de95a54bSJohannes Berg *pos++ = WLAN_EID_SUPP_RATES; 12118e664fb3SJohannes Berg *pos++ = supp_rates_len; 12128dcb2003SJouni Malinen memcpy(pos, rates, supp_rates_len); 12138dcb2003SJouni Malinen pos += supp_rates_len; 1214de95a54bSJohannes Berg 12158e664fb3SJohannes Berg /* insert "request information" if in custom IEs */ 12168e664fb3SJohannes Berg if (ie && ie_len) { 12178e664fb3SJohannes Berg static const u8 before_extrates[] = { 12188e664fb3SJohannes Berg WLAN_EID_SSID, 12198e664fb3SJohannes Berg WLAN_EID_SUPP_RATES, 12208e664fb3SJohannes Berg WLAN_EID_REQUEST, 12218e664fb3SJohannes Berg }; 12228e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 12238e664fb3SJohannes Berg before_extrates, 12248e664fb3SJohannes Berg ARRAY_SIZE(before_extrates), 12258e664fb3SJohannes Berg offset); 1226c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1227c604b9f2SJohannes Berg goto out_err; 12288e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12298e664fb3SJohannes Berg pos += noffset - offset; 12308e664fb3SJohannes Berg offset = noffset; 12318e664fb3SJohannes Berg } 12328e664fb3SJohannes Berg 12338dcb2003SJouni Malinen ext_rates_len = num_rates - supp_rates_len; 12348dcb2003SJouni Malinen if (ext_rates_len > 0) { 1235c604b9f2SJohannes Berg if (end - pos < 2 + ext_rates_len) 1236c604b9f2SJohannes Berg goto out_err; 1237de95a54bSJohannes Berg *pos++ = WLAN_EID_EXT_SUPP_RATES; 12388dcb2003SJouni Malinen *pos++ = ext_rates_len; 12398dcb2003SJouni Malinen memcpy(pos, rates + supp_rates_len, ext_rates_len); 12408dcb2003SJouni Malinen pos += ext_rates_len; 12418e664fb3SJohannes Berg } 12428e664fb3SJohannes Berg 12432103dec1SSimon Wunderlich if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) { 1244c604b9f2SJohannes Berg if (end - pos < 3) 1245c604b9f2SJohannes Berg goto out_err; 1246651b5225SJouni Malinen *pos++ = WLAN_EID_DS_PARAMS; 1247651b5225SJouni Malinen *pos++ = 1; 12482103dec1SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel( 12492103dec1SSimon Wunderlich chandef->chan->center_freq); 1250651b5225SJouni Malinen } 1251651b5225SJouni Malinen 12528e664fb3SJohannes Berg /* insert custom IEs that go before HT */ 12538e664fb3SJohannes Berg if (ie && ie_len) { 12548e664fb3SJohannes Berg static const u8 before_ht[] = { 12558e664fb3SJohannes Berg WLAN_EID_SSID, 12568e664fb3SJohannes Berg WLAN_EID_SUPP_RATES, 12578e664fb3SJohannes Berg WLAN_EID_REQUEST, 12588e664fb3SJohannes Berg WLAN_EID_EXT_SUPP_RATES, 12598e664fb3SJohannes Berg WLAN_EID_DS_PARAMS, 12608e664fb3SJohannes Berg WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 12618e664fb3SJohannes Berg }; 12628e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 12638e664fb3SJohannes Berg before_ht, ARRAY_SIZE(before_ht), 12648e664fb3SJohannes Berg offset); 1265c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1266c604b9f2SJohannes Berg goto out_err; 12678e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12688e664fb3SJohannes Berg pos += noffset - offset; 12698e664fb3SJohannes Berg offset = noffset; 1270de95a54bSJohannes Berg } 1271de95a54bSJohannes Berg 1272c604b9f2SJohannes Berg if (sband->ht_cap.ht_supported) { 1273c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) 1274c604b9f2SJohannes Berg goto out_err; 1275ef96a842SBen Greear pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, 1276ef96a842SBen Greear sband->ht_cap.cap); 1277c604b9f2SJohannes Berg } 12785ef2d41aSJohannes Berg 1279de95a54bSJohannes Berg /* 1280de95a54bSJohannes Berg * If adding more here, adjust code in main.c 1281de95a54bSJohannes Berg * that calculates local->scan_ies_len. 1282de95a54bSJohannes Berg */ 1283de95a54bSJohannes Berg 12848e664fb3SJohannes Berg /* add any remaining custom IEs */ 12858e664fb3SJohannes Berg if (ie && ie_len) { 12868e664fb3SJohannes Berg noffset = ie_len; 1287c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1288c604b9f2SJohannes Berg goto out_err; 12898e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12908e664fb3SJohannes Berg pos += noffset - offset; 1291de95a54bSJohannes Berg } 1292de95a54bSJohannes Berg 1293c604b9f2SJohannes Berg if (sband->vht_cap.vht_supported) { 1294c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) 1295c604b9f2SJohannes Berg goto out_err; 1296ba0afa2fSMahesh Palivela pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, 1297ba0afa2fSMahesh Palivela sband->vht_cap.cap); 1298c604b9f2SJohannes Berg } 1299ba0afa2fSMahesh Palivela 1300de95a54bSJohannes Berg return pos - buffer; 1301c604b9f2SJohannes Berg out_err: 1302c604b9f2SJohannes Berg WARN_ONCE(1, "not enough space for preq IEs\n"); 1303c604b9f2SJohannes Berg return pos - buffer; 1304de95a54bSJohannes Berg } 1305de95a54bSJohannes Berg 1306a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, 130785a237feSJohannes Berg u8 *dst, u32 ratemask, 13086b77863bSJohannes Berg struct ieee80211_channel *chan, 1309de95a54bSJohannes Berg const u8 *ssid, size_t ssid_len, 1310a806c558SPaul Stewart const u8 *ie, size_t ie_len, 1311a806c558SPaul Stewart bool directed) 131246900298SJohannes Berg { 131346900298SJohannes Berg struct ieee80211_local *local = sdata->local; 13142103dec1SSimon Wunderlich struct cfg80211_chan_def chandef; 131546900298SJohannes Berg struct sk_buff *skb; 131646900298SJohannes Berg struct ieee80211_mgmt *mgmt; 1317b9a9ada1SJohannes Berg int ies_len; 131846900298SJohannes Berg 1319a806c558SPaul Stewart /* 1320a806c558SPaul Stewart * Do not send DS Channel parameter for directed probe requests 1321a806c558SPaul Stewart * in order to maximize the chance that we get a response. Some 1322a806c558SPaul Stewart * badly-behaved APs don't respond when this parameter is included. 1323a806c558SPaul Stewart */ 13242103dec1SSimon Wunderlich chandef.width = sdata->vif.bss_conf.chandef.width; 1325a806c558SPaul Stewart if (directed) 13262103dec1SSimon Wunderlich chandef.chan = NULL; 1327a806c558SPaul Stewart else 13282103dec1SSimon Wunderlich chandef.chan = chan; 1329651b5225SJouni Malinen 13307c12ce8bSKalle Valo skb = ieee80211_probereq_get(&local->hw, &sdata->vif, 1331b9a9ada1SJohannes Berg ssid, ssid_len, 100 + ie_len); 13325b2bbf75SJohannes Berg if (!skb) 1333b9a9ada1SJohannes Berg return NULL; 1334b9a9ada1SJohannes Berg 1335b9a9ada1SJohannes Berg ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), 1336b9a9ada1SJohannes Berg skb_tailroom(skb), 1337c604b9f2SJohannes Berg ie, ie_len, chan->band, 13382103dec1SSimon Wunderlich ratemask, &chandef); 1339b9a9ada1SJohannes Berg skb_put(skb, ies_len); 13407c12ce8bSKalle Valo 134146900298SJohannes Berg if (dst) { 13427c12ce8bSKalle Valo mgmt = (struct ieee80211_mgmt *) skb->data; 134346900298SJohannes Berg memcpy(mgmt->da, dst, ETH_ALEN); 134446900298SJohannes Berg memcpy(mgmt->bssid, dst, ETH_ALEN); 134546900298SJohannes Berg } 134646900298SJohannes Berg 134762ae67beSJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 13485b2bbf75SJohannes Berg 1349a619a4c0SJuuso Oikarinen return skb; 1350a619a4c0SJuuso Oikarinen } 1351a619a4c0SJuuso Oikarinen 1352a619a4c0SJuuso Oikarinen void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, 1353a619a4c0SJuuso Oikarinen const u8 *ssid, size_t ssid_len, 1354a806c558SPaul Stewart const u8 *ie, size_t ie_len, 13551672c0e3SJohannes Berg u32 ratemask, bool directed, u32 tx_flags, 135655de908aSJohannes Berg struct ieee80211_channel *channel, bool scan) 1357a619a4c0SJuuso Oikarinen { 1358a619a4c0SJuuso Oikarinen struct sk_buff *skb; 1359a619a4c0SJuuso Oikarinen 1360fe94fe05SJohannes Berg skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, 13616b77863bSJohannes Berg ssid, ssid_len, 136285a237feSJohannes Berg ie, ie_len, directed); 1363aad14cebSRajkumar Manoharan if (skb) { 13641672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= tx_flags; 136555de908aSJohannes Berg if (scan) 136655de908aSJohannes Berg ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band); 136755de908aSJohannes Berg else 1368a619a4c0SJuuso Oikarinen ieee80211_tx_skb(sdata, skb); 136946900298SJohannes Berg } 1370aad14cebSRajkumar Manoharan } 137146900298SJohannes Berg 13722103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 137346900298SJohannes Berg struct ieee802_11_elems *elems, 13749ebb61a2SAshok Nagarajan enum ieee80211_band band, u32 *basic_rates) 137546900298SJohannes Berg { 137646900298SJohannes Berg struct ieee80211_supported_band *sband; 137746900298SJohannes Berg struct ieee80211_rate *bitrates; 137846900298SJohannes Berg size_t num_rates; 13792103dec1SSimon Wunderlich u32 supp_rates, rate_flags; 13802103dec1SSimon Wunderlich int i, j, shift; 13812103dec1SSimon Wunderlich sband = sdata->local->hw.wiphy->bands[band]; 13822103dec1SSimon Wunderlich 13832103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 13842103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 138546900298SJohannes Berg 13864ee73f33SMichal Kazior if (WARN_ON(!sband)) 13874ee73f33SMichal Kazior return 1; 138846900298SJohannes Berg 138946900298SJohannes Berg bitrates = sband->bitrates; 139046900298SJohannes Berg num_rates = sband->n_bitrates; 139146900298SJohannes Berg supp_rates = 0; 139246900298SJohannes Berg for (i = 0; i < elems->supp_rates_len + 139346900298SJohannes Berg elems->ext_supp_rates_len; i++) { 139446900298SJohannes Berg u8 rate = 0; 139546900298SJohannes Berg int own_rate; 13969ebb61a2SAshok Nagarajan bool is_basic; 139746900298SJohannes Berg if (i < elems->supp_rates_len) 139846900298SJohannes Berg rate = elems->supp_rates[i]; 139946900298SJohannes Berg else if (elems->ext_supp_rates) 140046900298SJohannes Berg rate = elems->ext_supp_rates 140146900298SJohannes Berg [i - elems->supp_rates_len]; 140246900298SJohannes Berg own_rate = 5 * (rate & 0x7f); 14039ebb61a2SAshok Nagarajan is_basic = !!(rate & 0x80); 14049ebb61a2SAshok Nagarajan 14059ebb61a2SAshok Nagarajan if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) 14069ebb61a2SAshok Nagarajan continue; 14079ebb61a2SAshok Nagarajan 14089ebb61a2SAshok Nagarajan for (j = 0; j < num_rates; j++) { 14092103dec1SSimon Wunderlich int brate; 14102103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[j].flags) 14112103dec1SSimon Wunderlich != rate_flags) 14122103dec1SSimon Wunderlich continue; 14132103dec1SSimon Wunderlich 14142103dec1SSimon Wunderlich brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 14152103dec1SSimon Wunderlich 1 << shift); 14162103dec1SSimon Wunderlich 14172103dec1SSimon Wunderlich if (brate == own_rate) { 141846900298SJohannes Berg supp_rates |= BIT(j); 14199ebb61a2SAshok Nagarajan if (basic_rates && is_basic) 14209ebb61a2SAshok Nagarajan *basic_rates |= BIT(j); 14219ebb61a2SAshok Nagarajan } 14229ebb61a2SAshok Nagarajan } 142346900298SJohannes Berg } 142446900298SJohannes Berg return supp_rates; 142546900298SJohannes Berg } 1426f2753ddbSJohannes Berg 142784f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local) 142884f6a01cSJohannes Berg { 142984f6a01cSJohannes Berg ieee80211_led_radio(local, false); 143067408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); 143184f6a01cSJohannes Berg 143284f6a01cSJohannes Berg cancel_work_sync(&local->reconfig_filter); 143384f6a01cSJohannes Berg 143484f6a01cSJohannes Berg flush_workqueue(local->workqueue); 1435678f415fSLennert Buytenhek drv_stop(local); 143684f6a01cSJohannes Berg } 143784f6a01cSJohannes Berg 1438153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local, 1439153a5fc4SStanislaw Gruszka struct ieee80211_sub_if_data *sdata) 1440153a5fc4SStanislaw Gruszka { 1441153a5fc4SStanislaw Gruszka struct ieee80211_chanctx_conf *conf; 1442153a5fc4SStanislaw Gruszka struct ieee80211_chanctx *ctx; 1443153a5fc4SStanislaw Gruszka 1444153a5fc4SStanislaw Gruszka if (!local->use_chanctx) 1445153a5fc4SStanislaw Gruszka return; 1446153a5fc4SStanislaw Gruszka 1447153a5fc4SStanislaw Gruszka mutex_lock(&local->chanctx_mtx); 1448153a5fc4SStanislaw Gruszka conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1449153a5fc4SStanislaw Gruszka lockdep_is_held(&local->chanctx_mtx)); 1450153a5fc4SStanislaw Gruszka if (conf) { 1451153a5fc4SStanislaw Gruszka ctx = container_of(conf, struct ieee80211_chanctx, conf); 1452153a5fc4SStanislaw Gruszka drv_assign_vif_chanctx(local, sdata, ctx); 1453153a5fc4SStanislaw Gruszka } 1454153a5fc4SStanislaw Gruszka mutex_unlock(&local->chanctx_mtx); 1455153a5fc4SStanislaw Gruszka } 1456153a5fc4SStanislaw Gruszka 1457f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local) 1458f2753ddbSJohannes Berg { 1459f2753ddbSJohannes Berg struct ieee80211_hw *hw = &local->hw; 1460f2753ddbSJohannes Berg struct ieee80211_sub_if_data *sdata; 146155de908aSJohannes Berg struct ieee80211_chanctx *ctx; 1462f2753ddbSJohannes Berg struct sta_info *sta; 14632683d65bSEliad Peller int res, i; 1464d888130aSJohannes Berg bool reconfig_due_to_wowlan = false; 1465d43c6b6eSDavid Spinadel struct ieee80211_sub_if_data *sched_scan_sdata; 1466d43c6b6eSDavid Spinadel bool sched_scan_stopped = false; 1467d888130aSJohannes Berg 14688f21b0adSJohannes Berg #ifdef CONFIG_PM 1469ceb99fe0SJohannes Berg if (local->suspended) 1470ceb99fe0SJohannes Berg local->resuming = true; 1471f2753ddbSJohannes Berg 1472eecc4800SJohannes Berg if (local->wowlan) { 1473eecc4800SJohannes Berg res = drv_resume(local); 147427b3eb9cSJohannes Berg local->wowlan = false; 1475eecc4800SJohannes Berg if (res < 0) { 1476eecc4800SJohannes Berg local->resuming = false; 1477eecc4800SJohannes Berg return res; 1478eecc4800SJohannes Berg } 1479eecc4800SJohannes Berg if (res == 0) 1480eecc4800SJohannes Berg goto wake_up; 1481eecc4800SJohannes Berg WARN_ON(res > 1); 1482eecc4800SJohannes Berg /* 1483eecc4800SJohannes Berg * res is 1, which means the driver requested 1484eecc4800SJohannes Berg * to go through a regular reset on wakeup. 1485eecc4800SJohannes Berg */ 1486d888130aSJohannes Berg reconfig_due_to_wowlan = true; 1487eecc4800SJohannes Berg } 1488eecc4800SJohannes Berg #endif 148994f9b97bSJohannes Berg /* everything else happens only if HW was up & running */ 149094f9b97bSJohannes Berg if (!local->open_count) 149194f9b97bSJohannes Berg goto wake_up; 149294f9b97bSJohannes Berg 149324feda00SLuis R. Rodriguez /* 149424feda00SLuis R. Rodriguez * Upon resume hardware can sometimes be goofy due to 149524feda00SLuis R. Rodriguez * various platform / driver / bus issues, so restarting 149624feda00SLuis R. Rodriguez * the device may at times not work immediately. Propagate 149724feda00SLuis R. Rodriguez * the error. 149824feda00SLuis R. Rodriguez */ 149924487981SJohannes Berg res = drv_start(local); 150024feda00SLuis R. Rodriguez if (res) { 1501c7a00dc7SJohn W. Linville WARN(local->suspended, "Hardware became unavailable " 1502c7a00dc7SJohn W. Linville "upon resume. This could be a software issue " 1503c7a00dc7SJohn W. Linville "prior to suspend or a hardware issue.\n"); 150424feda00SLuis R. Rodriguez return res; 150524feda00SLuis R. Rodriguez } 1506f2753ddbSJohannes Berg 15077f281975SYogesh Ashok Powar /* setup fragmentation threshold */ 15087f281975SYogesh Ashok Powar drv_set_frag_threshold(local, hw->wiphy->frag_threshold); 15097f281975SYogesh Ashok Powar 15107f281975SYogesh Ashok Powar /* setup RTS threshold */ 15117f281975SYogesh Ashok Powar drv_set_rts_threshold(local, hw->wiphy->rts_threshold); 15127f281975SYogesh Ashok Powar 15137f281975SYogesh Ashok Powar /* reset coverage class */ 15147f281975SYogesh Ashok Powar drv_set_coverage_class(local, hw->wiphy->coverage_class); 15157f281975SYogesh Ashok Powar 15161f87f7d3SJohannes Berg ieee80211_led_radio(local, true); 151767408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 151867408c8cSJohannes Berg IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); 1519f2753ddbSJohannes Berg 1520f2753ddbSJohannes Berg /* add interfaces */ 15214b6f1dd6SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 15224b6f1dd6SJohannes Berg if (sdata) { 15233c3e21e7SJohannes Berg /* in HW restart it exists already */ 15243c3e21e7SJohannes Berg WARN_ON(local->resuming); 15254b6f1dd6SJohannes Berg res = drv_add_interface(local, sdata); 15264b6f1dd6SJohannes Berg if (WARN_ON(res)) { 15274b6f1dd6SJohannes Berg rcu_assign_pointer(local->monitor_sdata, NULL); 15284b6f1dd6SJohannes Berg synchronize_net(); 15294b6f1dd6SJohannes Berg kfree(sdata); 15304b6f1dd6SJohannes Berg } 15314b6f1dd6SJohannes Berg } 15324b6f1dd6SJohannes Berg 1533f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1534f2753ddbSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 1535f2753ddbSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR && 15361ed32e4fSJohannes Berg ieee80211_sdata_running(sdata)) 15377b7eab6fSJohannes Berg res = drv_add_interface(local, sdata); 1538f2753ddbSJohannes Berg } 1539f2753ddbSJohannes Berg 154055de908aSJohannes Berg /* add channel contexts */ 1541f0dea9c7SArend van Spriel if (local->use_chanctx) { 154255de908aSJohannes Berg mutex_lock(&local->chanctx_mtx); 154355de908aSJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list) 154455de908aSJohannes Berg WARN_ON(drv_add_chanctx(local, ctx)); 154555de908aSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1546f0dea9c7SArend van Spriel } 154755de908aSJohannes Berg 15486352c87fSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 15496352c87fSJohannes Berg if (!ieee80211_sdata_running(sdata)) 15506352c87fSJohannes Berg continue; 1551153a5fc4SStanislaw Gruszka ieee80211_assign_chanctx(local, sdata); 15526352c87fSJohannes Berg } 15536352c87fSJohannes Berg 1554fe5f2559SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 1555153a5fc4SStanislaw Gruszka if (sdata && ieee80211_sdata_running(sdata)) 1556153a5fc4SStanislaw Gruszka ieee80211_assign_chanctx(local, sdata); 1557fe5f2559SJohannes Berg 1558f2753ddbSJohannes Berg /* add STAs back */ 155934e89507SJohannes Berg mutex_lock(&local->sta_mtx); 1560f2753ddbSJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 1561f09603a2SJohannes Berg enum ieee80211_sta_state state; 1562f09603a2SJohannes Berg 15632e8d397eSArik Nemtsov if (!sta->uploaded) 15642e8d397eSArik Nemtsov continue; 15652e8d397eSArik Nemtsov 15662e8d397eSArik Nemtsov /* AP-mode stations will be added later */ 15672e8d397eSArik Nemtsov if (sta->sdata->vif.type == NL80211_IFTYPE_AP) 15682e8d397eSArik Nemtsov continue; 15692e8d397eSArik Nemtsov 1570f09603a2SJohannes Berg for (state = IEEE80211_STA_NOTEXIST; 1571bd34ab62SMeenakshi Venkataraman state < sta->sta_state; state++) 15722e8d397eSArik Nemtsov WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 15732e8d397eSArik Nemtsov state + 1)); 1574f2753ddbSJohannes Berg } 157534e89507SJohannes Berg mutex_unlock(&local->sta_mtx); 1576f2753ddbSJohannes Berg 15772683d65bSEliad Peller /* reconfigure tx conf */ 157854bcbc69SJohannes Berg if (hw->queues >= IEEE80211_NUM_ACS) { 1579f6f3def3SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) { 1580f6f3def3SEliad Peller if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 1581f6f3def3SEliad Peller sdata->vif.type == NL80211_IFTYPE_MONITOR || 1582f6f3def3SEliad Peller !ieee80211_sdata_running(sdata)) 1583f6f3def3SEliad Peller continue; 1584f6f3def3SEliad Peller 158554bcbc69SJohannes Berg for (i = 0; i < IEEE80211_NUM_ACS; i++) 158654bcbc69SJohannes Berg drv_conf_tx(local, sdata, i, 158754bcbc69SJohannes Berg &sdata->tx_conf[i]); 158854bcbc69SJohannes Berg } 1589f6f3def3SEliad Peller } 15902683d65bSEliad Peller 1591f2753ddbSJohannes Berg /* reconfigure hardware */ 1592f2753ddbSJohannes Berg ieee80211_hw_config(local, ~0); 1593f2753ddbSJohannes Berg 1594f2753ddbSJohannes Berg ieee80211_configure_filter(local); 1595f2753ddbSJohannes Berg 1596f2753ddbSJohannes Berg /* Finally also reconfigure all the BSS information */ 1597f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1598ac8dd506SJohannes Berg u32 changed; 1599ac8dd506SJohannes Berg 16009607e6b6SJohannes Berg if (!ieee80211_sdata_running(sdata)) 1601f2753ddbSJohannes Berg continue; 1602ac8dd506SJohannes Berg 1603ac8dd506SJohannes Berg /* common change flags for all interface types */ 1604ac8dd506SJohannes Berg changed = BSS_CHANGED_ERP_CTS_PROT | 1605ac8dd506SJohannes Berg BSS_CHANGED_ERP_PREAMBLE | 1606ac8dd506SJohannes Berg BSS_CHANGED_ERP_SLOT | 1607ac8dd506SJohannes Berg BSS_CHANGED_HT | 1608ac8dd506SJohannes Berg BSS_CHANGED_BASIC_RATES | 1609ac8dd506SJohannes Berg BSS_CHANGED_BEACON_INT | 1610ac8dd506SJohannes Berg BSS_CHANGED_BSSID | 16114ced3f74SJohannes Berg BSS_CHANGED_CQM | 161255de47f6SEliad Peller BSS_CHANGED_QOS | 16131ea6f9c0SJohannes Berg BSS_CHANGED_IDLE | 16141ea6f9c0SJohannes Berg BSS_CHANGED_TXPOWER; 1615ac8dd506SJohannes Berg 1616f2753ddbSJohannes Berg switch (sdata->vif.type) { 1617f2753ddbSJohannes Berg case NL80211_IFTYPE_STATION: 16180d392e93SEliad Peller changed |= BSS_CHANGED_ASSOC | 1619ab095877SEliad Peller BSS_CHANGED_ARP_FILTER | 1620ab095877SEliad Peller BSS_CHANGED_PS; 1621c65dd147SEmmanuel Grumbach 1622989c6505SAlexander Bondar /* Re-send beacon info report to the driver */ 1623989c6505SAlexander Bondar if (sdata->u.mgd.have_beacon) 1624989c6505SAlexander Bondar changed |= BSS_CHANGED_BEACON_INFO; 1625c65dd147SEmmanuel Grumbach 16268d61ffa5SJohannes Berg sdata_lock(sdata); 1627ac8dd506SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 16288d61ffa5SJohannes Berg sdata_unlock(sdata); 1629ac8dd506SJohannes Berg break; 1630f2753ddbSJohannes Berg case NL80211_IFTYPE_ADHOC: 1631ac8dd506SJohannes Berg changed |= BSS_CHANGED_IBSS; 1632ac8dd506SJohannes Berg /* fall through */ 1633f2753ddbSJohannes Berg case NL80211_IFTYPE_AP: 1634339afbf4SJohannes Berg changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; 1635e7979ac7SArik Nemtsov 16361041638fSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP) { 1637e7979ac7SArik Nemtsov changed |= BSS_CHANGED_AP_PROBE_RESP; 1638e7979ac7SArik Nemtsov 16391041638fSJohannes Berg if (rcu_access_pointer(sdata->u.ap.beacon)) 16401041638fSJohannes Berg drv_start_ap(local, sdata); 16411041638fSJohannes Berg } 16421041638fSJohannes Berg 16437827493bSArik Nemtsov /* fall through */ 1644f2753ddbSJohannes Berg case NL80211_IFTYPE_MESH_POINT: 16458da34932SJohannes Berg if (sdata->vif.bss_conf.enable_beacon) { 1646ac8dd506SJohannes Berg changed |= BSS_CHANGED_BEACON | 1647ac8dd506SJohannes Berg BSS_CHANGED_BEACON_ENABLED; 16482d0ddec5SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 16498da34932SJohannes Berg } 1650f2753ddbSJohannes Berg break; 1651f2753ddbSJohannes Berg case NL80211_IFTYPE_WDS: 1652f2753ddbSJohannes Berg break; 1653f2753ddbSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 1654f2753ddbSJohannes Berg case NL80211_IFTYPE_MONITOR: 1655f2753ddbSJohannes Berg /* ignore virtual */ 1656f2753ddbSJohannes Berg break; 165798104fdeSJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 1658f142c6b9SJohannes Berg changed = BSS_CHANGED_IDLE; 1659f142c6b9SJohannes Berg break; 1660f2753ddbSJohannes Berg case NL80211_IFTYPE_UNSPECIFIED: 16612e161f78SJohannes Berg case NUM_NL80211_IFTYPES: 16622ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_CLIENT: 16632ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_GO: 1664f2753ddbSJohannes Berg WARN_ON(1); 1665f2753ddbSJohannes Berg break; 1666f2753ddbSJohannes Berg } 1667f2753ddbSJohannes Berg } 1668f2753ddbSJohannes Berg 16698e1b23b9SEyal Shapira ieee80211_recalc_ps(local, -1); 16708e1b23b9SEyal Shapira 16712a419056SJohannes Berg /* 16726e1b1b24SEliad Peller * The sta might be in psm against the ap (e.g. because 16736e1b1b24SEliad Peller * this was the state before a hw restart), so we 16746e1b1b24SEliad Peller * explicitly send a null packet in order to make sure 16756e1b1b24SEliad Peller * it'll sync against the ap (and get out of psm). 16766e1b1b24SEliad Peller */ 16776e1b1b24SEliad Peller if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { 16786e1b1b24SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) { 16796e1b1b24SEliad Peller if (sdata->vif.type != NL80211_IFTYPE_STATION) 16806e1b1b24SEliad Peller continue; 168120f544eeSJohannes Berg if (!sdata->u.mgd.associated) 168220f544eeSJohannes Berg continue; 16836e1b1b24SEliad Peller 16846e1b1b24SEliad Peller ieee80211_send_nullfunc(local, sdata, 0); 16856e1b1b24SEliad Peller } 16866e1b1b24SEliad Peller } 16876e1b1b24SEliad Peller 16882e8d397eSArik Nemtsov /* APs are now beaconing, add back stations */ 16892e8d397eSArik Nemtsov mutex_lock(&local->sta_mtx); 16902e8d397eSArik Nemtsov list_for_each_entry(sta, &local->sta_list, list) { 16912e8d397eSArik Nemtsov enum ieee80211_sta_state state; 16922e8d397eSArik Nemtsov 16932e8d397eSArik Nemtsov if (!sta->uploaded) 16942e8d397eSArik Nemtsov continue; 16952e8d397eSArik Nemtsov 16962e8d397eSArik Nemtsov if (sta->sdata->vif.type != NL80211_IFTYPE_AP) 16972e8d397eSArik Nemtsov continue; 16982e8d397eSArik Nemtsov 16992e8d397eSArik Nemtsov for (state = IEEE80211_STA_NOTEXIST; 17002e8d397eSArik Nemtsov state < sta->sta_state; state++) 17012e8d397eSArik Nemtsov WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 17022e8d397eSArik Nemtsov state + 1)); 17032e8d397eSArik Nemtsov } 17042e8d397eSArik Nemtsov mutex_unlock(&local->sta_mtx); 17052e8d397eSArik Nemtsov 17067b21aea0SEyal Shapira /* add back keys */ 17077b21aea0SEyal Shapira list_for_each_entry(sdata, &local->interfaces, list) 17087b21aea0SEyal Shapira if (ieee80211_sdata_running(sdata)) 17097b21aea0SEyal Shapira ieee80211_enable_keys(sdata); 17107b21aea0SEyal Shapira 1711c6209488SEliad Peller wake_up: 171204800adaSArik Nemtsov local->in_reconfig = false; 171304800adaSArik Nemtsov barrier(); 171404800adaSArik Nemtsov 17153c3e21e7SJohannes Berg if (local->monitors == local->open_count && local->monitors > 0) 17163c3e21e7SJohannes Berg ieee80211_add_virtual_monitor(local); 17173c3e21e7SJohannes Berg 17186e1b1b24SEliad Peller /* 17192a419056SJohannes Berg * Clear the WLAN_STA_BLOCK_BA flag so new aggregation 17202a419056SJohannes Berg * sessions can be established after a resume. 17212a419056SJohannes Berg * 17222a419056SJohannes Berg * Also tear down aggregation sessions since reconfiguring 17232a419056SJohannes Berg * them in a hardware restart scenario is not easily done 17242a419056SJohannes Berg * right now, and the hardware will have lost information 17252a419056SJohannes Berg * about the sessions, but we and the AP still think they 17262a419056SJohannes Berg * are active. This is really a workaround though. 17272a419056SJohannes Berg */ 172874e2bd1fSWey-Yi Guy if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { 17292a419056SJohannes Berg mutex_lock(&local->sta_mtx); 17302a419056SJohannes Berg 17312a419056SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 1732c82c4a80SJohannes Berg ieee80211_sta_tear_down_BA_sessions( 1733c82c4a80SJohannes Berg sta, AGG_STOP_LOCAL_REQUEST); 1734c2c98fdeSJohannes Berg clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 173574e2bd1fSWey-Yi Guy } 17362a419056SJohannes Berg 17372a419056SJohannes Berg mutex_unlock(&local->sta_mtx); 173874e2bd1fSWey-Yi Guy } 173974e2bd1fSWey-Yi Guy 1740445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 1741f2753ddbSJohannes Berg IEEE80211_QUEUE_STOP_REASON_SUSPEND); 1742f2753ddbSJohannes Berg 17435bb644a0SJohannes Berg /* 17445bb644a0SJohannes Berg * If this is for hw restart things are still running. 17455bb644a0SJohannes Berg * We may want to change that later, however. 17465bb644a0SJohannes Berg */ 17478f21b0adSJohannes Berg if (!local->suspended || reconfig_due_to_wowlan) 17489214ad7fSJohannes Berg drv_restart_complete(local); 17498f21b0adSJohannes Berg 17508f21b0adSJohannes Berg if (!local->suspended) 17515bb644a0SJohannes Berg return 0; 17525bb644a0SJohannes Berg 17535bb644a0SJohannes Berg #ifdef CONFIG_PM 1754ceb99fe0SJohannes Berg /* first set suspended false, then resuming */ 17555bb644a0SJohannes Berg local->suspended = false; 1756ceb99fe0SJohannes Berg mb(); 1757ceb99fe0SJohannes Berg local->resuming = false; 17585bb644a0SJohannes Berg 1759b8360ab8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1760b8360ab8SJohannes Berg if (!ieee80211_sdata_running(sdata)) 1761b8360ab8SJohannes Berg continue; 1762b8360ab8SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION) 1763b8360ab8SJohannes Berg ieee80211_sta_restart(sdata); 1764b8360ab8SJohannes Berg } 1765b8360ab8SJohannes Berg 176626d59535SJohannes Berg mod_timer(&local->sta_cleanup, jiffies + 1); 17675bb644a0SJohannes Berg #else 17685bb644a0SJohannes Berg WARN_ON(1); 17695bb644a0SJohannes Berg #endif 1770d43c6b6eSDavid Spinadel 1771d43c6b6eSDavid Spinadel /* 1772d43c6b6eSDavid Spinadel * Reconfigure sched scan if it was interrupted by FW restart or 1773d43c6b6eSDavid Spinadel * suspend. 1774d43c6b6eSDavid Spinadel */ 1775d43c6b6eSDavid Spinadel mutex_lock(&local->mtx); 1776d43c6b6eSDavid Spinadel sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, 1777d43c6b6eSDavid Spinadel lockdep_is_held(&local->mtx)); 1778d43c6b6eSDavid Spinadel if (sched_scan_sdata && local->sched_scan_req) 1779d43c6b6eSDavid Spinadel /* 1780d43c6b6eSDavid Spinadel * Sched scan stopped, but we don't want to report it. Instead, 1781d43c6b6eSDavid Spinadel * we're trying to reschedule. 1782d43c6b6eSDavid Spinadel */ 1783d43c6b6eSDavid Spinadel if (__ieee80211_request_sched_scan_start(sched_scan_sdata, 1784d43c6b6eSDavid Spinadel local->sched_scan_req)) 1785d43c6b6eSDavid Spinadel sched_scan_stopped = true; 1786d43c6b6eSDavid Spinadel mutex_unlock(&local->mtx); 1787d43c6b6eSDavid Spinadel 1788d43c6b6eSDavid Spinadel if (sched_scan_stopped) 1789d43c6b6eSDavid Spinadel cfg80211_sched_scan_stopped(local->hw.wiphy); 1790d43c6b6eSDavid Spinadel 1791f2753ddbSJohannes Berg return 0; 1792f2753ddbSJohannes Berg } 179342935ecaSLuis R. Rodriguez 179495acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif) 179595acac61SJohannes Berg { 179695acac61SJohannes Berg struct ieee80211_sub_if_data *sdata; 179795acac61SJohannes Berg struct ieee80211_local *local; 179895acac61SJohannes Berg struct ieee80211_key *key; 179995acac61SJohannes Berg 180095acac61SJohannes Berg if (WARN_ON(!vif)) 180195acac61SJohannes Berg return; 180295acac61SJohannes Berg 180395acac61SJohannes Berg sdata = vif_to_sdata(vif); 180495acac61SJohannes Berg local = sdata->local; 180595acac61SJohannes Berg 180695acac61SJohannes Berg if (WARN_ON(!local->resuming)) 180795acac61SJohannes Berg return; 180895acac61SJohannes Berg 180995acac61SJohannes Berg if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 181095acac61SJohannes Berg return; 181195acac61SJohannes Berg 181295acac61SJohannes Berg sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; 181395acac61SJohannes Berg 181495acac61SJohannes Berg mutex_lock(&local->key_mtx); 181595acac61SJohannes Berg list_for_each_entry(key, &sdata->key_list, list) 181695acac61SJohannes Berg key->flags |= KEY_FLAG_TAINTED; 181795acac61SJohannes Berg mutex_unlock(&local->key_mtx); 181895acac61SJohannes Berg } 181995acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 182095acac61SJohannes Berg 182104ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) 18220f78231bSJohannes Berg { 182304ecd257SJohannes Berg struct ieee80211_local *local = sdata->local; 182404ecd257SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 182504ecd257SJohannes Berg struct ieee80211_chanctx *chanctx; 18260f78231bSJohannes Berg 182704ecd257SJohannes Berg mutex_lock(&local->chanctx_mtx); 18280f78231bSJohannes Berg 182904ecd257SJohannes Berg chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 183004ecd257SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 18310f78231bSJohannes Berg 183204ecd257SJohannes Berg if (WARN_ON_ONCE(!chanctx_conf)) 18335d8e4237SJohannes Berg goto unlock; 18340f78231bSJohannes Berg 183504ecd257SJohannes Berg chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 183604ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, chanctx); 18375d8e4237SJohannes Berg unlock: 183804ecd257SJohannes Berg mutex_unlock(&local->chanctx_mtx); 18390f78231bSJohannes Berg } 18408e664fb3SJohannes Berg 184121f659bfSEliad Peller void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) 184221f659bfSEliad Peller { 184321f659bfSEliad Peller struct ieee80211_local *local = sdata->local; 184421f659bfSEliad Peller struct ieee80211_chanctx_conf *chanctx_conf; 184521f659bfSEliad Peller struct ieee80211_chanctx *chanctx; 184621f659bfSEliad Peller 184721f659bfSEliad Peller mutex_lock(&local->chanctx_mtx); 184821f659bfSEliad Peller 184921f659bfSEliad Peller chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 185021f659bfSEliad Peller lockdep_is_held(&local->chanctx_mtx)); 185121f659bfSEliad Peller 185221f659bfSEliad Peller if (WARN_ON_ONCE(!chanctx_conf)) 185321f659bfSEliad Peller goto unlock; 185421f659bfSEliad Peller 185521f659bfSEliad Peller chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 185621f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, chanctx); 185721f659bfSEliad Peller unlock: 185821f659bfSEliad Peller mutex_unlock(&local->chanctx_mtx); 185921f659bfSEliad Peller } 186021f659bfSEliad Peller 18618e664fb3SJohannes Berg static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) 18628e664fb3SJohannes Berg { 18638e664fb3SJohannes Berg int i; 18648e664fb3SJohannes Berg 18658e664fb3SJohannes Berg for (i = 0; i < n_ids; i++) 18668e664fb3SJohannes Berg if (ids[i] == id) 18678e664fb3SJohannes Berg return true; 18688e664fb3SJohannes Berg return false; 18698e664fb3SJohannes Berg } 18708e664fb3SJohannes Berg 18718e664fb3SJohannes Berg /** 18728e664fb3SJohannes Berg * ieee80211_ie_split - split an IE buffer according to ordering 18738e664fb3SJohannes Berg * 18748e664fb3SJohannes Berg * @ies: the IE buffer 18758e664fb3SJohannes Berg * @ielen: the length of the IE buffer 18768e664fb3SJohannes Berg * @ids: an array with element IDs that are allowed before 18778e664fb3SJohannes Berg * the split 18788e664fb3SJohannes Berg * @n_ids: the size of the element ID array 18798e664fb3SJohannes Berg * @offset: offset where to start splitting in the buffer 18808e664fb3SJohannes Berg * 18818e664fb3SJohannes Berg * This function splits an IE buffer by updating the @offset 18828e664fb3SJohannes Berg * variable to point to the location where the buffer should be 18838e664fb3SJohannes Berg * split. 18848e664fb3SJohannes Berg * 18858e664fb3SJohannes Berg * It assumes that the given IE buffer is well-formed, this 18868e664fb3SJohannes Berg * has to be guaranteed by the caller! 18878e664fb3SJohannes Berg * 18888e664fb3SJohannes Berg * It also assumes that the IEs in the buffer are ordered 18898e664fb3SJohannes Berg * correctly, if not the result of using this function will not 18908e664fb3SJohannes Berg * be ordered correctly either, i.e. it does no reordering. 18918e664fb3SJohannes Berg * 18928e664fb3SJohannes Berg * The function returns the offset where the next part of the 18938e664fb3SJohannes Berg * buffer starts, which may be @ielen if the entire (remainder) 18948e664fb3SJohannes Berg * of the buffer should be used. 18958e664fb3SJohannes Berg */ 18968e664fb3SJohannes Berg size_t ieee80211_ie_split(const u8 *ies, size_t ielen, 18978e664fb3SJohannes Berg const u8 *ids, int n_ids, size_t offset) 18988e664fb3SJohannes Berg { 18998e664fb3SJohannes Berg size_t pos = offset; 19008e664fb3SJohannes Berg 19018e664fb3SJohannes Berg while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) 19028e664fb3SJohannes Berg pos += 2 + ies[pos + 1]; 19038e664fb3SJohannes Berg 19048e664fb3SJohannes Berg return pos; 19058e664fb3SJohannes Berg } 19068e664fb3SJohannes Berg 19078e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) 19088e664fb3SJohannes Berg { 19098e664fb3SJohannes Berg size_t pos = offset; 19108e664fb3SJohannes Berg 19118e664fb3SJohannes Berg while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) 19128e664fb3SJohannes Berg pos += 2 + ies[pos + 1]; 19138e664fb3SJohannes Berg 19148e664fb3SJohannes Berg return pos; 19158e664fb3SJohannes Berg } 1916615f7b9bSMeenakshi Venkataraman 1917615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, 1918615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 1919615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 1920615f7b9bSMeenakshi Venkataraman { 1921615f7b9bSMeenakshi Venkataraman trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); 1922615f7b9bSMeenakshi Venkataraman 1923615f7b9bSMeenakshi Venkataraman if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 1924615f7b9bSMeenakshi Venkataraman return; 1925615f7b9bSMeenakshi Venkataraman 1926615f7b9bSMeenakshi Venkataraman /* 1927615f7b9bSMeenakshi Venkataraman * Scale up threshold values before storing it, as the RSSI averaging 1928615f7b9bSMeenakshi Venkataraman * algorithm uses a scaled up value as well. Change this scaling 1929615f7b9bSMeenakshi Venkataraman * factor if the RSSI averaging algorithm changes. 1930615f7b9bSMeenakshi Venkataraman */ 1931615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; 1932615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; 1933615f7b9bSMeenakshi Venkataraman } 1934615f7b9bSMeenakshi Venkataraman 1935615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, 1936615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 1937615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 1938615f7b9bSMeenakshi Venkataraman { 1939615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 1940615f7b9bSMeenakshi Venkataraman 1941615f7b9bSMeenakshi Venkataraman WARN_ON(rssi_min_thold == rssi_max_thold || 1942615f7b9bSMeenakshi Venkataraman rssi_min_thold > rssi_max_thold); 1943615f7b9bSMeenakshi Venkataraman 1944615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, 1945615f7b9bSMeenakshi Venkataraman rssi_max_thold); 1946615f7b9bSMeenakshi Venkataraman } 1947615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports); 1948615f7b9bSMeenakshi Venkataraman 1949615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) 1950615f7b9bSMeenakshi Venkataraman { 1951615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 1952615f7b9bSMeenakshi Venkataraman 1953615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, 0, 0); 1954615f7b9bSMeenakshi Venkataraman } 1955615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports); 1956768db343SArik Nemtsov 1957ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 195842e7aa77SAlexander Simon u16 cap) 195942e7aa77SAlexander Simon { 196042e7aa77SAlexander Simon __le16 tmp; 196142e7aa77SAlexander Simon 196242e7aa77SAlexander Simon *pos++ = WLAN_EID_HT_CAPABILITY; 196342e7aa77SAlexander Simon *pos++ = sizeof(struct ieee80211_ht_cap); 196442e7aa77SAlexander Simon memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 196542e7aa77SAlexander Simon 196642e7aa77SAlexander Simon /* capability flags */ 196742e7aa77SAlexander Simon tmp = cpu_to_le16(cap); 196842e7aa77SAlexander Simon memcpy(pos, &tmp, sizeof(u16)); 196942e7aa77SAlexander Simon pos += sizeof(u16); 197042e7aa77SAlexander Simon 197142e7aa77SAlexander Simon /* AMPDU parameters */ 1972ef96a842SBen Greear *pos++ = ht_cap->ampdu_factor | 1973ef96a842SBen Greear (ht_cap->ampdu_density << 197442e7aa77SAlexander Simon IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); 197542e7aa77SAlexander Simon 197642e7aa77SAlexander Simon /* MCS set */ 1977ef96a842SBen Greear memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); 1978ef96a842SBen Greear pos += sizeof(ht_cap->mcs); 197942e7aa77SAlexander Simon 198042e7aa77SAlexander Simon /* extended capabilities */ 198142e7aa77SAlexander Simon pos += sizeof(__le16); 198242e7aa77SAlexander Simon 198342e7aa77SAlexander Simon /* BF capabilities */ 198442e7aa77SAlexander Simon pos += sizeof(__le32); 198542e7aa77SAlexander Simon 198642e7aa77SAlexander Simon /* antenna selection */ 198742e7aa77SAlexander Simon pos += sizeof(u8); 198842e7aa77SAlexander Simon 198942e7aa77SAlexander Simon return pos; 199042e7aa77SAlexander Simon } 199142e7aa77SAlexander Simon 1992ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 1993ba0afa2fSMahesh Palivela u32 cap) 1994ba0afa2fSMahesh Palivela { 1995ba0afa2fSMahesh Palivela __le32 tmp; 1996ba0afa2fSMahesh Palivela 1997ba0afa2fSMahesh Palivela *pos++ = WLAN_EID_VHT_CAPABILITY; 1998d4950281SMahesh Palivela *pos++ = sizeof(struct ieee80211_vht_cap); 1999d4950281SMahesh Palivela memset(pos, 0, sizeof(struct ieee80211_vht_cap)); 2000ba0afa2fSMahesh Palivela 2001ba0afa2fSMahesh Palivela /* capability flags */ 2002ba0afa2fSMahesh Palivela tmp = cpu_to_le32(cap); 2003ba0afa2fSMahesh Palivela memcpy(pos, &tmp, sizeof(u32)); 2004ba0afa2fSMahesh Palivela pos += sizeof(u32); 2005ba0afa2fSMahesh Palivela 2006ba0afa2fSMahesh Palivela /* VHT MCS set */ 2007ba0afa2fSMahesh Palivela memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); 2008ba0afa2fSMahesh Palivela pos += sizeof(vht_cap->vht_mcs); 2009ba0afa2fSMahesh Palivela 2010ba0afa2fSMahesh Palivela return pos; 2011ba0afa2fSMahesh Palivela } 2012ba0afa2fSMahesh Palivela 2013074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 20144bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 2015431e3154SAshok Nagarajan u16 prot_mode) 201642e7aa77SAlexander Simon { 2017074d46d1SJohannes Berg struct ieee80211_ht_operation *ht_oper; 201842e7aa77SAlexander Simon /* Build HT Information */ 2019074d46d1SJohannes Berg *pos++ = WLAN_EID_HT_OPERATION; 2020074d46d1SJohannes Berg *pos++ = sizeof(struct ieee80211_ht_operation); 2021074d46d1SJohannes Berg ht_oper = (struct ieee80211_ht_operation *)pos; 20224bf88530SJohannes Berg ht_oper->primary_chan = ieee80211_frequency_to_channel( 20234bf88530SJohannes Berg chandef->chan->center_freq); 20244bf88530SJohannes Berg switch (chandef->width) { 20254bf88530SJohannes Berg case NL80211_CHAN_WIDTH_160: 20264bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80P80: 20274bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80: 20284bf88530SJohannes Berg case NL80211_CHAN_WIDTH_40: 20294bf88530SJohannes Berg if (chandef->center_freq1 > chandef->chan->center_freq) 20304bf88530SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 20314bf88530SJohannes Berg else 2032074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 203342e7aa77SAlexander Simon break; 203442e7aa77SAlexander Simon default: 2035074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; 203642e7aa77SAlexander Simon break; 203742e7aa77SAlexander Simon } 2038aee286c2SThomas Pedersen if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && 20394bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20_NOHT && 20404bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20) 2041074d46d1SJohannes Berg ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; 2042ff3cc5f4SSimon Wunderlich 2043431e3154SAshok Nagarajan ht_oper->operation_mode = cpu_to_le16(prot_mode); 2044074d46d1SJohannes Berg ht_oper->stbc_param = 0x0000; 204542e7aa77SAlexander Simon 204642e7aa77SAlexander Simon /* It seems that Basic MCS set and Supported MCS set 204742e7aa77SAlexander Simon are identical for the first 10 bytes */ 2048074d46d1SJohannes Berg memset(&ht_oper->basic_set, 0, 16); 2049074d46d1SJohannes Berg memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); 205042e7aa77SAlexander Simon 2051074d46d1SJohannes Berg return pos + sizeof(struct ieee80211_ht_operation); 205242e7aa77SAlexander Simon } 205342e7aa77SAlexander Simon 20544bf88530SJohannes Berg void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, 20554a3cb702SJohannes Berg const struct ieee80211_ht_operation *ht_oper, 20564bf88530SJohannes Berg struct cfg80211_chan_def *chandef) 205742e7aa77SAlexander Simon { 205842e7aa77SAlexander Simon enum nl80211_channel_type channel_type; 205942e7aa77SAlexander Simon 20604bf88530SJohannes Berg if (!ht_oper) { 20614bf88530SJohannes Berg cfg80211_chandef_create(chandef, control_chan, 20624bf88530SJohannes Berg NL80211_CHAN_NO_HT); 20634bf88530SJohannes Berg return; 20644bf88530SJohannes Berg } 206542e7aa77SAlexander Simon 2066074d46d1SJohannes Berg switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 206742e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_NONE: 206842e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT20; 206942e7aa77SAlexander Simon break; 207042e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 207142e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40PLUS; 207242e7aa77SAlexander Simon break; 207342e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 207442e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40MINUS; 207542e7aa77SAlexander Simon break; 207642e7aa77SAlexander Simon default: 207742e7aa77SAlexander Simon channel_type = NL80211_CHAN_NO_HT; 207842e7aa77SAlexander Simon } 207942e7aa77SAlexander Simon 20804bf88530SJohannes Berg cfg80211_chandef_create(chandef, control_chan, channel_type); 208142e7aa77SAlexander Simon } 208242e7aa77SAlexander Simon 20832103dec1SSimon Wunderlich int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, 20842103dec1SSimon Wunderlich const struct ieee80211_supported_band *sband, 20852103dec1SSimon Wunderlich const u8 *srates, int srates_len, u32 *rates) 20862103dec1SSimon Wunderlich { 20872103dec1SSimon Wunderlich u32 rate_flags = ieee80211_chandef_rate_flags(chandef); 20882103dec1SSimon Wunderlich int shift = ieee80211_chandef_get_shift(chandef); 20892103dec1SSimon Wunderlich struct ieee80211_rate *br; 20902103dec1SSimon Wunderlich int brate, rate, i, j, count = 0; 20912103dec1SSimon Wunderlich 20922103dec1SSimon Wunderlich *rates = 0; 20932103dec1SSimon Wunderlich 20942103dec1SSimon Wunderlich for (i = 0; i < srates_len; i++) { 20952103dec1SSimon Wunderlich rate = srates[i] & 0x7f; 20962103dec1SSimon Wunderlich 20972103dec1SSimon Wunderlich for (j = 0; j < sband->n_bitrates; j++) { 20982103dec1SSimon Wunderlich br = &sband->bitrates[j]; 20992103dec1SSimon Wunderlich if ((rate_flags & br->flags) != rate_flags) 21002103dec1SSimon Wunderlich continue; 21012103dec1SSimon Wunderlich 21022103dec1SSimon Wunderlich brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); 21032103dec1SSimon Wunderlich if (brate == rate) { 21042103dec1SSimon Wunderlich *rates |= BIT(j); 21052103dec1SSimon Wunderlich count++; 21062103dec1SSimon Wunderlich break; 21072103dec1SSimon Wunderlich } 21082103dec1SSimon Wunderlich } 21092103dec1SSimon Wunderlich } 21102103dec1SSimon Wunderlich return count; 21112103dec1SSimon Wunderlich } 21122103dec1SSimon Wunderlich 2113fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, 21146b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 21156b77863bSJohannes Berg enum ieee80211_band band) 2116768db343SArik Nemtsov { 2117768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 2118768db343SArik Nemtsov struct ieee80211_supported_band *sband; 21192103dec1SSimon Wunderlich int rate, shift; 2120768db343SArik Nemtsov u8 i, rates, *pos; 2121fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 21222103dec1SSimon Wunderlich u32 rate_flags; 2123768db343SArik Nemtsov 21242103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 21252103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 21266b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 21272103dec1SSimon Wunderlich rates = 0; 21282103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) { 21292103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 21302103dec1SSimon Wunderlich continue; 21312103dec1SSimon Wunderlich rates++; 21322103dec1SSimon Wunderlich } 2133768db343SArik Nemtsov if (rates > 8) 2134768db343SArik Nemtsov rates = 8; 2135768db343SArik Nemtsov 2136768db343SArik Nemtsov if (skb_tailroom(skb) < rates + 2) 2137768db343SArik Nemtsov return -ENOMEM; 2138768db343SArik Nemtsov 2139768db343SArik Nemtsov pos = skb_put(skb, rates + 2); 2140768db343SArik Nemtsov *pos++ = WLAN_EID_SUPP_RATES; 2141768db343SArik Nemtsov *pos++ = rates; 2142768db343SArik Nemtsov for (i = 0; i < rates; i++) { 2143657c3e0cSAshok Nagarajan u8 basic = 0; 21442103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 21452103dec1SSimon Wunderlich continue; 21462103dec1SSimon Wunderlich 2147657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 2148657c3e0cSAshok Nagarajan basic = 0x80; 2149768db343SArik Nemtsov rate = sband->bitrates[i].bitrate; 21502103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 21512103dec1SSimon Wunderlich 5 * (1 << shift)); 21522103dec1SSimon Wunderlich *pos++ = basic | (u8) rate; 2153768db343SArik Nemtsov } 2154768db343SArik Nemtsov 2155768db343SArik Nemtsov return 0; 2156768db343SArik Nemtsov } 2157768db343SArik Nemtsov 2158fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, 21596b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 21606b77863bSJohannes Berg enum ieee80211_band band) 2161768db343SArik Nemtsov { 2162768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 2163768db343SArik Nemtsov struct ieee80211_supported_band *sband; 2164cc63ec76SChun-Yeow Yeoh int rate, shift; 2165768db343SArik Nemtsov u8 i, exrates, *pos; 2166fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 21672103dec1SSimon Wunderlich u32 rate_flags; 21682103dec1SSimon Wunderlich 21692103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 21702103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif); 2171768db343SArik Nemtsov 21726b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 21732103dec1SSimon Wunderlich exrates = 0; 21742103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) { 21752103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 21762103dec1SSimon Wunderlich continue; 21772103dec1SSimon Wunderlich exrates++; 21782103dec1SSimon Wunderlich } 21792103dec1SSimon Wunderlich 2180768db343SArik Nemtsov if (exrates > 8) 2181768db343SArik Nemtsov exrates -= 8; 2182768db343SArik Nemtsov else 2183768db343SArik Nemtsov exrates = 0; 2184768db343SArik Nemtsov 2185768db343SArik Nemtsov if (skb_tailroom(skb) < exrates + 2) 2186768db343SArik Nemtsov return -ENOMEM; 2187768db343SArik Nemtsov 2188768db343SArik Nemtsov if (exrates) { 2189768db343SArik Nemtsov pos = skb_put(skb, exrates + 2); 2190768db343SArik Nemtsov *pos++ = WLAN_EID_EXT_SUPP_RATES; 2191768db343SArik Nemtsov *pos++ = exrates; 2192768db343SArik Nemtsov for (i = 8; i < sband->n_bitrates; i++) { 2193657c3e0cSAshok Nagarajan u8 basic = 0; 21942103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) 21952103dec1SSimon Wunderlich != rate_flags) 21962103dec1SSimon Wunderlich continue; 2197657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 2198657c3e0cSAshok Nagarajan basic = 0x80; 21992103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 22002103dec1SSimon Wunderlich 5 * (1 << shift)); 22012103dec1SSimon Wunderlich *pos++ = basic | (u8) rate; 2202768db343SArik Nemtsov } 2203768db343SArik Nemtsov } 2204768db343SArik Nemtsov return 0; 2205768db343SArik Nemtsov } 22061dae27f8SWey-Yi Guy 22071dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif) 22081dae27f8SWey-Yi Guy { 22091dae27f8SWey-Yi Guy struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 22101dae27f8SWey-Yi Guy struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 22111dae27f8SWey-Yi Guy 2212be6bcabcSWey-Yi Guy if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { 2213be6bcabcSWey-Yi Guy /* non-managed type inferfaces */ 2214be6bcabcSWey-Yi Guy return 0; 2215be6bcabcSWey-Yi Guy } 22163a7bba64SEmmanuel Grumbach return ifmgd->ave_beacon_signal / 16; 22171dae27f8SWey-Yi Guy } 22180d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 221904ecd257SJohannes Berg 222004ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) 222104ecd257SJohannes Berg { 222204ecd257SJohannes Berg if (!mcs) 222304ecd257SJohannes Berg return 1; 222404ecd257SJohannes Berg 222504ecd257SJohannes Berg /* TODO: consider rx_highest */ 222604ecd257SJohannes Berg 222704ecd257SJohannes Berg if (mcs->rx_mask[3]) 222804ecd257SJohannes Berg return 4; 222904ecd257SJohannes Berg if (mcs->rx_mask[2]) 223004ecd257SJohannes Berg return 3; 223104ecd257SJohannes Berg if (mcs->rx_mask[1]) 223204ecd257SJohannes Berg return 2; 223304ecd257SJohannes Berg return 1; 223404ecd257SJohannes Berg } 2235f4bda337SThomas Pedersen 2236f4bda337SThomas Pedersen /** 2237f4bda337SThomas Pedersen * ieee80211_calculate_rx_timestamp - calculate timestamp in frame 2238f4bda337SThomas Pedersen * @local: mac80211 hw info struct 2239f4bda337SThomas Pedersen * @status: RX status 2240f4bda337SThomas Pedersen * @mpdu_len: total MPDU length (including FCS) 2241f4bda337SThomas Pedersen * @mpdu_offset: offset into MPDU to calculate timestamp at 2242f4bda337SThomas Pedersen * 2243f4bda337SThomas Pedersen * This function calculates the RX timestamp at the given MPDU offset, taking 2244f4bda337SThomas Pedersen * into account what the RX timestamp was. An offset of 0 will just normalize 2245f4bda337SThomas Pedersen * the timestamp to TSF at beginning of MPDU reception. 2246f4bda337SThomas Pedersen */ 2247f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, 2248f4bda337SThomas Pedersen struct ieee80211_rx_status *status, 2249f4bda337SThomas Pedersen unsigned int mpdu_len, 2250f4bda337SThomas Pedersen unsigned int mpdu_offset) 2251f4bda337SThomas Pedersen { 2252f4bda337SThomas Pedersen u64 ts = status->mactime; 2253f4bda337SThomas Pedersen struct rate_info ri; 2254f4bda337SThomas Pedersen u16 rate; 2255f4bda337SThomas Pedersen 2256f4bda337SThomas Pedersen if (WARN_ON(!ieee80211_have_rx_timestamp(status))) 2257f4bda337SThomas Pedersen return 0; 2258f4bda337SThomas Pedersen 2259f4bda337SThomas Pedersen memset(&ri, 0, sizeof(ri)); 2260f4bda337SThomas Pedersen 2261f4bda337SThomas Pedersen /* Fill cfg80211 rate info */ 2262f4bda337SThomas Pedersen if (status->flag & RX_FLAG_HT) { 2263f4bda337SThomas Pedersen ri.mcs = status->rate_idx; 2264f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_MCS; 2265f4bda337SThomas Pedersen if (status->flag & RX_FLAG_40MHZ) 2266f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; 2267f4bda337SThomas Pedersen if (status->flag & RX_FLAG_SHORT_GI) 2268f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 22695614618eSJohannes Berg } else if (status->flag & RX_FLAG_VHT) { 22705614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_VHT_MCS; 22715614618eSJohannes Berg ri.mcs = status->rate_idx; 22725614618eSJohannes Berg ri.nss = status->vht_nss; 22735614618eSJohannes Berg if (status->flag & RX_FLAG_40MHZ) 22745614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; 22755614618eSJohannes Berg if (status->flag & RX_FLAG_80MHZ) 22765614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; 22775614618eSJohannes Berg if (status->flag & RX_FLAG_80P80MHZ) 22785614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; 22795614618eSJohannes Berg if (status->flag & RX_FLAG_160MHZ) 22805614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; 22815614618eSJohannes Berg if (status->flag & RX_FLAG_SHORT_GI) 22825614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 2283f4bda337SThomas Pedersen } else { 2284f4bda337SThomas Pedersen struct ieee80211_supported_band *sband; 22852103dec1SSimon Wunderlich int shift = 0; 22862103dec1SSimon Wunderlich int bitrate; 22872103dec1SSimon Wunderlich 22882103dec1SSimon Wunderlich if (status->flag & RX_FLAG_10MHZ) 22892103dec1SSimon Wunderlich shift = 1; 22902103dec1SSimon Wunderlich if (status->flag & RX_FLAG_5MHZ) 22912103dec1SSimon Wunderlich shift = 2; 2292f4bda337SThomas Pedersen 2293f4bda337SThomas Pedersen sband = local->hw.wiphy->bands[status->band]; 22942103dec1SSimon Wunderlich bitrate = sband->bitrates[status->rate_idx].bitrate; 22952103dec1SSimon Wunderlich ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); 2296f4bda337SThomas Pedersen } 2297f4bda337SThomas Pedersen 2298f4bda337SThomas Pedersen rate = cfg80211_calculate_bitrate(&ri); 2299d86aa4f8SJohannes Berg if (WARN_ONCE(!rate, 2300d86aa4f8SJohannes Berg "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n", 2301d86aa4f8SJohannes Berg status->flag, status->rate_idx, status->vht_nss)) 2302d86aa4f8SJohannes Berg return 0; 2303f4bda337SThomas Pedersen 2304f4bda337SThomas Pedersen /* rewind from end of MPDU */ 2305f4bda337SThomas Pedersen if (status->flag & RX_FLAG_MACTIME_END) 2306f4bda337SThomas Pedersen ts -= mpdu_len * 8 * 10 / rate; 2307f4bda337SThomas Pedersen 2308f4bda337SThomas Pedersen ts += mpdu_offset * 8 * 10 / rate; 2309f4bda337SThomas Pedersen 2310f4bda337SThomas Pedersen return ts; 2311f4bda337SThomas Pedersen } 2312164eb02dSSimon Wunderlich 2313164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) 2314164eb02dSSimon Wunderlich { 2315164eb02dSSimon Wunderlich struct ieee80211_sub_if_data *sdata; 2316d2859df5SJanusz Dziedzic struct cfg80211_chan_def chandef; 2317164eb02dSSimon Wunderlich 231834a3740dSJohannes Berg mutex_lock(&local->mtx); 2319164eb02dSSimon Wunderlich mutex_lock(&local->iflist_mtx); 2320164eb02dSSimon Wunderlich list_for_each_entry(sdata, &local->interfaces, list) { 232134a3740dSJohannes Berg /* it might be waiting for the local->mtx, but then 232234a3740dSJohannes Berg * by the time it gets it, sdata->wdev.cac_started 232334a3740dSJohannes Berg * will no longer be true 232434a3740dSJohannes Berg */ 232534a3740dSJohannes Berg cancel_delayed_work(&sdata->dfs_cac_timer_work); 2326164eb02dSSimon Wunderlich 2327164eb02dSSimon Wunderlich if (sdata->wdev.cac_started) { 2328d2859df5SJanusz Dziedzic chandef = sdata->vif.bss_conf.chandef; 2329164eb02dSSimon Wunderlich ieee80211_vif_release_channel(sdata); 2330164eb02dSSimon Wunderlich cfg80211_cac_event(sdata->dev, 2331d2859df5SJanusz Dziedzic &chandef, 2332164eb02dSSimon Wunderlich NL80211_RADAR_CAC_ABORTED, 2333164eb02dSSimon Wunderlich GFP_KERNEL); 2334164eb02dSSimon Wunderlich } 2335164eb02dSSimon Wunderlich } 2336164eb02dSSimon Wunderlich mutex_unlock(&local->iflist_mtx); 233734a3740dSJohannes Berg mutex_unlock(&local->mtx); 2338164eb02dSSimon Wunderlich } 2339164eb02dSSimon Wunderlich 2340164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work) 2341164eb02dSSimon Wunderlich { 2342164eb02dSSimon Wunderlich struct ieee80211_local *local = 2343164eb02dSSimon Wunderlich container_of(work, struct ieee80211_local, radar_detected_work); 234484a3d1c9SJanusz Dziedzic struct cfg80211_chan_def chandef = local->hw.conf.chandef; 2345164eb02dSSimon Wunderlich 2346164eb02dSSimon Wunderlich ieee80211_dfs_cac_cancel(local); 2347164eb02dSSimon Wunderlich 2348164eb02dSSimon Wunderlich if (local->use_chanctx) 2349164eb02dSSimon Wunderlich /* currently not handled */ 2350164eb02dSSimon Wunderlich WARN_ON(1); 235184a3d1c9SJanusz Dziedzic else 2352164eb02dSSimon Wunderlich cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); 2353164eb02dSSimon Wunderlich } 2354164eb02dSSimon Wunderlich 2355164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw) 2356164eb02dSSimon Wunderlich { 2357164eb02dSSimon Wunderlich struct ieee80211_local *local = hw_to_local(hw); 2358164eb02dSSimon Wunderlich 2359164eb02dSSimon Wunderlich trace_api_radar_detected(local); 2360164eb02dSSimon Wunderlich 2361164eb02dSSimon Wunderlich ieee80211_queue_work(hw, &local->radar_detected_work); 2362164eb02dSSimon Wunderlich } 2363164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected); 2364e6b7cde4SSimon Wunderlich 2365e6b7cde4SSimon Wunderlich u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) 2366e6b7cde4SSimon Wunderlich { 2367e6b7cde4SSimon Wunderlich u32 ret; 2368e6b7cde4SSimon Wunderlich int tmp; 2369e6b7cde4SSimon Wunderlich 2370e6b7cde4SSimon Wunderlich switch (c->width) { 2371e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20: 2372e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT; 2373e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 2374e6b7cde4SSimon Wunderlich break; 2375e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_40: 2376e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20; 2377e6b7cde4SSimon Wunderlich c->center_freq1 = c->chan->center_freq; 2378e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_40MHZ | 2379e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_VHT; 2380e6b7cde4SSimon Wunderlich break; 2381e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80: 2382e6b7cde4SSimon Wunderlich tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 2383e6b7cde4SSimon Wunderlich /* n_P40 */ 2384e6b7cde4SSimon Wunderlich tmp /= 2; 2385e6b7cde4SSimon Wunderlich /* freq_P40 */ 2386e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; 2387e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_40; 2388e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_VHT; 2389e6b7cde4SSimon Wunderlich break; 2390e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80P80: 2391e6b7cde4SSimon Wunderlich c->center_freq2 = 0; 2392e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80; 2393e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_80P80MHZ | 2394e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_160MHZ; 2395e6b7cde4SSimon Wunderlich break; 2396e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_160: 2397e6b7cde4SSimon Wunderlich /* n_P20 */ 2398e6b7cde4SSimon Wunderlich tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 2399e6b7cde4SSimon Wunderlich /* n_P80 */ 2400e6b7cde4SSimon Wunderlich tmp /= 4; 2401e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; 2402e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80; 2403e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_80P80MHZ | 2404e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_160MHZ; 2405e6b7cde4SSimon Wunderlich break; 2406e6b7cde4SSimon Wunderlich default: 2407e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20_NOHT: 2408e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1); 2409e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT; 2410e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 2411e6b7cde4SSimon Wunderlich break; 2412e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_5: 2413e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_10: 2414e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1); 2415e6b7cde4SSimon Wunderlich /* keep c->width */ 2416e6b7cde4SSimon Wunderlich ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 2417e6b7cde4SSimon Wunderlich break; 2418e6b7cde4SSimon Wunderlich } 2419e6b7cde4SSimon Wunderlich 2420e6b7cde4SSimon Wunderlich WARN_ON_ONCE(!cfg80211_chandef_valid(c)); 2421e6b7cde4SSimon Wunderlich 2422e6b7cde4SSimon Wunderlich return ret; 2423e6b7cde4SSimon Wunderlich } 2424687da132SEmmanuel Grumbach 2425687da132SEmmanuel Grumbach /* 2426687da132SEmmanuel Grumbach * Returns true if smps_mode_new is strictly more restrictive than 2427687da132SEmmanuel Grumbach * smps_mode_old. 2428687da132SEmmanuel Grumbach */ 2429687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, 2430687da132SEmmanuel Grumbach enum ieee80211_smps_mode smps_mode_new) 2431687da132SEmmanuel Grumbach { 2432687da132SEmmanuel Grumbach if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || 2433687da132SEmmanuel Grumbach smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) 2434687da132SEmmanuel Grumbach return false; 2435687da132SEmmanuel Grumbach 2436687da132SEmmanuel Grumbach switch (smps_mode_old) { 2437687da132SEmmanuel Grumbach case IEEE80211_SMPS_STATIC: 2438687da132SEmmanuel Grumbach return false; 2439687da132SEmmanuel Grumbach case IEEE80211_SMPS_DYNAMIC: 2440687da132SEmmanuel Grumbach return smps_mode_new == IEEE80211_SMPS_STATIC; 2441687da132SEmmanuel Grumbach case IEEE80211_SMPS_OFF: 2442687da132SEmmanuel Grumbach return smps_mode_new != IEEE80211_SMPS_OFF; 2443687da132SEmmanuel Grumbach default: 2444687da132SEmmanuel Grumbach WARN_ON(1); 2445687da132SEmmanuel Grumbach } 2446687da132SEmmanuel Grumbach 2447687da132SEmmanuel Grumbach return false; 2448687da132SEmmanuel Grumbach } 2449c6da674aSChun-Yeow Yeoh 2450c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, 2451c6da674aSChun-Yeow Yeoh struct cfg80211_csa_settings *csa_settings) 2452c6da674aSChun-Yeow Yeoh { 2453c6da674aSChun-Yeow Yeoh struct sk_buff *skb; 2454c6da674aSChun-Yeow Yeoh struct ieee80211_mgmt *mgmt; 2455c6da674aSChun-Yeow Yeoh struct ieee80211_local *local = sdata->local; 2456c6da674aSChun-Yeow Yeoh int freq; 2457c6da674aSChun-Yeow Yeoh int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + 2458c6da674aSChun-Yeow Yeoh sizeof(mgmt->u.action.u.chan_switch); 2459c6da674aSChun-Yeow Yeoh u8 *pos; 2460c6da674aSChun-Yeow Yeoh 2461c6da674aSChun-Yeow Yeoh if (sdata->vif.type != NL80211_IFTYPE_ADHOC && 2462c6da674aSChun-Yeow Yeoh sdata->vif.type != NL80211_IFTYPE_MESH_POINT) 2463c6da674aSChun-Yeow Yeoh return -EOPNOTSUPP; 2464c6da674aSChun-Yeow Yeoh 2465c6da674aSChun-Yeow Yeoh skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2466c6da674aSChun-Yeow Yeoh 5 + /* channel switch announcement element */ 2467c6da674aSChun-Yeow Yeoh 3 + /* secondary channel offset element */ 2468c6da674aSChun-Yeow Yeoh 8); /* mesh channel switch parameters element */ 2469c6da674aSChun-Yeow Yeoh if (!skb) 2470c6da674aSChun-Yeow Yeoh return -ENOMEM; 2471c6da674aSChun-Yeow Yeoh 2472c6da674aSChun-Yeow Yeoh skb_reserve(skb, local->tx_headroom); 2473c6da674aSChun-Yeow Yeoh mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); 2474c6da674aSChun-Yeow Yeoh memset(mgmt, 0, hdr_len); 2475c6da674aSChun-Yeow Yeoh mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 2476c6da674aSChun-Yeow Yeoh IEEE80211_STYPE_ACTION); 2477c6da674aSChun-Yeow Yeoh 2478c6da674aSChun-Yeow Yeoh eth_broadcast_addr(mgmt->da); 2479c6da674aSChun-Yeow Yeoh memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 2480c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) { 2481c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 2482c6da674aSChun-Yeow Yeoh } else { 2483c6da674aSChun-Yeow Yeoh struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 2484c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); 2485c6da674aSChun-Yeow Yeoh } 2486c6da674aSChun-Yeow Yeoh mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 2487c6da674aSChun-Yeow Yeoh mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; 2488c6da674aSChun-Yeow Yeoh pos = skb_put(skb, 5); 2489c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ 2490c6da674aSChun-Yeow Yeoh *pos++ = 3; /* IE length */ 2491c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ 2492c6da674aSChun-Yeow Yeoh freq = csa_settings->chandef.chan->center_freq; 2493c6da674aSChun-Yeow Yeoh *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ 2494c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->count; /* count */ 2495c6da674aSChun-Yeow Yeoh 2496c6da674aSChun-Yeow Yeoh if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { 2497c6da674aSChun-Yeow Yeoh enum nl80211_channel_type ch_type; 2498c6da674aSChun-Yeow Yeoh 2499c6da674aSChun-Yeow Yeoh skb_put(skb, 3); 2500c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ 2501c6da674aSChun-Yeow Yeoh *pos++ = 1; /* IE length */ 2502c6da674aSChun-Yeow Yeoh ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); 2503c6da674aSChun-Yeow Yeoh if (ch_type == NL80211_CHAN_HT40PLUS) 2504c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 2505c6da674aSChun-Yeow Yeoh else 2506c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 2507c6da674aSChun-Yeow Yeoh } 2508c6da674aSChun-Yeow Yeoh 2509c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) { 2510c6da674aSChun-Yeow Yeoh struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 2511c6da674aSChun-Yeow Yeoh 2512c6da674aSChun-Yeow Yeoh skb_put(skb, 8); 2513c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ 2514c6da674aSChun-Yeow Yeoh *pos++ = 6; /* IE length */ 2515c6da674aSChun-Yeow Yeoh *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ 2516c6da674aSChun-Yeow Yeoh *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ 2517c6da674aSChun-Yeow Yeoh *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; 2518c6da674aSChun-Yeow Yeoh *pos++ |= csa_settings->block_tx ? 2519c6da674aSChun-Yeow Yeoh WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; 2520c6da674aSChun-Yeow Yeoh put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ 2521c6da674aSChun-Yeow Yeoh pos += 2; 2522ca91dc97SChun-Yeow Yeoh put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ 2523c6da674aSChun-Yeow Yeoh pos += 2; 2524c6da674aSChun-Yeow Yeoh } 2525c6da674aSChun-Yeow Yeoh 2526c6da674aSChun-Yeow Yeoh ieee80211_tx_skb(sdata, skb); 2527c6da674aSChun-Yeow Yeoh return 0; 2528c6da674aSChun-Yeow Yeoh } 25292475b1ccSMax Stepanov 25302475b1ccSMax Stepanov bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) 25312475b1ccSMax Stepanov { 25322475b1ccSMax Stepanov return !(cs == NULL || cs->cipher == 0 || 25332475b1ccSMax Stepanov cs->hdr_len < cs->pn_len + cs->pn_off || 25342475b1ccSMax Stepanov cs->hdr_len <= cs->key_idx_off || 25352475b1ccSMax Stepanov cs->key_idx_shift > 7 || 25362475b1ccSMax Stepanov cs->key_idx_mask == 0); 25372475b1ccSMax Stepanov } 25382475b1ccSMax Stepanov 25392475b1ccSMax Stepanov bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) 25402475b1ccSMax Stepanov { 25412475b1ccSMax Stepanov int i; 25422475b1ccSMax Stepanov 25432475b1ccSMax Stepanov /* Ensure we have enough iftype bitmap space for all iftype values */ 25442475b1ccSMax Stepanov WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); 25452475b1ccSMax Stepanov 25462475b1ccSMax Stepanov for (i = 0; i < n; i++) 25472475b1ccSMax Stepanov if (!ieee80211_cs_valid(&cs[i])) 25482475b1ccSMax Stepanov return false; 25492475b1ccSMax Stepanov 25502475b1ccSMax Stepanov return true; 25512475b1ccSMax Stepanov } 25522475b1ccSMax Stepanov 25532475b1ccSMax Stepanov const struct ieee80211_cipher_scheme * 25542475b1ccSMax Stepanov ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, 25552475b1ccSMax Stepanov enum nl80211_iftype iftype) 25562475b1ccSMax Stepanov { 25572475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; 25582475b1ccSMax Stepanov int n = local->hw.n_cipher_schemes; 25592475b1ccSMax Stepanov int i; 25602475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *cs = NULL; 25612475b1ccSMax Stepanov 25622475b1ccSMax Stepanov for (i = 0; i < n; i++) { 25632475b1ccSMax Stepanov if (l[i].cipher == cipher) { 25642475b1ccSMax Stepanov cs = &l[i]; 25652475b1ccSMax Stepanov break; 25662475b1ccSMax Stepanov } 25672475b1ccSMax Stepanov } 25682475b1ccSMax Stepanov 25692475b1ccSMax Stepanov if (!cs || !(cs->iftype & BIT(iftype))) 25702475b1ccSMax Stepanov return NULL; 25712475b1ccSMax Stepanov 25722475b1ccSMax Stepanov return cs; 25732475b1ccSMax Stepanov } 25742475b1ccSMax Stepanov 25752475b1ccSMax Stepanov int ieee80211_cs_headroom(struct ieee80211_local *local, 25762475b1ccSMax Stepanov struct cfg80211_crypto_settings *crypto, 25772475b1ccSMax Stepanov enum nl80211_iftype iftype) 25782475b1ccSMax Stepanov { 25792475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *cs; 25802475b1ccSMax Stepanov int headroom = IEEE80211_ENCRYPT_HEADROOM; 25812475b1ccSMax Stepanov int i; 25822475b1ccSMax Stepanov 25832475b1ccSMax Stepanov for (i = 0; i < crypto->n_ciphers_pairwise; i++) { 25842475b1ccSMax Stepanov cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], 25852475b1ccSMax Stepanov iftype); 25862475b1ccSMax Stepanov 25872475b1ccSMax Stepanov if (cs && headroom < cs->hdr_len) 25882475b1ccSMax Stepanov headroom = cs->hdr_len; 25892475b1ccSMax Stepanov } 25902475b1ccSMax Stepanov 25912475b1ccSMax Stepanov cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); 25922475b1ccSMax Stepanov if (cs && headroom < cs->hdr_len) 25932475b1ccSMax Stepanov headroom = cs->hdr_len; 25942475b1ccSMax Stepanov 25952475b1ccSMax Stepanov return headroom; 25962475b1ccSMax Stepanov } 2597a7022e65SFelix Fietkau 2598a7022e65SFelix Fietkau static bool 2599a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) 2600a7022e65SFelix Fietkau { 2601a7022e65SFelix Fietkau s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); 2602a7022e65SFelix Fietkau int skip; 2603a7022e65SFelix Fietkau 2604a7022e65SFelix Fietkau if (end > 0) 2605a7022e65SFelix Fietkau return false; 2606a7022e65SFelix Fietkau 2607a7022e65SFelix Fietkau /* End time is in the past, check for repetitions */ 2608a7022e65SFelix Fietkau skip = DIV_ROUND_UP(-end, data->desc[i].interval); 2609a7022e65SFelix Fietkau if (data->count[i] < 255) { 2610a7022e65SFelix Fietkau if (data->count[i] <= skip) { 2611a7022e65SFelix Fietkau data->count[i] = 0; 2612a7022e65SFelix Fietkau return false; 2613a7022e65SFelix Fietkau } 2614a7022e65SFelix Fietkau 2615a7022e65SFelix Fietkau data->count[i] -= skip; 2616a7022e65SFelix Fietkau } 2617a7022e65SFelix Fietkau 2618a7022e65SFelix Fietkau data->desc[i].start += skip * data->desc[i].interval; 2619a7022e65SFelix Fietkau 2620a7022e65SFelix Fietkau return true; 2621a7022e65SFelix Fietkau } 2622a7022e65SFelix Fietkau 2623a7022e65SFelix Fietkau static bool 2624a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, 2625a7022e65SFelix Fietkau s32 *offset) 2626a7022e65SFelix Fietkau { 2627a7022e65SFelix Fietkau bool ret = false; 2628a7022e65SFelix Fietkau int i; 2629a7022e65SFelix Fietkau 2630a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 2631a7022e65SFelix Fietkau s32 cur; 2632a7022e65SFelix Fietkau 2633a7022e65SFelix Fietkau if (!data->count[i]) 2634a7022e65SFelix Fietkau continue; 2635a7022e65SFelix Fietkau 2636a7022e65SFelix Fietkau if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) 2637a7022e65SFelix Fietkau ret = true; 2638a7022e65SFelix Fietkau 2639a7022e65SFelix Fietkau cur = data->desc[i].start - tsf; 2640a7022e65SFelix Fietkau if (cur > *offset) 2641a7022e65SFelix Fietkau continue; 2642a7022e65SFelix Fietkau 2643a7022e65SFelix Fietkau cur = data->desc[i].start + data->desc[i].duration - tsf; 2644a7022e65SFelix Fietkau if (cur > *offset) 2645a7022e65SFelix Fietkau *offset = cur; 2646a7022e65SFelix Fietkau } 2647a7022e65SFelix Fietkau 2648a7022e65SFelix Fietkau return ret; 2649a7022e65SFelix Fietkau } 2650a7022e65SFelix Fietkau 2651a7022e65SFelix Fietkau static u32 2652a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) 2653a7022e65SFelix Fietkau { 2654a7022e65SFelix Fietkau s32 offset = 0; 2655a7022e65SFelix Fietkau int tries = 0; 2656a7022e65SFelix Fietkau /* 2657a7022e65SFelix Fietkau * arbitrary limit, used to avoid infinite loops when combined NoA 2658a7022e65SFelix Fietkau * descriptors cover the full time period. 2659a7022e65SFelix Fietkau */ 2660a7022e65SFelix Fietkau int max_tries = 5; 2661a7022e65SFelix Fietkau 2662a7022e65SFelix Fietkau ieee80211_extend_absent_time(data, tsf, &offset); 2663a7022e65SFelix Fietkau do { 2664a7022e65SFelix Fietkau if (!ieee80211_extend_absent_time(data, tsf, &offset)) 2665a7022e65SFelix Fietkau break; 2666a7022e65SFelix Fietkau 2667a7022e65SFelix Fietkau tries++; 2668a7022e65SFelix Fietkau } while (tries < max_tries); 2669a7022e65SFelix Fietkau 2670a7022e65SFelix Fietkau return offset; 2671a7022e65SFelix Fietkau } 2672a7022e65SFelix Fietkau 2673a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) 2674a7022e65SFelix Fietkau { 2675a7022e65SFelix Fietkau u32 next_offset = BIT(31) - 1; 2676a7022e65SFelix Fietkau int i; 2677a7022e65SFelix Fietkau 2678a7022e65SFelix Fietkau data->absent = 0; 2679a7022e65SFelix Fietkau data->has_next_tsf = false; 2680a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 2681a7022e65SFelix Fietkau s32 start; 2682a7022e65SFelix Fietkau 2683a7022e65SFelix Fietkau if (!data->count[i]) 2684a7022e65SFelix Fietkau continue; 2685a7022e65SFelix Fietkau 2686a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i); 2687a7022e65SFelix Fietkau start = data->desc[i].start - tsf; 2688a7022e65SFelix Fietkau if (start <= 0) 2689a7022e65SFelix Fietkau data->absent |= BIT(i); 2690a7022e65SFelix Fietkau 2691a7022e65SFelix Fietkau if (next_offset > start) 2692a7022e65SFelix Fietkau next_offset = start; 2693a7022e65SFelix Fietkau 2694a7022e65SFelix Fietkau data->has_next_tsf = true; 2695a7022e65SFelix Fietkau } 2696a7022e65SFelix Fietkau 2697a7022e65SFelix Fietkau if (data->absent) 2698a7022e65SFelix Fietkau next_offset = ieee80211_get_noa_absent_time(data, tsf); 2699a7022e65SFelix Fietkau 2700a7022e65SFelix Fietkau data->next_tsf = tsf + next_offset; 2701a7022e65SFelix Fietkau } 2702a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa); 2703a7022e65SFelix Fietkau 2704a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, 2705a7022e65SFelix Fietkau struct ieee80211_noa_data *data, u32 tsf) 2706a7022e65SFelix Fietkau { 2707a7022e65SFelix Fietkau int ret = 0; 2708a7022e65SFelix Fietkau int i; 2709a7022e65SFelix Fietkau 2710a7022e65SFelix Fietkau memset(data, 0, sizeof(*data)); 2711a7022e65SFelix Fietkau 2712a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 2713a7022e65SFelix Fietkau const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; 2714a7022e65SFelix Fietkau 2715a7022e65SFelix Fietkau if (!desc->count || !desc->duration) 2716a7022e65SFelix Fietkau continue; 2717a7022e65SFelix Fietkau 2718a7022e65SFelix Fietkau data->count[i] = desc->count; 2719a7022e65SFelix Fietkau data->desc[i].start = le32_to_cpu(desc->start_time); 2720a7022e65SFelix Fietkau data->desc[i].duration = le32_to_cpu(desc->duration); 2721a7022e65SFelix Fietkau data->desc[i].interval = le32_to_cpu(desc->interval); 2722a7022e65SFelix Fietkau 2723a7022e65SFelix Fietkau if (data->count[i] > 1 && 2724a7022e65SFelix Fietkau data->desc[i].interval < data->desc[i].duration) 2725a7022e65SFelix Fietkau continue; 2726a7022e65SFelix Fietkau 2727a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i); 2728a7022e65SFelix Fietkau ret++; 2729a7022e65SFelix Fietkau } 2730a7022e65SFelix Fietkau 2731a7022e65SFelix Fietkau if (ret) 2732a7022e65SFelix Fietkau ieee80211_update_p2p_noa(data, tsf); 2733a7022e65SFelix Fietkau 2734a7022e65SFelix Fietkau return ret; 2735a7022e65SFelix Fietkau } 2736a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa); 2737057d5f4bSThomas Pedersen 2738057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local, 2739057d5f4bSThomas Pedersen struct ieee80211_sub_if_data *sdata) 2740057d5f4bSThomas Pedersen { 2741057d5f4bSThomas Pedersen u64 tsf = drv_get_tsf(local, sdata); 2742057d5f4bSThomas Pedersen u64 dtim_count = 0; 2743057d5f4bSThomas Pedersen u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; 2744057d5f4bSThomas Pedersen u8 dtim_period = sdata->vif.bss_conf.dtim_period; 2745057d5f4bSThomas Pedersen struct ps_data *ps; 2746057d5f4bSThomas Pedersen u8 bcns_from_dtim; 2747057d5f4bSThomas Pedersen 2748057d5f4bSThomas Pedersen if (tsf == -1ULL || !beacon_int || !dtim_period) 2749057d5f4bSThomas Pedersen return; 2750057d5f4bSThomas Pedersen 2751057d5f4bSThomas Pedersen if (sdata->vif.type == NL80211_IFTYPE_AP || 2752057d5f4bSThomas Pedersen sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 2753057d5f4bSThomas Pedersen if (!sdata->bss) 2754057d5f4bSThomas Pedersen return; 2755057d5f4bSThomas Pedersen 2756057d5f4bSThomas Pedersen ps = &sdata->bss->ps; 2757057d5f4bSThomas Pedersen } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 2758057d5f4bSThomas Pedersen ps = &sdata->u.mesh.ps; 2759057d5f4bSThomas Pedersen } else { 2760057d5f4bSThomas Pedersen return; 2761057d5f4bSThomas Pedersen } 2762057d5f4bSThomas Pedersen 2763057d5f4bSThomas Pedersen /* 2764057d5f4bSThomas Pedersen * actually finds last dtim_count, mac80211 will update in 2765057d5f4bSThomas Pedersen * __beacon_add_tim(). 2766057d5f4bSThomas Pedersen * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period 2767057d5f4bSThomas Pedersen */ 2768057d5f4bSThomas Pedersen do_div(tsf, beacon_int); 2769057d5f4bSThomas Pedersen bcns_from_dtim = do_div(tsf, dtim_period); 2770057d5f4bSThomas Pedersen /* just had a DTIM */ 2771057d5f4bSThomas Pedersen if (!bcns_from_dtim) 2772057d5f4bSThomas Pedersen dtim_count = 0; 2773057d5f4bSThomas Pedersen else 2774057d5f4bSThomas Pedersen dtim_count = dtim_period - bcns_from_dtim; 2775057d5f4bSThomas Pedersen 2776057d5f4bSThomas Pedersen ps->dtim_count = dtim_count; 2777057d5f4bSThomas Pedersen } 2778