beacon.c revision 1a20034a73a40b8056731f9db0c535cec2961eb7
1f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez/*
2cee075a24eec64f1f5b2b3b14753b2d4b8ecce55Sujith * Copyright (c) 2008-2009 Atheros Communications Inc.
3f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *
4f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * Permission to use, copy, modify, and/or distribute this software for any
5f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * purpose with or without fee is hereby granted, provided that the above
6f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * copyright notice and this permission notice appear in all copies.
7f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *
8f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez */
16f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
17394cf0a1ca02e7998c8d01975b60a3cdc121e7d8Sujith#include "ath9k.h"
18f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
195379c8a26686e12058e23322615df68f9123bccdSujith#define FUDGE 2
205379c8a26686e12058e23322615df68f9123bccdSujith
21f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez/*
22f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  This function will modify certain transmit queue properties depending on
23f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
24f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  settings and channel width min/max
25f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez*/
2694db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajanint ath_beaconq_config(struct ath_softc *sc)
27f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
28cbe61d8a41210600bc76b212edcd4dc0f55c014fSujith	struct ath_hw *ah = sc->sc_ah;
29c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(ah);
3094db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan	struct ath9k_tx_queue_info qi, qi_be;
3194db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan	int qnum;
32f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
33b77f483fcf0579de28873828897f53371a33a0eaSujith	ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
342660b81a378ab227b78c4cc618453fa7e19a7c7bSujith	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
35f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/* Always burst out beacon and CAB traffic. */
36f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		qi.tqi_aifs = 1;
37f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		qi.tqi_cwmin = 0;
38f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		qi.tqi_cwmax = 0;
39f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	} else {
40f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/* Adhoc mode; important thing is to use 2x cwmin. */
4194db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan		qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA,
4294db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan				       ATH9K_WME_AC_BE);
4394db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan		ath9k_hw_get_txq_props(ah, qnum, &qi_be);
4494db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan		qi.tqi_aifs = qi_be.tqi_aifs;
4594db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan		qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
4694db29368a658b13a088db87c7b0bf59b1a7492dVivek Natarajan		qi.tqi_cwmax = qi_be.tqi_cwmax;
47f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
48f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
49b77f483fcf0579de28873828897f53371a33a0eaSujith	if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
50c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_FATAL,
51c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "Unable to update h/w beacon queue parameters\n");
52f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return 0;
53f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	} else {
549fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith		ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
55f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return 1;
56f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
57f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
58f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
59f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez/*
60f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  Associates the beacon frame buffer with a transmit descriptor.  Will set
61f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  up all required antenna switch parameters, rate codes, and channel flags.
62f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez *  Beacons are always sent out at the lowest rate, and are not retried.
63f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez*/
649fc9ab0a6929c9f137747df0ecf294e9582607f9Sujithstatic void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
659fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			     struct ath_buf *bf)
66f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
67a22be22ab8fe571cce88d0d30b49f297a563c4a7Sujith	struct sk_buff *skb = bf->bf_mpdu;
68cbe61d8a41210600bc76b212edcd4dc0f55c014fSujith	struct ath_hw *ah = sc->sc_ah;
6943c2761364b77cd7fd20eb1f14cfee4cd1462abdLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(ah);
70f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_desc *ds;
71980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith	struct ath9k_11n_rate_series series[4];
729fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	int flags, antenna, ctsrate = 0, ctsduration = 0;
73545750d36fa78203e28acefb4bab61ebb7c4d197Felix Fietkau	struct ieee80211_supported_band *sband;
74545750d36fa78203e28acefb4bab61ebb7c4d197Felix Fietkau	u8 rate = 0;
75f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
76f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	ds = bf->bf_desc;
77f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	flags = ATH9K_TXDESC_NOACK;
78f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
799cb5412b0760981d43ac3e612992c90cea690e72Pat Erley	if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
809cb5412b0760981d43ac3e612992c90cea690e72Pat Erley	     (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) &&
812660b81a378ab227b78c4cc618453fa7e19a7c7bSujith	    (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
82f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		ds->ds_link = bf->bf_daddr; /* self-linked */
83f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		flags |= ATH9K_TXDESC_VEOL;
84f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/* Let hardware handle antenna switching. */
85f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		antenna = 0;
86f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	} else {
87f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		ds->ds_link = 0;
88f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/*
89f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 * Switch antenna every beacon.
909fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith		 * Should only switch every beacon period, not for every SWBA
919fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith		 * XXX assumes two antennae
92f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 */
9317d7904de85125c62c7258d7cb21207f26d04048Sujith		antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1);
94f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
95f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
96f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	ds->ds_data = bf->bf_buf_addr;
97f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
98545750d36fa78203e28acefb4bab61ebb7c4d197Felix Fietkau	sband = &sc->sbands[common->hw->conf.channel->band];
99545750d36fa78203e28acefb4bab61ebb7c4d197Felix Fietkau	rate = sband->bitrates[0].hw_value;
100672840ac04f79f499b60b9f0eb41799c837db4ebSujith	if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
101545750d36fa78203e28acefb4bab61ebb7c4d197Felix Fietkau		rate |= sband->bitrates[0].hw_value_short;
1029fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
1039fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN,
1049fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       ATH9K_PKT_TYPE_BEACON,
1059fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       MAX_RATE_POWER,
1069fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       ATH9K_TXKEYIX_INVALID,
1079fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       ATH9K_KEY_TYPE_CLEAR,
1089fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       flags);
109f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
110f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/* NB: beacon's BufLen must be a multiple of 4 bytes */
1119fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4),
1129fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			    true, true, ds);
113f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
1140345f37be64905846a1ef38378061d4c2730242eLuis R. Rodriguez	memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
115f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	series[0].Tries = 1;
116f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	series[0].Rate = rate;
11743c2761364b77cd7fd20eb1f14cfee4cd1462abdLuis R. Rodriguez	series[0].ChSel = common->tx_chainmask;
118f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
1199fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration,
1209fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith				     series, 4, 0);
121f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
122f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
123c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinenstatic struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
1242c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen					   struct ieee80211_vif *vif)
125f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
126c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	struct ath_wiphy *aphy = hw->priv;
127c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	struct ath_softc *sc = aphy->sc;
128c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
129f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_buf *bf;
13017d7904de85125c62c7258d7cb21207f26d04048Sujith	struct ath_vif *avp;
131f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct sk_buff *skb;
132f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_txq *cabq;
133147583c057c43095925b5f331fe304f2d5b997baJouni Malinen	struct ieee80211_tx_info *info;
134980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith	int cabq_depth;
135980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
136f0ed85c6c7960b26666db013e02e748b56eef98aJouni Malinen	if (aphy->state != ATH_WIPHY_ACTIVE)
137f0ed85c6c7960b26666db013e02e748b56eef98aJouni Malinen		return NULL;
138f0ed85c6c7960b26666db013e02e748b56eef98aJouni Malinen
1395640b08ef7e88b606c740e746cb77bc97d78508eSujith	avp = (void *)vif->drv_priv;
140b77f483fcf0579de28873828897f53371a33a0eaSujith	cabq = sc->beacon.cabq;
141f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
142d8baa9392666d1c50ef42e9f6fbbb0cf536327b9Sujith	if (avp->av_bcbuf == NULL)
143f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return NULL;
144980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
1459fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	/* Release the old beacon first */
1469fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
147f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	bf = avp->av_bcbuf;
148a22be22ab8fe571cce88d0d30b49f297a563c4a7Sujith	skb = bf->bf_mpdu;
149a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	if (skb) {
1507da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos		dma_unmap_single(sc->dev, bf->bf_dmacontext,
1519fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith				 skb->len, DMA_TO_DEVICE);
1523fbb9d95a96c6a03f2e484bb1665d089412640dcJouni Malinen		dev_kfree_skb_any(skb);
153a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	}
154f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
1559fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	/* Get a new beacon from mac80211 */
1569fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
157c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	skb = ieee80211_beacon_get(hw, vif);
158a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	bf->bf_mpdu = skb;
159a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	if (skb == NULL)
160a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen		return NULL;
1614ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
1624ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		avp->tsf_adjust;
163980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
164147583c057c43095925b5f331fe304f2d5b997baJouni Malinen	info = IEEE80211_SKB_CB(skb);
165147583c057c43095925b5f331fe304f2d5b997baJouni Malinen	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
166147583c057c43095925b5f331fe304f2d5b997baJouni Malinen		/*
167147583c057c43095925b5f331fe304f2d5b997baJouni Malinen		 * TODO: make sure the seq# gets assigned properly (vs. other
168147583c057c43095925b5f331fe304f2d5b997baJouni Malinen		 * TX frames)
169147583c057c43095925b5f331fe304f2d5b997baJouni Malinen		 */
170980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
171b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->tx.seq_no += 0x10;
172147583c057c43095925b5f331fe304f2d5b997baJouni Malinen		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
173b77f483fcf0579de28873828897f53371a33a0eaSujith		hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
174147583c057c43095925b5f331fe304f2d5b997baJouni Malinen	}
175980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
176a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	bf->bf_buf_addr = bf->bf_dmacontext =
1777da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos		dma_map_single(sc->dev, skb->data,
1789fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       skb->len, DMA_TO_DEVICE);
1797da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
180f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		dev_kfree_skb_any(skb);
181f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		bf->bf_mpdu = NULL;
182c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_FATAL,
183c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "dma_mapping_error on beaconing\n");
184f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		return NULL;
185f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez	}
186f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
187c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	skb = ieee80211_get_buffered_bc(hw, vif);
188f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
189f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/*
190f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * if the CABQ traffic from previous DTIM is pending and the current
191f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *  beacon is also a DTIM.
19217d7904de85125c62c7258d7cb21207f26d04048Sujith	 *  1) if there is only one vif let the cab traffic continue.
19317d7904de85125c62c7258d7cb21207f26d04048Sujith	 *  2) if there are more than one vif and we are using staggered
194f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     beacons, then drain the cabq by dropping all the frames in
19517d7904de85125c62c7258d7cb21207f26d04048Sujith	 *     the cabq so that the current vifs cab traffic can be scheduled.
196f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 */
197f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	spin_lock_bh(&cabq->axq_lock);
198f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	cabq_depth = cabq->axq_depth;
199f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	spin_unlock_bh(&cabq->axq_lock);
200f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
201e022edbd2bfb5f9a7ddf1cca43544f7b54c6fe02Jouni Malinen	if (skb && cabq_depth) {
20217d7904de85125c62c7258d7cb21207f26d04048Sujith		if (sc->nvifs > 1) {
203c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			ath_print(common, ATH_DBG_BEACON,
204c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez				  "Flushing previous cabq traffic\n");
2059fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			ath_draintxq(sc, cabq, false);
206f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
207f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
208f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
209f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	ath_beacon_setup(sc, avp, bf);
210f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
211e022edbd2bfb5f9a7ddf1cca43544f7b54c6fe02Jouni Malinen	while (skb) {
212c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen		ath_tx_cabq(hw, skb);
213c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen		skb = ieee80211_get_buffered_bc(hw, vif);
214e022edbd2bfb5f9a7ddf1cca43544f7b54c6fe02Jouni Malinen	}
215f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
216f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	return bf;
217f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
218f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
219f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez/*
220f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * Startup beacon transmission for adhoc mode when they are sent entirely
221f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez * by the hardware using the self-linked descriptor + veol trick.
222f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez*/
2232c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinenstatic void ath_beacon_start_adhoc(struct ath_softc *sc,
2242c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen				   struct ieee80211_vif *vif)
225f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
226cbe61d8a41210600bc76b212edcd4dc0f55c014fSujith	struct ath_hw *ah = sc->sc_ah;
227c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(ah);
228f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_buf *bf;
22917d7904de85125c62c7258d7cb21207f26d04048Sujith	struct ath_vif *avp;
230f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct sk_buff *skb;
231f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
2325640b08ef7e88b606c740e746cb77bc97d78508eSujith	avp = (void *)vif->drv_priv;
233f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
2349fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	if (avp->av_bcbuf == NULL)
235f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return;
2369fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
237f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	bf = avp->av_bcbuf;
238a22be22ab8fe571cce88d0d30b49f297a563c4a7Sujith	skb = bf->bf_mpdu;
239f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
240f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	ath_beacon_setup(sc, avp, bf);
241f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
242f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/* NB: caller is known to have already stopped tx dma */
243b77f483fcf0579de28873828897f53371a33a0eaSujith	ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
244b77f483fcf0579de28873828897f53371a33a0eaSujith	ath9k_hw_txstart(ah, sc->beacon.beaconq);
245c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	ath_print(common, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n",
246c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc);
247f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
248f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
249c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinenint ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
250f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
251c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	struct ath_softc *sc = aphy->sc;
252c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
25317d7904de85125c62c7258d7cb21207f26d04048Sujith	struct ath_vif *avp;
254f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_buf *bf;
255f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct sk_buff *skb;
256459f5f90f1bd959ced04761406415b178b315177Sujith	__le64 tstamp;
257f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
2585640b08ef7e88b606c740e746cb77bc97d78508eSujith	avp = (void *)vif->drv_priv;
259f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
260f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/* Allocate a beacon descriptor if we haven't done so. */
261f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (!avp->av_bcbuf) {
262980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith		/* Allocate beacon state for hostap/ibss.  We know
263980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith		 * a buffer is available. */
264b77f483fcf0579de28873828897f53371a33a0eaSujith		avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf,
265980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith						 struct ath_buf, list);
266f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		list_del(&avp->av_bcbuf->list);
267f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
2682660b81a378ab227b78c4cc618453fa7e19a7c7bSujith		if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
2692660b81a378ab227b78c4cc618453fa7e19a7c7bSujith		    !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
270f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			int slot;
271f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			/*
27217d7904de85125c62c7258d7cb21207f26d04048Sujith			 * Assign the vif to a beacon xmit slot. As
273f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			 * above, this cannot fail to find one.
274f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			 */
275f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			avp->av_bslot = 0;
276f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			for (slot = 0; slot < ATH_BCBUF; slot++)
2772c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen				if (sc->beacon.bslot[slot] == NULL) {
278f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					/*
279f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					 * XXX hack, space out slots to better
280f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					 * deal with misses
281f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					 */
282f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					if (slot+1 < ATH_BCBUF &&
2832c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen					    sc->beacon.bslot[slot+1] == NULL) {
284f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez						avp->av_bslot = slot+1;
285f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez						break;
286f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					}
287f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					avp->av_bslot = slot;
288f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez					/* NB: keep looking for a double slot */
289f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez				}
2902c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen			BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
2912c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen			sc->beacon.bslot[avp->av_bslot] = vif;
292c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen			sc->beacon.bslot_aphy[avp->av_bslot] = aphy;
29317d7904de85125c62c7258d7cb21207f26d04048Sujith			sc->nbcnvifs++;
294f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
295f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
296f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
2979fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	/* release the previous beacon frame, if it already exists. */
298f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	bf = avp->av_bcbuf;
299f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (bf->bf_mpdu != NULL) {
300a22be22ab8fe571cce88d0d30b49f297a563c4a7Sujith		skb = bf->bf_mpdu;
3017da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos		dma_unmap_single(sc->dev, bf->bf_dmacontext,
3029fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith				 skb->len, DMA_TO_DEVICE);
303f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		dev_kfree_skb_any(skb);
304f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		bf->bf_mpdu = NULL;
305f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
306f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
3079fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith	/* NB: the beacon data buffer must be 32-bit aligned. */
3085640b08ef7e88b606c740e746cb77bc97d78508eSujith	skb = ieee80211_beacon_get(sc->hw, vif);
309f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (skb == NULL) {
310c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_BEACON, "cannot get skb\n");
311f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return -ENOMEM;
312f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
313f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
314459f5f90f1bd959ced04761406415b178b315177Sujith	tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
315b77f483fcf0579de28873828897f53371a33a0eaSujith	sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
3164ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	/* Calculate a TSF adjustment factor required for staggered beacons. */
317f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (avp->av_bslot > 0) {
318f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		u64 tsfadjust;
319f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		int intval;
320f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
32157c4d7b4c4986037be51476b8e3025d5ba18d8b8Johannes Berg		intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
322f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
323f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/*
3244ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * Calculate the TSF offset for this beacon slot, i.e., the
3254ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * number of usecs that need to be added to the timestamp field
3264ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * in Beacon and Probe Response frames. Beacon slot 0 is
3274ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * processed at the correct offset, so it does not require TSF
3284ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * adjustment. Other slots are adjusted to get the timestamp
3294ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		 * close to the TBTT for the BSS.
330f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 */
3314ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
3324ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
333f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
334c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_BEACON,
335c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "stagger beacons, bslot %d intval "
336c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "%u tsfadjust %llu\n",
337c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  avp->av_bslot, intval, (unsigned long long)tsfadjust);
338f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
3394ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
3404ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen			avp->tsf_adjust;
3414ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	} else
3424ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen		avp->tsf_adjust = cpu_to_le64(0);
343f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
344f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez	bf->bf_mpdu = skb;
345a8fff50e4d6aad520b261b3c32e2c67a7dfb7228Jouni Malinen	bf->bf_buf_addr = bf->bf_dmacontext =
3467da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos		dma_map_single(sc->dev, skb->data,
3479fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith			       skb->len, DMA_TO_DEVICE);
3487da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
349f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		dev_kfree_skb_any(skb);
350f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		bf->bf_mpdu = NULL;
351c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_FATAL,
352c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "dma_mapping_error on beacon alloc\n");
353f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez		return -ENOMEM;
354f8316df10c4e3bec5b4c3a5a8e026c577640c3a6Luis R. Rodriguez	}
355f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
356f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	return 0;
357f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
358f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
35917d7904de85125c62c7258d7cb21207f26d04048Sujithvoid ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp)
360f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
361f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (avp->av_bcbuf != NULL) {
362f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		struct ath_buf *bf;
363f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
364f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		if (avp->av_bslot != -1) {
3652c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen			sc->beacon.bslot[avp->av_bslot] = NULL;
366c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen			sc->beacon.bslot_aphy[avp->av_bslot] = NULL;
36717d7904de85125c62c7258d7cb21207f26d04048Sujith			sc->nbcnvifs--;
368f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
369f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
370f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		bf = avp->av_bcbuf;
371f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		if (bf->bf_mpdu != NULL) {
372a22be22ab8fe571cce88d0d30b49f297a563c4a7Sujith			struct sk_buff *skb = bf->bf_mpdu;
3737da3c55ce849e17fd9017c7bf770a03fa083d95bGabor Juhos			dma_unmap_single(sc->dev, bf->bf_dmacontext,
3749fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith					 skb->len, DMA_TO_DEVICE);
375f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			dev_kfree_skb_any(skb);
376f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			bf->bf_mpdu = NULL;
377f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
378b77f483fcf0579de28873828897f53371a33a0eaSujith		list_add_tail(&bf->list, &sc->beacon.bbuf);
379f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
380f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		avp->av_bcbuf = NULL;
381f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
382f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
383f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
3849fc9ab0a6929c9f137747df0ecf294e9582607f9Sujithvoid ath_beacon_tasklet(unsigned long data)
385f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
386f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_softc *sc = (struct ath_softc *)data;
387cbe61d8a41210600bc76b212edcd4dc0f55c014fSujith	struct ath_hw *ah = sc->sc_ah;
388c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(ah);
389f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	struct ath_buf *bf = NULL;
3902c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen	struct ieee80211_vif *vif;
391c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen	struct ath_wiphy *aphy;
3922c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen	int slot;
3939546aae0863c12a3d00b1ed5cbd316520733200bSujith	u32 bfaddr, bc = 0, tsftu;
394f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	u64 tsf;
395f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	u16 intval;
396f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
397f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/*
398f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * Check if the previous beacon has gone out.  If
399f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * not don't try to post another, skip this period
400f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * and wait for the next.  Missed beacons indicate
401f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * a problem and should not occur.  If we miss too
402f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * many consecutive beacons reset the device.
403f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 */
404b77f483fcf0579de28873828897f53371a33a0eaSujith	if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
405b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->beacon.bmisscnt++;
4069546aae0863c12a3d00b1ed5cbd316520733200bSujith
407b77f483fcf0579de28873828897f53371a33a0eaSujith		if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
408c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			ath_print(common, ATH_DBG_BEACON,
409c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez				  "missed %u consecutive beacons\n",
410c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez				  sc->beacon.bmisscnt);
411b77f483fcf0579de28873828897f53371a33a0eaSujith		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
412c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			ath_print(common, ATH_DBG_BEACON,
413c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez				  "beacon is officially stuck\n");
414b74444f8a9039603715973a56df588a5d800c4efJeff Hansen			sc->sc_flags |= SC_OP_TSF_RESET;
4159546aae0863c12a3d00b1ed5cbd316520733200bSujith			ath_reset(sc, false);
416f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
4179546aae0863c12a3d00b1ed5cbd316520733200bSujith
418f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		return;
419f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
420980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
421b77f483fcf0579de28873828897f53371a33a0eaSujith	if (sc->beacon.bmisscnt != 0) {
422c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_BEACON,
423c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "resume beacon xmit after %u misses\n",
424c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  sc->beacon.bmisscnt);
425b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->beacon.bmisscnt = 0;
426f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
427f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
428f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/*
429f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * Generate beacon frames. we are sending frames
430f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * staggered so calculate the slot for this frame based
431f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * on the tsf to safeguard against missing an swba.
432f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 */
433f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
43457c4d7b4c4986037be51476b8e3025d5ba18d8b8Johannes Berg	intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
435f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
436f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	tsf = ath9k_hw_gettsf64(ah);
437f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	tsftu = TSF_TO_TU(tsf>>32, tsf);
438f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	slot = ((tsftu % intval) * ATH_BCBUF) / intval;
4394ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	/*
4404ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 * Reverse the slot order to get slot 0 on the TBTT offset that does
4414ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 * not require TSF adjustment and other slots adding
4424ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 * slot/ATH_BCBUF * beacon_int to timestamp. For example, with
4434ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 ..
4444ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 * and slot 0 is at correct offset to TBTT.
4454ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	 */
4464ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	slot = ATH_BCBUF - slot - 1;
4474ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	vif = sc->beacon.bslot[slot];
4484ed96f04f8a1869757f4dd4a9283a18ec63c442fJouni Malinen	aphy = sc->beacon.bslot_aphy[slot];
449980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
450c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	ath_print(common, ATH_DBG_BEACON,
451c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
452c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  slot, tsf, tsftu, intval, vif);
453980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
454f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	bfaddr = 0;
4552c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen	if (vif) {
456c52f33d05e5f8d59f02722fbc308f5f391575ca5Jouni Malinen		bf = ath_beacon_generate(aphy->hw, vif);
457f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		if (bf != NULL) {
458f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			bfaddr = bf->bf_daddr;
459f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez			bc = 1;
460f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
461f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
4629546aae0863c12a3d00b1ed5cbd316520733200bSujith
463f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	/*
464f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * Handle slot time change when a non-ERP station joins/leaves
465f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * an 11g network.  The 802.11 layer notifies us via callback,
466f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * we mark updateslot, then wait one beacon before effecting
467f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * the change.  This gives associated stations at least one
468f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * beacon interval to note the state change.
469f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *
470f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 * NB: The slot time change state machine is clocked according
471f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     to whether we are bursting or staggering beacons.  We
472f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     recognize the request to update and record the current
473f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     slot then don't transition until that slot is reached
474f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     again.  If we miss a beacon for that slot then we'll be
475f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     slow to transition but we'll be sure at least one beacon
476f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     interval has passed.  When bursting slot is always left
477f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 *     set to ATH_BCBUF so this check is a noop.
478f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	 */
479b77f483fcf0579de28873828897f53371a33a0eaSujith	if (sc->beacon.updateslot == UPDATE) {
480b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->beacon.updateslot = COMMIT; /* commit next beacon */
481b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->beacon.slotupdate = slot;
482b77f483fcf0579de28873828897f53371a33a0eaSujith	} else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) {
4830005baf4a31efe6de6f922f73ccbd3762a110062Felix Fietkau		ah->slottime = sc->beacon.slottime;
4840005baf4a31efe6de6f922f73ccbd3762a110062Felix Fietkau		ath9k_hw_init_global_settings(ah);
485b77f483fcf0579de28873828897f53371a33a0eaSujith		sc->beacon.updateslot = OK;
486ff37e337beb838d4c2540fa93b2c4c632ee17750Sujith	}
487f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	if (bfaddr != 0) {
488f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/*
489f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 * Stop any current dma and put the new frame(s) on the queue.
490f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 * This should never fail since we check above that no frames
491f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 * are still pending on the queue.
492f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		 */
493b77f483fcf0579de28873828897f53371a33a0eaSujith		if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) {
494c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			ath_print(common, ATH_DBG_FATAL,
495b77f483fcf0579de28873828897f53371a33a0eaSujith				"beacon queue %u did not stop?\n", sc->beacon.beaconq);
496f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		}
497f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
498f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez		/* NB: cabq traffic should already be queued and primed */
499b77f483fcf0579de28873828897f53371a33a0eaSujith		ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
500b77f483fcf0579de28873828897f53371a33a0eaSujith		ath9k_hw_txstart(ah, sc->beacon.beaconq);
501f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
50217d7904de85125c62c7258d7cb21207f26d04048Sujith		sc->beacon.ast_be_xmit += bc;     /* XXX per-vif? */
503f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
504f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
505f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
50621526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguezstatic void ath9k_beacon_init(struct ath_softc *sc,
50721526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez			      u32 next_beacon,
50821526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez			      u32 beacon_period)
50921526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez{
51021526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez	if (beacon_period & ATH9K_BEACON_RESET_TSF)
51121526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez		ath9k_ps_wakeup(sc);
51221526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez
51321526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez	ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
51421526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez
51521526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez	if (beacon_period & ATH9K_BEACON_RESET_TSF)
51621526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez		ath9k_ps_restore(sc);
51721526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez}
51821526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez
519f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez/*
5205379c8a26686e12058e23322615df68f9123bccdSujith * For multi-bss ap support beacons are either staggered evenly over N slots or
5215379c8a26686e12058e23322615df68f9123bccdSujith * burst together.  For the former arrange for the SWBA to be delivered for each
5225379c8a26686e12058e23322615df68f9123bccdSujith * slot. Slots that are not occupied will generate nothing.
523f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez */
5245379c8a26686e12058e23322615df68f9123bccdSujithstatic void ath_beacon_config_ap(struct ath_softc *sc,
525d31e20af9f65e38429a3ed32175f8e233bdcd2b2Vasanthakumar Thiagarajan				 struct ath_beacon_config *conf)
526f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
527980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith	u32 nexttbtt, intval;
528f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
529b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith	/* Configure the timers only when the TSF has to be reset */
530b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith
531b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith	if (!(sc->sc_flags & SC_OP_TSF_RESET))
532b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith		return;
533b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith
5345379c8a26686e12058e23322615df68f9123bccdSujith	/* NB: the beacon interval is kept internally in TU's */
5355379c8a26686e12058e23322615df68f9123bccdSujith	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
5365379c8a26686e12058e23322615df68f9123bccdSujith	intval /= ATH_BCBUF;    /* for staggered beacons */
5375379c8a26686e12058e23322615df68f9123bccdSujith	nexttbtt = intval;
5385379c8a26686e12058e23322615df68f9123bccdSujith	intval |= ATH9K_BEACON_RESET_TSF;
539f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
5405379c8a26686e12058e23322615df68f9123bccdSujith	/*
5415379c8a26686e12058e23322615df68f9123bccdSujith	 * In AP mode we enable the beacon timers and SWBA interrupts to
5425379c8a26686e12058e23322615df68f9123bccdSujith	 * prepare beacon frames.
5435379c8a26686e12058e23322615df68f9123bccdSujith	 */
5445379c8a26686e12058e23322615df68f9123bccdSujith	intval |= ATH9K_BEACON_ENA;
5455379c8a26686e12058e23322615df68f9123bccdSujith	sc->imask |= ATH9K_INT_SWBA;
5465379c8a26686e12058e23322615df68f9123bccdSujith	ath_beaconq_config(sc);
547f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
5485379c8a26686e12058e23322615df68f9123bccdSujith	/* Set the computed AP beacon timers */
549f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
5505379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, 0);
55121526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez	ath9k_beacon_init(sc, nexttbtt, intval);
5525379c8a26686e12058e23322615df68f9123bccdSujith	sc->beacon.bmisscnt = 0;
5535379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
554b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith
555b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith	/* Clear the reset TSF flag, so that subsequent beacon updation
556b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith	   will not reset the HW TSF. */
557b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith
558b238e90e99fe51aed14d20eae8a6a1c04ce4ca30Sujith	sc->sc_flags &= ~SC_OP_TSF_RESET;
5595379c8a26686e12058e23322615df68f9123bccdSujith}
560459f5f90f1bd959ced04761406415b178b315177Sujith
5615379c8a26686e12058e23322615df68f9123bccdSujith/*
5625379c8a26686e12058e23322615df68f9123bccdSujith * This sets up the beacon timers according to the timestamp of the last
5635379c8a26686e12058e23322615df68f9123bccdSujith * received beacon and the current TSF, configures PCF and DTIM
5645379c8a26686e12058e23322615df68f9123bccdSujith * handling, programs the sleep registers so the hardware will wakeup in
5655379c8a26686e12058e23322615df68f9123bccdSujith * time to receive beacons, and configures the beacon miss handling so
5665379c8a26686e12058e23322615df68f9123bccdSujith * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
5675379c8a26686e12058e23322615df68f9123bccdSujith * we've associated with.
5685379c8a26686e12058e23322615df68f9123bccdSujith */
5695379c8a26686e12058e23322615df68f9123bccdSujithstatic void ath_beacon_config_sta(struct ath_softc *sc,
570d31e20af9f65e38429a3ed32175f8e233bdcd2b2Vasanthakumar Thiagarajan				  struct ath_beacon_config *conf)
5715379c8a26686e12058e23322615df68f9123bccdSujith{
572c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
5735379c8a26686e12058e23322615df68f9123bccdSujith	struct ath9k_beacon_state bs;
5745379c8a26686e12058e23322615df68f9123bccdSujith	int dtimperiod, dtimcount, sleepduration;
5755379c8a26686e12058e23322615df68f9123bccdSujith	int cfpperiod, cfpcount;
5765379c8a26686e12058e23322615df68f9123bccdSujith	u32 nexttbtt = 0, intval, tsftu;
5775379c8a26686e12058e23322615df68f9123bccdSujith	u64 tsf;
578267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	int num_beacons, offset, dtim_dec_count, cfp_dec_count;
5795379c8a26686e12058e23322615df68f9123bccdSujith
5801a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian	/* No need to configure beacon if we are not associated */
5811a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian	if (!common->curaid) {
5821a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian		ath_print(common, ATH_DBG_BEACON,
5831a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian			 "STA is not yet associated..skipping beacon config\n");
5841a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian		return;
5851a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian	}
5861a20034a73a40b8056731f9db0c535cec2961eb7Senthil Balasubramanian
5875379c8a26686e12058e23322615df68f9123bccdSujith	memset(&bs, 0, sizeof(bs));
5885379c8a26686e12058e23322615df68f9123bccdSujith	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
5895379c8a26686e12058e23322615df68f9123bccdSujith
5905379c8a26686e12058e23322615df68f9123bccdSujith	/*
5915379c8a26686e12058e23322615df68f9123bccdSujith	 * Setup dtim and cfp parameters according to
5925379c8a26686e12058e23322615df68f9123bccdSujith	 * last beacon we received (which may be none).
5935379c8a26686e12058e23322615df68f9123bccdSujith	 */
5945379c8a26686e12058e23322615df68f9123bccdSujith	dtimperiod = conf->dtim_period;
5955379c8a26686e12058e23322615df68f9123bccdSujith	if (dtimperiod <= 0)		/* NB: 0 if not known */
5965379c8a26686e12058e23322615df68f9123bccdSujith		dtimperiod = 1;
5975379c8a26686e12058e23322615df68f9123bccdSujith	dtimcount = conf->dtim_count;
5985379c8a26686e12058e23322615df68f9123bccdSujith	if (dtimcount >= dtimperiod)	/* NB: sanity check */
5995379c8a26686e12058e23322615df68f9123bccdSujith		dtimcount = 0;
6005379c8a26686e12058e23322615df68f9123bccdSujith	cfpperiod = 1;			/* NB: no PCF support yet */
6015379c8a26686e12058e23322615df68f9123bccdSujith	cfpcount = 0;
6025379c8a26686e12058e23322615df68f9123bccdSujith
6035379c8a26686e12058e23322615df68f9123bccdSujith	sleepduration = conf->listen_interval * intval;
6045379c8a26686e12058e23322615df68f9123bccdSujith	if (sleepduration <= 0)
6055379c8a26686e12058e23322615df68f9123bccdSujith		sleepduration = intval;
6065379c8a26686e12058e23322615df68f9123bccdSujith
6075379c8a26686e12058e23322615df68f9123bccdSujith	/*
6085379c8a26686e12058e23322615df68f9123bccdSujith	 * Pull nexttbtt forward to reflect the current
6095379c8a26686e12058e23322615df68f9123bccdSujith	 * TSF and calculate dtim+cfp state for the result.
6105379c8a26686e12058e23322615df68f9123bccdSujith	 */
6115379c8a26686e12058e23322615df68f9123bccdSujith	tsf = ath9k_hw_gettsf64(sc->sc_ah);
6125379c8a26686e12058e23322615df68f9123bccdSujith	tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
613267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen
614267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	num_beacons = tsftu / intval + 1;
615267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	offset = tsftu % intval;
616267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	nexttbtt = tsftu - offset;
617267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	if (offset)
6185379c8a26686e12058e23322615df68f9123bccdSujith		nexttbtt += intval;
619267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen
620267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	/* DTIM Beacon every dtimperiod Beacon */
621267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	dtim_dec_count = num_beacons % dtimperiod;
622267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	/* CFP every cfpperiod DTIM Beacon */
623267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
624267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	if (dtim_dec_count)
625267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen		cfp_dec_count++;
626267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen
627267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	dtimcount -= dtim_dec_count;
628267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	if (dtimcount < 0)
629267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen		dtimcount += dtimperiod;
630267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen
631267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	cfpcount -= cfp_dec_count;
632267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen	if (cfpcount < 0)
633267a90127472be70b02ab13cbd355b5013e2aa51Jouni Malinen		cfpcount += cfpperiod;
6345379c8a26686e12058e23322615df68f9123bccdSujith
6355379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_intval = intval;
6365379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_nexttbtt = nexttbtt;
6375379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_dtimperiod = dtimperiod*intval;
6385379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
6395379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
6405379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
6415379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_cfpmaxduration = 0;
6425379c8a26686e12058e23322615df68f9123bccdSujith
6435379c8a26686e12058e23322615df68f9123bccdSujith	/*
6445379c8a26686e12058e23322615df68f9123bccdSujith	 * Calculate the number of consecutive beacons to miss* before taking
6455379c8a26686e12058e23322615df68f9123bccdSujith	 * a BMISS interrupt. The configuration is specified in TU so we only
6465379c8a26686e12058e23322615df68f9123bccdSujith	 * need calculate based	on the beacon interval.  Note that we clamp the
6475379c8a26686e12058e23322615df68f9123bccdSujith	 * result to at most 15 beacons.
6485379c8a26686e12058e23322615df68f9123bccdSujith	 */
6495379c8a26686e12058e23322615df68f9123bccdSujith	if (sleepduration > intval) {
6505379c8a26686e12058e23322615df68f9123bccdSujith		bs.bs_bmissthreshold = conf->listen_interval *
6515379c8a26686e12058e23322615df68f9123bccdSujith			ATH_DEFAULT_BMISS_LIMIT / 2;
652f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	} else {
6535379c8a26686e12058e23322615df68f9123bccdSujith		bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval);
6545379c8a26686e12058e23322615df68f9123bccdSujith		if (bs.bs_bmissthreshold > 15)
6555379c8a26686e12058e23322615df68f9123bccdSujith			bs.bs_bmissthreshold = 15;
6565379c8a26686e12058e23322615df68f9123bccdSujith		else if (bs.bs_bmissthreshold <= 0)
6575379c8a26686e12058e23322615df68f9123bccdSujith			bs.bs_bmissthreshold = 1;
658f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez	}
659f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
6605379c8a26686e12058e23322615df68f9123bccdSujith	/*
6615379c8a26686e12058e23322615df68f9123bccdSujith	 * Calculate sleep duration. The configuration is given in ms.
6625379c8a26686e12058e23322615df68f9123bccdSujith	 * We ensure a multiple of the beacon period is used. Also, if the sleep
6635379c8a26686e12058e23322615df68f9123bccdSujith	 * duration is greater than the DTIM period then it makes senses
6645379c8a26686e12058e23322615df68f9123bccdSujith	 * to make it a multiple of that.
6655379c8a26686e12058e23322615df68f9123bccdSujith	 *
6665379c8a26686e12058e23322615df68f9123bccdSujith	 * XXX fixed at 100ms
6675379c8a26686e12058e23322615df68f9123bccdSujith	 */
668980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
6695379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
6705379c8a26686e12058e23322615df68f9123bccdSujith	if (bs.bs_sleepduration > bs.bs_dtimperiod)
6715379c8a26686e12058e23322615df68f9123bccdSujith		bs.bs_sleepduration = bs.bs_dtimperiod;
672980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
6735379c8a26686e12058e23322615df68f9123bccdSujith	/* TSF out of range threshold fixed at 1 second */
6745379c8a26686e12058e23322615df68f9123bccdSujith	bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
675f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
676c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
677c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	ath_print(common, ATH_DBG_BEACON,
678c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
679c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  bs.bs_bmissthreshold, bs.bs_sleepduration,
680c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
681f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
6825379c8a26686e12058e23322615df68f9123bccdSujith	/* Set the computed STA beacon timers */
683980b24da6f1725c2d0b32c9484d06cd7d09d3c4bSujith
6845379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, 0);
6855379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
6865379c8a26686e12058e23322615df68f9123bccdSujith	sc->imask |= ATH9K_INT_BMISS;
6875379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
6885379c8a26686e12058e23322615df68f9123bccdSujith}
689f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
6905379c8a26686e12058e23322615df68f9123bccdSujithstatic void ath_beacon_config_adhoc(struct ath_softc *sc,
6915379c8a26686e12058e23322615df68f9123bccdSujith				    struct ath_beacon_config *conf,
6922c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen				    struct ieee80211_vif *vif)
6935379c8a26686e12058e23322615df68f9123bccdSujith{
694c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
6955379c8a26686e12058e23322615df68f9123bccdSujith	u64 tsf;
6965379c8a26686e12058e23322615df68f9123bccdSujith	u32 tsftu, intval, nexttbtt;
697f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
6985379c8a26686e12058e23322615df68f9123bccdSujith	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
699f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
700546256fbd06d70a87381020ea8553fb78c9abf43Jouni Malinen
7015379c8a26686e12058e23322615df68f9123bccdSujith	/* Pull nexttbtt forward to reflect the current TSF */
7024af9cf4fda28c5f794861c52e0db5a3de9ee574dSujith
7035379c8a26686e12058e23322615df68f9123bccdSujith	nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
7045379c8a26686e12058e23322615df68f9123bccdSujith	if (nexttbtt == 0)
7055379c8a26686e12058e23322615df68f9123bccdSujith                nexttbtt = intval;
7065379c8a26686e12058e23322615df68f9123bccdSujith        else if (intval)
7075379c8a26686e12058e23322615df68f9123bccdSujith                nexttbtt = roundup(nexttbtt, intval);
7089fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
7095379c8a26686e12058e23322615df68f9123bccdSujith	tsf = ath9k_hw_gettsf64(sc->sc_ah);
7105379c8a26686e12058e23322615df68f9123bccdSujith	tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
7115379c8a26686e12058e23322615df68f9123bccdSujith	do {
7125379c8a26686e12058e23322615df68f9123bccdSujith		nexttbtt += intval;
7135379c8a26686e12058e23322615df68f9123bccdSujith	} while (nexttbtt < tsftu);
714f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
715c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	ath_print(common, ATH_DBG_BEACON,
716c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  "IBSS nexttbtt %u intval %u (%u)\n",
717c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		  nexttbtt, intval, conf->beacon_interval);
7189fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
7195379c8a26686e12058e23322615df68f9123bccdSujith	/*
7205379c8a26686e12058e23322615df68f9123bccdSujith	 * In IBSS mode enable the beacon timers but only enable SWBA interrupts
7215379c8a26686e12058e23322615df68f9123bccdSujith	 * if we need to manually prepare beacon frames.  Otherwise we use a
7225379c8a26686e12058e23322615df68f9123bccdSujith	 * self-linked tx descriptor and let the hardware deal with things.
7235379c8a26686e12058e23322615df68f9123bccdSujith	 */
7245379c8a26686e12058e23322615df68f9123bccdSujith	intval |= ATH9K_BEACON_ENA;
7255379c8a26686e12058e23322615df68f9123bccdSujith	if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL))
7265379c8a26686e12058e23322615df68f9123bccdSujith		sc->imask |= ATH9K_INT_SWBA;
7279fc9ab0a6929c9f137747df0ecf294e9582607f9Sujith
7285379c8a26686e12058e23322615df68f9123bccdSujith	ath_beaconq_config(sc);
7295379c8a26686e12058e23322615df68f9123bccdSujith
7305379c8a26686e12058e23322615df68f9123bccdSujith	/* Set the computed ADHOC beacon timers */
7315379c8a26686e12058e23322615df68f9123bccdSujith
7325379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, 0);
73321526d57ad98635fe12dc8efe46a3d992439a443Luis R. Rodriguez	ath9k_beacon_init(sc, nexttbtt, intval);
7345379c8a26686e12058e23322615df68f9123bccdSujith	sc->beacon.bmisscnt = 0;
7355379c8a26686e12058e23322615df68f9123bccdSujith	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
7365379c8a26686e12058e23322615df68f9123bccdSujith
7376b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	/* FIXME: Handle properly when vif is NULL */
7386b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	if (vif && sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
7392c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen		ath_beacon_start_adhoc(sc, vif);
740f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
741f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez
7422c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinenvoid ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
743f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez{
7446b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
745c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
7466b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	enum nl80211_iftype iftype;
7475379c8a26686e12058e23322615df68f9123bccdSujith
7485379c8a26686e12058e23322615df68f9123bccdSujith	/* Setup the beacon configuration parameters */
7492c3db3d51ee1fcf84f5828788905a4c091b9ae27Jouni Malinen	if (vif) {
7506b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
7516b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan
7526b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		iftype = vif->type;
7535379c8a26686e12058e23322615df68f9123bccdSujith
7546b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		cur_conf->beacon_interval = bss_conf->beacon_int;
7556b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		cur_conf->dtim_period = bss_conf->dtim_period;
7566b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		cur_conf->listen_interval = 1;
7576b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		cur_conf->dtim_count = 1;
7586b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		cur_conf->bmiss_timeout =
7596b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan			ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
7606b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	} else {
7616b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		iftype = sc->sc_ah->opmode;
7625379c8a26686e12058e23322615df68f9123bccdSujith	}
7636b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan
764c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	/*
765c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	 * It looks like mac80211 may end up using beacon interval of zero in
766c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	 * some cases (at least for mesh point). Avoid getting into an
767c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	 * infinite loop by using a bit safer value instead. To be safe,
768c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	 * do sanity check on beacon interval for all operating modes.
769c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	 */
770c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan	if (cur_conf->beacon_interval == 0)
771c4f9f16b309b65f9f578ec4ba78b3efa106cf65dVasanthakumar Thiagarajan		cur_conf->beacon_interval = 100;
7726b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan
7736b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	switch (iftype) {
7746b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	case NL80211_IFTYPE_AP:
7756b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		ath_beacon_config_ap(sc, cur_conf);
7766b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		break;
7776b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	case NL80211_IFTYPE_ADHOC:
7786b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	case NL80211_IFTYPE_MESH_POINT:
7796b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		ath_beacon_config_adhoc(sc, cur_conf, vif);
7806b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		break;
7816b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	case NL80211_IFTYPE_STATION:
7826b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		ath_beacon_config_sta(sc, cur_conf);
7836b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		break;
7846b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	default:
785c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez		ath_print(common, ATH_DBG_CONFIG,
786c46917bb53a546f60c7d3103407fe953c418dd5bLuis R. Rodriguez			  "Unsupported beaconing mode\n");
7876b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan		return;
7886b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	}
7896b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan
7906b96f93e962e25d38d7a73c0009597672d87c496Vasanthakumar Thiagarajan	sc->sc_flags |= SC_OP_BEACONS;
791f078f209704849c86bd43c0beccfc1f410ed1c66Luis R. Rodriguez}
792