xref: /openbmc/linux/net/mac80211/util.c (revision 057d5f4b)
1c2d1560aSJohannes Berg /*
2c2d1560aSJohannes Berg  * Copyright 2002-2005, Instant802 Networks, Inc.
3c2d1560aSJohannes Berg  * Copyright 2005-2006, Devicescape Software, Inc.
4c2d1560aSJohannes Berg  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
5c2d1560aSJohannes Berg  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
6c2d1560aSJohannes Berg  *
7c2d1560aSJohannes Berg  * This program is free software; you can redistribute it and/or modify
8c2d1560aSJohannes Berg  * it under the terms of the GNU General Public License version 2 as
9c2d1560aSJohannes Berg  * published by the Free Software Foundation.
10c2d1560aSJohannes Berg  *
11c2d1560aSJohannes Berg  * utilities for mac80211
12c2d1560aSJohannes Berg  */
13c2d1560aSJohannes Berg 
14c2d1560aSJohannes Berg #include <net/mac80211.h>
15c2d1560aSJohannes Berg #include <linux/netdevice.h>
16bc3b2d7fSPaul Gortmaker #include <linux/export.h>
17c2d1560aSJohannes Berg #include <linux/types.h>
18c2d1560aSJohannes Berg #include <linux/slab.h>
19c2d1560aSJohannes Berg #include <linux/skbuff.h>
20c2d1560aSJohannes Berg #include <linux/etherdevice.h>
21c2d1560aSJohannes Berg #include <linux/if_arp.h>
22c2d1560aSJohannes Berg #include <linux/bitmap.h>
23dd76986bSJohannes Berg #include <linux/crc32.h>
24881d966bSEric W. Biederman #include <net/net_namespace.h>
25c2d1560aSJohannes Berg #include <net/cfg80211.h>
26dabeb344SJohannes Berg #include <net/rtnetlink.h>
27c2d1560aSJohannes Berg 
28c2d1560aSJohannes Berg #include "ieee80211_i.h"
2924487981SJohannes Berg #include "driver-ops.h"
302c8dccc7SJohannes Berg #include "rate.h"
31ee385855SLuis Carlos Cobo #include "mesh.h"
32c2d1560aSJohannes Berg #include "wme.h"
33f2753ddbSJohannes Berg #include "led.h"
34fffd0934SJohannes Berg #include "wep.h"
35c2d1560aSJohannes Berg 
36c2d1560aSJohannes Berg /* privid for wiphys to determine whether they belong to us or not */
37c2d1560aSJohannes Berg void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
38c2d1560aSJohannes Berg 
399a95371aSLuis R. Rodriguez struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
409a95371aSLuis R. Rodriguez {
419a95371aSLuis R. Rodriguez 	struct ieee80211_local *local;
429a95371aSLuis R. Rodriguez 	BUG_ON(!wiphy);
439a95371aSLuis R. Rodriguez 
449a95371aSLuis R. Rodriguez 	local = wiphy_priv(wiphy);
459a95371aSLuis R. Rodriguez 	return &local->hw;
469a95371aSLuis R. Rodriguez }
479a95371aSLuis R. Rodriguez EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
48c2d1560aSJohannes Berg 
4971364716SRon Rindjunsky u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
5005c914feSJohannes Berg 			enum nl80211_iftype type)
51c2d1560aSJohannes Berg {
52a494bb1cSHarvey Harrison 	__le16 fc = hdr->frame_control;
53c2d1560aSJohannes Berg 
5498f0b0a3SRon Rindjunsky 	 /* drop ACK/CTS frames and incorrect hdr len (ctrl) */
5598f0b0a3SRon Rindjunsky 	if (len < 16)
56c2d1560aSJohannes Berg 		return NULL;
57c2d1560aSJohannes Berg 
58a494bb1cSHarvey Harrison 	if (ieee80211_is_data(fc)) {
5998f0b0a3SRon Rindjunsky 		if (len < 24) /* drop incorrect hdr len (data) */
6098f0b0a3SRon Rindjunsky 			return NULL;
61a494bb1cSHarvey Harrison 
62a494bb1cSHarvey Harrison 		if (ieee80211_has_a4(fc))
63c2d1560aSJohannes Berg 			return NULL;
64a494bb1cSHarvey Harrison 		if (ieee80211_has_tods(fc))
65a494bb1cSHarvey Harrison 			return hdr->addr1;
66a494bb1cSHarvey Harrison 		if (ieee80211_has_fromds(fc))
67c2d1560aSJohannes Berg 			return hdr->addr2;
68a494bb1cSHarvey Harrison 
69c2d1560aSJohannes Berg 		return hdr->addr3;
70c2d1560aSJohannes Berg 	}
71a494bb1cSHarvey Harrison 
72a494bb1cSHarvey Harrison 	if (ieee80211_is_mgmt(fc)) {
7398f0b0a3SRon Rindjunsky 		if (len < 24) /* drop incorrect hdr len (mgmt) */
7498f0b0a3SRon Rindjunsky 			return NULL;
75c2d1560aSJohannes Berg 		return hdr->addr3;
76a494bb1cSHarvey Harrison 	}
77a494bb1cSHarvey Harrison 
78a494bb1cSHarvey Harrison 	if (ieee80211_is_ctl(fc)) {
79a494bb1cSHarvey Harrison 		if (ieee80211_is_pspoll(fc))
80c2d1560aSJohannes Berg 			return hdr->addr1;
81a494bb1cSHarvey Harrison 
82a494bb1cSHarvey Harrison 		if (ieee80211_is_back_req(fc)) {
8371364716SRon Rindjunsky 			switch (type) {
8405c914feSJohannes Berg 			case NL80211_IFTYPE_STATION:
8571364716SRon Rindjunsky 				return hdr->addr2;
8605c914feSJohannes Berg 			case NL80211_IFTYPE_AP:
8705c914feSJohannes Berg 			case NL80211_IFTYPE_AP_VLAN:
8871364716SRon Rindjunsky 				return hdr->addr1;
8971364716SRon Rindjunsky 			default:
90a494bb1cSHarvey Harrison 				break; /* fall through to the return */
9171364716SRon Rindjunsky 			}
9271364716SRon Rindjunsky 		}
93c2d1560aSJohannes Berg 	}
94c2d1560aSJohannes Berg 
95c2d1560aSJohannes Berg 	return NULL;
96c2d1560aSJohannes Berg }
97c2d1560aSJohannes Berg 
985cf121c3SJohannes Berg void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
99c2d1560aSJohannes Berg {
100252b86c4SJohannes Berg 	struct sk_buff *skb;
1012de8e0d9SJohannes Berg 	struct ieee80211_hdr *hdr;
102c2d1560aSJohannes Berg 
103252b86c4SJohannes Berg 	skb_queue_walk(&tx->skbs, skb) {
1042de8e0d9SJohannes Berg 		hdr = (struct ieee80211_hdr *) skb->data;
105c2d1560aSJohannes Berg 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
106252b86c4SJohannes Berg 	}
107c2d1560aSJohannes Berg }
108c2d1560aSJohannes Berg 
1094ee73f33SMichal Kazior int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
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,
320ce7c9111SKalle Valo 				   enum queue_stop_reason reason)
321c2d1560aSJohannes Berg {
322c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
323c2d1560aSJohannes Berg 
324b5878a2dSJohannes Berg 	trace_wake_queue(local, queue, reason);
325b5878a2dSJohannes Berg 
326e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
32796f5e66eSJohannes Berg 		return;
32896f5e66eSJohannes Berg 
329ada15125SJohannes Berg 	if (!test_bit(reason, &local->queue_stop_reasons[queue]))
330ada15125SJohannes Berg 		return;
331ada15125SJohannes Berg 
332ce7c9111SKalle Valo 	__clear_bit(reason, &local->queue_stop_reasons[queue]);
333ce7c9111SKalle Valo 
334ce7c9111SKalle Valo 	if (local->queue_stop_reasons[queue] != 0)
335ce7c9111SKalle Valo 		/* someone still has this queue stopped */
336ce7c9111SKalle Valo 		return;
337ce7c9111SKalle Valo 
3387236fe29SJohannes Berg 	if (skb_queue_empty(&local->pending[queue])) {
339cf0277e7SJohannes Berg 		rcu_read_lock();
3403a25a8c8SJohannes Berg 		ieee80211_propagate_queue_wake(local, queue);
341cf0277e7SJohannes Berg 		rcu_read_unlock();
3427236fe29SJohannes Berg 	} else
3437236fe29SJohannes Berg 		tasklet_schedule(&local->tx_pending_tasklet);
344c2d1560aSJohannes Berg }
345ce7c9111SKalle Valo 
34696f5e66eSJohannes Berg void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
347ce7c9111SKalle Valo 				    enum queue_stop_reason reason)
348ce7c9111SKalle Valo {
349ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
350ce7c9111SKalle Valo 	unsigned long flags;
351ce7c9111SKalle Valo 
352ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
353ce7c9111SKalle Valo 	__ieee80211_wake_queue(hw, queue, reason);
354ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
355ce7c9111SKalle Valo }
356ce7c9111SKalle Valo 
357ce7c9111SKalle Valo void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
358ce7c9111SKalle Valo {
359ce7c9111SKalle Valo 	ieee80211_wake_queue_by_reason(hw, queue,
360ce7c9111SKalle Valo 				       IEEE80211_QUEUE_STOP_REASON_DRIVER);
361ce7c9111SKalle Valo }
362c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queue);
363c2d1560aSJohannes Berg 
364ce7c9111SKalle Valo static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
365ce7c9111SKalle Valo 				   enum queue_stop_reason reason)
366c2d1560aSJohannes Berg {
367c2d1560aSJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
368cf0277e7SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
369a6f38ac3SJohannes Berg 	int n_acs = IEEE80211_NUM_ACS;
370c2d1560aSJohannes Berg 
371b5878a2dSJohannes Berg 	trace_stop_queue(local, queue, reason);
372b5878a2dSJohannes Berg 
373e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
37496f5e66eSJohannes Berg 		return;
37596f5e66eSJohannes Berg 
376ada15125SJohannes Berg 	if (test_bit(reason, &local->queue_stop_reasons[queue]))
377ada15125SJohannes Berg 		return;
378ada15125SJohannes Berg 
3792a577d98SJohannes Berg 	__set_bit(reason, &local->queue_stop_reasons[queue]);
380cf0277e7SJohannes Berg 
381a6f38ac3SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
382a6f38ac3SJohannes Berg 		n_acs = 1;
383a6f38ac3SJohannes Berg 
384cf0277e7SJohannes Berg 	rcu_read_lock();
3853a25a8c8SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
3863a25a8c8SJohannes Berg 		int ac;
3873a25a8c8SJohannes Berg 
388f142c6b9SJohannes Berg 		if (!sdata->dev)
389f142c6b9SJohannes Berg 			continue;
390f142c6b9SJohannes Berg 
391a6f38ac3SJohannes Berg 		for (ac = 0; ac < n_acs; ac++) {
3923a25a8c8SJohannes Berg 			if (sdata->vif.hw_queue[ac] == queue ||
3933a25a8c8SJohannes Berg 			    sdata->vif.cab_queue == queue)
3943a25a8c8SJohannes Berg 				netif_stop_subqueue(sdata->dev, ac);
3953a25a8c8SJohannes Berg 		}
3963a25a8c8SJohannes Berg 	}
397cf0277e7SJohannes Berg 	rcu_read_unlock();
398c2d1560aSJohannes Berg }
399ce7c9111SKalle Valo 
40096f5e66eSJohannes Berg void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
401ce7c9111SKalle Valo 				    enum queue_stop_reason reason)
402ce7c9111SKalle Valo {
403ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
404ce7c9111SKalle Valo 	unsigned long flags;
405ce7c9111SKalle Valo 
406ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
407ce7c9111SKalle Valo 	__ieee80211_stop_queue(hw, queue, reason);
408ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
409ce7c9111SKalle Valo }
410ce7c9111SKalle Valo 
411ce7c9111SKalle Valo void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
412ce7c9111SKalle Valo {
413ce7c9111SKalle Valo 	ieee80211_stop_queue_by_reason(hw, queue,
414ce7c9111SKalle Valo 				       IEEE80211_QUEUE_STOP_REASON_DRIVER);
415ce7c9111SKalle Valo }
416c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queue);
417c2d1560aSJohannes Berg 
4188f77f384SJohannes Berg void ieee80211_add_pending_skb(struct ieee80211_local *local,
4198f77f384SJohannes Berg 			       struct sk_buff *skb)
4208f77f384SJohannes Berg {
4218f77f384SJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
4228f77f384SJohannes Berg 	unsigned long flags;
423a7bc376cSJohannes Berg 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
4243a25a8c8SJohannes Berg 	int queue = info->hw_queue;
425a7bc376cSJohannes Berg 
426a7bc376cSJohannes Berg 	if (WARN_ON(!info->control.vif)) {
427d4fa14cdSFelix Fietkau 		ieee80211_free_txskb(&local->hw, skb);
428a7bc376cSJohannes Berg 		return;
429a7bc376cSJohannes Berg 	}
4308f77f384SJohannes Berg 
4318f77f384SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
4328f77f384SJohannes Berg 	__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
4333b8d81e0SJohannes Berg 	__skb_queue_tail(&local->pending[queue], skb);
4348f77f384SJohannes Berg 	__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
4358f77f384SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
4368f77f384SJohannes Berg }
4378f77f384SJohannes Berg 
438b0b97a8aSJohannes Berg void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
43950a9432dSJohannes Berg 				   struct sk_buff_head *skbs,
44050a9432dSJohannes Berg 				   void (*fn)(void *data), void *data)
4418f77f384SJohannes Berg {
4428f77f384SJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
4438f77f384SJohannes Berg 	struct sk_buff *skb;
4448f77f384SJohannes Berg 	unsigned long flags;
445b0b97a8aSJohannes Berg 	int queue, i;
4468f77f384SJohannes Berg 
4478f77f384SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
4488f77f384SJohannes Berg 	while ((skb = skb_dequeue(skbs))) {
449a7bc376cSJohannes Berg 		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
450a7bc376cSJohannes Berg 
451a7bc376cSJohannes Berg 		if (WARN_ON(!info->control.vif)) {
452d4fa14cdSFelix Fietkau 			ieee80211_free_txskb(&local->hw, skb);
453a7bc376cSJohannes Berg 			continue;
454a7bc376cSJohannes Berg 		}
455a7bc376cSJohannes Berg 
4563a25a8c8SJohannes Berg 		queue = info->hw_queue;
4574644ae89SJohannes Berg 
4584644ae89SJohannes Berg 		__ieee80211_stop_queue(hw, queue,
4594644ae89SJohannes Berg 				IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
4604644ae89SJohannes Berg 
4613b8d81e0SJohannes Berg 		__skb_queue_tail(&local->pending[queue], skb);
4628f77f384SJohannes Berg 	}
4638f77f384SJohannes Berg 
46450a9432dSJohannes Berg 	if (fn)
46550a9432dSJohannes Berg 		fn(data);
46650a9432dSJohannes Berg 
4673b8d81e0SJohannes Berg 	for (i = 0; i < hw->queues; i++)
4688f77f384SJohannes Berg 		__ieee80211_wake_queue(hw, i,
4698f77f384SJohannes Berg 			IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
4708f77f384SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
4718f77f384SJohannes Berg }
4728f77f384SJohannes Berg 
473ce7c9111SKalle Valo void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
474445ea4e8SJohannes Berg 				     unsigned long queues,
475ce7c9111SKalle Valo 				     enum queue_stop_reason reason)
476c2d1560aSJohannes Berg {
477ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
478ce7c9111SKalle Valo 	unsigned long flags;
479c2d1560aSJohannes Berg 	int i;
480c2d1560aSJohannes Berg 
481ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
482ce7c9111SKalle Valo 
483445ea4e8SJohannes Berg 	for_each_set_bit(i, &queues, hw->queues)
484ce7c9111SKalle Valo 		__ieee80211_stop_queue(hw, i, reason);
485ce7c9111SKalle Valo 
486ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
487ce7c9111SKalle Valo }
488ce7c9111SKalle Valo 
489ce7c9111SKalle Valo void ieee80211_stop_queues(struct ieee80211_hw *hw)
490ce7c9111SKalle Valo {
491445ea4e8SJohannes Berg 	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
492ce7c9111SKalle Valo 					IEEE80211_QUEUE_STOP_REASON_DRIVER);
493c2d1560aSJohannes Berg }
494c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_stop_queues);
495c2d1560aSJohannes Berg 
49692ab8535STomas Winkler int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
49792ab8535STomas Winkler {
49892ab8535STomas Winkler 	struct ieee80211_local *local = hw_to_local(hw);
4993b8d81e0SJohannes Berg 	unsigned long flags;
5003b8d81e0SJohannes Berg 	int ret;
50196f5e66eSJohannes Berg 
502e4e72fb4SJohannes Berg 	if (WARN_ON(queue >= hw->queues))
50396f5e66eSJohannes Berg 		return true;
50496f5e66eSJohannes Berg 
5053b8d81e0SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
5062419ea14SThomas Pedersen 	ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER,
5072419ea14SThomas Pedersen 		       &local->queue_stop_reasons[queue]);
5083b8d81e0SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
5093b8d81e0SJohannes Berg 	return ret;
51092ab8535STomas Winkler }
51192ab8535STomas Winkler EXPORT_SYMBOL(ieee80211_queue_stopped);
51292ab8535STomas Winkler 
513ce7c9111SKalle Valo void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
514445ea4e8SJohannes Berg 				     unsigned long queues,
515ce7c9111SKalle Valo 				     enum queue_stop_reason reason)
516c2d1560aSJohannes Berg {
517ce7c9111SKalle Valo 	struct ieee80211_local *local = hw_to_local(hw);
518ce7c9111SKalle Valo 	unsigned long flags;
519c2d1560aSJohannes Berg 	int i;
520c2d1560aSJohannes Berg 
521ce7c9111SKalle Valo 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
522ce7c9111SKalle Valo 
523445ea4e8SJohannes Berg 	for_each_set_bit(i, &queues, hw->queues)
524ce7c9111SKalle Valo 		__ieee80211_wake_queue(hw, i, reason);
525ce7c9111SKalle Valo 
526ce7c9111SKalle Valo 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
527ce7c9111SKalle Valo }
528ce7c9111SKalle Valo 
529ce7c9111SKalle Valo void ieee80211_wake_queues(struct ieee80211_hw *hw)
530ce7c9111SKalle Valo {
531445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
532445ea4e8SJohannes Berg 					IEEE80211_QUEUE_STOP_REASON_DRIVER);
533c2d1560aSJohannes Berg }
534c2d1560aSJohannes Berg EXPORT_SYMBOL(ieee80211_wake_queues);
535dabeb344SJohannes Berg 
53639ecc01dSJohannes Berg void ieee80211_flush_queues(struct ieee80211_local *local,
53739ecc01dSJohannes Berg 			    struct ieee80211_sub_if_data *sdata)
53839ecc01dSJohannes Berg {
53939ecc01dSJohannes Berg 	u32 queues;
54039ecc01dSJohannes Berg 
54139ecc01dSJohannes Berg 	if (!local->ops->flush)
54239ecc01dSJohannes Berg 		return;
54339ecc01dSJohannes Berg 
54439ecc01dSJohannes Berg 	if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
54539ecc01dSJohannes Berg 		int ac;
54639ecc01dSJohannes Berg 
54739ecc01dSJohannes Berg 		queues = 0;
54839ecc01dSJohannes Berg 
54939ecc01dSJohannes Berg 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
55039ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.hw_queue[ac]);
55139ecc01dSJohannes Berg 		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE)
55239ecc01dSJohannes Berg 			queues |= BIT(sdata->vif.cab_queue);
55339ecc01dSJohannes Berg 	} else {
55439ecc01dSJohannes Berg 		/* all queues */
55539ecc01dSJohannes Berg 		queues = BIT(local->hw.queues) - 1;
55639ecc01dSJohannes Berg 	}
55739ecc01dSJohannes Berg 
558445ea4e8SJohannes Berg 	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
559445ea4e8SJohannes Berg 					IEEE80211_QUEUE_STOP_REASON_FLUSH);
560445ea4e8SJohannes Berg 
56139ecc01dSJohannes Berg 	drv_flush(local, queues, false);
562445ea4e8SJohannes Berg 
563445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
564445ea4e8SJohannes Berg 					IEEE80211_QUEUE_STOP_REASON_FLUSH);
56539ecc01dSJohannes Berg }
56639ecc01dSJohannes Berg 
567c7c71066SJohannes Berg static void __iterate_active_interfaces(struct ieee80211_local *local,
568c7c71066SJohannes Berg 					u32 iter_flags,
569dabeb344SJohannes Berg 					void (*iterator)(void *data, u8 *mac,
57032bfd35dSJohannes Berg 						struct ieee80211_vif *vif),
571dabeb344SJohannes Berg 					void *data)
572dabeb344SJohannes Berg {
573dabeb344SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
574dabeb344SJohannes Berg 
575e38bad47SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
57651fb61e7SJohannes Berg 		switch (sdata->vif.type) {
57705c914feSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
57831eba5bcSFelix Fietkau 			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
57931eba5bcSFelix Fietkau 				continue;
58031eba5bcSFelix Fietkau 			break;
58105c914feSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
582dabeb344SJohannes Berg 			continue;
5832ca27bcfSJohannes Berg 		default:
584dabeb344SJohannes Berg 			break;
585dabeb344SJohannes Berg 		}
5868b2c9824SJohannes Berg 		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
5878b2c9824SJohannes Berg 		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
5888b2c9824SJohannes Berg 			continue;
5899607e6b6SJohannes Berg 		if (ieee80211_sdata_running(sdata))
59047846c9bSJohannes Berg 			iterator(data, sdata->vif.addr,
59132bfd35dSJohannes Berg 				 &sdata->vif);
592dabeb344SJohannes Berg 	}
593e38bad47SJohannes Berg 
594c7c71066SJohannes Berg 	sdata = rcu_dereference_check(local->monitor_sdata,
595c7c71066SJohannes Berg 				      lockdep_is_held(&local->iflist_mtx) ||
596c7c71066SJohannes Berg 				      lockdep_rtnl_is_held());
5978b2c9824SJohannes Berg 	if (sdata &&
5988b2c9824SJohannes Berg 	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
5998b2c9824SJohannes Berg 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
600685fb72bSJohannes Berg 		iterator(data, sdata->vif.addr, &sdata->vif);
601c7c71066SJohannes Berg }
602685fb72bSJohannes Berg 
603c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces(
604c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
605c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
606c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
607c7c71066SJohannes Berg 	void *data)
608c7c71066SJohannes Berg {
609c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
610c7c71066SJohannes Berg 
611c7c71066SJohannes Berg 	mutex_lock(&local->iflist_mtx);
612c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
613c7c71066SJohannes Berg 	mutex_unlock(&local->iflist_mtx);
614c7c71066SJohannes Berg }
615c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
616c7c71066SJohannes Berg 
617c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_atomic(
618c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
619c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
620c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
621c7c71066SJohannes Berg 	void *data)
622c7c71066SJohannes Berg {
623c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
624c7c71066SJohannes Berg 
625c7c71066SJohannes Berg 	rcu_read_lock();
626c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
627e38bad47SJohannes Berg 	rcu_read_unlock();
628dabeb344SJohannes Berg }
6292f561febSIvo van Doorn EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
63037ffc8daSJohannes Berg 
631c7c71066SJohannes Berg void ieee80211_iterate_active_interfaces_rtnl(
632c7c71066SJohannes Berg 	struct ieee80211_hw *hw, u32 iter_flags,
633c7c71066SJohannes Berg 	void (*iterator)(void *data, u8 *mac,
634c7c71066SJohannes Berg 			 struct ieee80211_vif *vif),
635c7c71066SJohannes Berg 	void *data)
636c7c71066SJohannes Berg {
637c7c71066SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
638c7c71066SJohannes Berg 
639c7c71066SJohannes Berg 	ASSERT_RTNL();
640c7c71066SJohannes Berg 
641c7c71066SJohannes Berg 	__iterate_active_interfaces(local, iter_flags, iterator, data);
642c7c71066SJohannes Berg }
643c7c71066SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
644c7c71066SJohannes Berg 
645ad7e718cSJohannes Berg struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
646ad7e718cSJohannes Berg {
647ad7e718cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
648ad7e718cSJohannes Berg 
649ad7e718cSJohannes Berg 	if (!ieee80211_sdata_running(sdata) ||
650ad7e718cSJohannes Berg 	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
651ad7e718cSJohannes Berg 		return NULL;
652ad7e718cSJohannes Berg 	return &sdata->vif;
653ad7e718cSJohannes Berg }
654ad7e718cSJohannes Berg EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
655ad7e718cSJohannes Berg 
65642935ecaSLuis R. Rodriguez /*
65742935ecaSLuis R. Rodriguez  * Nothing should have been stuffed into the workqueue during
65842935ecaSLuis R. Rodriguez  * the suspend->resume cycle. If this WARN is seen then there
65942935ecaSLuis R. Rodriguez  * is a bug with either the driver suspend or something in
66042935ecaSLuis R. Rodriguez  * mac80211 stuffing into the workqueue which we haven't yet
66142935ecaSLuis R. Rodriguez  * cleared during mac80211's suspend cycle.
66242935ecaSLuis R. Rodriguez  */
66342935ecaSLuis R. Rodriguez static bool ieee80211_can_queue_work(struct ieee80211_local *local)
66442935ecaSLuis R. Rodriguez {
665ceb99fe0SJohannes Berg 	if (WARN(local->suspended && !local->resuming,
666ceb99fe0SJohannes Berg 		 "queueing ieee80211 work while going to suspend\n"))
66742935ecaSLuis R. Rodriguez 		return false;
66842935ecaSLuis R. Rodriguez 
66942935ecaSLuis R. Rodriguez 	return true;
67042935ecaSLuis R. Rodriguez }
67142935ecaSLuis R. Rodriguez 
67242935ecaSLuis R. Rodriguez void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
67342935ecaSLuis R. Rodriguez {
67442935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
67542935ecaSLuis R. Rodriguez 
67642935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
67742935ecaSLuis R. Rodriguez 		return;
67842935ecaSLuis R. Rodriguez 
67942935ecaSLuis R. Rodriguez 	queue_work(local->workqueue, work);
68042935ecaSLuis R. Rodriguez }
68142935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_work);
68242935ecaSLuis R. Rodriguez 
68342935ecaSLuis R. Rodriguez void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
68442935ecaSLuis R. Rodriguez 				  struct delayed_work *dwork,
68542935ecaSLuis R. Rodriguez 				  unsigned long delay)
68642935ecaSLuis R. Rodriguez {
68742935ecaSLuis R. Rodriguez 	struct ieee80211_local *local = hw_to_local(hw);
68842935ecaSLuis R. Rodriguez 
68942935ecaSLuis R. Rodriguez 	if (!ieee80211_can_queue_work(local))
69042935ecaSLuis R. Rodriguez 		return;
69142935ecaSLuis R. Rodriguez 
69242935ecaSLuis R. Rodriguez 	queue_delayed_work(local->workqueue, dwork, delay);
69342935ecaSLuis R. Rodriguez }
69442935ecaSLuis R. Rodriguez EXPORT_SYMBOL(ieee80211_queue_delayed_work);
69542935ecaSLuis R. Rodriguez 
69635d865afSJohannes Berg u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
697dd76986bSJohannes Berg 			       struct ieee802_11_elems *elems,
698dd76986bSJohannes Berg 			       u64 filter, u32 crc)
699dd76986bSJohannes Berg {
700dd76986bSJohannes Berg 	size_t left = len;
70135d865afSJohannes Berg 	const u8 *pos = start;
702dd76986bSJohannes Berg 	bool calc_crc = filter != 0;
703fcff4f10SPaul Stewart 	DECLARE_BITMAP(seen_elems, 256);
704b2e506bfSJohannes Berg 	const u8 *ie;
705dd76986bSJohannes Berg 
706fcff4f10SPaul Stewart 	bitmap_zero(seen_elems, 256);
707dd76986bSJohannes Berg 	memset(elems, 0, sizeof(*elems));
708dd76986bSJohannes Berg 	elems->ie_start = start;
709dd76986bSJohannes Berg 	elems->total_len = len;
710dd76986bSJohannes Berg 
711dd76986bSJohannes Berg 	while (left >= 2) {
712dd76986bSJohannes Berg 		u8 id, elen;
713fcff4f10SPaul Stewart 		bool elem_parse_failed;
714dd76986bSJohannes Berg 
715dd76986bSJohannes Berg 		id = *pos++;
716dd76986bSJohannes Berg 		elen = *pos++;
717dd76986bSJohannes Berg 		left -= 2;
718dd76986bSJohannes Berg 
719fcff4f10SPaul Stewart 		if (elen > left) {
720fcff4f10SPaul Stewart 			elems->parse_error = true;
721dd76986bSJohannes Berg 			break;
722fcff4f10SPaul Stewart 		}
723fcff4f10SPaul Stewart 
7249690fb16SJohannes Berg 		switch (id) {
7259690fb16SJohannes Berg 		case WLAN_EID_SSID:
7269690fb16SJohannes Berg 		case WLAN_EID_SUPP_RATES:
7279690fb16SJohannes Berg 		case WLAN_EID_FH_PARAMS:
7289690fb16SJohannes Berg 		case WLAN_EID_DS_PARAMS:
7299690fb16SJohannes Berg 		case WLAN_EID_CF_PARAMS:
7309690fb16SJohannes Berg 		case WLAN_EID_TIM:
7319690fb16SJohannes Berg 		case WLAN_EID_IBSS_PARAMS:
7329690fb16SJohannes Berg 		case WLAN_EID_CHALLENGE:
7339690fb16SJohannes Berg 		case WLAN_EID_RSN:
7349690fb16SJohannes Berg 		case WLAN_EID_ERP_INFO:
7359690fb16SJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
7369690fb16SJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
7379690fb16SJohannes Berg 		case WLAN_EID_HT_OPERATION:
7389690fb16SJohannes Berg 		case WLAN_EID_VHT_CAPABILITY:
7399690fb16SJohannes Berg 		case WLAN_EID_VHT_OPERATION:
7409690fb16SJohannes Berg 		case WLAN_EID_MESH_ID:
7419690fb16SJohannes Berg 		case WLAN_EID_MESH_CONFIG:
7429690fb16SJohannes Berg 		case WLAN_EID_PEER_MGMT:
7439690fb16SJohannes Berg 		case WLAN_EID_PREQ:
7449690fb16SJohannes Berg 		case WLAN_EID_PREP:
7459690fb16SJohannes Berg 		case WLAN_EID_PERR:
7469690fb16SJohannes Berg 		case WLAN_EID_RANN:
7479690fb16SJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
7489690fb16SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
7499690fb16SJohannes Berg 		case WLAN_EID_COUNTRY:
7509690fb16SJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
7519690fb16SJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
75285220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
753b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
7548f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
755b2e506bfSJohannes Berg 		/*
756b2e506bfSJohannes Berg 		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
757b2e506bfSJohannes Berg 		 * that if the content gets bigger it might be needed more than once
758b2e506bfSJohannes Berg 		 */
7599690fb16SJohannes Berg 			if (test_bit(id, seen_elems)) {
760fcff4f10SPaul Stewart 				elems->parse_error = true;
761fcff4f10SPaul Stewart 				left -= elen;
762fcff4f10SPaul Stewart 				pos += elen;
763fcff4f10SPaul Stewart 				continue;
764fcff4f10SPaul Stewart 			}
7659690fb16SJohannes Berg 			break;
7669690fb16SJohannes Berg 		}
767dd76986bSJohannes Berg 
768dd76986bSJohannes Berg 		if (calc_crc && id < 64 && (filter & (1ULL << id)))
769dd76986bSJohannes Berg 			crc = crc32_be(crc, pos - 2, elen + 2);
770dd76986bSJohannes Berg 
771fcff4f10SPaul Stewart 		elem_parse_failed = false;
772fcff4f10SPaul Stewart 
773dd76986bSJohannes Berg 		switch (id) {
774dd76986bSJohannes Berg 		case WLAN_EID_SSID:
775dd76986bSJohannes Berg 			elems->ssid = pos;
776dd76986bSJohannes Berg 			elems->ssid_len = elen;
777dd76986bSJohannes Berg 			break;
778dd76986bSJohannes Berg 		case WLAN_EID_SUPP_RATES:
779dd76986bSJohannes Berg 			elems->supp_rates = pos;
780dd76986bSJohannes Berg 			elems->supp_rates_len = elen;
781dd76986bSJohannes Berg 			break;
782dd76986bSJohannes Berg 		case WLAN_EID_DS_PARAMS:
7831cd8e88eSJohannes Berg 			if (elen >= 1)
784dd76986bSJohannes Berg 				elems->ds_params = pos;
7851cd8e88eSJohannes Berg 			else
7861cd8e88eSJohannes Berg 				elem_parse_failed = true;
787dd76986bSJohannes Berg 			break;
788dd76986bSJohannes Berg 		case WLAN_EID_TIM:
789dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_tim_ie)) {
790dd76986bSJohannes Berg 				elems->tim = (void *)pos;
791dd76986bSJohannes Berg 				elems->tim_len = elen;
792fcff4f10SPaul Stewart 			} else
793fcff4f10SPaul Stewart 				elem_parse_failed = true;
794dd76986bSJohannes Berg 			break;
795dd76986bSJohannes Berg 		case WLAN_EID_CHALLENGE:
796dd76986bSJohannes Berg 			elems->challenge = pos;
797dd76986bSJohannes Berg 			elems->challenge_len = elen;
798dd76986bSJohannes Berg 			break;
799dd76986bSJohannes Berg 		case WLAN_EID_VENDOR_SPECIFIC:
800dd76986bSJohannes Berg 			if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
801dd76986bSJohannes Berg 			    pos[2] == 0xf2) {
802dd76986bSJohannes Berg 				/* Microsoft OUI (00:50:F2) */
803dd76986bSJohannes Berg 
804dd76986bSJohannes Berg 				if (calc_crc)
805dd76986bSJohannes Berg 					crc = crc32_be(crc, pos - 2, elen + 2);
806dd76986bSJohannes Berg 
807441a33baSJohannes Berg 				if (elen >= 5 && pos[3] == 2) {
808dd76986bSJohannes Berg 					/* OUI Type 2 - WMM IE */
809dd76986bSJohannes Berg 					if (pos[4] == 0) {
810dd76986bSJohannes Berg 						elems->wmm_info = pos;
811dd76986bSJohannes Berg 						elems->wmm_info_len = elen;
812dd76986bSJohannes Berg 					} else if (pos[4] == 1) {
813dd76986bSJohannes Berg 						elems->wmm_param = pos;
814dd76986bSJohannes Berg 						elems->wmm_param_len = elen;
815dd76986bSJohannes Berg 					}
816dd76986bSJohannes Berg 				}
817dd76986bSJohannes Berg 			}
818dd76986bSJohannes Berg 			break;
819dd76986bSJohannes Berg 		case WLAN_EID_RSN:
820dd76986bSJohannes Berg 			elems->rsn = pos;
821dd76986bSJohannes Berg 			elems->rsn_len = elen;
822dd76986bSJohannes Berg 			break;
823dd76986bSJohannes Berg 		case WLAN_EID_ERP_INFO:
8241946bed9SJohannes Berg 			if (elen >= 1)
825dd76986bSJohannes Berg 				elems->erp_info = pos;
8261946bed9SJohannes Berg 			else
8271946bed9SJohannes Berg 				elem_parse_failed = true;
828dd76986bSJohannes Berg 			break;
829dd76986bSJohannes Berg 		case WLAN_EID_EXT_SUPP_RATES:
830dd76986bSJohannes Berg 			elems->ext_supp_rates = pos;
831dd76986bSJohannes Berg 			elems->ext_supp_rates_len = elen;
832dd76986bSJohannes Berg 			break;
833dd76986bSJohannes Berg 		case WLAN_EID_HT_CAPABILITY:
834dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_cap))
835dd76986bSJohannes Berg 				elems->ht_cap_elem = (void *)pos;
836fcff4f10SPaul Stewart 			else
837fcff4f10SPaul Stewart 				elem_parse_failed = true;
838dd76986bSJohannes Berg 			break;
839074d46d1SJohannes Berg 		case WLAN_EID_HT_OPERATION:
840074d46d1SJohannes Berg 			if (elen >= sizeof(struct ieee80211_ht_operation))
841074d46d1SJohannes Berg 				elems->ht_operation = (void *)pos;
842fcff4f10SPaul Stewart 			else
843fcff4f10SPaul Stewart 				elem_parse_failed = true;
844dd76986bSJohannes Berg 			break;
845818255eaSMahesh Palivela 		case WLAN_EID_VHT_CAPABILITY:
846818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_cap))
847818255eaSMahesh Palivela 				elems->vht_cap_elem = (void *)pos;
848818255eaSMahesh Palivela 			else
849818255eaSMahesh Palivela 				elem_parse_failed = true;
850818255eaSMahesh Palivela 			break;
851818255eaSMahesh Palivela 		case WLAN_EID_VHT_OPERATION:
852818255eaSMahesh Palivela 			if (elen >= sizeof(struct ieee80211_vht_operation))
853818255eaSMahesh Palivela 				elems->vht_operation = (void *)pos;
854818255eaSMahesh Palivela 			else
855818255eaSMahesh Palivela 				elem_parse_failed = true;
856818255eaSMahesh Palivela 			break;
857bee7f586SJohannes Berg 		case WLAN_EID_OPMODE_NOTIF:
858bee7f586SJohannes Berg 			if (elen > 0)
859bee7f586SJohannes Berg 				elems->opmode_notif = pos;
860bee7f586SJohannes Berg 			else
861bee7f586SJohannes Berg 				elem_parse_failed = true;
862bee7f586SJohannes Berg 			break;
863dd76986bSJohannes Berg 		case WLAN_EID_MESH_ID:
864dd76986bSJohannes Berg 			elems->mesh_id = pos;
865dd76986bSJohannes Berg 			elems->mesh_id_len = elen;
866dd76986bSJohannes Berg 			break;
867dd76986bSJohannes Berg 		case WLAN_EID_MESH_CONFIG:
868dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_meshconf_ie))
869dd76986bSJohannes Berg 				elems->mesh_config = (void *)pos;
870fcff4f10SPaul Stewart 			else
871fcff4f10SPaul Stewart 				elem_parse_failed = true;
872dd76986bSJohannes Berg 			break;
873dd76986bSJohannes Berg 		case WLAN_EID_PEER_MGMT:
874dd76986bSJohannes Berg 			elems->peering = pos;
875dd76986bSJohannes Berg 			elems->peering_len = elen;
876dd76986bSJohannes Berg 			break;
8773f52b7e3SMarco Porsch 		case WLAN_EID_MESH_AWAKE_WINDOW:
8783f52b7e3SMarco Porsch 			if (elen >= 2)
8793f52b7e3SMarco Porsch 				elems->awake_window = (void *)pos;
8803f52b7e3SMarco Porsch 			break;
881dd76986bSJohannes Berg 		case WLAN_EID_PREQ:
882dd76986bSJohannes Berg 			elems->preq = pos;
883dd76986bSJohannes Berg 			elems->preq_len = elen;
884dd76986bSJohannes Berg 			break;
885dd76986bSJohannes Berg 		case WLAN_EID_PREP:
886dd76986bSJohannes Berg 			elems->prep = pos;
887dd76986bSJohannes Berg 			elems->prep_len = elen;
888dd76986bSJohannes Berg 			break;
889dd76986bSJohannes Berg 		case WLAN_EID_PERR:
890dd76986bSJohannes Berg 			elems->perr = pos;
891dd76986bSJohannes Berg 			elems->perr_len = elen;
892dd76986bSJohannes Berg 			break;
893dd76986bSJohannes Berg 		case WLAN_EID_RANN:
894dd76986bSJohannes Berg 			if (elen >= sizeof(struct ieee80211_rann_ie))
895dd76986bSJohannes Berg 				elems->rann = (void *)pos;
896fcff4f10SPaul Stewart 			else
897fcff4f10SPaul Stewart 				elem_parse_failed = true;
898dd76986bSJohannes Berg 			break;
899dd76986bSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH:
9005bc1420bSJohannes Berg 			if (elen != sizeof(struct ieee80211_channel_sw_ie)) {
9015bc1420bSJohannes Berg 				elem_parse_failed = true;
9025bc1420bSJohannes Berg 				break;
9035bc1420bSJohannes Berg 			}
9045bc1420bSJohannes Berg 			elems->ch_switch_ie = (void *)pos;
905dd76986bSJohannes Berg 			break;
906b4f286a1SJohannes Berg 		case WLAN_EID_EXT_CHANSWITCH_ANN:
907b4f286a1SJohannes Berg 			if (elen != sizeof(struct ieee80211_ext_chansw_ie)) {
908b4f286a1SJohannes Berg 				elem_parse_failed = true;
909b4f286a1SJohannes Berg 				break;
910b4f286a1SJohannes Berg 			}
911b4f286a1SJohannes Berg 			elems->ext_chansw_ie = (void *)pos;
912b4f286a1SJohannes Berg 			break;
91385220d71SJohannes Berg 		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
91485220d71SJohannes Berg 			if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
91585220d71SJohannes Berg 				elem_parse_failed = true;
91685220d71SJohannes Berg 				break;
91785220d71SJohannes Berg 			}
91885220d71SJohannes Berg 			elems->sec_chan_offs = (void *)pos;
91985220d71SJohannes Berg 			break;
9208f2535b9SChun-Yeow Yeoh 		case WLAN_EID_CHAN_SWITCH_PARAM:
9218f2535b9SChun-Yeow Yeoh 			if (elen !=
9228f2535b9SChun-Yeow Yeoh 			    sizeof(*elems->mesh_chansw_params_ie)) {
9238f2535b9SChun-Yeow Yeoh 				elem_parse_failed = true;
9248f2535b9SChun-Yeow Yeoh 				break;
9258f2535b9SChun-Yeow Yeoh 			}
9268f2535b9SChun-Yeow Yeoh 			elems->mesh_chansw_params_ie = (void *)pos;
9278f2535b9SChun-Yeow Yeoh 			break;
928b2e506bfSJohannes Berg 		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
929b2e506bfSJohannes Berg 			if (!action ||
930b2e506bfSJohannes Berg 			    elen != sizeof(*elems->wide_bw_chansw_ie)) {
931b2e506bfSJohannes Berg 				elem_parse_failed = true;
932b2e506bfSJohannes Berg 				break;
933b2e506bfSJohannes Berg 			}
934b2e506bfSJohannes Berg 			elems->wide_bw_chansw_ie = (void *)pos;
935b2e506bfSJohannes Berg 			break;
936b2e506bfSJohannes Berg 		case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
937b2e506bfSJohannes Berg 			if (action) {
938b2e506bfSJohannes Berg 				elem_parse_failed = true;
939b2e506bfSJohannes Berg 				break;
940b2e506bfSJohannes Berg 			}
941b2e506bfSJohannes Berg 			/*
942b2e506bfSJohannes Berg 			 * This is a bit tricky, but as we only care about
943b2e506bfSJohannes Berg 			 * the wide bandwidth channel switch element, so
944b2e506bfSJohannes Berg 			 * just parse it out manually.
945b2e506bfSJohannes Berg 			 */
946b2e506bfSJohannes Berg 			ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
947b2e506bfSJohannes Berg 					      pos, elen);
948b2e506bfSJohannes Berg 			if (ie) {
949b2e506bfSJohannes Berg 				if (ie[1] == sizeof(*elems->wide_bw_chansw_ie))
950b2e506bfSJohannes Berg 					elems->wide_bw_chansw_ie =
951b2e506bfSJohannes Berg 						(void *)(ie + 2);
952b2e506bfSJohannes Berg 				else
953b2e506bfSJohannes Berg 					elem_parse_failed = true;
954b2e506bfSJohannes Berg 			}
955b2e506bfSJohannes Berg 			break;
956dd76986bSJohannes Berg 		case WLAN_EID_COUNTRY:
957dd76986bSJohannes Berg 			elems->country_elem = pos;
958dd76986bSJohannes Berg 			elems->country_elem_len = elen;
959dd76986bSJohannes Berg 			break;
960dd76986bSJohannes Berg 		case WLAN_EID_PWR_CONSTRAINT:
961761a48d2SJohannes Berg 			if (elen != 1) {
962761a48d2SJohannes Berg 				elem_parse_failed = true;
963761a48d2SJohannes Berg 				break;
964761a48d2SJohannes Berg 			}
965dd76986bSJohannes Berg 			elems->pwr_constr_elem = pos;
966dd76986bSJohannes Berg 			break;
967dd76986bSJohannes Berg 		case WLAN_EID_TIMEOUT_INTERVAL:
96879ba1d89SJohannes Berg 			if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
96979ba1d89SJohannes Berg 				elems->timeout_int = (void *)pos;
97079ba1d89SJohannes Berg 			else
97179ba1d89SJohannes Berg 				elem_parse_failed = true;
972dd76986bSJohannes Berg 			break;
973dd76986bSJohannes Berg 		default:
974dd76986bSJohannes Berg 			break;
975dd76986bSJohannes Berg 		}
976dd76986bSJohannes Berg 
977fcff4f10SPaul Stewart 		if (elem_parse_failed)
978fcff4f10SPaul Stewart 			elems->parse_error = true;
979fcff4f10SPaul Stewart 		else
9805df45690SJohannes Berg 			__set_bit(id, seen_elems);
981fcff4f10SPaul Stewart 
982dd76986bSJohannes Berg 		left -= elen;
983dd76986bSJohannes Berg 		pos += elen;
984dd76986bSJohannes Berg 	}
985dd76986bSJohannes Berg 
986fcff4f10SPaul Stewart 	if (left != 0)
987fcff4f10SPaul Stewart 		elems->parse_error = true;
988fcff4f10SPaul Stewart 
989dd76986bSJohannes Berg 	return crc;
990dd76986bSJohannes Berg }
991dd76986bSJohannes Berg 
9923abead59SJohannes Berg void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
9933abead59SJohannes Berg 			       bool bss_notify)
9945825fe10SJohannes Berg {
9955825fe10SJohannes Berg 	struct ieee80211_local *local = sdata->local;
9965825fe10SJohannes Berg 	struct ieee80211_tx_queue_params qparam;
99755de908aSJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
99854bcbc69SJohannes Berg 	int ac;
999a8ce8544SStanislaw Gruszka 	bool use_11b, enable_qos;
1000aa837e1dSJohannes Berg 	int aCWmin, aCWmax;
10015825fe10SJohannes Berg 
10025825fe10SJohannes Berg 	if (!local->ops->conf_tx)
10035825fe10SJohannes Berg 		return;
10045825fe10SJohannes Berg 
100554bcbc69SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
100654bcbc69SJohannes Berg 		return;
100754bcbc69SJohannes Berg 
10085825fe10SJohannes Berg 	memset(&qparam, 0, sizeof(qparam));
10095825fe10SJohannes Berg 
101055de908aSJohannes Berg 	rcu_read_lock();
101155de908aSJohannes Berg 	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
101255de908aSJohannes Berg 	use_11b = (chanctx_conf &&
10134bf88530SJohannes Berg 		   chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
1014aa837e1dSJohannes Berg 		 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
101555de908aSJohannes Berg 	rcu_read_unlock();
10165825fe10SJohannes Berg 
1017a8ce8544SStanislaw Gruszka 	/*
1018a8ce8544SStanislaw Gruszka 	 * By default disable QoS in STA mode for old access points, which do
1019a8ce8544SStanislaw Gruszka 	 * not support 802.11e. New APs will provide proper queue parameters,
1020a8ce8544SStanislaw Gruszka 	 * that we will configure later.
1021a8ce8544SStanislaw Gruszka 	 */
1022a8ce8544SStanislaw Gruszka 	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
1023a8ce8544SStanislaw Gruszka 
1024aa837e1dSJohannes Berg 	/* Set defaults according to 802.11-2007 Table 7-37 */
1025aa837e1dSJohannes Berg 	aCWmax = 1023;
1026aa837e1dSJohannes Berg 	if (use_11b)
1027aa837e1dSJohannes Berg 		aCWmin = 31;
10285825fe10SJohannes Berg 	else
1029aa837e1dSJohannes Berg 		aCWmin = 15;
10305825fe10SJohannes Berg 
10311f4ffde8SFred Zhou 	/* Confiure old 802.11b/g medium access rules. */
10321f4ffde8SFred Zhou 	qparam.cw_max = aCWmax;
10331f4ffde8SFred Zhou 	qparam.cw_min = aCWmin;
10341f4ffde8SFred Zhou 	qparam.txop = 0;
10351f4ffde8SFred Zhou 	qparam.aifs = 2;
10361f4ffde8SFred Zhou 
10371f4ffde8SFred Zhou 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
10381f4ffde8SFred Zhou 		/* Update if QoS is enabled. */
1039a8ce8544SStanislaw Gruszka 		if (enable_qos) {
104054bcbc69SJohannes Berg 			switch (ac) {
10411d98fb12SJohannes Berg 			case IEEE80211_AC_BK:
10427ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
10437ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
10445825fe10SJohannes Berg 				qparam.txop = 0;
1045aa837e1dSJohannes Berg 				qparam.aifs = 7;
1046aa837e1dSJohannes Berg 				break;
1047a8ce8544SStanislaw Gruszka 			/* never happens but let's not leave undefined */
1048a8ce8544SStanislaw Gruszka 			default:
10491d98fb12SJohannes Berg 			case IEEE80211_AC_BE:
10507ba10a8eSJohannes Berg 				qparam.cw_max = aCWmax;
10517ba10a8eSJohannes Berg 				qparam.cw_min = aCWmin;
1052aa837e1dSJohannes Berg 				qparam.txop = 0;
1053aa837e1dSJohannes Berg 				qparam.aifs = 3;
1054aa837e1dSJohannes Berg 				break;
10551d98fb12SJohannes Berg 			case IEEE80211_AC_VI:
1056aa837e1dSJohannes Berg 				qparam.cw_max = aCWmin;
1057aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 2 - 1;
1058aa837e1dSJohannes Berg 				if (use_11b)
1059aa837e1dSJohannes Berg 					qparam.txop = 6016/32;
1060aa837e1dSJohannes Berg 				else
1061aa837e1dSJohannes Berg 					qparam.txop = 3008/32;
1062aa837e1dSJohannes Berg 				qparam.aifs = 2;
1063aa837e1dSJohannes Berg 				break;
10641d98fb12SJohannes Berg 			case IEEE80211_AC_VO:
1065aa837e1dSJohannes Berg 				qparam.cw_max = (aCWmin + 1) / 2 - 1;
1066aa837e1dSJohannes Berg 				qparam.cw_min = (aCWmin + 1) / 4 - 1;
1067aa837e1dSJohannes Berg 				if (use_11b)
1068aa837e1dSJohannes Berg 					qparam.txop = 3264/32;
1069aa837e1dSJohannes Berg 				else
1070aa837e1dSJohannes Berg 					qparam.txop = 1504/32;
1071aa837e1dSJohannes Berg 				qparam.aifs = 2;
1072aa837e1dSJohannes Berg 				break;
1073aa837e1dSJohannes Berg 			}
1074a8ce8544SStanislaw Gruszka 		}
10755825fe10SJohannes Berg 
1076ab13315aSKalle Valo 		qparam.uapsd = false;
1077ab13315aSKalle Valo 
107854bcbc69SJohannes Berg 		sdata->tx_conf[ac] = qparam;
107954bcbc69SJohannes Berg 		drv_conf_tx(local, sdata, ac, &qparam);
1080aa837e1dSJohannes Berg 	}
1081e1b3ec1aSStanislaw Gruszka 
1082f142c6b9SJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
1083f142c6b9SJohannes Berg 	    sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) {
1084a8ce8544SStanislaw Gruszka 		sdata->vif.bss_conf.qos = enable_qos;
10853abead59SJohannes Berg 		if (bss_notify)
10863abead59SJohannes Berg 			ieee80211_bss_info_change_notify(sdata,
10873abead59SJohannes Berg 							 BSS_CHANGED_QOS);
10885825fe10SJohannes Berg 	}
1089d9734979SSujith }
1090e50db65cSJohannes Berg 
109146900298SJohannes Berg void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1092700e8ea6SJouni Malinen 			 u16 transaction, u16 auth_alg, u16 status,
10934a3cb702SJohannes Berg 			 const u8 *extra, size_t extra_len, const u8 *da,
10941672c0e3SJohannes Berg 			 const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
10951672c0e3SJohannes Berg 			 u32 tx_flags)
109646900298SJohannes Berg {
109746900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
109846900298SJohannes Berg 	struct sk_buff *skb;
109946900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1100fffd0934SJohannes Berg 	int err;
110146900298SJohannes Berg 
110215e230abSFred Zhou 	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
110315e230abSFred Zhou 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
1104d15b8459SJoe Perches 	if (!skb)
110546900298SJohannes Berg 		return;
1106d15b8459SJoe Perches 
110746900298SJohannes Berg 	skb_reserve(skb, local->hw.extra_tx_headroom);
110846900298SJohannes Berg 
110946900298SJohannes Berg 	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
111046900298SJohannes Berg 	memset(mgmt, 0, 24 + 6);
111146900298SJohannes Berg 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
111246900298SJohannes Berg 					  IEEE80211_STYPE_AUTH);
1113efa6a09dSAntonio Quartulli 	memcpy(mgmt->da, da, ETH_ALEN);
111447846c9bSJohannes Berg 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
111546900298SJohannes Berg 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
111646900298SJohannes Berg 	mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
111746900298SJohannes Berg 	mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
1118700e8ea6SJouni Malinen 	mgmt->u.auth.status_code = cpu_to_le16(status);
111946900298SJohannes Berg 	if (extra)
112046900298SJohannes Berg 		memcpy(skb_put(skb, extra_len), extra, extra_len);
112146900298SJohannes Berg 
1122fffd0934SJohannes Berg 	if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
1123fffd0934SJohannes Berg 		mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
1124fffd0934SJohannes Berg 		err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
1125fffd0934SJohannes Berg 		WARN_ON(err);
1126fffd0934SJohannes Berg 	}
1127fffd0934SJohannes Berg 
11281672c0e3SJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
11291672c0e3SJohannes Berg 					tx_flags;
113062ae67beSJohannes Berg 	ieee80211_tx_skb(sdata, skb);
113146900298SJohannes Berg }
113246900298SJohannes Berg 
11336ae16775SAntonio Quartulli void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
11346ae16775SAntonio Quartulli 				    const u8 *bssid, u16 stype, u16 reason,
11356ae16775SAntonio Quartulli 				    bool send_frame, u8 *frame_buf)
11366ae16775SAntonio Quartulli {
11376ae16775SAntonio Quartulli 	struct ieee80211_local *local = sdata->local;
11386ae16775SAntonio Quartulli 	struct sk_buff *skb;
11396ae16775SAntonio Quartulli 	struct ieee80211_mgmt *mgmt = (void *)frame_buf;
11406ae16775SAntonio Quartulli 
11416ae16775SAntonio Quartulli 	/* build frame */
11426ae16775SAntonio Quartulli 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
11436ae16775SAntonio Quartulli 	mgmt->duration = 0; /* initialize only */
11446ae16775SAntonio Quartulli 	mgmt->seq_ctrl = 0; /* initialize only */
11456ae16775SAntonio Quartulli 	memcpy(mgmt->da, bssid, ETH_ALEN);
11466ae16775SAntonio Quartulli 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
11476ae16775SAntonio Quartulli 	memcpy(mgmt->bssid, bssid, ETH_ALEN);
11486ae16775SAntonio Quartulli 	/* u.deauth.reason_code == u.disassoc.reason_code */
11496ae16775SAntonio Quartulli 	mgmt->u.deauth.reason_code = cpu_to_le16(reason);
11506ae16775SAntonio Quartulli 
11516ae16775SAntonio Quartulli 	if (send_frame) {
11526ae16775SAntonio Quartulli 		skb = dev_alloc_skb(local->hw.extra_tx_headroom +
11536ae16775SAntonio Quartulli 				    IEEE80211_DEAUTH_FRAME_LEN);
11546ae16775SAntonio Quartulli 		if (!skb)
11556ae16775SAntonio Quartulli 			return;
11566ae16775SAntonio Quartulli 
11576ae16775SAntonio Quartulli 		skb_reserve(skb, local->hw.extra_tx_headroom);
11586ae16775SAntonio Quartulli 
11596ae16775SAntonio Quartulli 		/* copy in frame */
11606ae16775SAntonio Quartulli 		memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
11616ae16775SAntonio Quartulli 		       mgmt, IEEE80211_DEAUTH_FRAME_LEN);
11626ae16775SAntonio Quartulli 
11636ae16775SAntonio Quartulli 		if (sdata->vif.type != NL80211_IFTYPE_STATION ||
11646ae16775SAntonio Quartulli 		    !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
11656ae16775SAntonio Quartulli 			IEEE80211_SKB_CB(skb)->flags |=
11666ae16775SAntonio Quartulli 				IEEE80211_TX_INTFL_DONT_ENCRYPT;
11676ae16775SAntonio Quartulli 
11686ae16775SAntonio Quartulli 		ieee80211_tx_skb(sdata, skb);
11696ae16775SAntonio Quartulli 	}
11706ae16775SAntonio Quartulli }
11716ae16775SAntonio Quartulli 
1172de95a54bSJohannes Berg int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
1173c604b9f2SJohannes Berg 			     size_t buffer_len, const u8 *ie, size_t ie_len,
1174651b5225SJouni Malinen 			     enum ieee80211_band band, u32 rate_mask,
11752103dec1SSimon Wunderlich 			     struct cfg80211_chan_def *chandef)
1176de95a54bSJohannes Berg {
1177de95a54bSJohannes Berg 	struct ieee80211_supported_band *sband;
1178c604b9f2SJohannes Berg 	u8 *pos = buffer, *end = buffer + buffer_len;
11798e664fb3SJohannes Berg 	size_t offset = 0, noffset;
11808e664fb3SJohannes Berg 	int supp_rates_len, i;
11818dcb2003SJouni Malinen 	u8 rates[32];
11828dcb2003SJouni Malinen 	int num_rates;
11838dcb2003SJouni Malinen 	int ext_rates_len;
11842103dec1SSimon Wunderlich 	int shift;
11852103dec1SSimon Wunderlich 	u32 rate_flags;
1186de95a54bSJohannes Berg 
11874d36ec58SJohannes Berg 	sband = local->hw.wiphy->bands[band];
1188d811b3d5SArik Nemtsov 	if (WARN_ON_ONCE(!sband))
1189d811b3d5SArik Nemtsov 		return 0;
1190de95a54bSJohannes Berg 
11912103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(chandef);
11922103dec1SSimon Wunderlich 	shift = ieee80211_chandef_get_shift(chandef);
11932103dec1SSimon Wunderlich 
11948dcb2003SJouni Malinen 	num_rates = 0;
11958dcb2003SJouni Malinen 	for (i = 0; i < sband->n_bitrates; i++) {
11968dcb2003SJouni Malinen 		if ((BIT(i) & rate_mask) == 0)
11978dcb2003SJouni Malinen 			continue; /* skip rate */
11982103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
11992103dec1SSimon Wunderlich 			continue;
12002103dec1SSimon Wunderlich 
12012103dec1SSimon Wunderlich 		rates[num_rates++] =
12022103dec1SSimon Wunderlich 			(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
12032103dec1SSimon Wunderlich 					  (1 << shift) * 5);
12048dcb2003SJouni Malinen 	}
12058dcb2003SJouni Malinen 
12068dcb2003SJouni Malinen 	supp_rates_len = min_t(int, num_rates, 8);
12078e664fb3SJohannes Berg 
1208c604b9f2SJohannes Berg 	if (end - pos < 2 + supp_rates_len)
1209c604b9f2SJohannes Berg 		goto out_err;
1210de95a54bSJohannes Berg 	*pos++ = WLAN_EID_SUPP_RATES;
12118e664fb3SJohannes Berg 	*pos++ = supp_rates_len;
12128dcb2003SJouni Malinen 	memcpy(pos, rates, supp_rates_len);
12138dcb2003SJouni Malinen 	pos += supp_rates_len;
1214de95a54bSJohannes Berg 
12158e664fb3SJohannes Berg 	/* insert "request information" if in custom IEs */
12168e664fb3SJohannes Berg 	if (ie && ie_len) {
12178e664fb3SJohannes Berg 		static const u8 before_extrates[] = {
12188e664fb3SJohannes Berg 			WLAN_EID_SSID,
12198e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
12208e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
12218e664fb3SJohannes Berg 		};
12228e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
12238e664fb3SJohannes Berg 					     before_extrates,
12248e664fb3SJohannes Berg 					     ARRAY_SIZE(before_extrates),
12258e664fb3SJohannes Berg 					     offset);
1226c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1227c604b9f2SJohannes Berg 			goto out_err;
12288e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
12298e664fb3SJohannes Berg 		pos += noffset - offset;
12308e664fb3SJohannes Berg 		offset = noffset;
12318e664fb3SJohannes Berg 	}
12328e664fb3SJohannes Berg 
12338dcb2003SJouni Malinen 	ext_rates_len = num_rates - supp_rates_len;
12348dcb2003SJouni Malinen 	if (ext_rates_len > 0) {
1235c604b9f2SJohannes Berg 		if (end - pos < 2 + ext_rates_len)
1236c604b9f2SJohannes Berg 			goto out_err;
1237de95a54bSJohannes Berg 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
12388dcb2003SJouni Malinen 		*pos++ = ext_rates_len;
12398dcb2003SJouni Malinen 		memcpy(pos, rates + supp_rates_len, ext_rates_len);
12408dcb2003SJouni Malinen 		pos += ext_rates_len;
12418e664fb3SJohannes Berg 	}
12428e664fb3SJohannes Berg 
12432103dec1SSimon Wunderlich 	if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
1244c604b9f2SJohannes Berg 		if (end - pos < 3)
1245c604b9f2SJohannes Berg 			goto out_err;
1246651b5225SJouni Malinen 		*pos++ = WLAN_EID_DS_PARAMS;
1247651b5225SJouni Malinen 		*pos++ = 1;
12482103dec1SSimon Wunderlich 		*pos++ = ieee80211_frequency_to_channel(
12492103dec1SSimon Wunderlich 				chandef->chan->center_freq);
1250651b5225SJouni Malinen 	}
1251651b5225SJouni Malinen 
12528e664fb3SJohannes Berg 	/* insert custom IEs that go before HT */
12538e664fb3SJohannes Berg 	if (ie && ie_len) {
12548e664fb3SJohannes Berg 		static const u8 before_ht[] = {
12558e664fb3SJohannes Berg 			WLAN_EID_SSID,
12568e664fb3SJohannes Berg 			WLAN_EID_SUPP_RATES,
12578e664fb3SJohannes Berg 			WLAN_EID_REQUEST,
12588e664fb3SJohannes Berg 			WLAN_EID_EXT_SUPP_RATES,
12598e664fb3SJohannes Berg 			WLAN_EID_DS_PARAMS,
12608e664fb3SJohannes Berg 			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
12618e664fb3SJohannes Berg 		};
12628e664fb3SJohannes Berg 		noffset = ieee80211_ie_split(ie, ie_len,
12638e664fb3SJohannes Berg 					     before_ht, ARRAY_SIZE(before_ht),
12648e664fb3SJohannes Berg 					     offset);
1265c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1266c604b9f2SJohannes Berg 			goto out_err;
12678e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
12688e664fb3SJohannes Berg 		pos += noffset - offset;
12698e664fb3SJohannes Berg 		offset = noffset;
1270de95a54bSJohannes Berg 	}
1271de95a54bSJohannes Berg 
1272c604b9f2SJohannes Berg 	if (sband->ht_cap.ht_supported) {
1273c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
1274c604b9f2SJohannes Berg 			goto out_err;
1275ef96a842SBen Greear 		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
1276ef96a842SBen Greear 						sband->ht_cap.cap);
1277c604b9f2SJohannes Berg 	}
12785ef2d41aSJohannes Berg 
1279de95a54bSJohannes Berg 	/*
1280de95a54bSJohannes Berg 	 * If adding more here, adjust code in main.c
1281de95a54bSJohannes Berg 	 * that calculates local->scan_ies_len.
1282de95a54bSJohannes Berg 	 */
1283de95a54bSJohannes Berg 
12848e664fb3SJohannes Berg 	/* add any remaining custom IEs */
12858e664fb3SJohannes Berg 	if (ie && ie_len) {
12868e664fb3SJohannes Berg 		noffset = ie_len;
1287c604b9f2SJohannes Berg 		if (end - pos < noffset - offset)
1288c604b9f2SJohannes Berg 			goto out_err;
12898e664fb3SJohannes Berg 		memcpy(pos, ie + offset, noffset - offset);
12908e664fb3SJohannes Berg 		pos += noffset - offset;
1291de95a54bSJohannes Berg 	}
1292de95a54bSJohannes Berg 
1293c604b9f2SJohannes Berg 	if (sband->vht_cap.vht_supported) {
1294c604b9f2SJohannes Berg 		if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
1295c604b9f2SJohannes Berg 			goto out_err;
1296ba0afa2fSMahesh Palivela 		pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
1297ba0afa2fSMahesh Palivela 						 sband->vht_cap.cap);
1298c604b9f2SJohannes Berg 	}
1299ba0afa2fSMahesh Palivela 
1300de95a54bSJohannes Berg 	return pos - buffer;
1301c604b9f2SJohannes Berg  out_err:
1302c604b9f2SJohannes Berg 	WARN_ONCE(1, "not enough space for preq IEs\n");
1303c604b9f2SJohannes Berg 	return pos - buffer;
1304de95a54bSJohannes Berg }
1305de95a54bSJohannes Berg 
1306a619a4c0SJuuso Oikarinen struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
130785a237feSJohannes Berg 					  u8 *dst, u32 ratemask,
13086b77863bSJohannes Berg 					  struct ieee80211_channel *chan,
1309de95a54bSJohannes Berg 					  const u8 *ssid, size_t ssid_len,
1310a806c558SPaul Stewart 					  const u8 *ie, size_t ie_len,
1311a806c558SPaul Stewart 					  bool directed)
131246900298SJohannes Berg {
131346900298SJohannes Berg 	struct ieee80211_local *local = sdata->local;
13142103dec1SSimon Wunderlich 	struct cfg80211_chan_def chandef;
131546900298SJohannes Berg 	struct sk_buff *skb;
131646900298SJohannes Berg 	struct ieee80211_mgmt *mgmt;
1317b9a9ada1SJohannes Berg 	int ies_len;
131846900298SJohannes Berg 
1319a806c558SPaul Stewart 	/*
1320a806c558SPaul Stewart 	 * Do not send DS Channel parameter for directed probe requests
1321a806c558SPaul Stewart 	 * in order to maximize the chance that we get a response.  Some
1322a806c558SPaul Stewart 	 * badly-behaved APs don't respond when this parameter is included.
1323a806c558SPaul Stewart 	 */
13242103dec1SSimon Wunderlich 	chandef.width = sdata->vif.bss_conf.chandef.width;
1325a806c558SPaul Stewart 	if (directed)
13262103dec1SSimon Wunderlich 		chandef.chan = NULL;
1327a806c558SPaul Stewart 	else
13282103dec1SSimon Wunderlich 		chandef.chan = chan;
1329651b5225SJouni Malinen 
13307c12ce8bSKalle Valo 	skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
1331b9a9ada1SJohannes Berg 				     ssid, ssid_len, 100 + ie_len);
13325b2bbf75SJohannes Berg 	if (!skb)
1333b9a9ada1SJohannes Berg 		return NULL;
1334b9a9ada1SJohannes Berg 
1335b9a9ada1SJohannes Berg 	ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
1336b9a9ada1SJohannes Berg 					   skb_tailroom(skb),
1337c604b9f2SJohannes Berg 					   ie, ie_len, chan->band,
13382103dec1SSimon Wunderlich 					   ratemask, &chandef);
1339b9a9ada1SJohannes Berg 	skb_put(skb, ies_len);
13407c12ce8bSKalle Valo 
134146900298SJohannes Berg 	if (dst) {
13427c12ce8bSKalle Valo 		mgmt = (struct ieee80211_mgmt *) skb->data;
134346900298SJohannes Berg 		memcpy(mgmt->da, dst, ETH_ALEN);
134446900298SJohannes Berg 		memcpy(mgmt->bssid, dst, ETH_ALEN);
134546900298SJohannes Berg 	}
134646900298SJohannes Berg 
134762ae67beSJohannes Berg 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
13485b2bbf75SJohannes Berg 
1349a619a4c0SJuuso Oikarinen 	return skb;
1350a619a4c0SJuuso Oikarinen }
1351a619a4c0SJuuso Oikarinen 
1352a619a4c0SJuuso Oikarinen void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
1353a619a4c0SJuuso Oikarinen 			      const u8 *ssid, size_t ssid_len,
1354a806c558SPaul Stewart 			      const u8 *ie, size_t ie_len,
13551672c0e3SJohannes Berg 			      u32 ratemask, bool directed, u32 tx_flags,
135655de908aSJohannes Berg 			      struct ieee80211_channel *channel, bool scan)
1357a619a4c0SJuuso Oikarinen {
1358a619a4c0SJuuso Oikarinen 	struct sk_buff *skb;
1359a619a4c0SJuuso Oikarinen 
1360fe94fe05SJohannes Berg 	skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
13616b77863bSJohannes Berg 					ssid, ssid_len,
136285a237feSJohannes Berg 					ie, ie_len, directed);
1363aad14cebSRajkumar Manoharan 	if (skb) {
13641672c0e3SJohannes Berg 		IEEE80211_SKB_CB(skb)->flags |= tx_flags;
136555de908aSJohannes Berg 		if (scan)
136655de908aSJohannes Berg 			ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
136755de908aSJohannes Berg 		else
1368a619a4c0SJuuso Oikarinen 			ieee80211_tx_skb(sdata, skb);
136946900298SJohannes Berg 	}
1370aad14cebSRajkumar Manoharan }
137146900298SJohannes Berg 
13722103dec1SSimon Wunderlich u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
137346900298SJohannes Berg 			    struct ieee802_11_elems *elems,
13749ebb61a2SAshok Nagarajan 			    enum ieee80211_band band, u32 *basic_rates)
137546900298SJohannes Berg {
137646900298SJohannes Berg 	struct ieee80211_supported_band *sband;
137746900298SJohannes Berg 	struct ieee80211_rate *bitrates;
137846900298SJohannes Berg 	size_t num_rates;
13792103dec1SSimon Wunderlich 	u32 supp_rates, rate_flags;
13802103dec1SSimon Wunderlich 	int i, j, shift;
13812103dec1SSimon Wunderlich 	sband = sdata->local->hw.wiphy->bands[band];
13822103dec1SSimon Wunderlich 
13832103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
13842103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
138546900298SJohannes Berg 
13864ee73f33SMichal Kazior 	if (WARN_ON(!sband))
13874ee73f33SMichal Kazior 		return 1;
138846900298SJohannes Berg 
138946900298SJohannes Berg 	bitrates = sband->bitrates;
139046900298SJohannes Berg 	num_rates = sband->n_bitrates;
139146900298SJohannes Berg 	supp_rates = 0;
139246900298SJohannes Berg 	for (i = 0; i < elems->supp_rates_len +
139346900298SJohannes Berg 		     elems->ext_supp_rates_len; i++) {
139446900298SJohannes Berg 		u8 rate = 0;
139546900298SJohannes Berg 		int own_rate;
13969ebb61a2SAshok Nagarajan 		bool is_basic;
139746900298SJohannes Berg 		if (i < elems->supp_rates_len)
139846900298SJohannes Berg 			rate = elems->supp_rates[i];
139946900298SJohannes Berg 		else if (elems->ext_supp_rates)
140046900298SJohannes Berg 			rate = elems->ext_supp_rates
140146900298SJohannes Berg 				[i - elems->supp_rates_len];
140246900298SJohannes Berg 		own_rate = 5 * (rate & 0x7f);
14039ebb61a2SAshok Nagarajan 		is_basic = !!(rate & 0x80);
14049ebb61a2SAshok Nagarajan 
14059ebb61a2SAshok Nagarajan 		if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
14069ebb61a2SAshok Nagarajan 			continue;
14079ebb61a2SAshok Nagarajan 
14089ebb61a2SAshok Nagarajan 		for (j = 0; j < num_rates; j++) {
14092103dec1SSimon Wunderlich 			int brate;
14102103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[j].flags)
14112103dec1SSimon Wunderlich 			    != rate_flags)
14122103dec1SSimon Wunderlich 				continue;
14132103dec1SSimon Wunderlich 
14142103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
14152103dec1SSimon Wunderlich 					     1 << shift);
14162103dec1SSimon Wunderlich 
14172103dec1SSimon Wunderlich 			if (brate == own_rate) {
141846900298SJohannes Berg 				supp_rates |= BIT(j);
14199ebb61a2SAshok Nagarajan 				if (basic_rates && is_basic)
14209ebb61a2SAshok Nagarajan 					*basic_rates |= BIT(j);
14219ebb61a2SAshok Nagarajan 			}
14229ebb61a2SAshok Nagarajan 		}
142346900298SJohannes Berg 	}
142446900298SJohannes Berg 	return supp_rates;
142546900298SJohannes Berg }
1426f2753ddbSJohannes Berg 
142784f6a01cSJohannes Berg void ieee80211_stop_device(struct ieee80211_local *local)
142884f6a01cSJohannes Berg {
142984f6a01cSJohannes Berg 	ieee80211_led_radio(local, false);
143067408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
143184f6a01cSJohannes Berg 
143284f6a01cSJohannes Berg 	cancel_work_sync(&local->reconfig_filter);
143384f6a01cSJohannes Berg 
143484f6a01cSJohannes Berg 	flush_workqueue(local->workqueue);
1435678f415fSLennert Buytenhek 	drv_stop(local);
143684f6a01cSJohannes Berg }
143784f6a01cSJohannes Berg 
1438153a5fc4SStanislaw Gruszka static void ieee80211_assign_chanctx(struct ieee80211_local *local,
1439153a5fc4SStanislaw Gruszka 				     struct ieee80211_sub_if_data *sdata)
1440153a5fc4SStanislaw Gruszka {
1441153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx_conf *conf;
1442153a5fc4SStanislaw Gruszka 	struct ieee80211_chanctx *ctx;
1443153a5fc4SStanislaw Gruszka 
1444153a5fc4SStanislaw Gruszka 	if (!local->use_chanctx)
1445153a5fc4SStanislaw Gruszka 		return;
1446153a5fc4SStanislaw Gruszka 
1447153a5fc4SStanislaw Gruszka 	mutex_lock(&local->chanctx_mtx);
1448153a5fc4SStanislaw Gruszka 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1449153a5fc4SStanislaw Gruszka 					 lockdep_is_held(&local->chanctx_mtx));
1450153a5fc4SStanislaw Gruszka 	if (conf) {
1451153a5fc4SStanislaw Gruszka 		ctx = container_of(conf, struct ieee80211_chanctx, conf);
1452153a5fc4SStanislaw Gruszka 		drv_assign_vif_chanctx(local, sdata, ctx);
1453153a5fc4SStanislaw Gruszka 	}
1454153a5fc4SStanislaw Gruszka 	mutex_unlock(&local->chanctx_mtx);
1455153a5fc4SStanislaw Gruszka }
1456153a5fc4SStanislaw Gruszka 
1457f2753ddbSJohannes Berg int ieee80211_reconfig(struct ieee80211_local *local)
1458f2753ddbSJohannes Berg {
1459f2753ddbSJohannes Berg 	struct ieee80211_hw *hw = &local->hw;
1460f2753ddbSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
146155de908aSJohannes Berg 	struct ieee80211_chanctx *ctx;
1462f2753ddbSJohannes Berg 	struct sta_info *sta;
14632683d65bSEliad Peller 	int res, i;
1464d888130aSJohannes Berg 	bool reconfig_due_to_wowlan = false;
1465d43c6b6eSDavid Spinadel 	struct ieee80211_sub_if_data *sched_scan_sdata;
1466d43c6b6eSDavid Spinadel 	bool sched_scan_stopped = false;
1467d888130aSJohannes Berg 
14688f21b0adSJohannes Berg #ifdef CONFIG_PM
1469ceb99fe0SJohannes Berg 	if (local->suspended)
1470ceb99fe0SJohannes Berg 		local->resuming = true;
1471f2753ddbSJohannes Berg 
1472eecc4800SJohannes Berg 	if (local->wowlan) {
1473eecc4800SJohannes Berg 		res = drv_resume(local);
147427b3eb9cSJohannes Berg 		local->wowlan = false;
1475eecc4800SJohannes Berg 		if (res < 0) {
1476eecc4800SJohannes Berg 			local->resuming = false;
1477eecc4800SJohannes Berg 			return res;
1478eecc4800SJohannes Berg 		}
1479eecc4800SJohannes Berg 		if (res == 0)
1480eecc4800SJohannes Berg 			goto wake_up;
1481eecc4800SJohannes Berg 		WARN_ON(res > 1);
1482eecc4800SJohannes Berg 		/*
1483eecc4800SJohannes Berg 		 * res is 1, which means the driver requested
1484eecc4800SJohannes Berg 		 * to go through a regular reset on wakeup.
1485eecc4800SJohannes Berg 		 */
1486d888130aSJohannes Berg 		reconfig_due_to_wowlan = true;
1487eecc4800SJohannes Berg 	}
1488eecc4800SJohannes Berg #endif
148994f9b97bSJohannes Berg 	/* everything else happens only if HW was up & running */
149094f9b97bSJohannes Berg 	if (!local->open_count)
149194f9b97bSJohannes Berg 		goto wake_up;
149294f9b97bSJohannes Berg 
149324feda00SLuis R. Rodriguez 	/*
149424feda00SLuis R. Rodriguez 	 * Upon resume hardware can sometimes be goofy due to
149524feda00SLuis R. Rodriguez 	 * various platform / driver / bus issues, so restarting
149624feda00SLuis R. Rodriguez 	 * the device may at times not work immediately. Propagate
149724feda00SLuis R. Rodriguez 	 * the error.
149824feda00SLuis R. Rodriguez 	 */
149924487981SJohannes Berg 	res = drv_start(local);
150024feda00SLuis R. Rodriguez 	if (res) {
1501c7a00dc7SJohn W. Linville 		WARN(local->suspended, "Hardware became unavailable "
1502c7a00dc7SJohn W. Linville 		     "upon resume. This could be a software issue "
1503c7a00dc7SJohn W. Linville 		     "prior to suspend or a hardware issue.\n");
150424feda00SLuis R. Rodriguez 		return res;
150524feda00SLuis R. Rodriguez 	}
1506f2753ddbSJohannes Berg 
15077f281975SYogesh Ashok Powar 	/* setup fragmentation threshold */
15087f281975SYogesh Ashok Powar 	drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
15097f281975SYogesh Ashok Powar 
15107f281975SYogesh Ashok Powar 	/* setup RTS threshold */
15117f281975SYogesh Ashok Powar 	drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
15127f281975SYogesh Ashok Powar 
15137f281975SYogesh Ashok Powar 	/* reset coverage class */
15147f281975SYogesh Ashok Powar 	drv_set_coverage_class(local, hw->wiphy->coverage_class);
15157f281975SYogesh Ashok Powar 
15161f87f7d3SJohannes Berg 	ieee80211_led_radio(local, true);
151767408c8cSJohannes Berg 	ieee80211_mod_tpt_led_trig(local,
151867408c8cSJohannes Berg 				   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
1519f2753ddbSJohannes Berg 
1520f2753ddbSJohannes Berg 	/* add interfaces */
15214b6f1dd6SJohannes Berg 	sdata = rtnl_dereference(local->monitor_sdata);
15224b6f1dd6SJohannes Berg 	if (sdata) {
15233c3e21e7SJohannes Berg 		/* in HW restart it exists already */
15243c3e21e7SJohannes Berg 		WARN_ON(local->resuming);
15254b6f1dd6SJohannes Berg 		res = drv_add_interface(local, sdata);
15264b6f1dd6SJohannes Berg 		if (WARN_ON(res)) {
15274b6f1dd6SJohannes Berg 			rcu_assign_pointer(local->monitor_sdata, NULL);
15284b6f1dd6SJohannes Berg 			synchronize_net();
15294b6f1dd6SJohannes Berg 			kfree(sdata);
15304b6f1dd6SJohannes Berg 		}
15314b6f1dd6SJohannes Berg 	}
15324b6f1dd6SJohannes Berg 
1533f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1534f2753ddbSJohannes Berg 		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
1535f2753ddbSJohannes Berg 		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
15361ed32e4fSJohannes Berg 		    ieee80211_sdata_running(sdata))
15377b7eab6fSJohannes Berg 			res = drv_add_interface(local, sdata);
1538f2753ddbSJohannes Berg 	}
1539f2753ddbSJohannes Berg 
154055de908aSJohannes Berg 	/* add channel contexts */
1541f0dea9c7SArend van Spriel 	if (local->use_chanctx) {
154255de908aSJohannes Berg 		mutex_lock(&local->chanctx_mtx);
154355de908aSJohannes Berg 		list_for_each_entry(ctx, &local->chanctx_list, list)
154455de908aSJohannes Berg 			WARN_ON(drv_add_chanctx(local, ctx));
154555de908aSJohannes Berg 		mutex_unlock(&local->chanctx_mtx);
1546f0dea9c7SArend van Spriel 	}
154755de908aSJohannes Berg 
15486352c87fSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
15496352c87fSJohannes Berg 		if (!ieee80211_sdata_running(sdata))
15506352c87fSJohannes Berg 			continue;
1551153a5fc4SStanislaw Gruszka 		ieee80211_assign_chanctx(local, sdata);
15526352c87fSJohannes Berg 	}
15536352c87fSJohannes Berg 
1554fe5f2559SJohannes Berg 	sdata = rtnl_dereference(local->monitor_sdata);
1555153a5fc4SStanislaw Gruszka 	if (sdata && ieee80211_sdata_running(sdata))
1556153a5fc4SStanislaw Gruszka 		ieee80211_assign_chanctx(local, sdata);
1557fe5f2559SJohannes Berg 
1558f2753ddbSJohannes Berg 	/* add STAs back */
155934e89507SJohannes Berg 	mutex_lock(&local->sta_mtx);
1560f2753ddbSJohannes Berg 	list_for_each_entry(sta, &local->sta_list, list) {
1561f09603a2SJohannes Berg 		enum ieee80211_sta_state state;
1562f09603a2SJohannes Berg 
15632e8d397eSArik Nemtsov 		if (!sta->uploaded)
15642e8d397eSArik Nemtsov 			continue;
15652e8d397eSArik Nemtsov 
15662e8d397eSArik Nemtsov 		/* AP-mode stations will be added later */
15672e8d397eSArik Nemtsov 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
15682e8d397eSArik Nemtsov 			continue;
15692e8d397eSArik Nemtsov 
1570f09603a2SJohannes Berg 		for (state = IEEE80211_STA_NOTEXIST;
1571bd34ab62SMeenakshi Venkataraman 		     state < sta->sta_state; state++)
15722e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
15732e8d397eSArik Nemtsov 					      state + 1));
1574f2753ddbSJohannes Berg 	}
157534e89507SJohannes Berg 	mutex_unlock(&local->sta_mtx);
1576f2753ddbSJohannes Berg 
15772683d65bSEliad Peller 	/* reconfigure tx conf */
157854bcbc69SJohannes Berg 	if (hw->queues >= IEEE80211_NUM_ACS) {
1579f6f3def3SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
1580f6f3def3SEliad Peller 			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
1581f6f3def3SEliad Peller 			    sdata->vif.type == NL80211_IFTYPE_MONITOR ||
1582f6f3def3SEliad Peller 			    !ieee80211_sdata_running(sdata))
1583f6f3def3SEliad Peller 				continue;
1584f6f3def3SEliad Peller 
158554bcbc69SJohannes Berg 			for (i = 0; i < IEEE80211_NUM_ACS; i++)
158654bcbc69SJohannes Berg 				drv_conf_tx(local, sdata, i,
158754bcbc69SJohannes Berg 					    &sdata->tx_conf[i]);
158854bcbc69SJohannes Berg 		}
1589f6f3def3SEliad Peller 	}
15902683d65bSEliad Peller 
1591f2753ddbSJohannes Berg 	/* reconfigure hardware */
1592f2753ddbSJohannes Berg 	ieee80211_hw_config(local, ~0);
1593f2753ddbSJohannes Berg 
1594f2753ddbSJohannes Berg 	ieee80211_configure_filter(local);
1595f2753ddbSJohannes Berg 
1596f2753ddbSJohannes Berg 	/* Finally also reconfigure all the BSS information */
1597f2753ddbSJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1598ac8dd506SJohannes Berg 		u32 changed;
1599ac8dd506SJohannes Berg 
16009607e6b6SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1601f2753ddbSJohannes Berg 			continue;
1602ac8dd506SJohannes Berg 
1603ac8dd506SJohannes Berg 		/* common change flags for all interface types */
1604ac8dd506SJohannes Berg 		changed = BSS_CHANGED_ERP_CTS_PROT |
1605ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_PREAMBLE |
1606ac8dd506SJohannes Berg 			  BSS_CHANGED_ERP_SLOT |
1607ac8dd506SJohannes Berg 			  BSS_CHANGED_HT |
1608ac8dd506SJohannes Berg 			  BSS_CHANGED_BASIC_RATES |
1609ac8dd506SJohannes Berg 			  BSS_CHANGED_BEACON_INT |
1610ac8dd506SJohannes Berg 			  BSS_CHANGED_BSSID |
16114ced3f74SJohannes Berg 			  BSS_CHANGED_CQM |
161255de47f6SEliad Peller 			  BSS_CHANGED_QOS |
16131ea6f9c0SJohannes Berg 			  BSS_CHANGED_IDLE |
16141ea6f9c0SJohannes Berg 			  BSS_CHANGED_TXPOWER;
1615ac8dd506SJohannes Berg 
1616f2753ddbSJohannes Berg 		switch (sdata->vif.type) {
1617f2753ddbSJohannes Berg 		case NL80211_IFTYPE_STATION:
16180d392e93SEliad Peller 			changed |= BSS_CHANGED_ASSOC |
1619ab095877SEliad Peller 				   BSS_CHANGED_ARP_FILTER |
1620ab095877SEliad Peller 				   BSS_CHANGED_PS;
1621c65dd147SEmmanuel Grumbach 
1622989c6505SAlexander Bondar 			/* Re-send beacon info report to the driver */
1623989c6505SAlexander Bondar 			if (sdata->u.mgd.have_beacon)
1624989c6505SAlexander Bondar 				changed |= BSS_CHANGED_BEACON_INFO;
1625c65dd147SEmmanuel Grumbach 
16268d61ffa5SJohannes Berg 			sdata_lock(sdata);
1627ac8dd506SJohannes Berg 			ieee80211_bss_info_change_notify(sdata, changed);
16288d61ffa5SJohannes Berg 			sdata_unlock(sdata);
1629ac8dd506SJohannes Berg 			break;
1630f2753ddbSJohannes Berg 		case NL80211_IFTYPE_ADHOC:
1631ac8dd506SJohannes Berg 			changed |= BSS_CHANGED_IBSS;
1632ac8dd506SJohannes Berg 			/* fall through */
1633f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP:
1634339afbf4SJohannes Berg 			changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
1635e7979ac7SArik Nemtsov 
16361041638fSJohannes Berg 			if (sdata->vif.type == NL80211_IFTYPE_AP) {
1637e7979ac7SArik Nemtsov 				changed |= BSS_CHANGED_AP_PROBE_RESP;
1638e7979ac7SArik Nemtsov 
16391041638fSJohannes Berg 				if (rcu_access_pointer(sdata->u.ap.beacon))
16401041638fSJohannes Berg 					drv_start_ap(local, sdata);
16411041638fSJohannes Berg 			}
16421041638fSJohannes Berg 
16437827493bSArik Nemtsov 			/* fall through */
1644f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
16458da34932SJohannes Berg 			if (sdata->vif.bss_conf.enable_beacon) {
1646ac8dd506SJohannes Berg 				changed |= BSS_CHANGED_BEACON |
1647ac8dd506SJohannes Berg 					   BSS_CHANGED_BEACON_ENABLED;
16482d0ddec5SJohannes Berg 				ieee80211_bss_info_change_notify(sdata, changed);
16498da34932SJohannes Berg 			}
1650f2753ddbSJohannes Berg 			break;
1651f2753ddbSJohannes Berg 		case NL80211_IFTYPE_WDS:
1652f2753ddbSJohannes Berg 			break;
1653f2753ddbSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
1654f2753ddbSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
1655f2753ddbSJohannes Berg 			/* ignore virtual */
1656f2753ddbSJohannes Berg 			break;
165798104fdeSJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
1658f142c6b9SJohannes Berg 			changed = BSS_CHANGED_IDLE;
1659f142c6b9SJohannes Berg 			break;
1660f2753ddbSJohannes Berg 		case NL80211_IFTYPE_UNSPECIFIED:
16612e161f78SJohannes Berg 		case NUM_NL80211_IFTYPES:
16622ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_CLIENT:
16632ca27bcfSJohannes Berg 		case NL80211_IFTYPE_P2P_GO:
1664f2753ddbSJohannes Berg 			WARN_ON(1);
1665f2753ddbSJohannes Berg 			break;
1666f2753ddbSJohannes Berg 		}
1667f2753ddbSJohannes Berg 	}
1668f2753ddbSJohannes Berg 
16698e1b23b9SEyal Shapira 	ieee80211_recalc_ps(local, -1);
16708e1b23b9SEyal Shapira 
16712a419056SJohannes Berg 	/*
16726e1b1b24SEliad Peller 	 * The sta might be in psm against the ap (e.g. because
16736e1b1b24SEliad Peller 	 * this was the state before a hw restart), so we
16746e1b1b24SEliad Peller 	 * explicitly send a null packet in order to make sure
16756e1b1b24SEliad Peller 	 * it'll sync against the ap (and get out of psm).
16766e1b1b24SEliad Peller 	 */
16776e1b1b24SEliad Peller 	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
16786e1b1b24SEliad Peller 		list_for_each_entry(sdata, &local->interfaces, list) {
16796e1b1b24SEliad Peller 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
16806e1b1b24SEliad Peller 				continue;
168120f544eeSJohannes Berg 			if (!sdata->u.mgd.associated)
168220f544eeSJohannes Berg 				continue;
16836e1b1b24SEliad Peller 
16846e1b1b24SEliad Peller 			ieee80211_send_nullfunc(local, sdata, 0);
16856e1b1b24SEliad Peller 		}
16866e1b1b24SEliad Peller 	}
16876e1b1b24SEliad Peller 
16882e8d397eSArik Nemtsov 	/* APs are now beaconing, add back stations */
16892e8d397eSArik Nemtsov 	mutex_lock(&local->sta_mtx);
16902e8d397eSArik Nemtsov 	list_for_each_entry(sta, &local->sta_list, list) {
16912e8d397eSArik Nemtsov 		enum ieee80211_sta_state state;
16922e8d397eSArik Nemtsov 
16932e8d397eSArik Nemtsov 		if (!sta->uploaded)
16942e8d397eSArik Nemtsov 			continue;
16952e8d397eSArik Nemtsov 
16962e8d397eSArik Nemtsov 		if (sta->sdata->vif.type != NL80211_IFTYPE_AP)
16972e8d397eSArik Nemtsov 			continue;
16982e8d397eSArik Nemtsov 
16992e8d397eSArik Nemtsov 		for (state = IEEE80211_STA_NOTEXIST;
17002e8d397eSArik Nemtsov 		     state < sta->sta_state; state++)
17012e8d397eSArik Nemtsov 			WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
17022e8d397eSArik Nemtsov 					      state + 1));
17032e8d397eSArik Nemtsov 	}
17042e8d397eSArik Nemtsov 	mutex_unlock(&local->sta_mtx);
17052e8d397eSArik Nemtsov 
17067b21aea0SEyal Shapira 	/* add back keys */
17077b21aea0SEyal Shapira 	list_for_each_entry(sdata, &local->interfaces, list)
17087b21aea0SEyal Shapira 		if (ieee80211_sdata_running(sdata))
17097b21aea0SEyal Shapira 			ieee80211_enable_keys(sdata);
17107b21aea0SEyal Shapira 
1711c6209488SEliad Peller  wake_up:
171204800adaSArik Nemtsov 	local->in_reconfig = false;
171304800adaSArik Nemtsov 	barrier();
171404800adaSArik Nemtsov 
17153c3e21e7SJohannes Berg 	if (local->monitors == local->open_count && local->monitors > 0)
17163c3e21e7SJohannes Berg 		ieee80211_add_virtual_monitor(local);
17173c3e21e7SJohannes Berg 
17186e1b1b24SEliad Peller 	/*
17192a419056SJohannes Berg 	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
17202a419056SJohannes Berg 	 * sessions can be established after a resume.
17212a419056SJohannes Berg 	 *
17222a419056SJohannes Berg 	 * Also tear down aggregation sessions since reconfiguring
17232a419056SJohannes Berg 	 * them in a hardware restart scenario is not easily done
17242a419056SJohannes Berg 	 * right now, and the hardware will have lost information
17252a419056SJohannes Berg 	 * about the sessions, but we and the AP still think they
17262a419056SJohannes Berg 	 * are active. This is really a workaround though.
17272a419056SJohannes Berg 	 */
172874e2bd1fSWey-Yi Guy 	if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
17292a419056SJohannes Berg 		mutex_lock(&local->sta_mtx);
17302a419056SJohannes Berg 
17312a419056SJohannes Berg 		list_for_each_entry(sta, &local->sta_list, list) {
1732c82c4a80SJohannes Berg 			ieee80211_sta_tear_down_BA_sessions(
1733c82c4a80SJohannes Berg 					sta, AGG_STOP_LOCAL_REQUEST);
1734c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
173574e2bd1fSWey-Yi Guy 		}
17362a419056SJohannes Berg 
17372a419056SJohannes Berg 		mutex_unlock(&local->sta_mtx);
173874e2bd1fSWey-Yi Guy 	}
173974e2bd1fSWey-Yi Guy 
1740445ea4e8SJohannes Berg 	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
1741f2753ddbSJohannes Berg 					IEEE80211_QUEUE_STOP_REASON_SUSPEND);
1742f2753ddbSJohannes Berg 
17435bb644a0SJohannes Berg 	/*
17445bb644a0SJohannes Berg 	 * If this is for hw restart things are still running.
17455bb644a0SJohannes Berg 	 * We may want to change that later, however.
17465bb644a0SJohannes Berg 	 */
17478f21b0adSJohannes Berg 	if (!local->suspended || reconfig_due_to_wowlan)
17489214ad7fSJohannes Berg 		drv_restart_complete(local);
17498f21b0adSJohannes Berg 
17508f21b0adSJohannes Berg 	if (!local->suspended)
17515bb644a0SJohannes Berg 		return 0;
17525bb644a0SJohannes Berg 
17535bb644a0SJohannes Berg #ifdef CONFIG_PM
1754ceb99fe0SJohannes Berg 	/* first set suspended false, then resuming */
17555bb644a0SJohannes Berg 	local->suspended = false;
1756ceb99fe0SJohannes Berg 	mb();
1757ceb99fe0SJohannes Berg 	local->resuming = false;
17585bb644a0SJohannes Berg 
1759b8360ab8SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
1760b8360ab8SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
1761b8360ab8SJohannes Berg 			continue;
1762b8360ab8SJohannes Berg 		if (sdata->vif.type == NL80211_IFTYPE_STATION)
1763b8360ab8SJohannes Berg 			ieee80211_sta_restart(sdata);
1764b8360ab8SJohannes Berg 	}
1765b8360ab8SJohannes Berg 
176626d59535SJohannes Berg 	mod_timer(&local->sta_cleanup, jiffies + 1);
17675bb644a0SJohannes Berg #else
17685bb644a0SJohannes Berg 	WARN_ON(1);
17695bb644a0SJohannes Berg #endif
1770d43c6b6eSDavid Spinadel 
1771d43c6b6eSDavid Spinadel 	/*
1772d43c6b6eSDavid Spinadel 	 * Reconfigure sched scan if it was interrupted by FW restart or
1773d43c6b6eSDavid Spinadel 	 * suspend.
1774d43c6b6eSDavid Spinadel 	 */
1775d43c6b6eSDavid Spinadel 	mutex_lock(&local->mtx);
1776d43c6b6eSDavid Spinadel 	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
1777d43c6b6eSDavid Spinadel 						lockdep_is_held(&local->mtx));
1778d43c6b6eSDavid Spinadel 	if (sched_scan_sdata && local->sched_scan_req)
1779d43c6b6eSDavid Spinadel 		/*
1780d43c6b6eSDavid Spinadel 		 * Sched scan stopped, but we don't want to report it. Instead,
1781d43c6b6eSDavid Spinadel 		 * we're trying to reschedule.
1782d43c6b6eSDavid Spinadel 		 */
1783d43c6b6eSDavid Spinadel 		if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
1784d43c6b6eSDavid Spinadel 							 local->sched_scan_req))
1785d43c6b6eSDavid Spinadel 			sched_scan_stopped = true;
1786d43c6b6eSDavid Spinadel 	mutex_unlock(&local->mtx);
1787d43c6b6eSDavid Spinadel 
1788d43c6b6eSDavid Spinadel 	if (sched_scan_stopped)
1789d43c6b6eSDavid Spinadel 		cfg80211_sched_scan_stopped(local->hw.wiphy);
1790d43c6b6eSDavid Spinadel 
1791f2753ddbSJohannes Berg 	return 0;
1792f2753ddbSJohannes Berg }
179342935ecaSLuis R. Rodriguez 
179495acac61SJohannes Berg void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
179595acac61SJohannes Berg {
179695acac61SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
179795acac61SJohannes Berg 	struct ieee80211_local *local;
179895acac61SJohannes Berg 	struct ieee80211_key *key;
179995acac61SJohannes Berg 
180095acac61SJohannes Berg 	if (WARN_ON(!vif))
180195acac61SJohannes Berg 		return;
180295acac61SJohannes Berg 
180395acac61SJohannes Berg 	sdata = vif_to_sdata(vif);
180495acac61SJohannes Berg 	local = sdata->local;
180595acac61SJohannes Berg 
180695acac61SJohannes Berg 	if (WARN_ON(!local->resuming))
180795acac61SJohannes Berg 		return;
180895acac61SJohannes Berg 
180995acac61SJohannes Berg 	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
181095acac61SJohannes Berg 		return;
181195acac61SJohannes Berg 
181295acac61SJohannes Berg 	sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME;
181395acac61SJohannes Berg 
181495acac61SJohannes Berg 	mutex_lock(&local->key_mtx);
181595acac61SJohannes Berg 	list_for_each_entry(key, &sdata->key_list, list)
181695acac61SJohannes Berg 		key->flags |= KEY_FLAG_TAINTED;
181795acac61SJohannes Berg 	mutex_unlock(&local->key_mtx);
181895acac61SJohannes Berg }
181995acac61SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
182095acac61SJohannes Berg 
182104ecd257SJohannes Berg void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
18220f78231bSJohannes Berg {
182304ecd257SJohannes Berg 	struct ieee80211_local *local = sdata->local;
182404ecd257SJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
182504ecd257SJohannes Berg 	struct ieee80211_chanctx *chanctx;
18260f78231bSJohannes Berg 
182704ecd257SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
18280f78231bSJohannes Berg 
182904ecd257SJohannes Berg 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
183004ecd257SJohannes Berg 					lockdep_is_held(&local->chanctx_mtx));
18310f78231bSJohannes Berg 
183204ecd257SJohannes Berg 	if (WARN_ON_ONCE(!chanctx_conf))
18335d8e4237SJohannes Berg 		goto unlock;
18340f78231bSJohannes Berg 
183504ecd257SJohannes Berg 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
183604ecd257SJohannes Berg 	ieee80211_recalc_smps_chanctx(local, chanctx);
18375d8e4237SJohannes Berg  unlock:
183804ecd257SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
18390f78231bSJohannes Berg }
18408e664fb3SJohannes Berg 
184121f659bfSEliad Peller void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
184221f659bfSEliad Peller {
184321f659bfSEliad Peller 	struct ieee80211_local *local = sdata->local;
184421f659bfSEliad Peller 	struct ieee80211_chanctx_conf *chanctx_conf;
184521f659bfSEliad Peller 	struct ieee80211_chanctx *chanctx;
184621f659bfSEliad Peller 
184721f659bfSEliad Peller 	mutex_lock(&local->chanctx_mtx);
184821f659bfSEliad Peller 
184921f659bfSEliad Peller 	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
185021f659bfSEliad Peller 					lockdep_is_held(&local->chanctx_mtx));
185121f659bfSEliad Peller 
185221f659bfSEliad Peller 	if (WARN_ON_ONCE(!chanctx_conf))
185321f659bfSEliad Peller 		goto unlock;
185421f659bfSEliad Peller 
185521f659bfSEliad Peller 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
185621f659bfSEliad Peller 	ieee80211_recalc_chanctx_min_def(local, chanctx);
185721f659bfSEliad Peller  unlock:
185821f659bfSEliad Peller 	mutex_unlock(&local->chanctx_mtx);
185921f659bfSEliad Peller }
186021f659bfSEliad Peller 
18618e664fb3SJohannes Berg static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
18628e664fb3SJohannes Berg {
18638e664fb3SJohannes Berg 	int i;
18648e664fb3SJohannes Berg 
18658e664fb3SJohannes Berg 	for (i = 0; i < n_ids; i++)
18668e664fb3SJohannes Berg 		if (ids[i] == id)
18678e664fb3SJohannes Berg 			return true;
18688e664fb3SJohannes Berg 	return false;
18698e664fb3SJohannes Berg }
18708e664fb3SJohannes Berg 
18718e664fb3SJohannes Berg /**
18728e664fb3SJohannes Berg  * ieee80211_ie_split - split an IE buffer according to ordering
18738e664fb3SJohannes Berg  *
18748e664fb3SJohannes Berg  * @ies: the IE buffer
18758e664fb3SJohannes Berg  * @ielen: the length of the IE buffer
18768e664fb3SJohannes Berg  * @ids: an array with element IDs that are allowed before
18778e664fb3SJohannes Berg  *	the split
18788e664fb3SJohannes Berg  * @n_ids: the size of the element ID array
18798e664fb3SJohannes Berg  * @offset: offset where to start splitting in the buffer
18808e664fb3SJohannes Berg  *
18818e664fb3SJohannes Berg  * This function splits an IE buffer by updating the @offset
18828e664fb3SJohannes Berg  * variable to point to the location where the buffer should be
18838e664fb3SJohannes Berg  * split.
18848e664fb3SJohannes Berg  *
18858e664fb3SJohannes Berg  * It assumes that the given IE buffer is well-formed, this
18868e664fb3SJohannes Berg  * has to be guaranteed by the caller!
18878e664fb3SJohannes Berg  *
18888e664fb3SJohannes Berg  * It also assumes that the IEs in the buffer are ordered
18898e664fb3SJohannes Berg  * correctly, if not the result of using this function will not
18908e664fb3SJohannes Berg  * be ordered correctly either, i.e. it does no reordering.
18918e664fb3SJohannes Berg  *
18928e664fb3SJohannes Berg  * The function returns the offset where the next part of the
18938e664fb3SJohannes Berg  * buffer starts, which may be @ielen if the entire (remainder)
18948e664fb3SJohannes Berg  * of the buffer should be used.
18958e664fb3SJohannes Berg  */
18968e664fb3SJohannes Berg size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
18978e664fb3SJohannes Berg 			  const u8 *ids, int n_ids, size_t offset)
18988e664fb3SJohannes Berg {
18998e664fb3SJohannes Berg 	size_t pos = offset;
19008e664fb3SJohannes Berg 
19018e664fb3SJohannes Berg 	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
19028e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
19038e664fb3SJohannes Berg 
19048e664fb3SJohannes Berg 	return pos;
19058e664fb3SJohannes Berg }
19068e664fb3SJohannes Berg 
19078e664fb3SJohannes Berg size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
19088e664fb3SJohannes Berg {
19098e664fb3SJohannes Berg 	size_t pos = offset;
19108e664fb3SJohannes Berg 
19118e664fb3SJohannes Berg 	while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
19128e664fb3SJohannes Berg 		pos += 2 + ies[pos + 1];
19138e664fb3SJohannes Berg 
19148e664fb3SJohannes Berg 	return pos;
19158e664fb3SJohannes Berg }
1916615f7b9bSMeenakshi Venkataraman 
1917615f7b9bSMeenakshi Venkataraman static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata,
1918615f7b9bSMeenakshi Venkataraman 					    int rssi_min_thold,
1919615f7b9bSMeenakshi Venkataraman 					    int rssi_max_thold)
1920615f7b9bSMeenakshi Venkataraman {
1921615f7b9bSMeenakshi Venkataraman 	trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold);
1922615f7b9bSMeenakshi Venkataraman 
1923615f7b9bSMeenakshi Venkataraman 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
1924615f7b9bSMeenakshi Venkataraman 		return;
1925615f7b9bSMeenakshi Venkataraman 
1926615f7b9bSMeenakshi Venkataraman 	/*
1927615f7b9bSMeenakshi Venkataraman 	 * Scale up threshold values before storing it, as the RSSI averaging
1928615f7b9bSMeenakshi Venkataraman 	 * algorithm uses a scaled up value as well. Change this scaling
1929615f7b9bSMeenakshi Venkataraman 	 * factor if the RSSI averaging algorithm changes.
1930615f7b9bSMeenakshi Venkataraman 	 */
1931615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_min_thold = rssi_min_thold*16;
1932615f7b9bSMeenakshi Venkataraman 	sdata->u.mgd.rssi_max_thold = rssi_max_thold*16;
1933615f7b9bSMeenakshi Venkataraman }
1934615f7b9bSMeenakshi Venkataraman 
1935615f7b9bSMeenakshi Venkataraman void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
1936615f7b9bSMeenakshi Venkataraman 				    int rssi_min_thold,
1937615f7b9bSMeenakshi Venkataraman 				    int rssi_max_thold)
1938615f7b9bSMeenakshi Venkataraman {
1939615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
1940615f7b9bSMeenakshi Venkataraman 
1941615f7b9bSMeenakshi Venkataraman 	WARN_ON(rssi_min_thold == rssi_max_thold ||
1942615f7b9bSMeenakshi Venkataraman 		rssi_min_thold > rssi_max_thold);
1943615f7b9bSMeenakshi Venkataraman 
1944615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, rssi_min_thold,
1945615f7b9bSMeenakshi Venkataraman 				       rssi_max_thold);
1946615f7b9bSMeenakshi Venkataraman }
1947615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_enable_rssi_reports);
1948615f7b9bSMeenakshi Venkataraman 
1949615f7b9bSMeenakshi Venkataraman void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
1950615f7b9bSMeenakshi Venkataraman {
1951615f7b9bSMeenakshi Venkataraman 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
1952615f7b9bSMeenakshi Venkataraman 
1953615f7b9bSMeenakshi Venkataraman 	_ieee80211_enable_rssi_reports(sdata, 0, 0);
1954615f7b9bSMeenakshi Venkataraman }
1955615f7b9bSMeenakshi Venkataraman EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
1956768db343SArik Nemtsov 
1957ef96a842SBen Greear u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
195842e7aa77SAlexander Simon 			      u16 cap)
195942e7aa77SAlexander Simon {
196042e7aa77SAlexander Simon 	__le16 tmp;
196142e7aa77SAlexander Simon 
196242e7aa77SAlexander Simon 	*pos++ = WLAN_EID_HT_CAPABILITY;
196342e7aa77SAlexander Simon 	*pos++ = sizeof(struct ieee80211_ht_cap);
196442e7aa77SAlexander Simon 	memset(pos, 0, sizeof(struct ieee80211_ht_cap));
196542e7aa77SAlexander Simon 
196642e7aa77SAlexander Simon 	/* capability flags */
196742e7aa77SAlexander Simon 	tmp = cpu_to_le16(cap);
196842e7aa77SAlexander Simon 	memcpy(pos, &tmp, sizeof(u16));
196942e7aa77SAlexander Simon 	pos += sizeof(u16);
197042e7aa77SAlexander Simon 
197142e7aa77SAlexander Simon 	/* AMPDU parameters */
1972ef96a842SBen Greear 	*pos++ = ht_cap->ampdu_factor |
1973ef96a842SBen Greear 		 (ht_cap->ampdu_density <<
197442e7aa77SAlexander Simon 			IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
197542e7aa77SAlexander Simon 
197642e7aa77SAlexander Simon 	/* MCS set */
1977ef96a842SBen Greear 	memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
1978ef96a842SBen Greear 	pos += sizeof(ht_cap->mcs);
197942e7aa77SAlexander Simon 
198042e7aa77SAlexander Simon 	/* extended capabilities */
198142e7aa77SAlexander Simon 	pos += sizeof(__le16);
198242e7aa77SAlexander Simon 
198342e7aa77SAlexander Simon 	/* BF capabilities */
198442e7aa77SAlexander Simon 	pos += sizeof(__le32);
198542e7aa77SAlexander Simon 
198642e7aa77SAlexander Simon 	/* antenna selection */
198742e7aa77SAlexander Simon 	pos += sizeof(u8);
198842e7aa77SAlexander Simon 
198942e7aa77SAlexander Simon 	return pos;
199042e7aa77SAlexander Simon }
199142e7aa77SAlexander Simon 
1992ba0afa2fSMahesh Palivela u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
1993ba0afa2fSMahesh Palivela 			       u32 cap)
1994ba0afa2fSMahesh Palivela {
1995ba0afa2fSMahesh Palivela 	__le32 tmp;
1996ba0afa2fSMahesh Palivela 
1997ba0afa2fSMahesh Palivela 	*pos++ = WLAN_EID_VHT_CAPABILITY;
1998d4950281SMahesh Palivela 	*pos++ = sizeof(struct ieee80211_vht_cap);
1999d4950281SMahesh Palivela 	memset(pos, 0, sizeof(struct ieee80211_vht_cap));
2000ba0afa2fSMahesh Palivela 
2001ba0afa2fSMahesh Palivela 	/* capability flags */
2002ba0afa2fSMahesh Palivela 	tmp = cpu_to_le32(cap);
2003ba0afa2fSMahesh Palivela 	memcpy(pos, &tmp, sizeof(u32));
2004ba0afa2fSMahesh Palivela 	pos += sizeof(u32);
2005ba0afa2fSMahesh Palivela 
2006ba0afa2fSMahesh Palivela 	/* VHT MCS set */
2007ba0afa2fSMahesh Palivela 	memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
2008ba0afa2fSMahesh Palivela 	pos += sizeof(vht_cap->vht_mcs);
2009ba0afa2fSMahesh Palivela 
2010ba0afa2fSMahesh Palivela 	return pos;
2011ba0afa2fSMahesh Palivela }
2012ba0afa2fSMahesh Palivela 
2013074d46d1SJohannes Berg u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
20144bf88530SJohannes Berg 			       const struct cfg80211_chan_def *chandef,
2015431e3154SAshok Nagarajan 			       u16 prot_mode)
201642e7aa77SAlexander Simon {
2017074d46d1SJohannes Berg 	struct ieee80211_ht_operation *ht_oper;
201842e7aa77SAlexander Simon 	/* Build HT Information */
2019074d46d1SJohannes Berg 	*pos++ = WLAN_EID_HT_OPERATION;
2020074d46d1SJohannes Berg 	*pos++ = sizeof(struct ieee80211_ht_operation);
2021074d46d1SJohannes Berg 	ht_oper = (struct ieee80211_ht_operation *)pos;
20224bf88530SJohannes Berg 	ht_oper->primary_chan = ieee80211_frequency_to_channel(
20234bf88530SJohannes Berg 					chandef->chan->center_freq);
20244bf88530SJohannes Berg 	switch (chandef->width) {
20254bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
20264bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
20274bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_80:
20284bf88530SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
20294bf88530SJohannes Berg 		if (chandef->center_freq1 > chandef->chan->center_freq)
20304bf88530SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
20314bf88530SJohannes Berg 		else
2032074d46d1SJohannes Berg 			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
203342e7aa77SAlexander Simon 		break;
203442e7aa77SAlexander Simon 	default:
2035074d46d1SJohannes Berg 		ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
203642e7aa77SAlexander Simon 		break;
203742e7aa77SAlexander Simon 	}
2038aee286c2SThomas Pedersen 	if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
20394bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
20404bf88530SJohannes Berg 	    chandef->width != NL80211_CHAN_WIDTH_20)
2041074d46d1SJohannes Berg 		ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
2042ff3cc5f4SSimon Wunderlich 
2043431e3154SAshok Nagarajan 	ht_oper->operation_mode = cpu_to_le16(prot_mode);
2044074d46d1SJohannes Berg 	ht_oper->stbc_param = 0x0000;
204542e7aa77SAlexander Simon 
204642e7aa77SAlexander Simon 	/* It seems that Basic MCS set and Supported MCS set
204742e7aa77SAlexander Simon 	   are identical for the first 10 bytes */
2048074d46d1SJohannes Berg 	memset(&ht_oper->basic_set, 0, 16);
2049074d46d1SJohannes Berg 	memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
205042e7aa77SAlexander Simon 
2051074d46d1SJohannes Berg 	return pos + sizeof(struct ieee80211_ht_operation);
205242e7aa77SAlexander Simon }
205342e7aa77SAlexander Simon 
20544bf88530SJohannes Berg void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
20554a3cb702SJohannes Berg 				  const struct ieee80211_ht_operation *ht_oper,
20564bf88530SJohannes Berg 				  struct cfg80211_chan_def *chandef)
205742e7aa77SAlexander Simon {
205842e7aa77SAlexander Simon 	enum nl80211_channel_type channel_type;
205942e7aa77SAlexander Simon 
20604bf88530SJohannes Berg 	if (!ht_oper) {
20614bf88530SJohannes Berg 		cfg80211_chandef_create(chandef, control_chan,
20624bf88530SJohannes Berg 					NL80211_CHAN_NO_HT);
20634bf88530SJohannes Berg 		return;
20644bf88530SJohannes Berg 	}
206542e7aa77SAlexander Simon 
2066074d46d1SJohannes Berg 	switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
206742e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
206842e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT20;
206942e7aa77SAlexander Simon 		break;
207042e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
207142e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40PLUS;
207242e7aa77SAlexander Simon 		break;
207342e7aa77SAlexander Simon 	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
207442e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_HT40MINUS;
207542e7aa77SAlexander Simon 		break;
207642e7aa77SAlexander Simon 	default:
207742e7aa77SAlexander Simon 		channel_type = NL80211_CHAN_NO_HT;
207842e7aa77SAlexander Simon 	}
207942e7aa77SAlexander Simon 
20804bf88530SJohannes Berg 	cfg80211_chandef_create(chandef, control_chan, channel_type);
208142e7aa77SAlexander Simon }
208242e7aa77SAlexander Simon 
20832103dec1SSimon Wunderlich int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
20842103dec1SSimon Wunderlich 			     const struct ieee80211_supported_band *sband,
20852103dec1SSimon Wunderlich 			     const u8 *srates, int srates_len, u32 *rates)
20862103dec1SSimon Wunderlich {
20872103dec1SSimon Wunderlich 	u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
20882103dec1SSimon Wunderlich 	int shift = ieee80211_chandef_get_shift(chandef);
20892103dec1SSimon Wunderlich 	struct ieee80211_rate *br;
20902103dec1SSimon Wunderlich 	int brate, rate, i, j, count = 0;
20912103dec1SSimon Wunderlich 
20922103dec1SSimon Wunderlich 	*rates = 0;
20932103dec1SSimon Wunderlich 
20942103dec1SSimon Wunderlich 	for (i = 0; i < srates_len; i++) {
20952103dec1SSimon Wunderlich 		rate = srates[i] & 0x7f;
20962103dec1SSimon Wunderlich 
20972103dec1SSimon Wunderlich 		for (j = 0; j < sband->n_bitrates; j++) {
20982103dec1SSimon Wunderlich 			br = &sband->bitrates[j];
20992103dec1SSimon Wunderlich 			if ((rate_flags & br->flags) != rate_flags)
21002103dec1SSimon Wunderlich 				continue;
21012103dec1SSimon Wunderlich 
21022103dec1SSimon Wunderlich 			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
21032103dec1SSimon Wunderlich 			if (brate == rate) {
21042103dec1SSimon Wunderlich 				*rates |= BIT(j);
21052103dec1SSimon Wunderlich 				count++;
21062103dec1SSimon Wunderlich 				break;
21072103dec1SSimon Wunderlich 			}
21082103dec1SSimon Wunderlich 		}
21092103dec1SSimon Wunderlich 	}
21102103dec1SSimon Wunderlich 	return count;
21112103dec1SSimon Wunderlich }
21122103dec1SSimon Wunderlich 
2113fc8a7321SJohannes Berg int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
21146b77863bSJohannes Berg 			    struct sk_buff *skb, bool need_basic,
21156b77863bSJohannes Berg 			    enum ieee80211_band band)
2116768db343SArik Nemtsov {
2117768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2118768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
21192103dec1SSimon Wunderlich 	int rate, shift;
2120768db343SArik Nemtsov 	u8 i, rates, *pos;
2121fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
21222103dec1SSimon Wunderlich 	u32 rate_flags;
2123768db343SArik Nemtsov 
21242103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
21252103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
21266b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
21272103dec1SSimon Wunderlich 	rates = 0;
21282103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
21292103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
21302103dec1SSimon Wunderlich 			continue;
21312103dec1SSimon Wunderlich 		rates++;
21322103dec1SSimon Wunderlich 	}
2133768db343SArik Nemtsov 	if (rates > 8)
2134768db343SArik Nemtsov 		rates = 8;
2135768db343SArik Nemtsov 
2136768db343SArik Nemtsov 	if (skb_tailroom(skb) < rates + 2)
2137768db343SArik Nemtsov 		return -ENOMEM;
2138768db343SArik Nemtsov 
2139768db343SArik Nemtsov 	pos = skb_put(skb, rates + 2);
2140768db343SArik Nemtsov 	*pos++ = WLAN_EID_SUPP_RATES;
2141768db343SArik Nemtsov 	*pos++ = rates;
2142768db343SArik Nemtsov 	for (i = 0; i < rates; i++) {
2143657c3e0cSAshok Nagarajan 		u8 basic = 0;
21442103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
21452103dec1SSimon Wunderlich 			continue;
21462103dec1SSimon Wunderlich 
2147657c3e0cSAshok Nagarajan 		if (need_basic && basic_rates & BIT(i))
2148657c3e0cSAshok Nagarajan 			basic = 0x80;
2149768db343SArik Nemtsov 		rate = sband->bitrates[i].bitrate;
21502103dec1SSimon Wunderlich 		rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
21512103dec1SSimon Wunderlich 				    5 * (1 << shift));
21522103dec1SSimon Wunderlich 		*pos++ = basic | (u8) rate;
2153768db343SArik Nemtsov 	}
2154768db343SArik Nemtsov 
2155768db343SArik Nemtsov 	return 0;
2156768db343SArik Nemtsov }
2157768db343SArik Nemtsov 
2158fc8a7321SJohannes Berg int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
21596b77863bSJohannes Berg 				struct sk_buff *skb, bool need_basic,
21606b77863bSJohannes Berg 				enum ieee80211_band band)
2161768db343SArik Nemtsov {
2162768db343SArik Nemtsov 	struct ieee80211_local *local = sdata->local;
2163768db343SArik Nemtsov 	struct ieee80211_supported_band *sband;
2164cc63ec76SChun-Yeow Yeoh 	int rate, shift;
2165768db343SArik Nemtsov 	u8 i, exrates, *pos;
2166fc8a7321SJohannes Berg 	u32 basic_rates = sdata->vif.bss_conf.basic_rates;
21672103dec1SSimon Wunderlich 	u32 rate_flags;
21682103dec1SSimon Wunderlich 
21692103dec1SSimon Wunderlich 	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
21702103dec1SSimon Wunderlich 	shift = ieee80211_vif_get_shift(&sdata->vif);
2171768db343SArik Nemtsov 
21726b77863bSJohannes Berg 	sband = local->hw.wiphy->bands[band];
21732103dec1SSimon Wunderlich 	exrates = 0;
21742103dec1SSimon Wunderlich 	for (i = 0; i < sband->n_bitrates; i++) {
21752103dec1SSimon Wunderlich 		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
21762103dec1SSimon Wunderlich 			continue;
21772103dec1SSimon Wunderlich 		exrates++;
21782103dec1SSimon Wunderlich 	}
21792103dec1SSimon Wunderlich 
2180768db343SArik Nemtsov 	if (exrates > 8)
2181768db343SArik Nemtsov 		exrates -= 8;
2182768db343SArik Nemtsov 	else
2183768db343SArik Nemtsov 		exrates = 0;
2184768db343SArik Nemtsov 
2185768db343SArik Nemtsov 	if (skb_tailroom(skb) < exrates + 2)
2186768db343SArik Nemtsov 		return -ENOMEM;
2187768db343SArik Nemtsov 
2188768db343SArik Nemtsov 	if (exrates) {
2189768db343SArik Nemtsov 		pos = skb_put(skb, exrates + 2);
2190768db343SArik Nemtsov 		*pos++ = WLAN_EID_EXT_SUPP_RATES;
2191768db343SArik Nemtsov 		*pos++ = exrates;
2192768db343SArik Nemtsov 		for (i = 8; i < sband->n_bitrates; i++) {
2193657c3e0cSAshok Nagarajan 			u8 basic = 0;
21942103dec1SSimon Wunderlich 			if ((rate_flags & sband->bitrates[i].flags)
21952103dec1SSimon Wunderlich 			    != rate_flags)
21962103dec1SSimon Wunderlich 				continue;
2197657c3e0cSAshok Nagarajan 			if (need_basic && basic_rates & BIT(i))
2198657c3e0cSAshok Nagarajan 				basic = 0x80;
21992103dec1SSimon Wunderlich 			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
22002103dec1SSimon Wunderlich 					    5 * (1 << shift));
22012103dec1SSimon Wunderlich 			*pos++ = basic | (u8) rate;
2202768db343SArik Nemtsov 		}
2203768db343SArik Nemtsov 	}
2204768db343SArik Nemtsov 	return 0;
2205768db343SArik Nemtsov }
22061dae27f8SWey-Yi Guy 
22071dae27f8SWey-Yi Guy int ieee80211_ave_rssi(struct ieee80211_vif *vif)
22081dae27f8SWey-Yi Guy {
22091dae27f8SWey-Yi Guy 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
22101dae27f8SWey-Yi Guy 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
22111dae27f8SWey-Yi Guy 
2212be6bcabcSWey-Yi Guy 	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) {
2213be6bcabcSWey-Yi Guy 		/* non-managed type inferfaces */
2214be6bcabcSWey-Yi Guy 		return 0;
2215be6bcabcSWey-Yi Guy 	}
22163a7bba64SEmmanuel Grumbach 	return ifmgd->ave_beacon_signal / 16;
22171dae27f8SWey-Yi Guy }
22180d8a0a17SWey-Yi Guy EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
221904ecd257SJohannes Berg 
222004ecd257SJohannes Berg u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
222104ecd257SJohannes Berg {
222204ecd257SJohannes Berg 	if (!mcs)
222304ecd257SJohannes Berg 		return 1;
222404ecd257SJohannes Berg 
222504ecd257SJohannes Berg 	/* TODO: consider rx_highest */
222604ecd257SJohannes Berg 
222704ecd257SJohannes Berg 	if (mcs->rx_mask[3])
222804ecd257SJohannes Berg 		return 4;
222904ecd257SJohannes Berg 	if (mcs->rx_mask[2])
223004ecd257SJohannes Berg 		return 3;
223104ecd257SJohannes Berg 	if (mcs->rx_mask[1])
223204ecd257SJohannes Berg 		return 2;
223304ecd257SJohannes Berg 	return 1;
223404ecd257SJohannes Berg }
2235f4bda337SThomas Pedersen 
2236f4bda337SThomas Pedersen /**
2237f4bda337SThomas Pedersen  * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
2238f4bda337SThomas Pedersen  * @local: mac80211 hw info struct
2239f4bda337SThomas Pedersen  * @status: RX status
2240f4bda337SThomas Pedersen  * @mpdu_len: total MPDU length (including FCS)
2241f4bda337SThomas Pedersen  * @mpdu_offset: offset into MPDU to calculate timestamp at
2242f4bda337SThomas Pedersen  *
2243f4bda337SThomas Pedersen  * This function calculates the RX timestamp at the given MPDU offset, taking
2244f4bda337SThomas Pedersen  * into account what the RX timestamp was. An offset of 0 will just normalize
2245f4bda337SThomas Pedersen  * the timestamp to TSF at beginning of MPDU reception.
2246f4bda337SThomas Pedersen  */
2247f4bda337SThomas Pedersen u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
2248f4bda337SThomas Pedersen 				     struct ieee80211_rx_status *status,
2249f4bda337SThomas Pedersen 				     unsigned int mpdu_len,
2250f4bda337SThomas Pedersen 				     unsigned int mpdu_offset)
2251f4bda337SThomas Pedersen {
2252f4bda337SThomas Pedersen 	u64 ts = status->mactime;
2253f4bda337SThomas Pedersen 	struct rate_info ri;
2254f4bda337SThomas Pedersen 	u16 rate;
2255f4bda337SThomas Pedersen 
2256f4bda337SThomas Pedersen 	if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
2257f4bda337SThomas Pedersen 		return 0;
2258f4bda337SThomas Pedersen 
2259f4bda337SThomas Pedersen 	memset(&ri, 0, sizeof(ri));
2260f4bda337SThomas Pedersen 
2261f4bda337SThomas Pedersen 	/* Fill cfg80211 rate info */
2262f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_HT) {
2263f4bda337SThomas Pedersen 		ri.mcs = status->rate_idx;
2264f4bda337SThomas Pedersen 		ri.flags |= RATE_INFO_FLAGS_MCS;
2265f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_40MHZ)
2266f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
2267f4bda337SThomas Pedersen 		if (status->flag & RX_FLAG_SHORT_GI)
2268f4bda337SThomas Pedersen 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
22695614618eSJohannes Berg 	} else if (status->flag & RX_FLAG_VHT) {
22705614618eSJohannes Berg 		ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
22715614618eSJohannes Berg 		ri.mcs = status->rate_idx;
22725614618eSJohannes Berg 		ri.nss = status->vht_nss;
22735614618eSJohannes Berg 		if (status->flag & RX_FLAG_40MHZ)
22745614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
22755614618eSJohannes Berg 		if (status->flag & RX_FLAG_80MHZ)
22765614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
22775614618eSJohannes Berg 		if (status->flag & RX_FLAG_80P80MHZ)
22785614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
22795614618eSJohannes Berg 		if (status->flag & RX_FLAG_160MHZ)
22805614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
22815614618eSJohannes Berg 		if (status->flag & RX_FLAG_SHORT_GI)
22825614618eSJohannes Berg 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
2283f4bda337SThomas Pedersen 	} else {
2284f4bda337SThomas Pedersen 		struct ieee80211_supported_band *sband;
22852103dec1SSimon Wunderlich 		int shift = 0;
22862103dec1SSimon Wunderlich 		int bitrate;
22872103dec1SSimon Wunderlich 
22882103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_10MHZ)
22892103dec1SSimon Wunderlich 			shift = 1;
22902103dec1SSimon Wunderlich 		if (status->flag & RX_FLAG_5MHZ)
22912103dec1SSimon Wunderlich 			shift = 2;
2292f4bda337SThomas Pedersen 
2293f4bda337SThomas Pedersen 		sband = local->hw.wiphy->bands[status->band];
22942103dec1SSimon Wunderlich 		bitrate = sband->bitrates[status->rate_idx].bitrate;
22952103dec1SSimon Wunderlich 		ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
2296f4bda337SThomas Pedersen 	}
2297f4bda337SThomas Pedersen 
2298f4bda337SThomas Pedersen 	rate = cfg80211_calculate_bitrate(&ri);
2299d86aa4f8SJohannes Berg 	if (WARN_ONCE(!rate,
2300d86aa4f8SJohannes Berg 		      "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
2301d86aa4f8SJohannes Berg 		      status->flag, status->rate_idx, status->vht_nss))
2302d86aa4f8SJohannes Berg 		return 0;
2303f4bda337SThomas Pedersen 
2304f4bda337SThomas Pedersen 	/* rewind from end of MPDU */
2305f4bda337SThomas Pedersen 	if (status->flag & RX_FLAG_MACTIME_END)
2306f4bda337SThomas Pedersen 		ts -= mpdu_len * 8 * 10 / rate;
2307f4bda337SThomas Pedersen 
2308f4bda337SThomas Pedersen 	ts += mpdu_offset * 8 * 10 / rate;
2309f4bda337SThomas Pedersen 
2310f4bda337SThomas Pedersen 	return ts;
2311f4bda337SThomas Pedersen }
2312164eb02dSSimon Wunderlich 
2313164eb02dSSimon Wunderlich void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
2314164eb02dSSimon Wunderlich {
2315164eb02dSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata;
2316d2859df5SJanusz Dziedzic 	struct cfg80211_chan_def chandef;
2317164eb02dSSimon Wunderlich 
231834a3740dSJohannes Berg 	mutex_lock(&local->mtx);
2319164eb02dSSimon Wunderlich 	mutex_lock(&local->iflist_mtx);
2320164eb02dSSimon Wunderlich 	list_for_each_entry(sdata, &local->interfaces, list) {
232134a3740dSJohannes Berg 		/* it might be waiting for the local->mtx, but then
232234a3740dSJohannes Berg 		 * by the time it gets it, sdata->wdev.cac_started
232334a3740dSJohannes Berg 		 * will no longer be true
232434a3740dSJohannes Berg 		 */
232534a3740dSJohannes Berg 		cancel_delayed_work(&sdata->dfs_cac_timer_work);
2326164eb02dSSimon Wunderlich 
2327164eb02dSSimon Wunderlich 		if (sdata->wdev.cac_started) {
2328d2859df5SJanusz Dziedzic 			chandef = sdata->vif.bss_conf.chandef;
2329164eb02dSSimon Wunderlich 			ieee80211_vif_release_channel(sdata);
2330164eb02dSSimon Wunderlich 			cfg80211_cac_event(sdata->dev,
2331d2859df5SJanusz Dziedzic 					   &chandef,
2332164eb02dSSimon Wunderlich 					   NL80211_RADAR_CAC_ABORTED,
2333164eb02dSSimon Wunderlich 					   GFP_KERNEL);
2334164eb02dSSimon Wunderlich 		}
2335164eb02dSSimon Wunderlich 	}
2336164eb02dSSimon Wunderlich 	mutex_unlock(&local->iflist_mtx);
233734a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
2338164eb02dSSimon Wunderlich }
2339164eb02dSSimon Wunderlich 
2340164eb02dSSimon Wunderlich void ieee80211_dfs_radar_detected_work(struct work_struct *work)
2341164eb02dSSimon Wunderlich {
2342164eb02dSSimon Wunderlich 	struct ieee80211_local *local =
2343164eb02dSSimon Wunderlich 		container_of(work, struct ieee80211_local, radar_detected_work);
234484a3d1c9SJanusz Dziedzic 	struct cfg80211_chan_def chandef = local->hw.conf.chandef;
2345164eb02dSSimon Wunderlich 
2346164eb02dSSimon Wunderlich 	ieee80211_dfs_cac_cancel(local);
2347164eb02dSSimon Wunderlich 
2348164eb02dSSimon Wunderlich 	if (local->use_chanctx)
2349164eb02dSSimon Wunderlich 		/* currently not handled */
2350164eb02dSSimon Wunderlich 		WARN_ON(1);
235184a3d1c9SJanusz Dziedzic 	else
2352164eb02dSSimon Wunderlich 		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
2353164eb02dSSimon Wunderlich }
2354164eb02dSSimon Wunderlich 
2355164eb02dSSimon Wunderlich void ieee80211_radar_detected(struct ieee80211_hw *hw)
2356164eb02dSSimon Wunderlich {
2357164eb02dSSimon Wunderlich 	struct ieee80211_local *local = hw_to_local(hw);
2358164eb02dSSimon Wunderlich 
2359164eb02dSSimon Wunderlich 	trace_api_radar_detected(local);
2360164eb02dSSimon Wunderlich 
2361164eb02dSSimon Wunderlich 	ieee80211_queue_work(hw, &local->radar_detected_work);
2362164eb02dSSimon Wunderlich }
2363164eb02dSSimon Wunderlich EXPORT_SYMBOL(ieee80211_radar_detected);
2364e6b7cde4SSimon Wunderlich 
2365e6b7cde4SSimon Wunderlich u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
2366e6b7cde4SSimon Wunderlich {
2367e6b7cde4SSimon Wunderlich 	u32 ret;
2368e6b7cde4SSimon Wunderlich 	int tmp;
2369e6b7cde4SSimon Wunderlich 
2370e6b7cde4SSimon Wunderlich 	switch (c->width) {
2371e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20:
2372e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2373e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2374e6b7cde4SSimon Wunderlich 		break;
2375e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_40:
2376e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20;
2377e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->chan->center_freq;
2378e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_40MHZ |
2379e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_VHT;
2380e6b7cde4SSimon Wunderlich 		break;
2381e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80:
2382e6b7cde4SSimon Wunderlich 		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
2383e6b7cde4SSimon Wunderlich 		/* n_P40 */
2384e6b7cde4SSimon Wunderlich 		tmp /= 2;
2385e6b7cde4SSimon Wunderlich 		/* freq_P40 */
2386e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
2387e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_40;
2388e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_VHT;
2389e6b7cde4SSimon Wunderlich 		break;
2390e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_80P80:
2391e6b7cde4SSimon Wunderlich 		c->center_freq2 = 0;
2392e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2393e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2394e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2395e6b7cde4SSimon Wunderlich 		break;
2396e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_160:
2397e6b7cde4SSimon Wunderlich 		/* n_P20 */
2398e6b7cde4SSimon Wunderlich 		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
2399e6b7cde4SSimon Wunderlich 		/* n_P80 */
2400e6b7cde4SSimon Wunderlich 		tmp /= 4;
2401e6b7cde4SSimon Wunderlich 		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
2402e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_80;
2403e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_80P80MHZ |
2404e6b7cde4SSimon Wunderlich 		      IEEE80211_STA_DISABLE_160MHZ;
2405e6b7cde4SSimon Wunderlich 		break;
2406e6b7cde4SSimon Wunderlich 	default:
2407e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_20_NOHT:
2408e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2409e6b7cde4SSimon Wunderlich 		c->width = NL80211_CHAN_WIDTH_20_NOHT;
2410e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2411e6b7cde4SSimon Wunderlich 		break;
2412e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_5:
2413e6b7cde4SSimon Wunderlich 	case NL80211_CHAN_WIDTH_10:
2414e6b7cde4SSimon Wunderlich 		WARN_ON_ONCE(1);
2415e6b7cde4SSimon Wunderlich 		/* keep c->width */
2416e6b7cde4SSimon Wunderlich 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
2417e6b7cde4SSimon Wunderlich 		break;
2418e6b7cde4SSimon Wunderlich 	}
2419e6b7cde4SSimon Wunderlich 
2420e6b7cde4SSimon Wunderlich 	WARN_ON_ONCE(!cfg80211_chandef_valid(c));
2421e6b7cde4SSimon Wunderlich 
2422e6b7cde4SSimon Wunderlich 	return ret;
2423e6b7cde4SSimon Wunderlich }
2424687da132SEmmanuel Grumbach 
2425687da132SEmmanuel Grumbach /*
2426687da132SEmmanuel Grumbach  * Returns true if smps_mode_new is strictly more restrictive than
2427687da132SEmmanuel Grumbach  * smps_mode_old.
2428687da132SEmmanuel Grumbach  */
2429687da132SEmmanuel Grumbach bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
2430687da132SEmmanuel Grumbach 				   enum ieee80211_smps_mode smps_mode_new)
2431687da132SEmmanuel Grumbach {
2432687da132SEmmanuel Grumbach 	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
2433687da132SEmmanuel Grumbach 			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
2434687da132SEmmanuel Grumbach 		return false;
2435687da132SEmmanuel Grumbach 
2436687da132SEmmanuel Grumbach 	switch (smps_mode_old) {
2437687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_STATIC:
2438687da132SEmmanuel Grumbach 		return false;
2439687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_DYNAMIC:
2440687da132SEmmanuel Grumbach 		return smps_mode_new == IEEE80211_SMPS_STATIC;
2441687da132SEmmanuel Grumbach 	case IEEE80211_SMPS_OFF:
2442687da132SEmmanuel Grumbach 		return smps_mode_new != IEEE80211_SMPS_OFF;
2443687da132SEmmanuel Grumbach 	default:
2444687da132SEmmanuel Grumbach 		WARN_ON(1);
2445687da132SEmmanuel Grumbach 	}
2446687da132SEmmanuel Grumbach 
2447687da132SEmmanuel Grumbach 	return false;
2448687da132SEmmanuel Grumbach }
2449c6da674aSChun-Yeow Yeoh 
2450c6da674aSChun-Yeow Yeoh int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
2451c6da674aSChun-Yeow Yeoh 			      struct cfg80211_csa_settings *csa_settings)
2452c6da674aSChun-Yeow Yeoh {
2453c6da674aSChun-Yeow Yeoh 	struct sk_buff *skb;
2454c6da674aSChun-Yeow Yeoh 	struct ieee80211_mgmt *mgmt;
2455c6da674aSChun-Yeow Yeoh 	struct ieee80211_local *local = sdata->local;
2456c6da674aSChun-Yeow Yeoh 	int freq;
2457c6da674aSChun-Yeow Yeoh 	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
2458c6da674aSChun-Yeow Yeoh 			       sizeof(mgmt->u.action.u.chan_switch);
2459c6da674aSChun-Yeow Yeoh 	u8 *pos;
2460c6da674aSChun-Yeow Yeoh 
2461c6da674aSChun-Yeow Yeoh 	if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
2462c6da674aSChun-Yeow Yeoh 	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
2463c6da674aSChun-Yeow Yeoh 		return -EOPNOTSUPP;
2464c6da674aSChun-Yeow Yeoh 
2465c6da674aSChun-Yeow Yeoh 	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
2466c6da674aSChun-Yeow Yeoh 			    5 + /* channel switch announcement element */
2467c6da674aSChun-Yeow Yeoh 			    3 + /* secondary channel offset element */
2468c6da674aSChun-Yeow Yeoh 			    8); /* mesh channel switch parameters element */
2469c6da674aSChun-Yeow Yeoh 	if (!skb)
2470c6da674aSChun-Yeow Yeoh 		return -ENOMEM;
2471c6da674aSChun-Yeow Yeoh 
2472c6da674aSChun-Yeow Yeoh 	skb_reserve(skb, local->tx_headroom);
2473c6da674aSChun-Yeow Yeoh 	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
2474c6da674aSChun-Yeow Yeoh 	memset(mgmt, 0, hdr_len);
2475c6da674aSChun-Yeow Yeoh 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2476c6da674aSChun-Yeow Yeoh 					  IEEE80211_STYPE_ACTION);
2477c6da674aSChun-Yeow Yeoh 
2478c6da674aSChun-Yeow Yeoh 	eth_broadcast_addr(mgmt->da);
2479c6da674aSChun-Yeow Yeoh 	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2480c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2481c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
2482c6da674aSChun-Yeow Yeoh 	} else {
2483c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
2484c6da674aSChun-Yeow Yeoh 		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
2485c6da674aSChun-Yeow Yeoh 	}
2486c6da674aSChun-Yeow Yeoh 	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
2487c6da674aSChun-Yeow Yeoh 	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
2488c6da674aSChun-Yeow Yeoh 	pos = skb_put(skb, 5);
2489c6da674aSChun-Yeow Yeoh 	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
2490c6da674aSChun-Yeow Yeoh 	*pos++ = 3;						/* IE length */
2491c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
2492c6da674aSChun-Yeow Yeoh 	freq = csa_settings->chandef.chan->center_freq;
2493c6da674aSChun-Yeow Yeoh 	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
2494c6da674aSChun-Yeow Yeoh 	*pos++ = csa_settings->count;				/* count */
2495c6da674aSChun-Yeow Yeoh 
2496c6da674aSChun-Yeow Yeoh 	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
2497c6da674aSChun-Yeow Yeoh 		enum nl80211_channel_type ch_type;
2498c6da674aSChun-Yeow Yeoh 
2499c6da674aSChun-Yeow Yeoh 		skb_put(skb, 3);
2500c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
2501c6da674aSChun-Yeow Yeoh 		*pos++ = 1;					/* IE length */
2502c6da674aSChun-Yeow Yeoh 		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
2503c6da674aSChun-Yeow Yeoh 		if (ch_type == NL80211_CHAN_HT40PLUS)
2504c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
2505c6da674aSChun-Yeow Yeoh 		else
2506c6da674aSChun-Yeow Yeoh 			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
2507c6da674aSChun-Yeow Yeoh 	}
2508c6da674aSChun-Yeow Yeoh 
2509c6da674aSChun-Yeow Yeoh 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
2510c6da674aSChun-Yeow Yeoh 		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2511c6da674aSChun-Yeow Yeoh 
2512c6da674aSChun-Yeow Yeoh 		skb_put(skb, 8);
2513c6da674aSChun-Yeow Yeoh 		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */
2514c6da674aSChun-Yeow Yeoh 		*pos++ = 6;					/* IE length */
2515c6da674aSChun-Yeow Yeoh 		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */
2516c6da674aSChun-Yeow Yeoh 		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */
2517c6da674aSChun-Yeow Yeoh 		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
2518c6da674aSChun-Yeow Yeoh 		*pos++ |= csa_settings->block_tx ?
2519c6da674aSChun-Yeow Yeoh 			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
2520c6da674aSChun-Yeow Yeoh 		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
2521c6da674aSChun-Yeow Yeoh 		pos += 2;
2522ca91dc97SChun-Yeow Yeoh 		put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */
2523c6da674aSChun-Yeow Yeoh 		pos += 2;
2524c6da674aSChun-Yeow Yeoh 	}
2525c6da674aSChun-Yeow Yeoh 
2526c6da674aSChun-Yeow Yeoh 	ieee80211_tx_skb(sdata, skb);
2527c6da674aSChun-Yeow Yeoh 	return 0;
2528c6da674aSChun-Yeow Yeoh }
25292475b1ccSMax Stepanov 
25302475b1ccSMax Stepanov bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
25312475b1ccSMax Stepanov {
25322475b1ccSMax Stepanov 	return !(cs == NULL || cs->cipher == 0 ||
25332475b1ccSMax Stepanov 		 cs->hdr_len < cs->pn_len + cs->pn_off ||
25342475b1ccSMax Stepanov 		 cs->hdr_len <= cs->key_idx_off ||
25352475b1ccSMax Stepanov 		 cs->key_idx_shift > 7 ||
25362475b1ccSMax Stepanov 		 cs->key_idx_mask == 0);
25372475b1ccSMax Stepanov }
25382475b1ccSMax Stepanov 
25392475b1ccSMax Stepanov bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
25402475b1ccSMax Stepanov {
25412475b1ccSMax Stepanov 	int i;
25422475b1ccSMax Stepanov 
25432475b1ccSMax Stepanov 	/* Ensure we have enough iftype bitmap space for all iftype values */
25442475b1ccSMax Stepanov 	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
25452475b1ccSMax Stepanov 
25462475b1ccSMax Stepanov 	for (i = 0; i < n; i++)
25472475b1ccSMax Stepanov 		if (!ieee80211_cs_valid(&cs[i]))
25482475b1ccSMax Stepanov 			return false;
25492475b1ccSMax Stepanov 
25502475b1ccSMax Stepanov 	return true;
25512475b1ccSMax Stepanov }
25522475b1ccSMax Stepanov 
25532475b1ccSMax Stepanov const struct ieee80211_cipher_scheme *
25542475b1ccSMax Stepanov ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
25552475b1ccSMax Stepanov 		 enum nl80211_iftype iftype)
25562475b1ccSMax Stepanov {
25572475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
25582475b1ccSMax Stepanov 	int n = local->hw.n_cipher_schemes;
25592475b1ccSMax Stepanov 	int i;
25602475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs = NULL;
25612475b1ccSMax Stepanov 
25622475b1ccSMax Stepanov 	for (i = 0; i < n; i++) {
25632475b1ccSMax Stepanov 		if (l[i].cipher == cipher) {
25642475b1ccSMax Stepanov 			cs = &l[i];
25652475b1ccSMax Stepanov 			break;
25662475b1ccSMax Stepanov 		}
25672475b1ccSMax Stepanov 	}
25682475b1ccSMax Stepanov 
25692475b1ccSMax Stepanov 	if (!cs || !(cs->iftype & BIT(iftype)))
25702475b1ccSMax Stepanov 		return NULL;
25712475b1ccSMax Stepanov 
25722475b1ccSMax Stepanov 	return cs;
25732475b1ccSMax Stepanov }
25742475b1ccSMax Stepanov 
25752475b1ccSMax Stepanov int ieee80211_cs_headroom(struct ieee80211_local *local,
25762475b1ccSMax Stepanov 			  struct cfg80211_crypto_settings *crypto,
25772475b1ccSMax Stepanov 			  enum nl80211_iftype iftype)
25782475b1ccSMax Stepanov {
25792475b1ccSMax Stepanov 	const struct ieee80211_cipher_scheme *cs;
25802475b1ccSMax Stepanov 	int headroom = IEEE80211_ENCRYPT_HEADROOM;
25812475b1ccSMax Stepanov 	int i;
25822475b1ccSMax Stepanov 
25832475b1ccSMax Stepanov 	for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
25842475b1ccSMax Stepanov 		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
25852475b1ccSMax Stepanov 				      iftype);
25862475b1ccSMax Stepanov 
25872475b1ccSMax Stepanov 		if (cs && headroom < cs->hdr_len)
25882475b1ccSMax Stepanov 			headroom = cs->hdr_len;
25892475b1ccSMax Stepanov 	}
25902475b1ccSMax Stepanov 
25912475b1ccSMax Stepanov 	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
25922475b1ccSMax Stepanov 	if (cs && headroom < cs->hdr_len)
25932475b1ccSMax Stepanov 		headroom = cs->hdr_len;
25942475b1ccSMax Stepanov 
25952475b1ccSMax Stepanov 	return headroom;
25962475b1ccSMax Stepanov }
2597a7022e65SFelix Fietkau 
2598a7022e65SFelix Fietkau static bool
2599a7022e65SFelix Fietkau ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
2600a7022e65SFelix Fietkau {
2601a7022e65SFelix Fietkau 	s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
2602a7022e65SFelix Fietkau 	int skip;
2603a7022e65SFelix Fietkau 
2604a7022e65SFelix Fietkau 	if (end > 0)
2605a7022e65SFelix Fietkau 		return false;
2606a7022e65SFelix Fietkau 
2607a7022e65SFelix Fietkau 	/* End time is in the past, check for repetitions */
2608a7022e65SFelix Fietkau 	skip = DIV_ROUND_UP(-end, data->desc[i].interval);
2609a7022e65SFelix Fietkau 	if (data->count[i] < 255) {
2610a7022e65SFelix Fietkau 		if (data->count[i] <= skip) {
2611a7022e65SFelix Fietkau 			data->count[i] = 0;
2612a7022e65SFelix Fietkau 			return false;
2613a7022e65SFelix Fietkau 		}
2614a7022e65SFelix Fietkau 
2615a7022e65SFelix Fietkau 		data->count[i] -= skip;
2616a7022e65SFelix Fietkau 	}
2617a7022e65SFelix Fietkau 
2618a7022e65SFelix Fietkau 	data->desc[i].start += skip * data->desc[i].interval;
2619a7022e65SFelix Fietkau 
2620a7022e65SFelix Fietkau 	return true;
2621a7022e65SFelix Fietkau }
2622a7022e65SFelix Fietkau 
2623a7022e65SFelix Fietkau static bool
2624a7022e65SFelix Fietkau ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
2625a7022e65SFelix Fietkau 			     s32 *offset)
2626a7022e65SFelix Fietkau {
2627a7022e65SFelix Fietkau 	bool ret = false;
2628a7022e65SFelix Fietkau 	int i;
2629a7022e65SFelix Fietkau 
2630a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2631a7022e65SFelix Fietkau 		s32 cur;
2632a7022e65SFelix Fietkau 
2633a7022e65SFelix Fietkau 		if (!data->count[i])
2634a7022e65SFelix Fietkau 			continue;
2635a7022e65SFelix Fietkau 
2636a7022e65SFelix Fietkau 		if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
2637a7022e65SFelix Fietkau 			ret = true;
2638a7022e65SFelix Fietkau 
2639a7022e65SFelix Fietkau 		cur = data->desc[i].start - tsf;
2640a7022e65SFelix Fietkau 		if (cur > *offset)
2641a7022e65SFelix Fietkau 			continue;
2642a7022e65SFelix Fietkau 
2643a7022e65SFelix Fietkau 		cur = data->desc[i].start + data->desc[i].duration - tsf;
2644a7022e65SFelix Fietkau 		if (cur > *offset)
2645a7022e65SFelix Fietkau 			*offset = cur;
2646a7022e65SFelix Fietkau 	}
2647a7022e65SFelix Fietkau 
2648a7022e65SFelix Fietkau 	return ret;
2649a7022e65SFelix Fietkau }
2650a7022e65SFelix Fietkau 
2651a7022e65SFelix Fietkau static u32
2652a7022e65SFelix Fietkau ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
2653a7022e65SFelix Fietkau {
2654a7022e65SFelix Fietkau 	s32 offset = 0;
2655a7022e65SFelix Fietkau 	int tries = 0;
2656a7022e65SFelix Fietkau 	/*
2657a7022e65SFelix Fietkau 	 * arbitrary limit, used to avoid infinite loops when combined NoA
2658a7022e65SFelix Fietkau 	 * descriptors cover the full time period.
2659a7022e65SFelix Fietkau 	 */
2660a7022e65SFelix Fietkau 	int max_tries = 5;
2661a7022e65SFelix Fietkau 
2662a7022e65SFelix Fietkau 	ieee80211_extend_absent_time(data, tsf, &offset);
2663a7022e65SFelix Fietkau 	do {
2664a7022e65SFelix Fietkau 		if (!ieee80211_extend_absent_time(data, tsf, &offset))
2665a7022e65SFelix Fietkau 			break;
2666a7022e65SFelix Fietkau 
2667a7022e65SFelix Fietkau 		tries++;
2668a7022e65SFelix Fietkau 	} while (tries < max_tries);
2669a7022e65SFelix Fietkau 
2670a7022e65SFelix Fietkau 	return offset;
2671a7022e65SFelix Fietkau }
2672a7022e65SFelix Fietkau 
2673a7022e65SFelix Fietkau void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
2674a7022e65SFelix Fietkau {
2675a7022e65SFelix Fietkau 	u32 next_offset = BIT(31) - 1;
2676a7022e65SFelix Fietkau 	int i;
2677a7022e65SFelix Fietkau 
2678a7022e65SFelix Fietkau 	data->absent = 0;
2679a7022e65SFelix Fietkau 	data->has_next_tsf = false;
2680a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2681a7022e65SFelix Fietkau 		s32 start;
2682a7022e65SFelix Fietkau 
2683a7022e65SFelix Fietkau 		if (!data->count[i])
2684a7022e65SFelix Fietkau 			continue;
2685a7022e65SFelix Fietkau 
2686a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2687a7022e65SFelix Fietkau 		start = data->desc[i].start - tsf;
2688a7022e65SFelix Fietkau 		if (start <= 0)
2689a7022e65SFelix Fietkau 			data->absent |= BIT(i);
2690a7022e65SFelix Fietkau 
2691a7022e65SFelix Fietkau 		if (next_offset > start)
2692a7022e65SFelix Fietkau 			next_offset = start;
2693a7022e65SFelix Fietkau 
2694a7022e65SFelix Fietkau 		data->has_next_tsf = true;
2695a7022e65SFelix Fietkau 	}
2696a7022e65SFelix Fietkau 
2697a7022e65SFelix Fietkau 	if (data->absent)
2698a7022e65SFelix Fietkau 		next_offset = ieee80211_get_noa_absent_time(data, tsf);
2699a7022e65SFelix Fietkau 
2700a7022e65SFelix Fietkau 	data->next_tsf = tsf + next_offset;
2701a7022e65SFelix Fietkau }
2702a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_update_p2p_noa);
2703a7022e65SFelix Fietkau 
2704a7022e65SFelix Fietkau int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
2705a7022e65SFelix Fietkau 			    struct ieee80211_noa_data *data, u32 tsf)
2706a7022e65SFelix Fietkau {
2707a7022e65SFelix Fietkau 	int ret = 0;
2708a7022e65SFelix Fietkau 	int i;
2709a7022e65SFelix Fietkau 
2710a7022e65SFelix Fietkau 	memset(data, 0, sizeof(*data));
2711a7022e65SFelix Fietkau 
2712a7022e65SFelix Fietkau 	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
2713a7022e65SFelix Fietkau 		const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
2714a7022e65SFelix Fietkau 
2715a7022e65SFelix Fietkau 		if (!desc->count || !desc->duration)
2716a7022e65SFelix Fietkau 			continue;
2717a7022e65SFelix Fietkau 
2718a7022e65SFelix Fietkau 		data->count[i] = desc->count;
2719a7022e65SFelix Fietkau 		data->desc[i].start = le32_to_cpu(desc->start_time);
2720a7022e65SFelix Fietkau 		data->desc[i].duration = le32_to_cpu(desc->duration);
2721a7022e65SFelix Fietkau 		data->desc[i].interval = le32_to_cpu(desc->interval);
2722a7022e65SFelix Fietkau 
2723a7022e65SFelix Fietkau 		if (data->count[i] > 1 &&
2724a7022e65SFelix Fietkau 		    data->desc[i].interval < data->desc[i].duration)
2725a7022e65SFelix Fietkau 			continue;
2726a7022e65SFelix Fietkau 
2727a7022e65SFelix Fietkau 		ieee80211_extend_noa_desc(data, tsf, i);
2728a7022e65SFelix Fietkau 		ret++;
2729a7022e65SFelix Fietkau 	}
2730a7022e65SFelix Fietkau 
2731a7022e65SFelix Fietkau 	if (ret)
2732a7022e65SFelix Fietkau 		ieee80211_update_p2p_noa(data, tsf);
2733a7022e65SFelix Fietkau 
2734a7022e65SFelix Fietkau 	return ret;
2735a7022e65SFelix Fietkau }
2736a7022e65SFelix Fietkau EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
2737057d5f4bSThomas Pedersen 
2738057d5f4bSThomas Pedersen void ieee80211_recalc_dtim(struct ieee80211_local *local,
2739057d5f4bSThomas Pedersen 			   struct ieee80211_sub_if_data *sdata)
2740057d5f4bSThomas Pedersen {
2741057d5f4bSThomas Pedersen 	u64 tsf = drv_get_tsf(local, sdata);
2742057d5f4bSThomas Pedersen 	u64 dtim_count = 0;
2743057d5f4bSThomas Pedersen 	u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
2744057d5f4bSThomas Pedersen 	u8 dtim_period = sdata->vif.bss_conf.dtim_period;
2745057d5f4bSThomas Pedersen 	struct ps_data *ps;
2746057d5f4bSThomas Pedersen 	u8 bcns_from_dtim;
2747057d5f4bSThomas Pedersen 
2748057d5f4bSThomas Pedersen 	if (tsf == -1ULL || !beacon_int || !dtim_period)
2749057d5f4bSThomas Pedersen 		return;
2750057d5f4bSThomas Pedersen 
2751057d5f4bSThomas Pedersen 	if (sdata->vif.type == NL80211_IFTYPE_AP ||
2752057d5f4bSThomas Pedersen 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
2753057d5f4bSThomas Pedersen 		if (!sdata->bss)
2754057d5f4bSThomas Pedersen 			return;
2755057d5f4bSThomas Pedersen 
2756057d5f4bSThomas Pedersen 		ps = &sdata->bss->ps;
2757057d5f4bSThomas Pedersen 	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
2758057d5f4bSThomas Pedersen 		ps = &sdata->u.mesh.ps;
2759057d5f4bSThomas Pedersen 	} else {
2760057d5f4bSThomas Pedersen 		return;
2761057d5f4bSThomas Pedersen 	}
2762057d5f4bSThomas Pedersen 
2763057d5f4bSThomas Pedersen 	/*
2764057d5f4bSThomas Pedersen 	 * actually finds last dtim_count, mac80211 will update in
2765057d5f4bSThomas Pedersen 	 * __beacon_add_tim().
2766057d5f4bSThomas Pedersen 	 * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
2767057d5f4bSThomas Pedersen 	 */
2768057d5f4bSThomas Pedersen 	do_div(tsf, beacon_int);
2769057d5f4bSThomas Pedersen 	bcns_from_dtim = do_div(tsf, dtim_period);
2770057d5f4bSThomas Pedersen 	/* just had a DTIM */
2771057d5f4bSThomas Pedersen 	if (!bcns_from_dtim)
2772057d5f4bSThomas Pedersen 		dtim_count = 0;
2773057d5f4bSThomas Pedersen 	else
2774057d5f4bSThomas Pedersen 		dtim_count = dtim_period - bcns_from_dtim;
2775057d5f4bSThomas Pedersen 
2776057d5f4bSThomas Pedersen 	ps->dtim_count = dtim_count;
2777057d5f4bSThomas Pedersen }
2778