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, 110c2d1560aSJohannes Berg int rate, int erp, int short_preamble) 111c2d1560aSJohannes Berg { 112c2d1560aSJohannes Berg int dur; 113c2d1560aSJohannes Berg 114c2d1560aSJohannes Berg /* calculate duration (in microseconds, rounded up to next higher 115c2d1560aSJohannes Berg * integer if it includes a fractional microsecond) to send frame of 116c2d1560aSJohannes Berg * len bytes (does not include FCS) at the given rate. Duration will 117c2d1560aSJohannes Berg * also include SIFS. 118c2d1560aSJohannes Berg * 119c2d1560aSJohannes Berg * rate is in 100 kbps, so divident is multiplied by 10 in the 120c2d1560aSJohannes Berg * DIV_ROUND_UP() operations. 121c2d1560aSJohannes Berg */ 122c2d1560aSJohannes Berg 1234ee73f33SMichal Kazior if (band == IEEE80211_BAND_5GHZ || erp) { 124c2d1560aSJohannes Berg /* 125c2d1560aSJohannes Berg * OFDM: 126c2d1560aSJohannes Berg * 127c2d1560aSJohannes Berg * N_DBPS = DATARATE x 4 128c2d1560aSJohannes Berg * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) 129c2d1560aSJohannes Berg * (16 = SIGNAL time, 6 = tail bits) 130c2d1560aSJohannes Berg * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext 131c2d1560aSJohannes Berg * 132c2d1560aSJohannes Berg * T_SYM = 4 usec 133c2d1560aSJohannes Berg * 802.11a - 17.5.2: aSIFSTime = 16 usec 134c2d1560aSJohannes Berg * 802.11g - 19.8.4: aSIFSTime = 10 usec + 135c2d1560aSJohannes Berg * signal ext = 6 usec 136c2d1560aSJohannes Berg */ 137c2d1560aSJohannes Berg dur = 16; /* SIFS + signal ext */ 138c2d1560aSJohannes Berg dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ 139c2d1560aSJohannes Berg dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ 140c2d1560aSJohannes Berg dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 141c2d1560aSJohannes Berg 4 * rate); /* T_SYM x N_SYM */ 142c2d1560aSJohannes Berg } else { 143c2d1560aSJohannes Berg /* 144c2d1560aSJohannes Berg * 802.11b or 802.11g with 802.11b compatibility: 145c2d1560aSJohannes Berg * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + 146c2d1560aSJohannes Berg * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 147c2d1560aSJohannes Berg * 148c2d1560aSJohannes Berg * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 149c2d1560aSJohannes Berg * aSIFSTime = 10 usec 150c2d1560aSJohannes Berg * aPreambleLength = 144 usec or 72 usec with short preamble 151c2d1560aSJohannes Berg * aPLCPHeaderLength = 48 usec or 24 usec with short preamble 152c2d1560aSJohannes Berg */ 153c2d1560aSJohannes Berg dur = 10; /* aSIFSTime = 10 usec */ 154c2d1560aSJohannes Berg dur += short_preamble ? (72 + 24) : (144 + 48); 155c2d1560aSJohannes Berg 156c2d1560aSJohannes Berg dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); 157c2d1560aSJohannes Berg } 158c2d1560aSJohannes Berg 159c2d1560aSJohannes Berg return dur; 160c2d1560aSJohannes Berg } 161c2d1560aSJohannes Berg 162c2d1560aSJohannes Berg /* Exported duration function for driver use */ 16332bfd35dSJohannes Berg __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, 16432bfd35dSJohannes Berg struct ieee80211_vif *vif, 1654ee73f33SMichal Kazior enum ieee80211_band band, 1668318d78aSJohannes Berg size_t frame_len, 1678318d78aSJohannes Berg struct ieee80211_rate *rate) 168c2d1560aSJohannes Berg { 16925d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 170c2d1560aSJohannes Berg u16 dur; 171c2d1560aSJohannes Berg int erp; 17225d834e1SJohannes Berg bool short_preamble = false; 173c2d1560aSJohannes Berg 1748318d78aSJohannes Berg erp = 0; 17525d834e1SJohannes Berg if (vif) { 17625d834e1SJohannes Berg sdata = vif_to_sdata(vif); 177bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 1788318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 1798318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 18025d834e1SJohannes Berg } 1818318d78aSJohannes Berg 1824ee73f33SMichal Kazior dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, 18325d834e1SJohannes Berg short_preamble); 184c2d1560aSJohannes Berg 185c2d1560aSJohannes Berg return cpu_to_le16(dur); 186c2d1560aSJohannes Berg } 187c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_generic_frame_duration); 188c2d1560aSJohannes Berg 18932bfd35dSJohannes Berg __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, 19032bfd35dSJohannes Berg struct ieee80211_vif *vif, size_t frame_len, 191e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 192c2d1560aSJohannes Berg { 193c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 194c2d1560aSJohannes Berg struct ieee80211_rate *rate; 19525d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 196471b3efdSJohannes Berg bool short_preamble; 197c2d1560aSJohannes Berg int erp; 198c2d1560aSJohannes Berg u16 dur; 1992e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2002e92e6f2SJohannes Berg 2014ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 202c2d1560aSJohannes Berg 20325d834e1SJohannes Berg short_preamble = false; 2047e9ed188SDaniel Drake 205e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2068318d78aSJohannes Berg 2078318d78aSJohannes Berg erp = 0; 20825d834e1SJohannes Berg if (vif) { 20925d834e1SJohannes Berg sdata = vif_to_sdata(vif); 210bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2118318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2128318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 21325d834e1SJohannes Berg } 214c2d1560aSJohannes Berg 215c2d1560aSJohannes Berg /* CTS duration */ 2164ee73f33SMichal Kazior dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate, 217c2d1560aSJohannes Berg erp, short_preamble); 218c2d1560aSJohannes Berg /* Data frame duration */ 2194ee73f33SMichal Kazior dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, 220c2d1560aSJohannes Berg erp, short_preamble); 221c2d1560aSJohannes Berg /* ACK duration */ 2224ee73f33SMichal Kazior dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, 223c2d1560aSJohannes Berg erp, short_preamble); 224c2d1560aSJohannes Berg 225c2d1560aSJohannes Berg return cpu_to_le16(dur); 226c2d1560aSJohannes Berg } 227c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_rts_duration); 228c2d1560aSJohannes Berg 22932bfd35dSJohannes Berg __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, 23032bfd35dSJohannes Berg struct ieee80211_vif *vif, 231c2d1560aSJohannes Berg size_t frame_len, 232e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl) 233c2d1560aSJohannes Berg { 234c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 235c2d1560aSJohannes Berg struct ieee80211_rate *rate; 23625d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata; 237471b3efdSJohannes Berg bool short_preamble; 238c2d1560aSJohannes Berg int erp; 239c2d1560aSJohannes Berg u16 dur; 2402e92e6f2SJohannes Berg struct ieee80211_supported_band *sband; 2412e92e6f2SJohannes Berg 2424ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band]; 243c2d1560aSJohannes Berg 24425d834e1SJohannes Berg short_preamble = false; 2457e9ed188SDaniel Drake 246e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2478318d78aSJohannes Berg erp = 0; 24825d834e1SJohannes Berg if (vif) { 24925d834e1SJohannes Berg sdata = vif_to_sdata(vif); 250bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble; 2518318d78aSJohannes Berg if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2528318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G; 25325d834e1SJohannes Berg } 254c2d1560aSJohannes Berg 255c2d1560aSJohannes Berg /* Data frame duration */ 2564ee73f33SMichal Kazior dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, 257c2d1560aSJohannes Berg erp, short_preamble); 258e039fa4aSJohannes Berg if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { 259c2d1560aSJohannes Berg /* ACK duration */ 2604ee73f33SMichal Kazior dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, 261c2d1560aSJohannes Berg erp, short_preamble); 262c2d1560aSJohannes Berg } 263c2d1560aSJohannes Berg 264c2d1560aSJohannes Berg return cpu_to_le16(dur); 265c2d1560aSJohannes Berg } 266c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_ctstoself_duration); 267c2d1560aSJohannes Berg 2683a25a8c8SJohannes Berg void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) 2693a25a8c8SJohannes Berg { 2703a25a8c8SJohannes Berg struct ieee80211_sub_if_data *sdata; 271a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 272a6f38ac3SJohannes Berg 273a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 274a6f38ac3SJohannes Berg n_acs = 1; 2753a25a8c8SJohannes Berg 2763a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 2773a25a8c8SJohannes Berg int ac; 2783a25a8c8SJohannes Berg 279f142c6b9SJohannes Berg if (!sdata->dev) 280f142c6b9SJohannes Berg continue; 281f142c6b9SJohannes Berg 2823a25a8c8SJohannes Berg if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) 2833a25a8c8SJohannes Berg continue; 2843a25a8c8SJohannes Berg 2853a25a8c8SJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && 2863a25a8c8SJohannes Berg local->queue_stop_reasons[sdata->vif.cab_queue] != 0) 2873a25a8c8SJohannes Berg continue; 2883a25a8c8SJohannes Berg 289a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 2903a25a8c8SJohannes Berg int ac_queue = sdata->vif.hw_queue[ac]; 2913a25a8c8SJohannes Berg 2923a25a8c8SJohannes Berg if (ac_queue == queue || 2933a25a8c8SJohannes Berg (sdata->vif.cab_queue == queue && 2943a25a8c8SJohannes Berg local->queue_stop_reasons[ac_queue] == 0 && 2953a25a8c8SJohannes Berg skb_queue_empty(&local->pending[ac_queue]))) 2963a25a8c8SJohannes Berg netif_wake_subqueue(sdata->dev, ac); 2973a25a8c8SJohannes Berg } 2983a25a8c8SJohannes Berg } 2993a25a8c8SJohannes Berg } 3003a25a8c8SJohannes Berg 301ce7c9111SKalle Valo static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, 302ce7c9111SKalle Valo enum queue_stop_reason reason) 303c2d1560aSJohannes Berg { 304c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 305c2d1560aSJohannes Berg 306b5878a2dSJohannes Berg trace_wake_queue(local, queue, reason); 307b5878a2dSJohannes Berg 308e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 30996f5e66eSJohannes Berg return; 31096f5e66eSJohannes Berg 311ada15125SJohannes Berg if (!test_bit(reason, &local->queue_stop_reasons[queue])) 312ada15125SJohannes Berg return; 313ada15125SJohannes Berg 314ce7c9111SKalle Valo __clear_bit(reason, &local->queue_stop_reasons[queue]); 315ce7c9111SKalle Valo 316ce7c9111SKalle Valo if (local->queue_stop_reasons[queue] != 0) 317ce7c9111SKalle Valo /* someone still has this queue stopped */ 318ce7c9111SKalle Valo return; 319ce7c9111SKalle Valo 3207236fe29SJohannes Berg if (skb_queue_empty(&local->pending[queue])) { 321cf0277e7SJohannes Berg rcu_read_lock(); 3223a25a8c8SJohannes Berg ieee80211_propagate_queue_wake(local, queue); 323cf0277e7SJohannes Berg rcu_read_unlock(); 3247236fe29SJohannes Berg } else 3257236fe29SJohannes Berg tasklet_schedule(&local->tx_pending_tasklet); 326c2d1560aSJohannes Berg } 327ce7c9111SKalle Valo 32896f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, 329ce7c9111SKalle Valo enum queue_stop_reason reason) 330ce7c9111SKalle Valo { 331ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 332ce7c9111SKalle Valo unsigned long flags; 333ce7c9111SKalle Valo 334ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 335ce7c9111SKalle Valo __ieee80211_wake_queue(hw, queue, reason); 336ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 337ce7c9111SKalle Valo } 338ce7c9111SKalle Valo 339ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) 340ce7c9111SKalle Valo { 341ce7c9111SKalle Valo ieee80211_wake_queue_by_reason(hw, queue, 342ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 343ce7c9111SKalle Valo } 344c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue); 345c2d1560aSJohannes Berg 346ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, 347ce7c9111SKalle Valo enum queue_stop_reason reason) 348c2d1560aSJohannes Berg { 349c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 350cf0277e7SJohannes Berg struct ieee80211_sub_if_data *sdata; 351a6f38ac3SJohannes Berg int n_acs = IEEE80211_NUM_ACS; 352c2d1560aSJohannes Berg 353b5878a2dSJohannes Berg trace_stop_queue(local, queue, reason); 354b5878a2dSJohannes Berg 355e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 35696f5e66eSJohannes Berg return; 35796f5e66eSJohannes Berg 358ada15125SJohannes Berg if (test_bit(reason, &local->queue_stop_reasons[queue])) 359ada15125SJohannes Berg return; 360ada15125SJohannes Berg 3612a577d98SJohannes Berg __set_bit(reason, &local->queue_stop_reasons[queue]); 362cf0277e7SJohannes Berg 363a6f38ac3SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 364a6f38ac3SJohannes Berg n_acs = 1; 365a6f38ac3SJohannes Berg 366cf0277e7SJohannes Berg rcu_read_lock(); 3673a25a8c8SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 3683a25a8c8SJohannes Berg int ac; 3693a25a8c8SJohannes Berg 370f142c6b9SJohannes Berg if (!sdata->dev) 371f142c6b9SJohannes Berg continue; 372f142c6b9SJohannes Berg 373a6f38ac3SJohannes Berg for (ac = 0; ac < n_acs; ac++) { 3743a25a8c8SJohannes Berg if (sdata->vif.hw_queue[ac] == queue || 3753a25a8c8SJohannes Berg sdata->vif.cab_queue == queue) 3763a25a8c8SJohannes Berg netif_stop_subqueue(sdata->dev, ac); 3773a25a8c8SJohannes Berg } 3783a25a8c8SJohannes Berg } 379cf0277e7SJohannes Berg rcu_read_unlock(); 380c2d1560aSJohannes Berg } 381ce7c9111SKalle Valo 38296f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, 383ce7c9111SKalle Valo enum queue_stop_reason reason) 384ce7c9111SKalle Valo { 385ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 386ce7c9111SKalle Valo unsigned long flags; 387ce7c9111SKalle Valo 388ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 389ce7c9111SKalle Valo __ieee80211_stop_queue(hw, queue, reason); 390ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 391ce7c9111SKalle Valo } 392ce7c9111SKalle Valo 393ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) 394ce7c9111SKalle Valo { 395ce7c9111SKalle Valo ieee80211_stop_queue_by_reason(hw, queue, 396ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 397ce7c9111SKalle Valo } 398c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue); 399c2d1560aSJohannes Berg 4008f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local, 4018f77f384SJohannes Berg struct sk_buff *skb) 4028f77f384SJohannes Berg { 4038f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 4048f77f384SJohannes Berg unsigned long flags; 405a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 4063a25a8c8SJohannes Berg int queue = info->hw_queue; 407a7bc376cSJohannes Berg 408a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 409d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 410a7bc376cSJohannes Berg return; 411a7bc376cSJohannes Berg } 4128f77f384SJohannes Berg 4138f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4148f77f384SJohannes Berg __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4153b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 4168f77f384SJohannes Berg __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4178f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4188f77f384SJohannes Berg } 4198f77f384SJohannes Berg 420b0b97a8aSJohannes Berg void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, 42150a9432dSJohannes Berg struct sk_buff_head *skbs, 42250a9432dSJohannes Berg void (*fn)(void *data), void *data) 4238f77f384SJohannes Berg { 4248f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw; 4258f77f384SJohannes Berg struct sk_buff *skb; 4268f77f384SJohannes Berg unsigned long flags; 427b0b97a8aSJohannes Berg int queue, i; 4288f77f384SJohannes Berg 4298f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4308f77f384SJohannes Berg while ((skb = skb_dequeue(skbs))) { 431a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 432a7bc376cSJohannes Berg 433a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) { 434d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb); 435a7bc376cSJohannes Berg continue; 436a7bc376cSJohannes Berg } 437a7bc376cSJohannes Berg 4383a25a8c8SJohannes Berg queue = info->hw_queue; 4394644ae89SJohannes Berg 4404644ae89SJohannes Berg __ieee80211_stop_queue(hw, queue, 4414644ae89SJohannes Berg IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4424644ae89SJohannes Berg 4433b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb); 4448f77f384SJohannes Berg } 4458f77f384SJohannes Berg 44650a9432dSJohannes Berg if (fn) 44750a9432dSJohannes Berg fn(data); 44850a9432dSJohannes Berg 4493b8d81e0SJohannes Berg for (i = 0; i < hw->queues; i++) 4508f77f384SJohannes Berg __ieee80211_wake_queue(hw, i, 4518f77f384SJohannes Berg IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 4528f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4538f77f384SJohannes Berg } 4548f77f384SJohannes Berg 455ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 456445ea4e8SJohannes Berg unsigned long queues, 457ce7c9111SKalle Valo enum queue_stop_reason reason) 458c2d1560aSJohannes Berg { 459ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 460ce7c9111SKalle Valo unsigned long flags; 461c2d1560aSJohannes Berg int i; 462c2d1560aSJohannes Berg 463ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 464ce7c9111SKalle Valo 465445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 466ce7c9111SKalle Valo __ieee80211_stop_queue(hw, i, reason); 467ce7c9111SKalle Valo 468ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 469ce7c9111SKalle Valo } 470ce7c9111SKalle Valo 471ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw) 472ce7c9111SKalle Valo { 473445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 474ce7c9111SKalle Valo IEEE80211_QUEUE_STOP_REASON_DRIVER); 475c2d1560aSJohannes Berg } 476c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues); 477c2d1560aSJohannes Berg 47892ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) 47992ab8535STomas Winkler { 48092ab8535STomas Winkler struct ieee80211_local *local = hw_to_local(hw); 4813b8d81e0SJohannes Berg unsigned long flags; 4823b8d81e0SJohannes Berg int ret; 48396f5e66eSJohannes Berg 484e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues)) 48596f5e66eSJohannes Berg return true; 48696f5e66eSJohannes Berg 4873b8d81e0SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4882419ea14SThomas Pedersen ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, 4892419ea14SThomas Pedersen &local->queue_stop_reasons[queue]); 4903b8d81e0SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4913b8d81e0SJohannes Berg return ret; 49292ab8535STomas Winkler } 49392ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped); 49492ab8535STomas Winkler 495ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, 496445ea4e8SJohannes Berg unsigned long queues, 497ce7c9111SKalle Valo enum queue_stop_reason reason) 498c2d1560aSJohannes Berg { 499ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw); 500ce7c9111SKalle Valo unsigned long flags; 501c2d1560aSJohannes Berg int i; 502c2d1560aSJohannes Berg 503ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 504ce7c9111SKalle Valo 505445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues) 506ce7c9111SKalle Valo __ieee80211_wake_queue(hw, i, reason); 507ce7c9111SKalle Valo 508ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 509ce7c9111SKalle Valo } 510ce7c9111SKalle Valo 511ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw) 512ce7c9111SKalle Valo { 513445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 514445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_DRIVER); 515c2d1560aSJohannes Berg } 516c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues); 517dabeb344SJohannes Berg 51839ecc01dSJohannes Berg void ieee80211_flush_queues(struct ieee80211_local *local, 51939ecc01dSJohannes Berg struct ieee80211_sub_if_data *sdata) 52039ecc01dSJohannes Berg { 52139ecc01dSJohannes Berg u32 queues; 52239ecc01dSJohannes Berg 52339ecc01dSJohannes Berg if (!local->ops->flush) 52439ecc01dSJohannes Berg return; 52539ecc01dSJohannes Berg 52639ecc01dSJohannes Berg if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { 52739ecc01dSJohannes Berg int ac; 52839ecc01dSJohannes Berg 52939ecc01dSJohannes Berg queues = 0; 53039ecc01dSJohannes Berg 53139ecc01dSJohannes Berg for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 53239ecc01dSJohannes Berg queues |= BIT(sdata->vif.hw_queue[ac]); 53339ecc01dSJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) 53439ecc01dSJohannes Berg queues |= BIT(sdata->vif.cab_queue); 53539ecc01dSJohannes Berg } else { 53639ecc01dSJohannes Berg /* all queues */ 53739ecc01dSJohannes Berg queues = BIT(local->hw.queues) - 1; 53839ecc01dSJohannes Berg } 53939ecc01dSJohannes Berg 540445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, 541445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_FLUSH); 542445ea4e8SJohannes Berg 54339ecc01dSJohannes Berg drv_flush(local, queues, false); 544445ea4e8SJohannes Berg 545445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, 546445ea4e8SJohannes Berg IEEE80211_QUEUE_STOP_REASON_FLUSH); 54739ecc01dSJohannes Berg } 54839ecc01dSJohannes Berg 54932bfd35dSJohannes Berg void ieee80211_iterate_active_interfaces( 5508b2c9824SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 551dabeb344SJohannes Berg void (*iterator)(void *data, u8 *mac, 55232bfd35dSJohannes Berg struct ieee80211_vif *vif), 553dabeb344SJohannes Berg void *data) 554dabeb344SJohannes Berg { 555dabeb344SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 556dabeb344SJohannes Berg struct ieee80211_sub_if_data *sdata; 557dabeb344SJohannes Berg 558c771c9d8SJohannes Berg mutex_lock(&local->iflist_mtx); 5592f561febSIvo van Doorn 5602f561febSIvo van Doorn list_for_each_entry(sdata, &local->interfaces, list) { 5612f561febSIvo van Doorn switch (sdata->vif.type) { 56205c914feSJohannes Berg case NL80211_IFTYPE_MONITOR: 56305c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 5642f561febSIvo van Doorn continue; 5652ca27bcfSJohannes Berg default: 5662f561febSIvo van Doorn break; 5672f561febSIvo van Doorn } 5688b2c9824SJohannes Berg if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 5698b2c9824SJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 5708b2c9824SJohannes Berg continue; 5719607e6b6SJohannes Berg if (ieee80211_sdata_running(sdata)) 57247846c9bSJohannes Berg iterator(data, sdata->vif.addr, 5732f561febSIvo van Doorn &sdata->vif); 5742f561febSIvo van Doorn } 5752f561febSIvo van Doorn 576685fb72bSJohannes Berg sdata = rcu_dereference_protected(local->monitor_sdata, 577685fb72bSJohannes Berg lockdep_is_held(&local->iflist_mtx)); 5788b2c9824SJohannes Berg if (sdata && 5798b2c9824SJohannes Berg (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || 5808b2c9824SJohannes Berg sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 581685fb72bSJohannes Berg iterator(data, sdata->vif.addr, &sdata->vif); 582685fb72bSJohannes Berg 583c771c9d8SJohannes Berg mutex_unlock(&local->iflist_mtx); 5842f561febSIvo van Doorn } 5852f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); 5862f561febSIvo van Doorn 5872f561febSIvo van Doorn void ieee80211_iterate_active_interfaces_atomic( 5888b2c9824SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags, 5892f561febSIvo van Doorn void (*iterator)(void *data, u8 *mac, 5902f561febSIvo van Doorn struct ieee80211_vif *vif), 5912f561febSIvo van Doorn void *data) 5922f561febSIvo van Doorn { 5932f561febSIvo van Doorn struct ieee80211_local *local = hw_to_local(hw); 5942f561febSIvo van Doorn struct ieee80211_sub_if_data *sdata; 5952f561febSIvo van Doorn 596e38bad47SJohannes Berg rcu_read_lock(); 597dabeb344SJohannes Berg 598e38bad47SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 59951fb61e7SJohannes Berg switch (sdata->vif.type) { 60005c914feSJohannes Berg case NL80211_IFTYPE_MONITOR: 60105c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 602dabeb344SJohannes Berg continue; 6032ca27bcfSJohannes Berg default: 604dabeb344SJohannes Berg break; 605dabeb344SJohannes Berg } 6068b2c9824SJohannes Berg if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 6078b2c9824SJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 6088b2c9824SJohannes Berg continue; 6099607e6b6SJohannes Berg if (ieee80211_sdata_running(sdata)) 61047846c9bSJohannes Berg iterator(data, sdata->vif.addr, 61132bfd35dSJohannes Berg &sdata->vif); 612dabeb344SJohannes Berg } 613e38bad47SJohannes Berg 614685fb72bSJohannes Berg sdata = rcu_dereference(local->monitor_sdata); 6158b2c9824SJohannes Berg if (sdata && 6168b2c9824SJohannes Berg (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || 6178b2c9824SJohannes Berg sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 618685fb72bSJohannes Berg iterator(data, sdata->vif.addr, &sdata->vif); 619685fb72bSJohannes Berg 620e38bad47SJohannes Berg rcu_read_unlock(); 621dabeb344SJohannes Berg } 6222f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 62337ffc8daSJohannes Berg 62442935ecaSLuis R. Rodriguez /* 62542935ecaSLuis R. Rodriguez * Nothing should have been stuffed into the workqueue during 62642935ecaSLuis R. Rodriguez * the suspend->resume cycle. If this WARN is seen then there 62742935ecaSLuis R. Rodriguez * is a bug with either the driver suspend or something in 62842935ecaSLuis R. Rodriguez * mac80211 stuffing into the workqueue which we haven't yet 62942935ecaSLuis R. Rodriguez * cleared during mac80211's suspend cycle. 63042935ecaSLuis R. Rodriguez */ 63142935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local) 63242935ecaSLuis R. Rodriguez { 633ceb99fe0SJohannes Berg if (WARN(local->suspended && !local->resuming, 634ceb99fe0SJohannes Berg "queueing ieee80211 work while going to suspend\n")) 63542935ecaSLuis R. Rodriguez return false; 63642935ecaSLuis R. Rodriguez 63742935ecaSLuis R. Rodriguez return true; 63842935ecaSLuis R. Rodriguez } 63942935ecaSLuis R. Rodriguez 64042935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) 64142935ecaSLuis R. Rodriguez { 64242935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 64342935ecaSLuis R. Rodriguez 64442935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 64542935ecaSLuis R. Rodriguez return; 64642935ecaSLuis R. Rodriguez 64742935ecaSLuis R. Rodriguez queue_work(local->workqueue, work); 64842935ecaSLuis R. Rodriguez } 64942935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work); 65042935ecaSLuis R. Rodriguez 65142935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, 65242935ecaSLuis R. Rodriguez struct delayed_work *dwork, 65342935ecaSLuis R. Rodriguez unsigned long delay) 65442935ecaSLuis R. Rodriguez { 65542935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw); 65642935ecaSLuis R. Rodriguez 65742935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local)) 65842935ecaSLuis R. Rodriguez return; 65942935ecaSLuis R. Rodriguez 66042935ecaSLuis R. Rodriguez queue_delayed_work(local->workqueue, dwork, delay); 66142935ecaSLuis R. Rodriguez } 66242935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work); 66342935ecaSLuis R. Rodriguez 664b2e506bfSJohannes Berg u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action, 665dd76986bSJohannes Berg struct ieee802_11_elems *elems, 666dd76986bSJohannes Berg u64 filter, u32 crc) 667dd76986bSJohannes Berg { 668dd76986bSJohannes Berg size_t left = len; 669dd76986bSJohannes Berg u8 *pos = start; 670dd76986bSJohannes Berg bool calc_crc = filter != 0; 671fcff4f10SPaul Stewart DECLARE_BITMAP(seen_elems, 256); 672b2e506bfSJohannes Berg const u8 *ie; 673dd76986bSJohannes Berg 674fcff4f10SPaul Stewart bitmap_zero(seen_elems, 256); 675dd76986bSJohannes Berg memset(elems, 0, sizeof(*elems)); 676dd76986bSJohannes Berg elems->ie_start = start; 677dd76986bSJohannes Berg elems->total_len = len; 678dd76986bSJohannes Berg 679dd76986bSJohannes Berg while (left >= 2) { 680dd76986bSJohannes Berg u8 id, elen; 681fcff4f10SPaul Stewart bool elem_parse_failed; 682dd76986bSJohannes Berg 683dd76986bSJohannes Berg id = *pos++; 684dd76986bSJohannes Berg elen = *pos++; 685dd76986bSJohannes Berg left -= 2; 686dd76986bSJohannes Berg 687fcff4f10SPaul Stewart if (elen > left) { 688fcff4f10SPaul Stewart elems->parse_error = true; 689dd76986bSJohannes Berg break; 690fcff4f10SPaul Stewart } 691fcff4f10SPaul Stewart 6929690fb16SJohannes Berg switch (id) { 6939690fb16SJohannes Berg case WLAN_EID_SSID: 6949690fb16SJohannes Berg case WLAN_EID_SUPP_RATES: 6959690fb16SJohannes Berg case WLAN_EID_FH_PARAMS: 6969690fb16SJohannes Berg case WLAN_EID_DS_PARAMS: 6979690fb16SJohannes Berg case WLAN_EID_CF_PARAMS: 6989690fb16SJohannes Berg case WLAN_EID_TIM: 6999690fb16SJohannes Berg case WLAN_EID_IBSS_PARAMS: 7009690fb16SJohannes Berg case WLAN_EID_CHALLENGE: 7019690fb16SJohannes Berg case WLAN_EID_RSN: 7029690fb16SJohannes Berg case WLAN_EID_ERP_INFO: 7039690fb16SJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 7049690fb16SJohannes Berg case WLAN_EID_HT_CAPABILITY: 7059690fb16SJohannes Berg case WLAN_EID_HT_OPERATION: 7069690fb16SJohannes Berg case WLAN_EID_VHT_CAPABILITY: 7079690fb16SJohannes Berg case WLAN_EID_VHT_OPERATION: 7089690fb16SJohannes Berg case WLAN_EID_MESH_ID: 7099690fb16SJohannes Berg case WLAN_EID_MESH_CONFIG: 7109690fb16SJohannes Berg case WLAN_EID_PEER_MGMT: 7119690fb16SJohannes Berg case WLAN_EID_PREQ: 7129690fb16SJohannes Berg case WLAN_EID_PREP: 7139690fb16SJohannes Berg case WLAN_EID_PERR: 7149690fb16SJohannes Berg case WLAN_EID_RANN: 7159690fb16SJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 7169690fb16SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 7179690fb16SJohannes Berg case WLAN_EID_COUNTRY: 7189690fb16SJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 7199690fb16SJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 72085220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 721b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 722b2e506bfSJohannes Berg /* 723b2e506bfSJohannes Berg * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 724b2e506bfSJohannes Berg * that if the content gets bigger it might be needed more than once 725b2e506bfSJohannes Berg */ 7269690fb16SJohannes Berg if (test_bit(id, seen_elems)) { 727fcff4f10SPaul Stewart elems->parse_error = true; 728fcff4f10SPaul Stewart left -= elen; 729fcff4f10SPaul Stewart pos += elen; 730fcff4f10SPaul Stewart continue; 731fcff4f10SPaul Stewart } 7329690fb16SJohannes Berg break; 7339690fb16SJohannes Berg } 734dd76986bSJohannes Berg 735dd76986bSJohannes Berg if (calc_crc && id < 64 && (filter & (1ULL << id))) 736dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 737dd76986bSJohannes Berg 738fcff4f10SPaul Stewart elem_parse_failed = false; 739fcff4f10SPaul Stewart 740dd76986bSJohannes Berg switch (id) { 741dd76986bSJohannes Berg case WLAN_EID_SSID: 742dd76986bSJohannes Berg elems->ssid = pos; 743dd76986bSJohannes Berg elems->ssid_len = elen; 744dd76986bSJohannes Berg break; 745dd76986bSJohannes Berg case WLAN_EID_SUPP_RATES: 746dd76986bSJohannes Berg elems->supp_rates = pos; 747dd76986bSJohannes Berg elems->supp_rates_len = elen; 748dd76986bSJohannes Berg break; 749dd76986bSJohannes Berg case WLAN_EID_DS_PARAMS: 7501cd8e88eSJohannes Berg if (elen >= 1) 751dd76986bSJohannes Berg elems->ds_params = pos; 7521cd8e88eSJohannes Berg else 7531cd8e88eSJohannes Berg elem_parse_failed = true; 754dd76986bSJohannes Berg break; 755dd76986bSJohannes Berg case WLAN_EID_TIM: 756dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_tim_ie)) { 757dd76986bSJohannes Berg elems->tim = (void *)pos; 758dd76986bSJohannes Berg elems->tim_len = elen; 759fcff4f10SPaul Stewart } else 760fcff4f10SPaul Stewart elem_parse_failed = true; 761dd76986bSJohannes Berg break; 762dd76986bSJohannes Berg case WLAN_EID_CHALLENGE: 763dd76986bSJohannes Berg elems->challenge = pos; 764dd76986bSJohannes Berg elems->challenge_len = elen; 765dd76986bSJohannes Berg break; 766dd76986bSJohannes Berg case WLAN_EID_VENDOR_SPECIFIC: 767dd76986bSJohannes Berg if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && 768dd76986bSJohannes Berg pos[2] == 0xf2) { 769dd76986bSJohannes Berg /* Microsoft OUI (00:50:F2) */ 770dd76986bSJohannes Berg 771dd76986bSJohannes Berg if (calc_crc) 772dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2); 773dd76986bSJohannes Berg 774441a33baSJohannes Berg if (elen >= 5 && pos[3] == 2) { 775dd76986bSJohannes Berg /* OUI Type 2 - WMM IE */ 776dd76986bSJohannes Berg if (pos[4] == 0) { 777dd76986bSJohannes Berg elems->wmm_info = pos; 778dd76986bSJohannes Berg elems->wmm_info_len = elen; 779dd76986bSJohannes Berg } else if (pos[4] == 1) { 780dd76986bSJohannes Berg elems->wmm_param = pos; 781dd76986bSJohannes Berg elems->wmm_param_len = elen; 782dd76986bSJohannes Berg } 783dd76986bSJohannes Berg } 784dd76986bSJohannes Berg } 785dd76986bSJohannes Berg break; 786dd76986bSJohannes Berg case WLAN_EID_RSN: 787dd76986bSJohannes Berg elems->rsn = pos; 788dd76986bSJohannes Berg elems->rsn_len = elen; 789dd76986bSJohannes Berg break; 790dd76986bSJohannes Berg case WLAN_EID_ERP_INFO: 7911946bed9SJohannes Berg if (elen >= 1) 792dd76986bSJohannes Berg elems->erp_info = pos; 7931946bed9SJohannes Berg else 7941946bed9SJohannes Berg elem_parse_failed = true; 795dd76986bSJohannes Berg break; 796dd76986bSJohannes Berg case WLAN_EID_EXT_SUPP_RATES: 797dd76986bSJohannes Berg elems->ext_supp_rates = pos; 798dd76986bSJohannes Berg elems->ext_supp_rates_len = elen; 799dd76986bSJohannes Berg break; 800dd76986bSJohannes Berg case WLAN_EID_HT_CAPABILITY: 801dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_ht_cap)) 802dd76986bSJohannes Berg elems->ht_cap_elem = (void *)pos; 803fcff4f10SPaul Stewart else 804fcff4f10SPaul Stewart elem_parse_failed = true; 805dd76986bSJohannes Berg break; 806074d46d1SJohannes Berg case WLAN_EID_HT_OPERATION: 807074d46d1SJohannes Berg if (elen >= sizeof(struct ieee80211_ht_operation)) 808074d46d1SJohannes Berg elems->ht_operation = (void *)pos; 809fcff4f10SPaul Stewart else 810fcff4f10SPaul Stewart elem_parse_failed = true; 811dd76986bSJohannes Berg break; 812818255eaSMahesh Palivela case WLAN_EID_VHT_CAPABILITY: 813818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_cap)) 814818255eaSMahesh Palivela elems->vht_cap_elem = (void *)pos; 815818255eaSMahesh Palivela else 816818255eaSMahesh Palivela elem_parse_failed = true; 817818255eaSMahesh Palivela break; 818818255eaSMahesh Palivela case WLAN_EID_VHT_OPERATION: 819818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_operation)) 820818255eaSMahesh Palivela elems->vht_operation = (void *)pos; 821818255eaSMahesh Palivela else 822818255eaSMahesh Palivela elem_parse_failed = true; 823818255eaSMahesh Palivela break; 824bee7f586SJohannes Berg case WLAN_EID_OPMODE_NOTIF: 825bee7f586SJohannes Berg if (elen > 0) 826bee7f586SJohannes Berg elems->opmode_notif = pos; 827bee7f586SJohannes Berg else 828bee7f586SJohannes Berg elem_parse_failed = true; 829bee7f586SJohannes Berg break; 830dd76986bSJohannes Berg case WLAN_EID_MESH_ID: 831dd76986bSJohannes Berg elems->mesh_id = pos; 832dd76986bSJohannes Berg elems->mesh_id_len = elen; 833dd76986bSJohannes Berg break; 834dd76986bSJohannes Berg case WLAN_EID_MESH_CONFIG: 835dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_meshconf_ie)) 836dd76986bSJohannes Berg elems->mesh_config = (void *)pos; 837fcff4f10SPaul Stewart else 838fcff4f10SPaul Stewart elem_parse_failed = true; 839dd76986bSJohannes Berg break; 840dd76986bSJohannes Berg case WLAN_EID_PEER_MGMT: 841dd76986bSJohannes Berg elems->peering = pos; 842dd76986bSJohannes Berg elems->peering_len = elen; 843dd76986bSJohannes Berg break; 8443f52b7e3SMarco Porsch case WLAN_EID_MESH_AWAKE_WINDOW: 8453f52b7e3SMarco Porsch if (elen >= 2) 8463f52b7e3SMarco Porsch elems->awake_window = (void *)pos; 8473f52b7e3SMarco Porsch break; 848dd76986bSJohannes Berg case WLAN_EID_PREQ: 849dd76986bSJohannes Berg elems->preq = pos; 850dd76986bSJohannes Berg elems->preq_len = elen; 851dd76986bSJohannes Berg break; 852dd76986bSJohannes Berg case WLAN_EID_PREP: 853dd76986bSJohannes Berg elems->prep = pos; 854dd76986bSJohannes Berg elems->prep_len = elen; 855dd76986bSJohannes Berg break; 856dd76986bSJohannes Berg case WLAN_EID_PERR: 857dd76986bSJohannes Berg elems->perr = pos; 858dd76986bSJohannes Berg elems->perr_len = elen; 859dd76986bSJohannes Berg break; 860dd76986bSJohannes Berg case WLAN_EID_RANN: 861dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_rann_ie)) 862dd76986bSJohannes Berg elems->rann = (void *)pos; 863fcff4f10SPaul Stewart else 864fcff4f10SPaul Stewart elem_parse_failed = true; 865dd76986bSJohannes Berg break; 866dd76986bSJohannes Berg case WLAN_EID_CHANNEL_SWITCH: 8675bc1420bSJohannes Berg if (elen != sizeof(struct ieee80211_channel_sw_ie)) { 8685bc1420bSJohannes Berg elem_parse_failed = true; 8695bc1420bSJohannes Berg break; 8705bc1420bSJohannes Berg } 8715bc1420bSJohannes Berg elems->ch_switch_ie = (void *)pos; 872dd76986bSJohannes Berg break; 873b4f286a1SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN: 874b4f286a1SJohannes Berg if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { 875b4f286a1SJohannes Berg elem_parse_failed = true; 876b4f286a1SJohannes Berg break; 877b4f286a1SJohannes Berg } 878b4f286a1SJohannes Berg elems->ext_chansw_ie = (void *)pos; 879b4f286a1SJohannes Berg break; 88085220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 88185220d71SJohannes Berg if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { 88285220d71SJohannes Berg elem_parse_failed = true; 88385220d71SJohannes Berg break; 88485220d71SJohannes Berg } 88585220d71SJohannes Berg elems->sec_chan_offs = (void *)pos; 88685220d71SJohannes Berg break; 887b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 888b2e506bfSJohannes Berg if (!action || 889b2e506bfSJohannes Berg elen != sizeof(*elems->wide_bw_chansw_ie)) { 890b2e506bfSJohannes Berg elem_parse_failed = true; 891b2e506bfSJohannes Berg break; 892b2e506bfSJohannes Berg } 893b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = (void *)pos; 894b2e506bfSJohannes Berg break; 895b2e506bfSJohannes Berg case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 896b2e506bfSJohannes Berg if (action) { 897b2e506bfSJohannes Berg elem_parse_failed = true; 898b2e506bfSJohannes Berg break; 899b2e506bfSJohannes Berg } 900b2e506bfSJohannes Berg /* 901b2e506bfSJohannes Berg * This is a bit tricky, but as we only care about 902b2e506bfSJohannes Berg * the wide bandwidth channel switch element, so 903b2e506bfSJohannes Berg * just parse it out manually. 904b2e506bfSJohannes Berg */ 905b2e506bfSJohannes Berg ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 906b2e506bfSJohannes Berg pos, elen); 907b2e506bfSJohannes Berg if (ie) { 908b2e506bfSJohannes Berg if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) 909b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = 910b2e506bfSJohannes Berg (void *)(ie + 2); 911b2e506bfSJohannes Berg else 912b2e506bfSJohannes Berg elem_parse_failed = true; 913b2e506bfSJohannes Berg } 914b2e506bfSJohannes Berg break; 915dd76986bSJohannes Berg case WLAN_EID_COUNTRY: 916dd76986bSJohannes Berg elems->country_elem = pos; 917dd76986bSJohannes Berg elems->country_elem_len = elen; 918dd76986bSJohannes Berg break; 919dd76986bSJohannes Berg case WLAN_EID_PWR_CONSTRAINT: 920761a48d2SJohannes Berg if (elen != 1) { 921761a48d2SJohannes Berg elem_parse_failed = true; 922761a48d2SJohannes Berg break; 923761a48d2SJohannes Berg } 924dd76986bSJohannes Berg elems->pwr_constr_elem = pos; 925dd76986bSJohannes Berg break; 926dd76986bSJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL: 92779ba1d89SJohannes Berg if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) 92879ba1d89SJohannes Berg elems->timeout_int = (void *)pos; 92979ba1d89SJohannes Berg else 93079ba1d89SJohannes Berg elem_parse_failed = true; 931dd76986bSJohannes Berg break; 932dd76986bSJohannes Berg default: 933dd76986bSJohannes Berg break; 934dd76986bSJohannes Berg } 935dd76986bSJohannes Berg 936fcff4f10SPaul Stewart if (elem_parse_failed) 937fcff4f10SPaul Stewart elems->parse_error = true; 938fcff4f10SPaul Stewart else 9395df45690SJohannes Berg __set_bit(id, seen_elems); 940fcff4f10SPaul Stewart 941dd76986bSJohannes Berg left -= elen; 942dd76986bSJohannes Berg pos += elen; 943dd76986bSJohannes Berg } 944dd76986bSJohannes Berg 945fcff4f10SPaul Stewart if (left != 0) 946fcff4f10SPaul Stewart elems->parse_error = true; 947fcff4f10SPaul Stewart 948dd76986bSJohannes Berg return crc; 949dd76986bSJohannes Berg } 950dd76986bSJohannes Berg 9513abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, 9523abead59SJohannes Berg bool bss_notify) 9535825fe10SJohannes Berg { 9545825fe10SJohannes Berg struct ieee80211_local *local = sdata->local; 9555825fe10SJohannes Berg struct ieee80211_tx_queue_params qparam; 95655de908aSJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 95754bcbc69SJohannes Berg int ac; 958a8ce8544SStanislaw Gruszka bool use_11b, enable_qos; 959aa837e1dSJohannes Berg int aCWmin, aCWmax; 9605825fe10SJohannes Berg 9615825fe10SJohannes Berg if (!local->ops->conf_tx) 9625825fe10SJohannes Berg return; 9635825fe10SJohannes Berg 96454bcbc69SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS) 96554bcbc69SJohannes Berg return; 96654bcbc69SJohannes Berg 9675825fe10SJohannes Berg memset(&qparam, 0, sizeof(qparam)); 9685825fe10SJohannes Berg 96955de908aSJohannes Berg rcu_read_lock(); 97055de908aSJohannes Berg chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 97155de908aSJohannes Berg use_11b = (chanctx_conf && 9724bf88530SJohannes Berg chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) && 973aa837e1dSJohannes Berg !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); 97455de908aSJohannes Berg rcu_read_unlock(); 9755825fe10SJohannes Berg 976a8ce8544SStanislaw Gruszka /* 977a8ce8544SStanislaw Gruszka * By default disable QoS in STA mode for old access points, which do 978a8ce8544SStanislaw Gruszka * not support 802.11e. New APs will provide proper queue parameters, 979a8ce8544SStanislaw Gruszka * that we will configure later. 980a8ce8544SStanislaw Gruszka */ 981a8ce8544SStanislaw Gruszka enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); 982a8ce8544SStanislaw Gruszka 98354bcbc69SJohannes Berg for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 984aa837e1dSJohannes Berg /* Set defaults according to 802.11-2007 Table 7-37 */ 985aa837e1dSJohannes Berg aCWmax = 1023; 986aa837e1dSJohannes Berg if (use_11b) 987aa837e1dSJohannes Berg aCWmin = 31; 9885825fe10SJohannes Berg else 989aa837e1dSJohannes Berg aCWmin = 15; 9905825fe10SJohannes Berg 991a8ce8544SStanislaw Gruszka if (enable_qos) { 99254bcbc69SJohannes Berg switch (ac) { 9931d98fb12SJohannes Berg case IEEE80211_AC_BK: 9947ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 9957ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 9965825fe10SJohannes Berg qparam.txop = 0; 997aa837e1dSJohannes Berg qparam.aifs = 7; 998aa837e1dSJohannes Berg break; 999a8ce8544SStanislaw Gruszka /* never happens but let's not leave undefined */ 1000a8ce8544SStanislaw Gruszka default: 10011d98fb12SJohannes Berg case IEEE80211_AC_BE: 10027ba10a8eSJohannes Berg qparam.cw_max = aCWmax; 10037ba10a8eSJohannes Berg qparam.cw_min = aCWmin; 1004aa837e1dSJohannes Berg qparam.txop = 0; 1005aa837e1dSJohannes Berg qparam.aifs = 3; 1006aa837e1dSJohannes Berg break; 10071d98fb12SJohannes Berg case IEEE80211_AC_VI: 1008aa837e1dSJohannes Berg qparam.cw_max = aCWmin; 1009aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 2 - 1; 1010aa837e1dSJohannes Berg if (use_11b) 1011aa837e1dSJohannes Berg qparam.txop = 6016/32; 1012aa837e1dSJohannes Berg else 1013aa837e1dSJohannes Berg qparam.txop = 3008/32; 1014aa837e1dSJohannes Berg qparam.aifs = 2; 1015aa837e1dSJohannes Berg break; 10161d98fb12SJohannes Berg case IEEE80211_AC_VO: 1017aa837e1dSJohannes Berg qparam.cw_max = (aCWmin + 1) / 2 - 1; 1018aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 4 - 1; 1019aa837e1dSJohannes Berg if (use_11b) 1020aa837e1dSJohannes Berg qparam.txop = 3264/32; 1021aa837e1dSJohannes Berg else 1022aa837e1dSJohannes Berg qparam.txop = 1504/32; 1023aa837e1dSJohannes Berg qparam.aifs = 2; 1024aa837e1dSJohannes Berg break; 1025aa837e1dSJohannes Berg } 1026a8ce8544SStanislaw Gruszka } else { 1027a8ce8544SStanislaw Gruszka /* Confiure old 802.11b/g medium access rules. */ 1028a8ce8544SStanislaw Gruszka qparam.cw_max = aCWmax; 1029a8ce8544SStanislaw Gruszka qparam.cw_min = aCWmin; 1030a8ce8544SStanislaw Gruszka qparam.txop = 0; 1031a8ce8544SStanislaw Gruszka qparam.aifs = 2; 1032a8ce8544SStanislaw Gruszka } 10335825fe10SJohannes Berg 1034ab13315aSKalle Valo qparam.uapsd = false; 1035ab13315aSKalle Valo 103654bcbc69SJohannes Berg sdata->tx_conf[ac] = qparam; 103754bcbc69SJohannes Berg drv_conf_tx(local, sdata, ac, &qparam); 1038aa837e1dSJohannes Berg } 1039e1b3ec1aSStanislaw Gruszka 1040f142c6b9SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_MONITOR && 1041f142c6b9SJohannes Berg sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { 1042a8ce8544SStanislaw Gruszka sdata->vif.bss_conf.qos = enable_qos; 10433abead59SJohannes Berg if (bss_notify) 10443abead59SJohannes Berg ieee80211_bss_info_change_notify(sdata, 10453abead59SJohannes Berg BSS_CHANGED_QOS); 10465825fe10SJohannes Berg } 1047d9734979SSujith } 1048e50db65cSJohannes Berg 104946900298SJohannes Berg void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, 105046900298SJohannes Berg const size_t supp_rates_len, 105146900298SJohannes Berg const u8 *supp_rates) 105246900298SJohannes Berg { 105355de908aSJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 105446900298SJohannes Berg int i, have_higher_than_11mbit = 0; 105546900298SJohannes Berg 105646900298SJohannes Berg /* cf. IEEE 802.11 9.2.12 */ 105746900298SJohannes Berg for (i = 0; i < supp_rates_len; i++) 105846900298SJohannes Berg if ((supp_rates[i] & 0x7f) * 5 > 110) 105946900298SJohannes Berg have_higher_than_11mbit = 1; 106046900298SJohannes Berg 106155de908aSJohannes Berg rcu_read_lock(); 106255de908aSJohannes Berg chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 106355de908aSJohannes Berg 106455de908aSJohannes Berg if (chanctx_conf && 10654bf88530SJohannes Berg chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ && 106646900298SJohannes Berg have_higher_than_11mbit) 106746900298SJohannes Berg sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; 106846900298SJohannes Berg else 106946900298SJohannes Berg sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; 107055de908aSJohannes Berg rcu_read_unlock(); 107146900298SJohannes Berg 10723abead59SJohannes Berg ieee80211_set_wmm_default(sdata, true); 107346900298SJohannes Berg } 107446900298SJohannes Berg 107546900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 1076700e8ea6SJouni Malinen u16 transaction, u16 auth_alg, u16 status, 10774a3cb702SJohannes Berg const u8 *extra, size_t extra_len, const u8 *da, 10781672c0e3SJohannes Berg const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, 10791672c0e3SJohannes Berg u32 tx_flags) 108046900298SJohannes Berg { 108146900298SJohannes Berg struct ieee80211_local *local = sdata->local; 108246900298SJohannes Berg struct sk_buff *skb; 108346900298SJohannes Berg struct ieee80211_mgmt *mgmt; 1084fffd0934SJohannes Berg int err; 108546900298SJohannes Berg 108646900298SJohannes Berg skb = dev_alloc_skb(local->hw.extra_tx_headroom + 108765fc73acSJouni Malinen sizeof(*mgmt) + 6 + extra_len); 1088d15b8459SJoe Perches if (!skb) 108946900298SJohannes Berg return; 1090d15b8459SJoe Perches 109146900298SJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 109246900298SJohannes Berg 109346900298SJohannes Berg mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); 109446900298SJohannes Berg memset(mgmt, 0, 24 + 6); 109546900298SJohannes Berg mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 109646900298SJohannes Berg IEEE80211_STYPE_AUTH); 1097efa6a09dSAntonio Quartulli memcpy(mgmt->da, da, ETH_ALEN); 109847846c9bSJohannes Berg memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 109946900298SJohannes Berg memcpy(mgmt->bssid, bssid, ETH_ALEN); 110046900298SJohannes Berg mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); 110146900298SJohannes Berg mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); 1102700e8ea6SJouni Malinen mgmt->u.auth.status_code = cpu_to_le16(status); 110346900298SJohannes Berg if (extra) 110446900298SJohannes Berg memcpy(skb_put(skb, extra_len), extra, extra_len); 110546900298SJohannes Berg 1106fffd0934SJohannes Berg if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { 1107fffd0934SJohannes Berg mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 1108fffd0934SJohannes Berg err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); 1109fffd0934SJohannes Berg WARN_ON(err); 1110fffd0934SJohannes Berg } 1111fffd0934SJohannes Berg 11121672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 11131672c0e3SJohannes Berg tx_flags; 111462ae67beSJohannes Berg ieee80211_tx_skb(sdata, skb); 111546900298SJohannes Berg } 111646900298SJohannes Berg 11176ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, 11186ae16775SAntonio Quartulli const u8 *bssid, u16 stype, u16 reason, 11196ae16775SAntonio Quartulli bool send_frame, u8 *frame_buf) 11206ae16775SAntonio Quartulli { 11216ae16775SAntonio Quartulli struct ieee80211_local *local = sdata->local; 11226ae16775SAntonio Quartulli struct sk_buff *skb; 11236ae16775SAntonio Quartulli struct ieee80211_mgmt *mgmt = (void *)frame_buf; 11246ae16775SAntonio Quartulli 11256ae16775SAntonio Quartulli /* build frame */ 11266ae16775SAntonio Quartulli mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 11276ae16775SAntonio Quartulli mgmt->duration = 0; /* initialize only */ 11286ae16775SAntonio Quartulli mgmt->seq_ctrl = 0; /* initialize only */ 11296ae16775SAntonio Quartulli memcpy(mgmt->da, bssid, ETH_ALEN); 11306ae16775SAntonio Quartulli memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 11316ae16775SAntonio Quartulli memcpy(mgmt->bssid, bssid, ETH_ALEN); 11326ae16775SAntonio Quartulli /* u.deauth.reason_code == u.disassoc.reason_code */ 11336ae16775SAntonio Quartulli mgmt->u.deauth.reason_code = cpu_to_le16(reason); 11346ae16775SAntonio Quartulli 11356ae16775SAntonio Quartulli if (send_frame) { 11366ae16775SAntonio Quartulli skb = dev_alloc_skb(local->hw.extra_tx_headroom + 11376ae16775SAntonio Quartulli IEEE80211_DEAUTH_FRAME_LEN); 11386ae16775SAntonio Quartulli if (!skb) 11396ae16775SAntonio Quartulli return; 11406ae16775SAntonio Quartulli 11416ae16775SAntonio Quartulli skb_reserve(skb, local->hw.extra_tx_headroom); 11426ae16775SAntonio Quartulli 11436ae16775SAntonio Quartulli /* copy in frame */ 11446ae16775SAntonio Quartulli memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN), 11456ae16775SAntonio Quartulli mgmt, IEEE80211_DEAUTH_FRAME_LEN); 11466ae16775SAntonio Quartulli 11476ae16775SAntonio Quartulli if (sdata->vif.type != NL80211_IFTYPE_STATION || 11486ae16775SAntonio Quartulli !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) 11496ae16775SAntonio Quartulli IEEE80211_SKB_CB(skb)->flags |= 11506ae16775SAntonio Quartulli IEEE80211_TX_INTFL_DONT_ENCRYPT; 11516ae16775SAntonio Quartulli 11526ae16775SAntonio Quartulli ieee80211_tx_skb(sdata, skb); 11536ae16775SAntonio Quartulli } 11546ae16775SAntonio Quartulli } 11556ae16775SAntonio Quartulli 1156de95a54bSJohannes Berg int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, 1157c604b9f2SJohannes Berg size_t buffer_len, const u8 *ie, size_t ie_len, 1158651b5225SJouni Malinen enum ieee80211_band band, u32 rate_mask, 1159651b5225SJouni Malinen u8 channel) 1160de95a54bSJohannes Berg { 1161de95a54bSJohannes Berg struct ieee80211_supported_band *sband; 1162c604b9f2SJohannes Berg u8 *pos = buffer, *end = buffer + buffer_len; 11638e664fb3SJohannes Berg size_t offset = 0, noffset; 11648e664fb3SJohannes Berg int supp_rates_len, i; 11658dcb2003SJouni Malinen u8 rates[32]; 11668dcb2003SJouni Malinen int num_rates; 11678dcb2003SJouni Malinen int ext_rates_len; 1168de95a54bSJohannes Berg 11694d36ec58SJohannes Berg sband = local->hw.wiphy->bands[band]; 1170d811b3d5SArik Nemtsov if (WARN_ON_ONCE(!sband)) 1171d811b3d5SArik Nemtsov return 0; 1172de95a54bSJohannes Berg 11738dcb2003SJouni Malinen num_rates = 0; 11748dcb2003SJouni Malinen for (i = 0; i < sband->n_bitrates; i++) { 11758dcb2003SJouni Malinen if ((BIT(i) & rate_mask) == 0) 11768dcb2003SJouni Malinen continue; /* skip rate */ 11778dcb2003SJouni Malinen rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); 11788dcb2003SJouni Malinen } 11798dcb2003SJouni Malinen 11808dcb2003SJouni Malinen supp_rates_len = min_t(int, num_rates, 8); 11818e664fb3SJohannes Berg 1182c604b9f2SJohannes Berg if (end - pos < 2 + supp_rates_len) 1183c604b9f2SJohannes Berg goto out_err; 1184de95a54bSJohannes Berg *pos++ = WLAN_EID_SUPP_RATES; 11858e664fb3SJohannes Berg *pos++ = supp_rates_len; 11868dcb2003SJouni Malinen memcpy(pos, rates, supp_rates_len); 11878dcb2003SJouni Malinen pos += supp_rates_len; 1188de95a54bSJohannes Berg 11898e664fb3SJohannes Berg /* insert "request information" if in custom IEs */ 11908e664fb3SJohannes Berg if (ie && ie_len) { 11918e664fb3SJohannes Berg static const u8 before_extrates[] = { 11928e664fb3SJohannes Berg WLAN_EID_SSID, 11938e664fb3SJohannes Berg WLAN_EID_SUPP_RATES, 11948e664fb3SJohannes Berg WLAN_EID_REQUEST, 11958e664fb3SJohannes Berg }; 11968e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 11978e664fb3SJohannes Berg before_extrates, 11988e664fb3SJohannes Berg ARRAY_SIZE(before_extrates), 11998e664fb3SJohannes Berg offset); 1200c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1201c604b9f2SJohannes Berg goto out_err; 12028e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12038e664fb3SJohannes Berg pos += noffset - offset; 12048e664fb3SJohannes Berg offset = noffset; 12058e664fb3SJohannes Berg } 12068e664fb3SJohannes Berg 12078dcb2003SJouni Malinen ext_rates_len = num_rates - supp_rates_len; 12088dcb2003SJouni Malinen if (ext_rates_len > 0) { 1209c604b9f2SJohannes Berg if (end - pos < 2 + ext_rates_len) 1210c604b9f2SJohannes Berg goto out_err; 1211de95a54bSJohannes Berg *pos++ = WLAN_EID_EXT_SUPP_RATES; 12128dcb2003SJouni Malinen *pos++ = ext_rates_len; 12138dcb2003SJouni Malinen memcpy(pos, rates + supp_rates_len, ext_rates_len); 12148dcb2003SJouni Malinen pos += ext_rates_len; 12158e664fb3SJohannes Berg } 12168e664fb3SJohannes Berg 1217651b5225SJouni Malinen if (channel && sband->band == IEEE80211_BAND_2GHZ) { 1218c604b9f2SJohannes Berg if (end - pos < 3) 1219c604b9f2SJohannes Berg goto out_err; 1220651b5225SJouni Malinen *pos++ = WLAN_EID_DS_PARAMS; 1221651b5225SJouni Malinen *pos++ = 1; 1222651b5225SJouni Malinen *pos++ = channel; 1223651b5225SJouni Malinen } 1224651b5225SJouni Malinen 12258e664fb3SJohannes Berg /* insert custom IEs that go before HT */ 12268e664fb3SJohannes Berg if (ie && ie_len) { 12278e664fb3SJohannes Berg static const u8 before_ht[] = { 12288e664fb3SJohannes Berg WLAN_EID_SSID, 12298e664fb3SJohannes Berg WLAN_EID_SUPP_RATES, 12308e664fb3SJohannes Berg WLAN_EID_REQUEST, 12318e664fb3SJohannes Berg WLAN_EID_EXT_SUPP_RATES, 12328e664fb3SJohannes Berg WLAN_EID_DS_PARAMS, 12338e664fb3SJohannes Berg WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 12348e664fb3SJohannes Berg }; 12358e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len, 12368e664fb3SJohannes Berg before_ht, ARRAY_SIZE(before_ht), 12378e664fb3SJohannes Berg offset); 1238c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1239c604b9f2SJohannes Berg goto out_err; 12408e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12418e664fb3SJohannes Berg pos += noffset - offset; 12428e664fb3SJohannes Berg offset = noffset; 1243de95a54bSJohannes Berg } 1244de95a54bSJohannes Berg 1245c604b9f2SJohannes Berg if (sband->ht_cap.ht_supported) { 1246c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) 1247c604b9f2SJohannes Berg goto out_err; 1248ef96a842SBen Greear pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, 1249ef96a842SBen Greear sband->ht_cap.cap); 1250c604b9f2SJohannes Berg } 12515ef2d41aSJohannes Berg 1252de95a54bSJohannes Berg /* 1253de95a54bSJohannes Berg * If adding more here, adjust code in main.c 1254de95a54bSJohannes Berg * that calculates local->scan_ies_len. 1255de95a54bSJohannes Berg */ 1256de95a54bSJohannes Berg 12578e664fb3SJohannes Berg /* add any remaining custom IEs */ 12588e664fb3SJohannes Berg if (ie && ie_len) { 12598e664fb3SJohannes Berg noffset = ie_len; 1260c604b9f2SJohannes Berg if (end - pos < noffset - offset) 1261c604b9f2SJohannes Berg goto out_err; 12628e664fb3SJohannes Berg memcpy(pos, ie + offset, noffset - offset); 12638e664fb3SJohannes Berg pos += noffset - offset; 1264de95a54bSJohannes Berg } 1265de95a54bSJohannes Berg 1266c604b9f2SJohannes Berg if (sband->vht_cap.vht_supported) { 1267c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) 1268c604b9f2SJohannes Berg goto out_err; 1269ba0afa2fSMahesh Palivela pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, 1270ba0afa2fSMahesh Palivela sband->vht_cap.cap); 1271c604b9f2SJohannes Berg } 1272ba0afa2fSMahesh Palivela 1273de95a54bSJohannes Berg return pos - buffer; 1274c604b9f2SJohannes Berg out_err: 1275c604b9f2SJohannes Berg WARN_ONCE(1, "not enough space for preq IEs\n"); 1276c604b9f2SJohannes Berg return pos - buffer; 1277de95a54bSJohannes Berg } 1278de95a54bSJohannes Berg 1279a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, 128085a237feSJohannes Berg u8 *dst, u32 ratemask, 12816b77863bSJohannes Berg struct ieee80211_channel *chan, 1282de95a54bSJohannes Berg const u8 *ssid, size_t ssid_len, 1283a806c558SPaul Stewart const u8 *ie, size_t ie_len, 1284a806c558SPaul Stewart bool directed) 128546900298SJohannes Berg { 128646900298SJohannes Berg struct ieee80211_local *local = sdata->local; 128746900298SJohannes Berg struct sk_buff *skb; 128846900298SJohannes Berg struct ieee80211_mgmt *mgmt; 12896b77863bSJohannes Berg u8 chan_no; 1290b9a9ada1SJohannes Berg int ies_len; 129146900298SJohannes Berg 1292a806c558SPaul Stewart /* 1293a806c558SPaul Stewart * Do not send DS Channel parameter for directed probe requests 1294a806c558SPaul Stewart * in order to maximize the chance that we get a response. Some 1295a806c558SPaul Stewart * badly-behaved APs don't respond when this parameter is included. 1296a806c558SPaul Stewart */ 1297a806c558SPaul Stewart if (directed) 12986b77863bSJohannes Berg chan_no = 0; 1299a806c558SPaul Stewart else 13006b77863bSJohannes Berg chan_no = ieee80211_frequency_to_channel(chan->center_freq); 1301651b5225SJouni Malinen 13027c12ce8bSKalle Valo skb = ieee80211_probereq_get(&local->hw, &sdata->vif, 1303b9a9ada1SJohannes Berg ssid, ssid_len, 100 + ie_len); 13045b2bbf75SJohannes Berg if (!skb) 1305b9a9ada1SJohannes Berg return NULL; 1306b9a9ada1SJohannes Berg 1307b9a9ada1SJohannes Berg ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), 1308b9a9ada1SJohannes Berg skb_tailroom(skb), 1309c604b9f2SJohannes Berg ie, ie_len, chan->band, 131062ae67beSJohannes Berg ratemask, chan_no); 1311b9a9ada1SJohannes Berg skb_put(skb, ies_len); 131246900298SJohannes Berg 131346900298SJohannes Berg if (dst) { 131446900298SJohannes Berg mgmt = (struct ieee80211_mgmt *) skb->data; 131546900298SJohannes Berg memcpy(mgmt->da, dst, ETH_ALEN); 131646900298SJohannes Berg memcpy(mgmt->bssid, dst, ETH_ALEN); 131746900298SJohannes Berg } 131846900298SJohannes Berg 131946900298SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 13205b2bbf75SJohannes Berg 1321a619a4c0SJuuso Oikarinen return skb; 1322a619a4c0SJuuso Oikarinen } 1323a619a4c0SJuuso Oikarinen 1324a619a4c0SJuuso Oikarinen void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, 1325a619a4c0SJuuso Oikarinen const u8 *ssid, size_t ssid_len, 1326a806c558SPaul Stewart const u8 *ie, size_t ie_len, 13271672c0e3SJohannes Berg u32 ratemask, bool directed, u32 tx_flags, 132855de908aSJohannes Berg struct ieee80211_channel *channel, bool scan) 1329a619a4c0SJuuso Oikarinen { 1330a619a4c0SJuuso Oikarinen struct sk_buff *skb; 1331a619a4c0SJuuso Oikarinen 1332fe94fe05SJohannes Berg skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, 13336b77863bSJohannes Berg ssid, ssid_len, 133485a237feSJohannes Berg ie, ie_len, directed); 1335aad14cebSRajkumar Manoharan if (skb) { 13361672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= tx_flags; 133755de908aSJohannes Berg if (scan) 133855de908aSJohannes Berg ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band); 133955de908aSJohannes Berg else 1340a619a4c0SJuuso Oikarinen ieee80211_tx_skb(sdata, skb); 134146900298SJohannes Berg } 1342aad14cebSRajkumar Manoharan } 134346900298SJohannes Berg 134446900298SJohannes Berg u32 ieee80211_sta_get_rates(struct ieee80211_local *local, 134546900298SJohannes Berg struct ieee802_11_elems *elems, 13469ebb61a2SAshok Nagarajan enum ieee80211_band band, u32 *basic_rates) 134746900298SJohannes Berg { 134846900298SJohannes Berg struct ieee80211_supported_band *sband; 134946900298SJohannes Berg struct ieee80211_rate *bitrates; 135046900298SJohannes Berg size_t num_rates; 135146900298SJohannes Berg u32 supp_rates; 135246900298SJohannes Berg int i, j; 135346900298SJohannes Berg sband = local->hw.wiphy->bands[band]; 135446900298SJohannes Berg 13554ee73f33SMichal Kazior if (WARN_ON(!sband)) 13564ee73f33SMichal Kazior return 1; 135746900298SJohannes Berg 135846900298SJohannes Berg bitrates = sband->bitrates; 135946900298SJohannes Berg num_rates = sband->n_bitrates; 136046900298SJohannes Berg supp_rates = 0; 136146900298SJohannes Berg for (i = 0; i < elems->supp_rates_len + 136246900298SJohannes Berg elems->ext_supp_rates_len; i++) { 136346900298SJohannes Berg u8 rate = 0; 136446900298SJohannes Berg int own_rate; 13659ebb61a2SAshok Nagarajan bool is_basic; 136646900298SJohannes Berg if (i < elems->supp_rates_len) 136746900298SJohannes Berg rate = elems->supp_rates[i]; 136846900298SJohannes Berg else if (elems->ext_supp_rates) 136946900298SJohannes Berg rate = elems->ext_supp_rates 137046900298SJohannes Berg [i - elems->supp_rates_len]; 137146900298SJohannes Berg own_rate = 5 * (rate & 0x7f); 13729ebb61a2SAshok Nagarajan is_basic = !!(rate & 0x80); 13739ebb61a2SAshok Nagarajan 13749ebb61a2SAshok Nagarajan if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) 13759ebb61a2SAshok Nagarajan continue; 13769ebb61a2SAshok Nagarajan 13779ebb61a2SAshok Nagarajan for (j = 0; j < num_rates; j++) { 13789ebb61a2SAshok Nagarajan if (bitrates[j].bitrate == own_rate) { 137946900298SJohannes Berg supp_rates |= BIT(j); 13809ebb61a2SAshok Nagarajan if (basic_rates && is_basic) 13819ebb61a2SAshok Nagarajan *basic_rates |= BIT(j); 13829ebb61a2SAshok Nagarajan } 13839ebb61a2SAshok Nagarajan } 138446900298SJohannes Berg } 138546900298SJohannes Berg return supp_rates; 138646900298SJohannes Berg } 1387f2753ddbSJohannes Berg 138884f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local) 138984f6a01cSJohannes Berg { 139084f6a01cSJohannes Berg ieee80211_led_radio(local, false); 139167408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); 139284f6a01cSJohannes Berg 139384f6a01cSJohannes Berg cancel_work_sync(&local->reconfig_filter); 139484f6a01cSJohannes Berg 139584f6a01cSJohannes Berg flush_workqueue(local->workqueue); 1396678f415fSLennert Buytenhek drv_stop(local); 139784f6a01cSJohannes Berg } 139884f6a01cSJohannes Berg 1399153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local, 1400153a5fc4SStanislaw Gruszka struct ieee80211_sub_if_data *sdata) 1401153a5fc4SStanislaw Gruszka { 1402153a5fc4SStanislaw Gruszka struct ieee80211_chanctx_conf *conf; 1403153a5fc4SStanislaw Gruszka struct ieee80211_chanctx *ctx; 1404153a5fc4SStanislaw Gruszka 1405153a5fc4SStanislaw Gruszka if (!local->use_chanctx) 1406153a5fc4SStanislaw Gruszka return; 1407153a5fc4SStanislaw Gruszka 1408153a5fc4SStanislaw Gruszka mutex_lock(&local->chanctx_mtx); 1409153a5fc4SStanislaw Gruszka conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1410153a5fc4SStanislaw Gruszka lockdep_is_held(&local->chanctx_mtx)); 1411153a5fc4SStanislaw Gruszka if (conf) { 1412153a5fc4SStanislaw Gruszka ctx = container_of(conf, struct ieee80211_chanctx, conf); 1413153a5fc4SStanislaw Gruszka drv_assign_vif_chanctx(local, sdata, ctx); 1414153a5fc4SStanislaw Gruszka } 1415153a5fc4SStanislaw Gruszka mutex_unlock(&local->chanctx_mtx); 1416153a5fc4SStanislaw Gruszka } 1417153a5fc4SStanislaw Gruszka 1418f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local) 1419f2753ddbSJohannes Berg { 1420f2753ddbSJohannes Berg struct ieee80211_hw *hw = &local->hw; 1421f2753ddbSJohannes Berg struct ieee80211_sub_if_data *sdata; 142255de908aSJohannes Berg struct ieee80211_chanctx *ctx; 1423f2753ddbSJohannes Berg struct sta_info *sta; 14242683d65bSEliad Peller int res, i; 1425d888130aSJohannes Berg bool reconfig_due_to_wowlan = false; 1426d888130aSJohannes Berg 14278f21b0adSJohannes Berg #ifdef CONFIG_PM 1428ceb99fe0SJohannes Berg if (local->suspended) 1429ceb99fe0SJohannes Berg local->resuming = true; 1430f2753ddbSJohannes Berg 1431eecc4800SJohannes Berg if (local->wowlan) { 1432eecc4800SJohannes Berg local->wowlan = false; 1433eecc4800SJohannes Berg res = drv_resume(local); 1434eecc4800SJohannes Berg if (res < 0) { 1435eecc4800SJohannes Berg local->resuming = false; 1436eecc4800SJohannes Berg return res; 1437eecc4800SJohannes Berg } 1438eecc4800SJohannes Berg if (res == 0) 1439eecc4800SJohannes Berg goto wake_up; 1440eecc4800SJohannes Berg WARN_ON(res > 1); 1441eecc4800SJohannes Berg /* 1442eecc4800SJohannes Berg * res is 1, which means the driver requested 1443eecc4800SJohannes Berg * to go through a regular reset on wakeup. 1444eecc4800SJohannes Berg */ 1445d888130aSJohannes Berg reconfig_due_to_wowlan = true; 1446eecc4800SJohannes Berg } 1447eecc4800SJohannes Berg #endif 144894f9b97bSJohannes Berg /* everything else happens only if HW was up & running */ 144994f9b97bSJohannes Berg if (!local->open_count) 145094f9b97bSJohannes Berg goto wake_up; 145194f9b97bSJohannes Berg 145224feda00SLuis R. Rodriguez /* 145324feda00SLuis R. Rodriguez * Upon resume hardware can sometimes be goofy due to 145424feda00SLuis R. Rodriguez * various platform / driver / bus issues, so restarting 145524feda00SLuis R. Rodriguez * the device may at times not work immediately. Propagate 145624feda00SLuis R. Rodriguez * the error. 145724feda00SLuis R. Rodriguez */ 145824487981SJohannes Berg res = drv_start(local); 145924feda00SLuis R. Rodriguez if (res) { 1460c7a00dc7SJohn W. Linville WARN(local->suspended, "Hardware became unavailable " 1461c7a00dc7SJohn W. Linville "upon resume. This could be a software issue " 1462c7a00dc7SJohn W. Linville "prior to suspend or a hardware issue.\n"); 146324feda00SLuis R. Rodriguez return res; 146424feda00SLuis R. Rodriguez } 1465f2753ddbSJohannes Berg 14667f281975SYogesh Ashok Powar /* setup fragmentation threshold */ 14677f281975SYogesh Ashok Powar drv_set_frag_threshold(local, hw->wiphy->frag_threshold); 14687f281975SYogesh Ashok Powar 14697f281975SYogesh Ashok Powar /* setup RTS threshold */ 14707f281975SYogesh Ashok Powar drv_set_rts_threshold(local, hw->wiphy->rts_threshold); 14717f281975SYogesh Ashok Powar 14727f281975SYogesh Ashok Powar /* reset coverage class */ 14737f281975SYogesh Ashok Powar drv_set_coverage_class(local, hw->wiphy->coverage_class); 14747f281975SYogesh Ashok Powar 14751f87f7d3SJohannes Berg ieee80211_led_radio(local, true); 147667408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 147767408c8cSJohannes Berg IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); 1478f2753ddbSJohannes Berg 1479f2753ddbSJohannes Berg /* add interfaces */ 14804b6f1dd6SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 14814b6f1dd6SJohannes Berg if (sdata) { 14823c3e21e7SJohannes Berg /* in HW restart it exists already */ 14833c3e21e7SJohannes Berg WARN_ON(local->resuming); 14844b6f1dd6SJohannes Berg res = drv_add_interface(local, sdata); 14854b6f1dd6SJohannes Berg if (WARN_ON(res)) { 14864b6f1dd6SJohannes Berg rcu_assign_pointer(local->monitor_sdata, NULL); 14874b6f1dd6SJohannes Berg synchronize_net(); 14884b6f1dd6SJohannes Berg kfree(sdata); 14894b6f1dd6SJohannes Berg } 14904b6f1dd6SJohannes Berg } 14914b6f1dd6SJohannes Berg 1492f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1493f2753ddbSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 1494f2753ddbSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR && 14951ed32e4fSJohannes Berg ieee80211_sdata_running(sdata)) 14967b7eab6fSJohannes Berg res = drv_add_interface(local, sdata); 1497f2753ddbSJohannes Berg } 1498f2753ddbSJohannes Berg 149955de908aSJohannes Berg /* add channel contexts */ 1500f0dea9c7SArend van Spriel if (local->use_chanctx) { 150155de908aSJohannes Berg mutex_lock(&local->chanctx_mtx); 150255de908aSJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list) 150355de908aSJohannes Berg WARN_ON(drv_add_chanctx(local, ctx)); 150455de908aSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1505f0dea9c7SArend van Spriel } 150655de908aSJohannes Berg 15076352c87fSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 15086352c87fSJohannes Berg if (!ieee80211_sdata_running(sdata)) 15096352c87fSJohannes Berg continue; 1510153a5fc4SStanislaw Gruszka ieee80211_assign_chanctx(local, sdata); 15116352c87fSJohannes Berg } 15126352c87fSJohannes Berg 1513fe5f2559SJohannes Berg sdata = rtnl_dereference(local->monitor_sdata); 1514153a5fc4SStanislaw Gruszka if (sdata && ieee80211_sdata_running(sdata)) 1515153a5fc4SStanislaw Gruszka ieee80211_assign_chanctx(local, sdata); 1516fe5f2559SJohannes Berg 1517f2753ddbSJohannes Berg /* add STAs back */ 151834e89507SJohannes Berg mutex_lock(&local->sta_mtx); 1519f2753ddbSJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 1520f09603a2SJohannes Berg enum ieee80211_sta_state state; 1521f09603a2SJohannes Berg 15222e8d397eSArik Nemtsov if (!sta->uploaded) 15232e8d397eSArik Nemtsov continue; 15242e8d397eSArik Nemtsov 15252e8d397eSArik Nemtsov /* AP-mode stations will be added later */ 15262e8d397eSArik Nemtsov if (sta->sdata->vif.type == NL80211_IFTYPE_AP) 15272e8d397eSArik Nemtsov continue; 15282e8d397eSArik Nemtsov 1529f09603a2SJohannes Berg for (state = IEEE80211_STA_NOTEXIST; 1530bd34ab62SMeenakshi Venkataraman state < sta->sta_state; state++) 15312e8d397eSArik Nemtsov WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 15322e8d397eSArik Nemtsov state + 1)); 1533f2753ddbSJohannes Berg } 153434e89507SJohannes Berg mutex_unlock(&local->sta_mtx); 1535f2753ddbSJohannes Berg 15362683d65bSEliad Peller /* reconfigure tx conf */ 153754bcbc69SJohannes Berg if (hw->queues >= IEEE80211_NUM_ACS) { 1538f6f3def3SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) { 1539f6f3def3SEliad Peller if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 1540f6f3def3SEliad Peller sdata->vif.type == NL80211_IFTYPE_MONITOR || 1541f6f3def3SEliad Peller !ieee80211_sdata_running(sdata)) 1542f6f3def3SEliad Peller continue; 1543f6f3def3SEliad Peller 154454bcbc69SJohannes Berg for (i = 0; i < IEEE80211_NUM_ACS; i++) 154554bcbc69SJohannes Berg drv_conf_tx(local, sdata, i, 154654bcbc69SJohannes Berg &sdata->tx_conf[i]); 154754bcbc69SJohannes Berg } 1548f6f3def3SEliad Peller } 15492683d65bSEliad Peller 1550f2753ddbSJohannes Berg /* reconfigure hardware */ 1551f2753ddbSJohannes Berg ieee80211_hw_config(local, ~0); 1552f2753ddbSJohannes Berg 1553f2753ddbSJohannes Berg ieee80211_configure_filter(local); 1554f2753ddbSJohannes Berg 1555f2753ddbSJohannes Berg /* Finally also reconfigure all the BSS information */ 1556f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1557ac8dd506SJohannes Berg u32 changed; 1558ac8dd506SJohannes Berg 15599607e6b6SJohannes Berg if (!ieee80211_sdata_running(sdata)) 1560f2753ddbSJohannes Berg continue; 1561ac8dd506SJohannes Berg 1562ac8dd506SJohannes Berg /* common change flags for all interface types */ 1563ac8dd506SJohannes Berg changed = BSS_CHANGED_ERP_CTS_PROT | 1564ac8dd506SJohannes Berg BSS_CHANGED_ERP_PREAMBLE | 1565ac8dd506SJohannes Berg BSS_CHANGED_ERP_SLOT | 1566ac8dd506SJohannes Berg BSS_CHANGED_HT | 1567ac8dd506SJohannes Berg BSS_CHANGED_BASIC_RATES | 1568ac8dd506SJohannes Berg BSS_CHANGED_BEACON_INT | 1569ac8dd506SJohannes Berg BSS_CHANGED_BSSID | 15704ced3f74SJohannes Berg BSS_CHANGED_CQM | 157155de47f6SEliad Peller BSS_CHANGED_QOS | 15721ea6f9c0SJohannes Berg BSS_CHANGED_IDLE | 15731ea6f9c0SJohannes Berg BSS_CHANGED_TXPOWER; 1574ac8dd506SJohannes Berg 1575f2753ddbSJohannes Berg switch (sdata->vif.type) { 1576f2753ddbSJohannes Berg case NL80211_IFTYPE_STATION: 15770d392e93SEliad Peller changed |= BSS_CHANGED_ASSOC | 1578ab095877SEliad Peller BSS_CHANGED_ARP_FILTER | 1579ab095877SEliad Peller BSS_CHANGED_PS; 1580c65dd147SEmmanuel Grumbach 1581c65dd147SEmmanuel Grumbach if (sdata->u.mgd.dtim_period) 1582c65dd147SEmmanuel Grumbach changed |= BSS_CHANGED_DTIM_PERIOD; 1583c65dd147SEmmanuel Grumbach 15848d61ffa5SJohannes Berg sdata_lock(sdata); 1585ac8dd506SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 15868d61ffa5SJohannes Berg sdata_unlock(sdata); 1587ac8dd506SJohannes Berg break; 1588f2753ddbSJohannes Berg case NL80211_IFTYPE_ADHOC: 1589ac8dd506SJohannes Berg changed |= BSS_CHANGED_IBSS; 1590ac8dd506SJohannes Berg /* fall through */ 1591f2753ddbSJohannes Berg case NL80211_IFTYPE_AP: 1592339afbf4SJohannes Berg changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; 1593e7979ac7SArik Nemtsov 15941041638fSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP) { 1595e7979ac7SArik Nemtsov changed |= BSS_CHANGED_AP_PROBE_RESP; 1596e7979ac7SArik Nemtsov 15971041638fSJohannes Berg if (rcu_access_pointer(sdata->u.ap.beacon)) 15981041638fSJohannes Berg drv_start_ap(local, sdata); 15991041638fSJohannes Berg } 16001041638fSJohannes Berg 16017827493bSArik Nemtsov /* fall through */ 1602f2753ddbSJohannes Berg case NL80211_IFTYPE_MESH_POINT: 16038da34932SJohannes Berg if (sdata->vif.bss_conf.enable_beacon) { 1604ac8dd506SJohannes Berg changed |= BSS_CHANGED_BEACON | 1605ac8dd506SJohannes Berg BSS_CHANGED_BEACON_ENABLED; 16062d0ddec5SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed); 16078da34932SJohannes Berg } 1608f2753ddbSJohannes Berg break; 1609f2753ddbSJohannes Berg case NL80211_IFTYPE_WDS: 1610f2753ddbSJohannes Berg break; 1611f2753ddbSJohannes Berg case NL80211_IFTYPE_AP_VLAN: 1612f2753ddbSJohannes Berg case NL80211_IFTYPE_MONITOR: 1613f2753ddbSJohannes Berg /* ignore virtual */ 1614f2753ddbSJohannes Berg break; 161598104fdeSJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 1616f142c6b9SJohannes Berg changed = BSS_CHANGED_IDLE; 1617f142c6b9SJohannes Berg break; 1618f2753ddbSJohannes Berg case NL80211_IFTYPE_UNSPECIFIED: 16192e161f78SJohannes Berg case NUM_NL80211_IFTYPES: 16202ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_CLIENT: 16212ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_GO: 1622f2753ddbSJohannes Berg WARN_ON(1); 1623f2753ddbSJohannes Berg break; 1624f2753ddbSJohannes Berg } 1625f2753ddbSJohannes Berg } 1626f2753ddbSJohannes Berg 16278e1b23b9SEyal Shapira ieee80211_recalc_ps(local, -1); 16288e1b23b9SEyal Shapira 16292a419056SJohannes Berg /* 16306e1b1b24SEliad Peller * The sta might be in psm against the ap (e.g. because 16316e1b1b24SEliad Peller * this was the state before a hw restart), so we 16326e1b1b24SEliad Peller * explicitly send a null packet in order to make sure 16336e1b1b24SEliad Peller * it'll sync against the ap (and get out of psm). 16346e1b1b24SEliad Peller */ 16356e1b1b24SEliad Peller if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { 16366e1b1b24SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) { 16376e1b1b24SEliad Peller if (sdata->vif.type != NL80211_IFTYPE_STATION) 16386e1b1b24SEliad Peller continue; 163920f544eeSJohannes Berg if (!sdata->u.mgd.associated) 164020f544eeSJohannes Berg continue; 16416e1b1b24SEliad Peller 16426e1b1b24SEliad Peller ieee80211_send_nullfunc(local, sdata, 0); 16436e1b1b24SEliad Peller } 16446e1b1b24SEliad Peller } 16456e1b1b24SEliad Peller 16462e8d397eSArik Nemtsov /* APs are now beaconing, add back stations */ 16472e8d397eSArik Nemtsov mutex_lock(&local->sta_mtx); 16482e8d397eSArik Nemtsov list_for_each_entry(sta, &local->sta_list, list) { 16492e8d397eSArik Nemtsov enum ieee80211_sta_state state; 16502e8d397eSArik Nemtsov 16512e8d397eSArik Nemtsov if (!sta->uploaded) 16522e8d397eSArik Nemtsov continue; 16532e8d397eSArik Nemtsov 16542e8d397eSArik Nemtsov if (sta->sdata->vif.type != NL80211_IFTYPE_AP) 16552e8d397eSArik Nemtsov continue; 16562e8d397eSArik Nemtsov 16572e8d397eSArik Nemtsov for (state = IEEE80211_STA_NOTEXIST; 16582e8d397eSArik Nemtsov state < sta->sta_state; state++) 16592e8d397eSArik Nemtsov WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 16602e8d397eSArik Nemtsov state + 1)); 16612e8d397eSArik Nemtsov } 16622e8d397eSArik Nemtsov mutex_unlock(&local->sta_mtx); 16632e8d397eSArik Nemtsov 16647b21aea0SEyal Shapira /* add back keys */ 16657b21aea0SEyal Shapira list_for_each_entry(sdata, &local->interfaces, list) 16667b21aea0SEyal Shapira if (ieee80211_sdata_running(sdata)) 16677b21aea0SEyal Shapira ieee80211_enable_keys(sdata); 16687b21aea0SEyal Shapira 1669c6209488SEliad Peller wake_up: 167004800adaSArik Nemtsov local->in_reconfig = false; 167104800adaSArik Nemtsov barrier(); 167204800adaSArik Nemtsov 16733c3e21e7SJohannes Berg if (local->monitors == local->open_count && local->monitors > 0) 16743c3e21e7SJohannes Berg ieee80211_add_virtual_monitor(local); 16753c3e21e7SJohannes Berg 16766e1b1b24SEliad Peller /* 16772a419056SJohannes Berg * Clear the WLAN_STA_BLOCK_BA flag so new aggregation 16782a419056SJohannes Berg * sessions can be established after a resume. 16792a419056SJohannes Berg * 16802a419056SJohannes Berg * Also tear down aggregation sessions since reconfiguring 16812a419056SJohannes Berg * them in a hardware restart scenario is not easily done 16822a419056SJohannes Berg * right now, and the hardware will have lost information 16832a419056SJohannes Berg * about the sessions, but we and the AP still think they 16842a419056SJohannes Berg * are active. This is really a workaround though. 16852a419056SJohannes Berg */ 168674e2bd1fSWey-Yi Guy if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { 16872a419056SJohannes Berg mutex_lock(&local->sta_mtx); 16882a419056SJohannes Berg 16892a419056SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 1690c82c4a80SJohannes Berg ieee80211_sta_tear_down_BA_sessions( 1691c82c4a80SJohannes Berg sta, AGG_STOP_LOCAL_REQUEST); 1692c2c98fdeSJohannes Berg clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 169374e2bd1fSWey-Yi Guy } 16942a419056SJohannes Berg 16952a419056SJohannes Berg mutex_unlock(&local->sta_mtx); 169674e2bd1fSWey-Yi Guy } 169774e2bd1fSWey-Yi Guy 1698445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 1699f2753ddbSJohannes Berg IEEE80211_QUEUE_STOP_REASON_SUSPEND); 1700f2753ddbSJohannes Berg 17015bb644a0SJohannes Berg /* 17025bb644a0SJohannes Berg * If this is for hw restart things are still running. 17035bb644a0SJohannes Berg * We may want to change that later, however. 17045bb644a0SJohannes Berg */ 17058f21b0adSJohannes Berg if (!local->suspended || reconfig_due_to_wowlan) 17069214ad7fSJohannes Berg drv_restart_complete(local); 17078f21b0adSJohannes Berg 17088f21b0adSJohannes Berg if (!local->suspended) 17095bb644a0SJohannes Berg return 0; 17105bb644a0SJohannes Berg 17115bb644a0SJohannes Berg #ifdef CONFIG_PM 1712ceb99fe0SJohannes Berg /* first set suspended false, then resuming */ 17135bb644a0SJohannes Berg local->suspended = false; 1714ceb99fe0SJohannes Berg mb(); 1715ceb99fe0SJohannes Berg local->resuming = false; 17165bb644a0SJohannes Berg 1717b8360ab8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) { 1718b8360ab8SJohannes Berg if (!ieee80211_sdata_running(sdata)) 1719b8360ab8SJohannes Berg continue; 1720b8360ab8SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION) 1721b8360ab8SJohannes Berg ieee80211_sta_restart(sdata); 1722b8360ab8SJohannes Berg } 1723b8360ab8SJohannes Berg 172426d59535SJohannes Berg mod_timer(&local->sta_cleanup, jiffies + 1); 17255bb644a0SJohannes Berg #else 17265bb644a0SJohannes Berg WARN_ON(1); 17275bb644a0SJohannes Berg #endif 1728f2753ddbSJohannes Berg return 0; 1729f2753ddbSJohannes Berg } 173042935ecaSLuis R. Rodriguez 173195acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif) 173295acac61SJohannes Berg { 173395acac61SJohannes Berg struct ieee80211_sub_if_data *sdata; 173495acac61SJohannes Berg struct ieee80211_local *local; 173595acac61SJohannes Berg struct ieee80211_key *key; 173695acac61SJohannes Berg 173795acac61SJohannes Berg if (WARN_ON(!vif)) 173895acac61SJohannes Berg return; 173995acac61SJohannes Berg 174095acac61SJohannes Berg sdata = vif_to_sdata(vif); 174195acac61SJohannes Berg local = sdata->local; 174295acac61SJohannes Berg 174395acac61SJohannes Berg if (WARN_ON(!local->resuming)) 174495acac61SJohannes Berg return; 174595acac61SJohannes Berg 174695acac61SJohannes Berg if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 174795acac61SJohannes Berg return; 174895acac61SJohannes Berg 174995acac61SJohannes Berg sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; 175095acac61SJohannes Berg 175195acac61SJohannes Berg mutex_lock(&local->key_mtx); 175295acac61SJohannes Berg list_for_each_entry(key, &sdata->key_list, list) 175395acac61SJohannes Berg key->flags |= KEY_FLAG_TAINTED; 175495acac61SJohannes Berg mutex_unlock(&local->key_mtx); 175595acac61SJohannes Berg } 175695acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 175795acac61SJohannes Berg 175804ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) 17590f78231bSJohannes Berg { 176004ecd257SJohannes Berg struct ieee80211_local *local = sdata->local; 176104ecd257SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf; 176204ecd257SJohannes Berg struct ieee80211_chanctx *chanctx; 17630f78231bSJohannes Berg 176404ecd257SJohannes Berg mutex_lock(&local->chanctx_mtx); 17650f78231bSJohannes Berg 176604ecd257SJohannes Berg chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 176704ecd257SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17680f78231bSJohannes Berg 176904ecd257SJohannes Berg if (WARN_ON_ONCE(!chanctx_conf)) 17705d8e4237SJohannes Berg goto unlock; 17710f78231bSJohannes Berg 177204ecd257SJohannes Berg chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 177304ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, chanctx); 17745d8e4237SJohannes Berg unlock: 177504ecd257SJohannes Berg mutex_unlock(&local->chanctx_mtx); 17760f78231bSJohannes Berg } 17778e664fb3SJohannes Berg 17788e664fb3SJohannes Berg static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) 17798e664fb3SJohannes Berg { 17808e664fb3SJohannes Berg int i; 17818e664fb3SJohannes Berg 17828e664fb3SJohannes Berg for (i = 0; i < n_ids; i++) 17838e664fb3SJohannes Berg if (ids[i] == id) 17848e664fb3SJohannes Berg return true; 17858e664fb3SJohannes Berg return false; 17868e664fb3SJohannes Berg } 17878e664fb3SJohannes Berg 17888e664fb3SJohannes Berg /** 17898e664fb3SJohannes Berg * ieee80211_ie_split - split an IE buffer according to ordering 17908e664fb3SJohannes Berg * 17918e664fb3SJohannes Berg * @ies: the IE buffer 17928e664fb3SJohannes Berg * @ielen: the length of the IE buffer 17938e664fb3SJohannes Berg * @ids: an array with element IDs that are allowed before 17948e664fb3SJohannes Berg * the split 17958e664fb3SJohannes Berg * @n_ids: the size of the element ID array 17968e664fb3SJohannes Berg * @offset: offset where to start splitting in the buffer 17978e664fb3SJohannes Berg * 17988e664fb3SJohannes Berg * This function splits an IE buffer by updating the @offset 17998e664fb3SJohannes Berg * variable to point to the location where the buffer should be 18008e664fb3SJohannes Berg * split. 18018e664fb3SJohannes Berg * 18028e664fb3SJohannes Berg * It assumes that the given IE buffer is well-formed, this 18038e664fb3SJohannes Berg * has to be guaranteed by the caller! 18048e664fb3SJohannes Berg * 18058e664fb3SJohannes Berg * It also assumes that the IEs in the buffer are ordered 18068e664fb3SJohannes Berg * correctly, if not the result of using this function will not 18078e664fb3SJohannes Berg * be ordered correctly either, i.e. it does no reordering. 18088e664fb3SJohannes Berg * 18098e664fb3SJohannes Berg * The function returns the offset where the next part of the 18108e664fb3SJohannes Berg * buffer starts, which may be @ielen if the entire (remainder) 18118e664fb3SJohannes Berg * of the buffer should be used. 18128e664fb3SJohannes Berg */ 18138e664fb3SJohannes Berg size_t ieee80211_ie_split(const u8 *ies, size_t ielen, 18148e664fb3SJohannes Berg const u8 *ids, int n_ids, size_t offset) 18158e664fb3SJohannes Berg { 18168e664fb3SJohannes Berg size_t pos = offset; 18178e664fb3SJohannes Berg 18188e664fb3SJohannes Berg while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) 18198e664fb3SJohannes Berg pos += 2 + ies[pos + 1]; 18208e664fb3SJohannes Berg 18218e664fb3SJohannes Berg return pos; 18228e664fb3SJohannes Berg } 18238e664fb3SJohannes Berg 18248e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) 18258e664fb3SJohannes Berg { 18268e664fb3SJohannes Berg size_t pos = offset; 18278e664fb3SJohannes Berg 18288e664fb3SJohannes Berg while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) 18298e664fb3SJohannes Berg pos += 2 + ies[pos + 1]; 18308e664fb3SJohannes Berg 18318e664fb3SJohannes Berg return pos; 18328e664fb3SJohannes Berg } 1833615f7b9bSMeenakshi Venkataraman 1834615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, 1835615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 1836615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 1837615f7b9bSMeenakshi Venkataraman { 1838615f7b9bSMeenakshi Venkataraman trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); 1839615f7b9bSMeenakshi Venkataraman 1840615f7b9bSMeenakshi Venkataraman if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 1841615f7b9bSMeenakshi Venkataraman return; 1842615f7b9bSMeenakshi Venkataraman 1843615f7b9bSMeenakshi Venkataraman /* 1844615f7b9bSMeenakshi Venkataraman * Scale up threshold values before storing it, as the RSSI averaging 1845615f7b9bSMeenakshi Venkataraman * algorithm uses a scaled up value as well. Change this scaling 1846615f7b9bSMeenakshi Venkataraman * factor if the RSSI averaging algorithm changes. 1847615f7b9bSMeenakshi Venkataraman */ 1848615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; 1849615f7b9bSMeenakshi Venkataraman sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; 1850615f7b9bSMeenakshi Venkataraman } 1851615f7b9bSMeenakshi Venkataraman 1852615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, 1853615f7b9bSMeenakshi Venkataraman int rssi_min_thold, 1854615f7b9bSMeenakshi Venkataraman int rssi_max_thold) 1855615f7b9bSMeenakshi Venkataraman { 1856615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 1857615f7b9bSMeenakshi Venkataraman 1858615f7b9bSMeenakshi Venkataraman WARN_ON(rssi_min_thold == rssi_max_thold || 1859615f7b9bSMeenakshi Venkataraman rssi_min_thold > rssi_max_thold); 1860615f7b9bSMeenakshi Venkataraman 1861615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, 1862615f7b9bSMeenakshi Venkataraman rssi_max_thold); 1863615f7b9bSMeenakshi Venkataraman } 1864615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports); 1865615f7b9bSMeenakshi Venkataraman 1866615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) 1867615f7b9bSMeenakshi Venkataraman { 1868615f7b9bSMeenakshi Venkataraman struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 1869615f7b9bSMeenakshi Venkataraman 1870615f7b9bSMeenakshi Venkataraman _ieee80211_enable_rssi_reports(sdata, 0, 0); 1871615f7b9bSMeenakshi Venkataraman } 1872615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports); 1873768db343SArik Nemtsov 1874ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 187542e7aa77SAlexander Simon u16 cap) 187642e7aa77SAlexander Simon { 187742e7aa77SAlexander Simon __le16 tmp; 187842e7aa77SAlexander Simon 187942e7aa77SAlexander Simon *pos++ = WLAN_EID_HT_CAPABILITY; 188042e7aa77SAlexander Simon *pos++ = sizeof(struct ieee80211_ht_cap); 188142e7aa77SAlexander Simon memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 188242e7aa77SAlexander Simon 188342e7aa77SAlexander Simon /* capability flags */ 188442e7aa77SAlexander Simon tmp = cpu_to_le16(cap); 188542e7aa77SAlexander Simon memcpy(pos, &tmp, sizeof(u16)); 188642e7aa77SAlexander Simon pos += sizeof(u16); 188742e7aa77SAlexander Simon 188842e7aa77SAlexander Simon /* AMPDU parameters */ 1889ef96a842SBen Greear *pos++ = ht_cap->ampdu_factor | 1890ef96a842SBen Greear (ht_cap->ampdu_density << 189142e7aa77SAlexander Simon IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); 189242e7aa77SAlexander Simon 189342e7aa77SAlexander Simon /* MCS set */ 1894ef96a842SBen Greear memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); 1895ef96a842SBen Greear pos += sizeof(ht_cap->mcs); 189642e7aa77SAlexander Simon 189742e7aa77SAlexander Simon /* extended capabilities */ 189842e7aa77SAlexander Simon pos += sizeof(__le16); 189942e7aa77SAlexander Simon 190042e7aa77SAlexander Simon /* BF capabilities */ 190142e7aa77SAlexander Simon pos += sizeof(__le32); 190242e7aa77SAlexander Simon 190342e7aa77SAlexander Simon /* antenna selection */ 190442e7aa77SAlexander Simon pos += sizeof(u8); 190542e7aa77SAlexander Simon 190642e7aa77SAlexander Simon return pos; 190742e7aa77SAlexander Simon } 190842e7aa77SAlexander Simon 1909ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 1910ba0afa2fSMahesh Palivela u32 cap) 1911ba0afa2fSMahesh Palivela { 1912ba0afa2fSMahesh Palivela __le32 tmp; 1913ba0afa2fSMahesh Palivela 1914ba0afa2fSMahesh Palivela *pos++ = WLAN_EID_VHT_CAPABILITY; 1915d4950281SMahesh Palivela *pos++ = sizeof(struct ieee80211_vht_cap); 1916d4950281SMahesh Palivela memset(pos, 0, sizeof(struct ieee80211_vht_cap)); 1917ba0afa2fSMahesh Palivela 1918ba0afa2fSMahesh Palivela /* capability flags */ 1919ba0afa2fSMahesh Palivela tmp = cpu_to_le32(cap); 1920ba0afa2fSMahesh Palivela memcpy(pos, &tmp, sizeof(u32)); 1921ba0afa2fSMahesh Palivela pos += sizeof(u32); 1922ba0afa2fSMahesh Palivela 1923ba0afa2fSMahesh Palivela /* VHT MCS set */ 1924ba0afa2fSMahesh Palivela memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); 1925ba0afa2fSMahesh Palivela pos += sizeof(vht_cap->vht_mcs); 1926ba0afa2fSMahesh Palivela 1927ba0afa2fSMahesh Palivela return pos; 1928ba0afa2fSMahesh Palivela } 1929ba0afa2fSMahesh Palivela 1930074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 19314bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 1932431e3154SAshok Nagarajan u16 prot_mode) 193342e7aa77SAlexander Simon { 1934074d46d1SJohannes Berg struct ieee80211_ht_operation *ht_oper; 193542e7aa77SAlexander Simon /* Build HT Information */ 1936074d46d1SJohannes Berg *pos++ = WLAN_EID_HT_OPERATION; 1937074d46d1SJohannes Berg *pos++ = sizeof(struct ieee80211_ht_operation); 1938074d46d1SJohannes Berg ht_oper = (struct ieee80211_ht_operation *)pos; 19394bf88530SJohannes Berg ht_oper->primary_chan = ieee80211_frequency_to_channel( 19404bf88530SJohannes Berg chandef->chan->center_freq); 19414bf88530SJohannes Berg switch (chandef->width) { 19424bf88530SJohannes Berg case NL80211_CHAN_WIDTH_160: 19434bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80P80: 19444bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80: 19454bf88530SJohannes Berg case NL80211_CHAN_WIDTH_40: 19464bf88530SJohannes Berg if (chandef->center_freq1 > chandef->chan->center_freq) 19474bf88530SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 19484bf88530SJohannes Berg else 1949074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 195042e7aa77SAlexander Simon break; 195142e7aa77SAlexander Simon default: 1952074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; 195342e7aa77SAlexander Simon break; 195442e7aa77SAlexander Simon } 1955aee286c2SThomas Pedersen if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && 19564bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20_NOHT && 19574bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20) 1958074d46d1SJohannes Berg ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; 1959ff3cc5f4SSimon Wunderlich 1960431e3154SAshok Nagarajan ht_oper->operation_mode = cpu_to_le16(prot_mode); 1961074d46d1SJohannes Berg ht_oper->stbc_param = 0x0000; 196242e7aa77SAlexander Simon 196342e7aa77SAlexander Simon /* It seems that Basic MCS set and Supported MCS set 196442e7aa77SAlexander Simon are identical for the first 10 bytes */ 1965074d46d1SJohannes Berg memset(&ht_oper->basic_set, 0, 16); 1966074d46d1SJohannes Berg memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); 196742e7aa77SAlexander Simon 1968074d46d1SJohannes Berg return pos + sizeof(struct ieee80211_ht_operation); 196942e7aa77SAlexander Simon } 197042e7aa77SAlexander Simon 19714bf88530SJohannes Berg void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, 19724a3cb702SJohannes Berg const struct ieee80211_ht_operation *ht_oper, 19734bf88530SJohannes Berg struct cfg80211_chan_def *chandef) 197442e7aa77SAlexander Simon { 197542e7aa77SAlexander Simon enum nl80211_channel_type channel_type; 197642e7aa77SAlexander Simon 19774bf88530SJohannes Berg if (!ht_oper) { 19784bf88530SJohannes Berg cfg80211_chandef_create(chandef, control_chan, 19794bf88530SJohannes Berg NL80211_CHAN_NO_HT); 19804bf88530SJohannes Berg return; 19814bf88530SJohannes Berg } 198242e7aa77SAlexander Simon 1983074d46d1SJohannes Berg switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 198442e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_NONE: 198542e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT20; 198642e7aa77SAlexander Simon break; 198742e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 198842e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40PLUS; 198942e7aa77SAlexander Simon break; 199042e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 199142e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40MINUS; 199242e7aa77SAlexander Simon break; 199342e7aa77SAlexander Simon default: 199442e7aa77SAlexander Simon channel_type = NL80211_CHAN_NO_HT; 199542e7aa77SAlexander Simon } 199642e7aa77SAlexander Simon 19974bf88530SJohannes Berg cfg80211_chandef_create(chandef, control_chan, channel_type); 199842e7aa77SAlexander Simon } 199942e7aa77SAlexander Simon 2000fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, 20016b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 20026b77863bSJohannes Berg enum ieee80211_band band) 2003768db343SArik Nemtsov { 2004768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 2005768db343SArik Nemtsov struct ieee80211_supported_band *sband; 2006768db343SArik Nemtsov int rate; 2007768db343SArik Nemtsov u8 i, rates, *pos; 2008fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 2009768db343SArik Nemtsov 20106b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 2011768db343SArik Nemtsov rates = sband->n_bitrates; 2012768db343SArik Nemtsov if (rates > 8) 2013768db343SArik Nemtsov rates = 8; 2014768db343SArik Nemtsov 2015768db343SArik Nemtsov if (skb_tailroom(skb) < rates + 2) 2016768db343SArik Nemtsov return -ENOMEM; 2017768db343SArik Nemtsov 2018768db343SArik Nemtsov pos = skb_put(skb, rates + 2); 2019768db343SArik Nemtsov *pos++ = WLAN_EID_SUPP_RATES; 2020768db343SArik Nemtsov *pos++ = rates; 2021768db343SArik Nemtsov for (i = 0; i < rates; i++) { 2022657c3e0cSAshok Nagarajan u8 basic = 0; 2023657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 2024657c3e0cSAshok Nagarajan basic = 0x80; 2025768db343SArik Nemtsov rate = sband->bitrates[i].bitrate; 2026657c3e0cSAshok Nagarajan *pos++ = basic | (u8) (rate / 5); 2027768db343SArik Nemtsov } 2028768db343SArik Nemtsov 2029768db343SArik Nemtsov return 0; 2030768db343SArik Nemtsov } 2031768db343SArik Nemtsov 2032fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, 20336b77863bSJohannes Berg struct sk_buff *skb, bool need_basic, 20346b77863bSJohannes Berg enum ieee80211_band band) 2035768db343SArik Nemtsov { 2036768db343SArik Nemtsov struct ieee80211_local *local = sdata->local; 2037768db343SArik Nemtsov struct ieee80211_supported_band *sband; 2038768db343SArik Nemtsov int rate; 2039768db343SArik Nemtsov u8 i, exrates, *pos; 2040fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates; 2041768db343SArik Nemtsov 20426b77863bSJohannes Berg sband = local->hw.wiphy->bands[band]; 2043768db343SArik Nemtsov exrates = sband->n_bitrates; 2044768db343SArik Nemtsov if (exrates > 8) 2045768db343SArik Nemtsov exrates -= 8; 2046768db343SArik Nemtsov else 2047768db343SArik Nemtsov exrates = 0; 2048768db343SArik Nemtsov 2049768db343SArik Nemtsov if (skb_tailroom(skb) < exrates + 2) 2050768db343SArik Nemtsov return -ENOMEM; 2051768db343SArik Nemtsov 2052768db343SArik Nemtsov if (exrates) { 2053768db343SArik Nemtsov pos = skb_put(skb, exrates + 2); 2054768db343SArik Nemtsov *pos++ = WLAN_EID_EXT_SUPP_RATES; 2055768db343SArik Nemtsov *pos++ = exrates; 2056768db343SArik Nemtsov for (i = 8; i < sband->n_bitrates; i++) { 2057657c3e0cSAshok Nagarajan u8 basic = 0; 2058657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i)) 2059657c3e0cSAshok Nagarajan basic = 0x80; 2060768db343SArik Nemtsov rate = sband->bitrates[i].bitrate; 2061657c3e0cSAshok Nagarajan *pos++ = basic | (u8) (rate / 5); 2062768db343SArik Nemtsov } 2063768db343SArik Nemtsov } 2064768db343SArik Nemtsov return 0; 2065768db343SArik Nemtsov } 20661dae27f8SWey-Yi Guy 20671dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif) 20681dae27f8SWey-Yi Guy { 20691dae27f8SWey-Yi Guy struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 20701dae27f8SWey-Yi Guy struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 20711dae27f8SWey-Yi Guy 2072be6bcabcSWey-Yi Guy if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { 2073be6bcabcSWey-Yi Guy /* non-managed type inferfaces */ 2074be6bcabcSWey-Yi Guy return 0; 2075be6bcabcSWey-Yi Guy } 20763a7bba64SEmmanuel Grumbach return ifmgd->ave_beacon_signal / 16; 20771dae27f8SWey-Yi Guy } 20780d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 207904ecd257SJohannes Berg 208004ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) 208104ecd257SJohannes Berg { 208204ecd257SJohannes Berg if (!mcs) 208304ecd257SJohannes Berg return 1; 208404ecd257SJohannes Berg 208504ecd257SJohannes Berg /* TODO: consider rx_highest */ 208604ecd257SJohannes Berg 208704ecd257SJohannes Berg if (mcs->rx_mask[3]) 208804ecd257SJohannes Berg return 4; 208904ecd257SJohannes Berg if (mcs->rx_mask[2]) 209004ecd257SJohannes Berg return 3; 209104ecd257SJohannes Berg if (mcs->rx_mask[1]) 209204ecd257SJohannes Berg return 2; 209304ecd257SJohannes Berg return 1; 209404ecd257SJohannes Berg } 2095f4bda337SThomas Pedersen 2096f4bda337SThomas Pedersen /** 2097f4bda337SThomas Pedersen * ieee80211_calculate_rx_timestamp - calculate timestamp in frame 2098f4bda337SThomas Pedersen * @local: mac80211 hw info struct 2099f4bda337SThomas Pedersen * @status: RX status 2100f4bda337SThomas Pedersen * @mpdu_len: total MPDU length (including FCS) 2101f4bda337SThomas Pedersen * @mpdu_offset: offset into MPDU to calculate timestamp at 2102f4bda337SThomas Pedersen * 2103f4bda337SThomas Pedersen * This function calculates the RX timestamp at the given MPDU offset, taking 2104f4bda337SThomas Pedersen * into account what the RX timestamp was. An offset of 0 will just normalize 2105f4bda337SThomas Pedersen * the timestamp to TSF at beginning of MPDU reception. 2106f4bda337SThomas Pedersen */ 2107f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, 2108f4bda337SThomas Pedersen struct ieee80211_rx_status *status, 2109f4bda337SThomas Pedersen unsigned int mpdu_len, 2110f4bda337SThomas Pedersen unsigned int mpdu_offset) 2111f4bda337SThomas Pedersen { 2112f4bda337SThomas Pedersen u64 ts = status->mactime; 2113f4bda337SThomas Pedersen struct rate_info ri; 2114f4bda337SThomas Pedersen u16 rate; 2115f4bda337SThomas Pedersen 2116f4bda337SThomas Pedersen if (WARN_ON(!ieee80211_have_rx_timestamp(status))) 2117f4bda337SThomas Pedersen return 0; 2118f4bda337SThomas Pedersen 2119f4bda337SThomas Pedersen memset(&ri, 0, sizeof(ri)); 2120f4bda337SThomas Pedersen 2121f4bda337SThomas Pedersen /* Fill cfg80211 rate info */ 2122f4bda337SThomas Pedersen if (status->flag & RX_FLAG_HT) { 2123f4bda337SThomas Pedersen ri.mcs = status->rate_idx; 2124f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_MCS; 2125f4bda337SThomas Pedersen if (status->flag & RX_FLAG_40MHZ) 2126f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; 2127f4bda337SThomas Pedersen if (status->flag & RX_FLAG_SHORT_GI) 2128f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 21295614618eSJohannes Berg } else if (status->flag & RX_FLAG_VHT) { 21305614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_VHT_MCS; 21315614618eSJohannes Berg ri.mcs = status->rate_idx; 21325614618eSJohannes Berg ri.nss = status->vht_nss; 21335614618eSJohannes Berg if (status->flag & RX_FLAG_40MHZ) 21345614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; 21355614618eSJohannes Berg if (status->flag & RX_FLAG_80MHZ) 21365614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; 21375614618eSJohannes Berg if (status->flag & RX_FLAG_80P80MHZ) 21385614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; 21395614618eSJohannes Berg if (status->flag & RX_FLAG_160MHZ) 21405614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; 21415614618eSJohannes Berg if (status->flag & RX_FLAG_SHORT_GI) 21425614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 2143f4bda337SThomas Pedersen } else { 2144f4bda337SThomas Pedersen struct ieee80211_supported_band *sband; 2145f4bda337SThomas Pedersen 2146f4bda337SThomas Pedersen sband = local->hw.wiphy->bands[status->band]; 2147f4bda337SThomas Pedersen ri.legacy = sband->bitrates[status->rate_idx].bitrate; 2148f4bda337SThomas Pedersen } 2149f4bda337SThomas Pedersen 2150f4bda337SThomas Pedersen rate = cfg80211_calculate_bitrate(&ri); 2151f4bda337SThomas Pedersen 2152f4bda337SThomas Pedersen /* rewind from end of MPDU */ 2153f4bda337SThomas Pedersen if (status->flag & RX_FLAG_MACTIME_END) 2154f4bda337SThomas Pedersen ts -= mpdu_len * 8 * 10 / rate; 2155f4bda337SThomas Pedersen 2156f4bda337SThomas Pedersen ts += mpdu_offset * 8 * 10 / rate; 2157f4bda337SThomas Pedersen 2158f4bda337SThomas Pedersen return ts; 2159f4bda337SThomas Pedersen } 2160164eb02dSSimon Wunderlich 2161164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) 2162164eb02dSSimon Wunderlich { 2163164eb02dSSimon Wunderlich struct ieee80211_sub_if_data *sdata; 2164164eb02dSSimon Wunderlich 2165164eb02dSSimon Wunderlich mutex_lock(&local->iflist_mtx); 2166164eb02dSSimon Wunderlich list_for_each_entry(sdata, &local->interfaces, list) { 2167164eb02dSSimon Wunderlich cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); 2168164eb02dSSimon Wunderlich 2169164eb02dSSimon Wunderlich if (sdata->wdev.cac_started) { 2170164eb02dSSimon Wunderlich ieee80211_vif_release_channel(sdata); 2171164eb02dSSimon Wunderlich cfg80211_cac_event(sdata->dev, 2172164eb02dSSimon Wunderlich NL80211_RADAR_CAC_ABORTED, 2173164eb02dSSimon Wunderlich GFP_KERNEL); 2174164eb02dSSimon Wunderlich } 2175164eb02dSSimon Wunderlich } 2176164eb02dSSimon Wunderlich mutex_unlock(&local->iflist_mtx); 2177164eb02dSSimon Wunderlich } 2178164eb02dSSimon Wunderlich 2179164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work) 2180164eb02dSSimon Wunderlich { 2181164eb02dSSimon Wunderlich struct ieee80211_local *local = 2182164eb02dSSimon Wunderlich container_of(work, struct ieee80211_local, radar_detected_work); 2183164eb02dSSimon Wunderlich struct cfg80211_chan_def chandef; 2184164eb02dSSimon Wunderlich 2185164eb02dSSimon Wunderlich ieee80211_dfs_cac_cancel(local); 2186164eb02dSSimon Wunderlich 2187164eb02dSSimon Wunderlich if (local->use_chanctx) 2188164eb02dSSimon Wunderlich /* currently not handled */ 2189164eb02dSSimon Wunderlich WARN_ON(1); 2190164eb02dSSimon Wunderlich else { 2191675a0b04SKarl Beldan chandef = local->hw.conf.chandef; 2192164eb02dSSimon Wunderlich cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); 2193164eb02dSSimon Wunderlich } 2194164eb02dSSimon Wunderlich } 2195164eb02dSSimon Wunderlich 2196164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw) 2197164eb02dSSimon Wunderlich { 2198164eb02dSSimon Wunderlich struct ieee80211_local *local = hw_to_local(hw); 2199164eb02dSSimon Wunderlich 2200164eb02dSSimon Wunderlich trace_api_radar_detected(local); 2201164eb02dSSimon Wunderlich 2202164eb02dSSimon Wunderlich ieee80211_queue_work(hw, &local->radar_detected_work); 2203164eb02dSSimon Wunderlich } 2204164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected); 2205