xref: /openbmc/linux/net/mac80211/util.c (revision c56ef672)
1c2d1560aSJohannes Berg /*
2c2d1560aSJohannes Berg  * Copyright 2002-2005, Instant802 Networks, Inc.
3c2d1560aSJohannes Berg  * Copyright 2005-2006, Devicescape Software, Inc.
4c2d1560aSJohannes Berg  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
5c2d1560aSJohannes Berg  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
6c2d1560aSJohannes Berg  *
7c2d1560aSJohannes Berg  * This program is free software; you can redistribute it and/or modify
8c2d1560aSJohannes Berg  * it under the terms of the GNU General Public License version 2 as
9c2d1560aSJohannes Berg  * published by the Free Software Foundation.
10c2d1560aSJohannes Berg  *
11c2d1560aSJohannes Berg  * utilities for mac80211
12c2d1560aSJohannes Berg  */
13c2d1560aSJohannes Berg 
14c2d1560aSJohannes Berg #include <net/mac80211.h>
15c2d1560aSJohannes Berg #include <linux/netdevice.h>
16bc3b2d7fSPaul Gortmaker #include <linux/export.h>
17c2d1560aSJohannes Berg #include <linux/types.h>
18c2d1560aSJohannes Berg #include <linux/slab.h>
19c2d1560aSJohannes Berg #include <linux/skbuff.h>
20c2d1560aSJohannes Berg #include <linux/etherdevice.h>
21c2d1560aSJohannes Berg #include <linux/if_arp.h>
22c2d1560aSJohannes Berg #include <linux/bitmap.h>
23dd76986bSJohannes Berg #include <linux/crc32.h>
24881d966bSEric W. Biederman #include <net/net_namespace.h>
25c2d1560aSJohannes Berg #include <net/cfg80211.h>
26dabeb344SJohannes Berg #include <net/rtnetlink.h>
27c2d1560aSJohannes Berg 
28c2d1560aSJohannes Berg #include "ieee80211_i.h"
2924487981SJohannes Berg #include "driver-ops.h"
302c8dccc7SJohannes Berg #include "rate.h"
31ee385855SLuis Carlos Cobo #include "mesh.h"
32c2d1560aSJohannes Berg #include "wme.h"
33f2753ddbSJohannes Berg #include "led.h"
34fffd0934SJohannes Berg #include "wep.h"
35c2d1560aSJohannes Berg 
36c2d1560aSJohannes Berg /* privid for wiphys to determine whether they belong to us or not */
378a47cea7SJohannes Berg const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;
38c2d1560aSJohannes Berg 
399a95371aSLuis R. Rodriguez struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
409a95371aSLuis R. Rodriguez {
419a95371aSLuis R. Rodriguez 	struct ieee80211_local *local;
429a95371aSLuis R. Rodriguez 	BUG_ON(!wiphy);
439a95371aSLuis R. Rodriguez 
449a95371aSLuis R. Rodriguez 	local = wiphy_priv(wiphy);
459a95371aSLuis R. Rodriguez 	return &local->hw;
469a95371aSLuis R. Rodriguez }
479a95371aSLuis R. Rodriguez EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
48c2d1560aSJohannes Berg 
4971364716SRon Rindjunsky u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
5005c914feSJohannes Berg 			enum nl80211_iftype type)
51c2d1560aSJohannes Berg {
52a494bb1cSHarvey Harrison 	__le16 fc = hdr->frame_control;
53c2d1560aSJohannes Berg 
5498f0b0a3SRon Rindjunsky 	 /* drop ACK/CTS frames and incorrect hdr len (ctrl) */
5598f0b0a3SRon Rindjunsky 	if (len < 16)
56c2d1560aSJohannes Berg 		return NULL;
57c2d1560aSJohannes Berg 
58a494bb1cSHarvey Harrison 	if (ieee80211_is_data(fc)) {
5998f0b0a3SRon Rindjunsky 		if (len < 24) /* drop incorrect hdr len (data) */
6098f0b0a3SRon Rindjunsky 			return NULL;
61a494bb1cSHarvey Harrison 
62a494bb1cSHarvey Harrison 		if (ieee80211_has_a4(fc))
63c2d1560aSJohannes Berg 			return NULL;
64a494bb1cSHarvey Harrison 		if (ieee80211_has_tods(fc))
65a494bb1cSHarvey Harrison 			return hdr->addr1;
66a494bb1cSHarvey Harrison 		if (ieee80211_has_fromds(fc))
67c2d1560aSJohannes Berg 			return hdr->addr2;
68a494bb1cSHarvey Harrison 
69c2d1560aSJohannes Berg 		return hdr->addr3;
70c2d1560aSJohannes Berg 	}
71a494bb1cSHarvey Harrison 
72a494bb1cSHarvey Harrison 	if (ieee80211_is_mgmt(fc)) {
7398f0b0a3SRon Rindjunsky 		if (len < 24) /* drop incorrect hdr len (mgmt) */
7498f0b0a3SRon Rindjunsky 			return NULL;
75c2d1560aSJohannes Berg 		return hdr->addr3;
76a494bb1cSHarvey Harrison 	}
77a494bb1cSHarvey Harrison 
78a494bb1cSHarvey Harrison 	if (ieee80211_is_ctl(fc)) {
79a494bb1cSHarvey Harrison 		if (ieee80211_is_pspoll(fc))
80c2d1560aSJohannes Berg 			return hdr->addr1;
81a494bb1cSHarvey Harrison 
82a494bb1cSHarvey Harrison 		if (ieee80211_is_back_req(fc)) {
8371364716SRon Rindjunsky 			switch (type) {
8405c914feSJohannes Berg 			case NL80211_IFTYPE_STATION:
8571364716SRon Rindjunsky 				return hdr->addr2;
8605c914feSJohannes Berg 			case NL80211_IFTYPE_AP:
8705c914feSJohannes Berg 			case NL80211_IFTYPE_AP_VLAN:
8871364716SRon Rindjunsky 				return hdr->addr1;
8971364716SRon Rindjunsky 			default:
90a494bb1cSHarvey Harrison 				break; /* fall through to the return */
9171364716SRon Rindjunsky 			}
9271364716SRon Rindjunsky 		}
93c2d1560aSJohannes Berg 	}
94c2d1560aSJohannes Berg 
95c2d1560aSJohannes Berg 	return NULL;
96c2d1560aSJohannes Berg }
97c2d1560aSJohannes Berg 
985cf121c3SJohannes Berg void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
99c2d1560aSJohannes Berg {
100252b86c4SJohannes Berg 	struct sk_buff *skb;
1012de8e0d9SJohannes Berg 	struct ieee80211_hdr *hdr;
102c2d1560aSJohannes Berg 
103252b86c4SJohannes Berg 	skb_queue_walk(&tx->skbs, skb) {
1042de8e0d9SJohannes Berg 		hdr = (struct ieee80211_hdr *) skb->data;
105c2d1560aSJohannes Berg 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
106252b86c4SJohannes Berg 	}
107c2d1560aSJohannes Berg }
108c2d1560aSJohannes Berg 
1094ee73f33SMichal Kazior int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
110438b61b7SSimon Wunderlich 			     int rate, int erp, int short_preamble,
111438b61b7SSimon Wunderlich 			     int shift)
112c2d1560aSJohannes Berg {
113c2d1560aSJohannes Berg 	int dur;
114c2d1560aSJohannes Berg 
115c2d1560aSJohannes Berg 	/* calculate duration (in microseconds, rounded up to next higher
116c2d1560aSJohannes Berg 	 * integer if it includes a fractional microsecond) to send frame of
117c2d1560aSJohannes Berg 	 * len bytes (does not include FCS) at the given rate. Duration will
118c2d1560aSJohannes Berg 	 * also include SIFS.
119c2d1560aSJohannes Berg 	 *
120c2d1560aSJohannes Berg 	 * rate is in 100 kbps, so divident is multiplied by 10 in the
121c2d1560aSJohannes Berg 	 * DIV_ROUND_UP() operations.
122438b61b7SSimon Wunderlich 	 *
123438b61b7SSimon Wunderlich 	 * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
124438b61b7SSimon Wunderlich 	 * is assumed to be 0 otherwise.
125c2d1560aSJohannes Berg 	 */
126c2d1560aSJohannes Berg 
1274ee73f33SMichal Kazior 	if (band == IEEE80211_BAND_5GHZ || erp) {
128c2d1560aSJohannes Berg 		/*
129c2d1560aSJohannes Berg 		 * OFDM:
130c2d1560aSJohannes Berg 		 *
131c2d1560aSJohannes Berg 		 * N_DBPS = DATARATE x 4
132c2d1560aSJohannes Berg 		 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
133c2d1560aSJohannes Berg 		 *	(16 = SIGNAL time, 6 = tail bits)
134c2d1560aSJohannes Berg 		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
135c2d1560aSJohannes Berg 		 *
136c2d1560aSJohannes Berg 		 * T_SYM = 4 usec
137438b61b7SSimon Wunderlich 		 * 802.11a - 18.5.2: aSIFSTime = 16 usec
138c2d1560aSJohannes Berg 		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
139c2d1560aSJohannes Berg 		 *	signal ext = 6 usec
140c2d1560aSJohannes Berg 		 */
141c2d1560aSJohannes Berg 		dur = 16; /* SIFS + signal ext */
142438b61b7SSimon Wunderlich 		dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
143438b61b7SSimon Wunderlich 		dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
144438b61b7SSimon Wunderlich 
145438b61b7SSimon Wunderlich 		/* IEEE 802.11-2012 18.3.2.4: all values above are:
146438b61b7SSimon Wunderlich 		 *  * times 4 for 5 MHz
147438b61b7SSimon Wunderlich 		 *  * times 2 for 10 MHz
148438b61b7SSimon Wunderlich 		 */
149438b61b7SSimon Wunderlich 		dur *= 1 << shift;
1502103dec1SSimon Wunderlich 
1512103dec1SSimon Wunderlich 		/* rates should already consider the channel bandwidth,
1522103dec1SSimon Wunderlich 		 * don't apply divisor again.
1532103dec1SSimon Wunderlich 		 */
1542103dec1SSimon Wunderlich 		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
1552103dec1SSimon Wunderlich 					4 * rate); /* T_SYM x N_SYM */
156c2d1560aSJohannes Berg 	} else {
157c2d1560aSJohannes Berg 		/*
158c2d1560aSJohannes Berg 		 * 802.11b or 802.11g with 802.11b compatibility:
159c2d1560aSJohannes Berg 		 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
160c2d1560aSJohannes Berg 		 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
161c2d1560aSJohannes Berg 		 *
162c2d1560aSJohannes Berg 		 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
163c2d1560aSJohannes Berg 		 * aSIFSTime = 10 usec
164c2d1560aSJohannes Berg 		 * aPreambleLength = 144 usec or 72 usec with short preamble
165c2d1560aSJohannes Berg 		 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
166c2d1560aSJohannes Berg 		 */
167c2d1560aSJohannes Berg 		dur = 10; /* aSIFSTime = 10 usec */
168c2d1560aSJohannes Berg 		dur += short_preamble ? (72 + 24) : (144 + 48);
169c2d1560aSJohannes Berg 
170c2d1560aSJohannes Berg 		dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
171c2d1560aSJohannes Berg 	}
172c2d1560aSJohannes Berg 
173c2d1560aSJohannes Berg 	return dur;
174c2d1560aSJohannes Berg }
175c2d1560aSJohannes Berg 
176c2d1560aSJohannes Berg /* Exported duration function for driver use */
17732bfd35dSJohannes Berg __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
17832bfd35dSJohannes Berg 					struct ieee80211_vif *vif,
1794ee73f33SMichal Kazior 					enum ieee80211_band band,
1808318d78aSJohannes Berg 					size_t frame_len,
1818318d78aSJohannes Berg 					struct ieee80211_rate *rate)
182c2d1560aSJohannes Berg {
18325d834e1SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
184c2d1560aSJohannes Berg 	u16 dur;
185438b61b7SSimon Wunderlich 	int erp, shift = 0;
18625d834e1SJohannes Berg 	bool short_preamble = false;
187c2d1560aSJohannes Berg 
1888318d78aSJohannes Berg 	erp = 0;
18925d834e1SJohannes Berg 	if (vif) {
19025d834e1SJohannes Berg 		sdata = vif_to_sdata(vif);
191bda3933aSJohannes Berg 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
1928318d78aSJohannes Berg 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
1938318d78aSJohannes Berg 			erp = rate->flags & IEEE80211_RATE_ERP_G;
194438b61b7SSimon Wunderlich 		shift = ieee80211_vif_get_shift(vif);
19525d834e1SJohannes Berg 	}
1968318d78aSJohannes Berg 
1974ee73f33SMichal Kazior 	dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
198438b61b7SSimon Wunderlich 				       short_preamble, shift);
199c2d1560aSJohannes Berg 
200c2d1560aSJohannes Berg 	return cpu_to_le16(dur);
201c2d1560aSJohannes Berg }
202c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_generic_frame_duration);
203c2d1560aSJohannes Berg 
20432bfd35dSJohannes Berg __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
20532bfd35dSJohannes Berg 			      struct ieee80211_vif *vif, size_t frame_len,
206e039fa4aSJohannes Berg 			      const struct ieee80211_tx_info *frame_txctl)
207c2d1560aSJohannes Berg {
208c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
209c2d1560aSJohannes Berg 	struct ieee80211_rate *rate;
21025d834e1SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
211471b3efdSJohannes Berg 	bool short_preamble;
2122103dec1SSimon Wunderlich 	int erp, shift = 0, bitrate;
213c2d1560aSJohannes Berg 	u16 dur;
2142e92e6f2SJohannes Berg 	struct ieee80211_supported_band *sband;
2152e92e6f2SJohannes Berg 
2164ee73f33SMichal Kazior 	sband = local->hw.wiphy->bands[frame_txctl->band];
217c2d1560aSJohannes Berg 
21825d834e1SJohannes Berg 	short_preamble = false;
2197e9ed188SDaniel Drake 
220e039fa4aSJohannes Berg 	rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
2218318d78aSJohannes Berg 
2228318d78aSJohannes Berg 	erp = 0;
22325d834e1SJohannes Berg 	if (vif) {
22425d834e1SJohannes Berg 		sdata = vif_to_sdata(vif);
225bda3933aSJohannes Berg 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
2268318d78aSJohannes Berg 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
2278318d78aSJohannes Berg 			erp = rate->flags & IEEE80211_RATE_ERP_G;
228438b61b7SSimon Wunderlich 		shift = ieee80211_vif_get_shift(vif);
22925d834e1SJohannes Berg 	}
230c2d1560aSJohannes Berg 
2312103dec1SSimon Wunderlich 	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
2322103dec1SSimon Wunderlich 
233c2d1560aSJohannes Berg 	/* CTS duration */
2342103dec1SSimon Wunderlich 	dur = ieee80211_frame_duration(sband->band, 10, bitrate,
235438b61b7SSimon Wunderlich 				       erp, short_preamble, shift);
236c2d1560aSJohannes Berg 	/* Data frame duration */
2372103dec1SSimon Wunderlich 	dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
238438b61b7SSimon Wunderlich 					erp, short_preamble, shift);
239c2d1560aSJohannes Berg 	/* ACK duration */
2402103dec1SSimon Wunderlich 	dur += ieee80211_frame_duration(sband->band, 10, bitrate,
241438b61b7SSimon Wunderlich 					erp, short_preamble, shift);
242c2d1560aSJohannes Berg 
243c2d1560aSJohannes Berg 	return cpu_to_le16(dur);
244c2d1560aSJohannes Berg }
245c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_rts_duration);
246c2d1560aSJohannes Berg 
24732bfd35dSJohannes Berg __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
24832bfd35dSJohannes Berg 				    struct ieee80211_vif *vif,
249c2d1560aSJohannes Berg 				    size_t frame_len,
250e039fa4aSJohannes Berg 				    const struct ieee80211_tx_info *frame_txctl)
251c2d1560aSJohannes Berg {
252c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
253c2d1560aSJohannes Berg 	struct ieee80211_rate *rate;
25425d834e1SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
255471b3efdSJohannes Berg 	bool short_preamble;
2562103dec1SSimon Wunderlich 	int erp, shift = 0, bitrate;
257c2d1560aSJohannes Berg 	u16 dur;
2582e92e6f2SJohannes Berg 	struct ieee80211_supported_band *sband;
2592e92e6f2SJohannes Berg 
2604ee73f33SMichal Kazior 	sband = local->hw.wiphy->bands[frame_txctl->band];
261c2d1560aSJohannes Berg 
26225d834e1SJohannes Berg 	short_preamble = false;
2637e9ed188SDaniel Drake 
264e039fa4aSJohannes Berg 	rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
2658318d78aSJohannes Berg 	erp = 0;
26625d834e1SJohannes Berg 	if (vif) {
26725d834e1SJohannes Berg 		sdata = vif_to_sdata(vif);
268bda3933aSJohannes Berg 		short_preamble = sdata->vif.bss_conf.use_short_preamble;
2698318d78aSJohannes Berg 		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
2708318d78aSJohannes Berg 			erp = rate->flags & IEEE80211_RATE_ERP_G;
271438b61b7SSimon Wunderlich 		shift = ieee80211_vif_get_shift(vif);
27225d834e1SJohannes Berg 	}
273c2d1560aSJohannes Berg 
2742103dec1SSimon Wunderlich 	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
2752103dec1SSimon Wunderlich 
276c2d1560aSJohannes Berg 	/* Data frame duration */
2772103dec1SSimon Wunderlich 	dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
278438b61b7SSimon Wunderlich 				       erp, short_preamble, shift);
279e039fa4aSJohannes Berg 	if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
280c2d1560aSJohannes Berg 		/* ACK duration */
2812103dec1SSimon Wunderlich 		dur += ieee80211_frame_duration(sband->band, 10, bitrate,
282438b61b7SSimon Wunderlich 						erp, short_preamble, shift);
283c2d1560aSJohannes Berg 	}
284c2d1560aSJohannes Berg 
285c2d1560aSJohannes Berg 	return cpu_to_le16(dur);
286c2d1560aSJohannes Berg }
287c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_ctstoself_duration);
288c2d1560aSJohannes Berg 
2893a25a8c8SJohannes Berg void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
2903a25a8c8SJohannes Berg {
2913a25a8c8SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
292a6f38ac3SJohannes Berg 	int n_acs = IEEE80211_NUM_ACS;
293a6f38ac3SJohannes Berg 
294a6f38ac3SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
295a6f38ac3SJohannes Berg 		n_acs = 1;
2963a25a8c8SJohannes Berg 
2973a25a8c8SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2983a25a8c8SJohannes Berg 		int ac;
2993a25a8c8SJohannes Berg 
300f142c6b9SJohannes Berg 		if (!sdata->dev)
301f142c6b9SJohannes Berg 			continue;
302f142c6b9SJohannes Berg 
3033a25a8c8SJohannes Berg 		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
3043a25a8c8SJohannes Berg 		    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
3053a25a8c8SJohannes Berg 			continue;
3063a25a8c8SJohannes Berg 
307a6f38ac3SJohannes Berg 		for (ac = 0; ac < n_acs; ac++) {
3083a25a8c8SJohannes Berg 			int ac_queue = sdata->vif.hw_queue[ac];
3093a25a8c8SJohannes Berg 
3103a25a8c8SJohannes Berg 			if (ac_queue == queue ||
3113a25a8c8SJohannes Berg 			    (sdata->vif.cab_queue == queue &&
3123a25a8c8SJohannes Berg 			     local->queue_stop_reasons[ac_queue] == 0 &&
3133a25a8c8SJohannes Berg 			     skb_queue_empty(&local->pending[ac_queue])))
3143a25a8c8SJohannes Berg 				netif_wake_subqueue(sdata->dev, ac);
3153a25a8c8SJohannes Berg 		}
3163a25a8c8SJohannes Berg 	}
3173a25a8c8SJohannes Berg }
3183a25a8c8SJohannes Berg 
319ce7c9111SKalle Valo static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
320cca07b00SLuciano Coelho 				   enum queue_stop_reason reason,
321cca07b00SLuciano Coelho 				   bool refcounted)
322c2d1560aSJohannes Berg {
323c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
324c2d1560aSJohannes Berg 
325b5878a2dSJohannes Berg 	trace_wake_queue(local, queue, reason);
326b5878a2dSJohannes Berg 
327e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
32896f5e66eSJohannes Berg 		return;
32996f5e66eSJohannes Berg 
330ada15125SJohannes Berg 	if (!test_bit(reason, &local->queue_stop_reasons[queue]))
331ada15125SJohannes Berg 		return;
332ada15125SJohannes Berg 
333cca07b00SLuciano Coelho 	if (!refcounted)
334cca07b00SLuciano Coelho 		local->q_stop_reasons[queue][reason] = 0;
335cca07b00SLuciano Coelho 	else
336cca07b00SLuciano Coelho 		local->q_stop_reasons[queue][reason]--;
337cca07b00SLuciano Coelho 
338cca07b00SLuciano Coelho 	if (local->q_stop_reasons[queue][reason] == 0)
339ce7c9111SKalle Valo 		__clear_bit(reason, &local->queue_stop_reasons[queue]);
340ce7c9111SKalle Valo 
341ce7c9111SKalle Valo 	if (local->queue_stop_reasons[queue] != 0)
342ce7c9111SKalle Valo 		/* someone still has this queue stopped */
343ce7c9111SKalle Valo 		return;
344ce7c9111SKalle Valo 
3457236fe29SJohannes Berg 	if (skb_queue_empty(&local->pending[queue])) {
346cf0277e7SJohannes Berg 		rcu_read_lock();
3473a25a8c8SJohannes Berg 		ieee80211_propagate_queue_wake(local, queue);
348cf0277e7SJohannes Berg 		rcu_read_unlock();
3497236fe29SJohannes Berg 	} else
3507236fe29SJohannes Berg 		tasklet_schedule(&local->tx_pending_tasklet);
351c2d1560aSJohannes Berg }
352ce7c9111SKalle Valo 
35396f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
354cca07b00SLuciano Coelho 				    enum queue_stop_reason reason,
355cca07b00SLuciano Coelho 				    bool refcounted)
356ce7c9111SKalle Valo {
357ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
358ce7c9111SKalle Valo 	unsigned long flags;
359ce7c9111SKalle Valo 
360ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
361cca07b00SLuciano Coelho 	__ieee80211_wake_queue(hw, queue, reason, refcounted);
362ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
363ce7c9111SKalle Valo }
364ce7c9111SKalle Valo 
365ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
366ce7c9111SKalle Valo {
367ce7c9111SKalle Valo 	ieee80211_wake_queue_by_reason(hw, queue,
368cca07b00SLuciano Coelho 				       IEEE80211_QUEUE_STOP_REASON_DRIVER,
369cca07b00SLuciano Coelho 				       false);
370ce7c9111SKalle Valo }
371c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue);
372c2d1560aSJohannes Berg 
373ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
374cca07b00SLuciano Coelho 				   enum queue_stop_reason reason,
375cca07b00SLuciano Coelho 				   bool refcounted)
376c2d1560aSJohannes Berg {
377c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
378cf0277e7SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
379a6f38ac3SJohannes Berg 	int n_acs = IEEE80211_NUM_ACS;
380c2d1560aSJohannes Berg 
381b5878a2dSJohannes Berg 	trace_stop_queue(local, queue, reason);
382b5878a2dSJohannes Berg 
383e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
38496f5e66eSJohannes Berg 		return;
38596f5e66eSJohannes Berg 
386cca07b00SLuciano Coelho 	if (!refcounted)
387cca07b00SLuciano Coelho 		local->q_stop_reasons[queue][reason] = 1;
388cca07b00SLuciano Coelho 	else
389cca07b00SLuciano Coelho 		local->q_stop_reasons[queue][reason]++;
390ada15125SJohannes Berg 
391cca07b00SLuciano Coelho 	if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
392cca07b00SLuciano Coelho 		return;
393cf0277e7SJohannes Berg 
394a6f38ac3SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
395a6f38ac3SJohannes Berg 		n_acs = 1;
396a6f38ac3SJohannes Berg 
397cf0277e7SJohannes Berg 	rcu_read_lock();
3983a25a8c8SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
3993a25a8c8SJohannes Berg 		int ac;
4003a25a8c8SJohannes Berg 
401f142c6b9SJohannes Berg 		if (!sdata->dev)
402f142c6b9SJohannes Berg 			continue;
403f142c6b9SJohannes Berg 
404a6f38ac3SJohannes Berg 		for (ac = 0; ac < n_acs; ac++) {
4053a25a8c8SJohannes Berg 			if (sdata->vif.hw_queue[ac] == queue ||
4063a25a8c8SJohannes Berg 			    sdata->vif.cab_queue == queue)
4073a25a8c8SJohannes Berg 				netif_stop_subqueue(sdata->dev, ac);
4083a25a8c8SJohannes Berg 		}
4093a25a8c8SJohannes Berg 	}
410cf0277e7SJohannes Berg 	rcu_read_unlock();
411c2d1560aSJohannes Berg }
412ce7c9111SKalle Valo 
41396f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
414cca07b00SLuciano Coelho 				    enum queue_stop_reason reason,
415cca07b00SLuciano Coelho 				    bool refcounted)
416ce7c9111SKalle Valo {
417ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
418ce7c9111SKalle Valo 	unsigned long flags;
419ce7c9111SKalle Valo 
420ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
421cca07b00SLuciano Coelho 	__ieee80211_stop_queue(hw, queue, reason, refcounted);
422ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
423ce7c9111SKalle Valo }
424ce7c9111SKalle Valo 
425ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
426ce7c9111SKalle Valo {
427ce7c9111SKalle Valo 	ieee80211_stop_queue_by_reason(hw, queue,
428cca07b00SLuciano Coelho 				       IEEE80211_QUEUE_STOP_REASON_DRIVER,
429cca07b00SLuciano Coelho 				       false);
430ce7c9111SKalle Valo }
431c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue);
432c2d1560aSJohannes Berg 
4338f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local,
4348f77f384SJohannes Berg 			       struct sk_buff *skb)
4358f77f384SJohannes Berg {
4368f77f384SJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
4378f77f384SJohannes Berg 	unsigned long flags;
438a7bc376cSJohannes Berg 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
4393a25a8c8SJohannes Berg 	int queue = info->hw_queue;
440a7bc376cSJohannes Berg 
441a7bc376cSJohannes Berg 	if (WARN_ON(!info->control.vif)) {
442d4fa14cdSFelix Fietkau 		ieee80211_free_txskb(&local->hw, skb);
443a7bc376cSJohannes Berg 		return;
444a7bc376cSJohannes Berg 	}
4458f77f384SJohannes Berg 
4468f77f384SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
447cca07b00SLuciano Coelho 	__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
448cca07b00SLuciano Coelho 			       false);
4493b8d81e0SJohannes Berg 	__skb_queue_tail(&local->pending[queue], skb);
450cca07b00SLuciano Coelho 	__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
451cca07b00SLuciano Coelho 			       false);
4528f77f384SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
4538f77f384SJohannes Berg }
4548f77f384SJohannes Berg 
455e3685e03SJohannes Berg void ieee80211_add_pending_skbs(struct ieee80211_local *local,
456e3685e03SJohannes Berg 				struct sk_buff_head *skbs)
4578f77f384SJohannes Berg {
4588f77f384SJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
4598f77f384SJohannes Berg 	struct sk_buff *skb;
4608f77f384SJohannes Berg 	unsigned long flags;
461b0b97a8aSJohannes Berg 	int queue, i;
4628f77f384SJohannes Berg 
4638f77f384SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
4648f77f384SJohannes Berg 	while ((skb = skb_dequeue(skbs))) {
465a7bc376cSJohannes Berg 		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
466a7bc376cSJohannes Berg 
467a7bc376cSJohannes Berg 		if (WARN_ON(!info->control.vif)) {
468d4fa14cdSFelix Fietkau 			ieee80211_free_txskb(&local->hw, skb);
469a7bc376cSJohannes Berg 			continue;
470a7bc376cSJohannes Berg 		}
471a7bc376cSJohannes Berg 
4723a25a8c8SJohannes Berg 		queue = info->hw_queue;
4734644ae89SJohannes Berg 
4744644ae89SJohannes Berg 		__ieee80211_stop_queue(hw, queue,
475cca07b00SLuciano Coelho 				IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
476cca07b00SLuciano Coelho 				false);
4774644ae89SJohannes Berg 
4783b8d81e0SJohannes Berg 		__skb_queue_tail(&local->pending[queue], skb);
4798f77f384SJohannes Berg 	}
4808f77f384SJohannes Berg 
4813b8d81e0SJohannes Berg 	for (i = 0; i < hw->queues; i++)
4828f77f384SJohannes Berg 		__ieee80211_wake_queue(hw, i,
483cca07b00SLuciano Coelho 			IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
484cca07b00SLuciano Coelho 			false);
4858f77f384SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
4868f77f384SJohannes Berg }
4878f77f384SJohannes Berg 
488ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
489445ea4e8SJohannes Berg 				     unsigned long queues,
490cca07b00SLuciano Coelho 				     enum queue_stop_reason reason,
491cca07b00SLuciano Coelho 				     bool refcounted)
492c2d1560aSJohannes Berg {
493ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
494ce7c9111SKalle Valo 	unsigned long flags;
495c2d1560aSJohannes Berg 	int i;
496c2d1560aSJohannes Berg 
497ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
498ce7c9111SKalle Valo 
499445ea4e8SJohannes Berg 	for_each_set_bit(i, &queues, hw->queues)
500cca07b00SLuciano Coelho 		__ieee80211_stop_queue(hw, i, reason, refcounted);
501ce7c9111SKalle Valo 
502ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
503ce7c9111SKalle Valo }
504ce7c9111SKalle Valo 
505ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw)
506ce7c9111SKalle Valo {
507445ea4e8SJohannes Berg 	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
508cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_DRIVER,
509cca07b00SLuciano Coelho 					false);
510c2d1560aSJohannes Berg }
511c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues);
512c2d1560aSJohannes Berg 
51392ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
51492ab8535STomas Winkler {
51592ab8535STomas Winkler 	struct ieee80211_local *local = hw_to_local(hw);
5163b8d81e0SJohannes Berg 	unsigned long flags;
5173b8d81e0SJohannes Berg 	int ret;
51896f5e66eSJohannes Berg 
519e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
52096f5e66eSJohannes Berg 		return true;
52196f5e66eSJohannes Berg 
5223b8d81e0SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
5232419ea14SThomas Pedersen 	ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER,
5242419ea14SThomas Pedersen 		       &local->queue_stop_reasons[queue]);
5253b8d81e0SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
5263b8d81e0SJohannes Berg 	return ret;
52792ab8535STomas Winkler }
52892ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped);
52992ab8535STomas Winkler 
530ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
531445ea4e8SJohannes Berg 				     unsigned long queues,
532cca07b00SLuciano Coelho 				     enum queue_stop_reason reason,
533cca07b00SLuciano Coelho 				     bool refcounted)
534c2d1560aSJohannes Berg {
535ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
536ce7c9111SKalle Valo 	unsigned long flags;
537c2d1560aSJohannes Berg 	int i;
538c2d1560aSJohannes Berg 
539ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
540ce7c9111SKalle Valo 
541445ea4e8SJohannes Berg 	for_each_set_bit(i, &queues, hw->queues)
542cca07b00SLuciano Coelho 		__ieee80211_wake_queue(hw, i, reason, refcounted);
543ce7c9111SKalle Valo 
544ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
545ce7c9111SKalle Valo }
546ce7c9111SKalle Valo 
547ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw)
548ce7c9111SKalle Valo {
549445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
550cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_DRIVER,
551cca07b00SLuciano Coelho 					false);
552c2d1560aSJohannes Berg }
553c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues);
554dabeb344SJohannes Berg 
55526da23b6SLuciano Coelho static unsigned int
55626da23b6SLuciano Coelho ieee80211_get_vif_queues(struct ieee80211_local *local,
55739ecc01dSJohannes Berg 			 struct ieee80211_sub_if_data *sdata)
55839ecc01dSJohannes Berg {
55926da23b6SLuciano Coelho 	unsigned int queues;
56039ecc01dSJohannes Berg 
56139ecc01dSJohannes Berg 	if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
56239ecc01dSJohannes Berg 		int ac;
56339ecc01dSJohannes Berg 
56439ecc01dSJohannes Berg 		queues = 0;
56539ecc01dSJohannes Berg 
56639ecc01dSJohannes Berg 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
56739ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.hw_queue[ac]);
56839ecc01dSJohannes Berg 		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
56939ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.cab_queue);
57039ecc01dSJohannes Berg 	} else {
57139ecc01dSJohannes Berg 		/* all queues */
57239ecc01dSJohannes Berg 		queues = BIT(local->hw.queues) - 1;
57339ecc01dSJohannes Berg 	}
57439ecc01dSJohannes Berg 
57526da23b6SLuciano Coelho 	return queues;
57626da23b6SLuciano Coelho }
57726da23b6SLuciano Coelho 
57826da23b6SLuciano Coelho void ieee80211_flush_queues(struct ieee80211_local *local,
57926da23b6SLuciano Coelho 			    struct ieee80211_sub_if_data *sdata)
58026da23b6SLuciano Coelho {
58126da23b6SLuciano Coelho 	unsigned int queues;
58226da23b6SLuciano Coelho 
58326da23b6SLuciano Coelho 	if (!local->ops->flush)
58426da23b6SLuciano Coelho 		return;
58526da23b6SLuciano Coelho 
58626da23b6SLuciano Coelho 	queues = ieee80211_get_vif_queues(local, sdata);
58726da23b6SLuciano Coelho 
58859f48fe2SLuciano Coelho 	ieee80211_stop_queues_by_reason(&local->hw, queues,
589cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
590cca07b00SLuciano Coelho 					false);
591445ea4e8SJohannes Berg 
59277be2c54SEmmanuel Grumbach 	drv_flush(local, sdata, queues, false);
593445ea4e8SJohannes Berg 
59459f48fe2SLuciano Coelho 	ieee80211_wake_queues_by_reason(&local->hw, queues,
595cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
596cca07b00SLuciano Coelho 					false);
59739ecc01dSJohannes Berg }
59839ecc01dSJohannes Berg 
59926da23b6SLuciano Coelho void ieee80211_stop_vif_queues(struct ieee80211_local *local,
60026da23b6SLuciano Coelho 			       struct ieee80211_sub_if_data *sdata,
60126da23b6SLuciano Coelho 			       enum queue_stop_reason reason)
60226da23b6SLuciano Coelho {
60326da23b6SLuciano Coelho 	ieee80211_stop_queues_by_reason(&local->hw,
60426da23b6SLuciano Coelho 					ieee80211_get_vif_queues(local, sdata),
60526da23b6SLuciano Coelho 					reason, true);
60626da23b6SLuciano Coelho }
60726da23b6SLuciano Coelho 
60826da23b6SLuciano Coelho void ieee80211_wake_vif_queues(struct ieee80211_local *local,
60926da23b6SLuciano Coelho 			       struct ieee80211_sub_if_data *sdata,
61026da23b6SLuciano Coelho 			       enum queue_stop_reason reason)
61126da23b6SLuciano Coelho {
61226da23b6SLuciano Coelho 	ieee80211_wake_queues_by_reason(&local->hw,
61326da23b6SLuciano Coelho 					ieee80211_get_vif_queues(local, sdata),
61426da23b6SLuciano Coelho 					reason, true);
61526da23b6SLuciano Coelho }
61626da23b6SLuciano Coelho 
617c7c71066SJohannes Berg static void __iterate_active_interfaces(struct ieee80211_local *local,
618c7c71066SJohannes Berg 					u32 iter_flags,
619dabeb344SJohannes Berg 					void (*iterator)(void *data, u8 *mac,
62032bfd35dSJohannes Berg 						struct ieee80211_vif *vif),
621dabeb344SJohannes Berg 					void *data)
622dabeb344SJohannes Berg {
623dabeb344SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
624dabeb344SJohannes Berg 
625e38bad47SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
62651fb61e7SJohannes Berg 		switch (sdata->vif.type) {
62705c914feSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
62831eba5bcSFelix Fietkau 			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
62931eba5bcSFelix Fietkau 				continue;
63031eba5bcSFelix Fietkau 			break;
63105c914feSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
632dabeb344SJohannes Berg 			continue;
6332ca27bcfSJohannes Berg 		default:
634dabeb344SJohannes Berg 			break;
635dabeb344SJohannes Berg 		}
6368b2c9824SJohannes Berg 		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
6378b2c9824SJohannes Berg 		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
6388b2c9824SJohannes Berg 			continue;
6399607e6b6SJohannes Berg 		if (ieee80211_sdata_running(sdata))
64047846c9bSJohannes Berg 			iterator(data, sdata->vif.addr,
64132bfd35dSJohannes Berg 				 &sdata->vif);
642dabeb344SJohannes Berg 	}
643e38bad47SJohannes Berg 
644c7c71066SJohannes Berg 	sdata = rcu_dereference_check(local->monitor_sdata,
645c7c71066SJohannes Berg 				      lockdep_is_held(&local->iflist_mtx) ||
646c7c71066SJohannes Berg 				      lockdep_rtnl_is_held());
6478b2c9824SJohannes Berg 	if (sdata &&
6488b2c9824SJohannes Berg 	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
6498b2c9824SJohannes Berg 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
650685fb72bSJohannes Berg 		iterator(data, sdata->vif.addr, &sdata->vif);
651c7c71066SJohannes Berg }
652685fb72bSJohannes Berg 
653c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces(
654c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
655c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
656c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
657c7c71066SJohannes Berg 	void *data)
658c7c71066SJohannes Berg {
659c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
660c7c71066SJohannes Berg 
661c7c71066SJohannes Berg 	mutex_lock(&local->iflist_mtx);
662c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
663c7c71066SJohannes Berg 	mutex_unlock(&local->iflist_mtx);
664c7c71066SJohannes Berg }
665c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
666c7c71066SJohannes Berg 
667c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic(
668c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
669c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
670c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
671c7c71066SJohannes Berg 	void *data)
672c7c71066SJohannes Berg {
673c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
674c7c71066SJohannes Berg 
675c7c71066SJohannes Berg 	rcu_read_lock();
676c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
677e38bad47SJohannes Berg 	rcu_read_unlock();
678dabeb344SJohannes Berg }
6792f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
68037ffc8daSJohannes Berg 
681c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_rtnl(
682c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
683c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
684c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
685c7c71066SJohannes Berg 	void *data)
686c7c71066SJohannes Berg {
687c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
688c7c71066SJohannes Berg 
689c7c71066SJohannes Berg 	ASSERT_RTNL();
690c7c71066SJohannes Berg 
691c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
692c7c71066SJohannes Berg }
693c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
694c7c71066SJohannes Berg 
695ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
696ad7e718cSJohannes Berg {
697ad7e718cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
698ad7e718cSJohannes Berg 
699ad7e718cSJohannes Berg 	if (!ieee80211_sdata_running(sdata) ||
700ad7e718cSJohannes Berg 	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
701ad7e718cSJohannes Berg 		return NULL;
702ad7e718cSJohannes Berg 	return &sdata->vif;
703ad7e718cSJohannes Berg }
704ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
705ad7e718cSJohannes Berg 
70642935ecaSLuis R. Rodriguez /*
70742935ecaSLuis R. Rodriguez  * Nothing should have been stuffed into the workqueue during
70842935ecaSLuis R. Rodriguez  * the suspend->resume cycle. If this WARN is seen then there
70942935ecaSLuis R. Rodriguez  * is a bug with either the driver suspend or something in
71042935ecaSLuis R. Rodriguez  * mac80211 stuffing into the workqueue which we haven't yet
71142935ecaSLuis R. Rodriguez  * cleared during mac80211's suspend cycle.
71242935ecaSLuis R. Rodriguez  */
71342935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local)
71442935ecaSLuis R. Rodriguez {
715ceb99fe0SJohannes Berg 	if (WARN(local->suspended && !local->resuming,
716ceb99fe0SJohannes Berg 		 "queueing ieee80211 work while going to suspend\n"))
71742935ecaSLuis R. Rodriguez 		return false;
71842935ecaSLuis R. Rodriguez 
71942935ecaSLuis R. Rodriguez 	return true;
72042935ecaSLuis R. Rodriguez }
72142935ecaSLuis R. Rodriguez 
72242935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
72342935ecaSLuis R. Rodriguez {
72442935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
72542935ecaSLuis R. Rodriguez 
72642935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
72742935ecaSLuis R. Rodriguez 		return;
72842935ecaSLuis R. Rodriguez 
72942935ecaSLuis R. Rodriguez 	queue_work(local->workqueue, work);
73042935ecaSLuis R. Rodriguez }
73142935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work);
73242935ecaSLuis R. Rodriguez 
73342935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
73442935ecaSLuis R. Rodriguez 				  struct delayed_work *dwork,
73542935ecaSLuis R. Rodriguez 				  unsigned long delay)
73642935ecaSLuis R. Rodriguez {
73742935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
73842935ecaSLuis R. Rodriguez 
73942935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
74042935ecaSLuis R. Rodriguez 		return;
74142935ecaSLuis R. Rodriguez 
74242935ecaSLuis R. Rodriguez 	queue_delayed_work(local->workqueue, dwork, delay);
74342935ecaSLuis R. Rodriguez }
74442935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work);
74542935ecaSLuis R. Rodriguez 
74635d865afSJohannes Berg u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
747dd76986bSJohannes Berg 			       struct ieee802_11_elems *elems,
748dd76986bSJohannes Berg 			       u64 filter, u32 crc)
749dd76986bSJohannes Berg {
750dd76986bSJohannes Berg 	size_t left = len;
75135d865afSJohannes Berg 	const u8 *pos = start;
752dd76986bSJohannes Berg 	bool calc_crc = filter != 0;
753fcff4f10SPaul Stewart 	DECLARE_BITMAP(seen_elems, 256);
754b2e506bfSJohannes Berg 	const u8 *ie;
755dd76986bSJohannes Berg 
756fcff4f10SPaul Stewart 	bitmap_zero(seen_elems, 256);
757dd76986bSJohannes Berg 	memset(elems, 0, sizeof(*elems));
758dd76986bSJohannes Berg 	elems->ie_start = start;
759dd76986bSJohannes Berg 	elems->total_len = len;
760dd76986bSJohannes Berg 
761dd76986bSJohannes Berg 	while (left >= 2) {
762dd76986bSJohannes Berg 		u8 id, elen;
763fcff4f10SPaul Stewart 		bool elem_parse_failed;
764dd76986bSJohannes Berg 
765dd76986bSJohannes Berg 		id = *pos++;
766dd76986bSJohannes Berg 		elen = *pos++;
767dd76986bSJohannes Berg 		left -= 2;
768dd76986bSJohannes Berg 
769fcff4f10SPaul Stewart 		if (elen > left) {
770fcff4f10SPaul Stewart 			elems->parse_error = true;
771dd76986bSJohannes Berg 			break;
772fcff4f10SPaul Stewart 		}
773fcff4f10SPaul Stewart 
7749690fb16SJohannes Berg 		switch (id) {
7759690fb16SJohannes Berg 		case WLAN_EID_SSID:
7769690fb16SJohannes Berg 		case WLAN_EID_SUPP_RATES:
7779690fb16SJohannes Berg 		case WLAN_EID_FH_PARAMS:
7789690fb16SJohannes Berg 		case WLAN_EID_DS_PARAMS:
7799690fb16SJohannes Berg 		case WLAN_EID_CF_PARAMS:
7809690fb16SJohannes Berg 		case WLAN_EID_TIM:
7819690fb16SJohannes Berg 		case WLAN_EID_IBSS_PARAMS:
7829690fb16SJohannes Berg 		case WLAN_EID_CHALLENGE:
7839690fb16SJohannes Berg 		case WLAN_EID_RSN:
7849690fb16SJohannes Berg 		case WLAN_EID_ERP_INFO:
7859690fb16SJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
7869690fb16SJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
7879690fb16SJohannes Berg 		case WLAN_EID_HT_OPERATION:
7889690fb16SJohannes Berg 		case WLAN_EID_VHT_CAPABILITY:
7899690fb16SJohannes Berg 		case WLAN_EID_VHT_OPERATION:
7909690fb16SJohannes Berg 		case WLAN_EID_MESH_ID:
7919690fb16SJohannes Berg 		case WLAN_EID_MESH_CONFIG:
7929690fb16SJohannes Berg 		case WLAN_EID_PEER_MGMT:
7939690fb16SJohannes Berg 		case WLAN_EID_PREQ:
7949690fb16SJohannes Berg 		case WLAN_EID_PREP:
7959690fb16SJohannes Berg 		case WLAN_EID_PERR:
7969690fb16SJohannes Berg 		case WLAN_EID_RANN:
7979690fb16SJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
7989690fb16SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
7999690fb16SJohannes Berg 		case WLAN_EID_COUNTRY:
8009690fb16SJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
8019690fb16SJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
80285220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
803b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
8048f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
805b2e506bfSJohannes Berg 		/*
806b2e506bfSJohannes Berg 		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
807b2e506bfSJohannes Berg 		 * that if the content gets bigger it might be needed more than once
808b2e506bfSJohannes Berg 		 */
8099690fb16SJohannes Berg 			if (test_bit(id, seen_elems)) {
810fcff4f10SPaul Stewart 				elems->parse_error = true;
811fcff4f10SPaul Stewart 				left -= elen;
812fcff4f10SPaul Stewart 				pos += elen;
813fcff4f10SPaul Stewart 				continue;
814fcff4f10SPaul Stewart 			}
8159690fb16SJohannes Berg 			break;
8169690fb16SJohannes Berg 		}
817dd76986bSJohannes Berg 
818dd76986bSJohannes Berg 		if (calc_crc && id < 64 && (filter & (1ULL << id)))
819dd76986bSJohannes Berg 			crc = crc32_be(crc, pos - 2, elen + 2);
820dd76986bSJohannes Berg 
821fcff4f10SPaul Stewart 		elem_parse_failed = false;
822fcff4f10SPaul Stewart 
823dd76986bSJohannes Berg 		switch (id) {
824dd76986bSJohannes Berg 		case WLAN_EID_SSID:
825dd76986bSJohannes Berg 			elems->ssid = pos;
826dd76986bSJohannes Berg 			elems->ssid_len = elen;
827dd76986bSJohannes Berg 			break;
828dd76986bSJohannes Berg 		case WLAN_EID_SUPP_RATES:
829dd76986bSJohannes Berg 			elems->supp_rates = pos;
830dd76986bSJohannes Berg 			elems->supp_rates_len = elen;
831dd76986bSJohannes Berg 			break;
832dd76986bSJohannes Berg 		case WLAN_EID_DS_PARAMS:
8331cd8e88eSJohannes Berg 			if (elen >= 1)
834dd76986bSJohannes Berg 				elems->ds_params = pos;
8351cd8e88eSJohannes Berg 			else
8361cd8e88eSJohannes Berg 				elem_parse_failed = true;
837dd76986bSJohannes Berg 			break;
838dd76986bSJohannes Berg 		case WLAN_EID_TIM:
839dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_tim_ie)) {
840dd76986bSJohannes Berg 				elems->tim = (void *)pos;
841dd76986bSJohannes Berg 				elems->tim_len = elen;
842fcff4f10SPaul Stewart 			} else
843fcff4f10SPaul Stewart 				elem_parse_failed = true;
844dd76986bSJohannes Berg 			break;
845dd76986bSJohannes Berg 		case WLAN_EID_CHALLENGE:
846dd76986bSJohannes Berg 			elems->challenge = pos;
847dd76986bSJohannes Berg 			elems->challenge_len = elen;
848dd76986bSJohannes Berg 			break;
849dd76986bSJohannes Berg 		case WLAN_EID_VENDOR_SPECIFIC:
850dd76986bSJohannes Berg 			if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
851dd76986bSJohannes Berg 			    pos[2] == 0xf2) {
852dd76986bSJohannes Berg 				/* Microsoft OUI (00:50:F2) */
853dd76986bSJohannes Berg 
854dd76986bSJohannes Berg 				if (calc_crc)
855dd76986bSJohannes Berg 					crc = crc32_be(crc, pos - 2, elen + 2);
856dd76986bSJohannes Berg 
857441a33baSJohannes Berg 				if (elen >= 5 && pos[3] == 2) {
858dd76986bSJohannes Berg 					/* OUI Type 2 - WMM IE */
859dd76986bSJohannes Berg 					if (pos[4] == 0) {
860dd76986bSJohannes Berg 						elems->wmm_info = pos;
861dd76986bSJohannes Berg 						elems->wmm_info_len = elen;
862dd76986bSJohannes Berg 					} else if (pos[4] == 1) {
863dd76986bSJohannes Berg 						elems->wmm_param = pos;
864dd76986bSJohannes Berg 						elems->wmm_param_len = elen;
865dd76986bSJohannes Berg 					}
866dd76986bSJohannes Berg 				}
867dd76986bSJohannes Berg 			}
868dd76986bSJohannes Berg 			break;
869dd76986bSJohannes Berg 		case WLAN_EID_RSN:
870dd76986bSJohannes Berg 			elems->rsn = pos;
871dd76986bSJohannes Berg 			elems->rsn_len = elen;
872dd76986bSJohannes Berg 			break;
873dd76986bSJohannes Berg 		case WLAN_EID_ERP_INFO:
8741946bed9SJohannes Berg 			if (elen >= 1)
875dd76986bSJohannes Berg 				elems->erp_info = pos;
8761946bed9SJohannes Berg 			else
8771946bed9SJohannes Berg 				elem_parse_failed = true;
878dd76986bSJohannes Berg 			break;
879dd76986bSJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
880dd76986bSJohannes Berg 			elems->ext_supp_rates = pos;
881dd76986bSJohannes Berg 			elems->ext_supp_rates_len = elen;
882dd76986bSJohannes Berg 			break;
883dd76986bSJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
884dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_cap))
885dd76986bSJohannes Berg 				elems->ht_cap_elem = (void *)pos;
886fcff4f10SPaul Stewart 			else
887fcff4f10SPaul Stewart 				elem_parse_failed = true;
888dd76986bSJohannes Berg 			break;
889074d46d1SJohannes Berg 		case WLAN_EID_HT_OPERATION:
890074d46d1SJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_operation))
891074d46d1SJohannes Berg 				elems->ht_operation = (void *)pos;
892fcff4f10SPaul Stewart 			else
893fcff4f10SPaul Stewart 				elem_parse_failed = true;
894dd76986bSJohannes Berg 			break;
895818255eaSMahesh Palivela 		case WLAN_EID_VHT_CAPABILITY:
896818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_cap))
897818255eaSMahesh Palivela 				elems->vht_cap_elem = (void *)pos;
898818255eaSMahesh Palivela 			else
899818255eaSMahesh Palivela 				elem_parse_failed = true;
900818255eaSMahesh Palivela 			break;
901818255eaSMahesh Palivela 		case WLAN_EID_VHT_OPERATION:
902818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_operation))
903818255eaSMahesh Palivela 				elems->vht_operation = (void *)pos;
904818255eaSMahesh Palivela 			else
905818255eaSMahesh Palivela 				elem_parse_failed = true;
906818255eaSMahesh Palivela 			break;
907bee7f586SJohannes Berg 		case WLAN_EID_OPMODE_NOTIF:
908bee7f586SJohannes Berg 			if (elen > 0)
909bee7f586SJohannes Berg 				elems->opmode_notif = pos;
910bee7f586SJohannes Berg 			else
911bee7f586SJohannes Berg 				elem_parse_failed = true;
912bee7f586SJohannes Berg 			break;
913dd76986bSJohannes Berg 		case WLAN_EID_MESH_ID:
914dd76986bSJohannes Berg 			elems->mesh_id = pos;
915dd76986bSJohannes Berg 			elems->mesh_id_len = elen;
916dd76986bSJohannes Berg 			break;
917dd76986bSJohannes Berg 		case WLAN_EID_MESH_CONFIG:
918dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_meshconf_ie))
919dd76986bSJohannes Berg 				elems->mesh_config = (void *)pos;
920fcff4f10SPaul Stewart 			else
921fcff4f10SPaul Stewart 				elem_parse_failed = true;
922dd76986bSJohannes Berg 			break;
923dd76986bSJohannes Berg 		case WLAN_EID_PEER_MGMT:
924dd76986bSJohannes Berg 			elems->peering = pos;
925dd76986bSJohannes Berg 			elems->peering_len = elen;
926dd76986bSJohannes Berg 			break;
9273f52b7e3SMarco Porsch 		case WLAN_EID_MESH_AWAKE_WINDOW:
9283f52b7e3SMarco Porsch 			if (elen >= 2)
9293f52b7e3SMarco Porsch 				elems->awake_window = (void *)pos;
9303f52b7e3SMarco Porsch 			break;
931dd76986bSJohannes Berg 		case WLAN_EID_PREQ:
932dd76986bSJohannes Berg 			elems->preq = pos;
933dd76986bSJohannes Berg 			elems->preq_len = elen;
934dd76986bSJohannes Berg 			break;
935dd76986bSJohannes Berg 		case WLAN_EID_PREP:
936dd76986bSJohannes Berg 			elems->prep = pos;
937dd76986bSJohannes Berg 			elems->prep_len = elen;
938dd76986bSJohannes Berg 			break;
939dd76986bSJohannes Berg 		case WLAN_EID_PERR:
940dd76986bSJohannes Berg 			elems->perr = pos;
941dd76986bSJohannes Berg 			elems->perr_len = elen;
942dd76986bSJohannes Berg 			break;
943dd76986bSJohannes Berg 		case WLAN_EID_RANN:
944dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_rann_ie))
945dd76986bSJohannes Berg 				elems->rann = (void *)pos;
946fcff4f10SPaul Stewart 			else
947fcff4f10SPaul Stewart 				elem_parse_failed = true;
948dd76986bSJohannes Berg 			break;
949dd76986bSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
9505bc1420bSJohannes Berg 			if (elen != sizeof(struct ieee80211_channel_sw_ie)) {
9515bc1420bSJohannes Berg 				elem_parse_failed = true;
9525bc1420bSJohannes Berg 				break;
9535bc1420bSJohannes Berg 			}
9545bc1420bSJohannes Berg 			elems->ch_switch_ie = (void *)pos;
955dd76986bSJohannes Berg 			break;
956b4f286a1SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
957b4f286a1SJohannes Berg 			if (elen != sizeof(struct ieee80211_ext_chansw_ie)) {
958b4f286a1SJohannes Berg 				elem_parse_failed = true;
959b4f286a1SJohannes Berg 				break;
960b4f286a1SJohannes Berg 			}
961b4f286a1SJohannes Berg 			elems->ext_chansw_ie = (void *)pos;
962b4f286a1SJohannes Berg 			break;
96385220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
96485220d71SJohannes Berg 			if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
96585220d71SJohannes Berg 				elem_parse_failed = true;
96685220d71SJohannes Berg 				break;
96785220d71SJohannes Berg 			}
96885220d71SJohannes Berg 			elems->sec_chan_offs = (void *)pos;
96985220d71SJohannes Berg 			break;
9708f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
9718f2535b9SChun-Yeow Yeoh 			if (elen !=
9728f2535b9SChun-Yeow Yeoh 			    sizeof(*elems->mesh_chansw_params_ie)) {
9738f2535b9SChun-Yeow Yeoh 				elem_parse_failed = true;
9748f2535b9SChun-Yeow Yeoh 				break;
9758f2535b9SChun-Yeow Yeoh 			}
9768f2535b9SChun-Yeow Yeoh 			elems->mesh_chansw_params_ie = (void *)pos;
9778f2535b9SChun-Yeow Yeoh 			break;
978b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
979b2e506bfSJohannes Berg 			if (!action ||
980b2e506bfSJohannes Berg 			    elen != sizeof(*elems->wide_bw_chansw_ie)) {
981b2e506bfSJohannes Berg 				elem_parse_failed = true;
982b2e506bfSJohannes Berg 				break;
983b2e506bfSJohannes Berg 			}
984b2e506bfSJohannes Berg 			elems->wide_bw_chansw_ie = (void *)pos;
985b2e506bfSJohannes Berg 			break;
986b2e506bfSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
987b2e506bfSJohannes Berg 			if (action) {
988b2e506bfSJohannes Berg 				elem_parse_failed = true;
989b2e506bfSJohannes Berg 				break;
990b2e506bfSJohannes Berg 			}
991b2e506bfSJohannes Berg 			/*
992b2e506bfSJohannes Berg 			 * This is a bit tricky, but as we only care about
993b2e506bfSJohannes Berg 			 * the wide bandwidth channel switch element, so
994b2e506bfSJohannes Berg 			 * just parse it out manually.
995b2e506bfSJohannes Berg 			 */
996b2e506bfSJohannes Berg 			ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
997b2e506bfSJohannes Berg 					      pos, elen);
998b2e506bfSJohannes Berg 			if (ie) {
999b2e506bfSJohannes Berg 				if (ie[1] == sizeof(*elems->wide_bw_chansw_ie))
1000b2e506bfSJohannes Berg 					elems->wide_bw_chansw_ie =
1001b2e506bfSJohannes Berg 						(void *)(ie + 2);
1002b2e506bfSJohannes Berg 				else
1003b2e506bfSJohannes Berg 					elem_parse_failed = true;
1004b2e506bfSJohannes Berg 			}
1005b2e506bfSJohannes Berg 			break;
1006dd76986bSJohannes Berg 		case WLAN_EID_COUNTRY:
1007dd76986bSJohannes Berg 			elems->country_elem = pos;
1008dd76986bSJohannes Berg 			elems->country_elem_len = elen;
1009dd76986bSJohannes Berg 			break;
1010dd76986bSJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
1011761a48d2SJohannes Berg 			if (elen != 1) {
1012761a48d2SJohannes Berg 				elem_parse_failed = true;
1013761a48d2SJohannes Berg 				break;
1014761a48d2SJohannes Berg 			}
1015dd76986bSJohannes Berg 			elems->pwr_constr_elem = pos;
1016dd76986bSJohannes Berg 			break;
1017dd76986bSJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
101879ba1d89SJohannes Berg 			if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
101979ba1d89SJohannes Berg 				elems->timeout_int = (void *)pos;
102079ba1d89SJohannes Berg 			else
102179ba1d89SJohannes Berg 				elem_parse_failed = true;
1022dd76986bSJohannes Berg 			break;
1023dd76986bSJohannes Berg 		default:
1024dd76986bSJohannes Berg 			break;
1025dd76986bSJohannes Berg 		}
1026dd76986bSJohannes Berg 
1027fcff4f10SPaul Stewart 		if (elem_parse_failed)
1028fcff4f10SPaul Stewart 			elems->parse_error = true;
1029fcff4f10SPaul Stewart 		else
10305df45690SJohannes Berg 			__set_bit(id, seen_elems);
1031fcff4f10SPaul Stewart 
1032dd76986bSJohannes Berg 		left -= elen;
1033dd76986bSJohannes Berg 		pos += elen;
1034dd76986bSJohannes Berg 	}
1035dd76986bSJohannes Berg 
1036fcff4f10SPaul Stewart 	if (left != 0)
1037fcff4f10SPaul Stewart 		elems->parse_error = true;
1038fcff4f10SPaul Stewart 
1039dd76986bSJohannes Berg 	return crc;
1040dd76986bSJohannes Berg }
1041dd76986bSJohannes Berg 
10423abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
10433abead59SJohannes Berg 			       bool bss_notify)
10445825fe10SJohannes Berg {
10455825fe10SJohannes Berg 	struct ieee80211_local *local = sdata->local;
10465825fe10SJohannes Berg 	struct ieee80211_tx_queue_params qparam;
104755de908aSJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
104854bcbc69SJohannes Berg 	int ac;
1049a8ce8544SStanislaw Gruszka 	bool use_11b, enable_qos;
1050aa837e1dSJohannes Berg 	int aCWmin, aCWmax;
10515825fe10SJohannes Berg 
10525825fe10SJohannes Berg 	if (!local->ops->conf_tx)
10535825fe10SJohannes Berg 		return;
10545825fe10SJohannes Berg 
105554bcbc69SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
105654bcbc69SJohannes Berg 		return;
105754bcbc69SJohannes Berg 
10585825fe10SJohannes Berg 	memset(&qparam, 0, sizeof(qparam));
10595825fe10SJohannes Berg 
106055de908aSJohannes Berg 	rcu_read_lock();
106155de908aSJohannes Berg 	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
106255de908aSJohannes Berg 	use_11b = (chanctx_conf &&
10634bf88530SJohannes Berg 		   chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
1064aa837e1dSJohannes Berg 		 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
106555de908aSJohannes Berg 	rcu_read_unlock();
10665825fe10SJohannes Berg 
1067a8ce8544SStanislaw Gruszka 	/*
1068a8ce8544SStanislaw Gruszka 	 * By default disable QoS in STA mode for old access points, which do
1069a8ce8544SStanislaw Gruszka 	 * not support 802.11e. New APs will provide proper queue parameters,
1070a8ce8544SStanislaw Gruszka 	 * that we will configure later.
1071a8ce8544SStanislaw Gruszka 	 */
1072a8ce8544SStanislaw Gruszka 	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
1073a8ce8544SStanislaw Gruszka 
1074aa837e1dSJohannes Berg 	/* Set defaults according to 802.11-2007 Table 7-37 */
1075aa837e1dSJohannes Berg 	aCWmax = 1023;
1076aa837e1dSJohannes Berg 	if (use_11b)
1077aa837e1dSJohannes Berg 		aCWmin = 31;
10785825fe10SJohannes Berg 	else
1079aa837e1dSJohannes Berg 		aCWmin = 15;
10805825fe10SJohannes Berg 
10811f4ffde8SFred Zhou 	/* Confiure old 802.11b/g medium access rules. */
10821f4ffde8SFred Zhou 	qparam.cw_max = aCWmax;
10831f4ffde8SFred Zhou 	qparam.cw_min = aCWmin;
10841f4ffde8SFred Zhou 	qparam.txop = 0;
10851f4ffde8SFred Zhou 	qparam.aifs = 2;
10861f4ffde8SFred Zhou 
10871f4ffde8SFred Zhou 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
10881f4ffde8SFred Zhou 		/* Update if QoS is enabled. */
1089a8ce8544SStanislaw Gruszka 		if (enable_qos) {
109054bcbc69SJohannes Berg 			switch (ac) {
10911d98fb12SJohannes Berg 			case IEEE80211_AC_BK:
10927ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
10937ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
10945825fe10SJohannes Berg 				qparam.txop = 0;
1095aa837e1dSJohannes Berg 				qparam.aifs = 7;
1096aa837e1dSJohannes Berg 				break;
1097a8ce8544SStanislaw Gruszka 			/* never happens but let's not leave undefined */
1098a8ce8544SStanislaw Gruszka 			default:
10991d98fb12SJohannes Berg 			case IEEE80211_AC_BE:
11007ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
11017ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
1102aa837e1dSJohannes Berg 				qparam.txop = 0;
1103aa837e1dSJohannes Berg 				qparam.aifs = 3;
1104aa837e1dSJohannes Berg 				break;
11051d98fb12SJohannes Berg 			case IEEE80211_AC_VI:
1106aa837e1dSJohannes Berg 				qparam.cw_max = aCWmin;
1107aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 2 - 1;
1108aa837e1dSJohannes Berg 				if (use_11b)
1109aa837e1dSJohannes Berg 					qparam.txop = 6016/32;
1110aa837e1dSJohannes Berg 				else
1111aa837e1dSJohannes Berg 					qparam.txop = 3008/32;
1112aa837e1dSJohannes Berg 				qparam.aifs = 2;
1113aa837e1dSJohannes Berg 				break;
11141d98fb12SJohannes Berg 			case IEEE80211_AC_VO:
1115aa837e1dSJohannes Berg 				qparam.cw_max = (aCWmin + 1) / 2 - 1;
1116aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 4 - 1;
1117aa837e1dSJohannes Berg 				if (use_11b)
1118aa837e1dSJohannes Berg 					qparam.txop = 3264/32;
1119aa837e1dSJohannes Berg 				else
1120aa837e1dSJohannes Berg 					qparam.txop = 1504/32;
1121aa837e1dSJohannes Berg 				qparam.aifs = 2;
1122aa837e1dSJohannes Berg 				break;
1123aa837e1dSJohannes Berg 			}
1124a8ce8544SStanislaw Gruszka 		}
11255825fe10SJohannes Berg 
1126ab13315aSKalle Valo 		qparam.uapsd = false;
1127ab13315aSKalle Valo 
112854bcbc69SJohannes Berg 		sdata->tx_conf[ac] = qparam;
112954bcbc69SJohannes Berg 		drv_conf_tx(local, sdata, ac, &qparam);
1130aa837e1dSJohannes Berg 	}
1131e1b3ec1aSStanislaw Gruszka 
1132f142c6b9SJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
1133f142c6b9SJohannes Berg 	    sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) {
1134a8ce8544SStanislaw Gruszka 		sdata->vif.bss_conf.qos = enable_qos;
11353abead59SJohannes Berg 		if (bss_notify)
11363abead59SJohannes Berg 			ieee80211_bss_info_change_notify(sdata,
11373abead59SJohannes Berg 							 BSS_CHANGED_QOS);
11385825fe10SJohannes Berg 	}
1139d9734979SSujith }
1140e50db65cSJohannes Berg 
114146900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1142700e8ea6SJouni Malinen 			 u16 transaction, u16 auth_alg, u16 status,
11434a3cb702SJohannes Berg 			 const u8 *extra, size_t extra_len, const u8 *da,
11441672c0e3SJohannes Berg 			 const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
11451672c0e3SJohannes Berg 			 u32 tx_flags)
114646900298SJohannes Berg {
114746900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
114846900298SJohannes Berg 	struct sk_buff *skb;
114946900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1150fffd0934SJohannes Berg 	int err;
115146900298SJohannes Berg 
115215e230abSFred Zhou 	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
115315e230abSFred Zhou 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
1154d15b8459SJoe Perches 	if (!skb)
115546900298SJohannes Berg 		return;
1156d15b8459SJoe Perches 
115746900298SJohannes Berg 	skb_reserve(skb, local->hw.extra_tx_headroom);
115846900298SJohannes Berg 
115946900298SJohannes Berg 	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
116046900298SJohannes Berg 	memset(mgmt, 0, 24 + 6);
116146900298SJohannes Berg 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
116246900298SJohannes Berg 					  IEEE80211_STYPE_AUTH);
1163efa6a09dSAntonio Quartulli 	memcpy(mgmt->da, da, ETH_ALEN);
116447846c9bSJohannes Berg 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
116546900298SJohannes Berg 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
116646900298SJohannes Berg 	mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
116746900298SJohannes Berg 	mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
1168700e8ea6SJouni Malinen 	mgmt->u.auth.status_code = cpu_to_le16(status);
116946900298SJohannes Berg 	if (extra)
117046900298SJohannes Berg 		memcpy(skb_put(skb, extra_len), extra, extra_len);
117146900298SJohannes Berg 
1172fffd0934SJohannes Berg 	if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
1173fffd0934SJohannes Berg 		mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
1174fffd0934SJohannes Berg 		err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
1175fffd0934SJohannes Berg 		WARN_ON(err);
1176fffd0934SJohannes Berg 	}
1177fffd0934SJohannes Berg 
11781672c0e3SJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
11791672c0e3SJohannes Berg 					tx_flags;
118062ae67beSJohannes Berg 	ieee80211_tx_skb(sdata, skb);
118146900298SJohannes Berg }
118246900298SJohannes Berg 
11836ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
11846ae16775SAntonio Quartulli 				    const u8 *bssid, u16 stype, u16 reason,
11856ae16775SAntonio Quartulli 				    bool send_frame, u8 *frame_buf)
11866ae16775SAntonio Quartulli {
11876ae16775SAntonio Quartulli 	struct ieee80211_local *local = sdata->local;
11886ae16775SAntonio Quartulli 	struct sk_buff *skb;
11896ae16775SAntonio Quartulli 	struct ieee80211_mgmt *mgmt = (void *)frame_buf;
11906ae16775SAntonio Quartulli 
11916ae16775SAntonio Quartulli 	/* build frame */
11926ae16775SAntonio Quartulli 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
11936ae16775SAntonio Quartulli 	mgmt->duration = 0; /* initialize only */
11946ae16775SAntonio Quartulli 	mgmt->seq_ctrl = 0; /* initialize only */
11956ae16775SAntonio Quartulli 	memcpy(mgmt->da, bssid, ETH_ALEN);
11966ae16775SAntonio Quartulli 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
11976ae16775SAntonio Quartulli 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
11986ae16775SAntonio Quartulli 	/* u.deauth.reason_code == u.disassoc.reason_code */
11996ae16775SAntonio Quartulli 	mgmt->u.deauth.reason_code = cpu_to_le16(reason);
12006ae16775SAntonio Quartulli 
12016ae16775SAntonio Quartulli 	if (send_frame) {
12026ae16775SAntonio Quartulli 		skb = dev_alloc_skb(local->hw.extra_tx_headroom +
12036ae16775SAntonio Quartulli 				    IEEE80211_DEAUTH_FRAME_LEN);
12046ae16775SAntonio Quartulli 		if (!skb)
12056ae16775SAntonio Quartulli 			return;
12066ae16775SAntonio Quartulli 
12076ae16775SAntonio Quartulli 		skb_reserve(skb, local->hw.extra_tx_headroom);
12086ae16775SAntonio Quartulli 
12096ae16775SAntonio Quartulli 		/* copy in frame */
12106ae16775SAntonio Quartulli 		memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
12116ae16775SAntonio Quartulli 		       mgmt, IEEE80211_DEAUTH_FRAME_LEN);
12126ae16775SAntonio Quartulli 
12136ae16775SAntonio Quartulli 		if (sdata->vif.type != NL80211_IFTYPE_STATION ||
12146ae16775SAntonio Quartulli 		    !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
12156ae16775SAntonio Quartulli 			IEEE80211_SKB_CB(skb)->flags |=
12166ae16775SAntonio Quartulli 				IEEE80211_TX_INTFL_DONT_ENCRYPT;
12176ae16775SAntonio Quartulli 
12186ae16775SAntonio Quartulli 		ieee80211_tx_skb(sdata, skb);
12196ae16775SAntonio Quartulli 	}
12206ae16775SAntonio Quartulli }
12216ae16775SAntonio Quartulli 
1222c56ef672SDavid Spinadel static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
1223c56ef672SDavid Spinadel 					 u8 *buffer, size_t buffer_len,
1224c56ef672SDavid Spinadel 					 const u8 *ie, size_t ie_len,
1225c56ef672SDavid Spinadel 					 enum ieee80211_band band,
1226c56ef672SDavid Spinadel 					 u32 rate_mask,
1227c56ef672SDavid Spinadel 					 struct cfg80211_chan_def *chandef,
1228c56ef672SDavid Spinadel 					 size_t *offset)
1229de95a54bSJohannes Berg {
1230de95a54bSJohannes Berg 	struct ieee80211_supported_band *sband;
1231c604b9f2SJohannes Berg 	u8 *pos = buffer, *end = buffer + buffer_len;
1232c56ef672SDavid Spinadel 	size_t noffset;
12338e664fb3SJohannes Berg 	int supp_rates_len, i;
12348dcb2003SJouni Malinen 	u8 rates[32];
12358dcb2003SJouni Malinen 	int num_rates;
12368dcb2003SJouni Malinen 	int ext_rates_len;
12372103dec1SSimon Wunderlich 	int shift;
12382103dec1SSimon Wunderlich 	u32 rate_flags;
1239de95a54bSJohannes Berg 
1240c56ef672SDavid Spinadel 	*offset = 0;
1241c56ef672SDavid Spinadel 
12424d36ec58SJohannes Berg 	sband = local->hw.wiphy->bands[band];
1243d811b3d5SArik Nemtsov 	if (WARN_ON_ONCE(!sband))
1244d811b3d5SArik Nemtsov 		return 0;
1245de95a54bSJohannes Berg 
12462103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(chandef);
12472103dec1SSimon Wunderlich 	shift = ieee80211_chandef_get_shift(chandef);
12482103dec1SSimon Wunderlich 
12498dcb2003SJouni Malinen 	num_rates = 0;
12508dcb2003SJouni Malinen 	for (i = 0; i < sband->n_bitrates; i++) {
12518dcb2003SJouni Malinen 		if ((BIT(i) & rate_mask) == 0)
12528dcb2003SJouni Malinen 			continue; /* skip rate */
12532103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
12542103dec1SSimon Wunderlich 			continue;
12552103dec1SSimon Wunderlich 
12562103dec1SSimon Wunderlich 		rates[num_rates++] =
12572103dec1SSimon Wunderlich 			(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
12582103dec1SSimon Wunderlich 					  (1 << shift) * 5);
12598dcb2003SJouni Malinen 	}
12608dcb2003SJouni Malinen 
12618dcb2003SJouni Malinen 	supp_rates_len = min_t(int, num_rates, 8);
12628e664fb3SJohannes Berg 
1263c604b9f2SJohannes Berg 	if (end - pos < 2 + supp_rates_len)
1264c604b9f2SJohannes Berg 		goto out_err;
1265de95a54bSJohannes Berg 	*pos++ = WLAN_EID_SUPP_RATES;
12668e664fb3SJohannes Berg 	*pos++ = supp_rates_len;
12678dcb2003SJouni Malinen 	memcpy(pos, rates, supp_rates_len);
12688dcb2003SJouni Malinen 	pos += supp_rates_len;
1269de95a54bSJohannes Berg 
12708e664fb3SJohannes Berg 	/* insert "request information" if in custom IEs */
12718e664fb3SJohannes Berg 	if (ie && ie_len) {
12728e664fb3SJohannes Berg 		static const u8 before_extrates[] = {
12738e664fb3SJohannes Berg 			WLAN_EID_SSID,
12748e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
12758e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
12768e664fb3SJohannes Berg 		};
12778e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
12788e664fb3SJohannes Berg 					     before_extrates,
12798e664fb3SJohannes Berg 					     ARRAY_SIZE(before_extrates),
1280c56ef672SDavid Spinadel 					     *offset);
1281c56ef672SDavid Spinadel 		if (end - pos < noffset - *offset)
1282c604b9f2SJohannes Berg 			goto out_err;
1283c56ef672SDavid Spinadel 		memcpy(pos, ie + *offset, noffset - *offset);
1284c56ef672SDavid Spinadel 		pos += noffset - *offset;
1285c56ef672SDavid Spinadel 		*offset = noffset;
12868e664fb3SJohannes Berg 	}
12878e664fb3SJohannes Berg 
12888dcb2003SJouni Malinen 	ext_rates_len = num_rates - supp_rates_len;
12898dcb2003SJouni Malinen 	if (ext_rates_len > 0) {
1290c604b9f2SJohannes Berg 		if (end - pos < 2 + ext_rates_len)
1291c604b9f2SJohannes Berg 			goto out_err;
1292de95a54bSJohannes Berg 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
12938dcb2003SJouni Malinen 		*pos++ = ext_rates_len;
12948dcb2003SJouni Malinen 		memcpy(pos, rates + supp_rates_len, ext_rates_len);
12958dcb2003SJouni Malinen 		pos += ext_rates_len;
12968e664fb3SJohannes Berg 	}
12978e664fb3SJohannes Berg 
12982103dec1SSimon Wunderlich 	if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
1299c604b9f2SJohannes Berg 		if (end - pos < 3)
1300c604b9f2SJohannes Berg 			goto out_err;
1301651b5225SJouni Malinen 		*pos++ = WLAN_EID_DS_PARAMS;
1302651b5225SJouni Malinen 		*pos++ = 1;
13032103dec1SSimon Wunderlich 		*pos++ = ieee80211_frequency_to_channel(
13042103dec1SSimon Wunderlich 				chandef->chan->center_freq);
1305651b5225SJouni Malinen 	}
1306651b5225SJouni Malinen 
13078e664fb3SJohannes Berg 	/* insert custom IEs that go before HT */
13088e664fb3SJohannes Berg 	if (ie && ie_len) {
13098e664fb3SJohannes Berg 		static const u8 before_ht[] = {
13108e664fb3SJohannes Berg 			WLAN_EID_SSID,
13118e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
13128e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
13138e664fb3SJohannes Berg 			WLAN_EID_EXT_SUPP_RATES,
13148e664fb3SJohannes Berg 			WLAN_EID_DS_PARAMS,
13158e664fb3SJohannes Berg 			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
13168e664fb3SJohannes Berg 		};
13178e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
13188e664fb3SJohannes Berg 					     before_ht, ARRAY_SIZE(before_ht),
1319c56ef672SDavid Spinadel 					     *offset);
1320c56ef672SDavid Spinadel 		if (end - pos < noffset - *offset)
1321c604b9f2SJohannes Berg 			goto out_err;
1322c56ef672SDavid Spinadel 		memcpy(pos, ie + *offset, noffset - *offset);
1323c56ef672SDavid Spinadel 		pos += noffset - *offset;
1324c56ef672SDavid Spinadel 		*offset = noffset;
1325de95a54bSJohannes Berg 	}
1326de95a54bSJohannes Berg 
1327c604b9f2SJohannes Berg 	if (sband->ht_cap.ht_supported) {
1328c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
1329c604b9f2SJohannes Berg 			goto out_err;
1330ef96a842SBen Greear 		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
1331ef96a842SBen Greear 						sband->ht_cap.cap);
1332c604b9f2SJohannes Berg 	}
13335ef2d41aSJohannes Berg 
1334de95a54bSJohannes Berg 	/*
1335de95a54bSJohannes Berg 	 * If adding more here, adjust code in main.c
1336de95a54bSJohannes Berg 	 * that calculates local->scan_ies_len.
1337de95a54bSJohannes Berg 	 */
1338de95a54bSJohannes Berg 
13394d952300SJohannes Berg 	/* insert custom IEs that go before VHT */
13408e664fb3SJohannes Berg 	if (ie && ie_len) {
13414d952300SJohannes Berg 		static const u8 before_vht[] = {
13424d952300SJohannes Berg 			WLAN_EID_SSID,
13434d952300SJohannes Berg 			WLAN_EID_SUPP_RATES,
13444d952300SJohannes Berg 			WLAN_EID_REQUEST,
13454d952300SJohannes Berg 			WLAN_EID_EXT_SUPP_RATES,
13464d952300SJohannes Berg 			WLAN_EID_DS_PARAMS,
13474d952300SJohannes Berg 			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
13484d952300SJohannes Berg 			WLAN_EID_HT_CAPABILITY,
13494d952300SJohannes Berg 			WLAN_EID_BSS_COEX_2040,
13504d952300SJohannes Berg 			WLAN_EID_EXT_CAPABILITY,
13514d952300SJohannes Berg 			WLAN_EID_SSID_LIST,
13524d952300SJohannes Berg 			WLAN_EID_CHANNEL_USAGE,
13534d952300SJohannes Berg 			WLAN_EID_INTERWORKING,
13544d952300SJohannes Berg 			/* mesh ID can't happen here */
13554d952300SJohannes Berg 			/* 60 GHz can't happen here right now */
13564d952300SJohannes Berg 		};
13574d952300SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
13584d952300SJohannes Berg 					     before_vht, ARRAY_SIZE(before_vht),
1359c56ef672SDavid Spinadel 					     *offset);
1360c56ef672SDavid Spinadel 		if (end - pos < noffset - *offset)
1361c604b9f2SJohannes Berg 			goto out_err;
1362c56ef672SDavid Spinadel 		memcpy(pos, ie + *offset, noffset - *offset);
1363c56ef672SDavid Spinadel 		pos += noffset - *offset;
1364c56ef672SDavid Spinadel 		*offset = noffset;
1365de95a54bSJohannes Berg 	}
1366de95a54bSJohannes Berg 
1367c604b9f2SJohannes Berg 	if (sband->vht_cap.vht_supported) {
1368c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
1369c604b9f2SJohannes Berg 			goto out_err;
1370ba0afa2fSMahesh Palivela 		pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
1371ba0afa2fSMahesh Palivela 						 sband->vht_cap.cap);
1372c604b9f2SJohannes Berg 	}
1373ba0afa2fSMahesh Palivela 
1374de95a54bSJohannes Berg 	return pos - buffer;
1375c604b9f2SJohannes Berg  out_err:
1376c604b9f2SJohannes Berg 	WARN_ONCE(1, "not enough space for preq IEs\n");
1377c604b9f2SJohannes Berg 	return pos - buffer;
1378de95a54bSJohannes Berg }
1379de95a54bSJohannes Berg 
1380c56ef672SDavid Spinadel int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
1381c56ef672SDavid Spinadel 			     size_t buffer_len,
1382c56ef672SDavid Spinadel 			     struct ieee80211_scan_ies *ie_desc,
1383c56ef672SDavid Spinadel 			     const u8 *ie, size_t ie_len,
1384c56ef672SDavid Spinadel 			     u8 bands_used, u32 *rate_masks,
1385c56ef672SDavid Spinadel 			     struct cfg80211_chan_def *chandef)
1386c56ef672SDavid Spinadel {
1387c56ef672SDavid Spinadel 	size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
1388c56ef672SDavid Spinadel 	int i;
1389c56ef672SDavid Spinadel 
1390c56ef672SDavid Spinadel 	memset(ie_desc, 0, sizeof(*ie_desc));
1391c56ef672SDavid Spinadel 
1392c56ef672SDavid Spinadel 	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
1393c56ef672SDavid Spinadel 		if (bands_used & BIT(i)) {
1394c56ef672SDavid Spinadel 			pos += ieee80211_build_preq_ies_band(local,
1395c56ef672SDavid Spinadel 							     buffer + pos,
1396c56ef672SDavid Spinadel 							     buffer_len - pos,
1397c56ef672SDavid Spinadel 							     ie, ie_len, i,
1398c56ef672SDavid Spinadel 							     rate_masks[i],
1399c56ef672SDavid Spinadel 							     chandef,
1400c56ef672SDavid Spinadel 							     &custom_ie_offset);
1401c56ef672SDavid Spinadel 			ie_desc->ies[i] = buffer + old_pos;
1402c56ef672SDavid Spinadel 			ie_desc->len[i] = pos - old_pos;
1403c56ef672SDavid Spinadel 			old_pos = pos;
1404c56ef672SDavid Spinadel 		}
1405c56ef672SDavid Spinadel 	}
1406c56ef672SDavid Spinadel 
1407c56ef672SDavid Spinadel 	/* add any remaining custom IEs */
1408c56ef672SDavid Spinadel 	if (ie && ie_len) {
1409c56ef672SDavid Spinadel 		if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
1410c56ef672SDavid Spinadel 			      "not enough space for preq custom IEs\n"))
1411c56ef672SDavid Spinadel 			return pos;
1412c56ef672SDavid Spinadel 		memcpy(buffer + pos, ie + custom_ie_offset,
1413c56ef672SDavid Spinadel 		       ie_len - custom_ie_offset);
1414c56ef672SDavid Spinadel 		ie_desc->common_ies = buffer + pos;
1415c56ef672SDavid Spinadel 		ie_desc->common_ie_len = ie_len - custom_ie_offset;
1416c56ef672SDavid Spinadel 		pos += ie_len - custom_ie_offset;
1417c56ef672SDavid Spinadel 	}
1418c56ef672SDavid Spinadel 
1419c56ef672SDavid Spinadel 	return pos;
1420c56ef672SDavid Spinadel };
1421c56ef672SDavid Spinadel 
1422a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
142385a237feSJohannes Berg 					  u8 *dst, u32 ratemask,
14246b77863bSJohannes Berg 					  struct ieee80211_channel *chan,
1425de95a54bSJohannes Berg 					  const u8 *ssid, size_t ssid_len,
1426a806c558SPaul Stewart 					  const u8 *ie, size_t ie_len,
1427a806c558SPaul Stewart 					  bool directed)
142846900298SJohannes Berg {
142946900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
14302103dec1SSimon Wunderlich 	struct cfg80211_chan_def chandef;
143146900298SJohannes Berg 	struct sk_buff *skb;
143246900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1433b9a9ada1SJohannes Berg 	int ies_len;
1434c56ef672SDavid Spinadel 	u32 rate_masks[IEEE80211_NUM_BANDS] = {};
1435c56ef672SDavid Spinadel 	struct ieee80211_scan_ies dummy_ie_desc;
143646900298SJohannes Berg 
1437a806c558SPaul Stewart 	/*
1438a806c558SPaul Stewart 	 * Do not send DS Channel parameter for directed probe requests
1439a806c558SPaul Stewart 	 * in order to maximize the chance that we get a response.  Some
1440a806c558SPaul Stewart 	 * badly-behaved APs don't respond when this parameter is included.
1441a806c558SPaul Stewart 	 */
14422103dec1SSimon Wunderlich 	chandef.width = sdata->vif.bss_conf.chandef.width;
1443a806c558SPaul Stewart 	if (directed)
14442103dec1SSimon Wunderlich 		chandef.chan = NULL;
1445a806c558SPaul Stewart 	else
14462103dec1SSimon Wunderlich 		chandef.chan = chan;
1447651b5225SJouni Malinen 
14487c12ce8bSKalle Valo 	skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
1449b9a9ada1SJohannes Berg 				     ssid, ssid_len, 100 + ie_len);
14505b2bbf75SJohannes Berg 	if (!skb)
1451b9a9ada1SJohannes Berg 		return NULL;
1452b9a9ada1SJohannes Berg 
1453c56ef672SDavid Spinadel 	rate_masks[chan->band] = ratemask;
1454b9a9ada1SJohannes Berg 	ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
1455c56ef672SDavid Spinadel 					   skb_tailroom(skb), &dummy_ie_desc,
1456c56ef672SDavid Spinadel 					   ie, ie_len, BIT(chan->band),
1457c56ef672SDavid Spinadel 					   rate_masks, &chandef);
1458b9a9ada1SJohannes Berg 	skb_put(skb, ies_len);
14597c12ce8bSKalle Valo 
146046900298SJohannes Berg 	if (dst) {
14617c12ce8bSKalle Valo 		mgmt = (struct ieee80211_mgmt *) skb->data;
146246900298SJohannes Berg 		memcpy(mgmt->da, dst, ETH_ALEN);
146346900298SJohannes Berg 		memcpy(mgmt->bssid, dst, ETH_ALEN);
146446900298SJohannes Berg 	}
146546900298SJohannes Berg 
146662ae67beSJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
14675b2bbf75SJohannes Berg 
1468a619a4c0SJuuso Oikarinen 	return skb;
1469a619a4c0SJuuso Oikarinen }
1470a619a4c0SJuuso Oikarinen 
1471a619a4c0SJuuso Oikarinen void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
1472a619a4c0SJuuso Oikarinen 			      const u8 *ssid, size_t ssid_len,
1473a806c558SPaul Stewart 			      const u8 *ie, size_t ie_len,
14741672c0e3SJohannes Berg 			      u32 ratemask, bool directed, u32 tx_flags,
147555de908aSJohannes Berg 			      struct ieee80211_channel *channel, bool scan)
1476a619a4c0SJuuso Oikarinen {
1477a619a4c0SJuuso Oikarinen 	struct sk_buff *skb;
1478a619a4c0SJuuso Oikarinen 
1479fe94fe05SJohannes Berg 	skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
14806b77863bSJohannes Berg 					ssid, ssid_len,
148185a237feSJohannes Berg 					ie, ie_len, directed);
1482aad14cebSRajkumar Manoharan 	if (skb) {
14831672c0e3SJohannes Berg 		IEEE80211_SKB_CB(skb)->flags |= tx_flags;
148455de908aSJohannes Berg 		if (scan)
148555de908aSJohannes Berg 			ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
148655de908aSJohannes Berg 		else
1487a619a4c0SJuuso Oikarinen 			ieee80211_tx_skb(sdata, skb);
148846900298SJohannes Berg 	}
1489aad14cebSRajkumar Manoharan }
149046900298SJohannes Berg 
14912103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
149246900298SJohannes Berg 			    struct ieee802_11_elems *elems,
14939ebb61a2SAshok Nagarajan 			    enum ieee80211_band band, u32 *basic_rates)
149446900298SJohannes Berg {
149546900298SJohannes Berg 	struct ieee80211_supported_band *sband;
149646900298SJohannes Berg 	size_t num_rates;
14972103dec1SSimon Wunderlich 	u32 supp_rates, rate_flags;
14982103dec1SSimon Wunderlich 	int i, j, shift;
14992103dec1SSimon Wunderlich 	sband = sdata->local->hw.wiphy->bands[band];
15002103dec1SSimon Wunderlich 
15012103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
15022103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
150346900298SJohannes Berg 
15044ee73f33SMichal Kazior 	if (WARN_ON(!sband))
15054ee73f33SMichal Kazior 		return 1;
150646900298SJohannes Berg 
150746900298SJohannes Berg 	num_rates = sband->n_bitrates;
150846900298SJohannes Berg 	supp_rates = 0;
150946900298SJohannes Berg 	for (i = 0; i < elems->supp_rates_len +
151046900298SJohannes Berg 		     elems->ext_supp_rates_len; i++) {
151146900298SJohannes Berg 		u8 rate = 0;
151246900298SJohannes Berg 		int own_rate;
15139ebb61a2SAshok Nagarajan 		bool is_basic;
151446900298SJohannes Berg 		if (i < elems->supp_rates_len)
151546900298SJohannes Berg 			rate = elems->supp_rates[i];
151646900298SJohannes Berg 		else if (elems->ext_supp_rates)
151746900298SJohannes Berg 			rate = elems->ext_supp_rates
151846900298SJohannes Berg 				[i - elems->supp_rates_len];
151946900298SJohannes Berg 		own_rate = 5 * (rate & 0x7f);
15209ebb61a2SAshok Nagarajan 		is_basic = !!(rate & 0x80);
15219ebb61a2SAshok Nagarajan 
15229ebb61a2SAshok Nagarajan 		if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
15239ebb61a2SAshok Nagarajan 			continue;
15249ebb61a2SAshok Nagarajan 
15259ebb61a2SAshok Nagarajan 		for (j = 0; j < num_rates; j++) {
15262103dec1SSimon Wunderlich 			int brate;
15272103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[j].flags)
15282103dec1SSimon Wunderlich 			    != rate_flags)
15292103dec1SSimon Wunderlich 				continue;
15302103dec1SSimon Wunderlich 
15312103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
15322103dec1SSimon Wunderlich 					     1 << shift);
15332103dec1SSimon Wunderlich 
15342103dec1SSimon Wunderlich 			if (brate == own_rate) {
153546900298SJohannes Berg 				supp_rates |= BIT(j);
15369ebb61a2SAshok Nagarajan 				if (basic_rates && is_basic)
15379ebb61a2SAshok Nagarajan 					*basic_rates |= BIT(j);
15389ebb61a2SAshok Nagarajan 			}
15399ebb61a2SAshok Nagarajan 		}
154046900298SJohannes Berg 	}
154146900298SJohannes Berg 	return supp_rates;
154246900298SJohannes Berg }
1543f2753ddbSJohannes Berg 
154484f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local)
154584f6a01cSJohannes Berg {
154684f6a01cSJohannes Berg 	ieee80211_led_radio(local, false);
154767408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
154884f6a01cSJohannes Berg 
154984f6a01cSJohannes Berg 	cancel_work_sync(&local->reconfig_filter);
155084f6a01cSJohannes Berg 
155184f6a01cSJohannes Berg 	flush_workqueue(local->workqueue);
1552678f415fSLennert Buytenhek 	drv_stop(local);
155384f6a01cSJohannes Berg }
155484f6a01cSJohannes Berg 
1555f6837ba8SJohannes Berg static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
1556f6837ba8SJohannes Berg {
1557f6837ba8SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
1558f6837ba8SJohannes Berg 	struct ieee80211_chanctx *ctx;
1559f6837ba8SJohannes Berg 
1560f6837ba8SJohannes Berg 	/*
1561f6837ba8SJohannes Berg 	 * We get here if during resume the device can't be restarted properly.
1562f6837ba8SJohannes Berg 	 * We might also get here if this happens during HW reset, which is a
1563f6837ba8SJohannes Berg 	 * slightly different situation and we need to drop all connections in
1564f6837ba8SJohannes Berg 	 * the latter case.
1565f6837ba8SJohannes Berg 	 *
1566f6837ba8SJohannes Berg 	 * Ask cfg80211 to turn off all interfaces, this will result in more
1567f6837ba8SJohannes Berg 	 * warnings but at least we'll then get into a clean stopped state.
1568f6837ba8SJohannes Berg 	 */
1569f6837ba8SJohannes Berg 
1570f6837ba8SJohannes Berg 	local->resuming = false;
1571f6837ba8SJohannes Berg 	local->suspended = false;
1572f6837ba8SJohannes Berg 	local->started = false;
1573f6837ba8SJohannes Berg 
1574f6837ba8SJohannes Berg 	/* scheduled scan clearly can't be running any more, but tell
1575f6837ba8SJohannes Berg 	 * cfg80211 and clear local state
1576f6837ba8SJohannes Berg 	 */
1577f6837ba8SJohannes Berg 	ieee80211_sched_scan_end(local);
1578f6837ba8SJohannes Berg 
1579f6837ba8SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list)
1580f6837ba8SJohannes Berg 		sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
1581f6837ba8SJohannes Berg 
1582f6837ba8SJohannes Berg 	/* Mark channel contexts as not being in the driver any more to avoid
1583f6837ba8SJohannes Berg 	 * removing them from the driver during the shutdown process...
1584f6837ba8SJohannes Berg 	 */
1585f6837ba8SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1586f6837ba8SJohannes Berg 	list_for_each_entry(ctx, &local->chanctx_list, list)
1587f6837ba8SJohannes Berg 		ctx->driver_present = false;
1588f6837ba8SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
1589f6837ba8SJohannes Berg 
1590f6837ba8SJohannes Berg 	cfg80211_shutdown_all_interfaces(local->hw.wiphy);
1591f6837ba8SJohannes Berg }
1592f6837ba8SJohannes Berg 
1593153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local,
1594153a5fc4SStanislaw Gruszka 				     struct ieee80211_sub_if_data *sdata)
1595153a5fc4SStanislaw Gruszka {
1596153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx_conf *conf;
1597153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx *ctx;
1598153a5fc4SStanislaw Gruszka 
1599153a5fc4SStanislaw Gruszka 	if (!local->use_chanctx)
1600153a5fc4SStanislaw Gruszka 		return;
1601153a5fc4SStanislaw Gruszka 
1602153a5fc4SStanislaw Gruszka 	mutex_lock(&local->chanctx_mtx);
1603153a5fc4SStanislaw Gruszka 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1604153a5fc4SStanislaw Gruszka 					 lockdep_is_held(&local->chanctx_mtx));
1605153a5fc4SStanislaw Gruszka 	if (conf) {
1606153a5fc4SStanislaw Gruszka 		ctx = container_of(conf, struct ieee80211_chanctx, conf);
1607153a5fc4SStanislaw Gruszka 		drv_assign_vif_chanctx(local, sdata, ctx);
1608153a5fc4SStanislaw Gruszka 	}
1609153a5fc4SStanislaw Gruszka 	mutex_unlock(&local->chanctx_mtx);
1610153a5fc4SStanislaw Gruszka }
1611153a5fc4SStanislaw Gruszka 
1612f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local)
1613f2753ddbSJohannes Berg {
1614f2753ddbSJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
1615f2753ddbSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
161655de908aSJohannes Berg 	struct ieee80211_chanctx *ctx;
1617f2753ddbSJohannes Berg 	struct sta_info *sta;
16182683d65bSEliad Peller 	int res, i;
1619d888130aSJohannes Berg 	bool reconfig_due_to_wowlan = false;
1620d43c6b6eSDavid Spinadel 	struct ieee80211_sub_if_data *sched_scan_sdata;
1621d43c6b6eSDavid Spinadel 	bool sched_scan_stopped = false;
1622d888130aSJohannes Berg 
16238f21b0adSJohannes Berg #ifdef CONFIG_PM
1624ceb99fe0SJohannes Berg 	if (local->suspended)
1625ceb99fe0SJohannes Berg 		local->resuming = true;
1626f2753ddbSJohannes Berg 
1627eecc4800SJohannes Berg 	if (local->wowlan) {
1628eecc4800SJohannes Berg 		res = drv_resume(local);
162927b3eb9cSJohannes Berg 		local->wowlan = false;
1630eecc4800SJohannes Berg 		if (res < 0) {
1631eecc4800SJohannes Berg 			local->resuming = false;
1632eecc4800SJohannes Berg 			return res;
1633eecc4800SJohannes Berg 		}
1634eecc4800SJohannes Berg 		if (res == 0)
1635eecc4800SJohannes Berg 			goto wake_up;
1636eecc4800SJohannes Berg 		WARN_ON(res > 1);
1637eecc4800SJohannes Berg 		/*
1638eecc4800SJohannes Berg 		 * res is 1, which means the driver requested
1639eecc4800SJohannes Berg 		 * to go through a regular reset on wakeup.
1640eecc4800SJohannes Berg 		 */
1641d888130aSJohannes Berg 		reconfig_due_to_wowlan = true;
1642eecc4800SJohannes Berg 	}
1643eecc4800SJohannes Berg #endif
164494f9b97bSJohannes Berg 	/* everything else happens only if HW was up & running */
164594f9b97bSJohannes Berg 	if (!local->open_count)
164694f9b97bSJohannes Berg 		goto wake_up;
164794f9b97bSJohannes Berg 
164824feda00SLuis R. Rodriguez 	/*
164924feda00SLuis R. Rodriguez 	 * Upon resume hardware can sometimes be goofy due to
165024feda00SLuis R. Rodriguez 	 * various platform / driver / bus issues, so restarting
165124feda00SLuis R. Rodriguez 	 * the device may at times not work immediately. Propagate
165224feda00SLuis R. Rodriguez 	 * the error.
165324feda00SLuis R. Rodriguez 	 */
165424487981SJohannes Berg 	res = drv_start(local);
165524feda00SLuis R. Rodriguez 	if (res) {
1656f6837ba8SJohannes Berg 		if (local->suspended)
1657f6837ba8SJohannes Berg 			WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
1658f6837ba8SJohannes Berg 		else
1659f6837ba8SJohannes Berg 			WARN(1, "Hardware became unavailable during restart.\n");
1660f6837ba8SJohannes Berg 		ieee80211_handle_reconfig_failure(local);
166124feda00SLuis R. Rodriguez 		return res;
166224feda00SLuis R. Rodriguez 	}
1663f2753ddbSJohannes Berg 
16647f281975SYogesh Ashok Powar 	/* setup fragmentation threshold */
16657f281975SYogesh Ashok Powar 	drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
16667f281975SYogesh Ashok Powar 
16677f281975SYogesh Ashok Powar 	/* setup RTS threshold */
16687f281975SYogesh Ashok Powar 	drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
16697f281975SYogesh Ashok Powar 
16707f281975SYogesh Ashok Powar 	/* reset coverage class */
16717f281975SYogesh Ashok Powar 	drv_set_coverage_class(local, hw->wiphy->coverage_class);
16727f281975SYogesh Ashok Powar 
16731f87f7d3SJohannes Berg 	ieee80211_led_radio(local, true);
167467408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local,
167567408c8cSJohannes Berg 				   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
1676f2753ddbSJohannes Berg 
1677f2753ddbSJohannes Berg 	/* add interfaces */
16784b6f1dd6SJohannes Berg 	sdata = rtnl_dereference(local->monitor_sdata);
16794b6f1dd6SJohannes Berg 	if (sdata) {
16803c3e21e7SJohannes Berg 		/* in HW restart it exists already */
16813c3e21e7SJohannes Berg 		WARN_ON(local->resuming);
16824b6f1dd6SJohannes Berg 		res = drv_add_interface(local, sdata);
16834b6f1dd6SJohannes Berg 		if (WARN_ON(res)) {
16840c2bef46SMonam Agarwal 			RCU_INIT_POINTER(local->monitor_sdata, NULL);
16854b6f1dd6SJohannes Berg 			synchronize_net();
16864b6f1dd6SJohannes Berg 			kfree(sdata);
16874b6f1dd6SJohannes Berg 		}
16884b6f1dd6SJohannes Berg 	}
16894b6f1dd6SJohannes Berg 
1690f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1691f2753ddbSJohannes Berg 		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
1692f2753ddbSJohannes Berg 		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
16931ed32e4fSJohannes Berg 		    ieee80211_sdata_running(sdata))
16947b7eab6fSJohannes Berg 			res = drv_add_interface(local, sdata);
1695f2753ddbSJohannes Berg 	}
1696f2753ddbSJohannes Berg 
169755de908aSJohannes Berg 	/* add channel contexts */
1698f0dea9c7SArend van Spriel 	if (local->use_chanctx) {
169955de908aSJohannes Berg 		mutex_lock(&local->chanctx_mtx);
170055de908aSJohannes Berg 		list_for_each_entry(ctx, &local->chanctx_list, list)
170155de908aSJohannes Berg 			WARN_ON(drv_add_chanctx(local, ctx));
170255de908aSJohannes Berg 		mutex_unlock(&local->chanctx_mtx);
170355de908aSJohannes Berg 
17046352c87fSJohannes Berg 		list_for_each_entry(sdata, &local->interfaces, list) {
17056352c87fSJohannes Berg 			if (!ieee80211_sdata_running(sdata))
17066352c87fSJohannes Berg 				continue;
1707153a5fc4SStanislaw Gruszka 			ieee80211_assign_chanctx(local, sdata);
17086352c87fSJohannes Berg 		}
17096352c87fSJohannes Berg 
1710fe5f2559SJohannes Berg 		sdata = rtnl_dereference(local->monitor_sdata);
1711153a5fc4SStanislaw Gruszka 		if (sdata && ieee80211_sdata_running(sdata))
1712153a5fc4SStanislaw Gruszka 			ieee80211_assign_chanctx(local, sdata);
17137df180f7SZhao, Gang 	}
1714fe5f2559SJohannes Berg 
1715f2753ddbSJohannes Berg 	/* add STAs back */
171634e89507SJohannes Berg 	mutex_lock(&local->sta_mtx);
1717f2753ddbSJohannes Berg 	list_for_each_entry(sta, &local->sta_list, list) {
1718f09603a2SJohannes Berg 		enum ieee80211_sta_state state;
1719f09603a2SJohannes Berg 
17202e8d397eSArik Nemtsov 		if (!sta->uploaded)
17212e8d397eSArik Nemtsov 			continue;
17222e8d397eSArik Nemtsov 
17232e8d397eSArik Nemtsov 		/* AP-mode stations will be added later */
17242e8d397eSArik Nemtsov 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
17252e8d397eSArik Nemtsov 			continue;
17262e8d397eSArik Nemtsov 
1727f09603a2SJohannes Berg 		for (state = IEEE80211_STA_NOTEXIST;
1728bd34ab62SMeenakshi Venkataraman 		     state < sta->sta_state; state++)
17292e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
17302e8d397eSArik Nemtsov 					      state + 1));
1731f2753ddbSJohannes Berg 	}
173234e89507SJohannes Berg 	mutex_unlock(&local->sta_mtx);
1733f2753ddbSJohannes Berg 
17342683d65bSEliad Peller 	/* reconfigure tx conf */
173554bcbc69SJohannes Berg 	if (hw->queues >= IEEE80211_NUM_ACS) {
1736f6f3def3SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
1737f6f3def3SEliad Peller 			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
1738f6f3def3SEliad Peller 			    sdata->vif.type == NL80211_IFTYPE_MONITOR ||
1739f6f3def3SEliad Peller 			    !ieee80211_sdata_running(sdata))
1740f6f3def3SEliad Peller 				continue;
1741f6f3def3SEliad Peller 
174254bcbc69SJohannes Berg 			for (i = 0; i < IEEE80211_NUM_ACS; i++)
174354bcbc69SJohannes Berg 				drv_conf_tx(local, sdata, i,
174454bcbc69SJohannes Berg 					    &sdata->tx_conf[i]);
174554bcbc69SJohannes Berg 		}
1746f6f3def3SEliad Peller 	}
17472683d65bSEliad Peller 
1748f2753ddbSJohannes Berg 	/* reconfigure hardware */
1749f2753ddbSJohannes Berg 	ieee80211_hw_config(local, ~0);
1750f2753ddbSJohannes Berg 
1751f2753ddbSJohannes Berg 	ieee80211_configure_filter(local);
1752f2753ddbSJohannes Berg 
1753f2753ddbSJohannes Berg 	/* Finally also reconfigure all the BSS information */
1754f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1755ac8dd506SJohannes Berg 		u32 changed;
1756ac8dd506SJohannes Berg 
17579607e6b6SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1758f2753ddbSJohannes Berg 			continue;
1759ac8dd506SJohannes Berg 
1760ac8dd506SJohannes Berg 		/* common change flags for all interface types */
1761ac8dd506SJohannes Berg 		changed = BSS_CHANGED_ERP_CTS_PROT |
1762ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_PREAMBLE |
1763ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_SLOT |
1764ac8dd506SJohannes Berg 			  BSS_CHANGED_HT |
1765ac8dd506SJohannes Berg 			  BSS_CHANGED_BASIC_RATES |
1766ac8dd506SJohannes Berg 			  BSS_CHANGED_BEACON_INT |
1767ac8dd506SJohannes Berg 			  BSS_CHANGED_BSSID |
17684ced3f74SJohannes Berg 			  BSS_CHANGED_CQM |
176955de47f6SEliad Peller 			  BSS_CHANGED_QOS |
17701ea6f9c0SJohannes Berg 			  BSS_CHANGED_IDLE |
17711ea6f9c0SJohannes Berg 			  BSS_CHANGED_TXPOWER;
1772ac8dd506SJohannes Berg 
1773f2753ddbSJohannes Berg 		switch (sdata->vif.type) {
1774f2753ddbSJohannes Berg 		case NL80211_IFTYPE_STATION:
17750d392e93SEliad Peller 			changed |= BSS_CHANGED_ASSOC |
1776ab095877SEliad Peller 				   BSS_CHANGED_ARP_FILTER |
1777ab095877SEliad Peller 				   BSS_CHANGED_PS;
1778c65dd147SEmmanuel Grumbach 
1779989c6505SAlexander Bondar 			/* Re-send beacon info report to the driver */
1780989c6505SAlexander Bondar 			if (sdata->u.mgd.have_beacon)
1781989c6505SAlexander Bondar 				changed |= BSS_CHANGED_BEACON_INFO;
1782c65dd147SEmmanuel Grumbach 
17838d61ffa5SJohannes Berg 			sdata_lock(sdata);
1784ac8dd506SJohannes Berg 			ieee80211_bss_info_change_notify(sdata, changed);
17858d61ffa5SJohannes Berg 			sdata_unlock(sdata);
1786ac8dd506SJohannes Berg 			break;
1787f2753ddbSJohannes Berg 		case NL80211_IFTYPE_ADHOC:
1788ac8dd506SJohannes Berg 			changed |= BSS_CHANGED_IBSS;
1789ac8dd506SJohannes Berg 			/* fall through */
1790f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP:
1791339afbf4SJohannes Berg 			changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
1792e7979ac7SArik Nemtsov 
17931041638fSJohannes Berg 			if (sdata->vif.type == NL80211_IFTYPE_AP) {
1794e7979ac7SArik Nemtsov 				changed |= BSS_CHANGED_AP_PROBE_RESP;
1795e7979ac7SArik Nemtsov 
17961041638fSJohannes Berg 				if (rcu_access_pointer(sdata->u.ap.beacon))
17971041638fSJohannes Berg 					drv_start_ap(local, sdata);
17981041638fSJohannes Berg 			}
17991041638fSJohannes Berg 
18007827493bSArik Nemtsov 			/* fall through */
1801f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
18028da34932SJohannes Berg 			if (sdata->vif.bss_conf.enable_beacon) {
1803ac8dd506SJohannes Berg 				changed |= BSS_CHANGED_BEACON |
1804ac8dd506SJohannes Berg 					   BSS_CHANGED_BEACON_ENABLED;
18052d0ddec5SJohannes Berg 				ieee80211_bss_info_change_notify(sdata, changed);
18068da34932SJohannes Berg 			}
1807f2753ddbSJohannes Berg 			break;
1808f2753ddbSJohannes Berg 		case NL80211_IFTYPE_WDS:
1809f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
1810f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
181198104fdeSJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
1812b205786eSZhao, Gang 			/* nothing to do */
1813f142c6b9SJohannes Berg 			break;
1814f2753ddbSJohannes Berg 		case NL80211_IFTYPE_UNSPECIFIED:
18152e161f78SJohannes Berg 		case NUM_NL80211_IFTYPES:
18162ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_CLIENT:
18172ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_GO:
1818f2753ddbSJohannes Berg 			WARN_ON(1);
1819f2753ddbSJohannes Berg 			break;
1820f2753ddbSJohannes Berg 		}
1821f2753ddbSJohannes Berg 	}
1822f2753ddbSJohannes Berg 
18238e1b23b9SEyal Shapira 	ieee80211_recalc_ps(local, -1);
18248e1b23b9SEyal Shapira 
18252a419056SJohannes Berg 	/*
18266e1b1b24SEliad Peller 	 * The sta might be in psm against the ap (e.g. because
18276e1b1b24SEliad Peller 	 * this was the state before a hw restart), so we
18286e1b1b24SEliad Peller 	 * explicitly send a null packet in order to make sure
18296e1b1b24SEliad Peller 	 * it'll sync against the ap (and get out of psm).
18306e1b1b24SEliad Peller 	 */
18316e1b1b24SEliad Peller 	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
18326e1b1b24SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
18336e1b1b24SEliad Peller 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
18346e1b1b24SEliad Peller 				continue;
183520f544eeSJohannes Berg 			if (!sdata->u.mgd.associated)
183620f544eeSJohannes Berg 				continue;
18376e1b1b24SEliad Peller 
18386e1b1b24SEliad Peller 			ieee80211_send_nullfunc(local, sdata, 0);
18396e1b1b24SEliad Peller 		}
18406e1b1b24SEliad Peller 	}
18416e1b1b24SEliad Peller 
18422e8d397eSArik Nemtsov 	/* APs are now beaconing, add back stations */
18432e8d397eSArik Nemtsov 	mutex_lock(&local->sta_mtx);
18442e8d397eSArik Nemtsov 	list_for_each_entry(sta, &local->sta_list, list) {
18452e8d397eSArik Nemtsov 		enum ieee80211_sta_state state;
18462e8d397eSArik Nemtsov 
18472e8d397eSArik Nemtsov 		if (!sta->uploaded)
18482e8d397eSArik Nemtsov 			continue;
18492e8d397eSArik Nemtsov 
18502e8d397eSArik Nemtsov 		if (sta->sdata->vif.type != NL80211_IFTYPE_AP)
18512e8d397eSArik Nemtsov 			continue;
18522e8d397eSArik Nemtsov 
18532e8d397eSArik Nemtsov 		for (state = IEEE80211_STA_NOTEXIST;
18542e8d397eSArik Nemtsov 		     state < sta->sta_state; state++)
18552e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
18562e8d397eSArik Nemtsov 					      state + 1));
18572e8d397eSArik Nemtsov 	}
18582e8d397eSArik Nemtsov 	mutex_unlock(&local->sta_mtx);
18592e8d397eSArik Nemtsov 
18607b21aea0SEyal Shapira 	/* add back keys */
18617b21aea0SEyal Shapira 	list_for_each_entry(sdata, &local->interfaces, list)
18627b21aea0SEyal Shapira 		if (ieee80211_sdata_running(sdata))
18637b21aea0SEyal Shapira 			ieee80211_enable_keys(sdata);
18647b21aea0SEyal Shapira 
1865c6209488SEliad Peller  wake_up:
186604800adaSArik Nemtsov 	local->in_reconfig = false;
186704800adaSArik Nemtsov 	barrier();
186804800adaSArik Nemtsov 
18693c3e21e7SJohannes Berg 	if (local->monitors == local->open_count && local->monitors > 0)
18703c3e21e7SJohannes Berg 		ieee80211_add_virtual_monitor(local);
18713c3e21e7SJohannes Berg 
18726e1b1b24SEliad Peller 	/*
18732a419056SJohannes Berg 	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
18742a419056SJohannes Berg 	 * sessions can be established after a resume.
18752a419056SJohannes Berg 	 *
18762a419056SJohannes Berg 	 * Also tear down aggregation sessions since reconfiguring
18772a419056SJohannes Berg 	 * them in a hardware restart scenario is not easily done
18782a419056SJohannes Berg 	 * right now, and the hardware will have lost information
18792a419056SJohannes Berg 	 * about the sessions, but we and the AP still think they
18802a419056SJohannes Berg 	 * are active. This is really a workaround though.
18812a419056SJohannes Berg 	 */
188274e2bd1fSWey-Yi Guy 	if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
18832a419056SJohannes Berg 		mutex_lock(&local->sta_mtx);
18842a419056SJohannes Berg 
18852a419056SJohannes Berg 		list_for_each_entry(sta, &local->sta_list, list) {
1886c82c4a80SJohannes Berg 			ieee80211_sta_tear_down_BA_sessions(
1887c82c4a80SJohannes Berg 					sta, AGG_STOP_LOCAL_REQUEST);
1888c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
188974e2bd1fSWey-Yi Guy 		}
18902a419056SJohannes Berg 
18912a419056SJohannes Berg 		mutex_unlock(&local->sta_mtx);
189274e2bd1fSWey-Yi Guy 	}
189374e2bd1fSWey-Yi Guy 
1894445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
1895cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
1896cca07b00SLuciano Coelho 					false);
1897f2753ddbSJohannes Berg 
18985bb644a0SJohannes Berg 	/*
189932769814SArik Nemtsov 	 * Reconfigure sched scan if it was interrupted by FW restart or
190032769814SArik Nemtsov 	 * suspend.
190132769814SArik Nemtsov 	 */
190232769814SArik Nemtsov 	mutex_lock(&local->mtx);
190332769814SArik Nemtsov 	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
190432769814SArik Nemtsov 						lockdep_is_held(&local->mtx));
190532769814SArik Nemtsov 	if (sched_scan_sdata && local->sched_scan_req)
190632769814SArik Nemtsov 		/*
190732769814SArik Nemtsov 		 * Sched scan stopped, but we don't want to report it. Instead,
190832769814SArik Nemtsov 		 * we're trying to reschedule.
190932769814SArik Nemtsov 		 */
191032769814SArik Nemtsov 		if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
191132769814SArik Nemtsov 							 local->sched_scan_req))
191232769814SArik Nemtsov 			sched_scan_stopped = true;
191332769814SArik Nemtsov 	mutex_unlock(&local->mtx);
191432769814SArik Nemtsov 
191532769814SArik Nemtsov 	if (sched_scan_stopped)
1916e669ba2dSEliad Peller 		cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
191732769814SArik Nemtsov 
191832769814SArik Nemtsov 	/*
19195bb644a0SJohannes Berg 	 * If this is for hw restart things are still running.
19205bb644a0SJohannes Berg 	 * We may want to change that later, however.
19215bb644a0SJohannes Berg 	 */
19228f21b0adSJohannes Berg 	if (!local->suspended || reconfig_due_to_wowlan)
19239214ad7fSJohannes Berg 		drv_restart_complete(local);
19248f21b0adSJohannes Berg 
19258f21b0adSJohannes Berg 	if (!local->suspended)
19265bb644a0SJohannes Berg 		return 0;
19275bb644a0SJohannes Berg 
19285bb644a0SJohannes Berg #ifdef CONFIG_PM
1929ceb99fe0SJohannes Berg 	/* first set suspended false, then resuming */
19305bb644a0SJohannes Berg 	local->suspended = false;
1931ceb99fe0SJohannes Berg 	mb();
1932ceb99fe0SJohannes Berg 	local->resuming = false;
19335bb644a0SJohannes Berg 
1934b8360ab8SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1935b8360ab8SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1936b8360ab8SJohannes Berg 			continue;
1937b8360ab8SJohannes Berg 		if (sdata->vif.type == NL80211_IFTYPE_STATION)
1938b8360ab8SJohannes Berg 			ieee80211_sta_restart(sdata);
1939b8360ab8SJohannes Berg 	}
1940b8360ab8SJohannes Berg 
194126d59535SJohannes Berg 	mod_timer(&local->sta_cleanup, jiffies + 1);
19425bb644a0SJohannes Berg #else
19435bb644a0SJohannes Berg 	WARN_ON(1);
19445bb644a0SJohannes Berg #endif
1945d43c6b6eSDavid Spinadel 
1946f2753ddbSJohannes Berg 	return 0;
1947f2753ddbSJohannes Berg }
194842935ecaSLuis R. Rodriguez 
194995acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
195095acac61SJohannes Berg {
195195acac61SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
195295acac61SJohannes Berg 	struct ieee80211_local *local;
195395acac61SJohannes Berg 	struct ieee80211_key *key;
195495acac61SJohannes Berg 
195595acac61SJohannes Berg 	if (WARN_ON(!vif))
195695acac61SJohannes Berg 		return;
195795acac61SJohannes Berg 
195895acac61SJohannes Berg 	sdata = vif_to_sdata(vif);
195995acac61SJohannes Berg 	local = sdata->local;
196095acac61SJohannes Berg 
196195acac61SJohannes Berg 	if (WARN_ON(!local->resuming))
196295acac61SJohannes Berg 		return;
196395acac61SJohannes Berg 
196495acac61SJohannes Berg 	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
196595acac61SJohannes Berg 		return;
196695acac61SJohannes Berg 
196795acac61SJohannes Berg 	sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME;
196895acac61SJohannes Berg 
196995acac61SJohannes Berg 	mutex_lock(&local->key_mtx);
197095acac61SJohannes Berg 	list_for_each_entry(key, &sdata->key_list, list)
197195acac61SJohannes Berg 		key->flags |= KEY_FLAG_TAINTED;
197295acac61SJohannes Berg 	mutex_unlock(&local->key_mtx);
197395acac61SJohannes Berg }
197495acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
197595acac61SJohannes Berg 
197604ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
19770f78231bSJohannes Berg {
197804ecd257SJohannes Berg 	struct ieee80211_local *local = sdata->local;
197904ecd257SJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
198004ecd257SJohannes Berg 	struct ieee80211_chanctx *chanctx;
19810f78231bSJohannes Berg 
198204ecd257SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
19830f78231bSJohannes Berg 
198404ecd257SJohannes Berg 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
198504ecd257SJohannes Berg 					lockdep_is_held(&local->chanctx_mtx));
19860f78231bSJohannes Berg 
198704ecd257SJohannes Berg 	if (WARN_ON_ONCE(!chanctx_conf))
19885d8e4237SJohannes Berg 		goto unlock;
19890f78231bSJohannes Berg 
199004ecd257SJohannes Berg 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
199104ecd257SJohannes Berg 	ieee80211_recalc_smps_chanctx(local, chanctx);
19925d8e4237SJohannes Berg  unlock:
199304ecd257SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
19940f78231bSJohannes Berg }
19958e664fb3SJohannes Berg 
199621f659bfSEliad Peller void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
199721f659bfSEliad Peller {
199821f659bfSEliad Peller 	struct ieee80211_local *local = sdata->local;
199921f659bfSEliad Peller 	struct ieee80211_chanctx_conf *chanctx_conf;
200021f659bfSEliad Peller 	struct ieee80211_chanctx *chanctx;
200121f659bfSEliad Peller 
200221f659bfSEliad Peller 	mutex_lock(&local->chanctx_mtx);
200321f659bfSEliad Peller 
200421f659bfSEliad Peller 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
200521f659bfSEliad Peller 					lockdep_is_held(&local->chanctx_mtx));
200621f659bfSEliad Peller 
200721f659bfSEliad Peller 	if (WARN_ON_ONCE(!chanctx_conf))
200821f659bfSEliad Peller 		goto unlock;
200921f659bfSEliad Peller 
201021f659bfSEliad Peller 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
201121f659bfSEliad Peller 	ieee80211_recalc_chanctx_min_def(local, chanctx);
201221f659bfSEliad Peller  unlock:
201321f659bfSEliad Peller 	mutex_unlock(&local->chanctx_mtx);
201421f659bfSEliad Peller }
201521f659bfSEliad Peller 
20168e664fb3SJohannes Berg static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
20178e664fb3SJohannes Berg {
20188e664fb3SJohannes Berg 	int i;
20198e664fb3SJohannes Berg 
20208e664fb3SJohannes Berg 	for (i = 0; i < n_ids; i++)
20218e664fb3SJohannes Berg 		if (ids[i] == id)
20228e664fb3SJohannes Berg 			return true;
20238e664fb3SJohannes Berg 	return false;
20248e664fb3SJohannes Berg }
20258e664fb3SJohannes Berg 
20268e664fb3SJohannes Berg /**
20278e664fb3SJohannes Berg  * ieee80211_ie_split - split an IE buffer according to ordering
20288e664fb3SJohannes Berg  *
20298e664fb3SJohannes Berg  * @ies: the IE buffer
20308e664fb3SJohannes Berg  * @ielen: the length of the IE buffer
20318e664fb3SJohannes Berg  * @ids: an array with element IDs that are allowed before
20328e664fb3SJohannes Berg  *	the split
20338e664fb3SJohannes Berg  * @n_ids: the size of the element ID array
20348e664fb3SJohannes Berg  * @offset: offset where to start splitting in the buffer
20358e664fb3SJohannes Berg  *
20368e664fb3SJohannes Berg  * This function splits an IE buffer by updating the @offset
20378e664fb3SJohannes Berg  * variable to point to the location where the buffer should be
20388e664fb3SJohannes Berg  * split.
20398e664fb3SJohannes Berg  *
20408e664fb3SJohannes Berg  * It assumes that the given IE buffer is well-formed, this
20418e664fb3SJohannes Berg  * has to be guaranteed by the caller!
20428e664fb3SJohannes Berg  *
20438e664fb3SJohannes Berg  * It also assumes that the IEs in the buffer are ordered
20448e664fb3SJohannes Berg  * correctly, if not the result of using this function will not
20458e664fb3SJohannes Berg  * be ordered correctly either, i.e. it does no reordering.
20468e664fb3SJohannes Berg  *
20478e664fb3SJohannes Berg  * The function returns the offset where the next part of the
20488e664fb3SJohannes Berg  * buffer starts, which may be @ielen if the entire (remainder)
20498e664fb3SJohannes Berg  * of the buffer should be used.
20508e664fb3SJohannes Berg  */
20518e664fb3SJohannes Berg size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
20528e664fb3SJohannes Berg 			  const u8 *ids, int n_ids, size_t offset)
20538e664fb3SJohannes Berg {
20548e664fb3SJohannes Berg 	size_t pos = offset;
20558e664fb3SJohannes Berg 
20568e664fb3SJohannes Berg 	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
20578e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
20588e664fb3SJohannes Berg 
20598e664fb3SJohannes Berg 	return pos;
20608e664fb3SJohannes Berg }
20618e664fb3SJohannes Berg 
20628e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
20638e664fb3SJohannes Berg {
20648e664fb3SJohannes Berg 	size_t pos = offset;
20658e664fb3SJohannes Berg 
20668e664fb3SJohannes Berg 	while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
20678e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
20688e664fb3SJohannes Berg 
20698e664fb3SJohannes Berg 	return pos;
20708e664fb3SJohannes Berg }
2071615f7b9bSMeenakshi Venkataraman 
2072615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata,
2073615f7b9bSMeenakshi Venkataraman 					    int rssi_min_thold,
2074615f7b9bSMeenakshi Venkataraman 					    int rssi_max_thold)
2075615f7b9bSMeenakshi Venkataraman {
2076615f7b9bSMeenakshi Venkataraman 	trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold);
2077615f7b9bSMeenakshi Venkataraman 
2078615f7b9bSMeenakshi Venkataraman 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
2079615f7b9bSMeenakshi Venkataraman 		return;
2080615f7b9bSMeenakshi Venkataraman 
2081615f7b9bSMeenakshi Venkataraman 	/*
2082615f7b9bSMeenakshi Venkataraman 	 * Scale up threshold values before storing it, as the RSSI averaging
2083615f7b9bSMeenakshi Venkataraman 	 * algorithm uses a scaled up value as well. Change this scaling
2084615f7b9bSMeenakshi Venkataraman 	 * factor if the RSSI averaging algorithm changes.
2085615f7b9bSMeenakshi Venkataraman 	 */
2086615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_min_thold = rssi_min_thold*16;
2087615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_max_thold = rssi_max_thold*16;
2088615f7b9bSMeenakshi Venkataraman }
2089615f7b9bSMeenakshi Venkataraman 
2090615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
2091615f7b9bSMeenakshi Venkataraman 				    int rssi_min_thold,
2092615f7b9bSMeenakshi Venkataraman 				    int rssi_max_thold)
2093615f7b9bSMeenakshi Venkataraman {
2094615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2095615f7b9bSMeenakshi Venkataraman 
2096615f7b9bSMeenakshi Venkataraman 	WARN_ON(rssi_min_thold == rssi_max_thold ||
2097615f7b9bSMeenakshi Venkataraman 		rssi_min_thold > rssi_max_thold);
2098615f7b9bSMeenakshi Venkataraman 
2099615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, rssi_min_thold,
2100615f7b9bSMeenakshi Venkataraman 				       rssi_max_thold);
2101615f7b9bSMeenakshi Venkataraman }
2102615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports);
2103615f7b9bSMeenakshi Venkataraman 
2104615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
2105615f7b9bSMeenakshi Venkataraman {
2106615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2107615f7b9bSMeenakshi Venkataraman 
2108615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, 0, 0);
2109615f7b9bSMeenakshi Venkataraman }
2110615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
2111768db343SArik Nemtsov 
2112ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
211342e7aa77SAlexander Simon 			      u16 cap)
211442e7aa77SAlexander Simon {
211542e7aa77SAlexander Simon 	__le16 tmp;
211642e7aa77SAlexander Simon 
211742e7aa77SAlexander Simon 	*pos++ = WLAN_EID_HT_CAPABILITY;
211842e7aa77SAlexander Simon 	*pos++ = sizeof(struct ieee80211_ht_cap);
211942e7aa77SAlexander Simon 	memset(pos, 0, sizeof(struct ieee80211_ht_cap));
212042e7aa77SAlexander Simon 
212142e7aa77SAlexander Simon 	/* capability flags */
212242e7aa77SAlexander Simon 	tmp = cpu_to_le16(cap);
212342e7aa77SAlexander Simon 	memcpy(pos, &tmp, sizeof(u16));
212442e7aa77SAlexander Simon 	pos += sizeof(u16);
212542e7aa77SAlexander Simon 
212642e7aa77SAlexander Simon 	/* AMPDU parameters */
2127ef96a842SBen Greear 	*pos++ = ht_cap->ampdu_factor |
2128ef96a842SBen Greear 		 (ht_cap->ampdu_density <<
212942e7aa77SAlexander Simon 			IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
213042e7aa77SAlexander Simon 
213142e7aa77SAlexander Simon 	/* MCS set */
2132ef96a842SBen Greear 	memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
2133ef96a842SBen Greear 	pos += sizeof(ht_cap->mcs);
213442e7aa77SAlexander Simon 
213542e7aa77SAlexander Simon 	/* extended capabilities */
213642e7aa77SAlexander Simon 	pos += sizeof(__le16);
213742e7aa77SAlexander Simon 
213842e7aa77SAlexander Simon 	/* BF capabilities */
213942e7aa77SAlexander Simon 	pos += sizeof(__le32);
214042e7aa77SAlexander Simon 
214142e7aa77SAlexander Simon 	/* antenna selection */
214242e7aa77SAlexander Simon 	pos += sizeof(u8);
214342e7aa77SAlexander Simon 
214442e7aa77SAlexander Simon 	return pos;
214542e7aa77SAlexander Simon }
214642e7aa77SAlexander Simon 
2147ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
2148ba0afa2fSMahesh Palivela 			       u32 cap)
2149ba0afa2fSMahesh Palivela {
2150ba0afa2fSMahesh Palivela 	__le32 tmp;
2151ba0afa2fSMahesh Palivela 
2152ba0afa2fSMahesh Palivela 	*pos++ = WLAN_EID_VHT_CAPABILITY;
2153d4950281SMahesh Palivela 	*pos++ = sizeof(struct ieee80211_vht_cap);
2154d4950281SMahesh Palivela 	memset(pos, 0, sizeof(struct ieee80211_vht_cap));
2155ba0afa2fSMahesh Palivela 
2156ba0afa2fSMahesh Palivela 	/* capability flags */
2157ba0afa2fSMahesh Palivela 	tmp = cpu_to_le32(cap);
2158ba0afa2fSMahesh Palivela 	memcpy(pos, &tmp, sizeof(u32));
2159ba0afa2fSMahesh Palivela 	pos += sizeof(u32);
2160ba0afa2fSMahesh Palivela 
2161ba0afa2fSMahesh Palivela 	/* VHT MCS set */
2162ba0afa2fSMahesh Palivela 	memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
2163ba0afa2fSMahesh Palivela 	pos += sizeof(vht_cap->vht_mcs);
2164ba0afa2fSMahesh Palivela 
2165ba0afa2fSMahesh Palivela 	return pos;
2166ba0afa2fSMahesh Palivela }
2167ba0afa2fSMahesh Palivela 
2168074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
21694bf88530SJohannes Berg 			       const struct cfg80211_chan_def *chandef,
2170431e3154SAshok Nagarajan 			       u16 prot_mode)
217142e7aa77SAlexander Simon {
2172074d46d1SJohannes Berg 	struct ieee80211_ht_operation *ht_oper;
217342e7aa77SAlexander Simon 	/* Build HT Information */
2174074d46d1SJohannes Berg 	*pos++ = WLAN_EID_HT_OPERATION;
2175074d46d1SJohannes Berg 	*pos++ = sizeof(struct ieee80211_ht_operation);
2176074d46d1SJohannes Berg 	ht_oper = (struct ieee80211_ht_operation *)pos;
21774bf88530SJohannes Berg 	ht_oper->primary_chan = ieee80211_frequency_to_channel(
21784bf88530SJohannes Berg 					chandef->chan->center_freq);
21794bf88530SJohannes Berg 	switch (chandef->width) {
21804bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
21814bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
21824bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80:
21834bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
21844bf88530SJohannes Berg 		if (chandef->center_freq1 > chandef->chan->center_freq)
21854bf88530SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
21864bf88530SJohannes Berg 		else
2187074d46d1SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
218842e7aa77SAlexander Simon 		break;
218942e7aa77SAlexander Simon 	default:
2190074d46d1SJohannes Berg 		ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
219142e7aa77SAlexander Simon 		break;
219242e7aa77SAlexander Simon 	}
2193aee286c2SThomas Pedersen 	if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
21944bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
21954bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20)
2196074d46d1SJohannes Berg 		ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
2197ff3cc5f4SSimon Wunderlich 
2198431e3154SAshok Nagarajan 	ht_oper->operation_mode = cpu_to_le16(prot_mode);
2199074d46d1SJohannes Berg 	ht_oper->stbc_param = 0x0000;
220042e7aa77SAlexander Simon 
220142e7aa77SAlexander Simon 	/* It seems that Basic MCS set and Supported MCS set
220242e7aa77SAlexander Simon 	   are identical for the first 10 bytes */
2203074d46d1SJohannes Berg 	memset(&ht_oper->basic_set, 0, 16);
2204074d46d1SJohannes Berg 	memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
220542e7aa77SAlexander Simon 
2206074d46d1SJohannes Berg 	return pos + sizeof(struct ieee80211_ht_operation);
220742e7aa77SAlexander Simon }
220842e7aa77SAlexander Simon 
22094bf88530SJohannes Berg void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
22104a3cb702SJohannes Berg 				  const struct ieee80211_ht_operation *ht_oper,
22114bf88530SJohannes Berg 				  struct cfg80211_chan_def *chandef)
221242e7aa77SAlexander Simon {
221342e7aa77SAlexander Simon 	enum nl80211_channel_type channel_type;
221442e7aa77SAlexander Simon 
22154bf88530SJohannes Berg 	if (!ht_oper) {
22164bf88530SJohannes Berg 		cfg80211_chandef_create(chandef, control_chan,
22174bf88530SJohannes Berg 					NL80211_CHAN_NO_HT);
22184bf88530SJohannes Berg 		return;
22194bf88530SJohannes Berg 	}
222042e7aa77SAlexander Simon 
2221074d46d1SJohannes Berg 	switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
222242e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
222342e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT20;
222442e7aa77SAlexander Simon 		break;
222542e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
222642e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40PLUS;
222742e7aa77SAlexander Simon 		break;
222842e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
222942e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40MINUS;
223042e7aa77SAlexander Simon 		break;
223142e7aa77SAlexander Simon 	default:
223242e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_NO_HT;
223342e7aa77SAlexander Simon 	}
223442e7aa77SAlexander Simon 
22354bf88530SJohannes Berg 	cfg80211_chandef_create(chandef, control_chan, channel_type);
223642e7aa77SAlexander Simon }
223742e7aa77SAlexander Simon 
22382103dec1SSimon Wunderlich int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
22392103dec1SSimon Wunderlich 			     const struct ieee80211_supported_band *sband,
22402103dec1SSimon Wunderlich 			     const u8 *srates, int srates_len, u32 *rates)
22412103dec1SSimon Wunderlich {
22422103dec1SSimon Wunderlich 	u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
22432103dec1SSimon Wunderlich 	int shift = ieee80211_chandef_get_shift(chandef);
22442103dec1SSimon Wunderlich 	struct ieee80211_rate *br;
22452103dec1SSimon Wunderlich 	int brate, rate, i, j, count = 0;
22462103dec1SSimon Wunderlich 
22472103dec1SSimon Wunderlich 	*rates = 0;
22482103dec1SSimon Wunderlich 
22492103dec1SSimon Wunderlich 	for (i = 0; i < srates_len; i++) {
22502103dec1SSimon Wunderlich 		rate = srates[i] & 0x7f;
22512103dec1SSimon Wunderlich 
22522103dec1SSimon Wunderlich 		for (j = 0; j < sband->n_bitrates; j++) {
22532103dec1SSimon Wunderlich 			br = &sband->bitrates[j];
22542103dec1SSimon Wunderlich 			if ((rate_flags & br->flags) != rate_flags)
22552103dec1SSimon Wunderlich 				continue;
22562103dec1SSimon Wunderlich 
22572103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
22582103dec1SSimon Wunderlich 			if (brate == rate) {
22592103dec1SSimon Wunderlich 				*rates |= BIT(j);
22602103dec1SSimon Wunderlich 				count++;
22612103dec1SSimon Wunderlich 				break;
22622103dec1SSimon Wunderlich 			}
22632103dec1SSimon Wunderlich 		}
22642103dec1SSimon Wunderlich 	}
22652103dec1SSimon Wunderlich 	return count;
22662103dec1SSimon Wunderlich }
22672103dec1SSimon Wunderlich 
2268fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
22696b77863bSJohannes Berg 			    struct sk_buff *skb, bool need_basic,
22706b77863bSJohannes Berg 			    enum ieee80211_band band)
2271768db343SArik Nemtsov {
2272768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2273768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
22742103dec1SSimon Wunderlich 	int rate, shift;
2275768db343SArik Nemtsov 	u8 i, rates, *pos;
2276fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
22772103dec1SSimon Wunderlich 	u32 rate_flags;
2278768db343SArik Nemtsov 
22792103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
22802103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
22816b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
22822103dec1SSimon Wunderlich 	rates = 0;
22832103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
22842103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
22852103dec1SSimon Wunderlich 			continue;
22862103dec1SSimon Wunderlich 		rates++;
22872103dec1SSimon Wunderlich 	}
2288768db343SArik Nemtsov 	if (rates > 8)
2289768db343SArik Nemtsov 		rates = 8;
2290768db343SArik Nemtsov 
2291768db343SArik Nemtsov 	if (skb_tailroom(skb) < rates + 2)
2292768db343SArik Nemtsov 		return -ENOMEM;
2293768db343SArik Nemtsov 
2294768db343SArik Nemtsov 	pos = skb_put(skb, rates + 2);
2295768db343SArik Nemtsov 	*pos++ = WLAN_EID_SUPP_RATES;
2296768db343SArik Nemtsov 	*pos++ = rates;
2297768db343SArik Nemtsov 	for (i = 0; i < rates; i++) {
2298657c3e0cSAshok Nagarajan 		u8 basic = 0;
22992103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
23002103dec1SSimon Wunderlich 			continue;
23012103dec1SSimon Wunderlich 
2302657c3e0cSAshok Nagarajan 		if (need_basic && basic_rates & BIT(i))
2303657c3e0cSAshok Nagarajan 			basic = 0x80;
2304768db343SArik Nemtsov 		rate = sband->bitrates[i].bitrate;
23052103dec1SSimon Wunderlich 		rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
23062103dec1SSimon Wunderlich 				    5 * (1 << shift));
23072103dec1SSimon Wunderlich 		*pos++ = basic | (u8) rate;
2308768db343SArik Nemtsov 	}
2309768db343SArik Nemtsov 
2310768db343SArik Nemtsov 	return 0;
2311768db343SArik Nemtsov }
2312768db343SArik Nemtsov 
2313fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
23146b77863bSJohannes Berg 				struct sk_buff *skb, bool need_basic,
23156b77863bSJohannes Berg 				enum ieee80211_band band)
2316768db343SArik Nemtsov {
2317768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2318768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
2319cc63ec76SChun-Yeow Yeoh 	int rate, shift;
2320768db343SArik Nemtsov 	u8 i, exrates, *pos;
2321fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
23222103dec1SSimon Wunderlich 	u32 rate_flags;
23232103dec1SSimon Wunderlich 
23242103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
23252103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
2326768db343SArik Nemtsov 
23276b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
23282103dec1SSimon Wunderlich 	exrates = 0;
23292103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
23302103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
23312103dec1SSimon Wunderlich 			continue;
23322103dec1SSimon Wunderlich 		exrates++;
23332103dec1SSimon Wunderlich 	}
23342103dec1SSimon Wunderlich 
2335768db343SArik Nemtsov 	if (exrates > 8)
2336768db343SArik Nemtsov 		exrates -= 8;
2337768db343SArik Nemtsov 	else
2338768db343SArik Nemtsov 		exrates = 0;
2339768db343SArik Nemtsov 
2340768db343SArik Nemtsov 	if (skb_tailroom(skb) < exrates + 2)
2341768db343SArik Nemtsov 		return -ENOMEM;
2342768db343SArik Nemtsov 
2343768db343SArik Nemtsov 	if (exrates) {
2344768db343SArik Nemtsov 		pos = skb_put(skb, exrates + 2);
2345768db343SArik Nemtsov 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
2346768db343SArik Nemtsov 		*pos++ = exrates;
2347768db343SArik Nemtsov 		for (i = 8; i < sband->n_bitrates; i++) {
2348657c3e0cSAshok Nagarajan 			u8 basic = 0;
23492103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[i].flags)
23502103dec1SSimon Wunderlich 			    != rate_flags)
23512103dec1SSimon Wunderlich 				continue;
2352657c3e0cSAshok Nagarajan 			if (need_basic && basic_rates & BIT(i))
2353657c3e0cSAshok Nagarajan 				basic = 0x80;
23542103dec1SSimon Wunderlich 			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
23552103dec1SSimon Wunderlich 					    5 * (1 << shift));
23562103dec1SSimon Wunderlich 			*pos++ = basic | (u8) rate;
2357768db343SArik Nemtsov 		}
2358768db343SArik Nemtsov 	}
2359768db343SArik Nemtsov 	return 0;
2360768db343SArik Nemtsov }
23611dae27f8SWey-Yi Guy 
23621dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif)
23631dae27f8SWey-Yi Guy {
23641dae27f8SWey-Yi Guy 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
23651dae27f8SWey-Yi Guy 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
23661dae27f8SWey-Yi Guy 
2367be6bcabcSWey-Yi Guy 	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) {
2368be6bcabcSWey-Yi Guy 		/* non-managed type inferfaces */
2369be6bcabcSWey-Yi Guy 		return 0;
2370be6bcabcSWey-Yi Guy 	}
23713a7bba64SEmmanuel Grumbach 	return ifmgd->ave_beacon_signal / 16;
23721dae27f8SWey-Yi Guy }
23730d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
237404ecd257SJohannes Berg 
237504ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
237604ecd257SJohannes Berg {
237704ecd257SJohannes Berg 	if (!mcs)
237804ecd257SJohannes Berg 		return 1;
237904ecd257SJohannes Berg 
238004ecd257SJohannes Berg 	/* TODO: consider rx_highest */
238104ecd257SJohannes Berg 
238204ecd257SJohannes Berg 	if (mcs->rx_mask[3])
238304ecd257SJohannes Berg 		return 4;
238404ecd257SJohannes Berg 	if (mcs->rx_mask[2])
238504ecd257SJohannes Berg 		return 3;
238604ecd257SJohannes Berg 	if (mcs->rx_mask[1])
238704ecd257SJohannes Berg 		return 2;
238804ecd257SJohannes Berg 	return 1;
238904ecd257SJohannes Berg }
2390f4bda337SThomas Pedersen 
2391f4bda337SThomas Pedersen /**
2392f4bda337SThomas Pedersen  * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
2393f4bda337SThomas Pedersen  * @local: mac80211 hw info struct
2394f4bda337SThomas Pedersen  * @status: RX status
2395f4bda337SThomas Pedersen  * @mpdu_len: total MPDU length (including FCS)
2396f4bda337SThomas Pedersen  * @mpdu_offset: offset into MPDU to calculate timestamp at
2397f4bda337SThomas Pedersen  *
2398f4bda337SThomas Pedersen  * This function calculates the RX timestamp at the given MPDU offset, taking
2399f4bda337SThomas Pedersen  * into account what the RX timestamp was. An offset of 0 will just normalize
2400f4bda337SThomas Pedersen  * the timestamp to TSF at beginning of MPDU reception.
2401f4bda337SThomas Pedersen  */
2402f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
2403f4bda337SThomas Pedersen 				     struct ieee80211_rx_status *status,
2404f4bda337SThomas Pedersen 				     unsigned int mpdu_len,
2405f4bda337SThomas Pedersen 				     unsigned int mpdu_offset)
2406f4bda337SThomas Pedersen {
2407f4bda337SThomas Pedersen 	u64 ts = status->mactime;
2408f4bda337SThomas Pedersen 	struct rate_info ri;
2409f4bda337SThomas Pedersen 	u16 rate;
2410f4bda337SThomas Pedersen 
2411f4bda337SThomas Pedersen 	if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
2412f4bda337SThomas Pedersen 		return 0;
2413f4bda337SThomas Pedersen 
2414f4bda337SThomas Pedersen 	memset(&ri, 0, sizeof(ri));
2415f4bda337SThomas Pedersen 
2416f4bda337SThomas Pedersen 	/* Fill cfg80211 rate info */
2417f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_HT) {
2418f4bda337SThomas Pedersen 		ri.mcs = status->rate_idx;
2419f4bda337SThomas Pedersen 		ri.flags |= RATE_INFO_FLAGS_MCS;
2420f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_40MHZ)
2421f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
2422f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_SHORT_GI)
2423f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
24245614618eSJohannes Berg 	} else if (status->flag & RX_FLAG_VHT) {
24255614618eSJohannes Berg 		ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
24265614618eSJohannes Berg 		ri.mcs = status->rate_idx;
24275614618eSJohannes Berg 		ri.nss = status->vht_nss;
24285614618eSJohannes Berg 		if (status->flag & RX_FLAG_40MHZ)
24295614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
24301b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_80MHZ)
24315614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
24321b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
24335614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
24341b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_160MHZ)
24355614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
24365614618eSJohannes Berg 		if (status->flag & RX_FLAG_SHORT_GI)
24375614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
2438f4bda337SThomas Pedersen 	} else {
2439f4bda337SThomas Pedersen 		struct ieee80211_supported_band *sband;
24402103dec1SSimon Wunderlich 		int shift = 0;
24412103dec1SSimon Wunderlich 		int bitrate;
24422103dec1SSimon Wunderlich 
24432103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_10MHZ)
24442103dec1SSimon Wunderlich 			shift = 1;
24452103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_5MHZ)
24462103dec1SSimon Wunderlich 			shift = 2;
2447f4bda337SThomas Pedersen 
2448f4bda337SThomas Pedersen 		sband = local->hw.wiphy->bands[status->band];
24492103dec1SSimon Wunderlich 		bitrate = sband->bitrates[status->rate_idx].bitrate;
24502103dec1SSimon Wunderlich 		ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
2451f4bda337SThomas Pedersen 	}
2452f4bda337SThomas Pedersen 
2453f4bda337SThomas Pedersen 	rate = cfg80211_calculate_bitrate(&ri);
2454d86aa4f8SJohannes Berg 	if (WARN_ONCE(!rate,
2455d86aa4f8SJohannes Berg 		      "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
2456d86aa4f8SJohannes Berg 		      status->flag, status->rate_idx, status->vht_nss))
2457d86aa4f8SJohannes Berg 		return 0;
2458f4bda337SThomas Pedersen 
2459f4bda337SThomas Pedersen 	/* rewind from end of MPDU */
2460f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_MACTIME_END)
2461f4bda337SThomas Pedersen 		ts -= mpdu_len * 8 * 10 / rate;
2462f4bda337SThomas Pedersen 
2463f4bda337SThomas Pedersen 	ts += mpdu_offset * 8 * 10 / rate;
2464f4bda337SThomas Pedersen 
2465f4bda337SThomas Pedersen 	return ts;
2466f4bda337SThomas Pedersen }
2467164eb02dSSimon Wunderlich 
2468164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
2469164eb02dSSimon Wunderlich {
2470164eb02dSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata;
2471d2859df5SJanusz Dziedzic 	struct cfg80211_chan_def chandef;
2472164eb02dSSimon Wunderlich 
247334a3740dSJohannes Berg 	mutex_lock(&local->mtx);
2474164eb02dSSimon Wunderlich 	mutex_lock(&local->iflist_mtx);
2475164eb02dSSimon Wunderlich 	list_for_each_entry(sdata, &local->interfaces, list) {
247634a3740dSJohannes Berg 		/* it might be waiting for the local->mtx, but then
247734a3740dSJohannes Berg 		 * by the time it gets it, sdata->wdev.cac_started
247834a3740dSJohannes Berg 		 * will no longer be true
247934a3740dSJohannes Berg 		 */
248034a3740dSJohannes Berg 		cancel_delayed_work(&sdata->dfs_cac_timer_work);
2481164eb02dSSimon Wunderlich 
2482164eb02dSSimon Wunderlich 		if (sdata->wdev.cac_started) {
2483d2859df5SJanusz Dziedzic 			chandef = sdata->vif.bss_conf.chandef;
2484164eb02dSSimon Wunderlich 			ieee80211_vif_release_channel(sdata);
2485164eb02dSSimon Wunderlich 			cfg80211_cac_event(sdata->dev,
2486d2859df5SJanusz Dziedzic 					   &chandef,
2487164eb02dSSimon Wunderlich 					   NL80211_RADAR_CAC_ABORTED,
2488164eb02dSSimon Wunderlich 					   GFP_KERNEL);
2489164eb02dSSimon Wunderlich 		}
2490164eb02dSSimon Wunderlich 	}
2491164eb02dSSimon Wunderlich 	mutex_unlock(&local->iflist_mtx);
249234a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
2493164eb02dSSimon Wunderlich }
2494164eb02dSSimon Wunderlich 
2495164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work)
2496164eb02dSSimon Wunderlich {
2497164eb02dSSimon Wunderlich 	struct ieee80211_local *local =
2498164eb02dSSimon Wunderlich 		container_of(work, struct ieee80211_local, radar_detected_work);
249984a3d1c9SJanusz Dziedzic 	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
2500164eb02dSSimon Wunderlich 
2501164eb02dSSimon Wunderlich 	ieee80211_dfs_cac_cancel(local);
2502164eb02dSSimon Wunderlich 
2503164eb02dSSimon Wunderlich 	if (local->use_chanctx)
2504164eb02dSSimon Wunderlich 		/* currently not handled */
2505164eb02dSSimon Wunderlich 		WARN_ON(1);
250684a3d1c9SJanusz Dziedzic 	else
2507164eb02dSSimon Wunderlich 		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
2508164eb02dSSimon Wunderlich }
2509164eb02dSSimon Wunderlich 
2510164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw)
2511164eb02dSSimon Wunderlich {
2512164eb02dSSimon Wunderlich 	struct ieee80211_local *local = hw_to_local(hw);
2513164eb02dSSimon Wunderlich 
2514164eb02dSSimon Wunderlich 	trace_api_radar_detected(local);
2515164eb02dSSimon Wunderlich 
2516164eb02dSSimon Wunderlich 	ieee80211_queue_work(hw, &local->radar_detected_work);
2517164eb02dSSimon Wunderlich }
2518164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected);
2519e6b7cde4SSimon Wunderlich 
2520e6b7cde4SSimon Wunderlich u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
2521e6b7cde4SSimon Wunderlich {
2522e6b7cde4SSimon Wunderlich 	u32 ret;
2523e6b7cde4SSimon Wunderlich 	int tmp;
2524e6b7cde4SSimon Wunderlich 
2525e6b7cde4SSimon Wunderlich 	switch (c->width) {
2526e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20:
2527e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2528e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2529e6b7cde4SSimon Wunderlich 		break;
2530e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_40:
2531e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20;
2532e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->chan->center_freq;
2533e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_40MHZ |
2534e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_VHT;
2535e6b7cde4SSimon Wunderlich 		break;
2536e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80:
2537e6b7cde4SSimon Wunderlich 		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
2538e6b7cde4SSimon Wunderlich 		/* n_P40 */
2539e6b7cde4SSimon Wunderlich 		tmp /= 2;
2540e6b7cde4SSimon Wunderlich 		/* freq_P40 */
2541e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
2542e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_40;
2543e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_VHT;
2544e6b7cde4SSimon Wunderlich 		break;
2545e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80P80:
2546e6b7cde4SSimon Wunderlich 		c->center_freq2 = 0;
2547e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2548e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2549e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2550e6b7cde4SSimon Wunderlich 		break;
2551e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_160:
2552e6b7cde4SSimon Wunderlich 		/* n_P20 */
2553e6b7cde4SSimon Wunderlich 		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
2554e6b7cde4SSimon Wunderlich 		/* n_P80 */
2555e6b7cde4SSimon Wunderlich 		tmp /= 4;
2556e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
2557e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2558e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2559e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2560e6b7cde4SSimon Wunderlich 		break;
2561e6b7cde4SSimon Wunderlich 	default:
2562e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20_NOHT:
2563e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2564e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2565e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2566e6b7cde4SSimon Wunderlich 		break;
2567e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_5:
2568e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_10:
2569e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2570e6b7cde4SSimon Wunderlich 		/* keep c->width */
2571e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2572e6b7cde4SSimon Wunderlich 		break;
2573e6b7cde4SSimon Wunderlich 	}
2574e6b7cde4SSimon Wunderlich 
2575e6b7cde4SSimon Wunderlich 	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
2576e6b7cde4SSimon Wunderlich 
2577e6b7cde4SSimon Wunderlich 	return ret;
2578e6b7cde4SSimon Wunderlich }
2579687da132SEmmanuel Grumbach 
2580687da132SEmmanuel Grumbach /*
2581687da132SEmmanuel Grumbach  * Returns true if smps_mode_new is strictly more restrictive than
2582687da132SEmmanuel Grumbach  * smps_mode_old.
2583687da132SEmmanuel Grumbach  */
2584687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
2585687da132SEmmanuel Grumbach 				   enum ieee80211_smps_mode smps_mode_new)
2586687da132SEmmanuel Grumbach {
2587687da132SEmmanuel Grumbach 	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
2588687da132SEmmanuel Grumbach 			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
2589687da132SEmmanuel Grumbach 		return false;
2590687da132SEmmanuel Grumbach 
2591687da132SEmmanuel Grumbach 	switch (smps_mode_old) {
2592687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_STATIC:
2593687da132SEmmanuel Grumbach 		return false;
2594687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_DYNAMIC:
2595687da132SEmmanuel Grumbach 		return smps_mode_new == IEEE80211_SMPS_STATIC;
2596687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_OFF:
2597687da132SEmmanuel Grumbach 		return smps_mode_new != IEEE80211_SMPS_OFF;
2598687da132SEmmanuel Grumbach 	default:
2599687da132SEmmanuel Grumbach 		WARN_ON(1);
2600687da132SEmmanuel Grumbach 	}
2601687da132SEmmanuel Grumbach 
2602687da132SEmmanuel Grumbach 	return false;
2603687da132SEmmanuel Grumbach }
2604c6da674aSChun-Yeow Yeoh 
2605c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
2606c6da674aSChun-Yeow Yeoh 			      struct cfg80211_csa_settings *csa_settings)
2607c6da674aSChun-Yeow Yeoh {
2608c6da674aSChun-Yeow Yeoh 	struct sk_buff *skb;
2609c6da674aSChun-Yeow Yeoh 	struct ieee80211_mgmt *mgmt;
2610c6da674aSChun-Yeow Yeoh 	struct ieee80211_local *local = sdata->local;
2611c6da674aSChun-Yeow Yeoh 	int freq;
2612c6da674aSChun-Yeow Yeoh 	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
2613c6da674aSChun-Yeow Yeoh 			       sizeof(mgmt->u.action.u.chan_switch);
2614c6da674aSChun-Yeow Yeoh 	u8 *pos;
2615c6da674aSChun-Yeow Yeoh 
2616c6da674aSChun-Yeow Yeoh 	if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
2617c6da674aSChun-Yeow Yeoh 	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
2618c6da674aSChun-Yeow Yeoh 		return -EOPNOTSUPP;
2619c6da674aSChun-Yeow Yeoh 
2620c6da674aSChun-Yeow Yeoh 	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
2621c6da674aSChun-Yeow Yeoh 			    5 + /* channel switch announcement element */
2622c6da674aSChun-Yeow Yeoh 			    3 + /* secondary channel offset element */
2623c6da674aSChun-Yeow Yeoh 			    8); /* mesh channel switch parameters element */
2624c6da674aSChun-Yeow Yeoh 	if (!skb)
2625c6da674aSChun-Yeow Yeoh 		return -ENOMEM;
2626c6da674aSChun-Yeow Yeoh 
2627c6da674aSChun-Yeow Yeoh 	skb_reserve(skb, local->tx_headroom);
2628c6da674aSChun-Yeow Yeoh 	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
2629c6da674aSChun-Yeow Yeoh 	memset(mgmt, 0, hdr_len);
2630c6da674aSChun-Yeow Yeoh 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2631c6da674aSChun-Yeow Yeoh 					  IEEE80211_STYPE_ACTION);
2632c6da674aSChun-Yeow Yeoh 
2633c6da674aSChun-Yeow Yeoh 	eth_broadcast_addr(mgmt->da);
2634c6da674aSChun-Yeow Yeoh 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2635c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2636c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
2637c6da674aSChun-Yeow Yeoh 	} else {
2638c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
2639c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
2640c6da674aSChun-Yeow Yeoh 	}
2641c6da674aSChun-Yeow Yeoh 	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
2642c6da674aSChun-Yeow Yeoh 	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
2643c6da674aSChun-Yeow Yeoh 	pos = skb_put(skb, 5);
2644c6da674aSChun-Yeow Yeoh 	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
2645c6da674aSChun-Yeow Yeoh 	*pos++ = 3;						/* IE length */
2646c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
2647c6da674aSChun-Yeow Yeoh 	freq = csa_settings->chandef.chan->center_freq;
2648c6da674aSChun-Yeow Yeoh 	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
2649c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->count;				/* count */
2650c6da674aSChun-Yeow Yeoh 
2651c6da674aSChun-Yeow Yeoh 	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
2652c6da674aSChun-Yeow Yeoh 		enum nl80211_channel_type ch_type;
2653c6da674aSChun-Yeow Yeoh 
2654c6da674aSChun-Yeow Yeoh 		skb_put(skb, 3);
2655c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
2656c6da674aSChun-Yeow Yeoh 		*pos++ = 1;					/* IE length */
2657c6da674aSChun-Yeow Yeoh 		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
2658c6da674aSChun-Yeow Yeoh 		if (ch_type == NL80211_CHAN_HT40PLUS)
2659c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
2660c6da674aSChun-Yeow Yeoh 		else
2661c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
2662c6da674aSChun-Yeow Yeoh 	}
2663c6da674aSChun-Yeow Yeoh 
2664c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2665c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2666c6da674aSChun-Yeow Yeoh 
2667c6da674aSChun-Yeow Yeoh 		skb_put(skb, 8);
2668c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */
2669c6da674aSChun-Yeow Yeoh 		*pos++ = 6;					/* IE length */
2670c6da674aSChun-Yeow Yeoh 		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */
2671c6da674aSChun-Yeow Yeoh 		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */
2672c6da674aSChun-Yeow Yeoh 		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
2673c6da674aSChun-Yeow Yeoh 		*pos++ |= csa_settings->block_tx ?
2674c6da674aSChun-Yeow Yeoh 			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
2675c6da674aSChun-Yeow Yeoh 		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
2676c6da674aSChun-Yeow Yeoh 		pos += 2;
2677ca91dc97SChun-Yeow Yeoh 		put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */
2678c6da674aSChun-Yeow Yeoh 		pos += 2;
2679c6da674aSChun-Yeow Yeoh 	}
2680c6da674aSChun-Yeow Yeoh 
2681c6da674aSChun-Yeow Yeoh 	ieee80211_tx_skb(sdata, skb);
2682c6da674aSChun-Yeow Yeoh 	return 0;
2683c6da674aSChun-Yeow Yeoh }
26842475b1ccSMax Stepanov 
26852475b1ccSMax Stepanov bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
26862475b1ccSMax Stepanov {
26872475b1ccSMax Stepanov 	return !(cs == NULL || cs->cipher == 0 ||
26882475b1ccSMax Stepanov 		 cs->hdr_len < cs->pn_len + cs->pn_off ||
26892475b1ccSMax Stepanov 		 cs->hdr_len <= cs->key_idx_off ||
26902475b1ccSMax Stepanov 		 cs->key_idx_shift > 7 ||
26912475b1ccSMax Stepanov 		 cs->key_idx_mask == 0);
26922475b1ccSMax Stepanov }
26932475b1ccSMax Stepanov 
26942475b1ccSMax Stepanov bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
26952475b1ccSMax Stepanov {
26962475b1ccSMax Stepanov 	int i;
26972475b1ccSMax Stepanov 
26982475b1ccSMax Stepanov 	/* Ensure we have enough iftype bitmap space for all iftype values */
26992475b1ccSMax Stepanov 	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
27002475b1ccSMax Stepanov 
27012475b1ccSMax Stepanov 	for (i = 0; i < n; i++)
27022475b1ccSMax Stepanov 		if (!ieee80211_cs_valid(&cs[i]))
27032475b1ccSMax Stepanov 			return false;
27042475b1ccSMax Stepanov 
27052475b1ccSMax Stepanov 	return true;
27062475b1ccSMax Stepanov }
27072475b1ccSMax Stepanov 
27082475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *
27092475b1ccSMax Stepanov ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
27102475b1ccSMax Stepanov 		 enum nl80211_iftype iftype)
27112475b1ccSMax Stepanov {
27122475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
27132475b1ccSMax Stepanov 	int n = local->hw.n_cipher_schemes;
27142475b1ccSMax Stepanov 	int i;
27152475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs = NULL;
27162475b1ccSMax Stepanov 
27172475b1ccSMax Stepanov 	for (i = 0; i < n; i++) {
27182475b1ccSMax Stepanov 		if (l[i].cipher == cipher) {
27192475b1ccSMax Stepanov 			cs = &l[i];
27202475b1ccSMax Stepanov 			break;
27212475b1ccSMax Stepanov 		}
27222475b1ccSMax Stepanov 	}
27232475b1ccSMax Stepanov 
27242475b1ccSMax Stepanov 	if (!cs || !(cs->iftype & BIT(iftype)))
27252475b1ccSMax Stepanov 		return NULL;
27262475b1ccSMax Stepanov 
27272475b1ccSMax Stepanov 	return cs;
27282475b1ccSMax Stepanov }
27292475b1ccSMax Stepanov 
27302475b1ccSMax Stepanov int ieee80211_cs_headroom(struct ieee80211_local *local,
27312475b1ccSMax Stepanov 			  struct cfg80211_crypto_settings *crypto,
27322475b1ccSMax Stepanov 			  enum nl80211_iftype iftype)
27332475b1ccSMax Stepanov {
27342475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs;
27352475b1ccSMax Stepanov 	int headroom = IEEE80211_ENCRYPT_HEADROOM;
27362475b1ccSMax Stepanov 	int i;
27372475b1ccSMax Stepanov 
27382475b1ccSMax Stepanov 	for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
27392475b1ccSMax Stepanov 		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
27402475b1ccSMax Stepanov 				      iftype);
27412475b1ccSMax Stepanov 
27422475b1ccSMax Stepanov 		if (cs && headroom < cs->hdr_len)
27432475b1ccSMax Stepanov 			headroom = cs->hdr_len;
27442475b1ccSMax Stepanov 	}
27452475b1ccSMax Stepanov 
27462475b1ccSMax Stepanov 	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
27472475b1ccSMax Stepanov 	if (cs && headroom < cs->hdr_len)
27482475b1ccSMax Stepanov 		headroom = cs->hdr_len;
27492475b1ccSMax Stepanov 
27502475b1ccSMax Stepanov 	return headroom;
27512475b1ccSMax Stepanov }
2752a7022e65SFelix Fietkau 
2753a7022e65SFelix Fietkau static bool
2754a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
2755a7022e65SFelix Fietkau {
2756a7022e65SFelix Fietkau 	s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
2757a7022e65SFelix Fietkau 	int skip;
2758a7022e65SFelix Fietkau 
2759a7022e65SFelix Fietkau 	if (end > 0)
2760a7022e65SFelix Fietkau 		return false;
2761a7022e65SFelix Fietkau 
2762a7022e65SFelix Fietkau 	/* End time is in the past, check for repetitions */
2763a7022e65SFelix Fietkau 	skip = DIV_ROUND_UP(-end, data->desc[i].interval);
2764a7022e65SFelix Fietkau 	if (data->count[i] < 255) {
2765a7022e65SFelix Fietkau 		if (data->count[i] <= skip) {
2766a7022e65SFelix Fietkau 			data->count[i] = 0;
2767a7022e65SFelix Fietkau 			return false;
2768a7022e65SFelix Fietkau 		}
2769a7022e65SFelix Fietkau 
2770a7022e65SFelix Fietkau 		data->count[i] -= skip;
2771a7022e65SFelix Fietkau 	}
2772a7022e65SFelix Fietkau 
2773a7022e65SFelix Fietkau 	data->desc[i].start += skip * data->desc[i].interval;
2774a7022e65SFelix Fietkau 
2775a7022e65SFelix Fietkau 	return true;
2776a7022e65SFelix Fietkau }
2777a7022e65SFelix Fietkau 
2778a7022e65SFelix Fietkau static bool
2779a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
2780a7022e65SFelix Fietkau 			     s32 *offset)
2781a7022e65SFelix Fietkau {
2782a7022e65SFelix Fietkau 	bool ret = false;
2783a7022e65SFelix Fietkau 	int i;
2784a7022e65SFelix Fietkau 
2785a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2786a7022e65SFelix Fietkau 		s32 cur;
2787a7022e65SFelix Fietkau 
2788a7022e65SFelix Fietkau 		if (!data->count[i])
2789a7022e65SFelix Fietkau 			continue;
2790a7022e65SFelix Fietkau 
2791a7022e65SFelix Fietkau 		if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
2792a7022e65SFelix Fietkau 			ret = true;
2793a7022e65SFelix Fietkau 
2794a7022e65SFelix Fietkau 		cur = data->desc[i].start - tsf;
2795a7022e65SFelix Fietkau 		if (cur > *offset)
2796a7022e65SFelix Fietkau 			continue;
2797a7022e65SFelix Fietkau 
2798a7022e65SFelix Fietkau 		cur = data->desc[i].start + data->desc[i].duration - tsf;
2799a7022e65SFelix Fietkau 		if (cur > *offset)
2800a7022e65SFelix Fietkau 			*offset = cur;
2801a7022e65SFelix Fietkau 	}
2802a7022e65SFelix Fietkau 
2803a7022e65SFelix Fietkau 	return ret;
2804a7022e65SFelix Fietkau }
2805a7022e65SFelix Fietkau 
2806a7022e65SFelix Fietkau static u32
2807a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
2808a7022e65SFelix Fietkau {
2809a7022e65SFelix Fietkau 	s32 offset = 0;
2810a7022e65SFelix Fietkau 	int tries = 0;
2811a7022e65SFelix Fietkau 	/*
2812a7022e65SFelix Fietkau 	 * arbitrary limit, used to avoid infinite loops when combined NoA
2813a7022e65SFelix Fietkau 	 * descriptors cover the full time period.
2814a7022e65SFelix Fietkau 	 */
2815a7022e65SFelix Fietkau 	int max_tries = 5;
2816a7022e65SFelix Fietkau 
2817a7022e65SFelix Fietkau 	ieee80211_extend_absent_time(data, tsf, &offset);
2818a7022e65SFelix Fietkau 	do {
2819a7022e65SFelix Fietkau 		if (!ieee80211_extend_absent_time(data, tsf, &offset))
2820a7022e65SFelix Fietkau 			break;
2821a7022e65SFelix Fietkau 
2822a7022e65SFelix Fietkau 		tries++;
2823a7022e65SFelix Fietkau 	} while (tries < max_tries);
2824a7022e65SFelix Fietkau 
2825a7022e65SFelix Fietkau 	return offset;
2826a7022e65SFelix Fietkau }
2827a7022e65SFelix Fietkau 
2828a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
2829a7022e65SFelix Fietkau {
2830a7022e65SFelix Fietkau 	u32 next_offset = BIT(31) - 1;
2831a7022e65SFelix Fietkau 	int i;
2832a7022e65SFelix Fietkau 
2833a7022e65SFelix Fietkau 	data->absent = 0;
2834a7022e65SFelix Fietkau 	data->has_next_tsf = false;
2835a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2836a7022e65SFelix Fietkau 		s32 start;
2837a7022e65SFelix Fietkau 
2838a7022e65SFelix Fietkau 		if (!data->count[i])
2839a7022e65SFelix Fietkau 			continue;
2840a7022e65SFelix Fietkau 
2841a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2842a7022e65SFelix Fietkau 		start = data->desc[i].start - tsf;
2843a7022e65SFelix Fietkau 		if (start <= 0)
2844a7022e65SFelix Fietkau 			data->absent |= BIT(i);
2845a7022e65SFelix Fietkau 
2846a7022e65SFelix Fietkau 		if (next_offset > start)
2847a7022e65SFelix Fietkau 			next_offset = start;
2848a7022e65SFelix Fietkau 
2849a7022e65SFelix Fietkau 		data->has_next_tsf = true;
2850a7022e65SFelix Fietkau 	}
2851a7022e65SFelix Fietkau 
2852a7022e65SFelix Fietkau 	if (data->absent)
2853a7022e65SFelix Fietkau 		next_offset = ieee80211_get_noa_absent_time(data, tsf);
2854a7022e65SFelix Fietkau 
2855a7022e65SFelix Fietkau 	data->next_tsf = tsf + next_offset;
2856a7022e65SFelix Fietkau }
2857a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa);
2858a7022e65SFelix Fietkau 
2859a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
2860a7022e65SFelix Fietkau 			    struct ieee80211_noa_data *data, u32 tsf)
2861a7022e65SFelix Fietkau {
2862a7022e65SFelix Fietkau 	int ret = 0;
2863a7022e65SFelix Fietkau 	int i;
2864a7022e65SFelix Fietkau 
2865a7022e65SFelix Fietkau 	memset(data, 0, sizeof(*data));
2866a7022e65SFelix Fietkau 
2867a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2868a7022e65SFelix Fietkau 		const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
2869a7022e65SFelix Fietkau 
2870a7022e65SFelix Fietkau 		if (!desc->count || !desc->duration)
2871a7022e65SFelix Fietkau 			continue;
2872a7022e65SFelix Fietkau 
2873a7022e65SFelix Fietkau 		data->count[i] = desc->count;
2874a7022e65SFelix Fietkau 		data->desc[i].start = le32_to_cpu(desc->start_time);
2875a7022e65SFelix Fietkau 		data->desc[i].duration = le32_to_cpu(desc->duration);
2876a7022e65SFelix Fietkau 		data->desc[i].interval = le32_to_cpu(desc->interval);
2877a7022e65SFelix Fietkau 
2878a7022e65SFelix Fietkau 		if (data->count[i] > 1 &&
2879a7022e65SFelix Fietkau 		    data->desc[i].interval < data->desc[i].duration)
2880a7022e65SFelix Fietkau 			continue;
2881a7022e65SFelix Fietkau 
2882a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2883a7022e65SFelix Fietkau 		ret++;
2884a7022e65SFelix Fietkau 	}
2885a7022e65SFelix Fietkau 
2886a7022e65SFelix Fietkau 	if (ret)
2887a7022e65SFelix Fietkau 		ieee80211_update_p2p_noa(data, tsf);
2888a7022e65SFelix Fietkau 
2889a7022e65SFelix Fietkau 	return ret;
2890a7022e65SFelix Fietkau }
2891a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
2892057d5f4bSThomas Pedersen 
2893057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local,
2894057d5f4bSThomas Pedersen 			   struct ieee80211_sub_if_data *sdata)
2895057d5f4bSThomas Pedersen {
2896057d5f4bSThomas Pedersen 	u64 tsf = drv_get_tsf(local, sdata);
2897057d5f4bSThomas Pedersen 	u64 dtim_count = 0;
2898057d5f4bSThomas Pedersen 	u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
2899057d5f4bSThomas Pedersen 	u8 dtim_period = sdata->vif.bss_conf.dtim_period;
2900057d5f4bSThomas Pedersen 	struct ps_data *ps;
2901057d5f4bSThomas Pedersen 	u8 bcns_from_dtim;
2902057d5f4bSThomas Pedersen 
2903057d5f4bSThomas Pedersen 	if (tsf == -1ULL || !beacon_int || !dtim_period)
2904057d5f4bSThomas Pedersen 		return;
2905057d5f4bSThomas Pedersen 
2906057d5f4bSThomas Pedersen 	if (sdata->vif.type == NL80211_IFTYPE_AP ||
2907057d5f4bSThomas Pedersen 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
2908057d5f4bSThomas Pedersen 		if (!sdata->bss)
2909057d5f4bSThomas Pedersen 			return;
2910057d5f4bSThomas Pedersen 
2911057d5f4bSThomas Pedersen 		ps = &sdata->bss->ps;
2912057d5f4bSThomas Pedersen 	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
2913057d5f4bSThomas Pedersen 		ps = &sdata->u.mesh.ps;
2914057d5f4bSThomas Pedersen 	} else {
2915057d5f4bSThomas Pedersen 		return;
2916057d5f4bSThomas Pedersen 	}
2917057d5f4bSThomas Pedersen 
2918057d5f4bSThomas Pedersen 	/*
2919057d5f4bSThomas Pedersen 	 * actually finds last dtim_count, mac80211 will update in
2920057d5f4bSThomas Pedersen 	 * __beacon_add_tim().
2921057d5f4bSThomas Pedersen 	 * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
2922057d5f4bSThomas Pedersen 	 */
2923057d5f4bSThomas Pedersen 	do_div(tsf, beacon_int);
2924057d5f4bSThomas Pedersen 	bcns_from_dtim = do_div(tsf, dtim_period);
2925057d5f4bSThomas Pedersen 	/* just had a DTIM */
2926057d5f4bSThomas Pedersen 	if (!bcns_from_dtim)
2927057d5f4bSThomas Pedersen 		dtim_count = 0;
2928057d5f4bSThomas Pedersen 	else
2929057d5f4bSThomas Pedersen 		dtim_count = dtim_period - bcns_from_dtim;
2930057d5f4bSThomas Pedersen 
2931057d5f4bSThomas Pedersen 	ps->dtim_count = dtim_count;
2932057d5f4bSThomas Pedersen }
293373de86a3SLuciano Coelho 
293473de86a3SLuciano Coelho int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
293573de86a3SLuciano Coelho 				 const struct cfg80211_chan_def *chandef,
293673de86a3SLuciano Coelho 				 enum ieee80211_chanctx_mode chanmode,
293773de86a3SLuciano Coelho 				 u8 radar_detect)
293873de86a3SLuciano Coelho {
293973de86a3SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
294073de86a3SLuciano Coelho 	struct ieee80211_sub_if_data *sdata_iter;
294173de86a3SLuciano Coelho 	enum nl80211_iftype iftype = sdata->wdev.iftype;
294273de86a3SLuciano Coelho 	int num[NUM_NL80211_IFTYPES];
294373de86a3SLuciano Coelho 	struct ieee80211_chanctx *ctx;
2944b6a55015SLuciano Coelho 	int num_different_channels = 0;
294573de86a3SLuciano Coelho 	int total = 1;
294673de86a3SLuciano Coelho 
294773de86a3SLuciano Coelho 	lockdep_assert_held(&local->chanctx_mtx);
294873de86a3SLuciano Coelho 
294973de86a3SLuciano Coelho 	if (WARN_ON(hweight32(radar_detect) > 1))
295073de86a3SLuciano Coelho 		return -EINVAL;
295173de86a3SLuciano Coelho 
2952b6a55015SLuciano Coelho 	if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
2953b6a55015SLuciano Coelho 		    !chandef->chan))
295473de86a3SLuciano Coelho 		return -EINVAL;
295573de86a3SLuciano Coelho 
2956b6a55015SLuciano Coelho 	if (chandef)
2957b6a55015SLuciano Coelho 		num_different_channels = 1;
2958b6a55015SLuciano Coelho 
295973de86a3SLuciano Coelho 	if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
296073de86a3SLuciano Coelho 		return -EINVAL;
296173de86a3SLuciano Coelho 
296273de86a3SLuciano Coelho 	/* Always allow software iftypes */
296373de86a3SLuciano Coelho 	if (local->hw.wiphy->software_iftypes & BIT(iftype)) {
296473de86a3SLuciano Coelho 		if (radar_detect)
296573de86a3SLuciano Coelho 			return -EINVAL;
296673de86a3SLuciano Coelho 		return 0;
296773de86a3SLuciano Coelho 	}
296873de86a3SLuciano Coelho 
296973de86a3SLuciano Coelho 	memset(num, 0, sizeof(num));
297073de86a3SLuciano Coelho 
297173de86a3SLuciano Coelho 	if (iftype != NL80211_IFTYPE_UNSPECIFIED)
297273de86a3SLuciano Coelho 		num[iftype] = 1;
297373de86a3SLuciano Coelho 
297473de86a3SLuciano Coelho 	list_for_each_entry(ctx, &local->chanctx_list, list) {
297573de86a3SLuciano Coelho 		if (ctx->conf.radar_enabled)
297673de86a3SLuciano Coelho 			radar_detect |= BIT(ctx->conf.def.width);
297773de86a3SLuciano Coelho 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
297873de86a3SLuciano Coelho 			num_different_channels++;
297973de86a3SLuciano Coelho 			continue;
298073de86a3SLuciano Coelho 		}
2981b6a55015SLuciano Coelho 		if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
298273de86a3SLuciano Coelho 		    cfg80211_chandef_compatible(chandef,
298373de86a3SLuciano Coelho 						&ctx->conf.def))
298473de86a3SLuciano Coelho 			continue;
298573de86a3SLuciano Coelho 		num_different_channels++;
298673de86a3SLuciano Coelho 	}
298773de86a3SLuciano Coelho 
298873de86a3SLuciano Coelho 	list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
298973de86a3SLuciano Coelho 		struct wireless_dev *wdev_iter;
299073de86a3SLuciano Coelho 
299173de86a3SLuciano Coelho 		wdev_iter = &sdata_iter->wdev;
299273de86a3SLuciano Coelho 
299373de86a3SLuciano Coelho 		if (sdata_iter == sdata ||
299473de86a3SLuciano Coelho 		    rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
299573de86a3SLuciano Coelho 		    local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
299673de86a3SLuciano Coelho 			continue;
299773de86a3SLuciano Coelho 
299873de86a3SLuciano Coelho 		num[wdev_iter->iftype]++;
299973de86a3SLuciano Coelho 		total++;
300073de86a3SLuciano Coelho 	}
300173de86a3SLuciano Coelho 
300273de86a3SLuciano Coelho 	if (total == 1 && !radar_detect)
300373de86a3SLuciano Coelho 		return 0;
300473de86a3SLuciano Coelho 
300573de86a3SLuciano Coelho 	return cfg80211_check_combinations(local->hw.wiphy,
300673de86a3SLuciano Coelho 					   num_different_channels,
300773de86a3SLuciano Coelho 					   radar_detect, num);
300873de86a3SLuciano Coelho }
30096fa001bcSMichal Kazior 
30106fa001bcSMichal Kazior static void
30116fa001bcSMichal Kazior ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
30126fa001bcSMichal Kazior 			 void *data)
30136fa001bcSMichal Kazior {
30146fa001bcSMichal Kazior 	u32 *max_num_different_channels = data;
30156fa001bcSMichal Kazior 
30166fa001bcSMichal Kazior 	*max_num_different_channels = max(*max_num_different_channels,
30176fa001bcSMichal Kazior 					  c->num_different_channels);
30186fa001bcSMichal Kazior }
30196fa001bcSMichal Kazior 
30206fa001bcSMichal Kazior int ieee80211_max_num_channels(struct ieee80211_local *local)
30216fa001bcSMichal Kazior {
30226fa001bcSMichal Kazior 	struct ieee80211_sub_if_data *sdata;
30236fa001bcSMichal Kazior 	int num[NUM_NL80211_IFTYPES] = {};
30246fa001bcSMichal Kazior 	struct ieee80211_chanctx *ctx;
30256fa001bcSMichal Kazior 	int num_different_channels = 0;
30266fa001bcSMichal Kazior 	u8 radar_detect = 0;
30276fa001bcSMichal Kazior 	u32 max_num_different_channels = 1;
30286fa001bcSMichal Kazior 	int err;
30296fa001bcSMichal Kazior 
30306fa001bcSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
30316fa001bcSMichal Kazior 
30326fa001bcSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
30336fa001bcSMichal Kazior 		num_different_channels++;
30346fa001bcSMichal Kazior 
30356fa001bcSMichal Kazior 		if (ctx->conf.radar_enabled)
30366fa001bcSMichal Kazior 			radar_detect |= BIT(ctx->conf.def.width);
30376fa001bcSMichal Kazior 	}
30386fa001bcSMichal Kazior 
30396fa001bcSMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list)
30406fa001bcSMichal Kazior 		num[sdata->wdev.iftype]++;
30416fa001bcSMichal Kazior 
30426fa001bcSMichal Kazior 	err = cfg80211_iter_combinations(local->hw.wiphy,
30436fa001bcSMichal Kazior 					 num_different_channels, radar_detect,
30446fa001bcSMichal Kazior 					 num, ieee80211_iter_max_chans,
30456fa001bcSMichal Kazior 					 &max_num_different_channels);
30466fa001bcSMichal Kazior 	if (err < 0)
30476fa001bcSMichal Kazior 		return err;
30486fa001bcSMichal Kazior 
30496fa001bcSMichal Kazior 	return max_num_different_channels;
30506fa001bcSMichal Kazior }
3051