xref: /linux/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1fb9987d0SSujith /*
25b68138eSSujith Manoharan  * Copyright (c) 2010-2011 Atheros Communications Inc.
3fb9987d0SSujith  *
4fb9987d0SSujith  * Permission to use, copy, modify, and/or distribute this software for any
5fb9987d0SSujith  * purpose with or without fee is hereby granted, provided that the above
6fb9987d0SSujith  * copyright notice and this permission notice appear in all copies.
7fb9987d0SSujith  *
8fb9987d0SSujith  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9fb9987d0SSujith  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10fb9987d0SSujith  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11fb9987d0SSujith  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12fb9987d0SSujith  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13fb9987d0SSujith  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14fb9987d0SSujith  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15fb9987d0SSujith  */
16fb9987d0SSujith 
17fb9987d0SSujith #include "htc.h"
18fb9987d0SSujith 
19fb9987d0SSujith #define FUDGE 2
20fb9987d0SSujith 
ath9k_htc_beaconq_config(struct ath9k_htc_priv * priv)212493a547SSujith Manoharan void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
222493a547SSujith Manoharan {
232493a547SSujith Manoharan 	struct ath_hw *ah = priv->ah;
242493a547SSujith Manoharan 	struct ath9k_tx_queue_info qi, qi_be;
252493a547SSujith Manoharan 
262493a547SSujith Manoharan 	memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
272493a547SSujith Manoharan 	memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info));
282493a547SSujith Manoharan 
29a099874eSOleksij Rempel 	ath9k_hw_get_txq_props(ah, priv->beacon.beaconq, &qi);
302493a547SSujith Manoharan 
31594e65b6SJavier Cardona 	if (priv->ah->opmode == NL80211_IFTYPE_AP ||
32594e65b6SJavier Cardona 	    priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
332493a547SSujith Manoharan 		qi.tqi_aifs = 1;
342493a547SSujith Manoharan 		qi.tqi_cwmin = 0;
352493a547SSujith Manoharan 		qi.tqi_cwmax = 0;
362493a547SSujith Manoharan 	} else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) {
37bea843c7SSujith Manoharan 		int qnum = priv->hwq_map[IEEE80211_AC_BE];
382493a547SSujith Manoharan 
392493a547SSujith Manoharan 		ath9k_hw_get_txq_props(ah, qnum, &qi_be);
402493a547SSujith Manoharan 
412493a547SSujith Manoharan 		qi.tqi_aifs = qi_be.tqi_aifs;
422493a547SSujith Manoharan 
432493a547SSujith Manoharan 		/*
442493a547SSujith Manoharan 		 * For WIFI Beacon Distribution
452493a547SSujith Manoharan 		 * Long slot time  : 2x cwmin
462493a547SSujith Manoharan 		 * Short slot time : 4x cwmin
472493a547SSujith Manoharan 		 */
4811b0ac2eSBenjamin Berg 		if (ah->slottime == 20)
492493a547SSujith Manoharan 			qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
502493a547SSujith Manoharan 		else
512493a547SSujith Manoharan 			qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
522493a547SSujith Manoharan 
532493a547SSujith Manoharan 		qi.tqi_cwmax = qi_be.tqi_cwmax;
542493a547SSujith Manoharan 
552493a547SSujith Manoharan 	}
562493a547SSujith Manoharan 
57a099874eSOleksij Rempel 	if (!ath9k_hw_set_txq_props(ah, priv->beacon.beaconq, &qi)) {
582493a547SSujith Manoharan 		ath_err(ath9k_hw_common(ah),
59a099874eSOleksij Rempel 			"Unable to update beacon queue %u!\n", priv->beacon.beaconq);
602493a547SSujith Manoharan 	} else {
61a099874eSOleksij Rempel 		ath9k_hw_resettxqueue(ah, priv->beacon.beaconq);
622493a547SSujith Manoharan 	}
632493a547SSujith Manoharan }
642493a547SSujith Manoharan 
657f5c4c83SOleksij Rempel /*
667f5c4c83SOleksij Rempel  * Both nexttbtt and intval have to be in usecs.
677f5c4c83SOleksij Rempel  */
ath9k_htc_beacon_init(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf,bool reset_tsf)687f5c4c83SOleksij Rempel static void ath9k_htc_beacon_init(struct ath9k_htc_priv *priv,
697f5c4c83SOleksij Rempel 				  struct ath_beacon_config *conf,
707f5c4c83SOleksij Rempel 				  bool reset_tsf)
717f5c4c83SOleksij Rempel {
727f5c4c83SOleksij Rempel 	struct ath_hw *ah = priv->ah;
737f5c4c83SOleksij Rempel 	int ret __attribute__ ((unused));
747f5c4c83SOleksij Rempel 	__be32 htc_imask = 0;
757f5c4c83SOleksij Rempel 	u8 cmd_rsp;
767f5c4c83SOleksij Rempel 
775f667642SOleksij Rempel 	if (conf->intval >= TU_TO_USEC(DEFAULT_SWBA_RESPONSE))
785f667642SOleksij Rempel 		ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE;
795f667642SOleksij Rempel 	else
805f667642SOleksij Rempel 		ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE;
815f667642SOleksij Rempel 
827f5c4c83SOleksij Rempel 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
837f5c4c83SOleksij Rempel 	if (reset_tsf)
847f5c4c83SOleksij Rempel 		ath9k_hw_reset_tsf(ah);
857f5c4c83SOleksij Rempel 	ath9k_htc_beaconq_config(priv);
867f5c4c83SOleksij Rempel 	ath9k_hw_beaconinit(ah, conf->nexttbtt, conf->intval);
877f5c4c83SOleksij Rempel 	priv->beacon.bmisscnt = 0;
887f5c4c83SOleksij Rempel 	htc_imask = cpu_to_be32(ah->imask);
897f5c4c83SOleksij Rempel 	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
907f5c4c83SOleksij Rempel }
917f5c4c83SOleksij Rempel 
ath9k_htc_beacon_config_sta(struct ath9k_htc_priv * priv,struct ath_beacon_config * bss_conf)92fb9987d0SSujith static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
933c4816d9SOleksij Rempel 					struct ath_beacon_config *bss_conf)
94fb9987d0SSujith {
95fb9987d0SSujith 	struct ath9k_beacon_state bs;
96fb9987d0SSujith 	enum ath9k_int imask = 0;
977f1f5a00SSujith 	__be32 htc_imask = 0;
980ff2b5c0SSujith Manoharan 	int ret __attribute__ ((unused));
99fb9987d0SSujith 	u8 cmd_rsp;
100fb9987d0SSujith 
101f8422440SOleksij Rempel 	if (ath9k_cmn_beacon_config_sta(priv->ah, bss_conf, &bs) == -EPERM)
102f8422440SOleksij Rempel 		return;
103fb9987d0SSujith 
104fb9987d0SSujith 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
105fb9987d0SSujith 	ath9k_hw_set_sta_beacon_timers(priv->ah, &bs);
106fb9987d0SSujith 	imask |= ATH9K_INT_BMISS;
107fb9987d0SSujith 	htc_imask = cpu_to_be32(imask);
108fb9987d0SSujith 	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
109fb9987d0SSujith }
110fb9987d0SSujith 
ath9k_htc_beacon_config_ap(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf)111a5fae37dSSujith Manoharan static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
1124b2d841fSOleksij Rempel 				       struct ath_beacon_config *conf)
113a5fae37dSSujith Manoharan {
1144b2d841fSOleksij Rempel 	struct ath_hw *ah = priv->ah;
1154b2d841fSOleksij Rempel 	ah->imask = 0;
116a5fae37dSSujith Manoharan 
1174b2d841fSOleksij Rempel 	ath9k_cmn_beacon_config_ap(ah, conf, ATH9K_HTC_MAX_BCN_VIF);
1184b2d841fSOleksij Rempel 	ath9k_htc_beacon_init(priv, conf, false);
119a5fae37dSSujith Manoharan }
120a5fae37dSSujith Manoharan 
ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf)121fb9987d0SSujith static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
12212f53c30SOleksij Rempel 					  struct ath_beacon_config *conf)
123fb9987d0SSujith {
124f7197924SOleksij Rempel 	struct ath_hw *ah = priv->ah;
12512f53c30SOleksij Rempel 	ah->imask = 0;
126fb9987d0SSujith 
12712f53c30SOleksij Rempel 	ath9k_cmn_beacon_config_adhoc(ah, conf);
12812f53c30SOleksij Rempel 	ath9k_htc_beacon_init(priv, conf, conf->ibss_creator);
129fb9987d0SSujith }
130fb9987d0SSujith 
ath9k_htc_beaconep(void * drv_priv,struct sk_buff * skb,enum htc_endpoint_id ep_id,bool txok)1319c6dda4eSSujith void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
1329c6dda4eSSujith 			enum htc_endpoint_id ep_id, bool txok)
133fb9987d0SSujith {
1349c6dda4eSSujith 	dev_kfree_skb_any(skb);
135fb9987d0SSujith }
136fb9987d0SSujith 
ath9k_htc_send_buffered(struct ath9k_htc_priv * priv,int slot)1377d547eb4SSujith Manoharan static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
1387d547eb4SSujith Manoharan 				    int slot)
1397d547eb4SSujith Manoharan {
1407d547eb4SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
1417d547eb4SSujith Manoharan 	struct ieee80211_vif *vif;
1427d547eb4SSujith Manoharan 	struct sk_buff *skb;
1437d547eb4SSujith Manoharan 	struct ieee80211_hdr *hdr;
1442c5d57f0SSujith Manoharan 	int padpos, padsize, ret, tx_slot;
1457d547eb4SSujith Manoharan 
1467d547eb4SSujith Manoharan 	spin_lock_bh(&priv->beacon_lock);
1477d547eb4SSujith Manoharan 
1483c4816d9SOleksij Rempel 	vif = priv->beacon.bslot[slot];
1497d547eb4SSujith Manoharan 
1507d547eb4SSujith Manoharan 	skb = ieee80211_get_buffered_bc(priv->hw, vif);
1517d547eb4SSujith Manoharan 
1527d547eb4SSujith Manoharan 	while(skb) {
1537d547eb4SSujith Manoharan 		hdr = (struct ieee80211_hdr *) skb->data;
1547d547eb4SSujith Manoharan 
155c60c9929SFelix Fietkau 		padpos = ieee80211_hdrlen(hdr->frame_control);
1567d547eb4SSujith Manoharan 		padsize = padpos & 3;
1577d547eb4SSujith Manoharan 		if (padsize && skb->len > padpos) {
1587d547eb4SSujith Manoharan 			if (skb_headroom(skb) < padsize) {
1597d547eb4SSujith Manoharan 				dev_kfree_skb_any(skb);
1607d547eb4SSujith Manoharan 				goto next;
1617d547eb4SSujith Manoharan 			}
1627d547eb4SSujith Manoharan 			skb_push(skb, padsize);
1637d547eb4SSujith Manoharan 			memmove(skb->data, skb->data + padsize, padpos);
1647d547eb4SSujith Manoharan 		}
1657d547eb4SSujith Manoharan 
1662c5d57f0SSujith Manoharan 		tx_slot = ath9k_htc_tx_get_slot(priv);
167cea3235cSRajkumar Manoharan 		if (tx_slot < 0) {
168d2182b69SJoe Perches 			ath_dbg(common, XMIT, "No free CAB slot\n");
1697d547eb4SSujith Manoharan 			dev_kfree_skb_any(skb);
1702c5d57f0SSujith Manoharan 			goto next;
1712c5d57f0SSujith Manoharan 		}
1722c5d57f0SSujith Manoharan 
17336323f81SThomas Huehn 		ret = ath9k_htc_tx_start(priv, NULL, skb, tx_slot, true);
1742c5d57f0SSujith Manoharan 		if (ret != 0) {
1752c5d57f0SSujith Manoharan 			ath9k_htc_tx_clear_slot(priv, tx_slot);
1762c5d57f0SSujith Manoharan 			dev_kfree_skb_any(skb);
1772c5d57f0SSujith Manoharan 
178d2182b69SJoe Perches 			ath_dbg(common, XMIT, "Failed to send CAB frame\n");
1798e86a547SSujith Manoharan 		} else {
1808e86a547SSujith Manoharan 			spin_lock_bh(&priv->tx.tx_lock);
1818e86a547SSujith Manoharan 			priv->tx.queued_cnt++;
1828e86a547SSujith Manoharan 			spin_unlock_bh(&priv->tx.tx_lock);
1837d547eb4SSujith Manoharan 		}
1847d547eb4SSujith Manoharan 	next:
1857d547eb4SSujith Manoharan 		skb = ieee80211_get_buffered_bc(priv->hw, vif);
1867d547eb4SSujith Manoharan 	}
1877d547eb4SSujith Manoharan 
1887d547eb4SSujith Manoharan 	spin_unlock_bh(&priv->beacon_lock);
1897d547eb4SSujith Manoharan }
1907d547eb4SSujith Manoharan 
ath9k_htc_send_beacon(struct ath9k_htc_priv * priv,int slot)191832f6a18SSujith Manoharan static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
192832f6a18SSujith Manoharan 				  int slot)
193fb9987d0SSujith {
194b0a6ba98SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
195832f6a18SSujith Manoharan 	struct ieee80211_vif *vif;
196832f6a18SSujith Manoharan 	struct ath9k_htc_vif *avp;
197fb9987d0SSujith 	struct tx_beacon_header beacon_hdr;
19840dc9e4bSSujith Manoharan 	struct ath9k_htc_tx_ctl *tx_ctl;
199fb9987d0SSujith 	struct ieee80211_tx_info *info;
2009b674a02SSujith Manoharan 	struct ieee80211_mgmt *mgmt;
2019c6dda4eSSujith 	struct sk_buff *beacon;
202fb9987d0SSujith 	u8 *tx_fhdr;
203b0a6ba98SSujith Manoharan 	int ret;
204fb9987d0SSujith 
205fb9987d0SSujith 	memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header));
206fb9987d0SSujith 
207fb9987d0SSujith 	spin_lock_bh(&priv->beacon_lock);
208fb9987d0SSujith 
2093c4816d9SOleksij Rempel 	vif = priv->beacon.bslot[slot];
210832f6a18SSujith Manoharan 	avp = (struct ath9k_htc_vif *)vif->drv_priv;
211832f6a18SSujith Manoharan 
21292c3f7efSOleksij Rempel 	if (unlikely(test_bit(ATH_OP_SCANNING, &common->op_flags))) {
213fb9987d0SSujith 		spin_unlock_bh(&priv->beacon_lock);
214fb9987d0SSujith 		return;
215fb9987d0SSujith 	}
216fb9987d0SSujith 
217fb9987d0SSujith 	/* Get a new beacon */
2186e8912a5SShaul Triebitz 	beacon = ieee80211_beacon_get(priv->hw, vif, 0);
2199c6dda4eSSujith 	if (!beacon) {
220fb9987d0SSujith 		spin_unlock_bh(&priv->beacon_lock);
221fb9987d0SSujith 		return;
222fb9987d0SSujith 	}
223fb9987d0SSujith 
2249b674a02SSujith Manoharan 	/*
2259b674a02SSujith Manoharan 	 * Update the TSF adjust value here, the HW will
2269b674a02SSujith Manoharan 	 * add this value for every beacon.
2279b674a02SSujith Manoharan 	 */
2289b674a02SSujith Manoharan 	mgmt = (struct ieee80211_mgmt *)beacon->data;
2299b674a02SSujith Manoharan 	mgmt->u.beacon.timestamp = avp->tsfadjust;
2309b674a02SSujith Manoharan 
2319c6dda4eSSujith 	info = IEEE80211_SKB_CB(beacon);
232fb9987d0SSujith 	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
233fb9987d0SSujith 		struct ieee80211_hdr *hdr =
2349c6dda4eSSujith 			(struct ieee80211_hdr *) beacon->data;
2359a3d025bSSujith Manoharan 		avp->seq_no += 0x10;
236fb9987d0SSujith 		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
2379a3d025bSSujith Manoharan 		hdr->seq_ctrl |= cpu_to_le16(avp->seq_no);
238fb9987d0SSujith 	}
239fb9987d0SSujith 
24040dc9e4bSSujith Manoharan 	tx_ctl = HTC_SKB_CB(beacon);
24140dc9e4bSSujith Manoharan 	memset(tx_ctl, 0, sizeof(*tx_ctl));
24240dc9e4bSSujith Manoharan 
24340dc9e4bSSujith Manoharan 	tx_ctl->type = ATH9K_HTC_BEACON;
244d67ee533SSujith Manoharan 	tx_ctl->epid = priv->beacon_ep;
24540dc9e4bSSujith Manoharan 
246fb9987d0SSujith 	beacon_hdr.vif_index = avp->index;
2479c6dda4eSSujith 	tx_fhdr = skb_push(beacon, sizeof(beacon_hdr));
248fb9987d0SSujith 	memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr));
249fb9987d0SSujith 
250d67ee533SSujith Manoharan 	ret = htc_send(priv->htc, beacon);
251b0a6ba98SSujith Manoharan 	if (ret != 0) {
252b0a6ba98SSujith Manoharan 		if (ret == -ENOMEM) {
253d2182b69SJoe Perches 			ath_dbg(common, BSTUCK,
254b0a6ba98SSujith Manoharan 				"Failed to send beacon, no free TX buffer\n");
255b0a6ba98SSujith Manoharan 		}
256b0a6ba98SSujith Manoharan 		dev_kfree_skb_any(beacon);
257b0a6ba98SSujith Manoharan 	}
258fb9987d0SSujith 
259fb9987d0SSujith 	spin_unlock_bh(&priv->beacon_lock);
260f0e44962SChun-Yeow Yeoh 
261f0e44962SChun-Yeow Yeoh 	ath9k_htc_csa_is_finished(priv);
262fb9987d0SSujith }
263fb9987d0SSujith 
ath9k_htc_choose_bslot(struct ath9k_htc_priv * priv,struct wmi_event_swba * swba)264f4c88991SSujith Manoharan static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv,
265f4c88991SSujith Manoharan 				  struct wmi_event_swba *swba)
266832f6a18SSujith Manoharan {
267832f6a18SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
268832f6a18SSujith Manoharan 	u64 tsf;
269832f6a18SSujith Manoharan 	u32 tsftu;
270832f6a18SSujith Manoharan 	u16 intval;
271832f6a18SSujith Manoharan 	int slot;
272832f6a18SSujith Manoharan 
273f29f5c08SRajkumar Manoharan 	intval = priv->cur_beacon_conf.beacon_interval;
274832f6a18SSujith Manoharan 
275f4c88991SSujith Manoharan 	tsf = be64_to_cpu(swba->tsf);
276832f6a18SSujith Manoharan 	tsftu = TSF_TO_TU(tsf >> 32, tsf);
277832f6a18SSujith Manoharan 	slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
278832f6a18SSujith Manoharan 	slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
279832f6a18SSujith Manoharan 
280d2182b69SJoe Perches 	ath_dbg(common, BEACON,
281832f6a18SSujith Manoharan 		"Choose slot: %d, tsf: %llu, tsftu: %u, intval: %u\n",
282832f6a18SSujith Manoharan 		slot, tsf, tsftu, intval);
283832f6a18SSujith Manoharan 
284832f6a18SSujith Manoharan 	return slot;
285832f6a18SSujith Manoharan }
286832f6a18SSujith Manoharan 
ath9k_htc_swba(struct ath9k_htc_priv * priv,struct wmi_event_swba * swba)287f4c88991SSujith Manoharan void ath9k_htc_swba(struct ath9k_htc_priv *priv,
288f4c88991SSujith Manoharan 		    struct wmi_event_swba *swba)
289832f6a18SSujith Manoharan {
290832f6a18SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
291832f6a18SSujith Manoharan 	int slot;
292832f6a18SSujith Manoharan 
293f4c88991SSujith Manoharan 	if (swba->beacon_pending != 0) {
2943c4816d9SOleksij Rempel 		priv->beacon.bmisscnt++;
2953c4816d9SOleksij Rempel 		if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
296d2182b69SJoe Perches 			ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n");
297f4c88991SSujith Manoharan 			ieee80211_queue_work(priv->hw,
298f4c88991SSujith Manoharan 					     &priv->fatal_work);
299832f6a18SSujith Manoharan 		}
300832f6a18SSujith Manoharan 		return;
301832f6a18SSujith Manoharan 	}
302832f6a18SSujith Manoharan 
3033c4816d9SOleksij Rempel 	if (priv->beacon.bmisscnt) {
304d2182b69SJoe Perches 		ath_dbg(common, BSTUCK,
305832f6a18SSujith Manoharan 			"Resuming beacon xmit after %u misses\n",
3063c4816d9SOleksij Rempel 			priv->beacon.bmisscnt);
3073c4816d9SOleksij Rempel 		priv->beacon.bmisscnt = 0;
308832f6a18SSujith Manoharan 	}
309832f6a18SSujith Manoharan 
310f4c88991SSujith Manoharan 	slot = ath9k_htc_choose_bslot(priv, swba);
311832f6a18SSujith Manoharan 	spin_lock_bh(&priv->beacon_lock);
3123c4816d9SOleksij Rempel 	if (priv->beacon.bslot[slot] == NULL) {
313832f6a18SSujith Manoharan 		spin_unlock_bh(&priv->beacon_lock);
314832f6a18SSujith Manoharan 		return;
315832f6a18SSujith Manoharan 	}
316832f6a18SSujith Manoharan 	spin_unlock_bh(&priv->beacon_lock);
317832f6a18SSujith Manoharan 
3187d547eb4SSujith Manoharan 	ath9k_htc_send_buffered(priv, slot);
319832f6a18SSujith Manoharan 	ath9k_htc_send_beacon(priv, slot);
320832f6a18SSujith Manoharan }
321832f6a18SSujith Manoharan 
ath9k_htc_assign_bslot(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)322832f6a18SSujith Manoharan void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv,
323832f6a18SSujith Manoharan 			    struct ieee80211_vif *vif)
324832f6a18SSujith Manoharan {
325832f6a18SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
326832f6a18SSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
327832f6a18SSujith Manoharan 	int i = 0;
328832f6a18SSujith Manoharan 
329832f6a18SSujith Manoharan 	spin_lock_bh(&priv->beacon_lock);
330832f6a18SSujith Manoharan 	for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) {
3313c4816d9SOleksij Rempel 		if (priv->beacon.bslot[i] == NULL) {
332832f6a18SSujith Manoharan 			avp->bslot = i;
333832f6a18SSujith Manoharan 			break;
334832f6a18SSujith Manoharan 		}
335832f6a18SSujith Manoharan 	}
336832f6a18SSujith Manoharan 
3373c4816d9SOleksij Rempel 	priv->beacon.bslot[avp->bslot] = vif;
338832f6a18SSujith Manoharan 	spin_unlock_bh(&priv->beacon_lock);
339832f6a18SSujith Manoharan 
340d2182b69SJoe Perches 	ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
341d2182b69SJoe Perches 		avp->bslot);
342832f6a18SSujith Manoharan }
343832f6a18SSujith Manoharan 
ath9k_htc_remove_bslot(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)344832f6a18SSujith Manoharan void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv,
345832f6a18SSujith Manoharan 			    struct ieee80211_vif *vif)
346832f6a18SSujith Manoharan {
347832f6a18SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
348832f6a18SSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
349832f6a18SSujith Manoharan 
350832f6a18SSujith Manoharan 	spin_lock_bh(&priv->beacon_lock);
3513c4816d9SOleksij Rempel 	priv->beacon.bslot[avp->bslot] = NULL;
352832f6a18SSujith Manoharan 	spin_unlock_bh(&priv->beacon_lock);
353832f6a18SSujith Manoharan 
354d2182b69SJoe Perches 	ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n",
355d2182b69SJoe Perches 		avp->bslot);
356832f6a18SSujith Manoharan }
357832f6a18SSujith Manoharan 
3589b674a02SSujith Manoharan /*
3599b674a02SSujith Manoharan  * Calculate the TSF adjustment value for all slots
3609b674a02SSujith Manoharan  * other than zero.
3619b674a02SSujith Manoharan  */
ath9k_htc_set_tsfadjust(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)3629b674a02SSujith Manoharan void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv,
3639b674a02SSujith Manoharan 			     struct ieee80211_vif *vif)
3649b674a02SSujith Manoharan {
3659b674a02SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
3669b674a02SSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
3673c4816d9SOleksij Rempel 	struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
3689b674a02SSujith Manoharan 	u64 tsfadjust;
3699b674a02SSujith Manoharan 
3709b674a02SSujith Manoharan 	if (avp->bslot == 0)
3719b674a02SSujith Manoharan 		return;
3729b674a02SSujith Manoharan 
3739b674a02SSujith Manoharan 	/*
3749b674a02SSujith Manoharan 	 * The beacon interval cannot be different for multi-AP mode,
3759b674a02SSujith Manoharan 	 * and we reach here only for VIF slots greater than zero,
3769b674a02SSujith Manoharan 	 * so beacon_interval is guaranteed to be set in cur_conf.
3779b674a02SSujith Manoharan 	 */
3789b674a02SSujith Manoharan 	tsfadjust = cur_conf->beacon_interval * avp->bslot / ATH9K_HTC_MAX_BCN_VIF;
3799b674a02SSujith Manoharan 	avp->tsfadjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
3809b674a02SSujith Manoharan 
381d2182b69SJoe Perches 	ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n",
3829b674a02SSujith Manoharan 		(unsigned long long)tsfadjust, avp->bslot);
3839b674a02SSujith Manoharan }
3849b674a02SSujith Manoharan 
ath9k_htc_beacon_iter(void * data,u8 * mac,struct ieee80211_vif * vif)385e7a2a4f5SSujith Manoharan static void ath9k_htc_beacon_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
386e7a2a4f5SSujith Manoharan {
38750c8cd44SHimanshu Jha 	bool *beacon_configured = data;
388e7a2a4f5SSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
389e7a2a4f5SSujith Manoharan 
390e7a2a4f5SSujith Manoharan 	if (vif->type == NL80211_IFTYPE_STATION &&
391e7a2a4f5SSujith Manoharan 	    avp->beacon_configured)
392e7a2a4f5SSujith Manoharan 		*beacon_configured = true;
393e7a2a4f5SSujith Manoharan }
394e7a2a4f5SSujith Manoharan 
ath9k_htc_check_beacon_config(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)395e7a2a4f5SSujith Manoharan static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv,
3961c3652a5SVivek Natarajan 					  struct ieee80211_vif *vif)
397fb9987d0SSujith {
398fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
3993c4816d9SOleksij Rempel 	struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
4001c3652a5SVivek Natarajan 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
401e7a2a4f5SSujith Manoharan 	bool beacon_configured;
402fcb9392fSSujith 
403a5fae37dSSujith Manoharan 	/*
404a5fae37dSSujith Manoharan 	 * Changing the beacon interval when multiple AP interfaces
405a5fae37dSSujith Manoharan 	 * are configured will affect beacon transmission of all
406a5fae37dSSujith Manoharan 	 * of them.
407a5fae37dSSujith Manoharan 	 */
408a5fae37dSSujith Manoharan 	if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
409a5fae37dSSujith Manoharan 	    (priv->num_ap_vif > 1) &&
410a5fae37dSSujith Manoharan 	    (vif->type == NL80211_IFTYPE_AP) &&
411a5fae37dSSujith Manoharan 	    (cur_conf->beacon_interval != bss_conf->beacon_int)) {
412d2182b69SJoe Perches 		ath_dbg(common, CONFIG,
413a5fae37dSSujith Manoharan 			"Changing beacon interval of multiple AP interfaces !\n");
414e7a2a4f5SSujith Manoharan 		return false;
415a5fae37dSSujith Manoharan 	}
416a5fae37dSSujith Manoharan 
417a5fae37dSSujith Manoharan 	/*
418a5fae37dSSujith Manoharan 	 * If the HW is operating in AP mode, any new station interfaces that
419a5fae37dSSujith Manoharan 	 * are added cannot change the beacon parameters.
420a5fae37dSSujith Manoharan 	 */
421a5fae37dSSujith Manoharan 	if (priv->num_ap_vif &&
422a5fae37dSSujith Manoharan 	    (vif->type != NL80211_IFTYPE_AP)) {
423d2182b69SJoe Perches 		ath_dbg(common, CONFIG,
424a5fae37dSSujith Manoharan 			"HW in AP mode, cannot set STA beacon parameters\n");
425e7a2a4f5SSujith Manoharan 		return false;
426a5fae37dSSujith Manoharan 	}
427a5fae37dSSujith Manoharan 
428e7a2a4f5SSujith Manoharan 	/*
429e7a2a4f5SSujith Manoharan 	 * The beacon parameters are configured only for the first
430e7a2a4f5SSujith Manoharan 	 * station interface.
431e7a2a4f5SSujith Manoharan 	 */
432e7a2a4f5SSujith Manoharan 	if ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
433e7a2a4f5SSujith Manoharan 	    (priv->num_sta_vif > 1) &&
434e7a2a4f5SSujith Manoharan 	    (vif->type == NL80211_IFTYPE_STATION)) {
435e7a2a4f5SSujith Manoharan 		beacon_configured = false;
4368b2c9824SJohannes Berg 		ieee80211_iterate_active_interfaces_atomic(
4378b2c9824SJohannes Berg 			priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
4388b2c9824SJohannes Berg 			ath9k_htc_beacon_iter, &beacon_configured);
439e7a2a4f5SSujith Manoharan 
440e7a2a4f5SSujith Manoharan 		if (beacon_configured) {
441d2182b69SJoe Perches 			ath_dbg(common, CONFIG,
442e7a2a4f5SSujith Manoharan 				"Beacon already configured for a station interface\n");
443e7a2a4f5SSujith Manoharan 			return false;
444e7a2a4f5SSujith Manoharan 		}
445e7a2a4f5SSujith Manoharan 	}
446e7a2a4f5SSujith Manoharan 
447e7a2a4f5SSujith Manoharan 	return true;
448e7a2a4f5SSujith Manoharan }
449e7a2a4f5SSujith Manoharan 
ath9k_htc_beacon_config(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)450e7a2a4f5SSujith Manoharan void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
451e7a2a4f5SSujith Manoharan 			     struct ieee80211_vif *vif)
452e7a2a4f5SSujith Manoharan {
453e7a2a4f5SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
4543c4816d9SOleksij Rempel 	struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
455e7a2a4f5SSujith Manoharan 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
456e7a2a4f5SSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
457e7a2a4f5SSujith Manoharan 
458e7a2a4f5SSujith Manoharan 	if (!ath9k_htc_check_beacon_config(priv, vif))
459e7a2a4f5SSujith Manoharan 		return;
460e7a2a4f5SSujith Manoharan 
4611c3652a5SVivek Natarajan 	cur_conf->beacon_interval = bss_conf->beacon_int;
462fcb9392fSSujith 	if (cur_conf->beacon_interval == 0)
463fcb9392fSSujith 		cur_conf->beacon_interval = 100;
464fcb9392fSSujith 
4651c3652a5SVivek Natarajan 	cur_conf->dtim_period = bss_conf->dtim_period;
4661c3652a5SVivek Natarajan 	cur_conf->bmiss_timeout =
4671c3652a5SVivek Natarajan 		ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
4681c3652a5SVivek Natarajan 
469fcb9392fSSujith 	switch (vif->type) {
470fb9987d0SSujith 	case NL80211_IFTYPE_STATION:
4711c3652a5SVivek Natarajan 		ath9k_htc_beacon_config_sta(priv, cur_conf);
472e7a2a4f5SSujith Manoharan 		avp->beacon_configured = true;
473fb9987d0SSujith 		break;
474fb9987d0SSujith 	case NL80211_IFTYPE_ADHOC:
4751c3652a5SVivek Natarajan 		ath9k_htc_beacon_config_adhoc(priv, cur_conf);
476fb9987d0SSujith 		break;
477594e65b6SJavier Cardona 	case NL80211_IFTYPE_MESH_POINT:
478a5fae37dSSujith Manoharan 	case NL80211_IFTYPE_AP:
479a5fae37dSSujith Manoharan 		ath9k_htc_beacon_config_ap(priv, cur_conf);
480a5fae37dSSujith Manoharan 		break;
481fb9987d0SSujith 	default:
482d2182b69SJoe Perches 		ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
483fb9987d0SSujith 		return;
484fb9987d0SSujith 	}
485fb9987d0SSujith }
4867c277349SSujith Manoharan 
ath9k_htc_beacon_reconfig(struct ath9k_htc_priv * priv)4877c277349SSujith Manoharan void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
4887c277349SSujith Manoharan {
4897c277349SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
4903c4816d9SOleksij Rempel 	struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
4917c277349SSujith Manoharan 
4927c277349SSujith Manoharan 	switch (priv->ah->opmode) {
4937c277349SSujith Manoharan 	case NL80211_IFTYPE_STATION:
4947c277349SSujith Manoharan 		ath9k_htc_beacon_config_sta(priv, cur_conf);
4957c277349SSujith Manoharan 		break;
4967c277349SSujith Manoharan 	case NL80211_IFTYPE_ADHOC:
4977c277349SSujith Manoharan 		ath9k_htc_beacon_config_adhoc(priv, cur_conf);
4987c277349SSujith Manoharan 		break;
499594e65b6SJavier Cardona 	case NL80211_IFTYPE_MESH_POINT:
500a5fae37dSSujith Manoharan 	case NL80211_IFTYPE_AP:
501a5fae37dSSujith Manoharan 		ath9k_htc_beacon_config_ap(priv, cur_conf);
502a5fae37dSSujith Manoharan 		break;
5037c277349SSujith Manoharan 	default:
504d2182b69SJoe Perches 		ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
5057c277349SSujith Manoharan 		return;
5067c277349SSujith Manoharan 	}
5077c277349SSujith Manoharan }
508f0e44962SChun-Yeow Yeoh 
ath9k_htc_csa_is_finished(struct ath9k_htc_priv * priv)509f0e44962SChun-Yeow Yeoh bool ath9k_htc_csa_is_finished(struct ath9k_htc_priv *priv)
510f0e44962SChun-Yeow Yeoh {
511f0e44962SChun-Yeow Yeoh 	struct ieee80211_vif *vif;
512f0e44962SChun-Yeow Yeoh 
513f0e44962SChun-Yeow Yeoh 	vif = priv->csa_vif;
514d0a9123eSJohannes Berg 	if (!vif || !vif->bss_conf.csa_active)
515f0e44962SChun-Yeow Yeoh 		return false;
516f0e44962SChun-Yeow Yeoh 
517*6030b3a4SAditya Kumar Singh 	if (!ieee80211_beacon_cntdwn_is_complete(vif, 0))
518f0e44962SChun-Yeow Yeoh 		return false;
519f0e44962SChun-Yeow Yeoh 
52004ada859SAditya Kumar Singh 	ieee80211_csa_finish(vif, 0);
521f0e44962SChun-Yeow Yeoh 
522f0e44962SChun-Yeow Yeoh 	priv->csa_vif = NULL;
523f0e44962SChun-Yeow Yeoh 	return true;
524f0e44962SChun-Yeow Yeoh }
525