xref: /openbmc/linux/net/mac80211/util.c (revision d69c7a4e)
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, &params);
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, &params,
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