1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c2d1560aSJohannes Berg /*
3c2d1560aSJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc.
4c2d1560aSJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc.
5c2d1560aSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
6c2d1560aSJohannes Berg * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
7d98ad83eSJohannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH
8da6a4352SJohannes Berg * Copyright (C) 2015-2017 Intel Deutschland GmbH
91ec7291eSJohannes Berg * Copyright (C) 2018-2023 Intel Corporation
10c2d1560aSJohannes Berg *
11c2d1560aSJohannes Berg * utilities for mac80211
12c2d1560aSJohannes Berg */
13c2d1560aSJohannes Berg
14c2d1560aSJohannes Berg #include <net/mac80211.h>
15c2d1560aSJohannes Berg #include <linux/netdevice.h>
16bc3b2d7fSPaul Gortmaker #include <linux/export.h>
17c2d1560aSJohannes Berg #include <linux/types.h>
18c2d1560aSJohannes Berg #include <linux/slab.h>
19c2d1560aSJohannes Berg #include <linux/skbuff.h>
20c2d1560aSJohannes Berg #include <linux/etherdevice.h>
21c2d1560aSJohannes Berg #include <linux/if_arp.h>
22c2d1560aSJohannes Berg #include <linux/bitmap.h>
23dd76986bSJohannes Berg #include <linux/crc32.h>
24881d966bSEric W. Biederman #include <net/net_namespace.h>
25c2d1560aSJohannes Berg #include <net/cfg80211.h>
26dabeb344SJohannes Berg #include <net/rtnetlink.h>
27c2d1560aSJohannes Berg
28c2d1560aSJohannes Berg #include "ieee80211_i.h"
2924487981SJohannes Berg #include "driver-ops.h"
302c8dccc7SJohannes Berg #include "rate.h"
31ee385855SLuis Carlos Cobo #include "mesh.h"
32c2d1560aSJohannes Berg #include "wme.h"
33f2753ddbSJohannes Berg #include "led.h"
34fffd0934SJohannes Berg #include "wep.h"
35c2d1560aSJohannes Berg
36c2d1560aSJohannes Berg /* privid for wiphys to determine whether they belong to us or not */
378a47cea7SJohannes Berg const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;
38c2d1560aSJohannes Berg
wiphy_to_ieee80211_hw(struct wiphy * wiphy)399a95371aSLuis R. Rodriguez struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
409a95371aSLuis R. Rodriguez {
419a95371aSLuis R. Rodriguez struct ieee80211_local *local;
429a95371aSLuis R. Rodriguez
439a95371aSLuis R. Rodriguez local = wiphy_priv(wiphy);
449a95371aSLuis R. Rodriguez return &local->hw;
459a95371aSLuis R. Rodriguez }
469a95371aSLuis R. Rodriguez EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
47c2d1560aSJohannes Berg
ieee80211_get_bssid(struct ieee80211_hdr * hdr,size_t len,enum nl80211_iftype type)4809a740ceSThomas Pedersen u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
4909a740ceSThomas Pedersen enum nl80211_iftype type)
5009a740ceSThomas Pedersen {
5109a740ceSThomas Pedersen __le16 fc = hdr->frame_control;
5209a740ceSThomas Pedersen
5309a740ceSThomas Pedersen if (ieee80211_is_data(fc)) {
5409a740ceSThomas Pedersen if (len < 24) /* drop incorrect hdr len (data) */
5509a740ceSThomas Pedersen return NULL;
5609a740ceSThomas Pedersen
5709a740ceSThomas Pedersen if (ieee80211_has_a4(fc))
5809a740ceSThomas Pedersen return NULL;
5909a740ceSThomas Pedersen if (ieee80211_has_tods(fc))
6009a740ceSThomas Pedersen return hdr->addr1;
6109a740ceSThomas Pedersen if (ieee80211_has_fromds(fc))
6209a740ceSThomas Pedersen return hdr->addr2;
6309a740ceSThomas Pedersen
6409a740ceSThomas Pedersen return hdr->addr3;
6509a740ceSThomas Pedersen }
6609a740ceSThomas Pedersen
6709a740ceSThomas Pedersen if (ieee80211_is_s1g_beacon(fc)) {
6809a740ceSThomas Pedersen struct ieee80211_ext *ext = (void *) hdr;
6909a740ceSThomas Pedersen
7009a740ceSThomas Pedersen return ext->u.s1g_beacon.sa;
7109a740ceSThomas Pedersen }
7209a740ceSThomas Pedersen
7309a740ceSThomas Pedersen if (ieee80211_is_mgmt(fc)) {
7409a740ceSThomas Pedersen if (len < 24) /* drop incorrect hdr len (mgmt) */
7509a740ceSThomas Pedersen return NULL;
7609a740ceSThomas Pedersen return hdr->addr3;
7709a740ceSThomas Pedersen }
7809a740ceSThomas Pedersen
7909a740ceSThomas Pedersen if (ieee80211_is_ctl(fc)) {
8009a740ceSThomas Pedersen if (ieee80211_is_pspoll(fc))
8109a740ceSThomas Pedersen return hdr->addr1;
8209a740ceSThomas Pedersen
8309a740ceSThomas Pedersen if (ieee80211_is_back_req(fc)) {
8409a740ceSThomas Pedersen switch (type) {
8509a740ceSThomas Pedersen case NL80211_IFTYPE_STATION:
8609a740ceSThomas Pedersen return hdr->addr2;
8709a740ceSThomas Pedersen case NL80211_IFTYPE_AP:
8809a740ceSThomas Pedersen case NL80211_IFTYPE_AP_VLAN:
8909a740ceSThomas Pedersen return hdr->addr1;
9009a740ceSThomas Pedersen default:
9109a740ceSThomas Pedersen break; /* fall through to the return */
9209a740ceSThomas Pedersen }
9309a740ceSThomas Pedersen }
9409a740ceSThomas Pedersen }
9509a740ceSThomas Pedersen
9609a740ceSThomas Pedersen return NULL;
9709a740ceSThomas Pedersen }
9809a740ceSThomas Pedersen EXPORT_SYMBOL(ieee80211_get_bssid);
9909a740ceSThomas Pedersen
ieee80211_tx_set_protected(struct ieee80211_tx_data * tx)1005cf121c3SJohannes Berg void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
101c2d1560aSJohannes Berg {
102252b86c4SJohannes Berg struct sk_buff *skb;
1032de8e0d9SJohannes Berg struct ieee80211_hdr *hdr;
104c2d1560aSJohannes Berg
105252b86c4SJohannes Berg skb_queue_walk(&tx->skbs, skb) {
1062de8e0d9SJohannes Berg hdr = (struct ieee80211_hdr *) skb->data;
107c2d1560aSJohannes Berg hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
108252b86c4SJohannes Berg }
109c2d1560aSJohannes Berg }
110c2d1560aSJohannes Berg
ieee80211_frame_duration(enum nl80211_band band,size_t len,int rate,int erp,int short_preamble,int shift)11157fbcce3SJohannes Berg int ieee80211_frame_duration(enum nl80211_band band, size_t len,
112438b61b7SSimon Wunderlich int rate, int erp, int short_preamble,
113438b61b7SSimon Wunderlich int shift)
114c2d1560aSJohannes Berg {
115c2d1560aSJohannes Berg int dur;
116c2d1560aSJohannes Berg
117c2d1560aSJohannes Berg /* calculate duration (in microseconds, rounded up to next higher
118c2d1560aSJohannes Berg * integer if it includes a fractional microsecond) to send frame of
119c2d1560aSJohannes Berg * len bytes (does not include FCS) at the given rate. Duration will
120c2d1560aSJohannes Berg * also include SIFS.
121c2d1560aSJohannes Berg *
122c2d1560aSJohannes Berg * rate is in 100 kbps, so divident is multiplied by 10 in the
123c2d1560aSJohannes Berg * DIV_ROUND_UP() operations.
124438b61b7SSimon Wunderlich *
125438b61b7SSimon Wunderlich * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
126438b61b7SSimon Wunderlich * is assumed to be 0 otherwise.
127c2d1560aSJohannes Berg */
128c2d1560aSJohannes Berg
12957fbcce3SJohannes Berg if (band == NL80211_BAND_5GHZ || erp) {
130c2d1560aSJohannes Berg /*
131c2d1560aSJohannes Berg * OFDM:
132c2d1560aSJohannes Berg *
133c2d1560aSJohannes Berg * N_DBPS = DATARATE x 4
134c2d1560aSJohannes Berg * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
135c2d1560aSJohannes Berg * (16 = SIGNAL time, 6 = tail bits)
136c2d1560aSJohannes Berg * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
137c2d1560aSJohannes Berg *
138c2d1560aSJohannes Berg * T_SYM = 4 usec
139438b61b7SSimon Wunderlich * 802.11a - 18.5.2: aSIFSTime = 16 usec
140c2d1560aSJohannes Berg * 802.11g - 19.8.4: aSIFSTime = 10 usec +
141c2d1560aSJohannes Berg * signal ext = 6 usec
142c2d1560aSJohannes Berg */
143c2d1560aSJohannes Berg dur = 16; /* SIFS + signal ext */
144438b61b7SSimon Wunderlich dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
145438b61b7SSimon Wunderlich dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
146438b61b7SSimon Wunderlich
147438b61b7SSimon Wunderlich /* IEEE 802.11-2012 18.3.2.4: all values above are:
148438b61b7SSimon Wunderlich * * times 4 for 5 MHz
149438b61b7SSimon Wunderlich * * times 2 for 10 MHz
150438b61b7SSimon Wunderlich */
151438b61b7SSimon Wunderlich dur *= 1 << shift;
1522103dec1SSimon Wunderlich
1532103dec1SSimon Wunderlich /* rates should already consider the channel bandwidth,
1542103dec1SSimon Wunderlich * don't apply divisor again.
1552103dec1SSimon Wunderlich */
1562103dec1SSimon Wunderlich dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
1572103dec1SSimon Wunderlich 4 * rate); /* T_SYM x N_SYM */
158c2d1560aSJohannes Berg } else {
159c2d1560aSJohannes Berg /*
160c2d1560aSJohannes Berg * 802.11b or 802.11g with 802.11b compatibility:
161c2d1560aSJohannes Berg * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
162c2d1560aSJohannes Berg * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
163c2d1560aSJohannes Berg *
164c2d1560aSJohannes Berg * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
165c2d1560aSJohannes Berg * aSIFSTime = 10 usec
166c2d1560aSJohannes Berg * aPreambleLength = 144 usec or 72 usec with short preamble
167c2d1560aSJohannes Berg * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
168c2d1560aSJohannes Berg */
169c2d1560aSJohannes Berg dur = 10; /* aSIFSTime = 10 usec */
170c2d1560aSJohannes Berg dur += short_preamble ? (72 + 24) : (144 + 48);
171c2d1560aSJohannes Berg
172c2d1560aSJohannes Berg dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
173c2d1560aSJohannes Berg }
174c2d1560aSJohannes Berg
175c2d1560aSJohannes Berg return dur;
176c2d1560aSJohannes Berg }
177c2d1560aSJohannes Berg
178c2d1560aSJohannes Berg /* Exported duration function for driver use */
ieee80211_generic_frame_duration(struct ieee80211_hw * hw,struct ieee80211_vif * vif,enum nl80211_band band,size_t frame_len,struct ieee80211_rate * rate)17932bfd35dSJohannes Berg __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
18032bfd35dSJohannes Berg struct ieee80211_vif *vif,
18157fbcce3SJohannes Berg enum nl80211_band band,
1828318d78aSJohannes Berg size_t frame_len,
1838318d78aSJohannes Berg struct ieee80211_rate *rate)
184c2d1560aSJohannes Berg {
18525d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata;
186c2d1560aSJohannes Berg u16 dur;
187438b61b7SSimon Wunderlich int erp, shift = 0;
18825d834e1SJohannes Berg bool short_preamble = false;
189c2d1560aSJohannes Berg
1908318d78aSJohannes Berg erp = 0;
19125d834e1SJohannes Berg if (vif) {
19225d834e1SJohannes Berg sdata = vif_to_sdata(vif);
193bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble;
19439eac2deSJohannes Berg if (sdata->deflink.operating_11g_mode)
1958318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G;
196438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif);
19725d834e1SJohannes Berg }
1988318d78aSJohannes Berg
1994ee73f33SMichal Kazior dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
200438b61b7SSimon Wunderlich short_preamble, shift);
201c2d1560aSJohannes Berg
202c2d1560aSJohannes Berg return cpu_to_le16(dur);
203c2d1560aSJohannes Berg }
204c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_generic_frame_duration);
205c2d1560aSJohannes Berg
ieee80211_rts_duration(struct ieee80211_hw * hw,struct ieee80211_vif * vif,size_t frame_len,const struct ieee80211_tx_info * frame_txctl)20632bfd35dSJohannes Berg __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
20732bfd35dSJohannes Berg struct ieee80211_vif *vif, size_t frame_len,
208e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl)
209c2d1560aSJohannes Berg {
210c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
211c2d1560aSJohannes Berg struct ieee80211_rate *rate;
21225d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata;
213471b3efdSJohannes Berg bool short_preamble;
2142103dec1SSimon Wunderlich int erp, shift = 0, bitrate;
215c2d1560aSJohannes Berg u16 dur;
2162e92e6f2SJohannes Berg struct ieee80211_supported_band *sband;
2172e92e6f2SJohannes Berg
2184ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band];
219c2d1560aSJohannes Berg
22025d834e1SJohannes Berg short_preamble = false;
2217e9ed188SDaniel Drake
222e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
2238318d78aSJohannes Berg
2248318d78aSJohannes Berg erp = 0;
22525d834e1SJohannes Berg if (vif) {
22625d834e1SJohannes Berg sdata = vif_to_sdata(vif);
227bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble;
22839eac2deSJohannes Berg if (sdata->deflink.operating_11g_mode)
2298318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G;
230438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif);
23125d834e1SJohannes Berg }
232c2d1560aSJohannes Berg
2332103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
2342103dec1SSimon Wunderlich
235c2d1560aSJohannes Berg /* CTS duration */
2362103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, 10, bitrate,
237438b61b7SSimon Wunderlich erp, short_preamble, shift);
238c2d1560aSJohannes Berg /* Data frame duration */
2392103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
240438b61b7SSimon Wunderlich erp, short_preamble, shift);
241c2d1560aSJohannes Berg /* ACK duration */
2422103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate,
243438b61b7SSimon Wunderlich erp, short_preamble, shift);
244c2d1560aSJohannes Berg
245c2d1560aSJohannes Berg return cpu_to_le16(dur);
246c2d1560aSJohannes Berg }
247c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_rts_duration);
248c2d1560aSJohannes Berg
ieee80211_ctstoself_duration(struct ieee80211_hw * hw,struct ieee80211_vif * vif,size_t frame_len,const struct ieee80211_tx_info * frame_txctl)24932bfd35dSJohannes Berg __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
25032bfd35dSJohannes Berg struct ieee80211_vif *vif,
251c2d1560aSJohannes Berg size_t frame_len,
252e039fa4aSJohannes Berg const struct ieee80211_tx_info *frame_txctl)
253c2d1560aSJohannes Berg {
254c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
255c2d1560aSJohannes Berg struct ieee80211_rate *rate;
25625d834e1SJohannes Berg struct ieee80211_sub_if_data *sdata;
257471b3efdSJohannes Berg bool short_preamble;
2582103dec1SSimon Wunderlich int erp, shift = 0, bitrate;
259c2d1560aSJohannes Berg u16 dur;
2602e92e6f2SJohannes Berg struct ieee80211_supported_band *sband;
2612e92e6f2SJohannes Berg
2624ee73f33SMichal Kazior sband = local->hw.wiphy->bands[frame_txctl->band];
263c2d1560aSJohannes Berg
26425d834e1SJohannes Berg short_preamble = false;
2657e9ed188SDaniel Drake
266e039fa4aSJohannes Berg rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
2678318d78aSJohannes Berg erp = 0;
26825d834e1SJohannes Berg if (vif) {
26925d834e1SJohannes Berg sdata = vif_to_sdata(vif);
270bda3933aSJohannes Berg short_preamble = sdata->vif.bss_conf.use_short_preamble;
27139eac2deSJohannes Berg if (sdata->deflink.operating_11g_mode)
2728318d78aSJohannes Berg erp = rate->flags & IEEE80211_RATE_ERP_G;
273438b61b7SSimon Wunderlich shift = ieee80211_vif_get_shift(vif);
27425d834e1SJohannes Berg }
275c2d1560aSJohannes Berg
2762103dec1SSimon Wunderlich bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
2772103dec1SSimon Wunderlich
278c2d1560aSJohannes Berg /* Data frame duration */
2792103dec1SSimon Wunderlich dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
280438b61b7SSimon Wunderlich erp, short_preamble, shift);
281e039fa4aSJohannes Berg if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
282c2d1560aSJohannes Berg /* ACK duration */
2832103dec1SSimon Wunderlich dur += ieee80211_frame_duration(sband->band, 10, bitrate,
284438b61b7SSimon Wunderlich erp, short_preamble, shift);
285c2d1560aSJohannes Berg }
286c2d1560aSJohannes Berg
287c2d1560aSJohannes Berg return cpu_to_le16(dur);
288c2d1560aSJohannes Berg }
289c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_ctstoself_duration);
290c2d1560aSJohannes Berg
wake_tx_push_queue(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_txq * queue)291c850e31fSAlexander Wetzel static void wake_tx_push_queue(struct ieee80211_local *local,
292c850e31fSAlexander Wetzel struct ieee80211_sub_if_data *sdata,
293c850e31fSAlexander Wetzel struct ieee80211_txq *queue)
294c850e31fSAlexander Wetzel {
295c850e31fSAlexander Wetzel struct ieee80211_tx_control control = {
296c850e31fSAlexander Wetzel .sta = queue->sta,
297c850e31fSAlexander Wetzel };
298c850e31fSAlexander Wetzel struct sk_buff *skb;
299c850e31fSAlexander Wetzel
300c850e31fSAlexander Wetzel while (1) {
301c850e31fSAlexander Wetzel skb = ieee80211_tx_dequeue(&local->hw, queue);
302c850e31fSAlexander Wetzel if (!skb)
303c850e31fSAlexander Wetzel break;
304c850e31fSAlexander Wetzel
305c850e31fSAlexander Wetzel drv_tx(local, &control, skb);
306c850e31fSAlexander Wetzel }
307c850e31fSAlexander Wetzel }
308c850e31fSAlexander Wetzel
309c850e31fSAlexander Wetzel /* wake_tx_queue handler for driver not implementing a custom one*/
ieee80211_handle_wake_tx_queue(struct ieee80211_hw * hw,struct ieee80211_txq * txq)310c850e31fSAlexander Wetzel void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
311c850e31fSAlexander Wetzel struct ieee80211_txq *txq)
312c850e31fSAlexander Wetzel {
313c850e31fSAlexander Wetzel struct ieee80211_local *local = hw_to_local(hw);
314c850e31fSAlexander Wetzel struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
315c850e31fSAlexander Wetzel struct ieee80211_txq *queue;
316c850e31fSAlexander Wetzel
317007ae9b2SAlexander Wetzel spin_lock(&local->handle_wake_tx_queue_lock);
318007ae9b2SAlexander Wetzel
319c850e31fSAlexander Wetzel /* Use ieee80211_next_txq() for airtime fairness accounting */
320c850e31fSAlexander Wetzel ieee80211_txq_schedule_start(hw, txq->ac);
321c850e31fSAlexander Wetzel while ((queue = ieee80211_next_txq(hw, txq->ac))) {
322c850e31fSAlexander Wetzel wake_tx_push_queue(local, sdata, queue);
323c850e31fSAlexander Wetzel ieee80211_return_txq(hw, queue, false);
324c850e31fSAlexander Wetzel }
325c850e31fSAlexander Wetzel ieee80211_txq_schedule_end(hw, txq->ac);
326007ae9b2SAlexander Wetzel spin_unlock(&local->handle_wake_tx_queue_lock);
327c850e31fSAlexander Wetzel }
328c850e31fSAlexander Wetzel EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
329c850e31fSAlexander Wetzel
__ieee80211_wake_txqs(struct ieee80211_sub_if_data * sdata,int ac)33021a5d4c3SManikanta Pubbisetty static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
33121a5d4c3SManikanta Pubbisetty {
33221a5d4c3SManikanta Pubbisetty struct ieee80211_local *local = sdata->local;
33321a5d4c3SManikanta Pubbisetty struct ieee80211_vif *vif = &sdata->vif;
33421a5d4c3SManikanta Pubbisetty struct fq *fq = &local->fq;
33521a5d4c3SManikanta Pubbisetty struct ps_data *ps = NULL;
33621a5d4c3SManikanta Pubbisetty struct txq_info *txqi;
33721a5d4c3SManikanta Pubbisetty struct sta_info *sta;
33821a5d4c3SManikanta Pubbisetty int i;
33921a5d4c3SManikanta Pubbisetty
340d8dec42bSJohannes Berg local_bh_disable();
341d8dec42bSJohannes Berg spin_lock(&fq->lock);
34221a5d4c3SManikanta Pubbisetty
343f856373eSFelix Fietkau if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
344f856373eSFelix Fietkau goto out;
345f856373eSFelix Fietkau
34621a5d4c3SManikanta Pubbisetty if (sdata->vif.type == NL80211_IFTYPE_AP)
34721a5d4c3SManikanta Pubbisetty ps = &sdata->bss->ps;
34821a5d4c3SManikanta Pubbisetty
34921a5d4c3SManikanta Pubbisetty list_for_each_entry_rcu(sta, &local->sta_list, list) {
35021a5d4c3SManikanta Pubbisetty if (sdata != sta->sdata)
35121a5d4c3SManikanta Pubbisetty continue;
35221a5d4c3SManikanta Pubbisetty
35321a5d4c3SManikanta Pubbisetty for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
35421a5d4c3SManikanta Pubbisetty struct ieee80211_txq *txq = sta->sta.txq[i];
35521a5d4c3SManikanta Pubbisetty
356a5ae3264SErik Stromdahl if (!txq)
357a5ae3264SErik Stromdahl continue;
358a5ae3264SErik Stromdahl
35921a5d4c3SManikanta Pubbisetty txqi = to_txq_info(txq);
36021a5d4c3SManikanta Pubbisetty
36121a5d4c3SManikanta Pubbisetty if (ac != txq->ac)
36221a5d4c3SManikanta Pubbisetty continue;
36321a5d4c3SManikanta Pubbisetty
3644444bc21SAlexander Wetzel if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
36521a5d4c3SManikanta Pubbisetty &txqi->flags))
36621a5d4c3SManikanta Pubbisetty continue;
36721a5d4c3SManikanta Pubbisetty
368d8dec42bSJohannes Berg spin_unlock(&fq->lock);
36921a5d4c3SManikanta Pubbisetty drv_wake_tx_queue(local, txqi);
370d8dec42bSJohannes Berg spin_lock(&fq->lock);
37121a5d4c3SManikanta Pubbisetty }
37221a5d4c3SManikanta Pubbisetty }
37321a5d4c3SManikanta Pubbisetty
37421a5d4c3SManikanta Pubbisetty if (!vif->txq)
37521a5d4c3SManikanta Pubbisetty goto out;
37621a5d4c3SManikanta Pubbisetty
37721a5d4c3SManikanta Pubbisetty txqi = to_txq_info(vif->txq);
37821a5d4c3SManikanta Pubbisetty
3794444bc21SAlexander Wetzel if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
38021a5d4c3SManikanta Pubbisetty (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
38121a5d4c3SManikanta Pubbisetty goto out;
38221a5d4c3SManikanta Pubbisetty
383d8dec42bSJohannes Berg spin_unlock(&fq->lock);
38421a5d4c3SManikanta Pubbisetty
38521a5d4c3SManikanta Pubbisetty drv_wake_tx_queue(local, txqi);
386d8dec42bSJohannes Berg local_bh_enable();
38721a5d4c3SManikanta Pubbisetty return;
38821a5d4c3SManikanta Pubbisetty out:
389d8dec42bSJohannes Berg spin_unlock(&fq->lock);
390d8dec42bSJohannes Berg local_bh_enable();
39121a5d4c3SManikanta Pubbisetty }
39221a5d4c3SManikanta Pubbisetty
393f6c7f03fSEmmanuel Grumbach static void
394f6c7f03fSEmmanuel Grumbach __releases(&local->queue_stop_reason_lock)
395f6c7f03fSEmmanuel Grumbach __acquires(&local->queue_stop_reason_lock)
_ieee80211_wake_txqs(struct ieee80211_local * local,unsigned long * flags)396f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
39721a5d4c3SManikanta Pubbisetty {
39821a5d4c3SManikanta Pubbisetty struct ieee80211_sub_if_data *sdata;
39921a5d4c3SManikanta Pubbisetty int n_acs = IEEE80211_NUM_ACS;
40021a5d4c3SManikanta Pubbisetty int i;
40121a5d4c3SManikanta Pubbisetty
40221a5d4c3SManikanta Pubbisetty rcu_read_lock();
40321a5d4c3SManikanta Pubbisetty
40421a5d4c3SManikanta Pubbisetty if (local->hw.queues < IEEE80211_NUM_ACS)
40521a5d4c3SManikanta Pubbisetty n_acs = 1;
40621a5d4c3SManikanta Pubbisetty
40721a5d4c3SManikanta Pubbisetty for (i = 0; i < local->hw.queues; i++) {
40821a5d4c3SManikanta Pubbisetty if (local->queue_stop_reasons[i])
40921a5d4c3SManikanta Pubbisetty continue;
41021a5d4c3SManikanta Pubbisetty
411f6c7f03fSEmmanuel Grumbach spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
41221a5d4c3SManikanta Pubbisetty list_for_each_entry_rcu(sdata, &local->interfaces, list) {
41321a5d4c3SManikanta Pubbisetty int ac;
41421a5d4c3SManikanta Pubbisetty
41521a5d4c3SManikanta Pubbisetty for (ac = 0; ac < n_acs; ac++) {
41621a5d4c3SManikanta Pubbisetty int ac_queue = sdata->vif.hw_queue[ac];
41721a5d4c3SManikanta Pubbisetty
41821a5d4c3SManikanta Pubbisetty if (ac_queue == i ||
41921a5d4c3SManikanta Pubbisetty sdata->vif.cab_queue == i)
42021a5d4c3SManikanta Pubbisetty __ieee80211_wake_txqs(sdata, ac);
42121a5d4c3SManikanta Pubbisetty }
42221a5d4c3SManikanta Pubbisetty }
423f6c7f03fSEmmanuel Grumbach spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
42421a5d4c3SManikanta Pubbisetty }
42521a5d4c3SManikanta Pubbisetty
42621a5d4c3SManikanta Pubbisetty rcu_read_unlock();
42721a5d4c3SManikanta Pubbisetty }
42821a5d4c3SManikanta Pubbisetty
ieee80211_wake_txqs(struct tasklet_struct * t)429da1cad73SAllen Pais void ieee80211_wake_txqs(struct tasklet_struct *t)
430f6c7f03fSEmmanuel Grumbach {
431da1cad73SAllen Pais struct ieee80211_local *local = from_tasklet(local, t,
432da1cad73SAllen Pais wake_txqs_tasklet);
433f6c7f03fSEmmanuel Grumbach unsigned long flags;
434f6c7f03fSEmmanuel Grumbach
435f6c7f03fSEmmanuel Grumbach spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
436f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(local, &flags);
437f6c7f03fSEmmanuel Grumbach spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
438f6c7f03fSEmmanuel Grumbach }
439f6c7f03fSEmmanuel Grumbach
__ieee80211_wake_queue(struct ieee80211_hw * hw,int queue,enum queue_stop_reason reason,bool refcounted,unsigned long * flags)440ce7c9111SKalle Valo static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
441cca07b00SLuciano Coelho enum queue_stop_reason reason,
442f6c7f03fSEmmanuel Grumbach bool refcounted,
443f6c7f03fSEmmanuel Grumbach unsigned long *flags)
444c2d1560aSJohannes Berg {
445c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
446c2d1560aSJohannes Berg
447b5878a2dSJohannes Berg trace_wake_queue(local, queue, reason);
448b5878a2dSJohannes Berg
449e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues))
45096f5e66eSJohannes Berg return;
45196f5e66eSJohannes Berg
452ada15125SJohannes Berg if (!test_bit(reason, &local->queue_stop_reasons[queue]))
453ada15125SJohannes Berg return;
454ada15125SJohannes Berg
455856142cdSJohannes Berg if (!refcounted) {
456cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason] = 0;
457856142cdSJohannes Berg } else {
458cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason]--;
459856142cdSJohannes Berg if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
460856142cdSJohannes Berg local->q_stop_reasons[queue][reason] = 0;
461856142cdSJohannes Berg }
462cca07b00SLuciano Coelho
463cca07b00SLuciano Coelho if (local->q_stop_reasons[queue][reason] == 0)
464ce7c9111SKalle Valo __clear_bit(reason, &local->queue_stop_reasons[queue]);
465ce7c9111SKalle Valo
466ce7c9111SKalle Valo if (local->queue_stop_reasons[queue] != 0)
467ce7c9111SKalle Valo /* someone still has this queue stopped */
468ce7c9111SKalle Valo return;
469ce7c9111SKalle Valo
470107395f9SAlexander Wetzel if (!skb_queue_empty(&local->pending[queue]))
4717236fe29SJohannes Berg tasklet_schedule(&local->tx_pending_tasklet);
47221a5d4c3SManikanta Pubbisetty
473f6c7f03fSEmmanuel Grumbach /*
474f6c7f03fSEmmanuel Grumbach * Calling _ieee80211_wake_txqs here can be a problem because it may
475f6c7f03fSEmmanuel Grumbach * release queue_stop_reason_lock which has been taken by
476f6c7f03fSEmmanuel Grumbach * __ieee80211_wake_queue's caller. It is certainly not very nice to
477f6c7f03fSEmmanuel Grumbach * release someone's lock, but it is fine because all the callers of
478f6c7f03fSEmmanuel Grumbach * __ieee80211_wake_queue call it right before releasing the lock.
479f6c7f03fSEmmanuel Grumbach */
480f6c7f03fSEmmanuel Grumbach if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
48121a5d4c3SManikanta Pubbisetty tasklet_schedule(&local->wake_txqs_tasklet);
482f6c7f03fSEmmanuel Grumbach else
483f6c7f03fSEmmanuel Grumbach _ieee80211_wake_txqs(local, flags);
484f6c7f03fSEmmanuel Grumbach }
485ce7c9111SKalle Valo
ieee80211_wake_queue_by_reason(struct ieee80211_hw * hw,int queue,enum queue_stop_reason reason,bool refcounted)48696f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
487cca07b00SLuciano Coelho enum queue_stop_reason reason,
488cca07b00SLuciano Coelho bool refcounted)
489ce7c9111SKalle Valo {
490ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw);
491ce7c9111SKalle Valo unsigned long flags;
492ce7c9111SKalle Valo
493ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
494f6c7f03fSEmmanuel Grumbach __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
495ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
496ce7c9111SKalle Valo }
497ce7c9111SKalle Valo
ieee80211_wake_queue(struct ieee80211_hw * hw,int queue)498ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
499ce7c9111SKalle Valo {
500ce7c9111SKalle Valo ieee80211_wake_queue_by_reason(hw, queue,
501cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER,
502cca07b00SLuciano Coelho false);
503ce7c9111SKalle Valo }
504c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue);
505c2d1560aSJohannes Berg
__ieee80211_stop_queue(struct ieee80211_hw * hw,int queue,enum queue_stop_reason reason,bool refcounted)506ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
507cca07b00SLuciano Coelho enum queue_stop_reason reason,
508cca07b00SLuciano Coelho bool refcounted)
509c2d1560aSJohannes Berg {
510c2d1560aSJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
511c2d1560aSJohannes Berg
512b5878a2dSJohannes Berg trace_stop_queue(local, queue, reason);
513b5878a2dSJohannes Berg
514e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues))
51596f5e66eSJohannes Berg return;
51696f5e66eSJohannes Berg
517cca07b00SLuciano Coelho if (!refcounted)
518cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason] = 1;
519cca07b00SLuciano Coelho else
520cca07b00SLuciano Coelho local->q_stop_reasons[queue][reason]++;
521ada15125SJohannes Berg
5224444bc21SAlexander Wetzel set_bit(reason, &local->queue_stop_reasons[queue]);
523c2d1560aSJohannes Berg }
524ce7c9111SKalle Valo
ieee80211_stop_queue_by_reason(struct ieee80211_hw * hw,int queue,enum queue_stop_reason reason,bool refcounted)52596f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
526cca07b00SLuciano Coelho enum queue_stop_reason reason,
527cca07b00SLuciano Coelho bool refcounted)
528ce7c9111SKalle Valo {
529ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw);
530ce7c9111SKalle Valo unsigned long flags;
531ce7c9111SKalle Valo
532ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
533cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, queue, reason, refcounted);
534ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
535ce7c9111SKalle Valo }
536ce7c9111SKalle Valo
ieee80211_stop_queue(struct ieee80211_hw * hw,int queue)537ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
538ce7c9111SKalle Valo {
539ce7c9111SKalle Valo ieee80211_stop_queue_by_reason(hw, queue,
540cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER,
541cca07b00SLuciano Coelho false);
542ce7c9111SKalle Valo }
543c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue);
544c2d1560aSJohannes Berg
ieee80211_add_pending_skb(struct ieee80211_local * local,struct sk_buff * skb)5458f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local,
5468f77f384SJohannes Berg struct sk_buff *skb)
5478f77f384SJohannes Berg {
5488f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw;
5498f77f384SJohannes Berg unsigned long flags;
550a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
5513a25a8c8SJohannes Berg int queue = info->hw_queue;
552a7bc376cSJohannes Berg
553a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) {
554d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb);
555a7bc376cSJohannes Berg return;
556a7bc376cSJohannes Berg }
5578f77f384SJohannes Berg
5588f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
559cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
560cca07b00SLuciano Coelho false);
5613b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb);
562cca07b00SLuciano Coelho __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
563f6c7f03fSEmmanuel Grumbach false, &flags);
5648f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
5658f77f384SJohannes Berg }
5668f77f384SJohannes Berg
ieee80211_add_pending_skbs(struct ieee80211_local * local,struct sk_buff_head * skbs)567e3685e03SJohannes Berg void ieee80211_add_pending_skbs(struct ieee80211_local *local,
568e3685e03SJohannes Berg struct sk_buff_head *skbs)
5698f77f384SJohannes Berg {
5708f77f384SJohannes Berg struct ieee80211_hw *hw = &local->hw;
5718f77f384SJohannes Berg struct sk_buff *skb;
5728f77f384SJohannes Berg unsigned long flags;
573b0b97a8aSJohannes Berg int queue, i;
5748f77f384SJohannes Berg
5758f77f384SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
5768f77f384SJohannes Berg while ((skb = skb_dequeue(skbs))) {
577a7bc376cSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
578a7bc376cSJohannes Berg
579a7bc376cSJohannes Berg if (WARN_ON(!info->control.vif)) {
580d4fa14cdSFelix Fietkau ieee80211_free_txskb(&local->hw, skb);
581a7bc376cSJohannes Berg continue;
582a7bc376cSJohannes Berg }
583a7bc376cSJohannes Berg
5843a25a8c8SJohannes Berg queue = info->hw_queue;
5854644ae89SJohannes Berg
5864644ae89SJohannes Berg __ieee80211_stop_queue(hw, queue,
587cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
588cca07b00SLuciano Coelho false);
5894644ae89SJohannes Berg
5903b8d81e0SJohannes Berg __skb_queue_tail(&local->pending[queue], skb);
5918f77f384SJohannes Berg }
5928f77f384SJohannes Berg
5933b8d81e0SJohannes Berg for (i = 0; i < hw->queues; i++)
5948f77f384SJohannes Berg __ieee80211_wake_queue(hw, i,
595cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
596f6c7f03fSEmmanuel Grumbach false, &flags);
5978f77f384SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
5988f77f384SJohannes Berg }
5998f77f384SJohannes Berg
ieee80211_stop_queues_by_reason(struct ieee80211_hw * hw,unsigned long queues,enum queue_stop_reason reason,bool refcounted)600ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
601445ea4e8SJohannes Berg unsigned long queues,
602cca07b00SLuciano Coelho enum queue_stop_reason reason,
603cca07b00SLuciano Coelho bool refcounted)
604c2d1560aSJohannes Berg {
605ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw);
606ce7c9111SKalle Valo unsigned long flags;
607c2d1560aSJohannes Berg int i;
608c2d1560aSJohannes Berg
609ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
610ce7c9111SKalle Valo
611445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues)
612cca07b00SLuciano Coelho __ieee80211_stop_queue(hw, i, reason, refcounted);
613ce7c9111SKalle Valo
614ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
615ce7c9111SKalle Valo }
616ce7c9111SKalle Valo
ieee80211_stop_queues(struct ieee80211_hw * hw)617ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw)
618ce7c9111SKalle Valo {
619445ea4e8SJohannes Berg ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
620cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER,
621cca07b00SLuciano Coelho false);
622c2d1560aSJohannes Berg }
623c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues);
624c2d1560aSJohannes Berg
ieee80211_queue_stopped(struct ieee80211_hw * hw,int queue)62592ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
62692ab8535STomas Winkler {
62792ab8535STomas Winkler struct ieee80211_local *local = hw_to_local(hw);
6283b8d81e0SJohannes Berg unsigned long flags;
6293b8d81e0SJohannes Berg int ret;
63096f5e66eSJohannes Berg
631e4e72fb4SJohannes Berg if (WARN_ON(queue >= hw->queues))
63296f5e66eSJohannes Berg return true;
63396f5e66eSJohannes Berg
6343b8d81e0SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
6352419ea14SThomas Pedersen ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER,
6362419ea14SThomas Pedersen &local->queue_stop_reasons[queue]);
6373b8d81e0SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
6383b8d81e0SJohannes Berg return ret;
63992ab8535STomas Winkler }
64092ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped);
64192ab8535STomas Winkler
ieee80211_wake_queues_by_reason(struct ieee80211_hw * hw,unsigned long queues,enum queue_stop_reason reason,bool refcounted)642ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
643445ea4e8SJohannes Berg unsigned long queues,
644cca07b00SLuciano Coelho enum queue_stop_reason reason,
645cca07b00SLuciano Coelho bool refcounted)
646c2d1560aSJohannes Berg {
647ce7c9111SKalle Valo struct ieee80211_local *local = hw_to_local(hw);
648ce7c9111SKalle Valo unsigned long flags;
649c2d1560aSJohannes Berg int i;
650c2d1560aSJohannes Berg
651ce7c9111SKalle Valo spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
652ce7c9111SKalle Valo
653445ea4e8SJohannes Berg for_each_set_bit(i, &queues, hw->queues)
654f6c7f03fSEmmanuel Grumbach __ieee80211_wake_queue(hw, i, reason, refcounted, &flags);
655ce7c9111SKalle Valo
656ce7c9111SKalle Valo spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
657ce7c9111SKalle Valo }
658ce7c9111SKalle Valo
ieee80211_wake_queues(struct ieee80211_hw * hw)659ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw)
660ce7c9111SKalle Valo {
661445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
662cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_DRIVER,
663cca07b00SLuciano Coelho false);
664c2d1560aSJohannes Berg }
665c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues);
666dabeb344SJohannes Berg
66726da23b6SLuciano Coelho static unsigned int
ieee80211_get_vif_queues(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)66826da23b6SLuciano Coelho ieee80211_get_vif_queues(struct ieee80211_local *local,
66939ecc01dSJohannes Berg struct ieee80211_sub_if_data *sdata)
67039ecc01dSJohannes Berg {
67126da23b6SLuciano Coelho unsigned int queues;
67239ecc01dSJohannes Berg
67330686bf7SJohannes Berg if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
67439ecc01dSJohannes Berg int ac;
67539ecc01dSJohannes Berg
67639ecc01dSJohannes Berg queues = 0;
67739ecc01dSJohannes Berg
67839ecc01dSJohannes Berg for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
67939ecc01dSJohannes Berg queues |= BIT(sdata->vif.hw_queue[ac]);
68039ecc01dSJohannes Berg if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
68139ecc01dSJohannes Berg queues |= BIT(sdata->vif.cab_queue);
68239ecc01dSJohannes Berg } else {
68339ecc01dSJohannes Berg /* all queues */
68439ecc01dSJohannes Berg queues = BIT(local->hw.queues) - 1;
68539ecc01dSJohannes Berg }
68639ecc01dSJohannes Berg
68726da23b6SLuciano Coelho return queues;
68826da23b6SLuciano Coelho }
68926da23b6SLuciano Coelho
__ieee80211_flush_queues(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,unsigned int queues,bool drop)6904f9610d5SLiad Kaufman void __ieee80211_flush_queues(struct ieee80211_local *local,
6914f9610d5SLiad Kaufman struct ieee80211_sub_if_data *sdata,
6923b24f4c6SEmmanuel Grumbach unsigned int queues, bool drop)
69326da23b6SLuciano Coelho {
69426da23b6SLuciano Coelho if (!local->ops->flush)
69526da23b6SLuciano Coelho return;
69626da23b6SLuciano Coelho
6974f9610d5SLiad Kaufman /*
6984f9610d5SLiad Kaufman * If no queue was set, or if the HW doesn't support
6994f9610d5SLiad Kaufman * IEEE80211_HW_QUEUE_CONTROL - flush all queues
7004f9610d5SLiad Kaufman */
70130686bf7SJohannes Berg if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
70226da23b6SLuciano Coelho queues = ieee80211_get_vif_queues(local, sdata);
70326da23b6SLuciano Coelho
70459f48fe2SLuciano Coelho ieee80211_stop_queues_by_reason(&local->hw, queues,
705cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_FLUSH,
706cca07b00SLuciano Coelho false);
707445ea4e8SJohannes Berg
7083b24f4c6SEmmanuel Grumbach drv_flush(local, sdata, queues, drop);
709445ea4e8SJohannes Berg
71059f48fe2SLuciano Coelho ieee80211_wake_queues_by_reason(&local->hw, queues,
711cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_FLUSH,
712cca07b00SLuciano Coelho false);
71339ecc01dSJohannes Berg }
71439ecc01dSJohannes Berg
ieee80211_flush_queues(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,bool drop)7154f9610d5SLiad Kaufman void ieee80211_flush_queues(struct ieee80211_local *local,
7163b24f4c6SEmmanuel Grumbach struct ieee80211_sub_if_data *sdata, bool drop)
7174f9610d5SLiad Kaufman {
7183b24f4c6SEmmanuel Grumbach __ieee80211_flush_queues(local, sdata, 0, drop);
7194f9610d5SLiad Kaufman }
7204f9610d5SLiad Kaufman
ieee80211_stop_vif_queues(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,enum queue_stop_reason reason)72126da23b6SLuciano Coelho void ieee80211_stop_vif_queues(struct ieee80211_local *local,
72226da23b6SLuciano Coelho struct ieee80211_sub_if_data *sdata,
72326da23b6SLuciano Coelho enum queue_stop_reason reason)
72426da23b6SLuciano Coelho {
72526da23b6SLuciano Coelho ieee80211_stop_queues_by_reason(&local->hw,
72626da23b6SLuciano Coelho ieee80211_get_vif_queues(local, sdata),
72726da23b6SLuciano Coelho reason, true);
72826da23b6SLuciano Coelho }
72926da23b6SLuciano Coelho
ieee80211_wake_vif_queues(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,enum queue_stop_reason reason)73026da23b6SLuciano Coelho void ieee80211_wake_vif_queues(struct ieee80211_local *local,
73126da23b6SLuciano Coelho struct ieee80211_sub_if_data *sdata,
73226da23b6SLuciano Coelho enum queue_stop_reason reason)
73326da23b6SLuciano Coelho {
73426da23b6SLuciano Coelho ieee80211_wake_queues_by_reason(&local->hw,
73526da23b6SLuciano Coelho ieee80211_get_vif_queues(local, sdata),
73626da23b6SLuciano Coelho reason, true);
73726da23b6SLuciano Coelho }
73826da23b6SLuciano Coelho
__iterate_interfaces(struct ieee80211_local * local,u32 iter_flags,void (* iterator)(void * data,u8 * mac,struct ieee80211_vif * vif),void * data)7393384d757SArik Nemtsov static void __iterate_interfaces(struct ieee80211_local *local,
740c7c71066SJohannes Berg u32 iter_flags,
741dabeb344SJohannes Berg void (*iterator)(void *data, u8 *mac,
74232bfd35dSJohannes Berg struct ieee80211_vif *vif),
743dabeb344SJohannes Berg void *data)
744dabeb344SJohannes Berg {
745dabeb344SJohannes Berg struct ieee80211_sub_if_data *sdata;
7463384d757SArik Nemtsov bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
747dabeb344SJohannes Berg
748e38bad47SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) {
74951fb61e7SJohannes Berg switch (sdata->vif.type) {
75005c914feSJohannes Berg case NL80211_IFTYPE_MONITOR:
751d8212184SAviya Erenfeld if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
75231eba5bcSFelix Fietkau continue;
75331eba5bcSFelix Fietkau break;
75405c914feSJohannes Berg case NL80211_IFTYPE_AP_VLAN:
755dabeb344SJohannes Berg continue;
7562ca27bcfSJohannes Berg default:
757dabeb344SJohannes Berg break;
758dabeb344SJohannes Berg }
7598b2c9824SJohannes Berg if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
7603384d757SArik Nemtsov active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
7618b2c9824SJohannes Berg continue;
762265a0708SBen Greear if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
763265a0708SBen Greear !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
764265a0708SBen Greear continue;
7653384d757SArik Nemtsov if (ieee80211_sdata_running(sdata) || !active_only)
76647846c9bSJohannes Berg iterator(data, sdata->vif.addr,
76732bfd35dSJohannes Berg &sdata->vif);
768dabeb344SJohannes Berg }
769e38bad47SJohannes Berg
770c7c71066SJohannes Berg sdata = rcu_dereference_check(local->monitor_sdata,
771c7c71066SJohannes Berg lockdep_is_held(&local->iflist_mtx) ||
7726dd23603SJohannes Berg lockdep_is_held(&local->hw.wiphy->mtx));
7738b2c9824SJohannes Berg if (sdata &&
7743384d757SArik Nemtsov (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
7758b2c9824SJohannes Berg sdata->flags & IEEE80211_SDATA_IN_DRIVER))
776685fb72bSJohannes Berg iterator(data, sdata->vif.addr, &sdata->vif);
777c7c71066SJohannes Berg }
778685fb72bSJohannes Berg
ieee80211_iterate_interfaces(struct ieee80211_hw * hw,u32 iter_flags,void (* iterator)(void * data,u8 * mac,struct ieee80211_vif * vif),void * data)7793384d757SArik Nemtsov void ieee80211_iterate_interfaces(
780c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags,
781c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac,
782c7c71066SJohannes Berg struct ieee80211_vif *vif),
783c7c71066SJohannes Berg void *data)
784c7c71066SJohannes Berg {
785c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
786c7c71066SJohannes Berg
787c7c71066SJohannes Berg mutex_lock(&local->iflist_mtx);
7883384d757SArik Nemtsov __iterate_interfaces(local, iter_flags, iterator, data);
789c7c71066SJohannes Berg mutex_unlock(&local->iflist_mtx);
790c7c71066SJohannes Berg }
7913384d757SArik Nemtsov EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);
792c7c71066SJohannes Berg
ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw * hw,u32 iter_flags,void (* iterator)(void * data,u8 * mac,struct ieee80211_vif * vif),void * data)793c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic(
794c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags,
795c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac,
796c7c71066SJohannes Berg struct ieee80211_vif *vif),
797c7c71066SJohannes Berg void *data)
798c7c71066SJohannes Berg {
799c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
800c7c71066SJohannes Berg
801c7c71066SJohannes Berg rcu_read_lock();
8023384d757SArik Nemtsov __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
8033384d757SArik Nemtsov iterator, data);
804e38bad47SJohannes Berg rcu_read_unlock();
805dabeb344SJohannes Berg }
8062f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
80737ffc8daSJohannes Berg
ieee80211_iterate_active_interfaces_mtx(struct ieee80211_hw * hw,u32 iter_flags,void (* iterator)(void * data,u8 * mac,struct ieee80211_vif * vif),void * data)808a05829a7SJohannes Berg void ieee80211_iterate_active_interfaces_mtx(
809c7c71066SJohannes Berg struct ieee80211_hw *hw, u32 iter_flags,
810c7c71066SJohannes Berg void (*iterator)(void *data, u8 *mac,
811c7c71066SJohannes Berg struct ieee80211_vif *vif),
812c7c71066SJohannes Berg void *data)
813c7c71066SJohannes Berg {
814c7c71066SJohannes Berg struct ieee80211_local *local = hw_to_local(hw);
815c7c71066SJohannes Berg
816a05829a7SJohannes Berg lockdep_assert_wiphy(hw->wiphy);
817c7c71066SJohannes Berg
8183384d757SArik Nemtsov __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
8193384d757SArik Nemtsov iterator, data);
820c7c71066SJohannes Berg }
821a05829a7SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx);
822c7c71066SJohannes Berg
__iterate_stations(struct ieee80211_local * local,void (* iterator)(void * data,struct ieee80211_sta * sta),void * data)8230fc1e049SArik Nemtsov static void __iterate_stations(struct ieee80211_local *local,
8240fc1e049SArik Nemtsov void (*iterator)(void *data,
8250fc1e049SArik Nemtsov struct ieee80211_sta *sta),
8260fc1e049SArik Nemtsov void *data)
8270fc1e049SArik Nemtsov {
8280fc1e049SArik Nemtsov struct sta_info *sta;
8290fc1e049SArik Nemtsov
8300fc1e049SArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) {
8310fc1e049SArik Nemtsov if (!sta->uploaded)
8320fc1e049SArik Nemtsov continue;
8330fc1e049SArik Nemtsov
8340fc1e049SArik Nemtsov iterator(data, &sta->sta);
8350fc1e049SArik Nemtsov }
8360fc1e049SArik Nemtsov }
8370fc1e049SArik Nemtsov
ieee80211_iterate_stations_atomic(struct ieee80211_hw * hw,void (* iterator)(void * data,struct ieee80211_sta * sta),void * data)8380fc1e049SArik Nemtsov void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
8390fc1e049SArik Nemtsov void (*iterator)(void *data,
8400fc1e049SArik Nemtsov struct ieee80211_sta *sta),
8410fc1e049SArik Nemtsov void *data)
8420fc1e049SArik Nemtsov {
8430fc1e049SArik Nemtsov struct ieee80211_local *local = hw_to_local(hw);
8440fc1e049SArik Nemtsov
8450fc1e049SArik Nemtsov rcu_read_lock();
8460fc1e049SArik Nemtsov __iterate_stations(local, iterator, data);
8470fc1e049SArik Nemtsov rcu_read_unlock();
8480fc1e049SArik Nemtsov }
8490fc1e049SArik Nemtsov EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
8500fc1e049SArik Nemtsov
wdev_to_ieee80211_vif(struct wireless_dev * wdev)851ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
852ad7e718cSJohannes Berg {
853ad7e718cSJohannes Berg struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
854ad7e718cSJohannes Berg
855ad7e718cSJohannes Berg if (!ieee80211_sdata_running(sdata) ||
856ad7e718cSJohannes Berg !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
857ad7e718cSJohannes Berg return NULL;
858ad7e718cSJohannes Berg return &sdata->vif;
859ad7e718cSJohannes Berg }
860ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
861ad7e718cSJohannes Berg
ieee80211_vif_to_wdev(struct ieee80211_vif * vif)862dc5a1ad7SEmmanuel Grumbach struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
863dc5a1ad7SEmmanuel Grumbach {
8646513e98eSJohannes Berg if (!vif)
8656513e98eSJohannes Berg return NULL;
8666513e98eSJohannes Berg
867f30386a8SEmmanuel Grumbach return &vif_to_sdata(vif)->wdev;
868dc5a1ad7SEmmanuel Grumbach }
869dc5a1ad7SEmmanuel Grumbach EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
870dc5a1ad7SEmmanuel Grumbach
87142935ecaSLuis R. Rodriguez /*
87242935ecaSLuis R. Rodriguez * Nothing should have been stuffed into the workqueue during
8734afaff17SEmmanuel Grumbach * the suspend->resume cycle. Since we can't check each caller
8744afaff17SEmmanuel Grumbach * of this function if we are already quiescing / suspended,
8754afaff17SEmmanuel Grumbach * check here and don't WARN since this can actually happen when
8764afaff17SEmmanuel Grumbach * the rx path (for example) is racing against __ieee80211_suspend
8774afaff17SEmmanuel Grumbach * and suspending / quiescing was set after the rx path checked
8784afaff17SEmmanuel Grumbach * them.
87942935ecaSLuis R. Rodriguez */
ieee80211_can_queue_work(struct ieee80211_local * local)88042935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local)
88142935ecaSLuis R. Rodriguez {
8824afaff17SEmmanuel Grumbach if (local->quiescing || (local->suspended && !local->resuming)) {
8834afaff17SEmmanuel Grumbach pr_warn("queueing ieee80211 work while going to suspend\n");
88442935ecaSLuis R. Rodriguez return false;
8854afaff17SEmmanuel Grumbach }
88642935ecaSLuis R. Rodriguez
88742935ecaSLuis R. Rodriguez return true;
88842935ecaSLuis R. Rodriguez }
88942935ecaSLuis R. Rodriguez
ieee80211_queue_work(struct ieee80211_hw * hw,struct work_struct * work)89042935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
89142935ecaSLuis R. Rodriguez {
89242935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw);
89342935ecaSLuis R. Rodriguez
89442935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local))
89542935ecaSLuis R. Rodriguez return;
89642935ecaSLuis R. Rodriguez
89742935ecaSLuis R. Rodriguez queue_work(local->workqueue, work);
89842935ecaSLuis R. Rodriguez }
89942935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work);
90042935ecaSLuis R. Rodriguez
ieee80211_queue_delayed_work(struct ieee80211_hw * hw,struct delayed_work * dwork,unsigned long delay)90142935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
90242935ecaSLuis R. Rodriguez struct delayed_work *dwork,
90342935ecaSLuis R. Rodriguez unsigned long delay)
90442935ecaSLuis R. Rodriguez {
90542935ecaSLuis R. Rodriguez struct ieee80211_local *local = hw_to_local(hw);
90642935ecaSLuis R. Rodriguez
90742935ecaSLuis R. Rodriguez if (!ieee80211_can_queue_work(local))
90842935ecaSLuis R. Rodriguez return;
90942935ecaSLuis R. Rodriguez
91042935ecaSLuis R. Rodriguez queue_delayed_work(local->workqueue, dwork, delay);
91142935ecaSLuis R. Rodriguez }
91242935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work);
91342935ecaSLuis R. Rodriguez
914ea5cba26SJohannes Berg static void
ieee80211_parse_extension_element(u32 * crc,const struct element * elem,struct ieee802_11_elems * elems,struct ieee80211_elems_parse_params * params)915ea5cba26SJohannes Berg ieee80211_parse_extension_element(u32 *crc,
916e4d005b8SJohannes Berg const struct element *elem,
917ea5cba26SJohannes Berg struct ieee802_11_elems *elems,
918ea5cba26SJohannes Berg struct ieee80211_elems_parse_params *params)
919e4d005b8SJohannes Berg {
920e4d005b8SJohannes Berg const void *data = elem->data + 1;
9212829b2fcSJohannes Berg bool calc_crc = false;
922768c0b19SJohannes Berg u8 len;
923768c0b19SJohannes Berg
924768c0b19SJohannes Berg if (!elem->datalen)
925768c0b19SJohannes Berg return;
926768c0b19SJohannes Berg
927768c0b19SJohannes Berg len = elem->datalen - 1;
928e4d005b8SJohannes Berg
929e4d005b8SJohannes Berg switch (elem->data[0]) {
930e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_MU_EDCA:
9312829b2fcSJohannes Berg calc_crc = true;
9322829b2fcSJohannes Berg if (len >= sizeof(*elems->mu_edca_param_set))
933e4d005b8SJohannes Berg elems->mu_edca_param_set = data;
934e4d005b8SJohannes Berg break;
935e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_CAPABILITY:
936bd4e4d62SJohannes Berg if (ieee80211_he_capa_size_ok(data, len)) {
937e4d005b8SJohannes Berg elems->he_cap = data;
938e4d005b8SJohannes Berg elems->he_cap_len = len;
939bd4e4d62SJohannes Berg }
940e4d005b8SJohannes Berg break;
941e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_OPERATION:
9422829b2fcSJohannes Berg calc_crc = true;
943e4d005b8SJohannes Berg if (len >= sizeof(*elems->he_operation) &&
9442829b2fcSJohannes Berg len >= ieee80211_he_oper_size(data) - 1)
945e4d005b8SJohannes Berg elems->he_operation = data;
946e4d005b8SJohannes Berg break;
947e4d005b8SJohannes Berg case WLAN_EID_EXT_UORA:
948652e8363SJohannes Berg if (len >= 1)
949e4d005b8SJohannes Berg elems->uora_element = data;
950e4d005b8SJohannes Berg break;
951e4d005b8SJohannes Berg case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME:
952e4d005b8SJohannes Berg if (len == 3)
953e4d005b8SJohannes Berg elems->max_channel_switch_time = data;
954e4d005b8SJohannes Berg break;
955e4d005b8SJohannes Berg case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION:
956652e8363SJohannes Berg if (len >= sizeof(*elems->mbssid_config_ie))
957e4d005b8SJohannes Berg elems->mbssid_config_ie = data;
958e4d005b8SJohannes Berg break;
959e4d005b8SJohannes Berg case WLAN_EID_EXT_HE_SPR:
960e4d005b8SJohannes Berg if (len >= sizeof(*elems->he_spr) &&
961e4d005b8SJohannes Berg len >= ieee80211_he_spr_size(data))
962e4d005b8SJohannes Berg elems->he_spr = data;
963e4d005b8SJohannes Berg break;
964a6cf28e0SRajkumar Manoharan case WLAN_EID_EXT_HE_6GHZ_CAPA:
965652e8363SJohannes Berg if (len >= sizeof(*elems->he_6ghz_capa))
966a6cf28e0SRajkumar Manoharan elems->he_6ghz_capa = data;
967a6cf28e0SRajkumar Manoharan break;
968f0e6bea8SIlan Peer case WLAN_EID_EXT_EHT_CAPABILITY:
969f0e6bea8SIlan Peer if (ieee80211_eht_capa_size_ok(elems->he_cap,
970ea5cba26SJohannes Berg data, len,
971ea5cba26SJohannes Berg params->from_ap)) {
972f0e6bea8SIlan Peer elems->eht_cap = data;
973f0e6bea8SIlan Peer elems->eht_cap_len = len;
974f0e6bea8SIlan Peer }
975f0e6bea8SIlan Peer break;
976f0e6bea8SIlan Peer case WLAN_EID_EXT_EHT_OPERATION:
977f0e6bea8SIlan Peer if (ieee80211_eht_oper_size_ok(data, len))
978f0e6bea8SIlan Peer elems->eht_operation = data;
9792829b2fcSJohannes Berg calc_crc = true;
980f0e6bea8SIlan Peer break;
981425f4b5fSJohannes Berg case WLAN_EID_EXT_EHT_MULTI_LINK:
9822829b2fcSJohannes Berg calc_crc = true;
9832829b2fcSJohannes Berg
98445ebac4fSIlan Peer if (ieee80211_mle_size_ok(data, len)) {
985cf36cdefSIlan Peer const struct ieee80211_multi_link_elem *mle =
986cf36cdefSIlan Peer (void *)data;
987cf36cdefSIlan Peer
988cf36cdefSIlan Peer switch (le16_get_bits(mle->control,
989cf36cdefSIlan Peer IEEE80211_ML_CONTROL_TYPE)) {
990cf36cdefSIlan Peer case IEEE80211_ML_CONTROL_TYPE_BASIC:
991a286de1aSIlan Peer elems->ml_basic_elem = (void *)elem;
992cf36cdefSIlan Peer elems->ml_basic = data;
993a286de1aSIlan Peer elems->ml_basic_len = len;
994cf36cdefSIlan Peer break;
995cf36cdefSIlan Peer case IEEE80211_ML_CONTROL_TYPE_RECONF:
996cf36cdefSIlan Peer elems->ml_reconf_elem = (void *)elem;
997cf36cdefSIlan Peer elems->ml_reconf = data;
998cf36cdefSIlan Peer elems->ml_reconf_len = len;
999cf36cdefSIlan Peer break;
1000cf36cdefSIlan Peer default:
1001cf36cdefSIlan Peer break;
1002cf36cdefSIlan Peer }
100345ebac4fSIlan Peer }
1004425f4b5fSJohannes Berg break;
1005e4d005b8SJohannes Berg }
10062829b2fcSJohannes Berg
10072829b2fcSJohannes Berg if (crc && calc_crc)
10082829b2fcSJohannes Berg *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2);
1009e4d005b8SJohannes Berg }
1010e4d005b8SJohannes Berg
101178ac51f8SSara Sharon static u32
_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params * params,struct ieee802_11_elems * elems,const struct element * check_inherit)1012fd17bf04SJohannes Berg _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
1013dd76986bSJohannes Berg struct ieee802_11_elems *elems,
1014671042a4SSara Sharon const struct element *check_inherit)
1015dd76986bSJohannes Berg {
1016671042a4SSara Sharon const struct element *elem;
1017fd17bf04SJohannes Berg bool calc_crc = params->filter != 0;
1018fcff4f10SPaul Stewart DECLARE_BITMAP(seen_elems, 256);
1019fd17bf04SJohannes Berg u32 crc = params->crc;
1020b2e506bfSJohannes Berg const u8 *ie;
1021dd76986bSJohannes Berg
1022fcff4f10SPaul Stewart bitmap_zero(seen_elems, 256);
1023dd76986bSJohannes Berg
1024fd17bf04SJohannes Berg for_each_element(elem, params->start, params->len) {
1025fcff4f10SPaul Stewart bool elem_parse_failed;
1026c17e28d1SJohannes Berg u8 id = elem->id;
1027c17e28d1SJohannes Berg u8 elen = elem->datalen;
1028c17e28d1SJohannes Berg const u8 *pos = elem->data;
1029fcff4f10SPaul Stewart
1030671042a4SSara Sharon if (check_inherit &&
1031671042a4SSara Sharon !cfg80211_is_element_inherited(elem,
1032671042a4SSara Sharon check_inherit))
1033671042a4SSara Sharon continue;
1034671042a4SSara Sharon
10359690fb16SJohannes Berg switch (id) {
10369690fb16SJohannes Berg case WLAN_EID_SSID:
10379690fb16SJohannes Berg case WLAN_EID_SUPP_RATES:
10389690fb16SJohannes Berg case WLAN_EID_FH_PARAMS:
10399690fb16SJohannes Berg case WLAN_EID_DS_PARAMS:
10409690fb16SJohannes Berg case WLAN_EID_CF_PARAMS:
10419690fb16SJohannes Berg case WLAN_EID_TIM:
10429690fb16SJohannes Berg case WLAN_EID_IBSS_PARAMS:
10439690fb16SJohannes Berg case WLAN_EID_CHALLENGE:
10449690fb16SJohannes Berg case WLAN_EID_RSN:
10459690fb16SJohannes Berg case WLAN_EID_ERP_INFO:
10469690fb16SJohannes Berg case WLAN_EID_EXT_SUPP_RATES:
10479690fb16SJohannes Berg case WLAN_EID_HT_CAPABILITY:
10489690fb16SJohannes Berg case WLAN_EID_HT_OPERATION:
10499690fb16SJohannes Berg case WLAN_EID_VHT_CAPABILITY:
10509690fb16SJohannes Berg case WLAN_EID_VHT_OPERATION:
10519690fb16SJohannes Berg case WLAN_EID_MESH_ID:
10529690fb16SJohannes Berg case WLAN_EID_MESH_CONFIG:
10539690fb16SJohannes Berg case WLAN_EID_PEER_MGMT:
10549690fb16SJohannes Berg case WLAN_EID_PREQ:
10559690fb16SJohannes Berg case WLAN_EID_PREP:
10569690fb16SJohannes Berg case WLAN_EID_PERR:
10579690fb16SJohannes Berg case WLAN_EID_RANN:
10589690fb16SJohannes Berg case WLAN_EID_CHANNEL_SWITCH:
10599690fb16SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN:
10609690fb16SJohannes Berg case WLAN_EID_COUNTRY:
10619690fb16SJohannes Berg case WLAN_EID_PWR_CONSTRAINT:
10629690fb16SJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL:
106385220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
1064b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
10658f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM:
10669041c1faSArik Nemtsov case WLAN_EID_EXT_CAPABILITY:
106753837584SArik Nemtsov case WLAN_EID_CHAN_SWITCH_TIMING:
106853837584SArik Nemtsov case WLAN_EID_LINK_ID:
1069e38a017bSAvraham Stern case WLAN_EID_BSS_MAX_IDLE_PERIOD:
1070c0058df7SShaul Triebitz case WLAN_EID_RSNX:
1071cd418ba6SThomas Pedersen case WLAN_EID_S1G_BCN_COMPAT:
1072cd418ba6SThomas Pedersen case WLAN_EID_S1G_CAPABILITIES:
1073cd418ba6SThomas Pedersen case WLAN_EID_S1G_OPERATION:
10741d00ce80SThomas Pedersen case WLAN_EID_AID_RESPONSE:
1075cd418ba6SThomas Pedersen case WLAN_EID_S1G_SHORT_BCN_INTERVAL:
1076b2e506bfSJohannes Berg /*
1077b2e506bfSJohannes Berg * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
1078b2e506bfSJohannes Berg * that if the content gets bigger it might be needed more than once
1079b2e506bfSJohannes Berg */
10809690fb16SJohannes Berg if (test_bit(id, seen_elems)) {
1081fcff4f10SPaul Stewart elems->parse_error = true;
1082fcff4f10SPaul Stewart continue;
1083fcff4f10SPaul Stewart }
10849690fb16SJohannes Berg break;
10859690fb16SJohannes Berg }
1086dd76986bSJohannes Berg
1087fd17bf04SJohannes Berg if (calc_crc && id < 64 && (params->filter & (1ULL << id)))
1088dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2);
1089dd76986bSJohannes Berg
1090fcff4f10SPaul Stewart elem_parse_failed = false;
1091fcff4f10SPaul Stewart
1092dd76986bSJohannes Berg switch (id) {
109353837584SArik Nemtsov case WLAN_EID_LINK_ID:
1094652e8363SJohannes Berg if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) {
109553837584SArik Nemtsov elem_parse_failed = true;
109653837584SArik Nemtsov break;
109753837584SArik Nemtsov }
109853837584SArik Nemtsov elems->lnk_id = (void *)(pos - 2);
109953837584SArik Nemtsov break;
110053837584SArik Nemtsov case WLAN_EID_CHAN_SWITCH_TIMING:
1101652e8363SJohannes Berg if (elen < sizeof(struct ieee80211_ch_switch_timing)) {
110253837584SArik Nemtsov elem_parse_failed = true;
110353837584SArik Nemtsov break;
110453837584SArik Nemtsov }
110553837584SArik Nemtsov elems->ch_sw_timing = (void *)pos;
110653837584SArik Nemtsov break;
11079041c1faSArik Nemtsov case WLAN_EID_EXT_CAPABILITY:
11089041c1faSArik Nemtsov elems->ext_capab = pos;
11099041c1faSArik Nemtsov elems->ext_capab_len = elen;
11109041c1faSArik Nemtsov break;
1111dd76986bSJohannes Berg case WLAN_EID_SSID:
1112dd76986bSJohannes Berg elems->ssid = pos;
1113dd76986bSJohannes Berg elems->ssid_len = elen;
1114dd76986bSJohannes Berg break;
1115dd76986bSJohannes Berg case WLAN_EID_SUPP_RATES:
1116dd76986bSJohannes Berg elems->supp_rates = pos;
1117dd76986bSJohannes Berg elems->supp_rates_len = elen;
1118dd76986bSJohannes Berg break;
1119dd76986bSJohannes Berg case WLAN_EID_DS_PARAMS:
11201cd8e88eSJohannes Berg if (elen >= 1)
1121dd76986bSJohannes Berg elems->ds_params = pos;
11221cd8e88eSJohannes Berg else
11231cd8e88eSJohannes Berg elem_parse_failed = true;
1124dd76986bSJohannes Berg break;
1125dd76986bSJohannes Berg case WLAN_EID_TIM:
1126dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_tim_ie)) {
1127dd76986bSJohannes Berg elems->tim = (void *)pos;
1128dd76986bSJohannes Berg elems->tim_len = elen;
1129fcff4f10SPaul Stewart } else
1130fcff4f10SPaul Stewart elem_parse_failed = true;
1131dd76986bSJohannes Berg break;
1132dd76986bSJohannes Berg case WLAN_EID_VENDOR_SPECIFIC:
1133dd76986bSJohannes Berg if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
1134dd76986bSJohannes Berg pos[2] == 0xf2) {
1135dd76986bSJohannes Berg /* Microsoft OUI (00:50:F2) */
1136dd76986bSJohannes Berg
1137dd76986bSJohannes Berg if (calc_crc)
1138dd76986bSJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2);
1139dd76986bSJohannes Berg
1140441a33baSJohannes Berg if (elen >= 5 && pos[3] == 2) {
1141dd76986bSJohannes Berg /* OUI Type 2 - WMM IE */
1142dd76986bSJohannes Berg if (pos[4] == 0) {
1143dd76986bSJohannes Berg elems->wmm_info = pos;
1144dd76986bSJohannes Berg elems->wmm_info_len = elen;
1145dd76986bSJohannes Berg } else if (pos[4] == 1) {
1146dd76986bSJohannes Berg elems->wmm_param = pos;
1147dd76986bSJohannes Berg elems->wmm_param_len = elen;
1148dd76986bSJohannes Berg }
1149dd76986bSJohannes Berg }
1150dd76986bSJohannes Berg }
1151dd76986bSJohannes Berg break;
1152dd76986bSJohannes Berg case WLAN_EID_RSN:
1153dd76986bSJohannes Berg elems->rsn = pos;
1154dd76986bSJohannes Berg elems->rsn_len = elen;
1155dd76986bSJohannes Berg break;
1156dd76986bSJohannes Berg case WLAN_EID_ERP_INFO:
11571946bed9SJohannes Berg if (elen >= 1)
1158dd76986bSJohannes Berg elems->erp_info = pos;
11591946bed9SJohannes Berg else
11601946bed9SJohannes Berg elem_parse_failed = true;
1161dd76986bSJohannes Berg break;
1162dd76986bSJohannes Berg case WLAN_EID_EXT_SUPP_RATES:
1163dd76986bSJohannes Berg elems->ext_supp_rates = pos;
1164dd76986bSJohannes Berg elems->ext_supp_rates_len = elen;
1165dd76986bSJohannes Berg break;
1166dd76986bSJohannes Berg case WLAN_EID_HT_CAPABILITY:
1167dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_ht_cap))
1168dd76986bSJohannes Berg elems->ht_cap_elem = (void *)pos;
1169fcff4f10SPaul Stewart else
1170fcff4f10SPaul Stewart elem_parse_failed = true;
1171dd76986bSJohannes Berg break;
1172074d46d1SJohannes Berg case WLAN_EID_HT_OPERATION:
1173074d46d1SJohannes Berg if (elen >= sizeof(struct ieee80211_ht_operation))
1174074d46d1SJohannes Berg elems->ht_operation = (void *)pos;
1175fcff4f10SPaul Stewart else
1176fcff4f10SPaul Stewart elem_parse_failed = true;
1177dd76986bSJohannes Berg break;
1178818255eaSMahesh Palivela case WLAN_EID_VHT_CAPABILITY:
1179818255eaSMahesh Palivela if (elen >= sizeof(struct ieee80211_vht_cap))
1180818255eaSMahesh Palivela elems->vht_cap_elem = (void *)pos;
1181818255eaSMahesh Palivela else
1182818255eaSMahesh Palivela elem_parse_failed = true;
1183818255eaSMahesh Palivela break;
1184818255eaSMahesh Palivela case WLAN_EID_VHT_OPERATION:
1185a04564c9SJohannes Berg if (elen >= sizeof(struct ieee80211_vht_operation)) {
1186818255eaSMahesh Palivela elems->vht_operation = (void *)pos;
1187a04564c9SJohannes Berg if (calc_crc)
1188a04564c9SJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2);
1189a04564c9SJohannes Berg break;
1190a04564c9SJohannes Berg }
1191818255eaSMahesh Palivela elem_parse_failed = true;
1192818255eaSMahesh Palivela break;
1193bee7f586SJohannes Berg case WLAN_EID_OPMODE_NOTIF:
1194a04564c9SJohannes Berg if (elen > 0) {
1195bee7f586SJohannes Berg elems->opmode_notif = pos;
1196a04564c9SJohannes Berg if (calc_crc)
1197a04564c9SJohannes Berg crc = crc32_be(crc, pos - 2, elen + 2);
1198a04564c9SJohannes Berg break;
1199a04564c9SJohannes Berg }
1200bee7f586SJohannes Berg elem_parse_failed = true;
1201bee7f586SJohannes Berg break;
1202dd76986bSJohannes Berg case WLAN_EID_MESH_ID:
1203dd76986bSJohannes Berg elems->mesh_id = pos;
1204dd76986bSJohannes Berg elems->mesh_id_len = elen;
1205dd76986bSJohannes Berg break;
1206dd76986bSJohannes Berg case WLAN_EID_MESH_CONFIG:
1207dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_meshconf_ie))
1208dd76986bSJohannes Berg elems->mesh_config = (void *)pos;
1209fcff4f10SPaul Stewart else
1210fcff4f10SPaul Stewart elem_parse_failed = true;
1211dd76986bSJohannes Berg break;
1212dd76986bSJohannes Berg case WLAN_EID_PEER_MGMT:
1213dd76986bSJohannes Berg elems->peering = pos;
1214dd76986bSJohannes Berg elems->peering_len = elen;
1215dd76986bSJohannes Berg break;
12163f52b7e3SMarco Porsch case WLAN_EID_MESH_AWAKE_WINDOW:
12173f52b7e3SMarco Porsch if (elen >= 2)
12183f52b7e3SMarco Porsch elems->awake_window = (void *)pos;
12193f52b7e3SMarco Porsch break;
1220dd76986bSJohannes Berg case WLAN_EID_PREQ:
1221dd76986bSJohannes Berg elems->preq = pos;
1222dd76986bSJohannes Berg elems->preq_len = elen;
1223dd76986bSJohannes Berg break;
1224dd76986bSJohannes Berg case WLAN_EID_PREP:
1225dd76986bSJohannes Berg elems->prep = pos;
1226dd76986bSJohannes Berg elems->prep_len = elen;
1227dd76986bSJohannes Berg break;
1228dd76986bSJohannes Berg case WLAN_EID_PERR:
1229dd76986bSJohannes Berg elems->perr = pos;
1230dd76986bSJohannes Berg elems->perr_len = elen;
1231dd76986bSJohannes Berg break;
1232dd76986bSJohannes Berg case WLAN_EID_RANN:
1233dd76986bSJohannes Berg if (elen >= sizeof(struct ieee80211_rann_ie))
1234dd76986bSJohannes Berg elems->rann = (void *)pos;
1235fcff4f10SPaul Stewart else
1236fcff4f10SPaul Stewart elem_parse_failed = true;
1237dd76986bSJohannes Berg break;
1238dd76986bSJohannes Berg case WLAN_EID_CHANNEL_SWITCH:
12395bc1420bSJohannes Berg if (elen != sizeof(struct ieee80211_channel_sw_ie)) {
12405bc1420bSJohannes Berg elem_parse_failed = true;
12415bc1420bSJohannes Berg break;
12425bc1420bSJohannes Berg }
12435bc1420bSJohannes Berg elems->ch_switch_ie = (void *)pos;
1244dd76986bSJohannes Berg break;
1245b4f286a1SJohannes Berg case WLAN_EID_EXT_CHANSWITCH_ANN:
1246b4f286a1SJohannes Berg if (elen != sizeof(struct ieee80211_ext_chansw_ie)) {
1247b4f286a1SJohannes Berg elem_parse_failed = true;
1248b4f286a1SJohannes Berg break;
1249b4f286a1SJohannes Berg }
1250b4f286a1SJohannes Berg elems->ext_chansw_ie = (void *)pos;
1251b4f286a1SJohannes Berg break;
125285220d71SJohannes Berg case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
125385220d71SJohannes Berg if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
125485220d71SJohannes Berg elem_parse_failed = true;
125585220d71SJohannes Berg break;
125685220d71SJohannes Berg }
125785220d71SJohannes Berg elems->sec_chan_offs = (void *)pos;
125885220d71SJohannes Berg break;
12598f2535b9SChun-Yeow Yeoh case WLAN_EID_CHAN_SWITCH_PARAM:
1260652e8363SJohannes Berg if (elen <
12618f2535b9SChun-Yeow Yeoh sizeof(*elems->mesh_chansw_params_ie)) {
12628f2535b9SChun-Yeow Yeoh elem_parse_failed = true;
12638f2535b9SChun-Yeow Yeoh break;
12648f2535b9SChun-Yeow Yeoh }
12658f2535b9SChun-Yeow Yeoh elems->mesh_chansw_params_ie = (void *)pos;
12668f2535b9SChun-Yeow Yeoh break;
1267b2e506bfSJohannes Berg case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
1268fd17bf04SJohannes Berg if (!params->action ||
1269652e8363SJohannes Berg elen < sizeof(*elems->wide_bw_chansw_ie)) {
1270b2e506bfSJohannes Berg elem_parse_failed = true;
1271b2e506bfSJohannes Berg break;
1272b2e506bfSJohannes Berg }
1273b2e506bfSJohannes Berg elems->wide_bw_chansw_ie = (void *)pos;
1274b2e506bfSJohannes Berg break;
1275b2e506bfSJohannes Berg case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
1276fd17bf04SJohannes Berg if (params->action) {
1277b2e506bfSJohannes Berg elem_parse_failed = true;
1278b2e506bfSJohannes Berg break;
1279b2e506bfSJohannes Berg }
1280b2e506bfSJohannes Berg /*
1281b2e506bfSJohannes Berg * This is a bit tricky, but as we only care about
1282b2e506bfSJohannes Berg * the wide bandwidth channel switch element, so
1283b2e506bfSJohannes Berg * just parse it out manually.
1284b2e506bfSJohannes Berg */
1285b2e506bfSJohannes Berg ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
1286b2e506bfSJohannes Berg pos, elen);
1287b2e506bfSJohannes Berg if (ie) {
1288652e8363SJohannes Berg if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie))
1289b2e506bfSJohannes Berg elems->wide_bw_chansw_ie =
1290b2e506bfSJohannes Berg (void *)(ie + 2);
1291b2e506bfSJohannes Berg else
1292b2e506bfSJohannes Berg elem_parse_failed = true;
1293b2e506bfSJohannes Berg }
1294b2e506bfSJohannes Berg break;
1295dd76986bSJohannes Berg case WLAN_EID_COUNTRY:
1296dd76986bSJohannes Berg elems->country_elem = pos;
1297dd76986bSJohannes Berg elems->country_elem_len = elen;
1298dd76986bSJohannes Berg break;
1299dd76986bSJohannes Berg case WLAN_EID_PWR_CONSTRAINT:
1300761a48d2SJohannes Berg if (elen != 1) {
1301761a48d2SJohannes Berg elem_parse_failed = true;
1302761a48d2SJohannes Berg break;
1303761a48d2SJohannes Berg }
1304dd76986bSJohannes Berg elems->pwr_constr_elem = pos;
1305dd76986bSJohannes Berg break;
1306c8d65917SSteinar H. Gunderson case WLAN_EID_CISCO_VENDOR_SPECIFIC:
1307c8d65917SSteinar H. Gunderson /* Lots of different options exist, but we only care
1308c8d65917SSteinar H. Gunderson * about the Dynamic Transmit Power Control element.
1309c8d65917SSteinar H. Gunderson * First check for the Cisco OUI, then for the DTPC
1310c8d65917SSteinar H. Gunderson * tag (0x00).
1311c8d65917SSteinar H. Gunderson */
1312c8d65917SSteinar H. Gunderson if (elen < 4) {
1313c8d65917SSteinar H. Gunderson elem_parse_failed = true;
1314c8d65917SSteinar H. Gunderson break;
1315c8d65917SSteinar H. Gunderson }
1316c8d65917SSteinar H. Gunderson
1317c8d65917SSteinar H. Gunderson if (pos[0] != 0x00 || pos[1] != 0x40 ||
1318c8d65917SSteinar H. Gunderson pos[2] != 0x96 || pos[3] != 0x00)
1319c8d65917SSteinar H. Gunderson break;
1320c8d65917SSteinar H. Gunderson
1321c8d65917SSteinar H. Gunderson if (elen != 6) {
1322c8d65917SSteinar H. Gunderson elem_parse_failed = true;
1323c8d65917SSteinar H. Gunderson break;
1324c8d65917SSteinar H. Gunderson }
1325c8d65917SSteinar H. Gunderson
1326c8d65917SSteinar H. Gunderson if (calc_crc)
1327c8d65917SSteinar H. Gunderson crc = crc32_be(crc, pos - 2, elen + 2);
1328c8d65917SSteinar H. Gunderson
1329c8d65917SSteinar H. Gunderson elems->cisco_dtpc_elem = pos;
1330c8d65917SSteinar H. Gunderson break;
13312aa485e1SJohn Crispin case WLAN_EID_ADDBA_EXT:
1332652e8363SJohannes Berg if (elen < sizeof(struct ieee80211_addba_ext_ie)) {
13332aa485e1SJohn Crispin elem_parse_failed = true;
13342aa485e1SJohn Crispin break;
13352aa485e1SJohn Crispin }
13362aa485e1SJohn Crispin elems->addba_ext_ie = (void *)pos;
13372aa485e1SJohn Crispin break;
1338dd76986bSJohannes Berg case WLAN_EID_TIMEOUT_INTERVAL:
133979ba1d89SJohannes Berg if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
134079ba1d89SJohannes Berg elems->timeout_int = (void *)pos;
134179ba1d89SJohannes Berg else
134279ba1d89SJohannes Berg elem_parse_failed = true;
1343dd76986bSJohannes Berg break;
1344e38a017bSAvraham Stern case WLAN_EID_BSS_MAX_IDLE_PERIOD:
1345e38a017bSAvraham Stern if (elen >= sizeof(*elems->max_idle_period_ie))
1346e38a017bSAvraham Stern elems->max_idle_period_ie = (void *)pos;
1347e38a017bSAvraham Stern break;
1348c0058df7SShaul Triebitz case WLAN_EID_RSNX:
1349c0058df7SShaul Triebitz elems->rsnx = pos;
1350c0058df7SShaul Triebitz elems->rsnx_len = elen;
1351c0058df7SShaul Triebitz break;
1352b0345850SWen Gong case WLAN_EID_TX_POWER_ENVELOPE:
1353b0345850SWen Gong if (elen < 1 ||
1354b0345850SWen Gong elen > sizeof(struct ieee80211_tx_pwr_env))
1355b0345850SWen Gong break;
1356b0345850SWen Gong
1357b0345850SWen Gong if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env))
1358b0345850SWen Gong break;
1359b0345850SWen Gong
1360b0345850SWen Gong elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos;
1361b0345850SWen Gong elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen;
1362b0345850SWen Gong elems->tx_pwr_env_num++;
1363b0345850SWen Gong break;
136441cbb0f5SLuca Coelho case WLAN_EID_EXTENSION:
1365e4d005b8SJohannes Berg ieee80211_parse_extension_element(calc_crc ?
1366e4d005b8SJohannes Berg &crc : NULL,
1367ea5cba26SJohannes Berg elem, elems, params);
136841cbb0f5SLuca Coelho break;
1369cd418ba6SThomas Pedersen case WLAN_EID_S1G_CAPABILITIES:
1370652e8363SJohannes Berg if (elen >= sizeof(*elems->s1g_capab))
1371cd418ba6SThomas Pedersen elems->s1g_capab = (void *)pos;
1372cd418ba6SThomas Pedersen else
1373cd418ba6SThomas Pedersen elem_parse_failed = true;
1374cd418ba6SThomas Pedersen break;
1375cd418ba6SThomas Pedersen case WLAN_EID_S1G_OPERATION:
1376cd418ba6SThomas Pedersen if (elen == sizeof(*elems->s1g_oper))
1377cd418ba6SThomas Pedersen elems->s1g_oper = (void *)pos;
1378cd418ba6SThomas Pedersen else
1379cd418ba6SThomas Pedersen elem_parse_failed = true;
1380cd418ba6SThomas Pedersen break;
1381cd418ba6SThomas Pedersen case WLAN_EID_S1G_BCN_COMPAT:
1382cd418ba6SThomas Pedersen if (elen == sizeof(*elems->s1g_bcn_compat))
1383cd418ba6SThomas Pedersen elems->s1g_bcn_compat = (void *)pos;
1384cd418ba6SThomas Pedersen else
1385cd418ba6SThomas Pedersen elem_parse_failed = true;
1386cd418ba6SThomas Pedersen break;
13871d00ce80SThomas Pedersen case WLAN_EID_AID_RESPONSE:
13881d00ce80SThomas Pedersen if (elen == sizeof(struct ieee80211_aid_response_ie))
13891d00ce80SThomas Pedersen elems->aid_resp = (void *)pos;
13901d00ce80SThomas Pedersen else
13911d00ce80SThomas Pedersen elem_parse_failed = true;
13921d00ce80SThomas Pedersen break;
1393dd76986bSJohannes Berg default:
1394dd76986bSJohannes Berg break;
1395dd76986bSJohannes Berg }
1396dd76986bSJohannes Berg
1397fcff4f10SPaul Stewart if (elem_parse_failed)
1398fcff4f10SPaul Stewart elems->parse_error = true;
1399fcff4f10SPaul Stewart else
14005df45690SJohannes Berg __set_bit(id, seen_elems);
1401dd76986bSJohannes Berg }
1402dd76986bSJohannes Berg
1403fd17bf04SJohannes Berg if (!for_each_element_completed(elem, params->start, params->len))
1404fcff4f10SPaul Stewart elems->parse_error = true;
1405fcff4f10SPaul Stewart
1406dd76986bSJohannes Berg return crc;
1407dd76986bSJohannes Berg }
1408dd76986bSJohannes Berg
ieee802_11_find_bssid_profile(const u8 * start,size_t len,struct ieee802_11_elems * elems,struct cfg80211_bss * bss,u8 * nontransmitted_profile)14095023b14cSSara Sharon static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
1410671042a4SSara Sharon struct ieee802_11_elems *elems,
141138c6aa29SJohannes Berg struct cfg80211_bss *bss,
14125809a5d5SDan Carpenter u8 *nontransmitted_profile)
1413671042a4SSara Sharon {
1414671042a4SSara Sharon const struct element *elem, *sub;
14155023b14cSSara Sharon size_t profile_len = 0;
14165023b14cSSara Sharon bool found = false;
1417671042a4SSara Sharon
141838c6aa29SJohannes Berg if (!bss || !bss->transmitted_bss)
14195023b14cSSara Sharon return profile_len;
1420671042a4SSara Sharon
1421671042a4SSara Sharon for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) {
1422671042a4SSara Sharon if (elem->datalen < 2)
1423671042a4SSara Sharon continue;
14248f033d2bSJohannes Berg if (elem->data[0] < 1 || elem->data[0] > 8)
14258f033d2bSJohannes Berg continue;
1426671042a4SSara Sharon
1427671042a4SSara Sharon for_each_element(sub, elem->data + 1, elem->datalen - 1) {
1428671042a4SSara Sharon u8 new_bssid[ETH_ALEN];
1429671042a4SSara Sharon const u8 *index;
1430671042a4SSara Sharon
1431671042a4SSara Sharon if (sub->id != 0 || sub->datalen < 4) {
1432671042a4SSara Sharon /* not a valid BSS profile */
1433671042a4SSara Sharon continue;
1434671042a4SSara Sharon }
1435671042a4SSara Sharon
1436671042a4SSara Sharon if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
1437671042a4SSara Sharon sub->data[1] != 2) {
1438671042a4SSara Sharon /* The first element of the
1439671042a4SSara Sharon * Nontransmitted BSSID Profile is not
1440671042a4SSara Sharon * the Nontransmitted BSSID Capability
1441671042a4SSara Sharon * element.
1442671042a4SSara Sharon */
1443671042a4SSara Sharon continue;
1444671042a4SSara Sharon }
1445671042a4SSara Sharon
14465809a5d5SDan Carpenter memset(nontransmitted_profile, 0, len);
14475023b14cSSara Sharon profile_len = cfg80211_merge_profile(start, len,
14485023b14cSSara Sharon elem,
14495023b14cSSara Sharon sub,
14505023b14cSSara Sharon nontransmitted_profile,
14515023b14cSSara Sharon len);
14525023b14cSSara Sharon
1453671042a4SSara Sharon /* found a Nontransmitted BSSID Profile */
1454671042a4SSara Sharon index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX,
14555809a5d5SDan Carpenter nontransmitted_profile,
14565023b14cSSara Sharon profile_len);
1457671042a4SSara Sharon if (!index || index[1] < 1 || index[2] == 0) {
1458671042a4SSara Sharon /* Invalid MBSSID Index element */
1459671042a4SSara Sharon continue;
1460671042a4SSara Sharon }
1461671042a4SSara Sharon
146238c6aa29SJohannes Berg cfg80211_gen_new_bssid(bss->transmitted_bss->bssid,
1463671042a4SSara Sharon elem->data[0],
1464671042a4SSara Sharon index[2],
1465671042a4SSara Sharon new_bssid);
146638c6aa29SJohannes Berg if (ether_addr_equal(new_bssid, bss->bssid)) {
14675023b14cSSara Sharon found = true;
1468671042a4SSara Sharon elems->bssid_index_len = index[1];
1469671042a4SSara Sharon elems->bssid_index = (void *)&index[2];
1470671042a4SSara Sharon break;
1471671042a4SSara Sharon }
1472671042a4SSara Sharon }
1473671042a4SSara Sharon }
14745023b14cSSara Sharon
14755023b14cSSara Sharon return found ? profile_len : 0;
1476671042a4SSara Sharon }
1477671042a4SSara Sharon
ieee80211_mle_get_sta_prof(struct ieee802_11_elems * elems,u8 link_id)147845ebac4fSIlan Peer static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
147945ebac4fSIlan Peer u8 link_id)
148045ebac4fSIlan Peer {
1481a286de1aSIlan Peer const struct ieee80211_multi_link_elem *ml = elems->ml_basic;
1482a286de1aSIlan Peer ssize_t ml_len = elems->ml_basic_len;
148345ebac4fSIlan Peer const struct element *sub;
148445ebac4fSIlan Peer
148545ebac4fSIlan Peer if (!ml || !ml_len)
148645ebac4fSIlan Peer return;
148745ebac4fSIlan Peer
148845ebac4fSIlan Peer if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) !=
148945ebac4fSIlan Peer IEEE80211_ML_CONTROL_TYPE_BASIC)
149045ebac4fSIlan Peer return;
149145ebac4fSIlan Peer
149245ebac4fSIlan Peer for_each_mle_subelement(sub, (u8 *)ml, ml_len) {
149345ebac4fSIlan Peer struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
1494a76236deSBenjamin Berg ssize_t sta_prof_len;
149545ebac4fSIlan Peer u16 control;
149645ebac4fSIlan Peer
149745ebac4fSIlan Peer if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
149845ebac4fSIlan Peer continue;
149945ebac4fSIlan Peer
1500e2efec97SIlan Peer if (!ieee80211_mle_basic_sta_prof_size_ok(sub->data,
1501e2efec97SIlan Peer sub->datalen))
150245ebac4fSIlan Peer return;
150345ebac4fSIlan Peer
150445ebac4fSIlan Peer control = le16_to_cpu(prof->control);
150545ebac4fSIlan Peer
150645ebac4fSIlan Peer if (link_id != u16_get_bits(control,
150745ebac4fSIlan Peer IEEE80211_MLE_STA_CONTROL_LINK_ID))
150845ebac4fSIlan Peer continue;
150945ebac4fSIlan Peer
151045ebac4fSIlan Peer if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
151145ebac4fSIlan Peer return;
151245ebac4fSIlan Peer
151345ebac4fSIlan Peer /* the sub element can be fragmented */
1514a76236deSBenjamin Berg sta_prof_len =
1515a76236deSBenjamin Berg cfg80211_defragment_element(sub,
1516a76236deSBenjamin Berg (u8 *)ml, ml_len,
1517a76236deSBenjamin Berg elems->scratch_pos,
1518a76236deSBenjamin Berg elems->scratch +
1519a76236deSBenjamin Berg elems->scratch_len -
1520a76236deSBenjamin Berg elems->scratch_pos,
152145ebac4fSIlan Peer IEEE80211_MLE_SUBELEM_FRAGMENT);
1522a76236deSBenjamin Berg
1523a76236deSBenjamin Berg if (sta_prof_len < 0)
1524a76236deSBenjamin Berg return;
1525a76236deSBenjamin Berg
1526a76236deSBenjamin Berg elems->prof = (void *)elems->scratch_pos;
1527a76236deSBenjamin Berg elems->sta_prof_len = sta_prof_len;
1528a76236deSBenjamin Berg elems->scratch_pos += sta_prof_len;
1529a76236deSBenjamin Berg
153045ebac4fSIlan Peer return;
153145ebac4fSIlan Peer }
153245ebac4fSIlan Peer }
153345ebac4fSIlan Peer
ieee80211_mle_parse_link(struct ieee802_11_elems * elems,struct ieee80211_elems_parse_params * params)153445ebac4fSIlan Peer static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
153545ebac4fSIlan Peer struct ieee80211_elems_parse_params *params)
153645ebac4fSIlan Peer {
153745ebac4fSIlan Peer struct ieee80211_mle_per_sta_profile *prof;
153845ebac4fSIlan Peer struct ieee80211_elems_parse_params sub = {
153945ebac4fSIlan Peer .action = params->action,
154045ebac4fSIlan Peer .from_ap = params->from_ap,
154145ebac4fSIlan Peer .link_id = -1,
154245ebac4fSIlan Peer };
1543a286de1aSIlan Peer ssize_t ml_len = elems->ml_basic_len;
154445ebac4fSIlan Peer const struct element *non_inherit = NULL;
154545ebac4fSIlan Peer const u8 *end;
154645ebac4fSIlan Peer
154745ebac4fSIlan Peer if (params->link_id == -1)
154845ebac4fSIlan Peer return;
154945ebac4fSIlan Peer
1550a286de1aSIlan Peer ml_len = cfg80211_defragment_element(elems->ml_basic_elem,
1551a76236deSBenjamin Berg elems->ie_start,
1552a76236deSBenjamin Berg elems->total_len,
1553a76236deSBenjamin Berg elems->scratch_pos,
1554a76236deSBenjamin Berg elems->scratch +
1555a76236deSBenjamin Berg elems->scratch_len -
1556a76236deSBenjamin Berg elems->scratch_pos,
155745ebac4fSIlan Peer WLAN_EID_FRAGMENT);
155845ebac4fSIlan Peer
1559a76236deSBenjamin Berg if (ml_len < 0)
1560a76236deSBenjamin Berg return;
1561a76236deSBenjamin Berg
1562a286de1aSIlan Peer elems->ml_basic = (const void *)elems->scratch_pos;
1563a286de1aSIlan Peer elems->ml_basic_len = ml_len;
1564a76236deSBenjamin Berg
156545ebac4fSIlan Peer ieee80211_mle_get_sta_prof(elems, params->link_id);
156645ebac4fSIlan Peer prof = elems->prof;
156745ebac4fSIlan Peer
156845ebac4fSIlan Peer if (!prof)
156945ebac4fSIlan Peer return;
157045ebac4fSIlan Peer
157145ebac4fSIlan Peer /* check if we have the 4 bytes for the fixed part in assoc response */
157245ebac4fSIlan Peer if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) {
157345ebac4fSIlan Peer elems->prof = NULL;
157445ebac4fSIlan Peer elems->sta_prof_len = 0;
157545ebac4fSIlan Peer return;
157645ebac4fSIlan Peer }
157745ebac4fSIlan Peer
157845ebac4fSIlan Peer /*
157945ebac4fSIlan Peer * Skip the capability information and the status code that are expected
158045ebac4fSIlan Peer * as part of the station profile in association response frames. Note
158145ebac4fSIlan Peer * the -1 is because the 'sta_info_len' is accounted to as part of the
158245ebac4fSIlan Peer * per-STA profile, but not part of the 'u8 variable[]' portion.
158345ebac4fSIlan Peer */
158445ebac4fSIlan Peer sub.start = prof->variable + prof->sta_info_len - 1 + 4;
158545ebac4fSIlan Peer end = (const u8 *)prof + elems->sta_prof_len;
158645ebac4fSIlan Peer sub.len = end - sub.start;
158745ebac4fSIlan Peer
158845ebac4fSIlan Peer non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
158945ebac4fSIlan Peer sub.start, sub.len);
159045ebac4fSIlan Peer _ieee802_11_parse_elems_full(&sub, elems, non_inherit);
159145ebac4fSIlan Peer }
159245ebac4fSIlan Peer
1593fd17bf04SJohannes Berg struct ieee802_11_elems *
ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params * params)1594fd17bf04SJohannes Berg ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
159578ac51f8SSara Sharon {
15965d24828dSJohannes Berg struct ieee802_11_elems *elems;
1597671042a4SSara Sharon const struct element *non_inherit = NULL;
15985023b14cSSara Sharon u8 *nontransmitted_profile;
15995023b14cSSara Sharon int nontransmitted_profile_len = 0;
160091f53ae9SJohannes Berg size_t scratch_len = 3 * params->len;
1601671042a4SSara Sharon
1602ff05d4b4SJohannes Berg elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC);
16035d24828dSJohannes Berg if (!elems)
16045d24828dSJohannes Berg return NULL;
1605fd17bf04SJohannes Berg elems->ie_start = params->start;
1606fd17bf04SJohannes Berg elems->total_len = params->len;
1607ff05d4b4SJohannes Berg elems->scratch_len = scratch_len;
1608ff05d4b4SJohannes Berg elems->scratch_pos = elems->scratch;
160978ac51f8SSara Sharon
1610ff05d4b4SJohannes Berg nontransmitted_profile = elems->scratch_pos;
16115023b14cSSara Sharon nontransmitted_profile_len =
1612fd17bf04SJohannes Berg ieee802_11_find_bssid_profile(params->start, params->len,
161338c6aa29SJohannes Berg elems, params->bss,
16145809a5d5SDan Carpenter nontransmitted_profile);
1615ff05d4b4SJohannes Berg elems->scratch_pos += nontransmitted_profile_len;
1616ff05d4b4SJohannes Berg elems->scratch_len -= nontransmitted_profile_len;
1617ff05d4b4SJohannes Berg non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
16185023b14cSSara Sharon nontransmitted_profile,
16195023b14cSSara Sharon nontransmitted_profile_len);
1620671042a4SSara Sharon
1621fd17bf04SJohannes Berg elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit);
162278ac51f8SSara Sharon
162378ac51f8SSara Sharon /* Override with nontransmitted profile, if found */
1624fd17bf04SJohannes Berg if (nontransmitted_profile_len) {
1625fd17bf04SJohannes Berg struct ieee80211_elems_parse_params sub = {
1626fd17bf04SJohannes Berg .start = nontransmitted_profile,
1627fd17bf04SJohannes Berg .len = nontransmitted_profile_len,
1628fd17bf04SJohannes Berg .action = params->action,
1629425f4b5fSJohannes Berg .link_id = params->link_id,
1630fd17bf04SJohannes Berg };
1631fd17bf04SJohannes Berg
1632fd17bf04SJohannes Berg _ieee802_11_parse_elems_full(&sub, elems, NULL);
1633fd17bf04SJohannes Berg }
163478ac51f8SSara Sharon
163545ebac4fSIlan Peer ieee80211_mle_parse_link(elems, params);
163645ebac4fSIlan Peer
163778ac51f8SSara Sharon if (elems->tim && !elems->parse_error) {
163878ac51f8SSara Sharon const struct ieee80211_tim_ie *tim_ie = elems->tim;
163978ac51f8SSara Sharon
164078ac51f8SSara Sharon elems->dtim_period = tim_ie->dtim_period;
164178ac51f8SSara Sharon elems->dtim_count = tim_ie->dtim_count;
164278ac51f8SSara Sharon }
164378ac51f8SSara Sharon
164478ac51f8SSara Sharon /* Override DTIM period and count if needed */
164578ac51f8SSara Sharon if (elems->bssid_index &&
164678ac51f8SSara Sharon elems->bssid_index_len >=
164778ac51f8SSara Sharon offsetofend(struct ieee80211_bssid_index, dtim_period))
164878ac51f8SSara Sharon elems->dtim_period = elems->bssid_index->dtim_period;
164978ac51f8SSara Sharon
165078ac51f8SSara Sharon if (elems->bssid_index &&
165178ac51f8SSara Sharon elems->bssid_index_len >=
165278ac51f8SSara Sharon offsetofend(struct ieee80211_bssid_index, dtim_count))
165378ac51f8SSara Sharon elems->dtim_count = elems->bssid_index->dtim_count;
165478ac51f8SSara Sharon
16555d24828dSJohannes Berg return elems;
165678ac51f8SSara Sharon }
165778ac51f8SSara Sharon
ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data * sdata,struct ieee80211_tx_queue_params * qparam,int ac)1658e552af05SHaim Dreyfuss void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
1659e552af05SHaim Dreyfuss struct ieee80211_tx_queue_params
1660e552af05SHaim Dreyfuss *qparam, int ac)
1661e552af05SHaim Dreyfuss {
1662e552af05SHaim Dreyfuss struct ieee80211_chanctx_conf *chanctx_conf;
1663e552af05SHaim Dreyfuss const struct ieee80211_reg_rule *rrule;
166438cb87eeSStanislaw Gruszka const struct ieee80211_wmm_ac *wmm_ac;
1665e552af05SHaim Dreyfuss u16 center_freq = 0;
1666e552af05SHaim Dreyfuss
1667e552af05SHaim Dreyfuss if (sdata->vif.type != NL80211_IFTYPE_AP &&
1668e552af05SHaim Dreyfuss sdata->vif.type != NL80211_IFTYPE_STATION)
1669e552af05SHaim Dreyfuss return;
1670e552af05SHaim Dreyfuss
1671e552af05SHaim Dreyfuss rcu_read_lock();
1672d0a9123eSJohannes Berg chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
1673e552af05SHaim Dreyfuss if (chanctx_conf)
1674e552af05SHaim Dreyfuss center_freq = chanctx_conf->def.chan->center_freq;
1675e552af05SHaim Dreyfuss
1676e552af05SHaim Dreyfuss if (!center_freq) {
1677e552af05SHaim Dreyfuss rcu_read_unlock();
1678e552af05SHaim Dreyfuss return;
1679e552af05SHaim Dreyfuss }
1680e552af05SHaim Dreyfuss
1681e552af05SHaim Dreyfuss rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));
1682e552af05SHaim Dreyfuss
168338cb87eeSStanislaw Gruszka if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
1684e552af05SHaim Dreyfuss rcu_read_unlock();
1685e552af05SHaim Dreyfuss return;
1686e552af05SHaim Dreyfuss }
1687e552af05SHaim Dreyfuss
1688e552af05SHaim Dreyfuss if (sdata->vif.type == NL80211_IFTYPE_AP)
168938cb87eeSStanislaw Gruszka wmm_ac = &rrule->wmm_rule.ap[ac];
1690e552af05SHaim Dreyfuss else
169138cb87eeSStanislaw Gruszka wmm_ac = &rrule->wmm_rule.client[ac];
1692e552af05SHaim Dreyfuss qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min);
1693e552af05SHaim Dreyfuss qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max);
1694e552af05SHaim Dreyfuss qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
1695abd76d25SDreyfuss, Haim qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32);
1696e552af05SHaim Dreyfuss rcu_read_unlock();
1697e552af05SHaim Dreyfuss }
1698e552af05SHaim Dreyfuss
ieee80211_set_wmm_default(struct ieee80211_link_data * link,bool bss_notify,bool enable_qos)1699b3e2130bSJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_link_data *link,
1700cec66283SJohannes Berg bool bss_notify, bool enable_qos)
17015825fe10SJohannes Berg {
1702b3e2130bSJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata;
17035825fe10SJohannes Berg struct ieee80211_local *local = sdata->local;
17045825fe10SJohannes Berg struct ieee80211_tx_queue_params qparam;
170555de908aSJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf;
170654bcbc69SJohannes Berg int ac;
1707cec66283SJohannes Berg bool use_11b;
1708239281f8SRostislav Lisovy bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
1709aa837e1dSJohannes Berg int aCWmin, aCWmax;
17105825fe10SJohannes Berg
17115825fe10SJohannes Berg if (!local->ops->conf_tx)
17125825fe10SJohannes Berg return;
17135825fe10SJohannes Berg
171454bcbc69SJohannes Berg if (local->hw.queues < IEEE80211_NUM_ACS)
171554bcbc69SJohannes Berg return;
171654bcbc69SJohannes Berg
17175825fe10SJohannes Berg memset(&qparam, 0, sizeof(qparam));
17185825fe10SJohannes Berg
171955de908aSJohannes Berg rcu_read_lock();
1720b3e2130bSJohannes Berg chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
172155de908aSJohannes Berg use_11b = (chanctx_conf &&
172257fbcce3SJohannes Berg chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) &&
172339eac2deSJohannes Berg !link->operating_11g_mode;
172455de908aSJohannes Berg rcu_read_unlock();
17255825fe10SJohannes Berg
1726239281f8SRostislav Lisovy is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
1727239281f8SRostislav Lisovy
1728aa837e1dSJohannes Berg /* Set defaults according to 802.11-2007 Table 7-37 */
1729aa837e1dSJohannes Berg aCWmax = 1023;
1730aa837e1dSJohannes Berg if (use_11b)
1731aa837e1dSJohannes Berg aCWmin = 31;
17325825fe10SJohannes Berg else
1733aa837e1dSJohannes Berg aCWmin = 15;
17345825fe10SJohannes Berg
17351f4ffde8SFred Zhou /* Confiure old 802.11b/g medium access rules. */
17361f4ffde8SFred Zhou qparam.cw_max = aCWmax;
17371f4ffde8SFred Zhou qparam.cw_min = aCWmin;
17381f4ffde8SFred Zhou qparam.txop = 0;
17391f4ffde8SFred Zhou qparam.aifs = 2;
17401f4ffde8SFred Zhou
17411f4ffde8SFred Zhou for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
17421f4ffde8SFred Zhou /* Update if QoS is enabled. */
1743a8ce8544SStanislaw Gruszka if (enable_qos) {
174454bcbc69SJohannes Berg switch (ac) {
17451d98fb12SJohannes Berg case IEEE80211_AC_BK:
17467ba10a8eSJohannes Berg qparam.cw_max = aCWmax;
17477ba10a8eSJohannes Berg qparam.cw_min = aCWmin;
17485825fe10SJohannes Berg qparam.txop = 0;
1749239281f8SRostislav Lisovy if (is_ocb)
1750239281f8SRostislav Lisovy qparam.aifs = 9;
1751239281f8SRostislav Lisovy else
1752aa837e1dSJohannes Berg qparam.aifs = 7;
1753aa837e1dSJohannes Berg break;
1754a8ce8544SStanislaw Gruszka /* never happens but let's not leave undefined */
1755a8ce8544SStanislaw Gruszka default:
17561d98fb12SJohannes Berg case IEEE80211_AC_BE:
17577ba10a8eSJohannes Berg qparam.cw_max = aCWmax;
17587ba10a8eSJohannes Berg qparam.cw_min = aCWmin;
1759aa837e1dSJohannes Berg qparam.txop = 0;
1760239281f8SRostislav Lisovy if (is_ocb)
1761239281f8SRostislav Lisovy qparam.aifs = 6;
1762239281f8SRostislav Lisovy else
1763aa837e1dSJohannes Berg qparam.aifs = 3;
1764aa837e1dSJohannes Berg break;
17651d98fb12SJohannes Berg case IEEE80211_AC_VI:
1766aa837e1dSJohannes Berg qparam.cw_max = aCWmin;
1767aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 2 - 1;
1768239281f8SRostislav Lisovy if (is_ocb)
1769239281f8SRostislav Lisovy qparam.txop = 0;
1770239281f8SRostislav Lisovy else if (use_11b)
1771aa837e1dSJohannes Berg qparam.txop = 6016/32;
1772aa837e1dSJohannes Berg else
1773aa837e1dSJohannes Berg qparam.txop = 3008/32;
1774239281f8SRostislav Lisovy
1775239281f8SRostislav Lisovy if (is_ocb)
1776239281f8SRostislav Lisovy qparam.aifs = 3;
1777239281f8SRostislav Lisovy else
1778aa837e1dSJohannes Berg qparam.aifs = 2;
1779aa837e1dSJohannes Berg break;
17801d98fb12SJohannes Berg case IEEE80211_AC_VO:
1781aa837e1dSJohannes Berg qparam.cw_max = (aCWmin + 1) / 2 - 1;
1782aa837e1dSJohannes Berg qparam.cw_min = (aCWmin + 1) / 4 - 1;
1783239281f8SRostislav Lisovy if (is_ocb)
1784239281f8SRostislav Lisovy qparam.txop = 0;
1785239281f8SRostislav Lisovy else if (use_11b)
1786aa837e1dSJohannes Berg qparam.txop = 3264/32;
1787aa837e1dSJohannes Berg else
1788aa837e1dSJohannes Berg qparam.txop = 1504/32;
1789aa837e1dSJohannes Berg qparam.aifs = 2;
1790aa837e1dSJohannes Berg break;
1791aa837e1dSJohannes Berg }
1792a8ce8544SStanislaw Gruszka }
1793e552af05SHaim Dreyfuss ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac);
17945825fe10SJohannes Berg
1795ab13315aSKalle Valo qparam.uapsd = false;
1796ab13315aSKalle Valo
1797b3e2130bSJohannes Berg link->tx_conf[ac] = qparam;
1798b3e2130bSJohannes Berg drv_conf_tx(local, link, ac, &qparam);
1799aa837e1dSJohannes Berg }
1800e1b3ec1aSStanislaw Gruszka
1801f142c6b9SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
1802708d50edSAyala Beker sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
1803708d50edSAyala Beker sdata->vif.type != NL80211_IFTYPE_NAN) {
1804b3e2130bSJohannes Berg link->conf->qos = enable_qos;
18053abead59SJohannes Berg if (bss_notify)
1806b3e2130bSJohannes Berg ieee80211_link_info_change_notify(sdata, link,
18073abead59SJohannes Berg BSS_CHANGED_QOS);
18085825fe10SJohannes Berg }
1809d9734979SSujith }
1810e50db65cSJohannes Berg
ieee80211_send_auth(struct ieee80211_sub_if_data * sdata,u16 transaction,u16 auth_alg,u16 status,const u8 * extra,size_t extra_len,const u8 * da,const u8 * bssid,const u8 * key,u8 key_len,u8 key_idx,u32 tx_flags)181146900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1812700e8ea6SJouni Malinen u16 transaction, u16 auth_alg, u16 status,
18134a3cb702SJohannes Berg const u8 *extra, size_t extra_len, const u8 *da,
18141672c0e3SJohannes Berg const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
18151672c0e3SJohannes Berg u32 tx_flags)
181646900298SJohannes Berg {
181746900298SJohannes Berg struct ieee80211_local *local = sdata->local;
181846900298SJohannes Berg struct sk_buff *skb;
181946900298SJohannes Berg struct ieee80211_mgmt *mgmt;
1820f1871abdSIlan Peer bool multi_link = ieee80211_vif_is_mld(&sdata->vif);
18218ec9a96bSJohannes Berg struct {
18228ec9a96bSJohannes Berg u8 id;
18238ec9a96bSJohannes Berg u8 len;
18248ec9a96bSJohannes Berg u8 ext_id;
18258ec9a96bSJohannes Berg struct ieee80211_multi_link_elem ml;
18268ec9a96bSJohannes Berg struct ieee80211_mle_basic_common_info basic;
18278ec9a96bSJohannes Berg } __packed mle = {
18288ec9a96bSJohannes Berg .id = WLAN_EID_EXTENSION,
18298ec9a96bSJohannes Berg .len = sizeof(mle) - 2,
18308ec9a96bSJohannes Berg .ext_id = WLAN_EID_EXT_EHT_MULTI_LINK,
18318ec9a96bSJohannes Berg .ml.control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC),
18328ec9a96bSJohannes Berg .basic.len = sizeof(mle.basic),
18338ec9a96bSJohannes Berg };
1834fffd0934SJohannes Berg int err;
183546900298SJohannes Berg
18368ec9a96bSJohannes Berg memcpy(mle.basic.mld_mac_addr, sdata->vif.addr, ETH_ALEN);
18378ec9a96bSJohannes Berg
183815e230abSFred Zhou /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
1839744462a9SMax Stepanov skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN +
18408ec9a96bSJohannes Berg 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN +
18418ec9a96bSJohannes Berg multi_link * sizeof(mle));
1842d15b8459SJoe Perches if (!skb)
184346900298SJohannes Berg return;
1844d15b8459SJoe Perches
1845744462a9SMax Stepanov skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);
184646900298SJohannes Berg
1847b080db58SJohannes Berg mgmt = skb_put_zero(skb, 24 + 6);
184846900298SJohannes Berg mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
184946900298SJohannes Berg IEEE80211_STYPE_AUTH);
1850efa6a09dSAntonio Quartulli memcpy(mgmt->da, da, ETH_ALEN);
185147846c9bSJohannes Berg memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
185246900298SJohannes Berg memcpy(mgmt->bssid, bssid, ETH_ALEN);
185346900298SJohannes Berg mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
185446900298SJohannes Berg mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
1855700e8ea6SJouni Malinen mgmt->u.auth.status_code = cpu_to_le16(status);
185646900298SJohannes Berg if (extra)
185759ae1d12SJohannes Berg skb_put_data(skb, extra, extra_len);
18588ec9a96bSJohannes Berg if (multi_link)
18598ec9a96bSJohannes Berg skb_put_data(skb, &mle, sizeof(mle));
186046900298SJohannes Berg
1861fffd0934SJohannes Berg if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
1862fffd0934SJohannes Berg mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
1863fffd0934SJohannes Berg err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
18647d7b0075SJohannes Berg if (WARN_ON(err)) {
18657d7b0075SJohannes Berg kfree_skb(skb);
18667d7b0075SJohannes Berg return;
18677d7b0075SJohannes Berg }
1868fffd0934SJohannes Berg }
1869fffd0934SJohannes Berg
18701672c0e3SJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
18711672c0e3SJohannes Berg tx_flags;
187262ae67beSJohannes Berg ieee80211_tx_skb(sdata, skb);
187346900298SJohannes Berg }
187446900298SJohannes Berg
ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data * sdata,const u8 * da,const u8 * bssid,u16 stype,u16 reason,bool send_frame,u8 * frame_buf)18756ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
18764b08d1b6SJohannes Berg const u8 *da, const u8 *bssid,
18774b08d1b6SJohannes Berg u16 stype, u16 reason,
18786ae16775SAntonio Quartulli bool send_frame, u8 *frame_buf)
18796ae16775SAntonio Quartulli {
18806ae16775SAntonio Quartulli struct ieee80211_local *local = sdata->local;
18816ae16775SAntonio Quartulli struct sk_buff *skb;
18826ae16775SAntonio Quartulli struct ieee80211_mgmt *mgmt = (void *)frame_buf;
18836ae16775SAntonio Quartulli
18846ae16775SAntonio Quartulli /* build frame */
18856ae16775SAntonio Quartulli mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
18866ae16775SAntonio Quartulli mgmt->duration = 0; /* initialize only */
18876ae16775SAntonio Quartulli mgmt->seq_ctrl = 0; /* initialize only */
18884b08d1b6SJohannes Berg memcpy(mgmt->da, da, ETH_ALEN);
18896ae16775SAntonio Quartulli memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
18906ae16775SAntonio Quartulli memcpy(mgmt->bssid, bssid, ETH_ALEN);
18916ae16775SAntonio Quartulli /* u.deauth.reason_code == u.disassoc.reason_code */
18926ae16775SAntonio Quartulli mgmt->u.deauth.reason_code = cpu_to_le16(reason);
18936ae16775SAntonio Quartulli
18946ae16775SAntonio Quartulli if (send_frame) {
18956ae16775SAntonio Quartulli skb = dev_alloc_skb(local->hw.extra_tx_headroom +
18966ae16775SAntonio Quartulli IEEE80211_DEAUTH_FRAME_LEN);
18976ae16775SAntonio Quartulli if (!skb)
18986ae16775SAntonio Quartulli return;
18996ae16775SAntonio Quartulli
19006ae16775SAntonio Quartulli skb_reserve(skb, local->hw.extra_tx_headroom);
19016ae16775SAntonio Quartulli
19026ae16775SAntonio Quartulli /* copy in frame */
190359ae1d12SJohannes Berg skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN);
19046ae16775SAntonio Quartulli
19056ae16775SAntonio Quartulli if (sdata->vif.type != NL80211_IFTYPE_STATION ||
19066ae16775SAntonio Quartulli !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
19076ae16775SAntonio Quartulli IEEE80211_SKB_CB(skb)->flags |=
19086ae16775SAntonio Quartulli IEEE80211_TX_INTFL_DONT_ENCRYPT;
19096ae16775SAntonio Quartulli
19106ae16775SAntonio Quartulli ieee80211_tx_skb(sdata, skb);
19116ae16775SAntonio Quartulli }
19126ae16775SAntonio Quartulli }
19136ae16775SAntonio Quartulli
ieee80211_write_he_6ghz_cap(u8 * pos,__le16 cap,u8 * end)191471b3b7acSAbhishek Naik u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end)
19152ad2274cSIlan Peer {
19162ad2274cSIlan Peer if ((end - pos) < 5)
19172ad2274cSIlan Peer return pos;
19182ad2274cSIlan Peer
19192ad2274cSIlan Peer *pos++ = WLAN_EID_EXTENSION;
19202ad2274cSIlan Peer *pos++ = 1 + sizeof(cap);
19212ad2274cSIlan Peer *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA;
19222ad2274cSIlan Peer memcpy(pos, &cap, sizeof(cap));
19232ad2274cSIlan Peer
19242ad2274cSIlan Peer return pos + 2;
19252ad2274cSIlan Peer }
19262ad2274cSIlan Peer
ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data * sdata,u8 * buffer,size_t buffer_len,const u8 * ie,size_t ie_len,enum nl80211_band band,u32 rate_mask,struct cfg80211_chan_def * chandef,size_t * offset,u32 flags)19272ad2274cSIlan Peer static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
1928c56ef672SDavid Spinadel u8 *buffer, size_t buffer_len,
1929c56ef672SDavid Spinadel const u8 *ie, size_t ie_len,
193057fbcce3SJohannes Berg enum nl80211_band band,
1931c56ef672SDavid Spinadel u32 rate_mask,
1932c56ef672SDavid Spinadel struct cfg80211_chan_def *chandef,
193300387f32SJohannes Berg size_t *offset, u32 flags)
1934de95a54bSJohannes Berg {
19352ad2274cSIlan Peer struct ieee80211_local *local = sdata->local;
1936de95a54bSJohannes Berg struct ieee80211_supported_band *sband;
193741cbb0f5SLuca Coelho const struct ieee80211_sta_he_cap *he_cap;
1938820acc81SIlan Peer const struct ieee80211_sta_eht_cap *eht_cap;
1939c604b9f2SJohannes Berg u8 *pos = buffer, *end = buffer + buffer_len;
1940c56ef672SDavid Spinadel size_t noffset;
19418e664fb3SJohannes Berg int supp_rates_len, i;
19428dcb2003SJouni Malinen u8 rates[32];
19438dcb2003SJouni Malinen int num_rates;
19448dcb2003SJouni Malinen int ext_rates_len;
19452103dec1SSimon Wunderlich int shift;
19462103dec1SSimon Wunderlich u32 rate_flags;
194740a11ca8SJohannes Berg bool have_80mhz = false;
1948de95a54bSJohannes Berg
1949c56ef672SDavid Spinadel *offset = 0;
1950c56ef672SDavid Spinadel
19514d36ec58SJohannes Berg sband = local->hw.wiphy->bands[band];
1952d811b3d5SArik Nemtsov if (WARN_ON_ONCE(!sband))
1953d811b3d5SArik Nemtsov return 0;
1954de95a54bSJohannes Berg
19552103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(chandef);
19562103dec1SSimon Wunderlich shift = ieee80211_chandef_get_shift(chandef);
19572103dec1SSimon Wunderlich
19580333a81bSKieran Frewen /* For direct scan add S1G IE and consider its override bits */
19590333a81bSKieran Frewen if (band == NL80211_BAND_S1GHZ) {
19600333a81bSKieran Frewen if (end - pos < 2 + sizeof(struct ieee80211_s1g_cap))
19610333a81bSKieran Frewen goto out_err;
19620333a81bSKieran Frewen pos = ieee80211_ie_build_s1g_cap(pos, &sband->s1g_cap);
19630333a81bSKieran Frewen goto done;
19640333a81bSKieran Frewen }
19650333a81bSKieran Frewen
19668dcb2003SJouni Malinen num_rates = 0;
19678dcb2003SJouni Malinen for (i = 0; i < sband->n_bitrates; i++) {
19688dcb2003SJouni Malinen if ((BIT(i) & rate_mask) == 0)
19698dcb2003SJouni Malinen continue; /* skip rate */
19702103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
19712103dec1SSimon Wunderlich continue;
19722103dec1SSimon Wunderlich
19732103dec1SSimon Wunderlich rates[num_rates++] =
19742103dec1SSimon Wunderlich (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
19752103dec1SSimon Wunderlich (1 << shift) * 5);
19768dcb2003SJouni Malinen }
19778dcb2003SJouni Malinen
19788dcb2003SJouni Malinen supp_rates_len = min_t(int, num_rates, 8);
19798e664fb3SJohannes Berg
1980c604b9f2SJohannes Berg if (end - pos < 2 + supp_rates_len)
1981c604b9f2SJohannes Berg goto out_err;
1982de95a54bSJohannes Berg *pos++ = WLAN_EID_SUPP_RATES;
19838e664fb3SJohannes Berg *pos++ = supp_rates_len;
19848dcb2003SJouni Malinen memcpy(pos, rates, supp_rates_len);
19858dcb2003SJouni Malinen pos += supp_rates_len;
1986de95a54bSJohannes Berg
19878e664fb3SJohannes Berg /* insert "request information" if in custom IEs */
19888e664fb3SJohannes Berg if (ie && ie_len) {
19898e664fb3SJohannes Berg static const u8 before_extrates[] = {
19908e664fb3SJohannes Berg WLAN_EID_SSID,
19918e664fb3SJohannes Berg WLAN_EID_SUPP_RATES,
19928e664fb3SJohannes Berg WLAN_EID_REQUEST,
19938e664fb3SJohannes Berg };
19948e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len,
19958e664fb3SJohannes Berg before_extrates,
19968e664fb3SJohannes Berg ARRAY_SIZE(before_extrates),
1997c56ef672SDavid Spinadel *offset);
1998c56ef672SDavid Spinadel if (end - pos < noffset - *offset)
1999c604b9f2SJohannes Berg goto out_err;
2000c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset);
2001c56ef672SDavid Spinadel pos += noffset - *offset;
2002c56ef672SDavid Spinadel *offset = noffset;
20038e664fb3SJohannes Berg }
20048e664fb3SJohannes Berg
20058dcb2003SJouni Malinen ext_rates_len = num_rates - supp_rates_len;
20068dcb2003SJouni Malinen if (ext_rates_len > 0) {
2007c604b9f2SJohannes Berg if (end - pos < 2 + ext_rates_len)
2008c604b9f2SJohannes Berg goto out_err;
2009de95a54bSJohannes Berg *pos++ = WLAN_EID_EXT_SUPP_RATES;
20108dcb2003SJouni Malinen *pos++ = ext_rates_len;
20118dcb2003SJouni Malinen memcpy(pos, rates + supp_rates_len, ext_rates_len);
20128dcb2003SJouni Malinen pos += ext_rates_len;
20138e664fb3SJohannes Berg }
20148e664fb3SJohannes Berg
201557fbcce3SJohannes Berg if (chandef->chan && sband->band == NL80211_BAND_2GHZ) {
2016c604b9f2SJohannes Berg if (end - pos < 3)
2017c604b9f2SJohannes Berg goto out_err;
2018651b5225SJouni Malinen *pos++ = WLAN_EID_DS_PARAMS;
2019651b5225SJouni Malinen *pos++ = 1;
20202103dec1SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel(
20212103dec1SSimon Wunderlich chandef->chan->center_freq);
2022651b5225SJouni Malinen }
2023651b5225SJouni Malinen
2024b9771d41SJohannes Berg if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT)
2025b9771d41SJohannes Berg goto done;
2026b9771d41SJohannes Berg
20278e664fb3SJohannes Berg /* insert custom IEs that go before HT */
20288e664fb3SJohannes Berg if (ie && ie_len) {
20298e664fb3SJohannes Berg static const u8 before_ht[] = {
2030a7f26d80SJohannes Berg /*
2031a7f26d80SJohannes Berg * no need to list the ones split off already
2032a7f26d80SJohannes Berg * (or generated here)
2033a7f26d80SJohannes Berg */
20348e664fb3SJohannes Berg WLAN_EID_DS_PARAMS,
20358e664fb3SJohannes Berg WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
20368e664fb3SJohannes Berg };
20378e664fb3SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len,
20388e664fb3SJohannes Berg before_ht, ARRAY_SIZE(before_ht),
2039c56ef672SDavid Spinadel *offset);
2040c56ef672SDavid Spinadel if (end - pos < noffset - *offset)
2041c604b9f2SJohannes Berg goto out_err;
2042c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset);
2043c56ef672SDavid Spinadel pos += noffset - *offset;
2044c56ef672SDavid Spinadel *offset = noffset;
2045de95a54bSJohannes Berg }
2046de95a54bSJohannes Berg
2047c604b9f2SJohannes Berg if (sband->ht_cap.ht_supported) {
2048c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
2049c604b9f2SJohannes Berg goto out_err;
2050ef96a842SBen Greear pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
2051ef96a842SBen Greear sband->ht_cap.cap);
2052c604b9f2SJohannes Berg }
20535ef2d41aSJohannes Berg
20544d952300SJohannes Berg /* insert custom IEs that go before VHT */
20558e664fb3SJohannes Berg if (ie && ie_len) {
20564d952300SJohannes Berg static const u8 before_vht[] = {
2057a7f26d80SJohannes Berg /*
2058a7f26d80SJohannes Berg * no need to list the ones split off already
2059a7f26d80SJohannes Berg * (or generated here)
2060a7f26d80SJohannes Berg */
20614d952300SJohannes Berg WLAN_EID_BSS_COEX_2040,
20624d952300SJohannes Berg WLAN_EID_EXT_CAPABILITY,
20634d952300SJohannes Berg WLAN_EID_SSID_LIST,
20644d952300SJohannes Berg WLAN_EID_CHANNEL_USAGE,
20654d952300SJohannes Berg WLAN_EID_INTERWORKING,
2066b44eebeaSLiad Kaufman WLAN_EID_MESH_ID,
2067a7f26d80SJohannes Berg /* 60 GHz (Multi-band, DMG, MMS) can't happen */
20684d952300SJohannes Berg };
20694d952300SJohannes Berg noffset = ieee80211_ie_split(ie, ie_len,
20704d952300SJohannes Berg before_vht, ARRAY_SIZE(before_vht),
2071c56ef672SDavid Spinadel *offset);
2072c56ef672SDavid Spinadel if (end - pos < noffset - *offset)
2073c604b9f2SJohannes Berg goto out_err;
2074c56ef672SDavid Spinadel memcpy(pos, ie + *offset, noffset - *offset);
2075c56ef672SDavid Spinadel pos += noffset - *offset;
2076c56ef672SDavid Spinadel *offset = noffset;
2077de95a54bSJohannes Berg }
2078de95a54bSJohannes Berg
207940a11ca8SJohannes Berg /* Check if any channel in this sband supports at least 80 MHz */
208040a11ca8SJohannes Berg for (i = 0; i < sband->n_channels; i++) {
2081fa44b988SArik Nemtsov if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
2082fa44b988SArik Nemtsov IEEE80211_CHAN_NO_80MHZ))
2083fa44b988SArik Nemtsov continue;
2084fa44b988SArik Nemtsov
208540a11ca8SJohannes Berg have_80mhz = true;
208640a11ca8SJohannes Berg break;
208740a11ca8SJohannes Berg }
208840a11ca8SJohannes Berg
208940a11ca8SJohannes Berg if (sband->vht_cap.vht_supported && have_80mhz) {
2090c604b9f2SJohannes Berg if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
2091c604b9f2SJohannes Berg goto out_err;
2092ba0afa2fSMahesh Palivela pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
2093ba0afa2fSMahesh Palivela sband->vht_cap.cap);
2094c604b9f2SJohannes Berg }
2095ba0afa2fSMahesh Palivela
209641cbb0f5SLuca Coelho /* insert custom IEs that go before HE */
209741cbb0f5SLuca Coelho if (ie && ie_len) {
209841cbb0f5SLuca Coelho static const u8 before_he[] = {
209941cbb0f5SLuca Coelho /*
210041cbb0f5SLuca Coelho * no need to list the ones split off before VHT
210141cbb0f5SLuca Coelho * or generated here
210241cbb0f5SLuca Coelho */
210341cbb0f5SLuca Coelho WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
210441cbb0f5SLuca Coelho WLAN_EID_AP_CSN,
210541cbb0f5SLuca Coelho /* TODO: add 11ah/11aj/11ak elements */
210641cbb0f5SLuca Coelho };
210741cbb0f5SLuca Coelho noffset = ieee80211_ie_split(ie, ie_len,
210841cbb0f5SLuca Coelho before_he, ARRAY_SIZE(before_he),
210941cbb0f5SLuca Coelho *offset);
211041cbb0f5SLuca Coelho if (end - pos < noffset - *offset)
211141cbb0f5SLuca Coelho goto out_err;
211241cbb0f5SLuca Coelho memcpy(pos, ie + *offset, noffset - *offset);
211341cbb0f5SLuca Coelho pos += noffset - *offset;
211441cbb0f5SLuca Coelho *offset = noffset;
211541cbb0f5SLuca Coelho }
211641cbb0f5SLuca Coelho
21171ec7291eSJohannes Berg he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
21180bc47057SJohannes Berg if (he_cap &&
21190bc47057SJohannes Berg cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
21200bc47057SJohannes Berg IEEE80211_CHAN_NO_HE)) {
21211f2c1044SJohannes Berg pos = ieee80211_ie_build_he_cap(0, pos, he_cap, end);
212241cbb0f5SLuca Coelho if (!pos)
212341cbb0f5SLuca Coelho goto out_err;
21247d29bc50SJohannes Berg }
21252ad2274cSIlan Peer
21261ec7291eSJohannes Berg eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
2127820acc81SIlan Peer
2128820acc81SIlan Peer if (eht_cap &&
2129820acc81SIlan Peer cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
2130820acc81SIlan Peer IEEE80211_CHAN_NO_HE |
2131820acc81SIlan Peer IEEE80211_CHAN_NO_EHT)) {
2132ea5cba26SJohannes Berg pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end,
2133ea5cba26SJohannes Berg sdata->vif.type == NL80211_IFTYPE_AP);
2134820acc81SIlan Peer if (!pos)
2135820acc81SIlan Peer goto out_err;
2136820acc81SIlan Peer }
2137820acc81SIlan Peer
21387d29bc50SJohannes Berg if (cfg80211_any_usable_channels(local->hw.wiphy,
21397d29bc50SJohannes Berg BIT(NL80211_BAND_6GHZ),
21407d29bc50SJohannes Berg IEEE80211_CHAN_NO_HE)) {
21417d29bc50SJohannes Berg struct ieee80211_supported_band *sband6;
21427d29bc50SJohannes Berg
21437d29bc50SJohannes Berg sband6 = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
21441ec7291eSJohannes Berg he_cap = ieee80211_get_he_iftype_cap_vif(sband6, &sdata->vif);
21457d29bc50SJohannes Berg
21467d29bc50SJohannes Berg if (he_cap) {
21472ad2274cSIlan Peer enum nl80211_iftype iftype =
21482ad2274cSIlan Peer ieee80211_vif_type_p2p(&sdata->vif);
2149b650009fSJames Prestwood __le16 cap = ieee80211_get_he_6ghz_capa(sband6, iftype);
21502ad2274cSIlan Peer
21512ad2274cSIlan Peer pos = ieee80211_write_he_6ghz_cap(pos, cap, end);
21522ad2274cSIlan Peer }
215341cbb0f5SLuca Coelho }
215441cbb0f5SLuca Coelho
215541cbb0f5SLuca Coelho /*
215641cbb0f5SLuca Coelho * If adding more here, adjust code in main.c
215741cbb0f5SLuca Coelho * that calculates local->scan_ies_len.
215841cbb0f5SLuca Coelho */
215941cbb0f5SLuca Coelho
2160de95a54bSJohannes Berg return pos - buffer;
2161c604b9f2SJohannes Berg out_err:
2162c604b9f2SJohannes Berg WARN_ONCE(1, "not enough space for preq IEs\n");
2163b9771d41SJohannes Berg done:
2164c604b9f2SJohannes Berg return pos - buffer;
2165de95a54bSJohannes Berg }
2166de95a54bSJohannes Berg
ieee80211_build_preq_ies(struct ieee80211_sub_if_data * sdata,u8 * buffer,size_t buffer_len,struct ieee80211_scan_ies * ie_desc,const u8 * ie,size_t ie_len,u8 bands_used,u32 * rate_masks,struct cfg80211_chan_def * chandef,u32 flags)21672ad2274cSIlan Peer int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
2168c56ef672SDavid Spinadel size_t buffer_len,
2169c56ef672SDavid Spinadel struct ieee80211_scan_ies *ie_desc,
2170c56ef672SDavid Spinadel const u8 *ie, size_t ie_len,
2171c56ef672SDavid Spinadel u8 bands_used, u32 *rate_masks,
217200387f32SJohannes Berg struct cfg80211_chan_def *chandef,
217300387f32SJohannes Berg u32 flags)
2174c56ef672SDavid Spinadel {
2175c56ef672SDavid Spinadel size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
2176c56ef672SDavid Spinadel int i;
2177c56ef672SDavid Spinadel
2178c56ef672SDavid Spinadel memset(ie_desc, 0, sizeof(*ie_desc));
2179c56ef672SDavid Spinadel
218057fbcce3SJohannes Berg for (i = 0; i < NUM_NL80211_BANDS; i++) {
2181c56ef672SDavid Spinadel if (bands_used & BIT(i)) {
21822ad2274cSIlan Peer pos += ieee80211_build_preq_ies_band(sdata,
2183c56ef672SDavid Spinadel buffer + pos,
2184c56ef672SDavid Spinadel buffer_len - pos,
2185c56ef672SDavid Spinadel ie, ie_len, i,
2186c56ef672SDavid Spinadel rate_masks[i],
2187c56ef672SDavid Spinadel chandef,
218800387f32SJohannes Berg &custom_ie_offset,
218900387f32SJohannes Berg flags);
2190c56ef672SDavid Spinadel ie_desc->ies[i] = buffer + old_pos;
2191c56ef672SDavid Spinadel ie_desc->len[i] = pos - old_pos;
2192c56ef672SDavid Spinadel old_pos = pos;
2193c56ef672SDavid Spinadel }
2194c56ef672SDavid Spinadel }
2195c56ef672SDavid Spinadel
2196c56ef672SDavid Spinadel /* add any remaining custom IEs */
2197c56ef672SDavid Spinadel if (ie && ie_len) {
2198c56ef672SDavid Spinadel if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
2199c56ef672SDavid Spinadel "not enough space for preq custom IEs\n"))
2200c56ef672SDavid Spinadel return pos;
2201c56ef672SDavid Spinadel memcpy(buffer + pos, ie + custom_ie_offset,
2202c56ef672SDavid Spinadel ie_len - custom_ie_offset);
2203c56ef672SDavid Spinadel ie_desc->common_ies = buffer + pos;
2204c56ef672SDavid Spinadel ie_desc->common_ie_len = ie_len - custom_ie_offset;
2205c56ef672SDavid Spinadel pos += ie_len - custom_ie_offset;
2206c56ef672SDavid Spinadel }
2207c56ef672SDavid Spinadel
2208c56ef672SDavid Spinadel return pos;
2209c56ef672SDavid Spinadel };
2210c56ef672SDavid Spinadel
ieee80211_build_probe_req(struct ieee80211_sub_if_data * sdata,const u8 * src,const u8 * dst,u32 ratemask,struct ieee80211_channel * chan,const u8 * ssid,size_t ssid_len,const u8 * ie,size_t ie_len,u32 flags)2211a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
2212a344d677SJohannes Berg const u8 *src, const u8 *dst,
2213a344d677SJohannes Berg u32 ratemask,
22146b77863bSJohannes Berg struct ieee80211_channel *chan,
2215de95a54bSJohannes Berg const u8 *ssid, size_t ssid_len,
2216a806c558SPaul Stewart const u8 *ie, size_t ie_len,
221700387f32SJohannes Berg u32 flags)
221846900298SJohannes Berg {
221946900298SJohannes Berg struct ieee80211_local *local = sdata->local;
22202103dec1SSimon Wunderlich struct cfg80211_chan_def chandef;
222146900298SJohannes Berg struct sk_buff *skb;
222246900298SJohannes Berg struct ieee80211_mgmt *mgmt;
2223b9a9ada1SJohannes Berg int ies_len;
222457fbcce3SJohannes Berg u32 rate_masks[NUM_NL80211_BANDS] = {};
2225c56ef672SDavid Spinadel struct ieee80211_scan_ies dummy_ie_desc;
222646900298SJohannes Berg
2227a806c558SPaul Stewart /*
2228a806c558SPaul Stewart * Do not send DS Channel parameter for directed probe requests
2229a806c558SPaul Stewart * in order to maximize the chance that we get a response. Some
2230a806c558SPaul Stewart * badly-behaved APs don't respond when this parameter is included.
2231a806c558SPaul Stewart */
22322103dec1SSimon Wunderlich chandef.width = sdata->vif.bss_conf.chandef.width;
223300387f32SJohannes Berg if (flags & IEEE80211_PROBE_FLAG_DIRECTED)
22342103dec1SSimon Wunderlich chandef.chan = NULL;
2235a806c558SPaul Stewart else
22362103dec1SSimon Wunderlich chandef.chan = chan;
2237651b5225SJouni Malinen
2238a344d677SJohannes Berg skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
2239f22d9813SIlan Peer local->scan_ies_len + ie_len);
22405b2bbf75SJohannes Berg if (!skb)
2241b9a9ada1SJohannes Berg return NULL;
2242b9a9ada1SJohannes Berg
2243c56ef672SDavid Spinadel rate_masks[chan->band] = ratemask;
22442ad2274cSIlan Peer ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb),
2245c56ef672SDavid Spinadel skb_tailroom(skb), &dummy_ie_desc,
2246c56ef672SDavid Spinadel ie, ie_len, BIT(chan->band),
224700387f32SJohannes Berg rate_masks, &chandef, flags);
2248b9a9ada1SJohannes Berg skb_put(skb, ies_len);
22497c12ce8bSKalle Valo
225046900298SJohannes Berg if (dst) {
22517c12ce8bSKalle Valo mgmt = (struct ieee80211_mgmt *) skb->data;
225246900298SJohannes Berg memcpy(mgmt->da, dst, ETH_ALEN);
225346900298SJohannes Berg memcpy(mgmt->bssid, dst, ETH_ALEN);
225446900298SJohannes Berg }
225546900298SJohannes Berg
225662ae67beSJohannes Berg IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
22575b2bbf75SJohannes Berg
2258a619a4c0SJuuso Oikarinen return skb;
2259a619a4c0SJuuso Oikarinen }
2260a619a4c0SJuuso Oikarinen
ieee80211_sta_get_rates(struct ieee80211_sub_if_data * sdata,struct ieee802_11_elems * elems,enum nl80211_band band,u32 * basic_rates)22612103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
226246900298SJohannes Berg struct ieee802_11_elems *elems,
226357fbcce3SJohannes Berg enum nl80211_band band, u32 *basic_rates)
226446900298SJohannes Berg {
226546900298SJohannes Berg struct ieee80211_supported_band *sband;
226646900298SJohannes Berg size_t num_rates;
22672103dec1SSimon Wunderlich u32 supp_rates, rate_flags;
22682103dec1SSimon Wunderlich int i, j, shift;
226921a8e9ddSMohammed Shafi Shajakhan
22702103dec1SSimon Wunderlich sband = sdata->local->hw.wiphy->bands[band];
227121a8e9ddSMohammed Shafi Shajakhan if (WARN_ON(!sband))
227221a8e9ddSMohammed Shafi Shajakhan return 1;
22732103dec1SSimon Wunderlich
22742103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
22752103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif);
227646900298SJohannes Berg
227746900298SJohannes Berg num_rates = sband->n_bitrates;
227846900298SJohannes Berg supp_rates = 0;
227946900298SJohannes Berg for (i = 0; i < elems->supp_rates_len +
228046900298SJohannes Berg elems->ext_supp_rates_len; i++) {
228146900298SJohannes Berg u8 rate = 0;
228246900298SJohannes Berg int own_rate;
22839ebb61a2SAshok Nagarajan bool is_basic;
228446900298SJohannes Berg if (i < elems->supp_rates_len)
228546900298SJohannes Berg rate = elems->supp_rates[i];
228646900298SJohannes Berg else if (elems->ext_supp_rates)
228746900298SJohannes Berg rate = elems->ext_supp_rates
228846900298SJohannes Berg [i - elems->supp_rates_len];
228946900298SJohannes Berg own_rate = 5 * (rate & 0x7f);
22909ebb61a2SAshok Nagarajan is_basic = !!(rate & 0x80);
22919ebb61a2SAshok Nagarajan
22929ebb61a2SAshok Nagarajan if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
22939ebb61a2SAshok Nagarajan continue;
22949ebb61a2SAshok Nagarajan
22959ebb61a2SAshok Nagarajan for (j = 0; j < num_rates; j++) {
22962103dec1SSimon Wunderlich int brate;
22972103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[j].flags)
22982103dec1SSimon Wunderlich != rate_flags)
22992103dec1SSimon Wunderlich continue;
23002103dec1SSimon Wunderlich
23012103dec1SSimon Wunderlich brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
23022103dec1SSimon Wunderlich 1 << shift);
23032103dec1SSimon Wunderlich
23042103dec1SSimon Wunderlich if (brate == own_rate) {
230546900298SJohannes Berg supp_rates |= BIT(j);
23069ebb61a2SAshok Nagarajan if (basic_rates && is_basic)
23079ebb61a2SAshok Nagarajan *basic_rates |= BIT(j);
23089ebb61a2SAshok Nagarajan }
23099ebb61a2SAshok Nagarajan }
231046900298SJohannes Berg }
231146900298SJohannes Berg return supp_rates;
231246900298SJohannes Berg }
2313f2753ddbSJohannes Berg
ieee80211_stop_device(struct ieee80211_local * local)231484f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local)
231584f6a01cSJohannes Berg {
231684f6a01cSJohannes Berg ieee80211_led_radio(local, false);
231767408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
231884f6a01cSJohannes Berg
231984f6a01cSJohannes Berg cancel_work_sync(&local->reconfig_filter);
232084f6a01cSJohannes Berg
232184f6a01cSJohannes Berg flush_workqueue(local->workqueue);
2322678f415fSLennert Buytenhek drv_stop(local);
232384f6a01cSJohannes Berg }
232484f6a01cSJohannes Berg
ieee80211_flush_completed_scan(struct ieee80211_local * local,bool aborted)232574430f94SJohannes Berg static void ieee80211_flush_completed_scan(struct ieee80211_local *local,
232674430f94SJohannes Berg bool aborted)
232774430f94SJohannes Berg {
232874430f94SJohannes Berg /* It's possible that we don't handle the scan completion in
232974430f94SJohannes Berg * time during suspend, so if it's still marked as completed
233074430f94SJohannes Berg * here, queue the work and flush it to clean things up.
233174430f94SJohannes Berg * Instead of calling the worker function directly here, we
233274430f94SJohannes Berg * really queue it to avoid potential races with other flows
233374430f94SJohannes Berg * scheduling the same work.
233474430f94SJohannes Berg */
233574430f94SJohannes Berg if (test_bit(SCAN_COMPLETED, &local->scanning)) {
233674430f94SJohannes Berg /* If coming from reconfiguration failure, abort the scan so
233774430f94SJohannes Berg * we don't attempt to continue a partial HW scan - which is
233874430f94SJohannes Berg * possible otherwise if (e.g.) the 2.4 GHz portion was the
233974430f94SJohannes Berg * completed scan, and a 5 GHz portion is still pending.
234074430f94SJohannes Berg */
234174430f94SJohannes Berg if (aborted)
234274430f94SJohannes Berg set_bit(SCAN_ABORTED, &local->scanning);
2343*d69c7a4eSJohannes Berg wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
2344*d69c7a4eSJohannes Berg wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
234574430f94SJohannes Berg }
234674430f94SJohannes Berg }
234774430f94SJohannes Berg
ieee80211_handle_reconfig_failure(struct ieee80211_local * local)2348f6837ba8SJohannes Berg static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
2349f6837ba8SJohannes Berg {
2350f6837ba8SJohannes Berg struct ieee80211_sub_if_data *sdata;
2351f6837ba8SJohannes Berg struct ieee80211_chanctx *ctx;
2352f6837ba8SJohannes Berg
2353f6837ba8SJohannes Berg /*
2354f6837ba8SJohannes Berg * We get here if during resume the device can't be restarted properly.
2355f6837ba8SJohannes Berg * We might also get here if this happens during HW reset, which is a
2356f6837ba8SJohannes Berg * slightly different situation and we need to drop all connections in
2357f6837ba8SJohannes Berg * the latter case.
2358f6837ba8SJohannes Berg *
2359f6837ba8SJohannes Berg * Ask cfg80211 to turn off all interfaces, this will result in more
2360f6837ba8SJohannes Berg * warnings but at least we'll then get into a clean stopped state.
2361f6837ba8SJohannes Berg */
2362f6837ba8SJohannes Berg
2363f6837ba8SJohannes Berg local->resuming = false;
2364f6837ba8SJohannes Berg local->suspended = false;
23657584f88fSEliad Peller local->in_reconfig = false;
2366c4fdb081SJohannes Berg local->reconfig_failure = true;
2367f6837ba8SJohannes Berg
236874430f94SJohannes Berg ieee80211_flush_completed_scan(local, true);
236974430f94SJohannes Berg
2370f6837ba8SJohannes Berg /* scheduled scan clearly can't be running any more, but tell
2371f6837ba8SJohannes Berg * cfg80211 and clear local state
2372f6837ba8SJohannes Berg */
2373f6837ba8SJohannes Berg ieee80211_sched_scan_end(local);
2374f6837ba8SJohannes Berg
2375f6837ba8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list)
2376f6837ba8SJohannes Berg sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
2377f6837ba8SJohannes Berg
2378f6837ba8SJohannes Berg /* Mark channel contexts as not being in the driver any more to avoid
2379f6837ba8SJohannes Berg * removing them from the driver during the shutdown process...
2380f6837ba8SJohannes Berg */
2381f6837ba8SJohannes Berg mutex_lock(&local->chanctx_mtx);
2382f6837ba8SJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list)
2383f6837ba8SJohannes Berg ctx->driver_present = false;
2384f6837ba8SJohannes Berg mutex_unlock(&local->chanctx_mtx);
2385f6837ba8SJohannes Berg }
2386f6837ba8SJohannes Berg
ieee80211_assign_chanctx(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_link_data * link)2387153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local,
2388b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata,
2389d8675a63SJohannes Berg struct ieee80211_link_data *link)
2390153a5fc4SStanislaw Gruszka {
2391153a5fc4SStanislaw Gruszka struct ieee80211_chanctx_conf *conf;
2392153a5fc4SStanislaw Gruszka struct ieee80211_chanctx *ctx;
2393153a5fc4SStanislaw Gruszka
2394153a5fc4SStanislaw Gruszka if (!local->use_chanctx)
2395153a5fc4SStanislaw Gruszka return;
2396153a5fc4SStanislaw Gruszka
2397153a5fc4SStanislaw Gruszka mutex_lock(&local->chanctx_mtx);
2398d8675a63SJohannes Berg conf = rcu_dereference_protected(link->conf->chanctx_conf,
2399153a5fc4SStanislaw Gruszka lockdep_is_held(&local->chanctx_mtx));
2400153a5fc4SStanislaw Gruszka if (conf) {
2401153a5fc4SStanislaw Gruszka ctx = container_of(conf, struct ieee80211_chanctx, conf);
2402727eff4dSGregory Greenman drv_assign_vif_chanctx(local, sdata, link->conf, ctx);
2403153a5fc4SStanislaw Gruszka }
2404153a5fc4SStanislaw Gruszka mutex_unlock(&local->chanctx_mtx);
2405153a5fc4SStanislaw Gruszka }
2406153a5fc4SStanislaw Gruszka
ieee80211_reconfig_stations(struct ieee80211_sub_if_data * sdata)24071ea2c864SJohannes Berg static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata)
24081ea2c864SJohannes Berg {
24091ea2c864SJohannes Berg struct ieee80211_local *local = sdata->local;
24101ea2c864SJohannes Berg struct sta_info *sta;
24111ea2c864SJohannes Berg
24121ea2c864SJohannes Berg /* add STAs back */
24131ea2c864SJohannes Berg mutex_lock(&local->sta_mtx);
24141ea2c864SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) {
24151ea2c864SJohannes Berg enum ieee80211_sta_state state;
24161ea2c864SJohannes Berg
24171ea2c864SJohannes Berg if (!sta->uploaded || sta->sdata != sdata)
24181ea2c864SJohannes Berg continue;
24191ea2c864SJohannes Berg
24201ea2c864SJohannes Berg for (state = IEEE80211_STA_NOTEXIST;
24211ea2c864SJohannes Berg state < sta->sta_state; state++)
24221ea2c864SJohannes Berg WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
24231ea2c864SJohannes Berg state + 1));
24241ea2c864SJohannes Berg }
24251ea2c864SJohannes Berg mutex_unlock(&local->sta_mtx);
24261ea2c864SJohannes Berg }
24271ea2c864SJohannes Berg
ieee80211_reconfig_nan(struct ieee80211_sub_if_data * sdata)2428167e33f4SAyala Beker static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata)
2429167e33f4SAyala Beker {
2430167e33f4SAyala Beker struct cfg80211_nan_func *func, **funcs;
2431167e33f4SAyala Beker int res, id, i = 0;
2432167e33f4SAyala Beker
2433167e33f4SAyala Beker res = drv_start_nan(sdata->local, sdata,
2434167e33f4SAyala Beker &sdata->u.nan.conf);
2435167e33f4SAyala Beker if (WARN_ON(res))
2436167e33f4SAyala Beker return res;
2437167e33f4SAyala Beker
24386396bb22SKees Cook funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1,
24396396bb22SKees Cook sizeof(*funcs),
24406396bb22SKees Cook GFP_KERNEL);
2441167e33f4SAyala Beker if (!funcs)
2442167e33f4SAyala Beker return -ENOMEM;
2443167e33f4SAyala Beker
2444167e33f4SAyala Beker /* Add all the functions:
2445167e33f4SAyala Beker * This is a little bit ugly. We need to call a potentially sleeping
2446167e33f4SAyala Beker * callback for each NAN function, so we can't hold the spinlock.
2447167e33f4SAyala Beker */
2448167e33f4SAyala Beker spin_lock_bh(&sdata->u.nan.func_lock);
2449167e33f4SAyala Beker
2450167e33f4SAyala Beker idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id)
2451167e33f4SAyala Beker funcs[i++] = func;
2452167e33f4SAyala Beker
2453167e33f4SAyala Beker spin_unlock_bh(&sdata->u.nan.func_lock);
2454167e33f4SAyala Beker
2455167e33f4SAyala Beker for (i = 0; funcs[i]; i++) {
2456167e33f4SAyala Beker res = drv_add_nan_func(sdata->local, sdata, funcs[i]);
2457167e33f4SAyala Beker if (WARN_ON(res))
2458167e33f4SAyala Beker ieee80211_nan_func_terminated(&sdata->vif,
2459167e33f4SAyala Beker funcs[i]->instance_id,
2460167e33f4SAyala Beker NL80211_NAN_FUNC_TERM_REASON_ERROR,
2461167e33f4SAyala Beker GFP_KERNEL);
2462167e33f4SAyala Beker }
2463167e33f4SAyala Beker
2464167e33f4SAyala Beker kfree(funcs);
2465167e33f4SAyala Beker
2466167e33f4SAyala Beker return 0;
2467167e33f4SAyala Beker }
2468167e33f4SAyala Beker
ieee80211_reconfig_ap_links(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,u64 changed)246961403414SJohannes Berg static void ieee80211_reconfig_ap_links(struct ieee80211_local *local,
247061403414SJohannes Berg struct ieee80211_sub_if_data *sdata,
24712a5325f8SMukesh Sisodiya u64 changed)
247261403414SJohannes Berg {
247361403414SJohannes Berg int link_id;
247461403414SJohannes Berg
247561403414SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
247661403414SJohannes Berg struct ieee80211_link_data *link;
247761403414SJohannes Berg
247861403414SJohannes Berg if (!(sdata->vif.active_links & BIT(link_id)))
247961403414SJohannes Berg continue;
248061403414SJohannes Berg
248161403414SJohannes Berg link = sdata_dereference(sdata->link[link_id], sdata);
248261403414SJohannes Berg if (!link)
248361403414SJohannes Berg continue;
248461403414SJohannes Berg
248561403414SJohannes Berg if (rcu_access_pointer(link->u.ap.beacon))
248661403414SJohannes Berg drv_start_ap(local, sdata, link->conf);
248761403414SJohannes Berg
248861403414SJohannes Berg if (!link->conf->enable_beacon)
248961403414SJohannes Berg continue;
249061403414SJohannes Berg
249161403414SJohannes Berg changed |= BSS_CHANGED_BEACON |
249261403414SJohannes Berg BSS_CHANGED_BEACON_ENABLED;
249361403414SJohannes Berg
249461403414SJohannes Berg ieee80211_link_info_change_notify(sdata, link, changed);
249561403414SJohannes Berg }
249661403414SJohannes Berg }
249761403414SJohannes Berg
ieee80211_reconfig(struct ieee80211_local * local)2498f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local)
2499f2753ddbSJohannes Berg {
2500f2753ddbSJohannes Berg struct ieee80211_hw *hw = &local->hw;
2501f2753ddbSJohannes Berg struct ieee80211_sub_if_data *sdata;
250255de908aSJohannes Berg struct ieee80211_chanctx *ctx;
2503f2753ddbSJohannes Berg struct sta_info *sta;
25042683d65bSEliad Peller int res, i;
2505d888130aSJohannes Berg bool reconfig_due_to_wowlan = false;
2506d43c6b6eSDavid Spinadel struct ieee80211_sub_if_data *sched_scan_sdata;
25076ea0a69cSJohannes Berg struct cfg80211_sched_scan_request *sched_scan_req;
2508d43c6b6eSDavid Spinadel bool sched_scan_stopped = false;
2509b0485e9fSEliad Peller bool suspended = local->suspended;
25107d352ccfSYoughandhar Chintala bool in_reconfig = false;
2511d888130aSJohannes Berg
25120f8b8245SEliad Peller /* nothing to do if HW shouldn't run */
25130f8b8245SEliad Peller if (!local->open_count)
25140f8b8245SEliad Peller goto wake_up;
25150f8b8245SEliad Peller
25168f21b0adSJohannes Berg #ifdef CONFIG_PM
2517b0485e9fSEliad Peller if (suspended)
2518ceb99fe0SJohannes Berg local->resuming = true;
2519f2753ddbSJohannes Berg
2520eecc4800SJohannes Berg if (local->wowlan) {
2521b0485e9fSEliad Peller /*
2522b0485e9fSEliad Peller * In the wowlan case, both mac80211 and the device
2523b0485e9fSEliad Peller * are functional when the resume op is called, so
2524b0485e9fSEliad Peller * clear local->suspended so the device could operate
2525b0485e9fSEliad Peller * normally (e.g. pass rx frames).
2526b0485e9fSEliad Peller */
2527b0485e9fSEliad Peller local->suspended = false;
2528eecc4800SJohannes Berg res = drv_resume(local);
252927b3eb9cSJohannes Berg local->wowlan = false;
2530eecc4800SJohannes Berg if (res < 0) {
2531eecc4800SJohannes Berg local->resuming = false;
2532eecc4800SJohannes Berg return res;
2533eecc4800SJohannes Berg }
2534eecc4800SJohannes Berg if (res == 0)
2535eecc4800SJohannes Berg goto wake_up;
2536eecc4800SJohannes Berg WARN_ON(res > 1);
2537eecc4800SJohannes Berg /*
2538eecc4800SJohannes Berg * res is 1, which means the driver requested
2539eecc4800SJohannes Berg * to go through a regular reset on wakeup.
2540b0485e9fSEliad Peller * restore local->suspended in this case.
2541eecc4800SJohannes Berg */
2542d888130aSJohannes Berg reconfig_due_to_wowlan = true;
2543b0485e9fSEliad Peller local->suspended = true;
2544eecc4800SJohannes Berg }
2545eecc4800SJohannes Berg #endif
254694f9b97bSJohannes Berg
254724feda00SLuis R. Rodriguez /*
254843d6df00SEliad Peller * In case of hw_restart during suspend (without wowlan),
254943d6df00SEliad Peller * cancel restart work, as we are reconfiguring the device
255043d6df00SEliad Peller * anyway.
255143d6df00SEliad Peller * Note that restart_work is scheduled on a frozen workqueue,
255243d6df00SEliad Peller * so we can't deadlock in this case.
255343d6df00SEliad Peller */
255443d6df00SEliad Peller if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
255543d6df00SEliad Peller cancel_work_sync(&local->restart_work);
255643d6df00SEliad Peller
2557968a76ceSEliad Peller local->started = false;
2558968a76ceSEliad Peller
255943d6df00SEliad Peller /*
256024feda00SLuis R. Rodriguez * Upon resume hardware can sometimes be goofy due to
256124feda00SLuis R. Rodriguez * various platform / driver / bus issues, so restarting
256224feda00SLuis R. Rodriguez * the device may at times not work immediately. Propagate
256324feda00SLuis R. Rodriguez * the error.
256424feda00SLuis R. Rodriguez */
256524487981SJohannes Berg res = drv_start(local);
256624feda00SLuis R. Rodriguez if (res) {
2567b0485e9fSEliad Peller if (suspended)
2568f6837ba8SJohannes Berg WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
2569f6837ba8SJohannes Berg else
2570f6837ba8SJohannes Berg WARN(1, "Hardware became unavailable during restart.\n");
2571f6837ba8SJohannes Berg ieee80211_handle_reconfig_failure(local);
257224feda00SLuis R. Rodriguez return res;
257324feda00SLuis R. Rodriguez }
2574f2753ddbSJohannes Berg
25757f281975SYogesh Ashok Powar /* setup fragmentation threshold */
25767f281975SYogesh Ashok Powar drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
25777f281975SYogesh Ashok Powar
25787f281975SYogesh Ashok Powar /* setup RTS threshold */
25797f281975SYogesh Ashok Powar drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
25807f281975SYogesh Ashok Powar
25817f281975SYogesh Ashok Powar /* reset coverage class */
25827f281975SYogesh Ashok Powar drv_set_coverage_class(local, hw->wiphy->coverage_class);
25837f281975SYogesh Ashok Powar
25841f87f7d3SJohannes Berg ieee80211_led_radio(local, true);
258567408c8cSJohannes Berg ieee80211_mod_tpt_led_trig(local,
258667408c8cSJohannes Berg IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
2587f2753ddbSJohannes Berg
2588f2753ddbSJohannes Berg /* add interfaces */
25896dd23603SJohannes Berg sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
25904b6f1dd6SJohannes Berg if (sdata) {
25913c3e21e7SJohannes Berg /* in HW restart it exists already */
25923c3e21e7SJohannes Berg WARN_ON(local->resuming);
25934b6f1dd6SJohannes Berg res = drv_add_interface(local, sdata);
25944b6f1dd6SJohannes Berg if (WARN_ON(res)) {
25950c2bef46SMonam Agarwal RCU_INIT_POINTER(local->monitor_sdata, NULL);
25964b6f1dd6SJohannes Berg synchronize_net();
25974b6f1dd6SJohannes Berg kfree(sdata);
25984b6f1dd6SJohannes Berg }
25994b6f1dd6SJohannes Berg }
26004b6f1dd6SJohannes Berg
2601f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) {
2602f2753ddbSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
2603f2753ddbSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR &&
2604c8fff3dcSLuciano Coelho ieee80211_sdata_running(sdata)) {
26057b7eab6fSJohannes Berg res = drv_add_interface(local, sdata);
2606c8fff3dcSLuciano Coelho if (WARN_ON(res))
2607c8fff3dcSLuciano Coelho break;
2608c8fff3dcSLuciano Coelho }
2609c8fff3dcSLuciano Coelho }
2610c8fff3dcSLuciano Coelho
2611c8fff3dcSLuciano Coelho /* If adding any of the interfaces failed above, roll back and
2612c8fff3dcSLuciano Coelho * report failure.
2613c8fff3dcSLuciano Coelho */
2614c8fff3dcSLuciano Coelho if (res) {
2615c8fff3dcSLuciano Coelho list_for_each_entry_continue_reverse(sdata, &local->interfaces,
2616c8fff3dcSLuciano Coelho list)
2617c8fff3dcSLuciano Coelho if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
2618c8fff3dcSLuciano Coelho sdata->vif.type != NL80211_IFTYPE_MONITOR &&
2619c8fff3dcSLuciano Coelho ieee80211_sdata_running(sdata))
2620c8fff3dcSLuciano Coelho drv_remove_interface(local, sdata);
2621c8fff3dcSLuciano Coelho ieee80211_handle_reconfig_failure(local);
2622c8fff3dcSLuciano Coelho return res;
2623f2753ddbSJohannes Berg }
2624f2753ddbSJohannes Berg
262555de908aSJohannes Berg /* add channel contexts */
2626f0dea9c7SArend van Spriel if (local->use_chanctx) {
262755de908aSJohannes Berg mutex_lock(&local->chanctx_mtx);
262855de908aSJohannes Berg list_for_each_entry(ctx, &local->chanctx_list, list)
26295bcae31dSMichal Kazior if (ctx->replace_state !=
26305bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)
263155de908aSJohannes Berg WARN_ON(drv_add_chanctx(local, ctx));
263255de908aSJohannes Berg mutex_unlock(&local->chanctx_mtx);
263355de908aSJohannes Berg
26346dd23603SJohannes Berg sdata = wiphy_dereference(local->hw.wiphy,
26356dd23603SJohannes Berg local->monitor_sdata);
2636153a5fc4SStanislaw Gruszka if (sdata && ieee80211_sdata_running(sdata))
2637d8675a63SJohannes Berg ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
26387df180f7SZhao, Gang }
2639fe5f2559SJohannes Berg
2640f2753ddbSJohannes Berg /* reconfigure hardware */
2641f2753ddbSJohannes Berg ieee80211_hw_config(local, ~0);
2642f2753ddbSJohannes Berg
2643f2753ddbSJohannes Berg ieee80211_configure_filter(local);
2644f2753ddbSJohannes Berg
2645f2753ddbSJohannes Berg /* Finally also reconfigure all the BSS information */
2646f2753ddbSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) {
2647acb8bca3SJohannes Berg /* common change flags for all interface types - link only */
26482a5325f8SMukesh Sisodiya u64 changed = BSS_CHANGED_ERP_CTS_PROT |
2649acb8bca3SJohannes Berg BSS_CHANGED_ERP_PREAMBLE |
2650acb8bca3SJohannes Berg BSS_CHANGED_ERP_SLOT |
2651acb8bca3SJohannes Berg BSS_CHANGED_HT |
2652acb8bca3SJohannes Berg BSS_CHANGED_BASIC_RATES |
2653acb8bca3SJohannes Berg BSS_CHANGED_BEACON_INT |
2654acb8bca3SJohannes Berg BSS_CHANGED_BSSID |
2655acb8bca3SJohannes Berg BSS_CHANGED_CQM |
2656acb8bca3SJohannes Berg BSS_CHANGED_QOS |
2657acb8bca3SJohannes Berg BSS_CHANGED_TXPOWER |
2658acb8bca3SJohannes Berg BSS_CHANGED_MCAST_RATE;
2659acb8bca3SJohannes Berg struct ieee80211_link_data *link = NULL;
2660d8675a63SJohannes Berg unsigned int link_id;
2661acb8bca3SJohannes Berg u32 active_links = 0;
2662ac8dd506SJohannes Berg
26639607e6b6SJohannes Berg if (!ieee80211_sdata_running(sdata))
2664f2753ddbSJohannes Berg continue;
2665ac8dd506SJohannes Berg
2666d8675a63SJohannes Berg sdata_lock(sdata);
2667f1871abdSIlan Peer if (ieee80211_vif_is_mld(&sdata->vif)) {
2668acb8bca3SJohannes Berg struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS] = {
2669acb8bca3SJohannes Berg [0] = &sdata->vif.bss_conf,
2670acb8bca3SJohannes Berg };
2671acb8bca3SJohannes Berg
2672acb8bca3SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION) {
2673acb8bca3SJohannes Berg /* start with a single active link */
2674acb8bca3SJohannes Berg active_links = sdata->vif.active_links;
2675acb8bca3SJohannes Berg link_id = ffs(active_links) - 1;
2676acb8bca3SJohannes Berg sdata->vif.active_links = BIT(link_id);
2677acb8bca3SJohannes Berg }
2678acb8bca3SJohannes Berg
2679acb8bca3SJohannes Berg drv_change_vif_links(local, sdata, 0,
2680acb8bca3SJohannes Berg sdata->vif.active_links,
2681acb8bca3SJohannes Berg old);
2682acb8bca3SJohannes Berg }
2683acb8bca3SJohannes Berg
2684d8675a63SJohannes Berg for (link_id = 0;
2685d8675a63SJohannes Berg link_id < ARRAY_SIZE(sdata->vif.link_conf);
2686d8675a63SJohannes Berg link_id++) {
2687f1871abdSIlan Peer if (ieee80211_vif_is_mld(&sdata->vif) &&
2688acb8bca3SJohannes Berg !(sdata->vif.active_links & BIT(link_id)))
2689acb8bca3SJohannes Berg continue;
2690d8675a63SJohannes Berg
2691d8675a63SJohannes Berg link = sdata_dereference(sdata->link[link_id], sdata);
2692acb8bca3SJohannes Berg if (!link)
2693acb8bca3SJohannes Berg continue;
2694acb8bca3SJohannes Berg
2695b4f85443SJohannes Berg ieee80211_assign_chanctx(local, sdata, link);
2696b4f85443SJohannes Berg }
26971ea2c864SJohannes Berg
26981ea2c864SJohannes Berg switch (sdata->vif.type) {
26991ea2c864SJohannes Berg case NL80211_IFTYPE_AP_VLAN:
27001ea2c864SJohannes Berg case NL80211_IFTYPE_MONITOR:
27011ea2c864SJohannes Berg break;
27024926b51bSJohannes Berg case NL80211_IFTYPE_ADHOC:
2703f276e20bSJohannes Berg if (sdata->vif.cfg.ibss_joined)
27044926b51bSJohannes Berg WARN_ON(drv_join_ibss(local, sdata));
2705fc0561dcSGustavo A. R. Silva fallthrough;
27061ea2c864SJohannes Berg default:
27071ea2c864SJohannes Berg ieee80211_reconfig_stations(sdata);
2708fc0561dcSGustavo A. R. Silva fallthrough;
27091ea2c864SJohannes Berg case NL80211_IFTYPE_AP: /* AP stations are handled later */
27101ea2c864SJohannes Berg for (i = 0; i < IEEE80211_NUM_ACS; i++)
2711b3e2130bSJohannes Berg drv_conf_tx(local, &sdata->deflink, i,
2712b3e2130bSJohannes Berg &sdata->deflink.tx_conf[i]);
27131ea2c864SJohannes Berg break;
27141ea2c864SJohannes Berg }
2715ac8dd506SJohannes Berg
2716d0a9123eSJohannes Berg if (sdata->vif.bss_conf.mu_mimo_owner)
271723a1f8d4SSara Sharon changed |= BSS_CHANGED_MU_GROUPS;
271823a1f8d4SSara Sharon
2719f1871abdSIlan Peer if (!ieee80211_vif_is_mld(&sdata->vif))
2720acb8bca3SJohannes Berg changed |= BSS_CHANGED_IDLE;
2721acb8bca3SJohannes Berg
2722f2753ddbSJohannes Berg switch (sdata->vif.type) {
2723f2753ddbSJohannes Berg case NL80211_IFTYPE_STATION:
2724f1871abdSIlan Peer if (!ieee80211_vif_is_mld(&sdata->vif)) {
27250d392e93SEliad Peller changed |= BSS_CHANGED_ASSOC |
2726ab095877SEliad Peller BSS_CHANGED_ARP_FILTER |
2727ab095877SEliad Peller BSS_CHANGED_PS;
2728c65dd147SEmmanuel Grumbach
2729989c6505SAlexander Bondar /* Re-send beacon info report to the driver */
2730bfd8403aSJohannes Berg if (sdata->deflink.u.mgd.have_beacon)
2731989c6505SAlexander Bondar changed |= BSS_CHANGED_BEACON_INFO;
2732c65dd147SEmmanuel Grumbach
2733e38a017bSAvraham Stern if (sdata->vif.bss_conf.max_idle_period ||
2734e38a017bSAvraham Stern sdata->vif.bss_conf.protected_keep_alive)
2735e38a017bSAvraham Stern changed |= BSS_CHANGED_KEEP_ALIVE;
2736e38a017bSAvraham Stern
2737acb8bca3SJohannes Berg if (sdata->vif.bss_conf.eht_puncturing)
2738acb8bca3SJohannes Berg changed |= BSS_CHANGED_EHT_PUNCTURING;
2739acb8bca3SJohannes Berg
2740acb8bca3SJohannes Berg ieee80211_bss_info_change_notify(sdata,
2741acb8bca3SJohannes Berg changed);
2742acb8bca3SJohannes Berg } else if (!WARN_ON(!link)) {
2743acb8bca3SJohannes Berg ieee80211_link_info_change_notify(sdata, link,
2744acb8bca3SJohannes Berg changed);
2745acb8bca3SJohannes Berg changed = BSS_CHANGED_ASSOC |
2746acb8bca3SJohannes Berg BSS_CHANGED_IDLE |
2747acb8bca3SJohannes Berg BSS_CHANGED_PS |
2748acb8bca3SJohannes Berg BSS_CHANGED_ARP_FILTER;
2749acb8bca3SJohannes Berg ieee80211_vif_cfg_change_notify(sdata, changed);
2750acb8bca3SJohannes Berg }
2751ac8dd506SJohannes Berg break;
27526e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB:
2753239281f8SRostislav Lisovy changed |= BSS_CHANGED_OCB;
2754239281f8SRostislav Lisovy ieee80211_bss_info_change_notify(sdata, changed);
27556e0bd6c3SRostislav Lisovy break;
2756f2753ddbSJohannes Berg case NL80211_IFTYPE_ADHOC:
2757ac8dd506SJohannes Berg changed |= BSS_CHANGED_IBSS;
2758fc0561dcSGustavo A. R. Silva fallthrough;
2759f2753ddbSJohannes Berg case NL80211_IFTYPE_AP:
276061403414SJohannes Berg changed |= BSS_CHANGED_P2P_PS;
276161403414SJohannes Berg
2762f1871abdSIlan Peer if (ieee80211_vif_is_mld(&sdata->vif))
276361403414SJohannes Berg ieee80211_vif_cfg_change_notify(sdata,
276461403414SJohannes Berg BSS_CHANGED_SSID);
276561403414SJohannes Berg else
276661403414SJohannes Berg changed |= BSS_CHANGED_SSID;
2767e7979ac7SArik Nemtsov
2768bc847970SPradeep Kumar Chitrapu if (sdata->vif.bss_conf.ftm_responder == 1 &&
2769bc847970SPradeep Kumar Chitrapu wiphy_ext_feature_isset(sdata->local->hw.wiphy,
2770bc847970SPradeep Kumar Chitrapu NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
2771bc847970SPradeep Kumar Chitrapu changed |= BSS_CHANGED_FTM_RESPONDER;
2772bc847970SPradeep Kumar Chitrapu
27731041638fSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP) {
2774e7979ac7SArik Nemtsov changed |= BSS_CHANGED_AP_PROBE_RESP;
2775e7979ac7SArik Nemtsov
2776f1871abdSIlan Peer if (ieee80211_vif_is_mld(&sdata->vif)) {
277761403414SJohannes Berg ieee80211_reconfig_ap_links(local,
277861403414SJohannes Berg sdata,
277961403414SJohannes Berg changed);
278061403414SJohannes Berg break;
278161403414SJohannes Berg }
278261403414SJohannes Berg
2783bfd8403aSJohannes Berg if (rcu_access_pointer(sdata->deflink.u.ap.beacon))
2784b327c84cSGregory Greenman drv_start_ap(local, sdata,
2785b327c84cSGregory Greenman sdata->deflink.conf);
27861041638fSJohannes Berg }
2787fc0561dcSGustavo A. R. Silva fallthrough;
2788f2753ddbSJohannes Berg case NL80211_IFTYPE_MESH_POINT:
27898da34932SJohannes Berg if (sdata->vif.bss_conf.enable_beacon) {
2790ac8dd506SJohannes Berg changed |= BSS_CHANGED_BEACON |
2791ac8dd506SJohannes Berg BSS_CHANGED_BEACON_ENABLED;
27922d0ddec5SJohannes Berg ieee80211_bss_info_change_notify(sdata, changed);
27938da34932SJohannes Berg }
2794f2753ddbSJohannes Berg break;
2795167e33f4SAyala Beker case NL80211_IFTYPE_NAN:
2796167e33f4SAyala Beker res = ieee80211_reconfig_nan(sdata);
2797167e33f4SAyala Beker if (res < 0) {
2798acb8bca3SJohannes Berg sdata_unlock(sdata);
2799167e33f4SAyala Beker ieee80211_handle_reconfig_failure(local);
2800167e33f4SAyala Beker return res;
2801167e33f4SAyala Beker }
2802167e33f4SAyala Beker break;
2803f2753ddbSJohannes Berg case NL80211_IFTYPE_AP_VLAN:
2804f2753ddbSJohannes Berg case NL80211_IFTYPE_MONITOR:
280598104fdeSJohannes Berg case NL80211_IFTYPE_P2P_DEVICE:
2806b205786eSZhao, Gang /* nothing to do */
2807f142c6b9SJohannes Berg break;
2808f2753ddbSJohannes Berg case NL80211_IFTYPE_UNSPECIFIED:
28092e161f78SJohannes Berg case NUM_NL80211_IFTYPES:
28102ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_CLIENT:
28112ca27bcfSJohannes Berg case NL80211_IFTYPE_P2P_GO:
281270d9c599SJohannes Berg case NL80211_IFTYPE_WDS:
2813f2753ddbSJohannes Berg WARN_ON(1);
2814f2753ddbSJohannes Berg break;
2815f2753ddbSJohannes Berg }
2816acb8bca3SJohannes Berg sdata_unlock(sdata);
2817acb8bca3SJohannes Berg
2818acb8bca3SJohannes Berg if (active_links)
2819acb8bca3SJohannes Berg ieee80211_set_active_links(&sdata->vif, active_links);
2820f2753ddbSJohannes Berg }
2821f2753ddbSJohannes Berg
28224a733ef1SJohannes Berg ieee80211_recalc_ps(local);
28238e1b23b9SEyal Shapira
28242a419056SJohannes Berg /*
28256e1b1b24SEliad Peller * The sta might be in psm against the ap (e.g. because
28266e1b1b24SEliad Peller * this was the state before a hw restart), so we
28276e1b1b24SEliad Peller * explicitly send a null packet in order to make sure
28286e1b1b24SEliad Peller * it'll sync against the ap (and get out of psm).
28296e1b1b24SEliad Peller */
28306e1b1b24SEliad Peller if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
28316e1b1b24SEliad Peller list_for_each_entry(sdata, &local->interfaces, list) {
28326e1b1b24SEliad Peller if (sdata->vif.type != NL80211_IFTYPE_STATION)
28336e1b1b24SEliad Peller continue;
283420f544eeSJohannes Berg if (!sdata->u.mgd.associated)
283520f544eeSJohannes Berg continue;
28366e1b1b24SEliad Peller
2837076cdcb1SJohannes Berg ieee80211_send_nullfunc(local, sdata, false);
28386e1b1b24SEliad Peller }
28396e1b1b24SEliad Peller }
28406e1b1b24SEliad Peller
28412e8d397eSArik Nemtsov /* APs are now beaconing, add back stations */
284248c5d82aSJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) {
284348c5d82aSJohannes Berg if (!ieee80211_sdata_running(sdata))
28442e8d397eSArik Nemtsov continue;
28452e8d397eSArik Nemtsov
284648c5d82aSJohannes Berg sdata_lock(sdata);
284748c5d82aSJohannes Berg switch (sdata->vif.type) {
284848c5d82aSJohannes Berg case NL80211_IFTYPE_AP_VLAN:
284948c5d82aSJohannes Berg case NL80211_IFTYPE_AP:
285048c5d82aSJohannes Berg ieee80211_reconfig_stations(sdata);
285148c5d82aSJohannes Berg break;
285248c5d82aSJohannes Berg default:
285348c5d82aSJohannes Berg break;
28542e8d397eSArik Nemtsov }
285548c5d82aSJohannes Berg sdata_unlock(sdata);
285648c5d82aSJohannes Berg }
28572e8d397eSArik Nemtsov
28587b21aea0SEyal Shapira /* add back keys */
28597b21aea0SEyal Shapira list_for_each_entry(sdata, &local->interfaces, list)
2860624ff4b2SLior Cohen ieee80211_reenable_keys(sdata);
28617b21aea0SEyal Shapira
28620d440ea2SEliad Peller /* Reconfigure sched scan if it was interrupted by FW restart */
28630d440ea2SEliad Peller mutex_lock(&local->mtx);
28640d440ea2SEliad Peller sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
28650d440ea2SEliad Peller lockdep_is_held(&local->mtx));
28660d440ea2SEliad Peller sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
28670d440ea2SEliad Peller lockdep_is_held(&local->mtx));
28680d440ea2SEliad Peller if (sched_scan_sdata && sched_scan_req)
28690d440ea2SEliad Peller /*
28700d440ea2SEliad Peller * Sched scan stopped, but we don't want to report it. Instead,
28710d440ea2SEliad Peller * we're trying to reschedule. However, if more than one scan
28720d440ea2SEliad Peller * plan was set, we cannot reschedule since we don't know which
28730d440ea2SEliad Peller * scan plan was currently running (and some scan plans may have
28740d440ea2SEliad Peller * already finished).
28750d440ea2SEliad Peller */
28760d440ea2SEliad Peller if (sched_scan_req->n_scan_plans > 1 ||
28770d440ea2SEliad Peller __ieee80211_request_sched_scan_start(sched_scan_sdata,
2878b9f628fcSEliad Peller sched_scan_req)) {
2879b9f628fcSEliad Peller RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
2880b9f628fcSEliad Peller RCU_INIT_POINTER(local->sched_scan_req, NULL);
28810d440ea2SEliad Peller sched_scan_stopped = true;
2882b9f628fcSEliad Peller }
28830d440ea2SEliad Peller mutex_unlock(&local->mtx);
28840d440ea2SEliad Peller
28850d440ea2SEliad Peller if (sched_scan_stopped)
2886a05829a7SJohannes Berg cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
28870d440ea2SEliad Peller
2888c6209488SEliad Peller wake_up:
2889470f4d61SEliad Peller
28903c3e21e7SJohannes Berg if (local->monitors == local->open_count && local->monitors > 0)
28913c3e21e7SJohannes Berg ieee80211_add_virtual_monitor(local);
28923c3e21e7SJohannes Berg
28936e1b1b24SEliad Peller /*
28942a419056SJohannes Berg * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
28952a419056SJohannes Berg * sessions can be established after a resume.
28962a419056SJohannes Berg *
28972a419056SJohannes Berg * Also tear down aggregation sessions since reconfiguring
28982a419056SJohannes Berg * them in a hardware restart scenario is not easily done
28992a419056SJohannes Berg * right now, and the hardware will have lost information
29002a419056SJohannes Berg * about the sessions, but we and the AP still think they
29012a419056SJohannes Berg * are active. This is really a workaround though.
29022a419056SJohannes Berg */
290330686bf7SJohannes Berg if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
29042a419056SJohannes Berg mutex_lock(&local->sta_mtx);
29052a419056SJohannes Berg
29062a419056SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) {
290727392719SEliad Peller if (!local->resuming)
2908c82c4a80SJohannes Berg ieee80211_sta_tear_down_BA_sessions(
2909c82c4a80SJohannes Berg sta, AGG_STOP_LOCAL_REQUEST);
2910c2c98fdeSJohannes Berg clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
291174e2bd1fSWey-Yi Guy }
29122a419056SJohannes Berg
29132a419056SJohannes Berg mutex_unlock(&local->sta_mtx);
291474e2bd1fSWey-Yi Guy }
291574e2bd1fSWey-Yi Guy
291613dee10bSJohannes Berg /*
291713dee10bSJohannes Berg * If this is for hw restart things are still running.
291813dee10bSJohannes Berg * We may want to change that later, however.
291913dee10bSJohannes Berg */
292013dee10bSJohannes Berg if (local->open_count && (!suspended || reconfig_due_to_wowlan))
292113dee10bSJohannes Berg drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
292213dee10bSJohannes Berg
29232316380fSSara Sharon if (local->in_reconfig) {
29247d352ccfSYoughandhar Chintala in_reconfig = local->in_reconfig;
29252316380fSSara Sharon local->in_reconfig = false;
29262316380fSSara Sharon barrier();
29272316380fSSara Sharon
29282316380fSSara Sharon /* Restart deferred ROCs */
29292316380fSSara Sharon mutex_lock(&local->mtx);
29302316380fSSara Sharon ieee80211_start_next_roc(local);
29312316380fSSara Sharon mutex_unlock(&local->mtx);
2932f8891461SNaftali Goldstein
2933f8891461SNaftali Goldstein /* Requeue all works */
2934f8891461SNaftali Goldstein list_for_each_entry(sdata, &local->interfaces, list)
293516114496SJohannes Berg wiphy_work_queue(local->hw.wiphy, &sdata->work);
29362316380fSSara Sharon }
29372316380fSSara Sharon
2938445ea4e8SJohannes Berg ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
2939cca07b00SLuciano Coelho IEEE80211_QUEUE_STOP_REASON_SUSPEND,
2940cca07b00SLuciano Coelho false);
2941f2753ddbSJohannes Berg
29427d352ccfSYoughandhar Chintala if (in_reconfig) {
29437d352ccfSYoughandhar Chintala list_for_each_entry(sdata, &local->interfaces, list) {
29447d352ccfSYoughandhar Chintala if (!ieee80211_sdata_running(sdata))
29457d352ccfSYoughandhar Chintala continue;
29467d352ccfSYoughandhar Chintala if (sdata->vif.type == NL80211_IFTYPE_STATION)
29477d352ccfSYoughandhar Chintala ieee80211_sta_restart(sdata);
29487d352ccfSYoughandhar Chintala }
29497d352ccfSYoughandhar Chintala }
29507d352ccfSYoughandhar Chintala
2951b0485e9fSEliad Peller if (!suspended)
29525bb644a0SJohannes Berg return 0;
29535bb644a0SJohannes Berg
29545bb644a0SJohannes Berg #ifdef CONFIG_PM
2955ceb99fe0SJohannes Berg /* first set suspended false, then resuming */
29565bb644a0SJohannes Berg local->suspended = false;
2957ceb99fe0SJohannes Berg mb();
2958ceb99fe0SJohannes Berg local->resuming = false;
29595bb644a0SJohannes Berg
296074430f94SJohannes Berg ieee80211_flush_completed_scan(local, false);
29619120d94eSLuciano Coelho
29620f8b8245SEliad Peller if (local->open_count && !reconfig_due_to_wowlan)
2963cf2c92d8SEliad Peller drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
2964cf2c92d8SEliad Peller
2965b8360ab8SJohannes Berg list_for_each_entry(sdata, &local->interfaces, list) {
2966b8360ab8SJohannes Berg if (!ieee80211_sdata_running(sdata))
2967b8360ab8SJohannes Berg continue;
2968b8360ab8SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION)
2969b8360ab8SJohannes Berg ieee80211_sta_restart(sdata);
2970b8360ab8SJohannes Berg }
2971b8360ab8SJohannes Berg
297226d59535SJohannes Berg mod_timer(&local->sta_cleanup, jiffies + 1);
29735bb644a0SJohannes Berg #else
29745bb644a0SJohannes Berg WARN_ON(1);
29755bb644a0SJohannes Berg #endif
2976d43c6b6eSDavid Spinadel
2977f2753ddbSJohannes Berg return 0;
2978f2753ddbSJohannes Berg }
297942935ecaSLuis R. Rodriguez
ieee80211_reconfig_disconnect(struct ieee80211_vif * vif,u8 flag)29807d352ccfSYoughandhar Chintala static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag)
298195acac61SJohannes Berg {
298295acac61SJohannes Berg struct ieee80211_sub_if_data *sdata;
298395acac61SJohannes Berg struct ieee80211_local *local;
298495acac61SJohannes Berg struct ieee80211_key *key;
298595acac61SJohannes Berg
298695acac61SJohannes Berg if (WARN_ON(!vif))
298795acac61SJohannes Berg return;
298895acac61SJohannes Berg
298995acac61SJohannes Berg sdata = vif_to_sdata(vif);
299095acac61SJohannes Berg local = sdata->local;
299195acac61SJohannes Berg
29927d352ccfSYoughandhar Chintala if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME &&
29937d352ccfSYoughandhar Chintala !local->resuming))
29947d352ccfSYoughandhar Chintala return;
29957d352ccfSYoughandhar Chintala
29967d352ccfSYoughandhar Chintala if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_HW_RESTART &&
29977d352ccfSYoughandhar Chintala !local->in_reconfig))
299895acac61SJohannes Berg return;
299995acac61SJohannes Berg
300095acac61SJohannes Berg if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
300195acac61SJohannes Berg return;
300295acac61SJohannes Berg
30037d352ccfSYoughandhar Chintala sdata->flags |= flag;
300495acac61SJohannes Berg
300595acac61SJohannes Berg mutex_lock(&local->key_mtx);
300695acac61SJohannes Berg list_for_each_entry(key, &sdata->key_list, list)
300795acac61SJohannes Berg key->flags |= KEY_FLAG_TAINTED;
300895acac61SJohannes Berg mutex_unlock(&local->key_mtx);
300995acac61SJohannes Berg }
30107d352ccfSYoughandhar Chintala
ieee80211_hw_restart_disconnect(struct ieee80211_vif * vif)30117d352ccfSYoughandhar Chintala void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif)
30127d352ccfSYoughandhar Chintala {
30137d352ccfSYoughandhar Chintala ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_HW_RESTART);
30147d352ccfSYoughandhar Chintala }
30157d352ccfSYoughandhar Chintala EXPORT_SYMBOL_GPL(ieee80211_hw_restart_disconnect);
30167d352ccfSYoughandhar Chintala
ieee80211_resume_disconnect(struct ieee80211_vif * vif)30177d352ccfSYoughandhar Chintala void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
30187d352ccfSYoughandhar Chintala {
30197d352ccfSYoughandhar Chintala ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_RESUME);
30207d352ccfSYoughandhar Chintala }
302195acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
302295acac61SJohannes Berg
ieee80211_recalc_smps(struct ieee80211_sub_if_data * sdata,struct ieee80211_link_data * link)3023e9aac179SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata,
3024d8675a63SJohannes Berg struct ieee80211_link_data *link)
30250f78231bSJohannes Berg {
302604ecd257SJohannes Berg struct ieee80211_local *local = sdata->local;
302704ecd257SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf;
302804ecd257SJohannes Berg struct ieee80211_chanctx *chanctx;
30290f78231bSJohannes Berg
303004ecd257SJohannes Berg mutex_lock(&local->chanctx_mtx);
30310f78231bSJohannes Berg
3032d8675a63SJohannes Berg chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf,
303304ecd257SJohannes Berg lockdep_is_held(&local->chanctx_mtx));
30340f78231bSJohannes Berg
3035c189a685SAndrei Otcheretianski /*
3036c189a685SAndrei Otcheretianski * This function can be called from a work, thus it may be possible
3037c189a685SAndrei Otcheretianski * that the chanctx_conf is removed (due to a disconnection, for
3038c189a685SAndrei Otcheretianski * example).
3039c189a685SAndrei Otcheretianski * So nothing should be done in such case.
3040c189a685SAndrei Otcheretianski */
3041c189a685SAndrei Otcheretianski if (!chanctx_conf)
30425d8e4237SJohannes Berg goto unlock;
30430f78231bSJohannes Berg
304404ecd257SJohannes Berg chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
304504ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, chanctx);
30465d8e4237SJohannes Berg unlock:
304704ecd257SJohannes Berg mutex_unlock(&local->chanctx_mtx);
30480f78231bSJohannes Berg }
30498e664fb3SJohannes Berg
ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data * sdata,int link_id)30500cbf348aSAndrei Otcheretianski void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata,
30510cbf348aSAndrei Otcheretianski int link_id)
305221f659bfSEliad Peller {
305321f659bfSEliad Peller struct ieee80211_local *local = sdata->local;
305421f659bfSEliad Peller struct ieee80211_chanctx_conf *chanctx_conf;
305521f659bfSEliad Peller struct ieee80211_chanctx *chanctx;
30560cbf348aSAndrei Otcheretianski int i;
305721f659bfSEliad Peller
305821f659bfSEliad Peller mutex_lock(&local->chanctx_mtx);
305921f659bfSEliad Peller
30600cbf348aSAndrei Otcheretianski for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) {
30610cbf348aSAndrei Otcheretianski struct ieee80211_bss_conf *bss_conf;
30620cbf348aSAndrei Otcheretianski
30630cbf348aSAndrei Otcheretianski if (link_id >= 0 && link_id != i)
30640cbf348aSAndrei Otcheretianski continue;
30650cbf348aSAndrei Otcheretianski
30660cbf348aSAndrei Otcheretianski rcu_read_lock();
30670cbf348aSAndrei Otcheretianski bss_conf = rcu_dereference(sdata->vif.link_conf[i]);
30680cbf348aSAndrei Otcheretianski if (!bss_conf) {
30690cbf348aSAndrei Otcheretianski rcu_read_unlock();
30700cbf348aSAndrei Otcheretianski continue;
30710cbf348aSAndrei Otcheretianski }
30720cbf348aSAndrei Otcheretianski
30730cbf348aSAndrei Otcheretianski chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf,
307421f659bfSEliad Peller lockdep_is_held(&local->chanctx_mtx));
30750cbf348aSAndrei Otcheretianski /*
30760cbf348aSAndrei Otcheretianski * Since we hold the chanctx_mtx (checked above)
30770cbf348aSAndrei Otcheretianski * we can take the chanctx_conf pointer out of the
30780cbf348aSAndrei Otcheretianski * RCU critical section, it cannot go away without
30790cbf348aSAndrei Otcheretianski * the mutex. Just the way we reached it could - in
30800cbf348aSAndrei Otcheretianski * theory - go away, but we don't really care and
30810cbf348aSAndrei Otcheretianski * it really shouldn't happen anyway.
30820cbf348aSAndrei Otcheretianski */
30830cbf348aSAndrei Otcheretianski rcu_read_unlock();
308421f659bfSEliad Peller
3085efe9c2bfSJohannes Berg if (!chanctx_conf)
308621f659bfSEliad Peller goto unlock;
308721f659bfSEliad Peller
30880cbf348aSAndrei Otcheretianski chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
30890cbf348aSAndrei Otcheretianski conf);
3090b72a455aSJohannes Berg ieee80211_recalc_chanctx_min_def(local, chanctx, NULL);
30910cbf348aSAndrei Otcheretianski }
309221f659bfSEliad Peller unlock:
309321f659bfSEliad Peller mutex_unlock(&local->chanctx_mtx);
309421f659bfSEliad Peller }
309521f659bfSEliad Peller
ieee80211_ie_split_vendor(const u8 * ies,size_t ielen,size_t offset)30968e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
30978e664fb3SJohannes Berg {
30988e664fb3SJohannes Berg size_t pos = offset;
30998e664fb3SJohannes Berg
31008e664fb3SJohannes Berg while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
31018e664fb3SJohannes Berg pos += 2 + ies[pos + 1];
31028e664fb3SJohannes Berg
31038e664fb3SJohannes Berg return pos;
31048e664fb3SJohannes Berg }
3105615f7b9bSMeenakshi Venkataraman
ieee80211_ie_build_s1g_cap(u8 * pos,struct ieee80211_sta_s1g_cap * s1g_cap)31060333a81bSKieran Frewen u8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap)
31070333a81bSKieran Frewen {
31080333a81bSKieran Frewen *pos++ = WLAN_EID_S1G_CAPABILITIES;
31090333a81bSKieran Frewen *pos++ = sizeof(struct ieee80211_s1g_cap);
31100333a81bSKieran Frewen memset(pos, 0, sizeof(struct ieee80211_s1g_cap));
31110333a81bSKieran Frewen
31120333a81bSKieran Frewen memcpy(pos, &s1g_cap->cap, sizeof(s1g_cap->cap));
31130333a81bSKieran Frewen pos += sizeof(s1g_cap->cap);
31140333a81bSKieran Frewen
31150333a81bSKieran Frewen memcpy(pos, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs));
31160333a81bSKieran Frewen pos += sizeof(s1g_cap->nss_mcs);
31170333a81bSKieran Frewen
31180333a81bSKieran Frewen return pos;
31190333a81bSKieran Frewen }
31200333a81bSKieran Frewen
ieee80211_ie_build_ht_cap(u8 * pos,struct ieee80211_sta_ht_cap * ht_cap,u16 cap)3121ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
312242e7aa77SAlexander Simon u16 cap)
312342e7aa77SAlexander Simon {
312442e7aa77SAlexander Simon __le16 tmp;
312542e7aa77SAlexander Simon
312642e7aa77SAlexander Simon *pos++ = WLAN_EID_HT_CAPABILITY;
312742e7aa77SAlexander Simon *pos++ = sizeof(struct ieee80211_ht_cap);
312842e7aa77SAlexander Simon memset(pos, 0, sizeof(struct ieee80211_ht_cap));
312942e7aa77SAlexander Simon
313042e7aa77SAlexander Simon /* capability flags */
313142e7aa77SAlexander Simon tmp = cpu_to_le16(cap);
313242e7aa77SAlexander Simon memcpy(pos, &tmp, sizeof(u16));
313342e7aa77SAlexander Simon pos += sizeof(u16);
313442e7aa77SAlexander Simon
313542e7aa77SAlexander Simon /* AMPDU parameters */
3136ef96a842SBen Greear *pos++ = ht_cap->ampdu_factor |
3137ef96a842SBen Greear (ht_cap->ampdu_density <<
313842e7aa77SAlexander Simon IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
313942e7aa77SAlexander Simon
314042e7aa77SAlexander Simon /* MCS set */
3141ef96a842SBen Greear memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
3142ef96a842SBen Greear pos += sizeof(ht_cap->mcs);
314342e7aa77SAlexander Simon
314442e7aa77SAlexander Simon /* extended capabilities */
314542e7aa77SAlexander Simon pos += sizeof(__le16);
314642e7aa77SAlexander Simon
314742e7aa77SAlexander Simon /* BF capabilities */
314842e7aa77SAlexander Simon pos += sizeof(__le32);
314942e7aa77SAlexander Simon
315042e7aa77SAlexander Simon /* antenna selection */
315142e7aa77SAlexander Simon pos += sizeof(u8);
315242e7aa77SAlexander Simon
315342e7aa77SAlexander Simon return pos;
315442e7aa77SAlexander Simon }
315542e7aa77SAlexander Simon
ieee80211_ie_build_vht_cap(u8 * pos,struct ieee80211_sta_vht_cap * vht_cap,u32 cap)3156ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
3157ba0afa2fSMahesh Palivela u32 cap)
3158ba0afa2fSMahesh Palivela {
3159ba0afa2fSMahesh Palivela __le32 tmp;
3160ba0afa2fSMahesh Palivela
3161ba0afa2fSMahesh Palivela *pos++ = WLAN_EID_VHT_CAPABILITY;
3162d4950281SMahesh Palivela *pos++ = sizeof(struct ieee80211_vht_cap);
3163d4950281SMahesh Palivela memset(pos, 0, sizeof(struct ieee80211_vht_cap));
3164ba0afa2fSMahesh Palivela
3165ba0afa2fSMahesh Palivela /* capability flags */
3166ba0afa2fSMahesh Palivela tmp = cpu_to_le32(cap);
3167ba0afa2fSMahesh Palivela memcpy(pos, &tmp, sizeof(u32));
3168ba0afa2fSMahesh Palivela pos += sizeof(u32);
3169ba0afa2fSMahesh Palivela
3170ba0afa2fSMahesh Palivela /* VHT MCS set */
3171ba0afa2fSMahesh Palivela memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
3172ba0afa2fSMahesh Palivela pos += sizeof(vht_cap->vht_mcs);
3173ba0afa2fSMahesh Palivela
3174ba0afa2fSMahesh Palivela return pos;
3175ba0afa2fSMahesh Palivela }
3176ba0afa2fSMahesh Palivela
ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data * sdata,u8 iftype)317760ad72daSSven Eckelmann u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
317860ad72daSSven Eckelmann {
317960ad72daSSven Eckelmann const struct ieee80211_sta_he_cap *he_cap;
318060ad72daSSven Eckelmann struct ieee80211_supported_band *sband;
318160ad72daSSven Eckelmann u8 n;
318260ad72daSSven Eckelmann
318360ad72daSSven Eckelmann sband = ieee80211_get_sband(sdata);
318460ad72daSSven Eckelmann if (!sband)
318560ad72daSSven Eckelmann return 0;
318660ad72daSSven Eckelmann
318760ad72daSSven Eckelmann he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
318860ad72daSSven Eckelmann if (!he_cap)
318960ad72daSSven Eckelmann return 0;
319060ad72daSSven Eckelmann
319160ad72daSSven Eckelmann n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
319260ad72daSSven Eckelmann return 2 + 1 +
319360ad72daSSven Eckelmann sizeof(he_cap->he_cap_elem) + n +
319460ad72daSSven Eckelmann ieee80211_he_ppe_size(he_cap->ppe_thres[0],
319560ad72daSSven Eckelmann he_cap->he_cap_elem.phy_cap_info);
319660ad72daSSven Eckelmann }
319760ad72daSSven Eckelmann
ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags,u8 * pos,const struct ieee80211_sta_he_cap * he_cap,u8 * end)3198ba323e29SJohannes Berg u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos,
319941cbb0f5SLuca Coelho const struct ieee80211_sta_he_cap *he_cap,
320041cbb0f5SLuca Coelho u8 *end)
320141cbb0f5SLuca Coelho {
32021f2c1044SJohannes Berg struct ieee80211_he_cap_elem elem;
320341cbb0f5SLuca Coelho u8 n;
320441cbb0f5SLuca Coelho u8 ie_len;
320541cbb0f5SLuca Coelho u8 *orig_pos = pos;
320641cbb0f5SLuca Coelho
320741cbb0f5SLuca Coelho /* Make sure we have place for the IE */
320841cbb0f5SLuca Coelho /*
320941cbb0f5SLuca Coelho * TODO: the 1 added is because this temporarily is under the EXTENSION
321041cbb0f5SLuca Coelho * IE. Get rid of it when it moves.
321141cbb0f5SLuca Coelho */
321241cbb0f5SLuca Coelho if (!he_cap)
321341cbb0f5SLuca Coelho return orig_pos;
321441cbb0f5SLuca Coelho
32151f2c1044SJohannes Berg /* modify on stack first to calculate 'n' and 'ie_len' correctly */
32161f2c1044SJohannes Berg elem = he_cap->he_cap_elem;
32171f2c1044SJohannes Berg
3218ba323e29SJohannes Berg if (disable_flags & IEEE80211_CONN_DISABLE_40MHZ)
32191f2c1044SJohannes Berg elem.phy_cap_info[0] &=
32201f2c1044SJohannes Berg ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
32211f2c1044SJohannes Berg IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G);
32221f2c1044SJohannes Berg
3223ba323e29SJohannes Berg if (disable_flags & IEEE80211_CONN_DISABLE_160MHZ)
32241f2c1044SJohannes Berg elem.phy_cap_info[0] &=
32251f2c1044SJohannes Berg ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
32261f2c1044SJohannes Berg
3227ba323e29SJohannes Berg if (disable_flags & IEEE80211_CONN_DISABLE_80P80MHZ)
32281f2c1044SJohannes Berg elem.phy_cap_info[0] &=
32291f2c1044SJohannes Berg ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
32301f2c1044SJohannes Berg
32311f2c1044SJohannes Berg n = ieee80211_he_mcs_nss_size(&elem);
323241cbb0f5SLuca Coelho ie_len = 2 + 1 +
323341cbb0f5SLuca Coelho sizeof(he_cap->he_cap_elem) + n +
323441cbb0f5SLuca Coelho ieee80211_he_ppe_size(he_cap->ppe_thres[0],
323541cbb0f5SLuca Coelho he_cap->he_cap_elem.phy_cap_info);
323641cbb0f5SLuca Coelho
323741cbb0f5SLuca Coelho if ((end - pos) < ie_len)
323841cbb0f5SLuca Coelho return orig_pos;
323941cbb0f5SLuca Coelho
324041cbb0f5SLuca Coelho *pos++ = WLAN_EID_EXTENSION;
324141cbb0f5SLuca Coelho pos++; /* We'll set the size later below */
324241cbb0f5SLuca Coelho *pos++ = WLAN_EID_EXT_HE_CAPABILITY;
324341cbb0f5SLuca Coelho
324441cbb0f5SLuca Coelho /* Fixed data */
32451f2c1044SJohannes Berg memcpy(pos, &elem, sizeof(elem));
32461f2c1044SJohannes Berg pos += sizeof(elem);
324741cbb0f5SLuca Coelho
324841cbb0f5SLuca Coelho memcpy(pos, &he_cap->he_mcs_nss_supp, n);
324941cbb0f5SLuca Coelho pos += n;
325041cbb0f5SLuca Coelho
325141cbb0f5SLuca Coelho /* Check if PPE Threshold should be present */
325241cbb0f5SLuca Coelho if ((he_cap->he_cap_elem.phy_cap_info[6] &
325341cbb0f5SLuca Coelho IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0)
325441cbb0f5SLuca Coelho goto end;
325541cbb0f5SLuca Coelho
325641cbb0f5SLuca Coelho /*
325741cbb0f5SLuca Coelho * Calculate how many PPET16/PPET8 pairs are to come. Algorithm:
325841cbb0f5SLuca Coelho * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK)
325941cbb0f5SLuca Coelho */
326041cbb0f5SLuca Coelho n = hweight8(he_cap->ppe_thres[0] &
326141cbb0f5SLuca Coelho IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
326241cbb0f5SLuca Coelho n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >>
326341cbb0f5SLuca Coelho IEEE80211_PPE_THRES_NSS_POS));
326441cbb0f5SLuca Coelho
326541cbb0f5SLuca Coelho /*
326641cbb0f5SLuca Coelho * Each pair is 6 bits, and we need to add the 7 "header" bits to the
326741cbb0f5SLuca Coelho * total size.
326841cbb0f5SLuca Coelho */
326941cbb0f5SLuca Coelho n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7;
327041cbb0f5SLuca Coelho n = DIV_ROUND_UP(n, 8);
327141cbb0f5SLuca Coelho
327241cbb0f5SLuca Coelho /* Copy PPE Thresholds */
327341cbb0f5SLuca Coelho memcpy(pos, &he_cap->ppe_thres, n);
327441cbb0f5SLuca Coelho pos += n;
327541cbb0f5SLuca Coelho
327641cbb0f5SLuca Coelho end:
327741cbb0f5SLuca Coelho orig_pos[1] = (pos - orig_pos) - 2;
327841cbb0f5SLuca Coelho return pos;
327941cbb0f5SLuca Coelho }
328041cbb0f5SLuca Coelho
ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data * sdata,enum ieee80211_smps_mode smps_mode,struct sk_buff * skb)328124a2042cSRajkumar Manoharan void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
3282abd27d06SJohannes Berg enum ieee80211_smps_mode smps_mode,
328324a2042cSRajkumar Manoharan struct sk_buff *skb)
328424a2042cSRajkumar Manoharan {
328524a2042cSRajkumar Manoharan struct ieee80211_supported_band *sband;
328624a2042cSRajkumar Manoharan const struct ieee80211_sband_iftype_data *iftd;
328724a2042cSRajkumar Manoharan enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
328824a2042cSRajkumar Manoharan u8 *pos;
328924a2042cSRajkumar Manoharan u16 cap;
329024a2042cSRajkumar Manoharan
329165be6aa3SJohannes Berg if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy,
329265be6aa3SJohannes Berg BIT(NL80211_BAND_6GHZ),
329365be6aa3SJohannes Berg IEEE80211_CHAN_NO_HE))
329424a2042cSRajkumar Manoharan return;
329524a2042cSRajkumar Manoharan
329665be6aa3SJohannes Berg sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ];
329765be6aa3SJohannes Berg
329824a2042cSRajkumar Manoharan iftd = ieee80211_get_sband_iftype_data(sband, iftype);
329965be6aa3SJohannes Berg if (!iftd)
330024a2042cSRajkumar Manoharan return;
330124a2042cSRajkumar Manoharan
330265ad3ef9SRajkumar Manoharan /* Check for device HE 6 GHz capability before adding element */
330365ad3ef9SRajkumar Manoharan if (!iftd->he_6ghz_capa.capa)
330465ad3ef9SRajkumar Manoharan return;
330565ad3ef9SRajkumar Manoharan
330624a2042cSRajkumar Manoharan cap = le16_to_cpu(iftd->he_6ghz_capa.capa);
330724a2042cSRajkumar Manoharan cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS;
330824a2042cSRajkumar Manoharan
3309abd27d06SJohannes Berg switch (smps_mode) {
331024a2042cSRajkumar Manoharan case IEEE80211_SMPS_AUTOMATIC:
331124a2042cSRajkumar Manoharan case IEEE80211_SMPS_NUM_MODES:
331224a2042cSRajkumar Manoharan WARN_ON(1);
3313fc0561dcSGustavo A. R. Silva fallthrough;
331424a2042cSRajkumar Manoharan case IEEE80211_SMPS_OFF:
331524a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED,
331624a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS);
331724a2042cSRajkumar Manoharan break;
331824a2042cSRajkumar Manoharan case IEEE80211_SMPS_STATIC:
331924a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC,
332024a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS);
332124a2042cSRajkumar Manoharan break;
332224a2042cSRajkumar Manoharan case IEEE80211_SMPS_DYNAMIC:
332324a2042cSRajkumar Manoharan cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC,
332424a2042cSRajkumar Manoharan IEEE80211_HE_6GHZ_CAP_SM_PS);
332524a2042cSRajkumar Manoharan break;
332624a2042cSRajkumar Manoharan }
332724a2042cSRajkumar Manoharan
332824a2042cSRajkumar Manoharan pos = skb_put(skb, 2 + 1 + sizeof(cap));
33292ad2274cSIlan Peer ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap),
33302ad2274cSIlan Peer pos + 2 + 1 + sizeof(cap));
333124a2042cSRajkumar Manoharan }
333224a2042cSRajkumar Manoharan
ieee80211_ie_build_ht_oper(u8 * pos,struct ieee80211_sta_ht_cap * ht_cap,const struct cfg80211_chan_def * chandef,u16 prot_mode,bool rifs_mode)3333074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
33344bf88530SJohannes Berg const struct cfg80211_chan_def *chandef,
333557f255f5SArik Nemtsov u16 prot_mode, bool rifs_mode)
333642e7aa77SAlexander Simon {
3337074d46d1SJohannes Berg struct ieee80211_ht_operation *ht_oper;
333842e7aa77SAlexander Simon /* Build HT Information */
3339074d46d1SJohannes Berg *pos++ = WLAN_EID_HT_OPERATION;
3340074d46d1SJohannes Berg *pos++ = sizeof(struct ieee80211_ht_operation);
3341074d46d1SJohannes Berg ht_oper = (struct ieee80211_ht_operation *)pos;
33424bf88530SJohannes Berg ht_oper->primary_chan = ieee80211_frequency_to_channel(
33434bf88530SJohannes Berg chandef->chan->center_freq);
33444bf88530SJohannes Berg switch (chandef->width) {
33454bf88530SJohannes Berg case NL80211_CHAN_WIDTH_160:
33464bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80P80:
33474bf88530SJohannes Berg case NL80211_CHAN_WIDTH_80:
33484bf88530SJohannes Berg case NL80211_CHAN_WIDTH_40:
33494bf88530SJohannes Berg if (chandef->center_freq1 > chandef->chan->center_freq)
33504bf88530SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
33514bf88530SJohannes Berg else
3352074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
335342e7aa77SAlexander Simon break;
33545dca295dSIlan Peer case NL80211_CHAN_WIDTH_320:
33555dca295dSIlan Peer /* HT information element should not be included on 6GHz */
33565dca295dSIlan Peer WARN_ON(1);
33575dca295dSIlan Peer return pos;
335842e7aa77SAlexander Simon default:
3359074d46d1SJohannes Berg ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
336042e7aa77SAlexander Simon break;
336142e7aa77SAlexander Simon }
3362aee286c2SThomas Pedersen if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
33634bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
33644bf88530SJohannes Berg chandef->width != NL80211_CHAN_WIDTH_20)
3365074d46d1SJohannes Berg ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
3366ff3cc5f4SSimon Wunderlich
336757f255f5SArik Nemtsov if (rifs_mode)
336857f255f5SArik Nemtsov ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE;
336957f255f5SArik Nemtsov
3370431e3154SAshok Nagarajan ht_oper->operation_mode = cpu_to_le16(prot_mode);
3371074d46d1SJohannes Berg ht_oper->stbc_param = 0x0000;
337242e7aa77SAlexander Simon
337342e7aa77SAlexander Simon /* It seems that Basic MCS set and Supported MCS set
337442e7aa77SAlexander Simon are identical for the first 10 bytes */
3375074d46d1SJohannes Berg memset(&ht_oper->basic_set, 0, 16);
3376074d46d1SJohannes Berg memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
337742e7aa77SAlexander Simon
3378074d46d1SJohannes Berg return pos + sizeof(struct ieee80211_ht_operation);
337942e7aa77SAlexander Simon }
338042e7aa77SAlexander Simon
ieee80211_ie_build_wide_bw_cs(u8 * pos,const struct cfg80211_chan_def * chandef)338175d627d5SSimon Wunderlich void ieee80211_ie_build_wide_bw_cs(u8 *pos,
338275d627d5SSimon Wunderlich const struct cfg80211_chan_def *chandef)
338375d627d5SSimon Wunderlich {
338475d627d5SSimon Wunderlich *pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH; /* EID */
338575d627d5SSimon Wunderlich *pos++ = 3; /* IE length */
338675d627d5SSimon Wunderlich /* New channel width */
338775d627d5SSimon Wunderlich switch (chandef->width) {
338875d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_80:
338975d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ;
339075d627d5SSimon Wunderlich break;
339175d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_160:
339275d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ;
339375d627d5SSimon Wunderlich break;
339475d627d5SSimon Wunderlich case NL80211_CHAN_WIDTH_80P80:
339575d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
339675d627d5SSimon Wunderlich break;
33975dca295dSIlan Peer case NL80211_CHAN_WIDTH_320:
33985dca295dSIlan Peer /* The behavior is not defined for 320 MHz channels */
33995dca295dSIlan Peer WARN_ON(1);
34005dca295dSIlan Peer fallthrough;
340175d627d5SSimon Wunderlich default:
340275d627d5SSimon Wunderlich *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT;
340375d627d5SSimon Wunderlich }
340475d627d5SSimon Wunderlich
340575d627d5SSimon Wunderlich /* new center frequency segment 0 */
340675d627d5SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel(chandef->center_freq1);
340775d627d5SSimon Wunderlich /* new center frequency segment 1 */
340875d627d5SSimon Wunderlich if (chandef->center_freq2)
340975d627d5SSimon Wunderlich *pos++ = ieee80211_frequency_to_channel(chandef->center_freq2);
341075d627d5SSimon Wunderlich else
341175d627d5SSimon Wunderlich *pos++ = 0;
341275d627d5SSimon Wunderlich }
341375d627d5SSimon Wunderlich
ieee80211_ie_build_vht_oper(u8 * pos,struct ieee80211_sta_vht_cap * vht_cap,const struct cfg80211_chan_def * chandef)3414fb28ec0cSArik Nemtsov u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
3415fb28ec0cSArik Nemtsov const struct cfg80211_chan_def *chandef)
3416fb28ec0cSArik Nemtsov {
3417fb28ec0cSArik Nemtsov struct ieee80211_vht_operation *vht_oper;
3418fb28ec0cSArik Nemtsov
3419fb28ec0cSArik Nemtsov *pos++ = WLAN_EID_VHT_OPERATION;
3420fb28ec0cSArik Nemtsov *pos++ = sizeof(struct ieee80211_vht_operation);
3421fb28ec0cSArik Nemtsov vht_oper = (struct ieee80211_vht_operation *)pos;
34222fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel(
3423fb28ec0cSArik Nemtsov chandef->center_freq1);
3424fb28ec0cSArik Nemtsov if (chandef->center_freq2)
34252fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx =
3426fb28ec0cSArik Nemtsov ieee80211_frequency_to_channel(chandef->center_freq2);
3427f020ae40SChun-Yeow Yeoh else
34282fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx = 0x00;
3429fb28ec0cSArik Nemtsov
3430fb28ec0cSArik Nemtsov switch (chandef->width) {
3431fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_160:
343223665aafSJouni Malinen /*
343323665aafSJouni Malinen * Convert 160 MHz channel width to new style as interop
343423665aafSJouni Malinen * workaround.
343523665aafSJouni Malinen */
343623665aafSJouni Malinen vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
34372fb51c35SJohannes Berg vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx;
343823665aafSJouni Malinen if (chandef->chan->center_freq < chandef->center_freq1)
34392fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx -= 8;
344023665aafSJouni Malinen else
34412fb51c35SJohannes Berg vht_oper->center_freq_seg0_idx += 8;
3442fb28ec0cSArik Nemtsov break;
3443fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_80P80:
344423665aafSJouni Malinen /*
344523665aafSJouni Malinen * Convert 80+80 MHz channel width to new style as interop
344623665aafSJouni Malinen * workaround.
344723665aafSJouni Malinen */
344823665aafSJouni Malinen vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
3449fb28ec0cSArik Nemtsov break;
3450fb28ec0cSArik Nemtsov case NL80211_CHAN_WIDTH_80:
3451fb28ec0cSArik Nemtsov vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
3452fb28ec0cSArik Nemtsov break;
34535dca295dSIlan Peer case NL80211_CHAN_WIDTH_320:
34545dca295dSIlan Peer /* VHT information element should not be included on 6GHz */
34555dca295dSIlan Peer WARN_ON(1);
34565dca295dSIlan Peer return pos;
3457fb28ec0cSArik Nemtsov default:
3458fb28ec0cSArik Nemtsov vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
3459fb28ec0cSArik Nemtsov break;
3460fb28ec0cSArik Nemtsov }
3461fb28ec0cSArik Nemtsov
3462fb28ec0cSArik Nemtsov /* don't require special VHT peer rates */
3463fb28ec0cSArik Nemtsov vht_oper->basic_mcs_set = cpu_to_le16(0xffff);
3464fb28ec0cSArik Nemtsov
3465fb28ec0cSArik Nemtsov return pos + sizeof(struct ieee80211_vht_operation);
3466fb28ec0cSArik Nemtsov }
3467fb28ec0cSArik Nemtsov
ieee80211_ie_build_he_oper(u8 * pos,struct cfg80211_chan_def * chandef)3468d1b7524bSRajkumar Manoharan u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef)
346960ad72daSSven Eckelmann {
347060ad72daSSven Eckelmann struct ieee80211_he_operation *he_oper;
3471d1b7524bSRajkumar Manoharan struct ieee80211_he_6ghz_oper *he_6ghz_op;
347260ad72daSSven Eckelmann u32 he_oper_params;
3473d1b7524bSRajkumar Manoharan u8 ie_len = 1 + sizeof(struct ieee80211_he_operation);
3474d1b7524bSRajkumar Manoharan
3475d1b7524bSRajkumar Manoharan if (chandef->chan->band == NL80211_BAND_6GHZ)
3476d1b7524bSRajkumar Manoharan ie_len += sizeof(struct ieee80211_he_6ghz_oper);
347760ad72daSSven Eckelmann
347860ad72daSSven Eckelmann *pos++ = WLAN_EID_EXTENSION;
3479d1b7524bSRajkumar Manoharan *pos++ = ie_len;
348060ad72daSSven Eckelmann *pos++ = WLAN_EID_EXT_HE_OPERATION;
348160ad72daSSven Eckelmann
348260ad72daSSven Eckelmann he_oper_params = 0;
348360ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1023, /* disabled */
348460ad72daSSven Eckelmann IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
348560ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1,
348660ad72daSSven Eckelmann IEEE80211_HE_OPERATION_ER_SU_DISABLE);
348760ad72daSSven Eckelmann he_oper_params |= u32_encode_bits(1,
348860ad72daSSven Eckelmann IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
3489d1b7524bSRajkumar Manoharan if (chandef->chan->band == NL80211_BAND_6GHZ)
3490d1b7524bSRajkumar Manoharan he_oper_params |= u32_encode_bits(1,
3491d1b7524bSRajkumar Manoharan IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
349260ad72daSSven Eckelmann
349360ad72daSSven Eckelmann he_oper = (struct ieee80211_he_operation *)pos;
349460ad72daSSven Eckelmann he_oper->he_oper_params = cpu_to_le32(he_oper_params);
349560ad72daSSven Eckelmann
349660ad72daSSven Eckelmann /* don't require special HE peer rates */
349760ad72daSSven Eckelmann he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
3498d1b7524bSRajkumar Manoharan pos += sizeof(struct ieee80211_he_operation);
349960ad72daSSven Eckelmann
3500d1b7524bSRajkumar Manoharan if (chandef->chan->band != NL80211_BAND_6GHZ)
3501d1b7524bSRajkumar Manoharan goto out;
350260ad72daSSven Eckelmann
3503d1b7524bSRajkumar Manoharan /* TODO add VHT operational */
3504d1b7524bSRajkumar Manoharan he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos;
3505d1b7524bSRajkumar Manoharan he_6ghz_op->minrate = 6; /* 6 Mbps */
3506d1b7524bSRajkumar Manoharan he_6ghz_op->primary =
3507d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->chan->center_freq);
3508d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 =
3509d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->center_freq1);
3510d1b7524bSRajkumar Manoharan if (chandef->center_freq2)
3511d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 =
3512d1b7524bSRajkumar Manoharan ieee80211_frequency_to_channel(chandef->center_freq2);
3513d1b7524bSRajkumar Manoharan else
3514d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 = 0;
3515d1b7524bSRajkumar Manoharan
3516d1b7524bSRajkumar Manoharan switch (chandef->width) {
35175dca295dSIlan Peer case NL80211_CHAN_WIDTH_320:
35185dca295dSIlan Peer /*
35195dca295dSIlan Peer * TODO: mesh operation is not defined over 6GHz 320 MHz
35205dca295dSIlan Peer * channels.
35215dca295dSIlan Peer */
35225dca295dSIlan Peer WARN_ON(1);
35235dca295dSIlan Peer break;
3524d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_160:
3525d1b7524bSRajkumar Manoharan /* Convert 160 MHz channel width to new style as interop
3526d1b7524bSRajkumar Manoharan * workaround.
3527d1b7524bSRajkumar Manoharan */
3528d1b7524bSRajkumar Manoharan he_6ghz_op->control =
3529d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
3530d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0;
3531d1b7524bSRajkumar Manoharan if (chandef->chan->center_freq < chandef->center_freq1)
3532d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 -= 8;
3533d1b7524bSRajkumar Manoharan else
3534d1b7524bSRajkumar Manoharan he_6ghz_op->ccfs0 += 8;
3535d1b7524bSRajkumar Manoharan fallthrough;
3536d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_80P80:
3537d1b7524bSRajkumar Manoharan he_6ghz_op->control =
3538d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
3539d1b7524bSRajkumar Manoharan break;
3540d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_80:
3541d1b7524bSRajkumar Manoharan he_6ghz_op->control =
3542d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
3543d1b7524bSRajkumar Manoharan break;
3544d1b7524bSRajkumar Manoharan case NL80211_CHAN_WIDTH_40:
3545d1b7524bSRajkumar Manoharan he_6ghz_op->control =
3546d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
3547d1b7524bSRajkumar Manoharan break;
3548d1b7524bSRajkumar Manoharan default:
3549d1b7524bSRajkumar Manoharan he_6ghz_op->control =
3550d1b7524bSRajkumar Manoharan IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
3551d1b7524bSRajkumar Manoharan break;
3552d1b7524bSRajkumar Manoharan }
3553d1b7524bSRajkumar Manoharan
3554d1b7524bSRajkumar Manoharan pos += sizeof(struct ieee80211_he_6ghz_oper);
3555d1b7524bSRajkumar Manoharan
3556d1b7524bSRajkumar Manoharan out:
3557d1b7524bSRajkumar Manoharan return pos;
355860ad72daSSven Eckelmann }
355960ad72daSSven Eckelmann
ieee80211_ie_build_eht_oper(u8 * pos,struct cfg80211_chan_def * chandef,const struct ieee80211_sta_eht_cap * eht_cap)3560df1875c4SRyder Lee u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef,
3561df1875c4SRyder Lee const struct ieee80211_sta_eht_cap *eht_cap)
3562df1875c4SRyder Lee
3563df1875c4SRyder Lee {
3564df1875c4SRyder Lee const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
3565df1875c4SRyder Lee &eht_cap->eht_mcs_nss_supp.only_20mhz;
3566df1875c4SRyder Lee struct ieee80211_eht_operation *eht_oper;
3567df1875c4SRyder Lee struct ieee80211_eht_operation_info *eht_oper_info;
3568df1875c4SRyder Lee u8 eht_oper_len = offsetof(struct ieee80211_eht_operation, optional);
3569df1875c4SRyder Lee u8 eht_oper_info_len =
3570df1875c4SRyder Lee offsetof(struct ieee80211_eht_operation_info, optional);
3571df1875c4SRyder Lee u8 chan_width = 0;
3572df1875c4SRyder Lee
3573df1875c4SRyder Lee *pos++ = WLAN_EID_EXTENSION;
3574df1875c4SRyder Lee *pos++ = 1 + eht_oper_len + eht_oper_info_len;
3575df1875c4SRyder Lee *pos++ = WLAN_EID_EXT_EHT_OPERATION;
3576df1875c4SRyder Lee
3577df1875c4SRyder Lee eht_oper = (struct ieee80211_eht_operation *)pos;
3578df1875c4SRyder Lee
3579df1875c4SRyder Lee memcpy(&eht_oper->basic_mcs_nss, eht_mcs_nss, sizeof(*eht_mcs_nss));
3580df1875c4SRyder Lee eht_oper->params |= IEEE80211_EHT_OPER_INFO_PRESENT;
3581df1875c4SRyder Lee pos += eht_oper_len;
3582df1875c4SRyder Lee
3583df1875c4SRyder Lee eht_oper_info =
3584df1875c4SRyder Lee (struct ieee80211_eht_operation_info *)eht_oper->optional;
3585df1875c4SRyder Lee
3586df1875c4SRyder Lee eht_oper_info->ccfs0 =
3587df1875c4SRyder Lee ieee80211_frequency_to_channel(chandef->center_freq1);
3588df1875c4SRyder Lee if (chandef->center_freq2)
3589df1875c4SRyder Lee eht_oper_info->ccfs1 =
3590df1875c4SRyder Lee ieee80211_frequency_to_channel(chandef->center_freq2);
3591df1875c4SRyder Lee else
3592df1875c4SRyder Lee eht_oper_info->ccfs1 = 0;
3593df1875c4SRyder Lee
3594df1875c4SRyder Lee switch (chandef->width) {
3595df1875c4SRyder Lee case NL80211_CHAN_WIDTH_320:
3596df1875c4SRyder Lee chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
3597df1875c4SRyder Lee eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
3598df1875c4SRyder Lee if (chandef->chan->center_freq < chandef->center_freq1)
3599df1875c4SRyder Lee eht_oper_info->ccfs0 -= 16;
3600df1875c4SRyder Lee else
3601df1875c4SRyder Lee eht_oper_info->ccfs0 += 16;
3602df1875c4SRyder Lee break;
3603df1875c4SRyder Lee case NL80211_CHAN_WIDTH_160:
3604df1875c4SRyder Lee eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
3605df1875c4SRyder Lee if (chandef->chan->center_freq < chandef->center_freq1)
3606df1875c4SRyder Lee eht_oper_info->ccfs0 -= 8;
3607df1875c4SRyder Lee else
3608df1875c4SRyder Lee eht_oper_info->ccfs0 += 8;
3609df1875c4SRyder Lee fallthrough;
3610df1875c4SRyder Lee case NL80211_CHAN_WIDTH_80P80:
3611df1875c4SRyder Lee chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ;
3612df1875c4SRyder Lee break;
3613df1875c4SRyder Lee case NL80211_CHAN_WIDTH_80:
3614df1875c4SRyder Lee chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ;
3615df1875c4SRyder Lee break;
3616df1875c4SRyder Lee case NL80211_CHAN_WIDTH_40:
3617df1875c4SRyder Lee chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ;
3618df1875c4SRyder Lee break;
3619df1875c4SRyder Lee default:
3620df1875c4SRyder Lee chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ;
3621df1875c4SRyder Lee break;
3622df1875c4SRyder Lee }
3623df1875c4SRyder Lee eht_oper_info->control = chan_width;
3624df1875c4SRyder Lee pos += eht_oper_info_len;
3625df1875c4SRyder Lee
3626df1875c4SRyder Lee /* TODO: eht_oper_info->optional */
3627df1875c4SRyder Lee
3628df1875c4SRyder Lee return pos;
3629df1875c4SRyder Lee }
3630df1875c4SRyder Lee
ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation * ht_oper,struct cfg80211_chan_def * chandef)36318ac3c704SJohannes Berg bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
36324bf88530SJohannes Berg struct cfg80211_chan_def *chandef)
363342e7aa77SAlexander Simon {
363442e7aa77SAlexander Simon enum nl80211_channel_type channel_type;
363542e7aa77SAlexander Simon
36368ac3c704SJohannes Berg if (!ht_oper)
36378ac3c704SJohannes Berg return false;
363842e7aa77SAlexander Simon
3639074d46d1SJohannes Berg switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
364042e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_NONE:
364142e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT20;
364242e7aa77SAlexander Simon break;
364342e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
364442e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40PLUS;
364542e7aa77SAlexander Simon break;
364642e7aa77SAlexander Simon case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
364742e7aa77SAlexander Simon channel_type = NL80211_CHAN_HT40MINUS;
364842e7aa77SAlexander Simon break;
364942e7aa77SAlexander Simon default:
36508ac3c704SJohannes Berg return false;
365142e7aa77SAlexander Simon }
365242e7aa77SAlexander Simon
36538ac3c704SJohannes Berg cfg80211_chandef_create(chandef, chandef->chan, channel_type);
36548ac3c704SJohannes Berg return true;
365542e7aa77SAlexander Simon }
365642e7aa77SAlexander Simon
ieee80211_chandef_vht_oper(struct ieee80211_hw * hw,u32 vht_cap_info,const struct ieee80211_vht_operation * oper,const struct ieee80211_ht_operation * htop,struct cfg80211_chan_def * chandef)36572a333a0dSJohannes Berg bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
36587eb26df2SJohannes Berg const struct ieee80211_vht_operation *oper,
36597eb26df2SJohannes Berg const struct ieee80211_ht_operation *htop,
3660abcff6efSJanusz.Dziedzic@tieto.com struct cfg80211_chan_def *chandef)
3661abcff6efSJanusz.Dziedzic@tieto.com {
36628ac3c704SJohannes Berg struct cfg80211_chan_def new = *chandef;
36637eb26df2SJohannes Berg int cf0, cf1;
36647eb26df2SJohannes Berg int ccfs0, ccfs1, ccfs2;
36657eb26df2SJohannes Berg int ccf0, ccf1;
366633181ea7SShay Bar u32 vht_cap;
366733181ea7SShay Bar bool support_80_80 = false;
366833181ea7SShay Bar bool support_160 = false;
36692a333a0dSJohannes Berg u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info,
36702a333a0dSJohannes Berg IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
36712a333a0dSJohannes Berg u8 supp_chwidth = u32_get_bits(vht_cap_info,
36722a333a0dSJohannes Berg IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
3673abcff6efSJanusz.Dziedzic@tieto.com
36747eb26df2SJohannes Berg if (!oper || !htop)
36758ac3c704SJohannes Berg return false;
36768ac3c704SJohannes Berg
367733181ea7SShay Bar vht_cap = hw->wiphy->bands[chandef->chan->band]->vht_cap.cap;
367833181ea7SShay Bar support_160 = (vht_cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK |
367933181ea7SShay Bar IEEE80211_VHT_CAP_EXT_NSS_BW_MASK));
368033181ea7SShay Bar support_80_80 = ((vht_cap &
368133181ea7SShay Bar IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) ||
368233181ea7SShay Bar (vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
368333181ea7SShay Bar vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) ||
368433181ea7SShay Bar ((vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >>
368533181ea7SShay Bar IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT > 1));
36867eb26df2SJohannes Berg ccfs0 = oper->center_freq_seg0_idx;
36877eb26df2SJohannes Berg ccfs1 = oper->center_freq_seg1_idx;
36887eb26df2SJohannes Berg ccfs2 = (le16_to_cpu(htop->operation_mode) &
36897eb26df2SJohannes Berg IEEE80211_HT_OP_MODE_CCFS2_MASK)
36907eb26df2SJohannes Berg >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT;
36917eb26df2SJohannes Berg
36927eb26df2SJohannes Berg ccf0 = ccfs0;
36932a333a0dSJohannes Berg
36942a333a0dSJohannes Berg /* if not supported, parse as though we didn't understand it */
36952a333a0dSJohannes Berg if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
36962a333a0dSJohannes Berg ext_nss_bw_supp = 0;
36972a333a0dSJohannes Berg
36982a333a0dSJohannes Berg /*
36992a333a0dSJohannes Berg * Cf. IEEE 802.11 Table 9-250
37002a333a0dSJohannes Berg *
37012a333a0dSJohannes Berg * We really just consider that because it's inefficient to connect
37022a333a0dSJohannes Berg * at a higher bandwidth than we'll actually be able to use.
37032a333a0dSJohannes Berg */
37042a333a0dSJohannes Berg switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
37052a333a0dSJohannes Berg default:
37062a333a0dSJohannes Berg case 0x00:
37072a333a0dSJohannes Berg ccf1 = 0;
37082a333a0dSJohannes Berg support_160 = false;
37092a333a0dSJohannes Berg support_80_80 = false;
37102a333a0dSJohannes Berg break;
37112a333a0dSJohannes Berg case 0x01:
37122a333a0dSJohannes Berg support_80_80 = false;
3713fc0561dcSGustavo A. R. Silva fallthrough;
37142a333a0dSJohannes Berg case 0x02:
37152a333a0dSJohannes Berg case 0x03:
37167eb26df2SJohannes Berg ccf1 = ccfs2;
37172a333a0dSJohannes Berg break;
37182a333a0dSJohannes Berg case 0x10:
37192a333a0dSJohannes Berg ccf1 = ccfs1;
37202a333a0dSJohannes Berg break;
37212a333a0dSJohannes Berg case 0x11:
37222a333a0dSJohannes Berg case 0x12:
37232a333a0dSJohannes Berg if (!ccfs1)
37242a333a0dSJohannes Berg ccf1 = ccfs2;
37252a333a0dSJohannes Berg else
37262a333a0dSJohannes Berg ccf1 = ccfs1;
37272a333a0dSJohannes Berg break;
37282a333a0dSJohannes Berg case 0x13:
37292a333a0dSJohannes Berg case 0x20:
37302a333a0dSJohannes Berg case 0x23:
37312a333a0dSJohannes Berg ccf1 = ccfs1;
37322a333a0dSJohannes Berg break;
37332a333a0dSJohannes Berg }
37347eb26df2SJohannes Berg
37357eb26df2SJohannes Berg cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band);
37367eb26df2SJohannes Berg cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band);
3737abcff6efSJanusz.Dziedzic@tieto.com
3738abcff6efSJanusz.Dziedzic@tieto.com switch (oper->chan_width) {
3739abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_USE_HT:
37407eb26df2SJohannes Berg /* just use HT information directly */
3741abcff6efSJanusz.Dziedzic@tieto.com break;
3742abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_80MHZ:
37438ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_80;
37447eb26df2SJohannes Berg new.center_freq1 = cf0;
374523665aafSJouni Malinen /* If needed, adjust based on the newer interop workaround. */
37467eb26df2SJohannes Berg if (ccf1) {
374723665aafSJouni Malinen unsigned int diff;
374823665aafSJouni Malinen
37497eb26df2SJohannes Berg diff = abs(ccf1 - ccf0);
375033181ea7SShay Bar if ((diff == 8) && support_160) {
375123665aafSJouni Malinen new.width = NL80211_CHAN_WIDTH_160;
37527eb26df2SJohannes Berg new.center_freq1 = cf1;
375333181ea7SShay Bar } else if ((diff > 8) && support_80_80) {
375423665aafSJouni Malinen new.width = NL80211_CHAN_WIDTH_80P80;
37557eb26df2SJohannes Berg new.center_freq2 = cf1;
375623665aafSJouni Malinen }
375723665aafSJouni Malinen }
3758abcff6efSJanusz.Dziedzic@tieto.com break;
3759abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_160MHZ:
37607eb26df2SJohannes Berg /* deprecated encoding */
37618ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_160;
37627eb26df2SJohannes Berg new.center_freq1 = cf0;
3763abcff6efSJanusz.Dziedzic@tieto.com break;
3764abcff6efSJanusz.Dziedzic@tieto.com case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
37657eb26df2SJohannes Berg /* deprecated encoding */
37668ac3c704SJohannes Berg new.width = NL80211_CHAN_WIDTH_80P80;
37677eb26df2SJohannes Berg new.center_freq1 = cf0;
37687eb26df2SJohannes Berg new.center_freq2 = cf1;
3769abcff6efSJanusz.Dziedzic@tieto.com break;
3770abcff6efSJanusz.Dziedzic@tieto.com default:
37718ac3c704SJohannes Berg return false;
3772abcff6efSJanusz.Dziedzic@tieto.com }
3773abcff6efSJanusz.Dziedzic@tieto.com
37748ac3c704SJohannes Berg if (!cfg80211_chandef_valid(&new))
37758ac3c704SJohannes Berg return false;
37768ac3c704SJohannes Berg
37778ac3c704SJohannes Berg *chandef = new;
37788ac3c704SJohannes Berg return true;
3779abcff6efSJanusz.Dziedzic@tieto.com }
3780abcff6efSJanusz.Dziedzic@tieto.com
ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation * eht_oper,bool support_160,bool support_320,struct cfg80211_chan_def * chandef)3781774e00c2SJohannes Berg void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
37821e0b3b0bSIlan Peer bool support_160, bool support_320,
37831e0b3b0bSIlan Peer struct cfg80211_chan_def *chandef)
37841e0b3b0bSIlan Peer {
37851e0b3b0bSIlan Peer struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional;
37861e0b3b0bSIlan Peer
37871e0b3b0bSIlan Peer chandef->center_freq1 =
37881e0b3b0bSIlan Peer ieee80211_channel_to_frequency(info->ccfs0,
37891e0b3b0bSIlan Peer chandef->chan->band);
37901e0b3b0bSIlan Peer
37911e0b3b0bSIlan Peer switch (u8_get_bits(info->control,
37921e0b3b0bSIlan Peer IEEE80211_EHT_OPER_CHAN_WIDTH)) {
37931e0b3b0bSIlan Peer case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ:
37941e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_20;
37951e0b3b0bSIlan Peer break;
37961e0b3b0bSIlan Peer case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ:
37971e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_40;
37981e0b3b0bSIlan Peer break;
37991e0b3b0bSIlan Peer case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ:
38001e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_80;
38011e0b3b0bSIlan Peer break;
38021e0b3b0bSIlan Peer case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ:
38031e0b3b0bSIlan Peer if (support_160) {
38041e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_160;
38051e0b3b0bSIlan Peer chandef->center_freq1 =
38061e0b3b0bSIlan Peer ieee80211_channel_to_frequency(info->ccfs1,
38071e0b3b0bSIlan Peer chandef->chan->band);
38081e0b3b0bSIlan Peer } else {
38091e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_80;
38101e0b3b0bSIlan Peer }
38111e0b3b0bSIlan Peer break;
38121e0b3b0bSIlan Peer case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ:
38131e0b3b0bSIlan Peer if (support_320) {
38141e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_320;
38151e0b3b0bSIlan Peer chandef->center_freq1 =
38161e0b3b0bSIlan Peer ieee80211_channel_to_frequency(info->ccfs1,
38171e0b3b0bSIlan Peer chandef->chan->band);
38181e0b3b0bSIlan Peer } else if (support_160) {
38191e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_160;
38201e0b3b0bSIlan Peer } else {
38211e0b3b0bSIlan Peer chandef->width = NL80211_CHAN_WIDTH_80;
38221e0b3b0bSIlan Peer
38231e0b3b0bSIlan Peer if (chandef->center_freq1 > chandef->chan->center_freq)
38241e0b3b0bSIlan Peer chandef->center_freq1 -= 40;
38251e0b3b0bSIlan Peer else
38261e0b3b0bSIlan Peer chandef->center_freq1 += 40;
38271e0b3b0bSIlan Peer }
38281e0b3b0bSIlan Peer break;
38291e0b3b0bSIlan Peer }
38301e0b3b0bSIlan Peer }
38311e0b3b0bSIlan Peer
ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data * sdata,const struct ieee80211_he_operation * he_oper,const struct ieee80211_eht_operation * eht_oper,struct cfg80211_chan_def * chandef)383257fa5e85SJohannes Berg bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
383357fa5e85SJohannes Berg const struct ieee80211_he_operation *he_oper,
38345dca295dSIlan Peer const struct ieee80211_eht_operation *eht_oper,
383557fa5e85SJohannes Berg struct cfg80211_chan_def *chandef)
383657fa5e85SJohannes Berg {
383757fa5e85SJohannes Berg struct ieee80211_local *local = sdata->local;
383857fa5e85SJohannes Berg struct ieee80211_supported_band *sband;
383957fa5e85SJohannes Berg enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
384057fa5e85SJohannes Berg const struct ieee80211_sta_he_cap *he_cap;
38415dca295dSIlan Peer const struct ieee80211_sta_eht_cap *eht_cap;
384257fa5e85SJohannes Berg struct cfg80211_chan_def he_chandef = *chandef;
384357fa5e85SJohannes Berg const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
3844cb751b7aSWen Gong struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
38455dca295dSIlan Peer bool support_80_80, support_160, support_320;
38465dca295dSIlan Peer u8 he_phy_cap, eht_phy_cap;
384757fa5e85SJohannes Berg u32 freq;
384857fa5e85SJohannes Berg
384957fa5e85SJohannes Berg if (chandef->chan->band != NL80211_BAND_6GHZ)
385057fa5e85SJohannes Berg return true;
385157fa5e85SJohannes Berg
385257fa5e85SJohannes Berg sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
385357fa5e85SJohannes Berg
385457fa5e85SJohannes Berg he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
385557fa5e85SJohannes Berg if (!he_cap) {
385657fa5e85SJohannes Berg sdata_info(sdata, "Missing iftype sband data/HE cap");
385757fa5e85SJohannes Berg return false;
385857fa5e85SJohannes Berg }
385957fa5e85SJohannes Berg
386057fa5e85SJohannes Berg he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0];
386157fa5e85SJohannes Berg support_160 =
386257fa5e85SJohannes Berg he_phy_cap &
386357fa5e85SJohannes Berg IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
386457fa5e85SJohannes Berg support_80_80 =
386557fa5e85SJohannes Berg he_phy_cap &
386657fa5e85SJohannes Berg IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
386757fa5e85SJohannes Berg
386857fa5e85SJohannes Berg if (!he_oper) {
386957fa5e85SJohannes Berg sdata_info(sdata,
387057fa5e85SJohannes Berg "HE is not advertised on (on %d MHz), expect issues\n",
387157fa5e85SJohannes Berg chandef->chan->center_freq);
387257fa5e85SJohannes Berg return false;
387357fa5e85SJohannes Berg }
387457fa5e85SJohannes Berg
38755dca295dSIlan Peer eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype);
38766e21e7b8SNicolas Cavallari if (!eht_cap)
38775dca295dSIlan Peer eht_oper = NULL;
38785dca295dSIlan Peer
387957fa5e85SJohannes Berg he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
388057fa5e85SJohannes Berg
388157fa5e85SJohannes Berg if (!he_6ghz_oper) {
388257fa5e85SJohannes Berg sdata_info(sdata,
388357fa5e85SJohannes Berg "HE 6GHz operation missing (on %d MHz), expect issues\n",
388457fa5e85SJohannes Berg chandef->chan->center_freq);
388557fa5e85SJohannes Berg return false;
388657fa5e85SJohannes Berg }
388757fa5e85SJohannes Berg
38885dca295dSIlan Peer /*
38895dca295dSIlan Peer * The EHT operation IE does not contain the primary channel so the
38905dca295dSIlan Peer * primary channel frequency should be taken from the 6 GHz operation
38915dca295dSIlan Peer * information.
38925dca295dSIlan Peer */
389357fa5e85SJohannes Berg freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary,
389457fa5e85SJohannes Berg NL80211_BAND_6GHZ);
389557fa5e85SJohannes Berg he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
389657fa5e85SJohannes Berg
389757fa5e85SJohannes Berg switch (u8_get_bits(he_6ghz_oper->control,
3898cb751b7aSWen Gong IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) {
3899cb751b7aSWen Gong case IEEE80211_6GHZ_CTRL_REG_LPI_AP:
3900cb751b7aSWen Gong bss_conf->power_type = IEEE80211_REG_LPI_AP;
3901cb751b7aSWen Gong break;
3902cb751b7aSWen Gong case IEEE80211_6GHZ_CTRL_REG_SP_AP:
3903cb751b7aSWen Gong bss_conf->power_type = IEEE80211_REG_SP_AP;
3904cb751b7aSWen Gong break;
3905cb751b7aSWen Gong default:
3906cb751b7aSWen Gong bss_conf->power_type = IEEE80211_REG_UNSET_AP;
3907cb751b7aSWen Gong break;
3908cb751b7aSWen Gong }
3909cb751b7aSWen Gong
39101e0b3b0bSIlan Peer if (!eht_oper ||
39111e0b3b0bSIlan Peer !(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
3912cb751b7aSWen Gong switch (u8_get_bits(he_6ghz_oper->control,
391357fa5e85SJohannes Berg IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) {
391457fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
391557fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_20;
391657fa5e85SJohannes Berg break;
391757fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
391857fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_40;
391957fa5e85SJohannes Berg break;
392057fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
392157fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80;
392257fa5e85SJohannes Berg break;
392357fa5e85SJohannes Berg case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
392457fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80;
392557fa5e85SJohannes Berg if (!he_6ghz_oper->ccfs1)
392657fa5e85SJohannes Berg break;
392757fa5e85SJohannes Berg if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) {
392857fa5e85SJohannes Berg if (support_160)
392957fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_160;
393057fa5e85SJohannes Berg } else {
393157fa5e85SJohannes Berg if (support_80_80)
393257fa5e85SJohannes Berg he_chandef.width = NL80211_CHAN_WIDTH_80P80;
393357fa5e85SJohannes Berg }
393457fa5e85SJohannes Berg break;
393557fa5e85SJohannes Berg }
393657fa5e85SJohannes Berg
393757fa5e85SJohannes Berg if (he_chandef.width == NL80211_CHAN_WIDTH_160) {
393857fa5e85SJohannes Berg he_chandef.center_freq1 =
393957fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
394057fa5e85SJohannes Berg NL80211_BAND_6GHZ);
394157fa5e85SJohannes Berg } else {
394257fa5e85SJohannes Berg he_chandef.center_freq1 =
394357fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0,
394457fa5e85SJohannes Berg NL80211_BAND_6GHZ);
394575bcbd69SJohn Crispin if (support_80_80 || support_160)
394657fa5e85SJohannes Berg he_chandef.center_freq2 =
394757fa5e85SJohannes Berg ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
394857fa5e85SJohannes Berg NL80211_BAND_6GHZ);
394957fa5e85SJohannes Berg }
39505dca295dSIlan Peer } else {
39515dca295dSIlan Peer eht_phy_cap = eht_cap->eht_cap_elem.phy_cap_info[0];
39525dca295dSIlan Peer support_320 =
39535dca295dSIlan Peer eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
39545dca295dSIlan Peer
3955774e00c2SJohannes Berg ieee80211_chandef_eht_oper(eht_oper, support_160,
39561e0b3b0bSIlan Peer support_320, &he_chandef);
39575dca295dSIlan Peer }
395857fa5e85SJohannes Berg
395957fa5e85SJohannes Berg if (!cfg80211_chandef_valid(&he_chandef)) {
396057fa5e85SJohannes Berg sdata_info(sdata,
396157fa5e85SJohannes Berg "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n",
396257fa5e85SJohannes Berg he_chandef.chan ? he_chandef.chan->center_freq : 0,
396357fa5e85SJohannes Berg he_chandef.width,
396457fa5e85SJohannes Berg he_chandef.center_freq1,
396557fa5e85SJohannes Berg he_chandef.center_freq2);
396657fa5e85SJohannes Berg return false;
396757fa5e85SJohannes Berg }
396857fa5e85SJohannes Berg
396957fa5e85SJohannes Berg *chandef = he_chandef;
397057fa5e85SJohannes Berg
39718fca2b87SWen Gong return true;
39721d00ce80SThomas Pedersen }
39731d00ce80SThomas Pedersen
ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie * oper,struct cfg80211_chan_def * chandef)39741d00ce80SThomas Pedersen bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
39751d00ce80SThomas Pedersen struct cfg80211_chan_def *chandef)
39761d00ce80SThomas Pedersen {
39771d00ce80SThomas Pedersen u32 oper_freq;
39781d00ce80SThomas Pedersen
39791d00ce80SThomas Pedersen if (!oper)
39801d00ce80SThomas Pedersen return false;
39811d00ce80SThomas Pedersen
39821d00ce80SThomas Pedersen switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) {
39831d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_1MHZ:
39841d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_1;
39851d00ce80SThomas Pedersen break;
39861d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_2MHZ:
39871d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_2;
39881d00ce80SThomas Pedersen break;
39891d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_4MHZ:
39901d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_4;
39911d00ce80SThomas Pedersen break;
39921d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_8MHZ:
39931d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_8;
39941d00ce80SThomas Pedersen break;
39951d00ce80SThomas Pedersen case IEEE80211_S1G_CHANWIDTH_16MHZ:
39961d00ce80SThomas Pedersen chandef->width = NL80211_CHAN_WIDTH_16;
39971d00ce80SThomas Pedersen break;
39981d00ce80SThomas Pedersen default:
39991d00ce80SThomas Pedersen return false;
40001d00ce80SThomas Pedersen }
40011d00ce80SThomas Pedersen
40021d00ce80SThomas Pedersen oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch,
40031d00ce80SThomas Pedersen NL80211_BAND_S1GHZ);
40041d00ce80SThomas Pedersen chandef->center_freq1 = KHZ_TO_MHZ(oper_freq);
40051d00ce80SThomas Pedersen chandef->freq1_offset = oper_freq % 1000;
40061d00ce80SThomas Pedersen
400757fa5e85SJohannes Berg return true;
400857fa5e85SJohannes Berg }
400957fa5e85SJohannes Berg
ieee80211_parse_bitrates(enum nl80211_chan_width width,const struct ieee80211_supported_band * sband,const u8 * srates,int srates_len,u32 * rates)40103dc05935SJohannes Berg int ieee80211_parse_bitrates(enum nl80211_chan_width width,
40112103dec1SSimon Wunderlich const struct ieee80211_supported_band *sband,
40122103dec1SSimon Wunderlich const u8 *srates, int srates_len, u32 *rates)
40132103dec1SSimon Wunderlich {
40143dc05935SJohannes Berg u32 rate_flags = ieee80211_chanwidth_rate_flags(width);
40153dc05935SJohannes Berg int shift = ieee80211_chanwidth_get_shift(width);
40162103dec1SSimon Wunderlich struct ieee80211_rate *br;
40172103dec1SSimon Wunderlich int brate, rate, i, j, count = 0;
40182103dec1SSimon Wunderlich
40192103dec1SSimon Wunderlich *rates = 0;
40202103dec1SSimon Wunderlich
40212103dec1SSimon Wunderlich for (i = 0; i < srates_len; i++) {
40222103dec1SSimon Wunderlich rate = srates[i] & 0x7f;
40232103dec1SSimon Wunderlich
40242103dec1SSimon Wunderlich for (j = 0; j < sband->n_bitrates; j++) {
40252103dec1SSimon Wunderlich br = &sband->bitrates[j];
40262103dec1SSimon Wunderlich if ((rate_flags & br->flags) != rate_flags)
40272103dec1SSimon Wunderlich continue;
40282103dec1SSimon Wunderlich
40292103dec1SSimon Wunderlich brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
40302103dec1SSimon Wunderlich if (brate == rate) {
40312103dec1SSimon Wunderlich *rates |= BIT(j);
40322103dec1SSimon Wunderlich count++;
40332103dec1SSimon Wunderlich break;
40342103dec1SSimon Wunderlich }
40352103dec1SSimon Wunderlich }
40362103dec1SSimon Wunderlich }
40372103dec1SSimon Wunderlich return count;
40382103dec1SSimon Wunderlich }
40392103dec1SSimon Wunderlich
ieee80211_add_srates_ie(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb,bool need_basic,enum nl80211_band band)4040fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
40416b77863bSJohannes Berg struct sk_buff *skb, bool need_basic,
404257fbcce3SJohannes Berg enum nl80211_band band)
4043768db343SArik Nemtsov {
4044768db343SArik Nemtsov struct ieee80211_local *local = sdata->local;
4045768db343SArik Nemtsov struct ieee80211_supported_band *sband;
40462103dec1SSimon Wunderlich int rate, shift;
4047768db343SArik Nemtsov u8 i, rates, *pos;
4048fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates;
40492103dec1SSimon Wunderlich u32 rate_flags;
4050768db343SArik Nemtsov
40512103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif);
40522103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
40536b77863bSJohannes Berg sband = local->hw.wiphy->bands[band];
40542103dec1SSimon Wunderlich rates = 0;
40552103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) {
40562103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
40572103dec1SSimon Wunderlich continue;
40582103dec1SSimon Wunderlich rates++;
40592103dec1SSimon Wunderlich }
4060768db343SArik Nemtsov if (rates > 8)
4061768db343SArik Nemtsov rates = 8;
4062768db343SArik Nemtsov
4063768db343SArik Nemtsov if (skb_tailroom(skb) < rates + 2)
4064768db343SArik Nemtsov return -ENOMEM;
4065768db343SArik Nemtsov
4066768db343SArik Nemtsov pos = skb_put(skb, rates + 2);
4067768db343SArik Nemtsov *pos++ = WLAN_EID_SUPP_RATES;
4068768db343SArik Nemtsov *pos++ = rates;
4069768db343SArik Nemtsov for (i = 0; i < rates; i++) {
4070657c3e0cSAshok Nagarajan u8 basic = 0;
40712103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
40722103dec1SSimon Wunderlich continue;
40732103dec1SSimon Wunderlich
4074657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i))
4075657c3e0cSAshok Nagarajan basic = 0x80;
40762103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
40772103dec1SSimon Wunderlich 5 * (1 << shift));
40782103dec1SSimon Wunderlich *pos++ = basic | (u8) rate;
4079768db343SArik Nemtsov }
4080768db343SArik Nemtsov
4081768db343SArik Nemtsov return 0;
4082768db343SArik Nemtsov }
4083768db343SArik Nemtsov
ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb,bool need_basic,enum nl80211_band band)4084fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
40856b77863bSJohannes Berg struct sk_buff *skb, bool need_basic,
408657fbcce3SJohannes Berg enum nl80211_band band)
4087768db343SArik Nemtsov {
4088768db343SArik Nemtsov struct ieee80211_local *local = sdata->local;
4089768db343SArik Nemtsov struct ieee80211_supported_band *sband;
4090cc63ec76SChun-Yeow Yeoh int rate, shift;
4091768db343SArik Nemtsov u8 i, exrates, *pos;
4092fc8a7321SJohannes Berg u32 basic_rates = sdata->vif.bss_conf.basic_rates;
40932103dec1SSimon Wunderlich u32 rate_flags;
40942103dec1SSimon Wunderlich
40952103dec1SSimon Wunderlich rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
40962103dec1SSimon Wunderlich shift = ieee80211_vif_get_shift(&sdata->vif);
4097768db343SArik Nemtsov
40986b77863bSJohannes Berg sband = local->hw.wiphy->bands[band];
40992103dec1SSimon Wunderlich exrates = 0;
41002103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) {
41012103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
41022103dec1SSimon Wunderlich continue;
41032103dec1SSimon Wunderlich exrates++;
41042103dec1SSimon Wunderlich }
41052103dec1SSimon Wunderlich
4106768db343SArik Nemtsov if (exrates > 8)
4107768db343SArik Nemtsov exrates -= 8;
4108768db343SArik Nemtsov else
4109768db343SArik Nemtsov exrates = 0;
4110768db343SArik Nemtsov
4111768db343SArik Nemtsov if (skb_tailroom(skb) < exrates + 2)
4112768db343SArik Nemtsov return -ENOMEM;
4113768db343SArik Nemtsov
4114768db343SArik Nemtsov if (exrates) {
4115768db343SArik Nemtsov pos = skb_put(skb, exrates + 2);
4116768db343SArik Nemtsov *pos++ = WLAN_EID_EXT_SUPP_RATES;
4117768db343SArik Nemtsov *pos++ = exrates;
4118768db343SArik Nemtsov for (i = 8; i < sband->n_bitrates; i++) {
4119657c3e0cSAshok Nagarajan u8 basic = 0;
41202103dec1SSimon Wunderlich if ((rate_flags & sband->bitrates[i].flags)
41212103dec1SSimon Wunderlich != rate_flags)
41222103dec1SSimon Wunderlich continue;
4123657c3e0cSAshok Nagarajan if (need_basic && basic_rates & BIT(i))
4124657c3e0cSAshok Nagarajan basic = 0x80;
41252103dec1SSimon Wunderlich rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
41262103dec1SSimon Wunderlich 5 * (1 << shift));
41272103dec1SSimon Wunderlich *pos++ = basic | (u8) rate;
4128768db343SArik Nemtsov }
4129768db343SArik Nemtsov }
4130768db343SArik Nemtsov return 0;
4131768db343SArik Nemtsov }
41321dae27f8SWey-Yi Guy
ieee80211_ave_rssi(struct ieee80211_vif * vif)41331dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif)
41341dae27f8SWey-Yi Guy {
41351dae27f8SWey-Yi Guy struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
41361dae27f8SWey-Yi Guy
4137bfd8403aSJohannes Berg if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
4138be6bcabcSWey-Yi Guy return 0;
4139bfd8403aSJohannes Berg
4140bfd8403aSJohannes Berg return -ewma_beacon_signal_read(&sdata->deflink.u.mgd.ave_beacon_signal);
41411dae27f8SWey-Yi Guy }
41420d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
414304ecd257SJohannes Berg
ieee80211_mcs_to_chains(const struct ieee80211_mcs_info * mcs)414404ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
414504ecd257SJohannes Berg {
414604ecd257SJohannes Berg if (!mcs)
414704ecd257SJohannes Berg return 1;
414804ecd257SJohannes Berg
414904ecd257SJohannes Berg /* TODO: consider rx_highest */
415004ecd257SJohannes Berg
415104ecd257SJohannes Berg if (mcs->rx_mask[3])
415204ecd257SJohannes Berg return 4;
415304ecd257SJohannes Berg if (mcs->rx_mask[2])
415404ecd257SJohannes Berg return 3;
415504ecd257SJohannes Berg if (mcs->rx_mask[1])
415604ecd257SJohannes Berg return 2;
415704ecd257SJohannes Berg return 1;
415804ecd257SJohannes Berg }
4159f4bda337SThomas Pedersen
4160f4bda337SThomas Pedersen /**
4161f4bda337SThomas Pedersen * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
4162f4bda337SThomas Pedersen * @local: mac80211 hw info struct
4163f4bda337SThomas Pedersen * @status: RX status
4164f4bda337SThomas Pedersen * @mpdu_len: total MPDU length (including FCS)
4165f4bda337SThomas Pedersen * @mpdu_offset: offset into MPDU to calculate timestamp at
4166f4bda337SThomas Pedersen *
4167f4bda337SThomas Pedersen * This function calculates the RX timestamp at the given MPDU offset, taking
4168f4bda337SThomas Pedersen * into account what the RX timestamp was. An offset of 0 will just normalize
4169f4bda337SThomas Pedersen * the timestamp to TSF at beginning of MPDU reception.
4170f4bda337SThomas Pedersen */
ieee80211_calculate_rx_timestamp(struct ieee80211_local * local,struct ieee80211_rx_status * status,unsigned int mpdu_len,unsigned int mpdu_offset)4171f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
4172f4bda337SThomas Pedersen struct ieee80211_rx_status *status,
4173f4bda337SThomas Pedersen unsigned int mpdu_len,
4174f4bda337SThomas Pedersen unsigned int mpdu_offset)
4175f4bda337SThomas Pedersen {
4176f4bda337SThomas Pedersen u64 ts = status->mactime;
4177f4bda337SThomas Pedersen struct rate_info ri;
4178f4bda337SThomas Pedersen u16 rate;
4179da388233SAvraham Stern u8 n_ltf;
4180f4bda337SThomas Pedersen
4181f4bda337SThomas Pedersen if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
4182f4bda337SThomas Pedersen return 0;
4183f4bda337SThomas Pedersen
4184f4bda337SThomas Pedersen memset(&ri, 0, sizeof(ri));
4185f4bda337SThomas Pedersen
418635f4962cSJohannes Berg ri.bw = status->bw;
418735f4962cSJohannes Berg
4188f4bda337SThomas Pedersen /* Fill cfg80211 rate info */
4189da6a4352SJohannes Berg switch (status->encoding) {
4190f66c48afSJohannes Berg case RX_ENC_EHT:
4191f66c48afSJohannes Berg ri.flags |= RATE_INFO_FLAGS_EHT_MCS;
4192f66c48afSJohannes Berg ri.mcs = status->rate_idx;
4193f66c48afSJohannes Berg ri.nss = status->nss;
4194f66c48afSJohannes Berg ri.eht_ru_alloc = status->eht.ru;
4195f66c48afSJohannes Berg if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
4196f66c48afSJohannes Berg ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
4197f66c48afSJohannes Berg /* TODO/FIXME: is this right? handle other PPDUs */
4198f66c48afSJohannes Berg if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
4199f66c48afSJohannes Berg mpdu_offset += 2;
4200f66c48afSJohannes Berg ts += 36;
4201f66c48afSJohannes Berg }
4202f66c48afSJohannes Berg break;
4203da388233SAvraham Stern case RX_ENC_HE:
4204da388233SAvraham Stern ri.flags |= RATE_INFO_FLAGS_HE_MCS;
4205da388233SAvraham Stern ri.mcs = status->rate_idx;
4206da388233SAvraham Stern ri.nss = status->nss;
4207da388233SAvraham Stern ri.he_ru_alloc = status->he_ru;
4208da388233SAvraham Stern if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
4209da388233SAvraham Stern ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
4210da388233SAvraham Stern
4211da388233SAvraham Stern /*
4212da388233SAvraham Stern * See P802.11ax_D6.0, section 27.3.4 for
4213da388233SAvraham Stern * VHT PPDU format.
4214da388233SAvraham Stern */
4215da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
4216da388233SAvraham Stern mpdu_offset += 2;
4217da388233SAvraham Stern ts += 36;
4218da388233SAvraham Stern
4219da388233SAvraham Stern /*
4220da388233SAvraham Stern * TODO:
4221da388233SAvraham Stern * For HE MU PPDU, add the HE-SIG-B.
4222da388233SAvraham Stern * For HE ER PPDU, add 8us for the HE-SIG-A.
4223da388233SAvraham Stern * For HE TB PPDU, add 4us for the HE-STF.
4224da388233SAvraham Stern * Add the HE-LTF durations - variable.
4225da388233SAvraham Stern */
4226da388233SAvraham Stern }
4227da388233SAvraham Stern
4228da388233SAvraham Stern break;
4229da6a4352SJohannes Berg case RX_ENC_HT:
4230f4bda337SThomas Pedersen ri.mcs = status->rate_idx;
4231f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_MCS;
42327fdd69c5SJohannes Berg if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
4233f4bda337SThomas Pedersen ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
4234da388233SAvraham Stern
4235da388233SAvraham Stern /*
4236da388233SAvraham Stern * See P802.11REVmd_D3.0, section 19.3.2 for
4237da388233SAvraham Stern * HT PPDU format.
4238da388233SAvraham Stern */
4239da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
4240da388233SAvraham Stern mpdu_offset += 2;
4241da388233SAvraham Stern if (status->enc_flags & RX_ENC_FLAG_HT_GF)
4242da388233SAvraham Stern ts += 24;
4243da388233SAvraham Stern else
4244da388233SAvraham Stern ts += 32;
4245da388233SAvraham Stern
4246da388233SAvraham Stern /*
4247da388233SAvraham Stern * Add Data HT-LTFs per streams
4248da388233SAvraham Stern * TODO: add Extension HT-LTFs, 4us per LTF
4249da388233SAvraham Stern */
4250da388233SAvraham Stern n_ltf = ((ri.mcs >> 3) & 3) + 1;
4251da388233SAvraham Stern n_ltf = n_ltf == 3 ? 4 : n_ltf;
4252da388233SAvraham Stern ts += n_ltf * 4;
4253da388233SAvraham Stern }
4254da388233SAvraham Stern
4255da6a4352SJohannes Berg break;
4256da6a4352SJohannes Berg case RX_ENC_VHT:
42575614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
42585614618eSJohannes Berg ri.mcs = status->rate_idx;
42598613c948SJohannes Berg ri.nss = status->nss;
42607fdd69c5SJohannes Berg if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
42615614618eSJohannes Berg ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
4262da388233SAvraham Stern
4263da388233SAvraham Stern /*
4264da388233SAvraham Stern * See P802.11REVmd_D3.0, section 21.3.2 for
4265da388233SAvraham Stern * VHT PPDU format.
4266da388233SAvraham Stern */
4267da388233SAvraham Stern if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
4268da388233SAvraham Stern mpdu_offset += 2;
4269da388233SAvraham Stern ts += 36;
4270da388233SAvraham Stern
4271da388233SAvraham Stern /*
4272da388233SAvraham Stern * Add VHT-LTFs per streams
4273da388233SAvraham Stern */
4274da388233SAvraham Stern n_ltf = (ri.nss != 1) && (ri.nss % 2) ?
4275da388233SAvraham Stern ri.nss + 1 : ri.nss;
4276da388233SAvraham Stern ts += 4 * n_ltf;
4277da388233SAvraham Stern }
4278da388233SAvraham Stern
4279da6a4352SJohannes Berg break;
4280da6a4352SJohannes Berg default:
4281da6a4352SJohannes Berg WARN_ON(1);
4282fc0561dcSGustavo A. R. Silva fallthrough;
4283da6a4352SJohannes Berg case RX_ENC_LEGACY: {
4284f4bda337SThomas Pedersen struct ieee80211_supported_band *sband;
42852103dec1SSimon Wunderlich int shift = 0;
42862103dec1SSimon Wunderlich int bitrate;
42872103dec1SSimon Wunderlich
4288da6a4352SJohannes Berg switch (status->bw) {
4289da6a4352SJohannes Berg case RATE_INFO_BW_10:
42902103dec1SSimon Wunderlich shift = 1;
4291da6a4352SJohannes Berg break;
4292da6a4352SJohannes Berg case RATE_INFO_BW_5:
42932103dec1SSimon Wunderlich shift = 2;
4294da6a4352SJohannes Berg break;
4295b51f3beeSJohannes Berg }
4296f4bda337SThomas Pedersen
4297f4bda337SThomas Pedersen sband = local->hw.wiphy->bands[status->band];
42982103dec1SSimon Wunderlich bitrate = sband->bitrates[status->rate_idx].bitrate;
42992103dec1SSimon Wunderlich ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
4300f4a0f0c5SJohannes Berg
4301f4a0f0c5SJohannes Berg if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
430257fbcce3SJohannes Berg if (status->band == NL80211_BAND_5GHZ) {
4303f4a0f0c5SJohannes Berg ts += 20 << shift;
4304f4a0f0c5SJohannes Berg mpdu_offset += 2;
43057fdd69c5SJohannes Berg } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) {
4306f4a0f0c5SJohannes Berg ts += 96;
4307f4a0f0c5SJohannes Berg } else {
4308f4a0f0c5SJohannes Berg ts += 192;
4309f4a0f0c5SJohannes Berg }
4310f4a0f0c5SJohannes Berg }
4311da6a4352SJohannes Berg break;
4312da6a4352SJohannes Berg }
4313f4bda337SThomas Pedersen }
4314f4bda337SThomas Pedersen
4315f4bda337SThomas Pedersen rate = cfg80211_calculate_bitrate(&ri);
4316d86aa4f8SJohannes Berg if (WARN_ONCE(!rate,
4317f980ebc0SSara Sharon "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n",
4318f980ebc0SSara Sharon (unsigned long long)status->flag, status->rate_idx,
43198613c948SJohannes Berg status->nss))
4320d86aa4f8SJohannes Berg return 0;
4321f4bda337SThomas Pedersen
4322f4bda337SThomas Pedersen /* rewind from end of MPDU */
4323f4bda337SThomas Pedersen if (status->flag & RX_FLAG_MACTIME_END)
4324f4bda337SThomas Pedersen ts -= mpdu_len * 8 * 10 / rate;
4325f4bda337SThomas Pedersen
4326f4bda337SThomas Pedersen ts += mpdu_offset * 8 * 10 / rate;
4327f4bda337SThomas Pedersen
4328f4bda337SThomas Pedersen return ts;
4329f4bda337SThomas Pedersen }
4330164eb02dSSimon Wunderlich
ieee80211_dfs_cac_cancel(struct ieee80211_local * local)4331164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
4332164eb02dSSimon Wunderlich {
4333164eb02dSSimon Wunderlich struct ieee80211_sub_if_data *sdata;
4334d2859df5SJanusz Dziedzic struct cfg80211_chan_def chandef;
4335164eb02dSSimon Wunderlich
43364a199068SJohannes Berg /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */
4337a05829a7SJohannes Berg lockdep_assert_wiphy(local->hw.wiphy);
43384a199068SJohannes Berg
433934a3740dSJohannes Berg mutex_lock(&local->mtx);
4340164eb02dSSimon Wunderlich list_for_each_entry(sdata, &local->interfaces, list) {
434134a3740dSJohannes Berg /* it might be waiting for the local->mtx, but then
434234a3740dSJohannes Berg * by the time it gets it, sdata->wdev.cac_started
434334a3740dSJohannes Berg * will no longer be true
434434a3740dSJohannes Berg */
4345bfd8403aSJohannes Berg cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
4346164eb02dSSimon Wunderlich
4347164eb02dSSimon Wunderlich if (sdata->wdev.cac_started) {
4348d2859df5SJanusz Dziedzic chandef = sdata->vif.bss_conf.chandef;
4349d8675a63SJohannes Berg ieee80211_link_release_channel(&sdata->deflink);
4350164eb02dSSimon Wunderlich cfg80211_cac_event(sdata->dev,
4351d2859df5SJanusz Dziedzic &chandef,
4352164eb02dSSimon Wunderlich NL80211_RADAR_CAC_ABORTED,
4353164eb02dSSimon Wunderlich GFP_KERNEL);
4354164eb02dSSimon Wunderlich }
4355164eb02dSSimon Wunderlich }
435634a3740dSJohannes Berg mutex_unlock(&local->mtx);
4357164eb02dSSimon Wunderlich }
4358164eb02dSSimon Wunderlich
ieee80211_dfs_radar_detected_work(struct wiphy * wiphy,struct wiphy_work * work)435974d64f0aSJohannes Berg void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
436074d64f0aSJohannes Berg struct wiphy_work *work)
4361164eb02dSSimon Wunderlich {
4362164eb02dSSimon Wunderlich struct ieee80211_local *local =
4363164eb02dSSimon Wunderlich container_of(work, struct ieee80211_local, radar_detected_work);
436484a3d1c9SJanusz Dziedzic struct cfg80211_chan_def chandef = local->hw.conf.chandef;
4365486cf4c0SMichal Kazior struct ieee80211_chanctx *ctx;
4366486cf4c0SMichal Kazior int num_chanctx = 0;
4367486cf4c0SMichal Kazior
4368486cf4c0SMichal Kazior mutex_lock(&local->chanctx_mtx);
4369486cf4c0SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) {
4370486cf4c0SMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
4371486cf4c0SMichal Kazior continue;
4372486cf4c0SMichal Kazior
4373486cf4c0SMichal Kazior num_chanctx++;
4374486cf4c0SMichal Kazior chandef = ctx->conf.def;
4375486cf4c0SMichal Kazior }
4376486cf4c0SMichal Kazior mutex_unlock(&local->chanctx_mtx);
4377164eb02dSSimon Wunderlich
4378164eb02dSSimon Wunderlich ieee80211_dfs_cac_cancel(local);
4379164eb02dSSimon Wunderlich
4380486cf4c0SMichal Kazior if (num_chanctx > 1)
4381486cf4c0SMichal Kazior /* XXX: multi-channel is not supported yet */
4382164eb02dSSimon Wunderlich WARN_ON(1);
438384a3d1c9SJanusz Dziedzic else
4384164eb02dSSimon Wunderlich cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
4385164eb02dSSimon Wunderlich }
4386164eb02dSSimon Wunderlich
ieee80211_radar_detected(struct ieee80211_hw * hw)4387164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw)
4388164eb02dSSimon Wunderlich {
4389164eb02dSSimon Wunderlich struct ieee80211_local *local = hw_to_local(hw);
4390164eb02dSSimon Wunderlich
4391164eb02dSSimon Wunderlich trace_api_radar_detected(local);
4392164eb02dSSimon Wunderlich
439374d64f0aSJohannes Berg wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
4394164eb02dSSimon Wunderlich }
4395164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected);
4396e6b7cde4SSimon Wunderlich
ieee80211_chandef_downgrade(struct cfg80211_chan_def * c)4397ba323e29SJohannes Berg ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
4398e6b7cde4SSimon Wunderlich {
4399ba323e29SJohannes Berg ieee80211_conn_flags_t ret;
4400e6b7cde4SSimon Wunderlich int tmp;
4401e6b7cde4SSimon Wunderlich
4402e6b7cde4SSimon Wunderlich switch (c->width) {
4403e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20:
4404e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT;
4405ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
4406e6b7cde4SSimon Wunderlich break;
4407e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_40:
4408e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20;
4409e6b7cde4SSimon Wunderlich c->center_freq1 = c->chan->center_freq;
4410ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_40MHZ |
4411ba323e29SJohannes Berg IEEE80211_CONN_DISABLE_VHT;
4412e6b7cde4SSimon Wunderlich break;
4413e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80:
4414e6b7cde4SSimon Wunderlich tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
4415e6b7cde4SSimon Wunderlich /* n_P40 */
4416e6b7cde4SSimon Wunderlich tmp /= 2;
4417e6b7cde4SSimon Wunderlich /* freq_P40 */
4418e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
4419e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_40;
4420ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_VHT;
4421e6b7cde4SSimon Wunderlich break;
4422e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_80P80:
4423e6b7cde4SSimon Wunderlich c->center_freq2 = 0;
4424e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80;
4425ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_80P80MHZ |
4426ba323e29SJohannes Berg IEEE80211_CONN_DISABLE_160MHZ;
4427e6b7cde4SSimon Wunderlich break;
4428e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_160:
4429e6b7cde4SSimon Wunderlich /* n_P20 */
4430e6b7cde4SSimon Wunderlich tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
4431e6b7cde4SSimon Wunderlich /* n_P80 */
4432e6b7cde4SSimon Wunderlich tmp /= 4;
4433e6b7cde4SSimon Wunderlich c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
4434e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_80;
4435ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_80P80MHZ |
4436ba323e29SJohannes Berg IEEE80211_CONN_DISABLE_160MHZ;
4437e6b7cde4SSimon Wunderlich break;
44385dca295dSIlan Peer case NL80211_CHAN_WIDTH_320:
44395dca295dSIlan Peer /* n_P20 */
44405dca295dSIlan Peer tmp = (150 + c->chan->center_freq - c->center_freq1) / 20;
44415dca295dSIlan Peer /* n_P160 */
444268608f99SMeiChia Chiu tmp /= 8;
44435dca295dSIlan Peer c->center_freq1 = c->center_freq1 - 80 + 160 * tmp;
44445dca295dSIlan Peer c->width = NL80211_CHAN_WIDTH_160;
4445ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_320MHZ;
44465dca295dSIlan Peer break;
4447e6b7cde4SSimon Wunderlich default:
4448e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_20_NOHT:
4449e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1);
4450e6b7cde4SSimon Wunderlich c->width = NL80211_CHAN_WIDTH_20_NOHT;
4451ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
4452e6b7cde4SSimon Wunderlich break;
4453df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_1:
4454df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_2:
4455df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_4:
4456df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_8:
4457df78a0c0SThomas Pedersen case NL80211_CHAN_WIDTH_16:
4458e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_5:
4459e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_10:
4460e6b7cde4SSimon Wunderlich WARN_ON_ONCE(1);
4461e6b7cde4SSimon Wunderlich /* keep c->width */
4462ba323e29SJohannes Berg ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
4463e6b7cde4SSimon Wunderlich break;
4464e6b7cde4SSimon Wunderlich }
4465e6b7cde4SSimon Wunderlich
4466e6b7cde4SSimon Wunderlich WARN_ON_ONCE(!cfg80211_chandef_valid(c));
4467e6b7cde4SSimon Wunderlich
4468e6b7cde4SSimon Wunderlich return ret;
4469e6b7cde4SSimon Wunderlich }
4470687da132SEmmanuel Grumbach
4471687da132SEmmanuel Grumbach /*
4472687da132SEmmanuel Grumbach * Returns true if smps_mode_new is strictly more restrictive than
4473687da132SEmmanuel Grumbach * smps_mode_old.
4474687da132SEmmanuel Grumbach */
ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,enum ieee80211_smps_mode smps_mode_new)4475687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
4476687da132SEmmanuel Grumbach enum ieee80211_smps_mode smps_mode_new)
4477687da132SEmmanuel Grumbach {
4478687da132SEmmanuel Grumbach if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
4479687da132SEmmanuel Grumbach smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
4480687da132SEmmanuel Grumbach return false;
4481687da132SEmmanuel Grumbach
4482687da132SEmmanuel Grumbach switch (smps_mode_old) {
4483687da132SEmmanuel Grumbach case IEEE80211_SMPS_STATIC:
4484687da132SEmmanuel Grumbach return false;
4485687da132SEmmanuel Grumbach case IEEE80211_SMPS_DYNAMIC:
4486687da132SEmmanuel Grumbach return smps_mode_new == IEEE80211_SMPS_STATIC;
4487687da132SEmmanuel Grumbach case IEEE80211_SMPS_OFF:
4488687da132SEmmanuel Grumbach return smps_mode_new != IEEE80211_SMPS_OFF;
4489687da132SEmmanuel Grumbach default:
4490687da132SEmmanuel Grumbach WARN_ON(1);
4491687da132SEmmanuel Grumbach }
4492687da132SEmmanuel Grumbach
4493687da132SEmmanuel Grumbach return false;
4494687da132SEmmanuel Grumbach }
4495c6da674aSChun-Yeow Yeoh
ieee80211_send_action_csa(struct ieee80211_sub_if_data * sdata,struct cfg80211_csa_settings * csa_settings)4496c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
4497c6da674aSChun-Yeow Yeoh struct cfg80211_csa_settings *csa_settings)
4498c6da674aSChun-Yeow Yeoh {
4499c6da674aSChun-Yeow Yeoh struct sk_buff *skb;
4500c6da674aSChun-Yeow Yeoh struct ieee80211_mgmt *mgmt;
4501c6da674aSChun-Yeow Yeoh struct ieee80211_local *local = sdata->local;
4502c6da674aSChun-Yeow Yeoh int freq;
45034c121fd6SJohannes Berg int hdr_len = offsetofend(struct ieee80211_mgmt,
45044c121fd6SJohannes Berg u.action.u.chan_switch);
4505c6da674aSChun-Yeow Yeoh u8 *pos;
4506c6da674aSChun-Yeow Yeoh
4507c6da674aSChun-Yeow Yeoh if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
4508c6da674aSChun-Yeow Yeoh sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
4509c6da674aSChun-Yeow Yeoh return -EOPNOTSUPP;
4510c6da674aSChun-Yeow Yeoh
4511c6da674aSChun-Yeow Yeoh skb = dev_alloc_skb(local->tx_headroom + hdr_len +
4512c6da674aSChun-Yeow Yeoh 5 + /* channel switch announcement element */
4513c6da674aSChun-Yeow Yeoh 3 + /* secondary channel offset element */
451475d627d5SSimon Wunderlich 5 + /* wide bandwidth channel switch announcement */
4515c6da674aSChun-Yeow Yeoh 8); /* mesh channel switch parameters element */
4516c6da674aSChun-Yeow Yeoh if (!skb)
4517c6da674aSChun-Yeow Yeoh return -ENOMEM;
4518c6da674aSChun-Yeow Yeoh
4519c6da674aSChun-Yeow Yeoh skb_reserve(skb, local->tx_headroom);
4520b080db58SJohannes Berg mgmt = skb_put_zero(skb, hdr_len);
4521c6da674aSChun-Yeow Yeoh mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
4522c6da674aSChun-Yeow Yeoh IEEE80211_STYPE_ACTION);
4523c6da674aSChun-Yeow Yeoh
4524c6da674aSChun-Yeow Yeoh eth_broadcast_addr(mgmt->da);
4525c6da674aSChun-Yeow Yeoh memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
4526c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) {
4527c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
4528c6da674aSChun-Yeow Yeoh } else {
4529c6da674aSChun-Yeow Yeoh struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
4530c6da674aSChun-Yeow Yeoh memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
4531c6da674aSChun-Yeow Yeoh }
4532c6da674aSChun-Yeow Yeoh mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
4533c6da674aSChun-Yeow Yeoh mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
4534c6da674aSChun-Yeow Yeoh pos = skb_put(skb, 5);
4535c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
4536c6da674aSChun-Yeow Yeoh *pos++ = 3; /* IE length */
4537c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
4538c6da674aSChun-Yeow Yeoh freq = csa_settings->chandef.chan->center_freq;
4539c6da674aSChun-Yeow Yeoh *pos++ = ieee80211_frequency_to_channel(freq); /* channel */
4540c6da674aSChun-Yeow Yeoh *pos++ = csa_settings->count; /* count */
4541c6da674aSChun-Yeow Yeoh
4542c6da674aSChun-Yeow Yeoh if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
4543c6da674aSChun-Yeow Yeoh enum nl80211_channel_type ch_type;
4544c6da674aSChun-Yeow Yeoh
4545c6da674aSChun-Yeow Yeoh skb_put(skb, 3);
4546c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
4547c6da674aSChun-Yeow Yeoh *pos++ = 1; /* IE length */
4548c6da674aSChun-Yeow Yeoh ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
4549c6da674aSChun-Yeow Yeoh if (ch_type == NL80211_CHAN_HT40PLUS)
4550c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
4551c6da674aSChun-Yeow Yeoh else
4552c6da674aSChun-Yeow Yeoh *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
4553c6da674aSChun-Yeow Yeoh }
4554c6da674aSChun-Yeow Yeoh
4555c6da674aSChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sdata->vif)) {
4556c6da674aSChun-Yeow Yeoh struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
4557c6da674aSChun-Yeow Yeoh
4558c6da674aSChun-Yeow Yeoh skb_put(skb, 8);
4559c6da674aSChun-Yeow Yeoh *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */
4560c6da674aSChun-Yeow Yeoh *pos++ = 6; /* IE length */
4561c6da674aSChun-Yeow Yeoh *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */
4562c6da674aSChun-Yeow Yeoh *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */
4563c6da674aSChun-Yeow Yeoh *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
4564c6da674aSChun-Yeow Yeoh *pos++ |= csa_settings->block_tx ?
4565c6da674aSChun-Yeow Yeoh WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
4566c6da674aSChun-Yeow Yeoh put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
4567c6da674aSChun-Yeow Yeoh pos += 2;
4568ca91dc97SChun-Yeow Yeoh put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */
4569c6da674aSChun-Yeow Yeoh pos += 2;
4570c6da674aSChun-Yeow Yeoh }
4571c6da674aSChun-Yeow Yeoh
457275d627d5SSimon Wunderlich if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 ||
457375d627d5SSimon Wunderlich csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 ||
457475d627d5SSimon Wunderlich csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) {
457575d627d5SSimon Wunderlich skb_put(skb, 5);
457675d627d5SSimon Wunderlich ieee80211_ie_build_wide_bw_cs(pos, &csa_settings->chandef);
457775d627d5SSimon Wunderlich }
457875d627d5SSimon Wunderlich
4579c6da674aSChun-Yeow Yeoh ieee80211_tx_skb(sdata, skb);
4580c6da674aSChun-Yeow Yeoh return 0;
4581c6da674aSChun-Yeow Yeoh }
45822475b1ccSMax Stepanov
4583a7022e65SFelix Fietkau static bool
ieee80211_extend_noa_desc(struct ieee80211_noa_data * data,u32 tsf,int i)4584a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
4585a7022e65SFelix Fietkau {
4586a7022e65SFelix Fietkau s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
4587a7022e65SFelix Fietkau int skip;
4588a7022e65SFelix Fietkau
4589a7022e65SFelix Fietkau if (end > 0)
4590a7022e65SFelix Fietkau return false;
4591a7022e65SFelix Fietkau
4592519ee691SJanusz.Dziedzic@tieto.com /* One shot NOA */
4593519ee691SJanusz.Dziedzic@tieto.com if (data->count[i] == 1)
4594519ee691SJanusz.Dziedzic@tieto.com return false;
4595519ee691SJanusz.Dziedzic@tieto.com
4596519ee691SJanusz.Dziedzic@tieto.com if (data->desc[i].interval == 0)
4597519ee691SJanusz.Dziedzic@tieto.com return false;
4598519ee691SJanusz.Dziedzic@tieto.com
4599a7022e65SFelix Fietkau /* End time is in the past, check for repetitions */
4600a7022e65SFelix Fietkau skip = DIV_ROUND_UP(-end, data->desc[i].interval);
4601a7022e65SFelix Fietkau if (data->count[i] < 255) {
4602a7022e65SFelix Fietkau if (data->count[i] <= skip) {
4603a7022e65SFelix Fietkau data->count[i] = 0;
4604a7022e65SFelix Fietkau return false;
4605a7022e65SFelix Fietkau }
4606a7022e65SFelix Fietkau
4607a7022e65SFelix Fietkau data->count[i] -= skip;
4608a7022e65SFelix Fietkau }
4609a7022e65SFelix Fietkau
4610a7022e65SFelix Fietkau data->desc[i].start += skip * data->desc[i].interval;
4611a7022e65SFelix Fietkau
4612a7022e65SFelix Fietkau return true;
4613a7022e65SFelix Fietkau }
4614a7022e65SFelix Fietkau
4615a7022e65SFelix Fietkau static bool
ieee80211_extend_absent_time(struct ieee80211_noa_data * data,u32 tsf,s32 * offset)4616a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
4617a7022e65SFelix Fietkau s32 *offset)
4618a7022e65SFelix Fietkau {
4619a7022e65SFelix Fietkau bool ret = false;
4620a7022e65SFelix Fietkau int i;
4621a7022e65SFelix Fietkau
4622a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
4623a7022e65SFelix Fietkau s32 cur;
4624a7022e65SFelix Fietkau
4625a7022e65SFelix Fietkau if (!data->count[i])
4626a7022e65SFelix Fietkau continue;
4627a7022e65SFelix Fietkau
4628a7022e65SFelix Fietkau if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
4629a7022e65SFelix Fietkau ret = true;
4630a7022e65SFelix Fietkau
4631a7022e65SFelix Fietkau cur = data->desc[i].start - tsf;
4632a7022e65SFelix Fietkau if (cur > *offset)
4633a7022e65SFelix Fietkau continue;
4634a7022e65SFelix Fietkau
4635a7022e65SFelix Fietkau cur = data->desc[i].start + data->desc[i].duration - tsf;
4636a7022e65SFelix Fietkau if (cur > *offset)
4637a7022e65SFelix Fietkau *offset = cur;
4638a7022e65SFelix Fietkau }
4639a7022e65SFelix Fietkau
4640a7022e65SFelix Fietkau return ret;
4641a7022e65SFelix Fietkau }
4642a7022e65SFelix Fietkau
4643a7022e65SFelix Fietkau static u32
ieee80211_get_noa_absent_time(struct ieee80211_noa_data * data,u32 tsf)4644a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
4645a7022e65SFelix Fietkau {
4646a7022e65SFelix Fietkau s32 offset = 0;
4647a7022e65SFelix Fietkau int tries = 0;
4648a7022e65SFelix Fietkau /*
4649a7022e65SFelix Fietkau * arbitrary limit, used to avoid infinite loops when combined NoA
4650a7022e65SFelix Fietkau * descriptors cover the full time period.
4651a7022e65SFelix Fietkau */
4652a7022e65SFelix Fietkau int max_tries = 5;
4653a7022e65SFelix Fietkau
4654a7022e65SFelix Fietkau ieee80211_extend_absent_time(data, tsf, &offset);
4655a7022e65SFelix Fietkau do {
4656a7022e65SFelix Fietkau if (!ieee80211_extend_absent_time(data, tsf, &offset))
4657a7022e65SFelix Fietkau break;
4658a7022e65SFelix Fietkau
4659a7022e65SFelix Fietkau tries++;
4660a7022e65SFelix Fietkau } while (tries < max_tries);
4661a7022e65SFelix Fietkau
4662a7022e65SFelix Fietkau return offset;
4663a7022e65SFelix Fietkau }
4664a7022e65SFelix Fietkau
ieee80211_update_p2p_noa(struct ieee80211_noa_data * data,u32 tsf)4665a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
4666a7022e65SFelix Fietkau {
4667a7022e65SFelix Fietkau u32 next_offset = BIT(31) - 1;
4668a7022e65SFelix Fietkau int i;
4669a7022e65SFelix Fietkau
4670a7022e65SFelix Fietkau data->absent = 0;
4671a7022e65SFelix Fietkau data->has_next_tsf = false;
4672a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
4673a7022e65SFelix Fietkau s32 start;
4674a7022e65SFelix Fietkau
4675a7022e65SFelix Fietkau if (!data->count[i])
4676a7022e65SFelix Fietkau continue;
4677a7022e65SFelix Fietkau
4678a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i);
4679a7022e65SFelix Fietkau start = data->desc[i].start - tsf;
4680a7022e65SFelix Fietkau if (start <= 0)
4681a7022e65SFelix Fietkau data->absent |= BIT(i);
4682a7022e65SFelix Fietkau
4683a7022e65SFelix Fietkau if (next_offset > start)
4684a7022e65SFelix Fietkau next_offset = start;
4685a7022e65SFelix Fietkau
4686a7022e65SFelix Fietkau data->has_next_tsf = true;
4687a7022e65SFelix Fietkau }
4688a7022e65SFelix Fietkau
4689a7022e65SFelix Fietkau if (data->absent)
4690a7022e65SFelix Fietkau next_offset = ieee80211_get_noa_absent_time(data, tsf);
4691a7022e65SFelix Fietkau
4692a7022e65SFelix Fietkau data->next_tsf = tsf + next_offset;
4693a7022e65SFelix Fietkau }
4694a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa);
4695a7022e65SFelix Fietkau
ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr * attr,struct ieee80211_noa_data * data,u32 tsf)4696a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
4697a7022e65SFelix Fietkau struct ieee80211_noa_data *data, u32 tsf)
4698a7022e65SFelix Fietkau {
4699a7022e65SFelix Fietkau int ret = 0;
4700a7022e65SFelix Fietkau int i;
4701a7022e65SFelix Fietkau
4702a7022e65SFelix Fietkau memset(data, 0, sizeof(*data));
4703a7022e65SFelix Fietkau
4704a7022e65SFelix Fietkau for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
4705a7022e65SFelix Fietkau const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
4706a7022e65SFelix Fietkau
4707a7022e65SFelix Fietkau if (!desc->count || !desc->duration)
4708a7022e65SFelix Fietkau continue;
4709a7022e65SFelix Fietkau
4710a7022e65SFelix Fietkau data->count[i] = desc->count;
4711a7022e65SFelix Fietkau data->desc[i].start = le32_to_cpu(desc->start_time);
4712a7022e65SFelix Fietkau data->desc[i].duration = le32_to_cpu(desc->duration);
4713a7022e65SFelix Fietkau data->desc[i].interval = le32_to_cpu(desc->interval);
4714a7022e65SFelix Fietkau
4715a7022e65SFelix Fietkau if (data->count[i] > 1 &&
4716a7022e65SFelix Fietkau data->desc[i].interval < data->desc[i].duration)
4717a7022e65SFelix Fietkau continue;
4718a7022e65SFelix Fietkau
4719a7022e65SFelix Fietkau ieee80211_extend_noa_desc(data, tsf, i);
4720a7022e65SFelix Fietkau ret++;
4721a7022e65SFelix Fietkau }
4722a7022e65SFelix Fietkau
4723a7022e65SFelix Fietkau if (ret)
4724a7022e65SFelix Fietkau ieee80211_update_p2p_noa(data, tsf);
4725a7022e65SFelix Fietkau
4726a7022e65SFelix Fietkau return ret;
4727a7022e65SFelix Fietkau }
4728a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
4729057d5f4bSThomas Pedersen
ieee80211_recalc_dtim(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)4730057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local,
4731057d5f4bSThomas Pedersen struct ieee80211_sub_if_data *sdata)
4732057d5f4bSThomas Pedersen {
4733057d5f4bSThomas Pedersen u64 tsf = drv_get_tsf(local, sdata);
4734057d5f4bSThomas Pedersen u64 dtim_count = 0;
4735057d5f4bSThomas Pedersen u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
4736057d5f4bSThomas Pedersen u8 dtim_period = sdata->vif.bss_conf.dtim_period;
4737057d5f4bSThomas Pedersen struct ps_data *ps;
4738057d5f4bSThomas Pedersen u8 bcns_from_dtim;
4739057d5f4bSThomas Pedersen
4740057d5f4bSThomas Pedersen if (tsf == -1ULL || !beacon_int || !dtim_period)
4741057d5f4bSThomas Pedersen return;
4742057d5f4bSThomas Pedersen
4743057d5f4bSThomas Pedersen if (sdata->vif.type == NL80211_IFTYPE_AP ||
4744057d5f4bSThomas Pedersen sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
4745057d5f4bSThomas Pedersen if (!sdata->bss)
4746057d5f4bSThomas Pedersen return;
4747057d5f4bSThomas Pedersen
4748057d5f4bSThomas Pedersen ps = &sdata->bss->ps;
4749057d5f4bSThomas Pedersen } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
4750057d5f4bSThomas Pedersen ps = &sdata->u.mesh.ps;
4751057d5f4bSThomas Pedersen } else {
4752057d5f4bSThomas Pedersen return;
4753057d5f4bSThomas Pedersen }
4754057d5f4bSThomas Pedersen
4755057d5f4bSThomas Pedersen /*
4756057d5f4bSThomas Pedersen * actually finds last dtim_count, mac80211 will update in
4757057d5f4bSThomas Pedersen * __beacon_add_tim().
4758057d5f4bSThomas Pedersen * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
4759057d5f4bSThomas Pedersen */
4760057d5f4bSThomas Pedersen do_div(tsf, beacon_int);
4761057d5f4bSThomas Pedersen bcns_from_dtim = do_div(tsf, dtim_period);
4762057d5f4bSThomas Pedersen /* just had a DTIM */
4763057d5f4bSThomas Pedersen if (!bcns_from_dtim)
4764057d5f4bSThomas Pedersen dtim_count = 0;
4765057d5f4bSThomas Pedersen else
4766057d5f4bSThomas Pedersen dtim_count = dtim_period - bcns_from_dtim;
4767057d5f4bSThomas Pedersen
4768057d5f4bSThomas Pedersen ps->dtim_count = dtim_count;
4769057d5f4bSThomas Pedersen }
477073de86a3SLuciano Coelho
ieee80211_chanctx_radar_detect(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)477171e6195eSMichal Kazior static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
477271e6195eSMichal Kazior struct ieee80211_chanctx *ctx)
477371e6195eSMichal Kazior {
4774b4f85443SJohannes Berg struct ieee80211_link_data *link;
477571e6195eSMichal Kazior u8 radar_detect = 0;
477671e6195eSMichal Kazior
477771e6195eSMichal Kazior lockdep_assert_held(&local->chanctx_mtx);
477871e6195eSMichal Kazior
477971e6195eSMichal Kazior if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
478071e6195eSMichal Kazior return 0;
478171e6195eSMichal Kazior
4782b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list)
4783b4f85443SJohannes Berg if (link->reserved_radar_required)
4784b4f85443SJohannes Berg radar_detect |= BIT(link->reserved_chandef.width);
478571e6195eSMichal Kazior
478671e6195eSMichal Kazior /*
478771e6195eSMichal Kazior * An in-place reservation context should not have any assigned vifs
478871e6195eSMichal Kazior * until it replaces the other context.
478971e6195eSMichal Kazior */
479071e6195eSMichal Kazior WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
4791b4f85443SJohannes Berg !list_empty(&ctx->assigned_links));
479271e6195eSMichal Kazior
4793b4f85443SJohannes Berg list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) {
4794b4f85443SJohannes Berg if (!link->radar_required)
4795b4f85443SJohannes Berg continue;
4796b4f85443SJohannes Berg
4797b4f85443SJohannes Berg radar_detect |=
4798d8675a63SJohannes Berg BIT(link->conf->chandef.width);
4799b4f85443SJohannes Berg }
480071e6195eSMichal Kazior
480171e6195eSMichal Kazior return radar_detect;
480271e6195eSMichal Kazior }
480371e6195eSMichal Kazior
ieee80211_check_combinations(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode chanmode,u8 radar_detect)480473de86a3SLuciano Coelho int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
480573de86a3SLuciano Coelho const struct cfg80211_chan_def *chandef,
480673de86a3SLuciano Coelho enum ieee80211_chanctx_mode chanmode,
480773de86a3SLuciano Coelho u8 radar_detect)
480873de86a3SLuciano Coelho {
480973de86a3SLuciano Coelho struct ieee80211_local *local = sdata->local;
481073de86a3SLuciano Coelho struct ieee80211_sub_if_data *sdata_iter;
481173de86a3SLuciano Coelho enum nl80211_iftype iftype = sdata->wdev.iftype;
481273de86a3SLuciano Coelho struct ieee80211_chanctx *ctx;
481373de86a3SLuciano Coelho int total = 1;
4814e227300cSPurushottam Kushwaha struct iface_combination_params params = {
4815e227300cSPurushottam Kushwaha .radar_detect = radar_detect,
4816e227300cSPurushottam Kushwaha };
481773de86a3SLuciano Coelho
481873de86a3SLuciano Coelho lockdep_assert_held(&local->chanctx_mtx);
481973de86a3SLuciano Coelho
482073de86a3SLuciano Coelho if (WARN_ON(hweight32(radar_detect) > 1))
482173de86a3SLuciano Coelho return -EINVAL;
482273de86a3SLuciano Coelho
4823b6a55015SLuciano Coelho if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
4824b6a55015SLuciano Coelho !chandef->chan))
482573de86a3SLuciano Coelho return -EINVAL;
482673de86a3SLuciano Coelho
482773de86a3SLuciano Coelho if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
482873de86a3SLuciano Coelho return -EINVAL;
482973de86a3SLuciano Coelho
4830ac668afeSJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_AP ||
4831ac668afeSJohannes Berg sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
4832ac668afeSJohannes Berg /*
4833ac668afeSJohannes Berg * always passing this is harmless, since it'll be the
4834ac668afeSJohannes Berg * same value that cfg80211 finds if it finds the same
4835ac668afeSJohannes Berg * interface ... and that's always allowed
4836ac668afeSJohannes Berg */
4837ac668afeSJohannes Berg params.new_beacon_int = sdata->vif.bss_conf.beacon_int;
4838ac668afeSJohannes Berg }
4839ac668afeSJohannes Berg
484073de86a3SLuciano Coelho /* Always allow software iftypes */
4841e6f40511SManikanta Pubbisetty if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) {
484273de86a3SLuciano Coelho if (radar_detect)
484373de86a3SLuciano Coelho return -EINVAL;
484473de86a3SLuciano Coelho return 0;
484573de86a3SLuciano Coelho }
484673de86a3SLuciano Coelho
4847e227300cSPurushottam Kushwaha if (chandef)
4848e227300cSPurushottam Kushwaha params.num_different_channels = 1;
484973de86a3SLuciano Coelho
485073de86a3SLuciano Coelho if (iftype != NL80211_IFTYPE_UNSPECIFIED)
4851e227300cSPurushottam Kushwaha params.iftype_num[iftype] = 1;
485273de86a3SLuciano Coelho
485373de86a3SLuciano Coelho list_for_each_entry(ctx, &local->chanctx_list, list) {
48545bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
48555bcae31dSMichal Kazior continue;
4856e227300cSPurushottam Kushwaha params.radar_detect |=
4857e227300cSPurushottam Kushwaha ieee80211_chanctx_radar_detect(local, ctx);
485873de86a3SLuciano Coelho if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
4859e227300cSPurushottam Kushwaha params.num_different_channels++;
486073de86a3SLuciano Coelho continue;
486173de86a3SLuciano Coelho }
4862b6a55015SLuciano Coelho if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
486373de86a3SLuciano Coelho cfg80211_chandef_compatible(chandef,
486473de86a3SLuciano Coelho &ctx->conf.def))
486573de86a3SLuciano Coelho continue;
4866e227300cSPurushottam Kushwaha params.num_different_channels++;
486773de86a3SLuciano Coelho }
486873de86a3SLuciano Coelho
486973de86a3SLuciano Coelho list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
487073de86a3SLuciano Coelho struct wireless_dev *wdev_iter;
487173de86a3SLuciano Coelho
487273de86a3SLuciano Coelho wdev_iter = &sdata_iter->wdev;
487373de86a3SLuciano Coelho
487473de86a3SLuciano Coelho if (sdata_iter == sdata ||
48750f611d28SAndrei Otcheretianski !ieee80211_sdata_running(sdata_iter) ||
4876e6f40511SManikanta Pubbisetty cfg80211_iftype_allowed(local->hw.wiphy,
4877e6f40511SManikanta Pubbisetty wdev_iter->iftype, 0, 1))
487873de86a3SLuciano Coelho continue;
487973de86a3SLuciano Coelho
4880e227300cSPurushottam Kushwaha params.iftype_num[wdev_iter->iftype]++;
488173de86a3SLuciano Coelho total++;
488273de86a3SLuciano Coelho }
488373de86a3SLuciano Coelho
4884e227300cSPurushottam Kushwaha if (total == 1 && !params.radar_detect)
488573de86a3SLuciano Coelho return 0;
488673de86a3SLuciano Coelho
4887e227300cSPurushottam Kushwaha return cfg80211_check_combinations(local->hw.wiphy, ¶ms);
488873de86a3SLuciano Coelho }
48896fa001bcSMichal Kazior
48906fa001bcSMichal Kazior static void
ieee80211_iter_max_chans(const struct ieee80211_iface_combination * c,void * data)48916fa001bcSMichal Kazior ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
48926fa001bcSMichal Kazior void *data)
48936fa001bcSMichal Kazior {
48946fa001bcSMichal Kazior u32 *max_num_different_channels = data;
48956fa001bcSMichal Kazior
48966fa001bcSMichal Kazior *max_num_different_channels = max(*max_num_different_channels,
48976fa001bcSMichal Kazior c->num_different_channels);
48986fa001bcSMichal Kazior }
48996fa001bcSMichal Kazior
ieee80211_max_num_channels(struct ieee80211_local * local)49006fa001bcSMichal Kazior int ieee80211_max_num_channels(struct ieee80211_local *local)
49016fa001bcSMichal Kazior {
49026fa001bcSMichal Kazior struct ieee80211_sub_if_data *sdata;
49036fa001bcSMichal Kazior struct ieee80211_chanctx *ctx;
49046fa001bcSMichal Kazior u32 max_num_different_channels = 1;
49056fa001bcSMichal Kazior int err;
4906e227300cSPurushottam Kushwaha struct iface_combination_params params = {0};
49076fa001bcSMichal Kazior
49086fa001bcSMichal Kazior lockdep_assert_held(&local->chanctx_mtx);
49096fa001bcSMichal Kazior
49106fa001bcSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) {
49115bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
49125bcae31dSMichal Kazior continue;
49135bcae31dSMichal Kazior
4914e227300cSPurushottam Kushwaha params.num_different_channels++;
49156fa001bcSMichal Kazior
4916e227300cSPurushottam Kushwaha params.radar_detect |=
4917e227300cSPurushottam Kushwaha ieee80211_chanctx_radar_detect(local, ctx);
49186fa001bcSMichal Kazior }
49196fa001bcSMichal Kazior
49206fa001bcSMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list)
4921e227300cSPurushottam Kushwaha params.iftype_num[sdata->wdev.iftype]++;
49226fa001bcSMichal Kazior
4923e227300cSPurushottam Kushwaha err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms,
4924e227300cSPurushottam Kushwaha ieee80211_iter_max_chans,
49256fa001bcSMichal Kazior &max_num_different_channels);
49266fa001bcSMichal Kazior if (err < 0)
49276fa001bcSMichal Kazior return err;
49286fa001bcSMichal Kazior
49296fa001bcSMichal Kazior return max_num_different_channels;
49306fa001bcSMichal Kazior }
493140b861a0SArik Nemtsov
ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data * sdata,struct ieee80211_sta_s1g_cap * caps,struct sk_buff * skb)49327957c6c8SThomas Pedersen void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
49337957c6c8SThomas Pedersen struct ieee80211_sta_s1g_cap *caps,
49347957c6c8SThomas Pedersen struct sk_buff *skb)
49357957c6c8SThomas Pedersen {
49367957c6c8SThomas Pedersen struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
49377957c6c8SThomas Pedersen struct ieee80211_s1g_cap s1g_capab;
49387957c6c8SThomas Pedersen u8 *pos;
49397957c6c8SThomas Pedersen int i;
49407957c6c8SThomas Pedersen
49417957c6c8SThomas Pedersen if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
49427957c6c8SThomas Pedersen return;
49437957c6c8SThomas Pedersen
49447957c6c8SThomas Pedersen if (!caps->s1g)
49457957c6c8SThomas Pedersen return;
49467957c6c8SThomas Pedersen
49477957c6c8SThomas Pedersen memcpy(s1g_capab.capab_info, caps->cap, sizeof(caps->cap));
49487957c6c8SThomas Pedersen memcpy(s1g_capab.supp_mcs_nss, caps->nss_mcs, sizeof(caps->nss_mcs));
49497957c6c8SThomas Pedersen
49507957c6c8SThomas Pedersen /* override the capability info */
49517957c6c8SThomas Pedersen for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) {
49527957c6c8SThomas Pedersen u8 mask = ifmgd->s1g_capa_mask.capab_info[i];
49537957c6c8SThomas Pedersen
49547957c6c8SThomas Pedersen s1g_capab.capab_info[i] &= ~mask;
49557957c6c8SThomas Pedersen s1g_capab.capab_info[i] |= ifmgd->s1g_capa.capab_info[i] & mask;
49567957c6c8SThomas Pedersen }
49577957c6c8SThomas Pedersen
49587957c6c8SThomas Pedersen /* then MCS and NSS set */
49597957c6c8SThomas Pedersen for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) {
49607957c6c8SThomas Pedersen u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i];
49617957c6c8SThomas Pedersen
49627957c6c8SThomas Pedersen s1g_capab.supp_mcs_nss[i] &= ~mask;
49637957c6c8SThomas Pedersen s1g_capab.supp_mcs_nss[i] |=
49647957c6c8SThomas Pedersen ifmgd->s1g_capa.supp_mcs_nss[i] & mask;
49657957c6c8SThomas Pedersen }
49667957c6c8SThomas Pedersen
49677957c6c8SThomas Pedersen pos = skb_put(skb, 2 + sizeof(s1g_capab));
49687957c6c8SThomas Pedersen *pos++ = WLAN_EID_S1G_CAPABILITIES;
49697957c6c8SThomas Pedersen *pos++ = sizeof(s1g_capab);
49707957c6c8SThomas Pedersen
49717957c6c8SThomas Pedersen memcpy(pos, &s1g_capab, sizeof(s1g_capab));
49727957c6c8SThomas Pedersen }
49737957c6c8SThomas Pedersen
ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)49741d00ce80SThomas Pedersen void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata,
49751d00ce80SThomas Pedersen struct sk_buff *skb)
49761d00ce80SThomas Pedersen {
49771d00ce80SThomas Pedersen u8 *pos = skb_put(skb, 3);
49781d00ce80SThomas Pedersen
49791d00ce80SThomas Pedersen *pos++ = WLAN_EID_AID_REQUEST;
49801d00ce80SThomas Pedersen *pos++ = 1;
49811d00ce80SThomas Pedersen *pos++ = 0;
49821d00ce80SThomas Pedersen }
49831d00ce80SThomas Pedersen
ieee80211_add_wmm_info_ie(u8 * buf,u8 qosinfo)498440b861a0SArik Nemtsov u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
498540b861a0SArik Nemtsov {
498640b861a0SArik Nemtsov *buf++ = WLAN_EID_VENDOR_SPECIFIC;
498740b861a0SArik Nemtsov *buf++ = 7; /* len */
498840b861a0SArik Nemtsov *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
498940b861a0SArik Nemtsov *buf++ = 0x50;
499040b861a0SArik Nemtsov *buf++ = 0xf2;
499140b861a0SArik Nemtsov *buf++ = 2; /* WME */
499240b861a0SArik Nemtsov *buf++ = 0; /* WME info */
499340b861a0SArik Nemtsov *buf++ = 1; /* WME ver */
499440b861a0SArik Nemtsov *buf++ = qosinfo; /* U-APSD no in use */
499540b861a0SArik Nemtsov
499640b861a0SArik Nemtsov return buf;
499740b861a0SArik Nemtsov }
4998ba8c3d6fSFelix Fietkau
ieee80211_txq_get_depth(struct ieee80211_txq * txq,unsigned long * frame_cnt,unsigned long * byte_cnt)4999f2ac7e30SMichal Kazior void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
5000f2ac7e30SMichal Kazior unsigned long *frame_cnt,
5001f2ac7e30SMichal Kazior unsigned long *byte_cnt)
5002f2ac7e30SMichal Kazior {
5003f2ac7e30SMichal Kazior struct txq_info *txqi = to_txq_info(txq);
5004bb42f2d1SToke Høiland-Jørgensen u32 frag_cnt = 0, frag_bytes = 0;
5005bb42f2d1SToke Høiland-Jørgensen struct sk_buff *skb;
5006bb42f2d1SToke Høiland-Jørgensen
5007bb42f2d1SToke Høiland-Jørgensen skb_queue_walk(&txqi->frags, skb) {
5008bb42f2d1SToke Høiland-Jørgensen frag_cnt++;
5009bb42f2d1SToke Høiland-Jørgensen frag_bytes += skb->len;
5010bb42f2d1SToke Høiland-Jørgensen }
5011f2ac7e30SMichal Kazior
5012f2ac7e30SMichal Kazior if (frame_cnt)
5013bb42f2d1SToke Høiland-Jørgensen *frame_cnt = txqi->tin.backlog_packets + frag_cnt;
5014f2ac7e30SMichal Kazior
5015f2ac7e30SMichal Kazior if (byte_cnt)
5016bb42f2d1SToke Høiland-Jørgensen *byte_cnt = txqi->tin.backlog_bytes + frag_bytes;
5017f2ac7e30SMichal Kazior }
5018f2ac7e30SMichal Kazior EXPORT_SYMBOL(ieee80211_txq_get_depth);
5019f438ceb8SEmmanuel Grumbach
5020f438ceb8SEmmanuel Grumbach const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = {
5021f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
5022f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
5023f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
5024f438ceb8SEmmanuel Grumbach IEEE80211_WMM_IE_STA_QOSINFO_AC_BK
5025f438ceb8SEmmanuel Grumbach };
502605d10957SThomas Pedersen
ieee80211_encode_usf(int listen_interval)502705d10957SThomas Pedersen u16 ieee80211_encode_usf(int listen_interval)
502805d10957SThomas Pedersen {
502905d10957SThomas Pedersen static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
503005d10957SThomas Pedersen u16 ui, usf = 0;
503105d10957SThomas Pedersen
503205d10957SThomas Pedersen /* find greatest USF */
503305d10957SThomas Pedersen while (usf < IEEE80211_MAX_USF) {
503405d10957SThomas Pedersen if (listen_interval % listen_int_usf[usf + 1])
503505d10957SThomas Pedersen break;
503605d10957SThomas Pedersen usf += 1;
503705d10957SThomas Pedersen }
503805d10957SThomas Pedersen ui = listen_interval / listen_int_usf[usf];
503905d10957SThomas Pedersen
504005d10957SThomas Pedersen /* error if there is a remainder. Should've been checked by user */
504105d10957SThomas Pedersen WARN_ON_ONCE(ui > IEEE80211_MAX_UI);
504205d10957SThomas Pedersen listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) |
504305d10957SThomas Pedersen FIELD_PREP(LISTEN_INT_UI, ui);
504405d10957SThomas Pedersen
504505d10957SThomas Pedersen return (u16) listen_interval;
504605d10957SThomas Pedersen }
5047820acc81SIlan Peer
ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data * sdata,u8 iftype)5048820acc81SIlan Peer u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
5049820acc81SIlan Peer {
5050820acc81SIlan Peer const struct ieee80211_sta_he_cap *he_cap;
5051820acc81SIlan Peer const struct ieee80211_sta_eht_cap *eht_cap;
5052820acc81SIlan Peer struct ieee80211_supported_band *sband;
5053ea5cba26SJohannes Berg bool is_ap;
5054820acc81SIlan Peer u8 n;
5055820acc81SIlan Peer
5056820acc81SIlan Peer sband = ieee80211_get_sband(sdata);
5057820acc81SIlan Peer if (!sband)
5058820acc81SIlan Peer return 0;
5059820acc81SIlan Peer
5060820acc81SIlan Peer he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
5061820acc81SIlan Peer eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype);
5062820acc81SIlan Peer if (!he_cap || !eht_cap)
5063820acc81SIlan Peer return 0;
5064820acc81SIlan Peer
5065ea5cba26SJohannes Berg is_ap = iftype == NL80211_IFTYPE_AP ||
5066ea5cba26SJohannes Berg iftype == NL80211_IFTYPE_P2P_GO;
5067ea5cba26SJohannes Berg
5068820acc81SIlan Peer n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
5069ea5cba26SJohannes Berg &eht_cap->eht_cap_elem,
5070ea5cba26SJohannes Berg is_ap);
5071820acc81SIlan Peer return 2 + 1 +
5072dd01579eSRyder Lee sizeof(eht_cap->eht_cap_elem) + n +
5073820acc81SIlan Peer ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
5074820acc81SIlan Peer eht_cap->eht_cap_elem.phy_cap_info);
5075820acc81SIlan Peer return 0;
5076820acc81SIlan Peer }
5077820acc81SIlan Peer
ieee80211_ie_build_eht_cap(u8 * pos,const struct ieee80211_sta_he_cap * he_cap,const struct ieee80211_sta_eht_cap * eht_cap,u8 * end,bool for_ap)5078820acc81SIlan Peer u8 *ieee80211_ie_build_eht_cap(u8 *pos,
5079820acc81SIlan Peer const struct ieee80211_sta_he_cap *he_cap,
5080820acc81SIlan Peer const struct ieee80211_sta_eht_cap *eht_cap,
5081ea5cba26SJohannes Berg u8 *end,
5082ea5cba26SJohannes Berg bool for_ap)
5083820acc81SIlan Peer {
5084820acc81SIlan Peer u8 mcs_nss_len, ppet_len;
5085820acc81SIlan Peer u8 ie_len;
5086820acc81SIlan Peer u8 *orig_pos = pos;
5087820acc81SIlan Peer
5088820acc81SIlan Peer /* Make sure we have place for the IE */
5089820acc81SIlan Peer if (!he_cap || !eht_cap)
5090820acc81SIlan Peer return orig_pos;
5091820acc81SIlan Peer
5092820acc81SIlan Peer mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
5093ea5cba26SJohannes Berg &eht_cap->eht_cap_elem,
5094ea5cba26SJohannes Berg for_ap);
5095820acc81SIlan Peer ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
5096820acc81SIlan Peer eht_cap->eht_cap_elem.phy_cap_info);
5097820acc81SIlan Peer
5098820acc81SIlan Peer ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len;
5099820acc81SIlan Peer if ((end - pos) < ie_len)
5100820acc81SIlan Peer return orig_pos;
5101820acc81SIlan Peer
5102820acc81SIlan Peer *pos++ = WLAN_EID_EXTENSION;
5103820acc81SIlan Peer *pos++ = ie_len - 2;
5104820acc81SIlan Peer *pos++ = WLAN_EID_EXT_EHT_CAPABILITY;
5105820acc81SIlan Peer
5106820acc81SIlan Peer /* Fixed data */
5107820acc81SIlan Peer memcpy(pos, &eht_cap->eht_cap_elem, sizeof(eht_cap->eht_cap_elem));
5108820acc81SIlan Peer pos += sizeof(eht_cap->eht_cap_elem);
5109820acc81SIlan Peer
5110820acc81SIlan Peer memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len);
5111820acc81SIlan Peer pos += mcs_nss_len;
5112820acc81SIlan Peer
5113820acc81SIlan Peer if (ppet_len) {
5114820acc81SIlan Peer memcpy(pos, &eht_cap->eht_ppe_thres, ppet_len);
5115820acc81SIlan Peer pos += ppet_len;
5116820acc81SIlan Peer }
5117820acc81SIlan Peer
5118820acc81SIlan Peer return pos;
5119820acc81SIlan Peer }
5120e4342549SJohannes Berg
ieee80211_fragment_element(struct sk_buff * skb,u8 * len_pos,u8 frag_id)5121d094482cSBenjamin Berg void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id)
5122e4342549SJohannes Berg {
5123e4342549SJohannes Berg unsigned int elem_len;
5124e4342549SJohannes Berg
5125e4342549SJohannes Berg if (!len_pos)
5126e4342549SJohannes Berg return;
5127e4342549SJohannes Berg
5128e4342549SJohannes Berg elem_len = skb->data + skb->len - len_pos - 1;
5129e4342549SJohannes Berg
5130e4342549SJohannes Berg while (elem_len > 255) {
5131e4342549SJohannes Berg /* this one is 255 */
5132e4342549SJohannes Berg *len_pos = 255;
5133e4342549SJohannes Berg /* remaining data gets smaller */
5134e4342549SJohannes Berg elem_len -= 255;
5135e4342549SJohannes Berg /* make space for the fragment ID/len in SKB */
5136e4342549SJohannes Berg skb_put(skb, 2);
5137e4342549SJohannes Berg /* shift back the remaining data to place fragment ID/len */
5138e4342549SJohannes Berg memmove(len_pos + 255 + 3, len_pos + 255 + 1, elem_len);
5139e4342549SJohannes Berg /* place the fragment ID */
5140e4342549SJohannes Berg len_pos += 255 + 1;
5141d094482cSBenjamin Berg *len_pos = frag_id;
5142e4342549SJohannes Berg /* and point to fragment length to update later */
5143e4342549SJohannes Berg len_pos++;
5144e4342549SJohannes Berg }
5145e4342549SJohannes Berg
5146e4342549SJohannes Berg *len_pos = elem_len;
5147e4342549SJohannes Berg }
5148