xref: /openbmc/linux/net/mac80211/util.c (revision cca07b00)
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 
55539ecc01dSJohannes Berg void ieee80211_flush_queues(struct ieee80211_local *local,
55639ecc01dSJohannes Berg 			    struct ieee80211_sub_if_data *sdata)
55739ecc01dSJohannes Berg {
55839ecc01dSJohannes Berg 	u32 queues;
55939ecc01dSJohannes Berg 
56039ecc01dSJohannes Berg 	if (!local->ops->flush)
56139ecc01dSJohannes Berg 		return;
56239ecc01dSJohannes Berg 
56339ecc01dSJohannes Berg 	if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
56439ecc01dSJohannes Berg 		int ac;
56539ecc01dSJohannes Berg 
56639ecc01dSJohannes Berg 		queues = 0;
56739ecc01dSJohannes Berg 
56839ecc01dSJohannes Berg 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
56939ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.hw_queue[ac]);
57039ecc01dSJohannes Berg 		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
57139ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.cab_queue);
57239ecc01dSJohannes Berg 	} else {
57339ecc01dSJohannes Berg 		/* all queues */
57439ecc01dSJohannes Berg 		queues = BIT(local->hw.queues) - 1;
57539ecc01dSJohannes Berg 	}
57639ecc01dSJohannes Berg 
57759f48fe2SLuciano Coelho 	ieee80211_stop_queues_by_reason(&local->hw, queues,
578cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
579cca07b00SLuciano Coelho 					false);
580445ea4e8SJohannes Berg 
58177be2c54SEmmanuel Grumbach 	drv_flush(local, sdata, queues, false);
582445ea4e8SJohannes Berg 
58359f48fe2SLuciano Coelho 	ieee80211_wake_queues_by_reason(&local->hw, queues,
584cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
585cca07b00SLuciano Coelho 					false);
58639ecc01dSJohannes Berg }
58739ecc01dSJohannes Berg 
588c7c71066SJohannes Berg static void __iterate_active_interfaces(struct ieee80211_local *local,
589c7c71066SJohannes Berg 					u32 iter_flags,
590dabeb344SJohannes Berg 					void (*iterator)(void *data, u8 *mac,
59132bfd35dSJohannes Berg 						struct ieee80211_vif *vif),
592dabeb344SJohannes Berg 					void *data)
593dabeb344SJohannes Berg {
594dabeb344SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
595dabeb344SJohannes Berg 
596e38bad47SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
59751fb61e7SJohannes Berg 		switch (sdata->vif.type) {
59805c914feSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
59931eba5bcSFelix Fietkau 			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
60031eba5bcSFelix Fietkau 				continue;
60131eba5bcSFelix Fietkau 			break;
60205c914feSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
603dabeb344SJohannes Berg 			continue;
6042ca27bcfSJohannes Berg 		default:
605dabeb344SJohannes Berg 			break;
606dabeb344SJohannes Berg 		}
6078b2c9824SJohannes Berg 		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
6088b2c9824SJohannes Berg 		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
6098b2c9824SJohannes Berg 			continue;
6109607e6b6SJohannes Berg 		if (ieee80211_sdata_running(sdata))
61147846c9bSJohannes Berg 			iterator(data, sdata->vif.addr,
61232bfd35dSJohannes Berg 				 &sdata->vif);
613dabeb344SJohannes Berg 	}
614e38bad47SJohannes Berg 
615c7c71066SJohannes Berg 	sdata = rcu_dereference_check(local->monitor_sdata,
616c7c71066SJohannes Berg 				      lockdep_is_held(&local->iflist_mtx) ||
617c7c71066SJohannes Berg 				      lockdep_rtnl_is_held());
6188b2c9824SJohannes Berg 	if (sdata &&
6198b2c9824SJohannes Berg 	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
6208b2c9824SJohannes Berg 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
621685fb72bSJohannes Berg 		iterator(data, sdata->vif.addr, &sdata->vif);
622c7c71066SJohannes Berg }
623685fb72bSJohannes Berg 
624c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces(
625c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
626c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
627c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
628c7c71066SJohannes Berg 	void *data)
629c7c71066SJohannes Berg {
630c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
631c7c71066SJohannes Berg 
632c7c71066SJohannes Berg 	mutex_lock(&local->iflist_mtx);
633c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
634c7c71066SJohannes Berg 	mutex_unlock(&local->iflist_mtx);
635c7c71066SJohannes Berg }
636c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
637c7c71066SJohannes Berg 
638c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic(
639c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
640c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
641c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
642c7c71066SJohannes Berg 	void *data)
643c7c71066SJohannes Berg {
644c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
645c7c71066SJohannes Berg 
646c7c71066SJohannes Berg 	rcu_read_lock();
647c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
648e38bad47SJohannes Berg 	rcu_read_unlock();
649dabeb344SJohannes Berg }
6502f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
65137ffc8daSJohannes Berg 
652c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_rtnl(
653c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
654c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
655c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
656c7c71066SJohannes Berg 	void *data)
657c7c71066SJohannes Berg {
658c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
659c7c71066SJohannes Berg 
660c7c71066SJohannes Berg 	ASSERT_RTNL();
661c7c71066SJohannes Berg 
662c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
663c7c71066SJohannes Berg }
664c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
665c7c71066SJohannes Berg 
666ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
667ad7e718cSJohannes Berg {
668ad7e718cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
669ad7e718cSJohannes Berg 
670ad7e718cSJohannes Berg 	if (!ieee80211_sdata_running(sdata) ||
671ad7e718cSJohannes Berg 	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
672ad7e718cSJohannes Berg 		return NULL;
673ad7e718cSJohannes Berg 	return &sdata->vif;
674ad7e718cSJohannes Berg }
675ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
676ad7e718cSJohannes Berg 
67742935ecaSLuis R. Rodriguez /*
67842935ecaSLuis R. Rodriguez  * Nothing should have been stuffed into the workqueue during
67942935ecaSLuis R. Rodriguez  * the suspend->resume cycle. If this WARN is seen then there
68042935ecaSLuis R. Rodriguez  * is a bug with either the driver suspend or something in
68142935ecaSLuis R. Rodriguez  * mac80211 stuffing into the workqueue which we haven't yet
68242935ecaSLuis R. Rodriguez  * cleared during mac80211's suspend cycle.
68342935ecaSLuis R. Rodriguez  */
68442935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local)
68542935ecaSLuis R. Rodriguez {
686ceb99fe0SJohannes Berg 	if (WARN(local->suspended && !local->resuming,
687ceb99fe0SJohannes Berg 		 "queueing ieee80211 work while going to suspend\n"))
68842935ecaSLuis R. Rodriguez 		return false;
68942935ecaSLuis R. Rodriguez 
69042935ecaSLuis R. Rodriguez 	return true;
69142935ecaSLuis R. Rodriguez }
69242935ecaSLuis R. Rodriguez 
69342935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
69442935ecaSLuis R. Rodriguez {
69542935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
69642935ecaSLuis R. Rodriguez 
69742935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
69842935ecaSLuis R. Rodriguez 		return;
69942935ecaSLuis R. Rodriguez 
70042935ecaSLuis R. Rodriguez 	queue_work(local->workqueue, work);
70142935ecaSLuis R. Rodriguez }
70242935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work);
70342935ecaSLuis R. Rodriguez 
70442935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
70542935ecaSLuis R. Rodriguez 				  struct delayed_work *dwork,
70642935ecaSLuis R. Rodriguez 				  unsigned long delay)
70742935ecaSLuis R. Rodriguez {
70842935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
70942935ecaSLuis R. Rodriguez 
71042935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
71142935ecaSLuis R. Rodriguez 		return;
71242935ecaSLuis R. Rodriguez 
71342935ecaSLuis R. Rodriguez 	queue_delayed_work(local->workqueue, dwork, delay);
71442935ecaSLuis R. Rodriguez }
71542935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work);
71642935ecaSLuis R. Rodriguez 
71735d865afSJohannes Berg u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
718dd76986bSJohannes Berg 			       struct ieee802_11_elems *elems,
719dd76986bSJohannes Berg 			       u64 filter, u32 crc)
720dd76986bSJohannes Berg {
721dd76986bSJohannes Berg 	size_t left = len;
72235d865afSJohannes Berg 	const u8 *pos = start;
723dd76986bSJohannes Berg 	bool calc_crc = filter != 0;
724fcff4f10SPaul Stewart 	DECLARE_BITMAP(seen_elems, 256);
725b2e506bfSJohannes Berg 	const u8 *ie;
726dd76986bSJohannes Berg 
727fcff4f10SPaul Stewart 	bitmap_zero(seen_elems, 256);
728dd76986bSJohannes Berg 	memset(elems, 0, sizeof(*elems));
729dd76986bSJohannes Berg 	elems->ie_start = start;
730dd76986bSJohannes Berg 	elems->total_len = len;
731dd76986bSJohannes Berg 
732dd76986bSJohannes Berg 	while (left >= 2) {
733dd76986bSJohannes Berg 		u8 id, elen;
734fcff4f10SPaul Stewart 		bool elem_parse_failed;
735dd76986bSJohannes Berg 
736dd76986bSJohannes Berg 		id = *pos++;
737dd76986bSJohannes Berg 		elen = *pos++;
738dd76986bSJohannes Berg 		left -= 2;
739dd76986bSJohannes Berg 
740fcff4f10SPaul Stewart 		if (elen > left) {
741fcff4f10SPaul Stewart 			elems->parse_error = true;
742dd76986bSJohannes Berg 			break;
743fcff4f10SPaul Stewart 		}
744fcff4f10SPaul Stewart 
7459690fb16SJohannes Berg 		switch (id) {
7469690fb16SJohannes Berg 		case WLAN_EID_SSID:
7479690fb16SJohannes Berg 		case WLAN_EID_SUPP_RATES:
7489690fb16SJohannes Berg 		case WLAN_EID_FH_PARAMS:
7499690fb16SJohannes Berg 		case WLAN_EID_DS_PARAMS:
7509690fb16SJohannes Berg 		case WLAN_EID_CF_PARAMS:
7519690fb16SJohannes Berg 		case WLAN_EID_TIM:
7529690fb16SJohannes Berg 		case WLAN_EID_IBSS_PARAMS:
7539690fb16SJohannes Berg 		case WLAN_EID_CHALLENGE:
7549690fb16SJohannes Berg 		case WLAN_EID_RSN:
7559690fb16SJohannes Berg 		case WLAN_EID_ERP_INFO:
7569690fb16SJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
7579690fb16SJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
7589690fb16SJohannes Berg 		case WLAN_EID_HT_OPERATION:
7599690fb16SJohannes Berg 		case WLAN_EID_VHT_CAPABILITY:
7609690fb16SJohannes Berg 		case WLAN_EID_VHT_OPERATION:
7619690fb16SJohannes Berg 		case WLAN_EID_MESH_ID:
7629690fb16SJohannes Berg 		case WLAN_EID_MESH_CONFIG:
7639690fb16SJohannes Berg 		case WLAN_EID_PEER_MGMT:
7649690fb16SJohannes Berg 		case WLAN_EID_PREQ:
7659690fb16SJohannes Berg 		case WLAN_EID_PREP:
7669690fb16SJohannes Berg 		case WLAN_EID_PERR:
7679690fb16SJohannes Berg 		case WLAN_EID_RANN:
7689690fb16SJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
7699690fb16SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
7709690fb16SJohannes Berg 		case WLAN_EID_COUNTRY:
7719690fb16SJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
7729690fb16SJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
77385220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
774b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
7758f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
776b2e506bfSJohannes Berg 		/*
777b2e506bfSJohannes Berg 		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
778b2e506bfSJohannes Berg 		 * that if the content gets bigger it might be needed more than once
779b2e506bfSJohannes Berg 		 */
7809690fb16SJohannes Berg 			if (test_bit(id, seen_elems)) {
781fcff4f10SPaul Stewart 				elems->parse_error = true;
782fcff4f10SPaul Stewart 				left -= elen;
783fcff4f10SPaul Stewart 				pos += elen;
784fcff4f10SPaul Stewart 				continue;
785fcff4f10SPaul Stewart 			}
7869690fb16SJohannes Berg 			break;
7879690fb16SJohannes Berg 		}
788dd76986bSJohannes Berg 
789dd76986bSJohannes Berg 		if (calc_crc && id < 64 && (filter & (1ULL << id)))
790dd76986bSJohannes Berg 			crc = crc32_be(crc, pos - 2, elen + 2);
791dd76986bSJohannes Berg 
792fcff4f10SPaul Stewart 		elem_parse_failed = false;
793fcff4f10SPaul Stewart 
794dd76986bSJohannes Berg 		switch (id) {
795dd76986bSJohannes Berg 		case WLAN_EID_SSID:
796dd76986bSJohannes Berg 			elems->ssid = pos;
797dd76986bSJohannes Berg 			elems->ssid_len = elen;
798dd76986bSJohannes Berg 			break;
799dd76986bSJohannes Berg 		case WLAN_EID_SUPP_RATES:
800dd76986bSJohannes Berg 			elems->supp_rates = pos;
801dd76986bSJohannes Berg 			elems->supp_rates_len = elen;
802dd76986bSJohannes Berg 			break;
803dd76986bSJohannes Berg 		case WLAN_EID_DS_PARAMS:
8041cd8e88eSJohannes Berg 			if (elen >= 1)
805dd76986bSJohannes Berg 				elems->ds_params = pos;
8061cd8e88eSJohannes Berg 			else
8071cd8e88eSJohannes Berg 				elem_parse_failed = true;
808dd76986bSJohannes Berg 			break;
809dd76986bSJohannes Berg 		case WLAN_EID_TIM:
810dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_tim_ie)) {
811dd76986bSJohannes Berg 				elems->tim = (void *)pos;
812dd76986bSJohannes Berg 				elems->tim_len = elen;
813fcff4f10SPaul Stewart 			} else
814fcff4f10SPaul Stewart 				elem_parse_failed = true;
815dd76986bSJohannes Berg 			break;
816dd76986bSJohannes Berg 		case WLAN_EID_CHALLENGE:
817dd76986bSJohannes Berg 			elems->challenge = pos;
818dd76986bSJohannes Berg 			elems->challenge_len = elen;
819dd76986bSJohannes Berg 			break;
820dd76986bSJohannes Berg 		case WLAN_EID_VENDOR_SPECIFIC:
821dd76986bSJohannes Berg 			if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
822dd76986bSJohannes Berg 			    pos[2] == 0xf2) {
823dd76986bSJohannes Berg 				/* Microsoft OUI (00:50:F2) */
824dd76986bSJohannes Berg 
825dd76986bSJohannes Berg 				if (calc_crc)
826dd76986bSJohannes Berg 					crc = crc32_be(crc, pos - 2, elen + 2);
827dd76986bSJohannes Berg 
828441a33baSJohannes Berg 				if (elen >= 5 && pos[3] == 2) {
829dd76986bSJohannes Berg 					/* OUI Type 2 - WMM IE */
830dd76986bSJohannes Berg 					if (pos[4] == 0) {
831dd76986bSJohannes Berg 						elems->wmm_info = pos;
832dd76986bSJohannes Berg 						elems->wmm_info_len = elen;
833dd76986bSJohannes Berg 					} else if (pos[4] == 1) {
834dd76986bSJohannes Berg 						elems->wmm_param = pos;
835dd76986bSJohannes Berg 						elems->wmm_param_len = elen;
836dd76986bSJohannes Berg 					}
837dd76986bSJohannes Berg 				}
838dd76986bSJohannes Berg 			}
839dd76986bSJohannes Berg 			break;
840dd76986bSJohannes Berg 		case WLAN_EID_RSN:
841dd76986bSJohannes Berg 			elems->rsn = pos;
842dd76986bSJohannes Berg 			elems->rsn_len = elen;
843dd76986bSJohannes Berg 			break;
844dd76986bSJohannes Berg 		case WLAN_EID_ERP_INFO:
8451946bed9SJohannes Berg 			if (elen >= 1)
846dd76986bSJohannes Berg 				elems->erp_info = pos;
8471946bed9SJohannes Berg 			else
8481946bed9SJohannes Berg 				elem_parse_failed = true;
849dd76986bSJohannes Berg 			break;
850dd76986bSJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
851dd76986bSJohannes Berg 			elems->ext_supp_rates = pos;
852dd76986bSJohannes Berg 			elems->ext_supp_rates_len = elen;
853dd76986bSJohannes Berg 			break;
854dd76986bSJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
855dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_cap))
856dd76986bSJohannes Berg 				elems->ht_cap_elem = (void *)pos;
857fcff4f10SPaul Stewart 			else
858fcff4f10SPaul Stewart 				elem_parse_failed = true;
859dd76986bSJohannes Berg 			break;
860074d46d1SJohannes Berg 		case WLAN_EID_HT_OPERATION:
861074d46d1SJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_operation))
862074d46d1SJohannes Berg 				elems->ht_operation = (void *)pos;
863fcff4f10SPaul Stewart 			else
864fcff4f10SPaul Stewart 				elem_parse_failed = true;
865dd76986bSJohannes Berg 			break;
866818255eaSMahesh Palivela 		case WLAN_EID_VHT_CAPABILITY:
867818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_cap))
868818255eaSMahesh Palivela 				elems->vht_cap_elem = (void *)pos;
869818255eaSMahesh Palivela 			else
870818255eaSMahesh Palivela 				elem_parse_failed = true;
871818255eaSMahesh Palivela 			break;
872818255eaSMahesh Palivela 		case WLAN_EID_VHT_OPERATION:
873818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_operation))
874818255eaSMahesh Palivela 				elems->vht_operation = (void *)pos;
875818255eaSMahesh Palivela 			else
876818255eaSMahesh Palivela 				elem_parse_failed = true;
877818255eaSMahesh Palivela 			break;
878bee7f586SJohannes Berg 		case WLAN_EID_OPMODE_NOTIF:
879bee7f586SJohannes Berg 			if (elen > 0)
880bee7f586SJohannes Berg 				elems->opmode_notif = pos;
881bee7f586SJohannes Berg 			else
882bee7f586SJohannes Berg 				elem_parse_failed = true;
883bee7f586SJohannes Berg 			break;
884dd76986bSJohannes Berg 		case WLAN_EID_MESH_ID:
885dd76986bSJohannes Berg 			elems->mesh_id = pos;
886dd76986bSJohannes Berg 			elems->mesh_id_len = elen;
887dd76986bSJohannes Berg 			break;
888dd76986bSJohannes Berg 		case WLAN_EID_MESH_CONFIG:
889dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_meshconf_ie))
890dd76986bSJohannes Berg 				elems->mesh_config = (void *)pos;
891fcff4f10SPaul Stewart 			else
892fcff4f10SPaul Stewart 				elem_parse_failed = true;
893dd76986bSJohannes Berg 			break;
894dd76986bSJohannes Berg 		case WLAN_EID_PEER_MGMT:
895dd76986bSJohannes Berg 			elems->peering = pos;
896dd76986bSJohannes Berg 			elems->peering_len = elen;
897dd76986bSJohannes Berg 			break;
8983f52b7e3SMarco Porsch 		case WLAN_EID_MESH_AWAKE_WINDOW:
8993f52b7e3SMarco Porsch 			if (elen >= 2)
9003f52b7e3SMarco Porsch 				elems->awake_window = (void *)pos;
9013f52b7e3SMarco Porsch 			break;
902dd76986bSJohannes Berg 		case WLAN_EID_PREQ:
903dd76986bSJohannes Berg 			elems->preq = pos;
904dd76986bSJohannes Berg 			elems->preq_len = elen;
905dd76986bSJohannes Berg 			break;
906dd76986bSJohannes Berg 		case WLAN_EID_PREP:
907dd76986bSJohannes Berg 			elems->prep = pos;
908dd76986bSJohannes Berg 			elems->prep_len = elen;
909dd76986bSJohannes Berg 			break;
910dd76986bSJohannes Berg 		case WLAN_EID_PERR:
911dd76986bSJohannes Berg 			elems->perr = pos;
912dd76986bSJohannes Berg 			elems->perr_len = elen;
913dd76986bSJohannes Berg 			break;
914dd76986bSJohannes Berg 		case WLAN_EID_RANN:
915dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_rann_ie))
916dd76986bSJohannes Berg 				elems->rann = (void *)pos;
917fcff4f10SPaul Stewart 			else
918fcff4f10SPaul Stewart 				elem_parse_failed = true;
919dd76986bSJohannes Berg 			break;
920dd76986bSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
9215bc1420bSJohannes Berg 			if (elen != sizeof(struct ieee80211_channel_sw_ie)) {
9225bc1420bSJohannes Berg 				elem_parse_failed = true;
9235bc1420bSJohannes Berg 				break;
9245bc1420bSJohannes Berg 			}
9255bc1420bSJohannes Berg 			elems->ch_switch_ie = (void *)pos;
926dd76986bSJohannes Berg 			break;
927b4f286a1SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
928b4f286a1SJohannes Berg 			if (elen != sizeof(struct ieee80211_ext_chansw_ie)) {
929b4f286a1SJohannes Berg 				elem_parse_failed = true;
930b4f286a1SJohannes Berg 				break;
931b4f286a1SJohannes Berg 			}
932b4f286a1SJohannes Berg 			elems->ext_chansw_ie = (void *)pos;
933b4f286a1SJohannes Berg 			break;
93485220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
93585220d71SJohannes Berg 			if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
93685220d71SJohannes Berg 				elem_parse_failed = true;
93785220d71SJohannes Berg 				break;
93885220d71SJohannes Berg 			}
93985220d71SJohannes Berg 			elems->sec_chan_offs = (void *)pos;
94085220d71SJohannes Berg 			break;
9418f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
9428f2535b9SChun-Yeow Yeoh 			if (elen !=
9438f2535b9SChun-Yeow Yeoh 			    sizeof(*elems->mesh_chansw_params_ie)) {
9448f2535b9SChun-Yeow Yeoh 				elem_parse_failed = true;
9458f2535b9SChun-Yeow Yeoh 				break;
9468f2535b9SChun-Yeow Yeoh 			}
9478f2535b9SChun-Yeow Yeoh 			elems->mesh_chansw_params_ie = (void *)pos;
9488f2535b9SChun-Yeow Yeoh 			break;
949b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
950b2e506bfSJohannes Berg 			if (!action ||
951b2e506bfSJohannes Berg 			    elen != sizeof(*elems->wide_bw_chansw_ie)) {
952b2e506bfSJohannes Berg 				elem_parse_failed = true;
953b2e506bfSJohannes Berg 				break;
954b2e506bfSJohannes Berg 			}
955b2e506bfSJohannes Berg 			elems->wide_bw_chansw_ie = (void *)pos;
956b2e506bfSJohannes Berg 			break;
957b2e506bfSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
958b2e506bfSJohannes Berg 			if (action) {
959b2e506bfSJohannes Berg 				elem_parse_failed = true;
960b2e506bfSJohannes Berg 				break;
961b2e506bfSJohannes Berg 			}
962b2e506bfSJohannes Berg 			/*
963b2e506bfSJohannes Berg 			 * This is a bit tricky, but as we only care about
964b2e506bfSJohannes Berg 			 * the wide bandwidth channel switch element, so
965b2e506bfSJohannes Berg 			 * just parse it out manually.
966b2e506bfSJohannes Berg 			 */
967b2e506bfSJohannes Berg 			ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
968b2e506bfSJohannes Berg 					      pos, elen);
969b2e506bfSJohannes Berg 			if (ie) {
970b2e506bfSJohannes Berg 				if (ie[1] == sizeof(*elems->wide_bw_chansw_ie))
971b2e506bfSJohannes Berg 					elems->wide_bw_chansw_ie =
972b2e506bfSJohannes Berg 						(void *)(ie + 2);
973b2e506bfSJohannes Berg 				else
974b2e506bfSJohannes Berg 					elem_parse_failed = true;
975b2e506bfSJohannes Berg 			}
976b2e506bfSJohannes Berg 			break;
977dd76986bSJohannes Berg 		case WLAN_EID_COUNTRY:
978dd76986bSJohannes Berg 			elems->country_elem = pos;
979dd76986bSJohannes Berg 			elems->country_elem_len = elen;
980dd76986bSJohannes Berg 			break;
981dd76986bSJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
982761a48d2SJohannes Berg 			if (elen != 1) {
983761a48d2SJohannes Berg 				elem_parse_failed = true;
984761a48d2SJohannes Berg 				break;
985761a48d2SJohannes Berg 			}
986dd76986bSJohannes Berg 			elems->pwr_constr_elem = pos;
987dd76986bSJohannes Berg 			break;
988dd76986bSJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
98979ba1d89SJohannes Berg 			if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
99079ba1d89SJohannes Berg 				elems->timeout_int = (void *)pos;
99179ba1d89SJohannes Berg 			else
99279ba1d89SJohannes Berg 				elem_parse_failed = true;
993dd76986bSJohannes Berg 			break;
994dd76986bSJohannes Berg 		default:
995dd76986bSJohannes Berg 			break;
996dd76986bSJohannes Berg 		}
997dd76986bSJohannes Berg 
998fcff4f10SPaul Stewart 		if (elem_parse_failed)
999fcff4f10SPaul Stewart 			elems->parse_error = true;
1000fcff4f10SPaul Stewart 		else
10015df45690SJohannes Berg 			__set_bit(id, seen_elems);
1002fcff4f10SPaul Stewart 
1003dd76986bSJohannes Berg 		left -= elen;
1004dd76986bSJohannes Berg 		pos += elen;
1005dd76986bSJohannes Berg 	}
1006dd76986bSJohannes Berg 
1007fcff4f10SPaul Stewart 	if (left != 0)
1008fcff4f10SPaul Stewart 		elems->parse_error = true;
1009fcff4f10SPaul Stewart 
1010dd76986bSJohannes Berg 	return crc;
1011dd76986bSJohannes Berg }
1012dd76986bSJohannes Berg 
10133abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
10143abead59SJohannes Berg 			       bool bss_notify)
10155825fe10SJohannes Berg {
10165825fe10SJohannes Berg 	struct ieee80211_local *local = sdata->local;
10175825fe10SJohannes Berg 	struct ieee80211_tx_queue_params qparam;
101855de908aSJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
101954bcbc69SJohannes Berg 	int ac;
1020a8ce8544SStanislaw Gruszka 	bool use_11b, enable_qos;
1021aa837e1dSJohannes Berg 	int aCWmin, aCWmax;
10225825fe10SJohannes Berg 
10235825fe10SJohannes Berg 	if (!local->ops->conf_tx)
10245825fe10SJohannes Berg 		return;
10255825fe10SJohannes Berg 
102654bcbc69SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
102754bcbc69SJohannes Berg 		return;
102854bcbc69SJohannes Berg 
10295825fe10SJohannes Berg 	memset(&qparam, 0, sizeof(qparam));
10305825fe10SJohannes Berg 
103155de908aSJohannes Berg 	rcu_read_lock();
103255de908aSJohannes Berg 	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
103355de908aSJohannes Berg 	use_11b = (chanctx_conf &&
10344bf88530SJohannes Berg 		   chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
1035aa837e1dSJohannes Berg 		 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
103655de908aSJohannes Berg 	rcu_read_unlock();
10375825fe10SJohannes Berg 
1038a8ce8544SStanislaw Gruszka 	/*
1039a8ce8544SStanislaw Gruszka 	 * By default disable QoS in STA mode for old access points, which do
1040a8ce8544SStanislaw Gruszka 	 * not support 802.11e. New APs will provide proper queue parameters,
1041a8ce8544SStanislaw Gruszka 	 * that we will configure later.
1042a8ce8544SStanislaw Gruszka 	 */
1043a8ce8544SStanislaw Gruszka 	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
1044a8ce8544SStanislaw Gruszka 
1045aa837e1dSJohannes Berg 	/* Set defaults according to 802.11-2007 Table 7-37 */
1046aa837e1dSJohannes Berg 	aCWmax = 1023;
1047aa837e1dSJohannes Berg 	if (use_11b)
1048aa837e1dSJohannes Berg 		aCWmin = 31;
10495825fe10SJohannes Berg 	else
1050aa837e1dSJohannes Berg 		aCWmin = 15;
10515825fe10SJohannes Berg 
10521f4ffde8SFred Zhou 	/* Confiure old 802.11b/g medium access rules. */
10531f4ffde8SFred Zhou 	qparam.cw_max = aCWmax;
10541f4ffde8SFred Zhou 	qparam.cw_min = aCWmin;
10551f4ffde8SFred Zhou 	qparam.txop = 0;
10561f4ffde8SFred Zhou 	qparam.aifs = 2;
10571f4ffde8SFred Zhou 
10581f4ffde8SFred Zhou 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
10591f4ffde8SFred Zhou 		/* Update if QoS is enabled. */
1060a8ce8544SStanislaw Gruszka 		if (enable_qos) {
106154bcbc69SJohannes Berg 			switch (ac) {
10621d98fb12SJohannes Berg 			case IEEE80211_AC_BK:
10637ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
10647ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
10655825fe10SJohannes Berg 				qparam.txop = 0;
1066aa837e1dSJohannes Berg 				qparam.aifs = 7;
1067aa837e1dSJohannes Berg 				break;
1068a8ce8544SStanislaw Gruszka 			/* never happens but let's not leave undefined */
1069a8ce8544SStanislaw Gruszka 			default:
10701d98fb12SJohannes Berg 			case IEEE80211_AC_BE:
10717ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
10727ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
1073aa837e1dSJohannes Berg 				qparam.txop = 0;
1074aa837e1dSJohannes Berg 				qparam.aifs = 3;
1075aa837e1dSJohannes Berg 				break;
10761d98fb12SJohannes Berg 			case IEEE80211_AC_VI:
1077aa837e1dSJohannes Berg 				qparam.cw_max = aCWmin;
1078aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 2 - 1;
1079aa837e1dSJohannes Berg 				if (use_11b)
1080aa837e1dSJohannes Berg 					qparam.txop = 6016/32;
1081aa837e1dSJohannes Berg 				else
1082aa837e1dSJohannes Berg 					qparam.txop = 3008/32;
1083aa837e1dSJohannes Berg 				qparam.aifs = 2;
1084aa837e1dSJohannes Berg 				break;
10851d98fb12SJohannes Berg 			case IEEE80211_AC_VO:
1086aa837e1dSJohannes Berg 				qparam.cw_max = (aCWmin + 1) / 2 - 1;
1087aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 4 - 1;
1088aa837e1dSJohannes Berg 				if (use_11b)
1089aa837e1dSJohannes Berg 					qparam.txop = 3264/32;
1090aa837e1dSJohannes Berg 				else
1091aa837e1dSJohannes Berg 					qparam.txop = 1504/32;
1092aa837e1dSJohannes Berg 				qparam.aifs = 2;
1093aa837e1dSJohannes Berg 				break;
1094aa837e1dSJohannes Berg 			}
1095a8ce8544SStanislaw Gruszka 		}
10965825fe10SJohannes Berg 
1097ab13315aSKalle Valo 		qparam.uapsd = false;
1098ab13315aSKalle Valo 
109954bcbc69SJohannes Berg 		sdata->tx_conf[ac] = qparam;
110054bcbc69SJohannes Berg 		drv_conf_tx(local, sdata, ac, &qparam);
1101aa837e1dSJohannes Berg 	}
1102e1b3ec1aSStanislaw Gruszka 
1103f142c6b9SJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
1104f142c6b9SJohannes Berg 	    sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) {
1105a8ce8544SStanislaw Gruszka 		sdata->vif.bss_conf.qos = enable_qos;
11063abead59SJohannes Berg 		if (bss_notify)
11073abead59SJohannes Berg 			ieee80211_bss_info_change_notify(sdata,
11083abead59SJohannes Berg 							 BSS_CHANGED_QOS);
11095825fe10SJohannes Berg 	}
1110d9734979SSujith }
1111e50db65cSJohannes Berg 
111246900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1113700e8ea6SJouni Malinen 			 u16 transaction, u16 auth_alg, u16 status,
11144a3cb702SJohannes Berg 			 const u8 *extra, size_t extra_len, const u8 *da,
11151672c0e3SJohannes Berg 			 const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
11161672c0e3SJohannes Berg 			 u32 tx_flags)
111746900298SJohannes Berg {
111846900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
111946900298SJohannes Berg 	struct sk_buff *skb;
112046900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1121fffd0934SJohannes Berg 	int err;
112246900298SJohannes Berg 
112315e230abSFred Zhou 	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
112415e230abSFred Zhou 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
1125d15b8459SJoe Perches 	if (!skb)
112646900298SJohannes Berg 		return;
1127d15b8459SJoe Perches 
112846900298SJohannes Berg 	skb_reserve(skb, local->hw.extra_tx_headroom);
112946900298SJohannes Berg 
113046900298SJohannes Berg 	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
113146900298SJohannes Berg 	memset(mgmt, 0, 24 + 6);
113246900298SJohannes Berg 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
113346900298SJohannes Berg 					  IEEE80211_STYPE_AUTH);
1134efa6a09dSAntonio Quartulli 	memcpy(mgmt->da, da, ETH_ALEN);
113547846c9bSJohannes Berg 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
113646900298SJohannes Berg 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
113746900298SJohannes Berg 	mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
113846900298SJohannes Berg 	mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
1139700e8ea6SJouni Malinen 	mgmt->u.auth.status_code = cpu_to_le16(status);
114046900298SJohannes Berg 	if (extra)
114146900298SJohannes Berg 		memcpy(skb_put(skb, extra_len), extra, extra_len);
114246900298SJohannes Berg 
1143fffd0934SJohannes Berg 	if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
1144fffd0934SJohannes Berg 		mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
1145fffd0934SJohannes Berg 		err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
1146fffd0934SJohannes Berg 		WARN_ON(err);
1147fffd0934SJohannes Berg 	}
1148fffd0934SJohannes Berg 
11491672c0e3SJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
11501672c0e3SJohannes Berg 					tx_flags;
115162ae67beSJohannes Berg 	ieee80211_tx_skb(sdata, skb);
115246900298SJohannes Berg }
115346900298SJohannes Berg 
11546ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
11556ae16775SAntonio Quartulli 				    const u8 *bssid, u16 stype, u16 reason,
11566ae16775SAntonio Quartulli 				    bool send_frame, u8 *frame_buf)
11576ae16775SAntonio Quartulli {
11586ae16775SAntonio Quartulli 	struct ieee80211_local *local = sdata->local;
11596ae16775SAntonio Quartulli 	struct sk_buff *skb;
11606ae16775SAntonio Quartulli 	struct ieee80211_mgmt *mgmt = (void *)frame_buf;
11616ae16775SAntonio Quartulli 
11626ae16775SAntonio Quartulli 	/* build frame */
11636ae16775SAntonio Quartulli 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
11646ae16775SAntonio Quartulli 	mgmt->duration = 0; /* initialize only */
11656ae16775SAntonio Quartulli 	mgmt->seq_ctrl = 0; /* initialize only */
11666ae16775SAntonio Quartulli 	memcpy(mgmt->da, bssid, ETH_ALEN);
11676ae16775SAntonio Quartulli 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
11686ae16775SAntonio Quartulli 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
11696ae16775SAntonio Quartulli 	/* u.deauth.reason_code == u.disassoc.reason_code */
11706ae16775SAntonio Quartulli 	mgmt->u.deauth.reason_code = cpu_to_le16(reason);
11716ae16775SAntonio Quartulli 
11726ae16775SAntonio Quartulli 	if (send_frame) {
11736ae16775SAntonio Quartulli 		skb = dev_alloc_skb(local->hw.extra_tx_headroom +
11746ae16775SAntonio Quartulli 				    IEEE80211_DEAUTH_FRAME_LEN);
11756ae16775SAntonio Quartulli 		if (!skb)
11766ae16775SAntonio Quartulli 			return;
11776ae16775SAntonio Quartulli 
11786ae16775SAntonio Quartulli 		skb_reserve(skb, local->hw.extra_tx_headroom);
11796ae16775SAntonio Quartulli 
11806ae16775SAntonio Quartulli 		/* copy in frame */
11816ae16775SAntonio Quartulli 		memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
11826ae16775SAntonio Quartulli 		       mgmt, IEEE80211_DEAUTH_FRAME_LEN);
11836ae16775SAntonio Quartulli 
11846ae16775SAntonio Quartulli 		if (sdata->vif.type != NL80211_IFTYPE_STATION ||
11856ae16775SAntonio Quartulli 		    !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
11866ae16775SAntonio Quartulli 			IEEE80211_SKB_CB(skb)->flags |=
11876ae16775SAntonio Quartulli 				IEEE80211_TX_INTFL_DONT_ENCRYPT;
11886ae16775SAntonio Quartulli 
11896ae16775SAntonio Quartulli 		ieee80211_tx_skb(sdata, skb);
11906ae16775SAntonio Quartulli 	}
11916ae16775SAntonio Quartulli }
11926ae16775SAntonio Quartulli 
1193de95a54bSJohannes Berg int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
1194c604b9f2SJohannes Berg 			     size_t buffer_len, const u8 *ie, size_t ie_len,
1195651b5225SJouni Malinen 			     enum ieee80211_band band, u32 rate_mask,
11962103dec1SSimon Wunderlich 			     struct cfg80211_chan_def *chandef)
1197de95a54bSJohannes Berg {
1198de95a54bSJohannes Berg 	struct ieee80211_supported_band *sband;
1199c604b9f2SJohannes Berg 	u8 *pos = buffer, *end = buffer + buffer_len;
12008e664fb3SJohannes Berg 	size_t offset = 0, noffset;
12018e664fb3SJohannes Berg 	int supp_rates_len, i;
12028dcb2003SJouni Malinen 	u8 rates[32];
12038dcb2003SJouni Malinen 	int num_rates;
12048dcb2003SJouni Malinen 	int ext_rates_len;
12052103dec1SSimon Wunderlich 	int shift;
12062103dec1SSimon Wunderlich 	u32 rate_flags;
1207de95a54bSJohannes Berg 
12084d36ec58SJohannes Berg 	sband = local->hw.wiphy->bands[band];
1209d811b3d5SArik Nemtsov 	if (WARN_ON_ONCE(!sband))
1210d811b3d5SArik Nemtsov 		return 0;
1211de95a54bSJohannes Berg 
12122103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(chandef);
12132103dec1SSimon Wunderlich 	shift = ieee80211_chandef_get_shift(chandef);
12142103dec1SSimon Wunderlich 
12158dcb2003SJouni Malinen 	num_rates = 0;
12168dcb2003SJouni Malinen 	for (i = 0; i < sband->n_bitrates; i++) {
12178dcb2003SJouni Malinen 		if ((BIT(i) & rate_mask) == 0)
12188dcb2003SJouni Malinen 			continue; /* skip rate */
12192103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
12202103dec1SSimon Wunderlich 			continue;
12212103dec1SSimon Wunderlich 
12222103dec1SSimon Wunderlich 		rates[num_rates++] =
12232103dec1SSimon Wunderlich 			(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
12242103dec1SSimon Wunderlich 					  (1 << shift) * 5);
12258dcb2003SJouni Malinen 	}
12268dcb2003SJouni Malinen 
12278dcb2003SJouni Malinen 	supp_rates_len = min_t(int, num_rates, 8);
12288e664fb3SJohannes Berg 
1229c604b9f2SJohannes Berg 	if (end - pos < 2 + supp_rates_len)
1230c604b9f2SJohannes Berg 		goto out_err;
1231de95a54bSJohannes Berg 	*pos++ = WLAN_EID_SUPP_RATES;
12328e664fb3SJohannes Berg 	*pos++ = supp_rates_len;
12338dcb2003SJouni Malinen 	memcpy(pos, rates, supp_rates_len);
12348dcb2003SJouni Malinen 	pos += supp_rates_len;
1235de95a54bSJohannes Berg 
12368e664fb3SJohannes Berg 	/* insert "request information" if in custom IEs */
12378e664fb3SJohannes Berg 	if (ie && ie_len) {
12388e664fb3SJohannes Berg 		static const u8 before_extrates[] = {
12398e664fb3SJohannes Berg 			WLAN_EID_SSID,
12408e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
12418e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
12428e664fb3SJohannes Berg 		};
12438e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
12448e664fb3SJohannes Berg 					     before_extrates,
12458e664fb3SJohannes Berg 					     ARRAY_SIZE(before_extrates),
12468e664fb3SJohannes Berg 					     offset);
1247c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1248c604b9f2SJohannes Berg 			goto out_err;
12498e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
12508e664fb3SJohannes Berg 		pos += noffset - offset;
12518e664fb3SJohannes Berg 		offset = noffset;
12528e664fb3SJohannes Berg 	}
12538e664fb3SJohannes Berg 
12548dcb2003SJouni Malinen 	ext_rates_len = num_rates - supp_rates_len;
12558dcb2003SJouni Malinen 	if (ext_rates_len > 0) {
1256c604b9f2SJohannes Berg 		if (end - pos < 2 + ext_rates_len)
1257c604b9f2SJohannes Berg 			goto out_err;
1258de95a54bSJohannes Berg 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
12598dcb2003SJouni Malinen 		*pos++ = ext_rates_len;
12608dcb2003SJouni Malinen 		memcpy(pos, rates + supp_rates_len, ext_rates_len);
12618dcb2003SJouni Malinen 		pos += ext_rates_len;
12628e664fb3SJohannes Berg 	}
12638e664fb3SJohannes Berg 
12642103dec1SSimon Wunderlich 	if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
1265c604b9f2SJohannes Berg 		if (end - pos < 3)
1266c604b9f2SJohannes Berg 			goto out_err;
1267651b5225SJouni Malinen 		*pos++ = WLAN_EID_DS_PARAMS;
1268651b5225SJouni Malinen 		*pos++ = 1;
12692103dec1SSimon Wunderlich 		*pos++ = ieee80211_frequency_to_channel(
12702103dec1SSimon Wunderlich 				chandef->chan->center_freq);
1271651b5225SJouni Malinen 	}
1272651b5225SJouni Malinen 
12738e664fb3SJohannes Berg 	/* insert custom IEs that go before HT */
12748e664fb3SJohannes Berg 	if (ie && ie_len) {
12758e664fb3SJohannes Berg 		static const u8 before_ht[] = {
12768e664fb3SJohannes Berg 			WLAN_EID_SSID,
12778e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
12788e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
12798e664fb3SJohannes Berg 			WLAN_EID_EXT_SUPP_RATES,
12808e664fb3SJohannes Berg 			WLAN_EID_DS_PARAMS,
12818e664fb3SJohannes Berg 			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
12828e664fb3SJohannes Berg 		};
12838e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
12848e664fb3SJohannes Berg 					     before_ht, ARRAY_SIZE(before_ht),
12858e664fb3SJohannes Berg 					     offset);
1286c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1287c604b9f2SJohannes Berg 			goto out_err;
12888e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
12898e664fb3SJohannes Berg 		pos += noffset - offset;
12908e664fb3SJohannes Berg 		offset = noffset;
1291de95a54bSJohannes Berg 	}
1292de95a54bSJohannes Berg 
1293c604b9f2SJohannes Berg 	if (sband->ht_cap.ht_supported) {
1294c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
1295c604b9f2SJohannes Berg 			goto out_err;
1296ef96a842SBen Greear 		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
1297ef96a842SBen Greear 						sband->ht_cap.cap);
1298c604b9f2SJohannes Berg 	}
12995ef2d41aSJohannes Berg 
1300de95a54bSJohannes Berg 	/*
1301de95a54bSJohannes Berg 	 * If adding more here, adjust code in main.c
1302de95a54bSJohannes Berg 	 * that calculates local->scan_ies_len.
1303de95a54bSJohannes Berg 	 */
1304de95a54bSJohannes Berg 
13054d952300SJohannes Berg 	/* insert custom IEs that go before VHT */
13068e664fb3SJohannes Berg 	if (ie && ie_len) {
13074d952300SJohannes Berg 		static const u8 before_vht[] = {
13084d952300SJohannes Berg 			WLAN_EID_SSID,
13094d952300SJohannes Berg 			WLAN_EID_SUPP_RATES,
13104d952300SJohannes Berg 			WLAN_EID_REQUEST,
13114d952300SJohannes Berg 			WLAN_EID_EXT_SUPP_RATES,
13124d952300SJohannes Berg 			WLAN_EID_DS_PARAMS,
13134d952300SJohannes Berg 			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
13144d952300SJohannes Berg 			WLAN_EID_HT_CAPABILITY,
13154d952300SJohannes Berg 			WLAN_EID_BSS_COEX_2040,
13164d952300SJohannes Berg 			WLAN_EID_EXT_CAPABILITY,
13174d952300SJohannes Berg 			WLAN_EID_SSID_LIST,
13184d952300SJohannes Berg 			WLAN_EID_CHANNEL_USAGE,
13194d952300SJohannes Berg 			WLAN_EID_INTERWORKING,
13204d952300SJohannes Berg 			/* mesh ID can't happen here */
13214d952300SJohannes Berg 			/* 60 GHz can't happen here right now */
13224d952300SJohannes Berg 		};
13234d952300SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
13244d952300SJohannes Berg 					     before_vht, ARRAY_SIZE(before_vht),
13254d952300SJohannes Berg 					     offset);
1326c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1327c604b9f2SJohannes Berg 			goto out_err;
13288e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
13298e664fb3SJohannes Berg 		pos += noffset - offset;
13304d952300SJohannes Berg 		offset = noffset;
1331de95a54bSJohannes Berg 	}
1332de95a54bSJohannes Berg 
1333c604b9f2SJohannes Berg 	if (sband->vht_cap.vht_supported) {
1334c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
1335c604b9f2SJohannes Berg 			goto out_err;
1336ba0afa2fSMahesh Palivela 		pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
1337ba0afa2fSMahesh Palivela 						 sband->vht_cap.cap);
1338c604b9f2SJohannes Berg 	}
1339ba0afa2fSMahesh Palivela 
13404d952300SJohannes Berg 	/* add any remaining custom IEs */
13414d952300SJohannes Berg 	if (ie && ie_len) {
13424d952300SJohannes Berg 		noffset = ie_len;
13434d952300SJohannes Berg 		if (end - pos < noffset - offset)
13444d952300SJohannes Berg 			goto out_err;
13454d952300SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
13464d952300SJohannes Berg 		pos += noffset - offset;
13474d952300SJohannes Berg 	}
13484d952300SJohannes Berg 
1349de95a54bSJohannes Berg 	return pos - buffer;
1350c604b9f2SJohannes Berg  out_err:
1351c604b9f2SJohannes Berg 	WARN_ONCE(1, "not enough space for preq IEs\n");
1352c604b9f2SJohannes Berg 	return pos - buffer;
1353de95a54bSJohannes Berg }
1354de95a54bSJohannes Berg 
1355a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
135685a237feSJohannes Berg 					  u8 *dst, u32 ratemask,
13576b77863bSJohannes Berg 					  struct ieee80211_channel *chan,
1358de95a54bSJohannes Berg 					  const u8 *ssid, size_t ssid_len,
1359a806c558SPaul Stewart 					  const u8 *ie, size_t ie_len,
1360a806c558SPaul Stewart 					  bool directed)
136146900298SJohannes Berg {
136246900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
13632103dec1SSimon Wunderlich 	struct cfg80211_chan_def chandef;
136446900298SJohannes Berg 	struct sk_buff *skb;
136546900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1366b9a9ada1SJohannes Berg 	int ies_len;
136746900298SJohannes Berg 
1368a806c558SPaul Stewart 	/*
1369a806c558SPaul Stewart 	 * Do not send DS Channel parameter for directed probe requests
1370a806c558SPaul Stewart 	 * in order to maximize the chance that we get a response.  Some
1371a806c558SPaul Stewart 	 * badly-behaved APs don't respond when this parameter is included.
1372a806c558SPaul Stewart 	 */
13732103dec1SSimon Wunderlich 	chandef.width = sdata->vif.bss_conf.chandef.width;
1374a806c558SPaul Stewart 	if (directed)
13752103dec1SSimon Wunderlich 		chandef.chan = NULL;
1376a806c558SPaul Stewart 	else
13772103dec1SSimon Wunderlich 		chandef.chan = chan;
1378651b5225SJouni Malinen 
13797c12ce8bSKalle Valo 	skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
1380b9a9ada1SJohannes Berg 				     ssid, ssid_len, 100 + ie_len);
13815b2bbf75SJohannes Berg 	if (!skb)
1382b9a9ada1SJohannes Berg 		return NULL;
1383b9a9ada1SJohannes Berg 
1384b9a9ada1SJohannes Berg 	ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
1385b9a9ada1SJohannes Berg 					   skb_tailroom(skb),
1386c604b9f2SJohannes Berg 					   ie, ie_len, chan->band,
13872103dec1SSimon Wunderlich 					   ratemask, &chandef);
1388b9a9ada1SJohannes Berg 	skb_put(skb, ies_len);
13897c12ce8bSKalle Valo 
139046900298SJohannes Berg 	if (dst) {
13917c12ce8bSKalle Valo 		mgmt = (struct ieee80211_mgmt *) skb->data;
139246900298SJohannes Berg 		memcpy(mgmt->da, dst, ETH_ALEN);
139346900298SJohannes Berg 		memcpy(mgmt->bssid, dst, ETH_ALEN);
139446900298SJohannes Berg 	}
139546900298SJohannes Berg 
139662ae67beSJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
13975b2bbf75SJohannes Berg 
1398a619a4c0SJuuso Oikarinen 	return skb;
1399a619a4c0SJuuso Oikarinen }
1400a619a4c0SJuuso Oikarinen 
1401a619a4c0SJuuso Oikarinen void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
1402a619a4c0SJuuso Oikarinen 			      const u8 *ssid, size_t ssid_len,
1403a806c558SPaul Stewart 			      const u8 *ie, size_t ie_len,
14041672c0e3SJohannes Berg 			      u32 ratemask, bool directed, u32 tx_flags,
140555de908aSJohannes Berg 			      struct ieee80211_channel *channel, bool scan)
1406a619a4c0SJuuso Oikarinen {
1407a619a4c0SJuuso Oikarinen 	struct sk_buff *skb;
1408a619a4c0SJuuso Oikarinen 
1409fe94fe05SJohannes Berg 	skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
14106b77863bSJohannes Berg 					ssid, ssid_len,
141185a237feSJohannes Berg 					ie, ie_len, directed);
1412aad14cebSRajkumar Manoharan 	if (skb) {
14131672c0e3SJohannes Berg 		IEEE80211_SKB_CB(skb)->flags |= tx_flags;
141455de908aSJohannes Berg 		if (scan)
141555de908aSJohannes Berg 			ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
141655de908aSJohannes Berg 		else
1417a619a4c0SJuuso Oikarinen 			ieee80211_tx_skb(sdata, skb);
141846900298SJohannes Berg 	}
1419aad14cebSRajkumar Manoharan }
142046900298SJohannes Berg 
14212103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
142246900298SJohannes Berg 			    struct ieee802_11_elems *elems,
14239ebb61a2SAshok Nagarajan 			    enum ieee80211_band band, u32 *basic_rates)
142446900298SJohannes Berg {
142546900298SJohannes Berg 	struct ieee80211_supported_band *sband;
142646900298SJohannes Berg 	size_t num_rates;
14272103dec1SSimon Wunderlich 	u32 supp_rates, rate_flags;
14282103dec1SSimon Wunderlich 	int i, j, shift;
14292103dec1SSimon Wunderlich 	sband = sdata->local->hw.wiphy->bands[band];
14302103dec1SSimon Wunderlich 
14312103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
14322103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
143346900298SJohannes Berg 
14344ee73f33SMichal Kazior 	if (WARN_ON(!sband))
14354ee73f33SMichal Kazior 		return 1;
143646900298SJohannes Berg 
143746900298SJohannes Berg 	num_rates = sband->n_bitrates;
143846900298SJohannes Berg 	supp_rates = 0;
143946900298SJohannes Berg 	for (i = 0; i < elems->supp_rates_len +
144046900298SJohannes Berg 		     elems->ext_supp_rates_len; i++) {
144146900298SJohannes Berg 		u8 rate = 0;
144246900298SJohannes Berg 		int own_rate;
14439ebb61a2SAshok Nagarajan 		bool is_basic;
144446900298SJohannes Berg 		if (i < elems->supp_rates_len)
144546900298SJohannes Berg 			rate = elems->supp_rates[i];
144646900298SJohannes Berg 		else if (elems->ext_supp_rates)
144746900298SJohannes Berg 			rate = elems->ext_supp_rates
144846900298SJohannes Berg 				[i - elems->supp_rates_len];
144946900298SJohannes Berg 		own_rate = 5 * (rate & 0x7f);
14509ebb61a2SAshok Nagarajan 		is_basic = !!(rate & 0x80);
14519ebb61a2SAshok Nagarajan 
14529ebb61a2SAshok Nagarajan 		if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
14539ebb61a2SAshok Nagarajan 			continue;
14549ebb61a2SAshok Nagarajan 
14559ebb61a2SAshok Nagarajan 		for (j = 0; j < num_rates; j++) {
14562103dec1SSimon Wunderlich 			int brate;
14572103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[j].flags)
14582103dec1SSimon Wunderlich 			    != rate_flags)
14592103dec1SSimon Wunderlich 				continue;
14602103dec1SSimon Wunderlich 
14612103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
14622103dec1SSimon Wunderlich 					     1 << shift);
14632103dec1SSimon Wunderlich 
14642103dec1SSimon Wunderlich 			if (brate == own_rate) {
146546900298SJohannes Berg 				supp_rates |= BIT(j);
14669ebb61a2SAshok Nagarajan 				if (basic_rates && is_basic)
14679ebb61a2SAshok Nagarajan 					*basic_rates |= BIT(j);
14689ebb61a2SAshok Nagarajan 			}
14699ebb61a2SAshok Nagarajan 		}
147046900298SJohannes Berg 	}
147146900298SJohannes Berg 	return supp_rates;
147246900298SJohannes Berg }
1473f2753ddbSJohannes Berg 
147484f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local)
147584f6a01cSJohannes Berg {
147684f6a01cSJohannes Berg 	ieee80211_led_radio(local, false);
147767408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
147884f6a01cSJohannes Berg 
147984f6a01cSJohannes Berg 	cancel_work_sync(&local->reconfig_filter);
148084f6a01cSJohannes Berg 
148184f6a01cSJohannes Berg 	flush_workqueue(local->workqueue);
1482678f415fSLennert Buytenhek 	drv_stop(local);
148384f6a01cSJohannes Berg }
148484f6a01cSJohannes Berg 
1485f6837ba8SJohannes Berg static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
1486f6837ba8SJohannes Berg {
1487f6837ba8SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
1488f6837ba8SJohannes Berg 	struct ieee80211_chanctx *ctx;
1489f6837ba8SJohannes Berg 
1490f6837ba8SJohannes Berg 	/*
1491f6837ba8SJohannes Berg 	 * We get here if during resume the device can't be restarted properly.
1492f6837ba8SJohannes Berg 	 * We might also get here if this happens during HW reset, which is a
1493f6837ba8SJohannes Berg 	 * slightly different situation and we need to drop all connections in
1494f6837ba8SJohannes Berg 	 * the latter case.
1495f6837ba8SJohannes Berg 	 *
1496f6837ba8SJohannes Berg 	 * Ask cfg80211 to turn off all interfaces, this will result in more
1497f6837ba8SJohannes Berg 	 * warnings but at least we'll then get into a clean stopped state.
1498f6837ba8SJohannes Berg 	 */
1499f6837ba8SJohannes Berg 
1500f6837ba8SJohannes Berg 	local->resuming = false;
1501f6837ba8SJohannes Berg 	local->suspended = false;
1502f6837ba8SJohannes Berg 	local->started = false;
1503f6837ba8SJohannes Berg 
1504f6837ba8SJohannes Berg 	/* scheduled scan clearly can't be running any more, but tell
1505f6837ba8SJohannes Berg 	 * cfg80211 and clear local state
1506f6837ba8SJohannes Berg 	 */
1507f6837ba8SJohannes Berg 	ieee80211_sched_scan_end(local);
1508f6837ba8SJohannes Berg 
1509f6837ba8SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list)
1510f6837ba8SJohannes Berg 		sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
1511f6837ba8SJohannes Berg 
1512f6837ba8SJohannes Berg 	/* Mark channel contexts as not being in the driver any more to avoid
1513f6837ba8SJohannes Berg 	 * removing them from the driver during the shutdown process...
1514f6837ba8SJohannes Berg 	 */
1515f6837ba8SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1516f6837ba8SJohannes Berg 	list_for_each_entry(ctx, &local->chanctx_list, list)
1517f6837ba8SJohannes Berg 		ctx->driver_present = false;
1518f6837ba8SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
1519f6837ba8SJohannes Berg 
1520f6837ba8SJohannes Berg 	cfg80211_shutdown_all_interfaces(local->hw.wiphy);
1521f6837ba8SJohannes Berg }
1522f6837ba8SJohannes Berg 
1523153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local,
1524153a5fc4SStanislaw Gruszka 				     struct ieee80211_sub_if_data *sdata)
1525153a5fc4SStanislaw Gruszka {
1526153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx_conf *conf;
1527153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx *ctx;
1528153a5fc4SStanislaw Gruszka 
1529153a5fc4SStanislaw Gruszka 	if (!local->use_chanctx)
1530153a5fc4SStanislaw Gruszka 		return;
1531153a5fc4SStanislaw Gruszka 
1532153a5fc4SStanislaw Gruszka 	mutex_lock(&local->chanctx_mtx);
1533153a5fc4SStanislaw Gruszka 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1534153a5fc4SStanislaw Gruszka 					 lockdep_is_held(&local->chanctx_mtx));
1535153a5fc4SStanislaw Gruszka 	if (conf) {
1536153a5fc4SStanislaw Gruszka 		ctx = container_of(conf, struct ieee80211_chanctx, conf);
1537153a5fc4SStanislaw Gruszka 		drv_assign_vif_chanctx(local, sdata, ctx);
1538153a5fc4SStanislaw Gruszka 	}
1539153a5fc4SStanislaw Gruszka 	mutex_unlock(&local->chanctx_mtx);
1540153a5fc4SStanislaw Gruszka }
1541153a5fc4SStanislaw Gruszka 
1542f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local)
1543f2753ddbSJohannes Berg {
1544f2753ddbSJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
1545f2753ddbSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
154655de908aSJohannes Berg 	struct ieee80211_chanctx *ctx;
1547f2753ddbSJohannes Berg 	struct sta_info *sta;
15482683d65bSEliad Peller 	int res, i;
1549d888130aSJohannes Berg 	bool reconfig_due_to_wowlan = false;
1550d43c6b6eSDavid Spinadel 	struct ieee80211_sub_if_data *sched_scan_sdata;
1551d43c6b6eSDavid Spinadel 	bool sched_scan_stopped = false;
1552d888130aSJohannes Berg 
15538f21b0adSJohannes Berg #ifdef CONFIG_PM
1554ceb99fe0SJohannes Berg 	if (local->suspended)
1555ceb99fe0SJohannes Berg 		local->resuming = true;
1556f2753ddbSJohannes Berg 
1557eecc4800SJohannes Berg 	if (local->wowlan) {
1558eecc4800SJohannes Berg 		res = drv_resume(local);
155927b3eb9cSJohannes Berg 		local->wowlan = false;
1560eecc4800SJohannes Berg 		if (res < 0) {
1561eecc4800SJohannes Berg 			local->resuming = false;
1562eecc4800SJohannes Berg 			return res;
1563eecc4800SJohannes Berg 		}
1564eecc4800SJohannes Berg 		if (res == 0)
1565eecc4800SJohannes Berg 			goto wake_up;
1566eecc4800SJohannes Berg 		WARN_ON(res > 1);
1567eecc4800SJohannes Berg 		/*
1568eecc4800SJohannes Berg 		 * res is 1, which means the driver requested
1569eecc4800SJohannes Berg 		 * to go through a regular reset on wakeup.
1570eecc4800SJohannes Berg 		 */
1571d888130aSJohannes Berg 		reconfig_due_to_wowlan = true;
1572eecc4800SJohannes Berg 	}
1573eecc4800SJohannes Berg #endif
157494f9b97bSJohannes Berg 	/* everything else happens only if HW was up & running */
157594f9b97bSJohannes Berg 	if (!local->open_count)
157694f9b97bSJohannes Berg 		goto wake_up;
157794f9b97bSJohannes Berg 
157824feda00SLuis R. Rodriguez 	/*
157924feda00SLuis R. Rodriguez 	 * Upon resume hardware can sometimes be goofy due to
158024feda00SLuis R. Rodriguez 	 * various platform / driver / bus issues, so restarting
158124feda00SLuis R. Rodriguez 	 * the device may at times not work immediately. Propagate
158224feda00SLuis R. Rodriguez 	 * the error.
158324feda00SLuis R. Rodriguez 	 */
158424487981SJohannes Berg 	res = drv_start(local);
158524feda00SLuis R. Rodriguez 	if (res) {
1586f6837ba8SJohannes Berg 		if (local->suspended)
1587f6837ba8SJohannes Berg 			WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
1588f6837ba8SJohannes Berg 		else
1589f6837ba8SJohannes Berg 			WARN(1, "Hardware became unavailable during restart.\n");
1590f6837ba8SJohannes Berg 		ieee80211_handle_reconfig_failure(local);
159124feda00SLuis R. Rodriguez 		return res;
159224feda00SLuis R. Rodriguez 	}
1593f2753ddbSJohannes Berg 
15947f281975SYogesh Ashok Powar 	/* setup fragmentation threshold */
15957f281975SYogesh Ashok Powar 	drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
15967f281975SYogesh Ashok Powar 
15977f281975SYogesh Ashok Powar 	/* setup RTS threshold */
15987f281975SYogesh Ashok Powar 	drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
15997f281975SYogesh Ashok Powar 
16007f281975SYogesh Ashok Powar 	/* reset coverage class */
16017f281975SYogesh Ashok Powar 	drv_set_coverage_class(local, hw->wiphy->coverage_class);
16027f281975SYogesh Ashok Powar 
16031f87f7d3SJohannes Berg 	ieee80211_led_radio(local, true);
160467408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local,
160567408c8cSJohannes Berg 				   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
1606f2753ddbSJohannes Berg 
1607f2753ddbSJohannes Berg 	/* add interfaces */
16084b6f1dd6SJohannes Berg 	sdata = rtnl_dereference(local->monitor_sdata);
16094b6f1dd6SJohannes Berg 	if (sdata) {
16103c3e21e7SJohannes Berg 		/* in HW restart it exists already */
16113c3e21e7SJohannes Berg 		WARN_ON(local->resuming);
16124b6f1dd6SJohannes Berg 		res = drv_add_interface(local, sdata);
16134b6f1dd6SJohannes Berg 		if (WARN_ON(res)) {
16140c2bef46SMonam Agarwal 			RCU_INIT_POINTER(local->monitor_sdata, NULL);
16154b6f1dd6SJohannes Berg 			synchronize_net();
16164b6f1dd6SJohannes Berg 			kfree(sdata);
16174b6f1dd6SJohannes Berg 		}
16184b6f1dd6SJohannes Berg 	}
16194b6f1dd6SJohannes Berg 
1620f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1621f2753ddbSJohannes Berg 		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
1622f2753ddbSJohannes Berg 		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
16231ed32e4fSJohannes Berg 		    ieee80211_sdata_running(sdata))
16247b7eab6fSJohannes Berg 			res = drv_add_interface(local, sdata);
1625f2753ddbSJohannes Berg 	}
1626f2753ddbSJohannes Berg 
162755de908aSJohannes Berg 	/* add channel contexts */
1628f0dea9c7SArend van Spriel 	if (local->use_chanctx) {
162955de908aSJohannes Berg 		mutex_lock(&local->chanctx_mtx);
163055de908aSJohannes Berg 		list_for_each_entry(ctx, &local->chanctx_list, list)
163155de908aSJohannes Berg 			WARN_ON(drv_add_chanctx(local, ctx));
163255de908aSJohannes Berg 		mutex_unlock(&local->chanctx_mtx);
163355de908aSJohannes Berg 
16346352c87fSJohannes Berg 		list_for_each_entry(sdata, &local->interfaces, list) {
16356352c87fSJohannes Berg 			if (!ieee80211_sdata_running(sdata))
16366352c87fSJohannes Berg 				continue;
1637153a5fc4SStanislaw Gruszka 			ieee80211_assign_chanctx(local, sdata);
16386352c87fSJohannes Berg 		}
16396352c87fSJohannes Berg 
1640fe5f2559SJohannes Berg 		sdata = rtnl_dereference(local->monitor_sdata);
1641153a5fc4SStanislaw Gruszka 		if (sdata && ieee80211_sdata_running(sdata))
1642153a5fc4SStanislaw Gruszka 			ieee80211_assign_chanctx(local, sdata);
16437df180f7SZhao, Gang 	}
1644fe5f2559SJohannes Berg 
1645f2753ddbSJohannes Berg 	/* add STAs back */
164634e89507SJohannes Berg 	mutex_lock(&local->sta_mtx);
1647f2753ddbSJohannes Berg 	list_for_each_entry(sta, &local->sta_list, list) {
1648f09603a2SJohannes Berg 		enum ieee80211_sta_state state;
1649f09603a2SJohannes Berg 
16502e8d397eSArik Nemtsov 		if (!sta->uploaded)
16512e8d397eSArik Nemtsov 			continue;
16522e8d397eSArik Nemtsov 
16532e8d397eSArik Nemtsov 		/* AP-mode stations will be added later */
16542e8d397eSArik Nemtsov 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
16552e8d397eSArik Nemtsov 			continue;
16562e8d397eSArik Nemtsov 
1657f09603a2SJohannes Berg 		for (state = IEEE80211_STA_NOTEXIST;
1658bd34ab62SMeenakshi Venkataraman 		     state < sta->sta_state; state++)
16592e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
16602e8d397eSArik Nemtsov 					      state + 1));
1661f2753ddbSJohannes Berg 	}
166234e89507SJohannes Berg 	mutex_unlock(&local->sta_mtx);
1663f2753ddbSJohannes Berg 
16642683d65bSEliad Peller 	/* reconfigure tx conf */
166554bcbc69SJohannes Berg 	if (hw->queues >= IEEE80211_NUM_ACS) {
1666f6f3def3SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
1667f6f3def3SEliad Peller 			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
1668f6f3def3SEliad Peller 			    sdata->vif.type == NL80211_IFTYPE_MONITOR ||
1669f6f3def3SEliad Peller 			    !ieee80211_sdata_running(sdata))
1670f6f3def3SEliad Peller 				continue;
1671f6f3def3SEliad Peller 
167254bcbc69SJohannes Berg 			for (i = 0; i < IEEE80211_NUM_ACS; i++)
167354bcbc69SJohannes Berg 				drv_conf_tx(local, sdata, i,
167454bcbc69SJohannes Berg 					    &sdata->tx_conf[i]);
167554bcbc69SJohannes Berg 		}
1676f6f3def3SEliad Peller 	}
16772683d65bSEliad Peller 
1678f2753ddbSJohannes Berg 	/* reconfigure hardware */
1679f2753ddbSJohannes Berg 	ieee80211_hw_config(local, ~0);
1680f2753ddbSJohannes Berg 
1681f2753ddbSJohannes Berg 	ieee80211_configure_filter(local);
1682f2753ddbSJohannes Berg 
1683f2753ddbSJohannes Berg 	/* Finally also reconfigure all the BSS information */
1684f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1685ac8dd506SJohannes Berg 		u32 changed;
1686ac8dd506SJohannes Berg 
16879607e6b6SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1688f2753ddbSJohannes Berg 			continue;
1689ac8dd506SJohannes Berg 
1690ac8dd506SJohannes Berg 		/* common change flags for all interface types */
1691ac8dd506SJohannes Berg 		changed = BSS_CHANGED_ERP_CTS_PROT |
1692ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_PREAMBLE |
1693ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_SLOT |
1694ac8dd506SJohannes Berg 			  BSS_CHANGED_HT |
1695ac8dd506SJohannes Berg 			  BSS_CHANGED_BASIC_RATES |
1696ac8dd506SJohannes Berg 			  BSS_CHANGED_BEACON_INT |
1697ac8dd506SJohannes Berg 			  BSS_CHANGED_BSSID |
16984ced3f74SJohannes Berg 			  BSS_CHANGED_CQM |
169955de47f6SEliad Peller 			  BSS_CHANGED_QOS |
17001ea6f9c0SJohannes Berg 			  BSS_CHANGED_IDLE |
17011ea6f9c0SJohannes Berg 			  BSS_CHANGED_TXPOWER;
1702ac8dd506SJohannes Berg 
1703f2753ddbSJohannes Berg 		switch (sdata->vif.type) {
1704f2753ddbSJohannes Berg 		case NL80211_IFTYPE_STATION:
17050d392e93SEliad Peller 			changed |= BSS_CHANGED_ASSOC |
1706ab095877SEliad Peller 				   BSS_CHANGED_ARP_FILTER |
1707ab095877SEliad Peller 				   BSS_CHANGED_PS;
1708c65dd147SEmmanuel Grumbach 
1709989c6505SAlexander Bondar 			/* Re-send beacon info report to the driver */
1710989c6505SAlexander Bondar 			if (sdata->u.mgd.have_beacon)
1711989c6505SAlexander Bondar 				changed |= BSS_CHANGED_BEACON_INFO;
1712c65dd147SEmmanuel Grumbach 
17138d61ffa5SJohannes Berg 			sdata_lock(sdata);
1714ac8dd506SJohannes Berg 			ieee80211_bss_info_change_notify(sdata, changed);
17158d61ffa5SJohannes Berg 			sdata_unlock(sdata);
1716ac8dd506SJohannes Berg 			break;
1717f2753ddbSJohannes Berg 		case NL80211_IFTYPE_ADHOC:
1718ac8dd506SJohannes Berg 			changed |= BSS_CHANGED_IBSS;
1719ac8dd506SJohannes Berg 			/* fall through */
1720f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP:
1721339afbf4SJohannes Berg 			changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
1722e7979ac7SArik Nemtsov 
17231041638fSJohannes Berg 			if (sdata->vif.type == NL80211_IFTYPE_AP) {
1724e7979ac7SArik Nemtsov 				changed |= BSS_CHANGED_AP_PROBE_RESP;
1725e7979ac7SArik Nemtsov 
17261041638fSJohannes Berg 				if (rcu_access_pointer(sdata->u.ap.beacon))
17271041638fSJohannes Berg 					drv_start_ap(local, sdata);
17281041638fSJohannes Berg 			}
17291041638fSJohannes Berg 
17307827493bSArik Nemtsov 			/* fall through */
1731f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
17328da34932SJohannes Berg 			if (sdata->vif.bss_conf.enable_beacon) {
1733ac8dd506SJohannes Berg 				changed |= BSS_CHANGED_BEACON |
1734ac8dd506SJohannes Berg 					   BSS_CHANGED_BEACON_ENABLED;
17352d0ddec5SJohannes Berg 				ieee80211_bss_info_change_notify(sdata, changed);
17368da34932SJohannes Berg 			}
1737f2753ddbSJohannes Berg 			break;
1738f2753ddbSJohannes Berg 		case NL80211_IFTYPE_WDS:
1739f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
1740f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
174198104fdeSJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
1742b205786eSZhao, Gang 			/* nothing to do */
1743f142c6b9SJohannes Berg 			break;
1744f2753ddbSJohannes Berg 		case NL80211_IFTYPE_UNSPECIFIED:
17452e161f78SJohannes Berg 		case NUM_NL80211_IFTYPES:
17462ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_CLIENT:
17472ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_GO:
1748f2753ddbSJohannes Berg 			WARN_ON(1);
1749f2753ddbSJohannes Berg 			break;
1750f2753ddbSJohannes Berg 		}
1751f2753ddbSJohannes Berg 	}
1752f2753ddbSJohannes Berg 
17538e1b23b9SEyal Shapira 	ieee80211_recalc_ps(local, -1);
17548e1b23b9SEyal Shapira 
17552a419056SJohannes Berg 	/*
17566e1b1b24SEliad Peller 	 * The sta might be in psm against the ap (e.g. because
17576e1b1b24SEliad Peller 	 * this was the state before a hw restart), so we
17586e1b1b24SEliad Peller 	 * explicitly send a null packet in order to make sure
17596e1b1b24SEliad Peller 	 * it'll sync against the ap (and get out of psm).
17606e1b1b24SEliad Peller 	 */
17616e1b1b24SEliad Peller 	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
17626e1b1b24SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
17636e1b1b24SEliad Peller 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
17646e1b1b24SEliad Peller 				continue;
176520f544eeSJohannes Berg 			if (!sdata->u.mgd.associated)
176620f544eeSJohannes Berg 				continue;
17676e1b1b24SEliad Peller 
17686e1b1b24SEliad Peller 			ieee80211_send_nullfunc(local, sdata, 0);
17696e1b1b24SEliad Peller 		}
17706e1b1b24SEliad Peller 	}
17716e1b1b24SEliad Peller 
17722e8d397eSArik Nemtsov 	/* APs are now beaconing, add back stations */
17732e8d397eSArik Nemtsov 	mutex_lock(&local->sta_mtx);
17742e8d397eSArik Nemtsov 	list_for_each_entry(sta, &local->sta_list, list) {
17752e8d397eSArik Nemtsov 		enum ieee80211_sta_state state;
17762e8d397eSArik Nemtsov 
17772e8d397eSArik Nemtsov 		if (!sta->uploaded)
17782e8d397eSArik Nemtsov 			continue;
17792e8d397eSArik Nemtsov 
17802e8d397eSArik Nemtsov 		if (sta->sdata->vif.type != NL80211_IFTYPE_AP)
17812e8d397eSArik Nemtsov 			continue;
17822e8d397eSArik Nemtsov 
17832e8d397eSArik Nemtsov 		for (state = IEEE80211_STA_NOTEXIST;
17842e8d397eSArik Nemtsov 		     state < sta->sta_state; state++)
17852e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
17862e8d397eSArik Nemtsov 					      state + 1));
17872e8d397eSArik Nemtsov 	}
17882e8d397eSArik Nemtsov 	mutex_unlock(&local->sta_mtx);
17892e8d397eSArik Nemtsov 
17907b21aea0SEyal Shapira 	/* add back keys */
17917b21aea0SEyal Shapira 	list_for_each_entry(sdata, &local->interfaces, list)
17927b21aea0SEyal Shapira 		if (ieee80211_sdata_running(sdata))
17937b21aea0SEyal Shapira 			ieee80211_enable_keys(sdata);
17947b21aea0SEyal Shapira 
1795c6209488SEliad Peller  wake_up:
179604800adaSArik Nemtsov 	local->in_reconfig = false;
179704800adaSArik Nemtsov 	barrier();
179804800adaSArik Nemtsov 
17993c3e21e7SJohannes Berg 	if (local->monitors == local->open_count && local->monitors > 0)
18003c3e21e7SJohannes Berg 		ieee80211_add_virtual_monitor(local);
18013c3e21e7SJohannes Berg 
18026e1b1b24SEliad Peller 	/*
18032a419056SJohannes Berg 	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
18042a419056SJohannes Berg 	 * sessions can be established after a resume.
18052a419056SJohannes Berg 	 *
18062a419056SJohannes Berg 	 * Also tear down aggregation sessions since reconfiguring
18072a419056SJohannes Berg 	 * them in a hardware restart scenario is not easily done
18082a419056SJohannes Berg 	 * right now, and the hardware will have lost information
18092a419056SJohannes Berg 	 * about the sessions, but we and the AP still think they
18102a419056SJohannes Berg 	 * are active. This is really a workaround though.
18112a419056SJohannes Berg 	 */
181274e2bd1fSWey-Yi Guy 	if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
18132a419056SJohannes Berg 		mutex_lock(&local->sta_mtx);
18142a419056SJohannes Berg 
18152a419056SJohannes Berg 		list_for_each_entry(sta, &local->sta_list, list) {
1816c82c4a80SJohannes Berg 			ieee80211_sta_tear_down_BA_sessions(
1817c82c4a80SJohannes Berg 					sta, AGG_STOP_LOCAL_REQUEST);
1818c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
181974e2bd1fSWey-Yi Guy 		}
18202a419056SJohannes Berg 
18212a419056SJohannes Berg 		mutex_unlock(&local->sta_mtx);
182274e2bd1fSWey-Yi Guy 	}
182374e2bd1fSWey-Yi Guy 
1824445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
1825cca07b00SLuciano Coelho 					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
1826cca07b00SLuciano Coelho 					false);
1827f2753ddbSJohannes Berg 
18285bb644a0SJohannes Berg 	/*
182932769814SArik Nemtsov 	 * Reconfigure sched scan if it was interrupted by FW restart or
183032769814SArik Nemtsov 	 * suspend.
183132769814SArik Nemtsov 	 */
183232769814SArik Nemtsov 	mutex_lock(&local->mtx);
183332769814SArik Nemtsov 	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
183432769814SArik Nemtsov 						lockdep_is_held(&local->mtx));
183532769814SArik Nemtsov 	if (sched_scan_sdata && local->sched_scan_req)
183632769814SArik Nemtsov 		/*
183732769814SArik Nemtsov 		 * Sched scan stopped, but we don't want to report it. Instead,
183832769814SArik Nemtsov 		 * we're trying to reschedule.
183932769814SArik Nemtsov 		 */
184032769814SArik Nemtsov 		if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
184132769814SArik Nemtsov 							 local->sched_scan_req))
184232769814SArik Nemtsov 			sched_scan_stopped = true;
184332769814SArik Nemtsov 	mutex_unlock(&local->mtx);
184432769814SArik Nemtsov 
184532769814SArik Nemtsov 	if (sched_scan_stopped)
1846e669ba2dSEliad Peller 		cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
184732769814SArik Nemtsov 
184832769814SArik Nemtsov 	/*
18495bb644a0SJohannes Berg 	 * If this is for hw restart things are still running.
18505bb644a0SJohannes Berg 	 * We may want to change that later, however.
18515bb644a0SJohannes Berg 	 */
18528f21b0adSJohannes Berg 	if (!local->suspended || reconfig_due_to_wowlan)
18539214ad7fSJohannes Berg 		drv_restart_complete(local);
18548f21b0adSJohannes Berg 
18558f21b0adSJohannes Berg 	if (!local->suspended)
18565bb644a0SJohannes Berg 		return 0;
18575bb644a0SJohannes Berg 
18585bb644a0SJohannes Berg #ifdef CONFIG_PM
1859ceb99fe0SJohannes Berg 	/* first set suspended false, then resuming */
18605bb644a0SJohannes Berg 	local->suspended = false;
1861ceb99fe0SJohannes Berg 	mb();
1862ceb99fe0SJohannes Berg 	local->resuming = false;
18635bb644a0SJohannes Berg 
1864b8360ab8SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1865b8360ab8SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1866b8360ab8SJohannes Berg 			continue;
1867b8360ab8SJohannes Berg 		if (sdata->vif.type == NL80211_IFTYPE_STATION)
1868b8360ab8SJohannes Berg 			ieee80211_sta_restart(sdata);
1869b8360ab8SJohannes Berg 	}
1870b8360ab8SJohannes Berg 
187126d59535SJohannes Berg 	mod_timer(&local->sta_cleanup, jiffies + 1);
18725bb644a0SJohannes Berg #else
18735bb644a0SJohannes Berg 	WARN_ON(1);
18745bb644a0SJohannes Berg #endif
1875d43c6b6eSDavid Spinadel 
1876f2753ddbSJohannes Berg 	return 0;
1877f2753ddbSJohannes Berg }
187842935ecaSLuis R. Rodriguez 
187995acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
188095acac61SJohannes Berg {
188195acac61SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
188295acac61SJohannes Berg 	struct ieee80211_local *local;
188395acac61SJohannes Berg 	struct ieee80211_key *key;
188495acac61SJohannes Berg 
188595acac61SJohannes Berg 	if (WARN_ON(!vif))
188695acac61SJohannes Berg 		return;
188795acac61SJohannes Berg 
188895acac61SJohannes Berg 	sdata = vif_to_sdata(vif);
188995acac61SJohannes Berg 	local = sdata->local;
189095acac61SJohannes Berg 
189195acac61SJohannes Berg 	if (WARN_ON(!local->resuming))
189295acac61SJohannes Berg 		return;
189395acac61SJohannes Berg 
189495acac61SJohannes Berg 	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
189595acac61SJohannes Berg 		return;
189695acac61SJohannes Berg 
189795acac61SJohannes Berg 	sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME;
189895acac61SJohannes Berg 
189995acac61SJohannes Berg 	mutex_lock(&local->key_mtx);
190095acac61SJohannes Berg 	list_for_each_entry(key, &sdata->key_list, list)
190195acac61SJohannes Berg 		key->flags |= KEY_FLAG_TAINTED;
190295acac61SJohannes Berg 	mutex_unlock(&local->key_mtx);
190395acac61SJohannes Berg }
190495acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
190595acac61SJohannes Berg 
190604ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
19070f78231bSJohannes Berg {
190804ecd257SJohannes Berg 	struct ieee80211_local *local = sdata->local;
190904ecd257SJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
191004ecd257SJohannes Berg 	struct ieee80211_chanctx *chanctx;
19110f78231bSJohannes Berg 
191204ecd257SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
19130f78231bSJohannes Berg 
191404ecd257SJohannes Berg 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
191504ecd257SJohannes Berg 					lockdep_is_held(&local->chanctx_mtx));
19160f78231bSJohannes Berg 
191704ecd257SJohannes Berg 	if (WARN_ON_ONCE(!chanctx_conf))
19185d8e4237SJohannes Berg 		goto unlock;
19190f78231bSJohannes Berg 
192004ecd257SJohannes Berg 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
192104ecd257SJohannes Berg 	ieee80211_recalc_smps_chanctx(local, chanctx);
19225d8e4237SJohannes Berg  unlock:
192304ecd257SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
19240f78231bSJohannes Berg }
19258e664fb3SJohannes Berg 
192621f659bfSEliad Peller void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
192721f659bfSEliad Peller {
192821f659bfSEliad Peller 	struct ieee80211_local *local = sdata->local;
192921f659bfSEliad Peller 	struct ieee80211_chanctx_conf *chanctx_conf;
193021f659bfSEliad Peller 	struct ieee80211_chanctx *chanctx;
193121f659bfSEliad Peller 
193221f659bfSEliad Peller 	mutex_lock(&local->chanctx_mtx);
193321f659bfSEliad Peller 
193421f659bfSEliad Peller 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
193521f659bfSEliad Peller 					lockdep_is_held(&local->chanctx_mtx));
193621f659bfSEliad Peller 
193721f659bfSEliad Peller 	if (WARN_ON_ONCE(!chanctx_conf))
193821f659bfSEliad Peller 		goto unlock;
193921f659bfSEliad Peller 
194021f659bfSEliad Peller 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
194121f659bfSEliad Peller 	ieee80211_recalc_chanctx_min_def(local, chanctx);
194221f659bfSEliad Peller  unlock:
194321f659bfSEliad Peller 	mutex_unlock(&local->chanctx_mtx);
194421f659bfSEliad Peller }
194521f659bfSEliad Peller 
19468e664fb3SJohannes Berg static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
19478e664fb3SJohannes Berg {
19488e664fb3SJohannes Berg 	int i;
19498e664fb3SJohannes Berg 
19508e664fb3SJohannes Berg 	for (i = 0; i < n_ids; i++)
19518e664fb3SJohannes Berg 		if (ids[i] == id)
19528e664fb3SJohannes Berg 			return true;
19538e664fb3SJohannes Berg 	return false;
19548e664fb3SJohannes Berg }
19558e664fb3SJohannes Berg 
19568e664fb3SJohannes Berg /**
19578e664fb3SJohannes Berg  * ieee80211_ie_split - split an IE buffer according to ordering
19588e664fb3SJohannes Berg  *
19598e664fb3SJohannes Berg  * @ies: the IE buffer
19608e664fb3SJohannes Berg  * @ielen: the length of the IE buffer
19618e664fb3SJohannes Berg  * @ids: an array with element IDs that are allowed before
19628e664fb3SJohannes Berg  *	the split
19638e664fb3SJohannes Berg  * @n_ids: the size of the element ID array
19648e664fb3SJohannes Berg  * @offset: offset where to start splitting in the buffer
19658e664fb3SJohannes Berg  *
19668e664fb3SJohannes Berg  * This function splits an IE buffer by updating the @offset
19678e664fb3SJohannes Berg  * variable to point to the location where the buffer should be
19688e664fb3SJohannes Berg  * split.
19698e664fb3SJohannes Berg  *
19708e664fb3SJohannes Berg  * It assumes that the given IE buffer is well-formed, this
19718e664fb3SJohannes Berg  * has to be guaranteed by the caller!
19728e664fb3SJohannes Berg  *
19738e664fb3SJohannes Berg  * It also assumes that the IEs in the buffer are ordered
19748e664fb3SJohannes Berg  * correctly, if not the result of using this function will not
19758e664fb3SJohannes Berg  * be ordered correctly either, i.e. it does no reordering.
19768e664fb3SJohannes Berg  *
19778e664fb3SJohannes Berg  * The function returns the offset where the next part of the
19788e664fb3SJohannes Berg  * buffer starts, which may be @ielen if the entire (remainder)
19798e664fb3SJohannes Berg  * of the buffer should be used.
19808e664fb3SJohannes Berg  */
19818e664fb3SJohannes Berg size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
19828e664fb3SJohannes Berg 			  const u8 *ids, int n_ids, size_t offset)
19838e664fb3SJohannes Berg {
19848e664fb3SJohannes Berg 	size_t pos = offset;
19858e664fb3SJohannes Berg 
19868e664fb3SJohannes Berg 	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
19878e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
19888e664fb3SJohannes Berg 
19898e664fb3SJohannes Berg 	return pos;
19908e664fb3SJohannes Berg }
19918e664fb3SJohannes Berg 
19928e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
19938e664fb3SJohannes Berg {
19948e664fb3SJohannes Berg 	size_t pos = offset;
19958e664fb3SJohannes Berg 
19968e664fb3SJohannes Berg 	while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
19978e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
19988e664fb3SJohannes Berg 
19998e664fb3SJohannes Berg 	return pos;
20008e664fb3SJohannes Berg }
2001615f7b9bSMeenakshi Venkataraman 
2002615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata,
2003615f7b9bSMeenakshi Venkataraman 					    int rssi_min_thold,
2004615f7b9bSMeenakshi Venkataraman 					    int rssi_max_thold)
2005615f7b9bSMeenakshi Venkataraman {
2006615f7b9bSMeenakshi Venkataraman 	trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold);
2007615f7b9bSMeenakshi Venkataraman 
2008615f7b9bSMeenakshi Venkataraman 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
2009615f7b9bSMeenakshi Venkataraman 		return;
2010615f7b9bSMeenakshi Venkataraman 
2011615f7b9bSMeenakshi Venkataraman 	/*
2012615f7b9bSMeenakshi Venkataraman 	 * Scale up threshold values before storing it, as the RSSI averaging
2013615f7b9bSMeenakshi Venkataraman 	 * algorithm uses a scaled up value as well. Change this scaling
2014615f7b9bSMeenakshi Venkataraman 	 * factor if the RSSI averaging algorithm changes.
2015615f7b9bSMeenakshi Venkataraman 	 */
2016615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_min_thold = rssi_min_thold*16;
2017615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_max_thold = rssi_max_thold*16;
2018615f7b9bSMeenakshi Venkataraman }
2019615f7b9bSMeenakshi Venkataraman 
2020615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
2021615f7b9bSMeenakshi Venkataraman 				    int rssi_min_thold,
2022615f7b9bSMeenakshi Venkataraman 				    int rssi_max_thold)
2023615f7b9bSMeenakshi Venkataraman {
2024615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2025615f7b9bSMeenakshi Venkataraman 
2026615f7b9bSMeenakshi Venkataraman 	WARN_ON(rssi_min_thold == rssi_max_thold ||
2027615f7b9bSMeenakshi Venkataraman 		rssi_min_thold > rssi_max_thold);
2028615f7b9bSMeenakshi Venkataraman 
2029615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, rssi_min_thold,
2030615f7b9bSMeenakshi Venkataraman 				       rssi_max_thold);
2031615f7b9bSMeenakshi Venkataraman }
2032615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports);
2033615f7b9bSMeenakshi Venkataraman 
2034615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
2035615f7b9bSMeenakshi Venkataraman {
2036615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2037615f7b9bSMeenakshi Venkataraman 
2038615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, 0, 0);
2039615f7b9bSMeenakshi Venkataraman }
2040615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
2041768db343SArik Nemtsov 
2042ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
204342e7aa77SAlexander Simon 			      u16 cap)
204442e7aa77SAlexander Simon {
204542e7aa77SAlexander Simon 	__le16 tmp;
204642e7aa77SAlexander Simon 
204742e7aa77SAlexander Simon 	*pos++ = WLAN_EID_HT_CAPABILITY;
204842e7aa77SAlexander Simon 	*pos++ = sizeof(struct ieee80211_ht_cap);
204942e7aa77SAlexander Simon 	memset(pos, 0, sizeof(struct ieee80211_ht_cap));
205042e7aa77SAlexander Simon 
205142e7aa77SAlexander Simon 	/* capability flags */
205242e7aa77SAlexander Simon 	tmp = cpu_to_le16(cap);
205342e7aa77SAlexander Simon 	memcpy(pos, &tmp, sizeof(u16));
205442e7aa77SAlexander Simon 	pos += sizeof(u16);
205542e7aa77SAlexander Simon 
205642e7aa77SAlexander Simon 	/* AMPDU parameters */
2057ef96a842SBen Greear 	*pos++ = ht_cap->ampdu_factor |
2058ef96a842SBen Greear 		 (ht_cap->ampdu_density <<
205942e7aa77SAlexander Simon 			IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
206042e7aa77SAlexander Simon 
206142e7aa77SAlexander Simon 	/* MCS set */
2062ef96a842SBen Greear 	memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
2063ef96a842SBen Greear 	pos += sizeof(ht_cap->mcs);
206442e7aa77SAlexander Simon 
206542e7aa77SAlexander Simon 	/* extended capabilities */
206642e7aa77SAlexander Simon 	pos += sizeof(__le16);
206742e7aa77SAlexander Simon 
206842e7aa77SAlexander Simon 	/* BF capabilities */
206942e7aa77SAlexander Simon 	pos += sizeof(__le32);
207042e7aa77SAlexander Simon 
207142e7aa77SAlexander Simon 	/* antenna selection */
207242e7aa77SAlexander Simon 	pos += sizeof(u8);
207342e7aa77SAlexander Simon 
207442e7aa77SAlexander Simon 	return pos;
207542e7aa77SAlexander Simon }
207642e7aa77SAlexander Simon 
2077ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
2078ba0afa2fSMahesh Palivela 			       u32 cap)
2079ba0afa2fSMahesh Palivela {
2080ba0afa2fSMahesh Palivela 	__le32 tmp;
2081ba0afa2fSMahesh Palivela 
2082ba0afa2fSMahesh Palivela 	*pos++ = WLAN_EID_VHT_CAPABILITY;
2083d4950281SMahesh Palivela 	*pos++ = sizeof(struct ieee80211_vht_cap);
2084d4950281SMahesh Palivela 	memset(pos, 0, sizeof(struct ieee80211_vht_cap));
2085ba0afa2fSMahesh Palivela 
2086ba0afa2fSMahesh Palivela 	/* capability flags */
2087ba0afa2fSMahesh Palivela 	tmp = cpu_to_le32(cap);
2088ba0afa2fSMahesh Palivela 	memcpy(pos, &tmp, sizeof(u32));
2089ba0afa2fSMahesh Palivela 	pos += sizeof(u32);
2090ba0afa2fSMahesh Palivela 
2091ba0afa2fSMahesh Palivela 	/* VHT MCS set */
2092ba0afa2fSMahesh Palivela 	memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
2093ba0afa2fSMahesh Palivela 	pos += sizeof(vht_cap->vht_mcs);
2094ba0afa2fSMahesh Palivela 
2095ba0afa2fSMahesh Palivela 	return pos;
2096ba0afa2fSMahesh Palivela }
2097ba0afa2fSMahesh Palivela 
2098074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
20994bf88530SJohannes Berg 			       const struct cfg80211_chan_def *chandef,
2100431e3154SAshok Nagarajan 			       u16 prot_mode)
210142e7aa77SAlexander Simon {
2102074d46d1SJohannes Berg 	struct ieee80211_ht_operation *ht_oper;
210342e7aa77SAlexander Simon 	/* Build HT Information */
2104074d46d1SJohannes Berg 	*pos++ = WLAN_EID_HT_OPERATION;
2105074d46d1SJohannes Berg 	*pos++ = sizeof(struct ieee80211_ht_operation);
2106074d46d1SJohannes Berg 	ht_oper = (struct ieee80211_ht_operation *)pos;
21074bf88530SJohannes Berg 	ht_oper->primary_chan = ieee80211_frequency_to_channel(
21084bf88530SJohannes Berg 					chandef->chan->center_freq);
21094bf88530SJohannes Berg 	switch (chandef->width) {
21104bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
21114bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
21124bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80:
21134bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
21144bf88530SJohannes Berg 		if (chandef->center_freq1 > chandef->chan->center_freq)
21154bf88530SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
21164bf88530SJohannes Berg 		else
2117074d46d1SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
211842e7aa77SAlexander Simon 		break;
211942e7aa77SAlexander Simon 	default:
2120074d46d1SJohannes Berg 		ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
212142e7aa77SAlexander Simon 		break;
212242e7aa77SAlexander Simon 	}
2123aee286c2SThomas Pedersen 	if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
21244bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
21254bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20)
2126074d46d1SJohannes Berg 		ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
2127ff3cc5f4SSimon Wunderlich 
2128431e3154SAshok Nagarajan 	ht_oper->operation_mode = cpu_to_le16(prot_mode);
2129074d46d1SJohannes Berg 	ht_oper->stbc_param = 0x0000;
213042e7aa77SAlexander Simon 
213142e7aa77SAlexander Simon 	/* It seems that Basic MCS set and Supported MCS set
213242e7aa77SAlexander Simon 	   are identical for the first 10 bytes */
2133074d46d1SJohannes Berg 	memset(&ht_oper->basic_set, 0, 16);
2134074d46d1SJohannes Berg 	memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
213542e7aa77SAlexander Simon 
2136074d46d1SJohannes Berg 	return pos + sizeof(struct ieee80211_ht_operation);
213742e7aa77SAlexander Simon }
213842e7aa77SAlexander Simon 
21394bf88530SJohannes Berg void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
21404a3cb702SJohannes Berg 				  const struct ieee80211_ht_operation *ht_oper,
21414bf88530SJohannes Berg 				  struct cfg80211_chan_def *chandef)
214242e7aa77SAlexander Simon {
214342e7aa77SAlexander Simon 	enum nl80211_channel_type channel_type;
214442e7aa77SAlexander Simon 
21454bf88530SJohannes Berg 	if (!ht_oper) {
21464bf88530SJohannes Berg 		cfg80211_chandef_create(chandef, control_chan,
21474bf88530SJohannes Berg 					NL80211_CHAN_NO_HT);
21484bf88530SJohannes Berg 		return;
21494bf88530SJohannes Berg 	}
215042e7aa77SAlexander Simon 
2151074d46d1SJohannes Berg 	switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
215242e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
215342e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT20;
215442e7aa77SAlexander Simon 		break;
215542e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
215642e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40PLUS;
215742e7aa77SAlexander Simon 		break;
215842e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
215942e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40MINUS;
216042e7aa77SAlexander Simon 		break;
216142e7aa77SAlexander Simon 	default:
216242e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_NO_HT;
216342e7aa77SAlexander Simon 	}
216442e7aa77SAlexander Simon 
21654bf88530SJohannes Berg 	cfg80211_chandef_create(chandef, control_chan, channel_type);
216642e7aa77SAlexander Simon }
216742e7aa77SAlexander Simon 
21682103dec1SSimon Wunderlich int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
21692103dec1SSimon Wunderlich 			     const struct ieee80211_supported_band *sband,
21702103dec1SSimon Wunderlich 			     const u8 *srates, int srates_len, u32 *rates)
21712103dec1SSimon Wunderlich {
21722103dec1SSimon Wunderlich 	u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
21732103dec1SSimon Wunderlich 	int shift = ieee80211_chandef_get_shift(chandef);
21742103dec1SSimon Wunderlich 	struct ieee80211_rate *br;
21752103dec1SSimon Wunderlich 	int brate, rate, i, j, count = 0;
21762103dec1SSimon Wunderlich 
21772103dec1SSimon Wunderlich 	*rates = 0;
21782103dec1SSimon Wunderlich 
21792103dec1SSimon Wunderlich 	for (i = 0; i < srates_len; i++) {
21802103dec1SSimon Wunderlich 		rate = srates[i] & 0x7f;
21812103dec1SSimon Wunderlich 
21822103dec1SSimon Wunderlich 		for (j = 0; j < sband->n_bitrates; j++) {
21832103dec1SSimon Wunderlich 			br = &sband->bitrates[j];
21842103dec1SSimon Wunderlich 			if ((rate_flags & br->flags) != rate_flags)
21852103dec1SSimon Wunderlich 				continue;
21862103dec1SSimon Wunderlich 
21872103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
21882103dec1SSimon Wunderlich 			if (brate == rate) {
21892103dec1SSimon Wunderlich 				*rates |= BIT(j);
21902103dec1SSimon Wunderlich 				count++;
21912103dec1SSimon Wunderlich 				break;
21922103dec1SSimon Wunderlich 			}
21932103dec1SSimon Wunderlich 		}
21942103dec1SSimon Wunderlich 	}
21952103dec1SSimon Wunderlich 	return count;
21962103dec1SSimon Wunderlich }
21972103dec1SSimon Wunderlich 
2198fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
21996b77863bSJohannes Berg 			    struct sk_buff *skb, bool need_basic,
22006b77863bSJohannes Berg 			    enum ieee80211_band band)
2201768db343SArik Nemtsov {
2202768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2203768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
22042103dec1SSimon Wunderlich 	int rate, shift;
2205768db343SArik Nemtsov 	u8 i, rates, *pos;
2206fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
22072103dec1SSimon Wunderlich 	u32 rate_flags;
2208768db343SArik Nemtsov 
22092103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
22102103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
22116b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
22122103dec1SSimon Wunderlich 	rates = 0;
22132103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
22142103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
22152103dec1SSimon Wunderlich 			continue;
22162103dec1SSimon Wunderlich 		rates++;
22172103dec1SSimon Wunderlich 	}
2218768db343SArik Nemtsov 	if (rates > 8)
2219768db343SArik Nemtsov 		rates = 8;
2220768db343SArik Nemtsov 
2221768db343SArik Nemtsov 	if (skb_tailroom(skb) < rates + 2)
2222768db343SArik Nemtsov 		return -ENOMEM;
2223768db343SArik Nemtsov 
2224768db343SArik Nemtsov 	pos = skb_put(skb, rates + 2);
2225768db343SArik Nemtsov 	*pos++ = WLAN_EID_SUPP_RATES;
2226768db343SArik Nemtsov 	*pos++ = rates;
2227768db343SArik Nemtsov 	for (i = 0; i < rates; i++) {
2228657c3e0cSAshok Nagarajan 		u8 basic = 0;
22292103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
22302103dec1SSimon Wunderlich 			continue;
22312103dec1SSimon Wunderlich 
2232657c3e0cSAshok Nagarajan 		if (need_basic && basic_rates & BIT(i))
2233657c3e0cSAshok Nagarajan 			basic = 0x80;
2234768db343SArik Nemtsov 		rate = sband->bitrates[i].bitrate;
22352103dec1SSimon Wunderlich 		rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
22362103dec1SSimon Wunderlich 				    5 * (1 << shift));
22372103dec1SSimon Wunderlich 		*pos++ = basic | (u8) rate;
2238768db343SArik Nemtsov 	}
2239768db343SArik Nemtsov 
2240768db343SArik Nemtsov 	return 0;
2241768db343SArik Nemtsov }
2242768db343SArik Nemtsov 
2243fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
22446b77863bSJohannes Berg 				struct sk_buff *skb, bool need_basic,
22456b77863bSJohannes Berg 				enum ieee80211_band band)
2246768db343SArik Nemtsov {
2247768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2248768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
2249cc63ec76SChun-Yeow Yeoh 	int rate, shift;
2250768db343SArik Nemtsov 	u8 i, exrates, *pos;
2251fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
22522103dec1SSimon Wunderlich 	u32 rate_flags;
22532103dec1SSimon Wunderlich 
22542103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
22552103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
2256768db343SArik Nemtsov 
22576b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
22582103dec1SSimon Wunderlich 	exrates = 0;
22592103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
22602103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
22612103dec1SSimon Wunderlich 			continue;
22622103dec1SSimon Wunderlich 		exrates++;
22632103dec1SSimon Wunderlich 	}
22642103dec1SSimon Wunderlich 
2265768db343SArik Nemtsov 	if (exrates > 8)
2266768db343SArik Nemtsov 		exrates -= 8;
2267768db343SArik Nemtsov 	else
2268768db343SArik Nemtsov 		exrates = 0;
2269768db343SArik Nemtsov 
2270768db343SArik Nemtsov 	if (skb_tailroom(skb) < exrates + 2)
2271768db343SArik Nemtsov 		return -ENOMEM;
2272768db343SArik Nemtsov 
2273768db343SArik Nemtsov 	if (exrates) {
2274768db343SArik Nemtsov 		pos = skb_put(skb, exrates + 2);
2275768db343SArik Nemtsov 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
2276768db343SArik Nemtsov 		*pos++ = exrates;
2277768db343SArik Nemtsov 		for (i = 8; i < sband->n_bitrates; i++) {
2278657c3e0cSAshok Nagarajan 			u8 basic = 0;
22792103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[i].flags)
22802103dec1SSimon Wunderlich 			    != rate_flags)
22812103dec1SSimon Wunderlich 				continue;
2282657c3e0cSAshok Nagarajan 			if (need_basic && basic_rates & BIT(i))
2283657c3e0cSAshok Nagarajan 				basic = 0x80;
22842103dec1SSimon Wunderlich 			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
22852103dec1SSimon Wunderlich 					    5 * (1 << shift));
22862103dec1SSimon Wunderlich 			*pos++ = basic | (u8) rate;
2287768db343SArik Nemtsov 		}
2288768db343SArik Nemtsov 	}
2289768db343SArik Nemtsov 	return 0;
2290768db343SArik Nemtsov }
22911dae27f8SWey-Yi Guy 
22921dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif)
22931dae27f8SWey-Yi Guy {
22941dae27f8SWey-Yi Guy 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
22951dae27f8SWey-Yi Guy 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
22961dae27f8SWey-Yi Guy 
2297be6bcabcSWey-Yi Guy 	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) {
2298be6bcabcSWey-Yi Guy 		/* non-managed type inferfaces */
2299be6bcabcSWey-Yi Guy 		return 0;
2300be6bcabcSWey-Yi Guy 	}
23013a7bba64SEmmanuel Grumbach 	return ifmgd->ave_beacon_signal / 16;
23021dae27f8SWey-Yi Guy }
23030d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
230404ecd257SJohannes Berg 
230504ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
230604ecd257SJohannes Berg {
230704ecd257SJohannes Berg 	if (!mcs)
230804ecd257SJohannes Berg 		return 1;
230904ecd257SJohannes Berg 
231004ecd257SJohannes Berg 	/* TODO: consider rx_highest */
231104ecd257SJohannes Berg 
231204ecd257SJohannes Berg 	if (mcs->rx_mask[3])
231304ecd257SJohannes Berg 		return 4;
231404ecd257SJohannes Berg 	if (mcs->rx_mask[2])
231504ecd257SJohannes Berg 		return 3;
231604ecd257SJohannes Berg 	if (mcs->rx_mask[1])
231704ecd257SJohannes Berg 		return 2;
231804ecd257SJohannes Berg 	return 1;
231904ecd257SJohannes Berg }
2320f4bda337SThomas Pedersen 
2321f4bda337SThomas Pedersen /**
2322f4bda337SThomas Pedersen  * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
2323f4bda337SThomas Pedersen  * @local: mac80211 hw info struct
2324f4bda337SThomas Pedersen  * @status: RX status
2325f4bda337SThomas Pedersen  * @mpdu_len: total MPDU length (including FCS)
2326f4bda337SThomas Pedersen  * @mpdu_offset: offset into MPDU to calculate timestamp at
2327f4bda337SThomas Pedersen  *
2328f4bda337SThomas Pedersen  * This function calculates the RX timestamp at the given MPDU offset, taking
2329f4bda337SThomas Pedersen  * into account what the RX timestamp was. An offset of 0 will just normalize
2330f4bda337SThomas Pedersen  * the timestamp to TSF at beginning of MPDU reception.
2331f4bda337SThomas Pedersen  */
2332f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
2333f4bda337SThomas Pedersen 				     struct ieee80211_rx_status *status,
2334f4bda337SThomas Pedersen 				     unsigned int mpdu_len,
2335f4bda337SThomas Pedersen 				     unsigned int mpdu_offset)
2336f4bda337SThomas Pedersen {
2337f4bda337SThomas Pedersen 	u64 ts = status->mactime;
2338f4bda337SThomas Pedersen 	struct rate_info ri;
2339f4bda337SThomas Pedersen 	u16 rate;
2340f4bda337SThomas Pedersen 
2341f4bda337SThomas Pedersen 	if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
2342f4bda337SThomas Pedersen 		return 0;
2343f4bda337SThomas Pedersen 
2344f4bda337SThomas Pedersen 	memset(&ri, 0, sizeof(ri));
2345f4bda337SThomas Pedersen 
2346f4bda337SThomas Pedersen 	/* Fill cfg80211 rate info */
2347f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_HT) {
2348f4bda337SThomas Pedersen 		ri.mcs = status->rate_idx;
2349f4bda337SThomas Pedersen 		ri.flags |= RATE_INFO_FLAGS_MCS;
2350f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_40MHZ)
2351f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
2352f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_SHORT_GI)
2353f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
23545614618eSJohannes Berg 	} else if (status->flag & RX_FLAG_VHT) {
23555614618eSJohannes Berg 		ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
23565614618eSJohannes Berg 		ri.mcs = status->rate_idx;
23575614618eSJohannes Berg 		ri.nss = status->vht_nss;
23585614618eSJohannes Berg 		if (status->flag & RX_FLAG_40MHZ)
23595614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
23601b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_80MHZ)
23615614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
23621b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
23635614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
23641b8d242aSEmmanuel Grumbach 		if (status->vht_flag & RX_VHT_FLAG_160MHZ)
23655614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
23665614618eSJohannes Berg 		if (status->flag & RX_FLAG_SHORT_GI)
23675614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
2368f4bda337SThomas Pedersen 	} else {
2369f4bda337SThomas Pedersen 		struct ieee80211_supported_band *sband;
23702103dec1SSimon Wunderlich 		int shift = 0;
23712103dec1SSimon Wunderlich 		int bitrate;
23722103dec1SSimon Wunderlich 
23732103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_10MHZ)
23742103dec1SSimon Wunderlich 			shift = 1;
23752103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_5MHZ)
23762103dec1SSimon Wunderlich 			shift = 2;
2377f4bda337SThomas Pedersen 
2378f4bda337SThomas Pedersen 		sband = local->hw.wiphy->bands[status->band];
23792103dec1SSimon Wunderlich 		bitrate = sband->bitrates[status->rate_idx].bitrate;
23802103dec1SSimon Wunderlich 		ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
2381f4bda337SThomas Pedersen 	}
2382f4bda337SThomas Pedersen 
2383f4bda337SThomas Pedersen 	rate = cfg80211_calculate_bitrate(&ri);
2384d86aa4f8SJohannes Berg 	if (WARN_ONCE(!rate,
2385d86aa4f8SJohannes Berg 		      "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
2386d86aa4f8SJohannes Berg 		      status->flag, status->rate_idx, status->vht_nss))
2387d86aa4f8SJohannes Berg 		return 0;
2388f4bda337SThomas Pedersen 
2389f4bda337SThomas Pedersen 	/* rewind from end of MPDU */
2390f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_MACTIME_END)
2391f4bda337SThomas Pedersen 		ts -= mpdu_len * 8 * 10 / rate;
2392f4bda337SThomas Pedersen 
2393f4bda337SThomas Pedersen 	ts += mpdu_offset * 8 * 10 / rate;
2394f4bda337SThomas Pedersen 
2395f4bda337SThomas Pedersen 	return ts;
2396f4bda337SThomas Pedersen }
2397164eb02dSSimon Wunderlich 
2398164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
2399164eb02dSSimon Wunderlich {
2400164eb02dSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata;
2401d2859df5SJanusz Dziedzic 	struct cfg80211_chan_def chandef;
2402164eb02dSSimon Wunderlich 
240334a3740dSJohannes Berg 	mutex_lock(&local->mtx);
2404164eb02dSSimon Wunderlich 	mutex_lock(&local->iflist_mtx);
2405164eb02dSSimon Wunderlich 	list_for_each_entry(sdata, &local->interfaces, list) {
240634a3740dSJohannes Berg 		/* it might be waiting for the local->mtx, but then
240734a3740dSJohannes Berg 		 * by the time it gets it, sdata->wdev.cac_started
240834a3740dSJohannes Berg 		 * will no longer be true
240934a3740dSJohannes Berg 		 */
241034a3740dSJohannes Berg 		cancel_delayed_work(&sdata->dfs_cac_timer_work);
2411164eb02dSSimon Wunderlich 
2412164eb02dSSimon Wunderlich 		if (sdata->wdev.cac_started) {
2413d2859df5SJanusz Dziedzic 			chandef = sdata->vif.bss_conf.chandef;
2414164eb02dSSimon Wunderlich 			ieee80211_vif_release_channel(sdata);
2415164eb02dSSimon Wunderlich 			cfg80211_cac_event(sdata->dev,
2416d2859df5SJanusz Dziedzic 					   &chandef,
2417164eb02dSSimon Wunderlich 					   NL80211_RADAR_CAC_ABORTED,
2418164eb02dSSimon Wunderlich 					   GFP_KERNEL);
2419164eb02dSSimon Wunderlich 		}
2420164eb02dSSimon Wunderlich 	}
2421164eb02dSSimon Wunderlich 	mutex_unlock(&local->iflist_mtx);
242234a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
2423164eb02dSSimon Wunderlich }
2424164eb02dSSimon Wunderlich 
2425164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work)
2426164eb02dSSimon Wunderlich {
2427164eb02dSSimon Wunderlich 	struct ieee80211_local *local =
2428164eb02dSSimon Wunderlich 		container_of(work, struct ieee80211_local, radar_detected_work);
242984a3d1c9SJanusz Dziedzic 	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
2430164eb02dSSimon Wunderlich 
2431164eb02dSSimon Wunderlich 	ieee80211_dfs_cac_cancel(local);
2432164eb02dSSimon Wunderlich 
2433164eb02dSSimon Wunderlich 	if (local->use_chanctx)
2434164eb02dSSimon Wunderlich 		/* currently not handled */
2435164eb02dSSimon Wunderlich 		WARN_ON(1);
243684a3d1c9SJanusz Dziedzic 	else
2437164eb02dSSimon Wunderlich 		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
2438164eb02dSSimon Wunderlich }
2439164eb02dSSimon Wunderlich 
2440164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw)
2441164eb02dSSimon Wunderlich {
2442164eb02dSSimon Wunderlich 	struct ieee80211_local *local = hw_to_local(hw);
2443164eb02dSSimon Wunderlich 
2444164eb02dSSimon Wunderlich 	trace_api_radar_detected(local);
2445164eb02dSSimon Wunderlich 
2446164eb02dSSimon Wunderlich 	ieee80211_queue_work(hw, &local->radar_detected_work);
2447164eb02dSSimon Wunderlich }
2448164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected);
2449e6b7cde4SSimon Wunderlich 
2450e6b7cde4SSimon Wunderlich u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
2451e6b7cde4SSimon Wunderlich {
2452e6b7cde4SSimon Wunderlich 	u32 ret;
2453e6b7cde4SSimon Wunderlich 	int tmp;
2454e6b7cde4SSimon Wunderlich 
2455e6b7cde4SSimon Wunderlich 	switch (c->width) {
2456e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20:
2457e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2458e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2459e6b7cde4SSimon Wunderlich 		break;
2460e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_40:
2461e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20;
2462e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->chan->center_freq;
2463e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_40MHZ |
2464e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_VHT;
2465e6b7cde4SSimon Wunderlich 		break;
2466e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80:
2467e6b7cde4SSimon Wunderlich 		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
2468e6b7cde4SSimon Wunderlich 		/* n_P40 */
2469e6b7cde4SSimon Wunderlich 		tmp /= 2;
2470e6b7cde4SSimon Wunderlich 		/* freq_P40 */
2471e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
2472e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_40;
2473e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_VHT;
2474e6b7cde4SSimon Wunderlich 		break;
2475e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80P80:
2476e6b7cde4SSimon Wunderlich 		c->center_freq2 = 0;
2477e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2478e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2479e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2480e6b7cde4SSimon Wunderlich 		break;
2481e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_160:
2482e6b7cde4SSimon Wunderlich 		/* n_P20 */
2483e6b7cde4SSimon Wunderlich 		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
2484e6b7cde4SSimon Wunderlich 		/* n_P80 */
2485e6b7cde4SSimon Wunderlich 		tmp /= 4;
2486e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
2487e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2488e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2489e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2490e6b7cde4SSimon Wunderlich 		break;
2491e6b7cde4SSimon Wunderlich 	default:
2492e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20_NOHT:
2493e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2494e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2495e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2496e6b7cde4SSimon Wunderlich 		break;
2497e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_5:
2498e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_10:
2499e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2500e6b7cde4SSimon Wunderlich 		/* keep c->width */
2501e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2502e6b7cde4SSimon Wunderlich 		break;
2503e6b7cde4SSimon Wunderlich 	}
2504e6b7cde4SSimon Wunderlich 
2505e6b7cde4SSimon Wunderlich 	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
2506e6b7cde4SSimon Wunderlich 
2507e6b7cde4SSimon Wunderlich 	return ret;
2508e6b7cde4SSimon Wunderlich }
2509687da132SEmmanuel Grumbach 
2510687da132SEmmanuel Grumbach /*
2511687da132SEmmanuel Grumbach  * Returns true if smps_mode_new is strictly more restrictive than
2512687da132SEmmanuel Grumbach  * smps_mode_old.
2513687da132SEmmanuel Grumbach  */
2514687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
2515687da132SEmmanuel Grumbach 				   enum ieee80211_smps_mode smps_mode_new)
2516687da132SEmmanuel Grumbach {
2517687da132SEmmanuel Grumbach 	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
2518687da132SEmmanuel Grumbach 			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
2519687da132SEmmanuel Grumbach 		return false;
2520687da132SEmmanuel Grumbach 
2521687da132SEmmanuel Grumbach 	switch (smps_mode_old) {
2522687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_STATIC:
2523687da132SEmmanuel Grumbach 		return false;
2524687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_DYNAMIC:
2525687da132SEmmanuel Grumbach 		return smps_mode_new == IEEE80211_SMPS_STATIC;
2526687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_OFF:
2527687da132SEmmanuel Grumbach 		return smps_mode_new != IEEE80211_SMPS_OFF;
2528687da132SEmmanuel Grumbach 	default:
2529687da132SEmmanuel Grumbach 		WARN_ON(1);
2530687da132SEmmanuel Grumbach 	}
2531687da132SEmmanuel Grumbach 
2532687da132SEmmanuel Grumbach 	return false;
2533687da132SEmmanuel Grumbach }
2534c6da674aSChun-Yeow Yeoh 
2535c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
2536c6da674aSChun-Yeow Yeoh 			      struct cfg80211_csa_settings *csa_settings)
2537c6da674aSChun-Yeow Yeoh {
2538c6da674aSChun-Yeow Yeoh 	struct sk_buff *skb;
2539c6da674aSChun-Yeow Yeoh 	struct ieee80211_mgmt *mgmt;
2540c6da674aSChun-Yeow Yeoh 	struct ieee80211_local *local = sdata->local;
2541c6da674aSChun-Yeow Yeoh 	int freq;
2542c6da674aSChun-Yeow Yeoh 	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
2543c6da674aSChun-Yeow Yeoh 			       sizeof(mgmt->u.action.u.chan_switch);
2544c6da674aSChun-Yeow Yeoh 	u8 *pos;
2545c6da674aSChun-Yeow Yeoh 
2546c6da674aSChun-Yeow Yeoh 	if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
2547c6da674aSChun-Yeow Yeoh 	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
2548c6da674aSChun-Yeow Yeoh 		return -EOPNOTSUPP;
2549c6da674aSChun-Yeow Yeoh 
2550c6da674aSChun-Yeow Yeoh 	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
2551c6da674aSChun-Yeow Yeoh 			    5 + /* channel switch announcement element */
2552c6da674aSChun-Yeow Yeoh 			    3 + /* secondary channel offset element */
2553c6da674aSChun-Yeow Yeoh 			    8); /* mesh channel switch parameters element */
2554c6da674aSChun-Yeow Yeoh 	if (!skb)
2555c6da674aSChun-Yeow Yeoh 		return -ENOMEM;
2556c6da674aSChun-Yeow Yeoh 
2557c6da674aSChun-Yeow Yeoh 	skb_reserve(skb, local->tx_headroom);
2558c6da674aSChun-Yeow Yeoh 	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
2559c6da674aSChun-Yeow Yeoh 	memset(mgmt, 0, hdr_len);
2560c6da674aSChun-Yeow Yeoh 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2561c6da674aSChun-Yeow Yeoh 					  IEEE80211_STYPE_ACTION);
2562c6da674aSChun-Yeow Yeoh 
2563c6da674aSChun-Yeow Yeoh 	eth_broadcast_addr(mgmt->da);
2564c6da674aSChun-Yeow Yeoh 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2565c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2566c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
2567c6da674aSChun-Yeow Yeoh 	} else {
2568c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
2569c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
2570c6da674aSChun-Yeow Yeoh 	}
2571c6da674aSChun-Yeow Yeoh 	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
2572c6da674aSChun-Yeow Yeoh 	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
2573c6da674aSChun-Yeow Yeoh 	pos = skb_put(skb, 5);
2574c6da674aSChun-Yeow Yeoh 	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
2575c6da674aSChun-Yeow Yeoh 	*pos++ = 3;						/* IE length */
2576c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
2577c6da674aSChun-Yeow Yeoh 	freq = csa_settings->chandef.chan->center_freq;
2578c6da674aSChun-Yeow Yeoh 	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
2579c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->count;				/* count */
2580c6da674aSChun-Yeow Yeoh 
2581c6da674aSChun-Yeow Yeoh 	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
2582c6da674aSChun-Yeow Yeoh 		enum nl80211_channel_type ch_type;
2583c6da674aSChun-Yeow Yeoh 
2584c6da674aSChun-Yeow Yeoh 		skb_put(skb, 3);
2585c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
2586c6da674aSChun-Yeow Yeoh 		*pos++ = 1;					/* IE length */
2587c6da674aSChun-Yeow Yeoh 		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
2588c6da674aSChun-Yeow Yeoh 		if (ch_type == NL80211_CHAN_HT40PLUS)
2589c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
2590c6da674aSChun-Yeow Yeoh 		else
2591c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
2592c6da674aSChun-Yeow Yeoh 	}
2593c6da674aSChun-Yeow Yeoh 
2594c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2595c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2596c6da674aSChun-Yeow Yeoh 
2597c6da674aSChun-Yeow Yeoh 		skb_put(skb, 8);
2598c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */
2599c6da674aSChun-Yeow Yeoh 		*pos++ = 6;					/* IE length */
2600c6da674aSChun-Yeow Yeoh 		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */
2601c6da674aSChun-Yeow Yeoh 		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */
2602c6da674aSChun-Yeow Yeoh 		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
2603c6da674aSChun-Yeow Yeoh 		*pos++ |= csa_settings->block_tx ?
2604c6da674aSChun-Yeow Yeoh 			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
2605c6da674aSChun-Yeow Yeoh 		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
2606c6da674aSChun-Yeow Yeoh 		pos += 2;
2607ca91dc97SChun-Yeow Yeoh 		put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */
2608c6da674aSChun-Yeow Yeoh 		pos += 2;
2609c6da674aSChun-Yeow Yeoh 	}
2610c6da674aSChun-Yeow Yeoh 
2611c6da674aSChun-Yeow Yeoh 	ieee80211_tx_skb(sdata, skb);
2612c6da674aSChun-Yeow Yeoh 	return 0;
2613c6da674aSChun-Yeow Yeoh }
26142475b1ccSMax Stepanov 
26152475b1ccSMax Stepanov bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
26162475b1ccSMax Stepanov {
26172475b1ccSMax Stepanov 	return !(cs == NULL || cs->cipher == 0 ||
26182475b1ccSMax Stepanov 		 cs->hdr_len < cs->pn_len + cs->pn_off ||
26192475b1ccSMax Stepanov 		 cs->hdr_len <= cs->key_idx_off ||
26202475b1ccSMax Stepanov 		 cs->key_idx_shift > 7 ||
26212475b1ccSMax Stepanov 		 cs->key_idx_mask == 0);
26222475b1ccSMax Stepanov }
26232475b1ccSMax Stepanov 
26242475b1ccSMax Stepanov bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
26252475b1ccSMax Stepanov {
26262475b1ccSMax Stepanov 	int i;
26272475b1ccSMax Stepanov 
26282475b1ccSMax Stepanov 	/* Ensure we have enough iftype bitmap space for all iftype values */
26292475b1ccSMax Stepanov 	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
26302475b1ccSMax Stepanov 
26312475b1ccSMax Stepanov 	for (i = 0; i < n; i++)
26322475b1ccSMax Stepanov 		if (!ieee80211_cs_valid(&cs[i]))
26332475b1ccSMax Stepanov 			return false;
26342475b1ccSMax Stepanov 
26352475b1ccSMax Stepanov 	return true;
26362475b1ccSMax Stepanov }
26372475b1ccSMax Stepanov 
26382475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *
26392475b1ccSMax Stepanov ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
26402475b1ccSMax Stepanov 		 enum nl80211_iftype iftype)
26412475b1ccSMax Stepanov {
26422475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
26432475b1ccSMax Stepanov 	int n = local->hw.n_cipher_schemes;
26442475b1ccSMax Stepanov 	int i;
26452475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs = NULL;
26462475b1ccSMax Stepanov 
26472475b1ccSMax Stepanov 	for (i = 0; i < n; i++) {
26482475b1ccSMax Stepanov 		if (l[i].cipher == cipher) {
26492475b1ccSMax Stepanov 			cs = &l[i];
26502475b1ccSMax Stepanov 			break;
26512475b1ccSMax Stepanov 		}
26522475b1ccSMax Stepanov 	}
26532475b1ccSMax Stepanov 
26542475b1ccSMax Stepanov 	if (!cs || !(cs->iftype & BIT(iftype)))
26552475b1ccSMax Stepanov 		return NULL;
26562475b1ccSMax Stepanov 
26572475b1ccSMax Stepanov 	return cs;
26582475b1ccSMax Stepanov }
26592475b1ccSMax Stepanov 
26602475b1ccSMax Stepanov int ieee80211_cs_headroom(struct ieee80211_local *local,
26612475b1ccSMax Stepanov 			  struct cfg80211_crypto_settings *crypto,
26622475b1ccSMax Stepanov 			  enum nl80211_iftype iftype)
26632475b1ccSMax Stepanov {
26642475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs;
26652475b1ccSMax Stepanov 	int headroom = IEEE80211_ENCRYPT_HEADROOM;
26662475b1ccSMax Stepanov 	int i;
26672475b1ccSMax Stepanov 
26682475b1ccSMax Stepanov 	for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
26692475b1ccSMax Stepanov 		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
26702475b1ccSMax Stepanov 				      iftype);
26712475b1ccSMax Stepanov 
26722475b1ccSMax Stepanov 		if (cs && headroom < cs->hdr_len)
26732475b1ccSMax Stepanov 			headroom = cs->hdr_len;
26742475b1ccSMax Stepanov 	}
26752475b1ccSMax Stepanov 
26762475b1ccSMax Stepanov 	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
26772475b1ccSMax Stepanov 	if (cs && headroom < cs->hdr_len)
26782475b1ccSMax Stepanov 		headroom = cs->hdr_len;
26792475b1ccSMax Stepanov 
26802475b1ccSMax Stepanov 	return headroom;
26812475b1ccSMax Stepanov }
2682a7022e65SFelix Fietkau 
2683a7022e65SFelix Fietkau static bool
2684a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
2685a7022e65SFelix Fietkau {
2686a7022e65SFelix Fietkau 	s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
2687a7022e65SFelix Fietkau 	int skip;
2688a7022e65SFelix Fietkau 
2689a7022e65SFelix Fietkau 	if (end > 0)
2690a7022e65SFelix Fietkau 		return false;
2691a7022e65SFelix Fietkau 
2692a7022e65SFelix Fietkau 	/* End time is in the past, check for repetitions */
2693a7022e65SFelix Fietkau 	skip = DIV_ROUND_UP(-end, data->desc[i].interval);
2694a7022e65SFelix Fietkau 	if (data->count[i] < 255) {
2695a7022e65SFelix Fietkau 		if (data->count[i] <= skip) {
2696a7022e65SFelix Fietkau 			data->count[i] = 0;
2697a7022e65SFelix Fietkau 			return false;
2698a7022e65SFelix Fietkau 		}
2699a7022e65SFelix Fietkau 
2700a7022e65SFelix Fietkau 		data->count[i] -= skip;
2701a7022e65SFelix Fietkau 	}
2702a7022e65SFelix Fietkau 
2703a7022e65SFelix Fietkau 	data->desc[i].start += skip * data->desc[i].interval;
2704a7022e65SFelix Fietkau 
2705a7022e65SFelix Fietkau 	return true;
2706a7022e65SFelix Fietkau }
2707a7022e65SFelix Fietkau 
2708a7022e65SFelix Fietkau static bool
2709a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
2710a7022e65SFelix Fietkau 			     s32 *offset)
2711a7022e65SFelix Fietkau {
2712a7022e65SFelix Fietkau 	bool ret = false;
2713a7022e65SFelix Fietkau 	int i;
2714a7022e65SFelix Fietkau 
2715a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2716a7022e65SFelix Fietkau 		s32 cur;
2717a7022e65SFelix Fietkau 
2718a7022e65SFelix Fietkau 		if (!data->count[i])
2719a7022e65SFelix Fietkau 			continue;
2720a7022e65SFelix Fietkau 
2721a7022e65SFelix Fietkau 		if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
2722a7022e65SFelix Fietkau 			ret = true;
2723a7022e65SFelix Fietkau 
2724a7022e65SFelix Fietkau 		cur = data->desc[i].start - tsf;
2725a7022e65SFelix Fietkau 		if (cur > *offset)
2726a7022e65SFelix Fietkau 			continue;
2727a7022e65SFelix Fietkau 
2728a7022e65SFelix Fietkau 		cur = data->desc[i].start + data->desc[i].duration - tsf;
2729a7022e65SFelix Fietkau 		if (cur > *offset)
2730a7022e65SFelix Fietkau 			*offset = cur;
2731a7022e65SFelix Fietkau 	}
2732a7022e65SFelix Fietkau 
2733a7022e65SFelix Fietkau 	return ret;
2734a7022e65SFelix Fietkau }
2735a7022e65SFelix Fietkau 
2736a7022e65SFelix Fietkau static u32
2737a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
2738a7022e65SFelix Fietkau {
2739a7022e65SFelix Fietkau 	s32 offset = 0;
2740a7022e65SFelix Fietkau 	int tries = 0;
2741a7022e65SFelix Fietkau 	/*
2742a7022e65SFelix Fietkau 	 * arbitrary limit, used to avoid infinite loops when combined NoA
2743a7022e65SFelix Fietkau 	 * descriptors cover the full time period.
2744a7022e65SFelix Fietkau 	 */
2745a7022e65SFelix Fietkau 	int max_tries = 5;
2746a7022e65SFelix Fietkau 
2747a7022e65SFelix Fietkau 	ieee80211_extend_absent_time(data, tsf, &offset);
2748a7022e65SFelix Fietkau 	do {
2749a7022e65SFelix Fietkau 		if (!ieee80211_extend_absent_time(data, tsf, &offset))
2750a7022e65SFelix Fietkau 			break;
2751a7022e65SFelix Fietkau 
2752a7022e65SFelix Fietkau 		tries++;
2753a7022e65SFelix Fietkau 	} while (tries < max_tries);
2754a7022e65SFelix Fietkau 
2755a7022e65SFelix Fietkau 	return offset;
2756a7022e65SFelix Fietkau }
2757a7022e65SFelix Fietkau 
2758a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
2759a7022e65SFelix Fietkau {
2760a7022e65SFelix Fietkau 	u32 next_offset = BIT(31) - 1;
2761a7022e65SFelix Fietkau 	int i;
2762a7022e65SFelix Fietkau 
2763a7022e65SFelix Fietkau 	data->absent = 0;
2764a7022e65SFelix Fietkau 	data->has_next_tsf = false;
2765a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2766a7022e65SFelix Fietkau 		s32 start;
2767a7022e65SFelix Fietkau 
2768a7022e65SFelix Fietkau 		if (!data->count[i])
2769a7022e65SFelix Fietkau 			continue;
2770a7022e65SFelix Fietkau 
2771a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2772a7022e65SFelix Fietkau 		start = data->desc[i].start - tsf;
2773a7022e65SFelix Fietkau 		if (start <= 0)
2774a7022e65SFelix Fietkau 			data->absent |= BIT(i);
2775a7022e65SFelix Fietkau 
2776a7022e65SFelix Fietkau 		if (next_offset > start)
2777a7022e65SFelix Fietkau 			next_offset = start;
2778a7022e65SFelix Fietkau 
2779a7022e65SFelix Fietkau 		data->has_next_tsf = true;
2780a7022e65SFelix Fietkau 	}
2781a7022e65SFelix Fietkau 
2782a7022e65SFelix Fietkau 	if (data->absent)
2783a7022e65SFelix Fietkau 		next_offset = ieee80211_get_noa_absent_time(data, tsf);
2784a7022e65SFelix Fietkau 
2785a7022e65SFelix Fietkau 	data->next_tsf = tsf + next_offset;
2786a7022e65SFelix Fietkau }
2787a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa);
2788a7022e65SFelix Fietkau 
2789a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
2790a7022e65SFelix Fietkau 			    struct ieee80211_noa_data *data, u32 tsf)
2791a7022e65SFelix Fietkau {
2792a7022e65SFelix Fietkau 	int ret = 0;
2793a7022e65SFelix Fietkau 	int i;
2794a7022e65SFelix Fietkau 
2795a7022e65SFelix Fietkau 	memset(data, 0, sizeof(*data));
2796a7022e65SFelix Fietkau 
2797a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2798a7022e65SFelix Fietkau 		const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
2799a7022e65SFelix Fietkau 
2800a7022e65SFelix Fietkau 		if (!desc->count || !desc->duration)
2801a7022e65SFelix Fietkau 			continue;
2802a7022e65SFelix Fietkau 
2803a7022e65SFelix Fietkau 		data->count[i] = desc->count;
2804a7022e65SFelix Fietkau 		data->desc[i].start = le32_to_cpu(desc->start_time);
2805a7022e65SFelix Fietkau 		data->desc[i].duration = le32_to_cpu(desc->duration);
2806a7022e65SFelix Fietkau 		data->desc[i].interval = le32_to_cpu(desc->interval);
2807a7022e65SFelix Fietkau 
2808a7022e65SFelix Fietkau 		if (data->count[i] > 1 &&
2809a7022e65SFelix Fietkau 		    data->desc[i].interval < data->desc[i].duration)
2810a7022e65SFelix Fietkau 			continue;
2811a7022e65SFelix Fietkau 
2812a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2813a7022e65SFelix Fietkau 		ret++;
2814a7022e65SFelix Fietkau 	}
2815a7022e65SFelix Fietkau 
2816a7022e65SFelix Fietkau 	if (ret)
2817a7022e65SFelix Fietkau 		ieee80211_update_p2p_noa(data, tsf);
2818a7022e65SFelix Fietkau 
2819a7022e65SFelix Fietkau 	return ret;
2820a7022e65SFelix Fietkau }
2821a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
2822057d5f4bSThomas Pedersen 
2823057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local,
2824057d5f4bSThomas Pedersen 			   struct ieee80211_sub_if_data *sdata)
2825057d5f4bSThomas Pedersen {
2826057d5f4bSThomas Pedersen 	u64 tsf = drv_get_tsf(local, sdata);
2827057d5f4bSThomas Pedersen 	u64 dtim_count = 0;
2828057d5f4bSThomas Pedersen 	u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
2829057d5f4bSThomas Pedersen 	u8 dtim_period = sdata->vif.bss_conf.dtim_period;
2830057d5f4bSThomas Pedersen 	struct ps_data *ps;
2831057d5f4bSThomas Pedersen 	u8 bcns_from_dtim;
2832057d5f4bSThomas Pedersen 
2833057d5f4bSThomas Pedersen 	if (tsf == -1ULL || !beacon_int || !dtim_period)
2834057d5f4bSThomas Pedersen 		return;
2835057d5f4bSThomas Pedersen 
2836057d5f4bSThomas Pedersen 	if (sdata->vif.type == NL80211_IFTYPE_AP ||
2837057d5f4bSThomas Pedersen 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
2838057d5f4bSThomas Pedersen 		if (!sdata->bss)
2839057d5f4bSThomas Pedersen 			return;
2840057d5f4bSThomas Pedersen 
2841057d5f4bSThomas Pedersen 		ps = &sdata->bss->ps;
2842057d5f4bSThomas Pedersen 	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
2843057d5f4bSThomas Pedersen 		ps = &sdata->u.mesh.ps;
2844057d5f4bSThomas Pedersen 	} else {
2845057d5f4bSThomas Pedersen 		return;
2846057d5f4bSThomas Pedersen 	}
2847057d5f4bSThomas Pedersen 
2848057d5f4bSThomas Pedersen 	/*
2849057d5f4bSThomas Pedersen 	 * actually finds last dtim_count, mac80211 will update in
2850057d5f4bSThomas Pedersen 	 * __beacon_add_tim().
2851057d5f4bSThomas Pedersen 	 * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
2852057d5f4bSThomas Pedersen 	 */
2853057d5f4bSThomas Pedersen 	do_div(tsf, beacon_int);
2854057d5f4bSThomas Pedersen 	bcns_from_dtim = do_div(tsf, dtim_period);
2855057d5f4bSThomas Pedersen 	/* just had a DTIM */
2856057d5f4bSThomas Pedersen 	if (!bcns_from_dtim)
2857057d5f4bSThomas Pedersen 		dtim_count = 0;
2858057d5f4bSThomas Pedersen 	else
2859057d5f4bSThomas Pedersen 		dtim_count = dtim_period - bcns_from_dtim;
2860057d5f4bSThomas Pedersen 
2861057d5f4bSThomas Pedersen 	ps->dtim_count = dtim_count;
2862057d5f4bSThomas Pedersen }
286373de86a3SLuciano Coelho 
286473de86a3SLuciano Coelho int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
286573de86a3SLuciano Coelho 				 const struct cfg80211_chan_def *chandef,
286673de86a3SLuciano Coelho 				 enum ieee80211_chanctx_mode chanmode,
286773de86a3SLuciano Coelho 				 u8 radar_detect)
286873de86a3SLuciano Coelho {
286973de86a3SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
287073de86a3SLuciano Coelho 	struct ieee80211_sub_if_data *sdata_iter;
287173de86a3SLuciano Coelho 	enum nl80211_iftype iftype = sdata->wdev.iftype;
287273de86a3SLuciano Coelho 	int num[NUM_NL80211_IFTYPES];
287373de86a3SLuciano Coelho 	struct ieee80211_chanctx *ctx;
2874b6a55015SLuciano Coelho 	int num_different_channels = 0;
287573de86a3SLuciano Coelho 	int total = 1;
287673de86a3SLuciano Coelho 
287773de86a3SLuciano Coelho 	lockdep_assert_held(&local->chanctx_mtx);
287873de86a3SLuciano Coelho 
287973de86a3SLuciano Coelho 	if (WARN_ON(hweight32(radar_detect) > 1))
288073de86a3SLuciano Coelho 		return -EINVAL;
288173de86a3SLuciano Coelho 
2882b6a55015SLuciano Coelho 	if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
2883b6a55015SLuciano Coelho 		    !chandef->chan))
288473de86a3SLuciano Coelho 		return -EINVAL;
288573de86a3SLuciano Coelho 
2886b6a55015SLuciano Coelho 	if (chandef)
2887b6a55015SLuciano Coelho 		num_different_channels = 1;
2888b6a55015SLuciano Coelho 
288973de86a3SLuciano Coelho 	if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
289073de86a3SLuciano Coelho 		return -EINVAL;
289173de86a3SLuciano Coelho 
289273de86a3SLuciano Coelho 	/* Always allow software iftypes */
289373de86a3SLuciano Coelho 	if (local->hw.wiphy->software_iftypes & BIT(iftype)) {
289473de86a3SLuciano Coelho 		if (radar_detect)
289573de86a3SLuciano Coelho 			return -EINVAL;
289673de86a3SLuciano Coelho 		return 0;
289773de86a3SLuciano Coelho 	}
289873de86a3SLuciano Coelho 
289973de86a3SLuciano Coelho 	memset(num, 0, sizeof(num));
290073de86a3SLuciano Coelho 
290173de86a3SLuciano Coelho 	if (iftype != NL80211_IFTYPE_UNSPECIFIED)
290273de86a3SLuciano Coelho 		num[iftype] = 1;
290373de86a3SLuciano Coelho 
290473de86a3SLuciano Coelho 	list_for_each_entry(ctx, &local->chanctx_list, list) {
290573de86a3SLuciano Coelho 		if (ctx->conf.radar_enabled)
290673de86a3SLuciano Coelho 			radar_detect |= BIT(ctx->conf.def.width);
290773de86a3SLuciano Coelho 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
290873de86a3SLuciano Coelho 			num_different_channels++;
290973de86a3SLuciano Coelho 			continue;
291073de86a3SLuciano Coelho 		}
2911b6a55015SLuciano Coelho 		if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
291273de86a3SLuciano Coelho 		    cfg80211_chandef_compatible(chandef,
291373de86a3SLuciano Coelho 						&ctx->conf.def))
291473de86a3SLuciano Coelho 			continue;
291573de86a3SLuciano Coelho 		num_different_channels++;
291673de86a3SLuciano Coelho 	}
291773de86a3SLuciano Coelho 
291873de86a3SLuciano Coelho 	list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
291973de86a3SLuciano Coelho 		struct wireless_dev *wdev_iter;
292073de86a3SLuciano Coelho 
292173de86a3SLuciano Coelho 		wdev_iter = &sdata_iter->wdev;
292273de86a3SLuciano Coelho 
292373de86a3SLuciano Coelho 		if (sdata_iter == sdata ||
292473de86a3SLuciano Coelho 		    rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
292573de86a3SLuciano Coelho 		    local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
292673de86a3SLuciano Coelho 			continue;
292773de86a3SLuciano Coelho 
292873de86a3SLuciano Coelho 		num[wdev_iter->iftype]++;
292973de86a3SLuciano Coelho 		total++;
293073de86a3SLuciano Coelho 	}
293173de86a3SLuciano Coelho 
293273de86a3SLuciano Coelho 	if (total == 1 && !radar_detect)
293373de86a3SLuciano Coelho 		return 0;
293473de86a3SLuciano Coelho 
293573de86a3SLuciano Coelho 	return cfg80211_check_combinations(local->hw.wiphy,
293673de86a3SLuciano Coelho 					   num_different_channels,
293773de86a3SLuciano Coelho 					   radar_detect, num);
293873de86a3SLuciano Coelho }
29396fa001bcSMichal Kazior 
29406fa001bcSMichal Kazior static void
29416fa001bcSMichal Kazior ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
29426fa001bcSMichal Kazior 			 void *data)
29436fa001bcSMichal Kazior {
29446fa001bcSMichal Kazior 	u32 *max_num_different_channels = data;
29456fa001bcSMichal Kazior 
29466fa001bcSMichal Kazior 	*max_num_different_channels = max(*max_num_different_channels,
29476fa001bcSMichal Kazior 					  c->num_different_channels);
29486fa001bcSMichal Kazior }
29496fa001bcSMichal Kazior 
29506fa001bcSMichal Kazior int ieee80211_max_num_channels(struct ieee80211_local *local)
29516fa001bcSMichal Kazior {
29526fa001bcSMichal Kazior 	struct ieee80211_sub_if_data *sdata;
29536fa001bcSMichal Kazior 	int num[NUM_NL80211_IFTYPES] = {};
29546fa001bcSMichal Kazior 	struct ieee80211_chanctx *ctx;
29556fa001bcSMichal Kazior 	int num_different_channels = 0;
29566fa001bcSMichal Kazior 	u8 radar_detect = 0;
29576fa001bcSMichal Kazior 	u32 max_num_different_channels = 1;
29586fa001bcSMichal Kazior 	int err;
29596fa001bcSMichal Kazior 
29606fa001bcSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
29616fa001bcSMichal Kazior 
29626fa001bcSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
29636fa001bcSMichal Kazior 		num_different_channels++;
29646fa001bcSMichal Kazior 
29656fa001bcSMichal Kazior 		if (ctx->conf.radar_enabled)
29666fa001bcSMichal Kazior 			radar_detect |= BIT(ctx->conf.def.width);
29676fa001bcSMichal Kazior 	}
29686fa001bcSMichal Kazior 
29696fa001bcSMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list)
29706fa001bcSMichal Kazior 		num[sdata->wdev.iftype]++;
29716fa001bcSMichal Kazior 
29726fa001bcSMichal Kazior 	err = cfg80211_iter_combinations(local->hw.wiphy,
29736fa001bcSMichal Kazior 					 num_different_channels, radar_detect,
29746fa001bcSMichal Kazior 					 num, ieee80211_iter_max_chans,
29756fa001bcSMichal Kazior 					 &max_num_different_channels);
29766fa001bcSMichal Kazior 	if (err < 0)
29776fa001bcSMichal Kazior 		return err;
29786fa001bcSMichal Kazior 
29796fa001bcSMichal Kazior 	return max_num_different_channels;
29806fa001bcSMichal Kazior }
2981