xref: /linux/drivers/net/wireless/ath/ath9k/beacon.c (revision 3800276a40751539a920ef8e0537ef2e19126799)
1f078f209SLuis R. Rodriguez /*
2cee075a2SSujith  * Copyright (c) 2008-2009 Atheros Communications Inc.
3f078f209SLuis R. Rodriguez  *
4f078f209SLuis R. Rodriguez  * Permission to use, copy, modify, and/or distribute this software for any
5f078f209SLuis R. Rodriguez  * purpose with or without fee is hereby granted, provided that the above
6f078f209SLuis R. Rodriguez  * copyright notice and this permission notice appear in all copies.
7f078f209SLuis R. Rodriguez  *
8f078f209SLuis R. Rodriguez  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9f078f209SLuis R. Rodriguez  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10f078f209SLuis R. Rodriguez  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11f078f209SLuis R. Rodriguez  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12f078f209SLuis R. Rodriguez  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13f078f209SLuis R. Rodriguez  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14f078f209SLuis R. Rodriguez  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15f078f209SLuis R. Rodriguez  */
16f078f209SLuis R. Rodriguez 
17394cf0a1SSujith #include "ath9k.h"
18f078f209SLuis R. Rodriguez 
195379c8a2SSujith #define FUDGE 2
205379c8a2SSujith 
21f078f209SLuis R. Rodriguez /*
22f078f209SLuis R. Rodriguez  *  This function will modify certain transmit queue properties depending on
23f078f209SLuis R. Rodriguez  *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
24f078f209SLuis R. Rodriguez  *  settings and channel width min/max
25f078f209SLuis R. Rodriguez */
2694db2936SVivek Natarajan int ath_beaconq_config(struct ath_softc *sc)
27f078f209SLuis R. Rodriguez {
28cbe61d8aSSujith 	struct ath_hw *ah = sc->sc_ah;
29c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
3094db2936SVivek Natarajan 	struct ath9k_tx_queue_info qi, qi_be;
31066dae93SFelix Fietkau 	struct ath_txq *txq;
32f078f209SLuis R. Rodriguez 
33b77f483fSSujith 	ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
342660b81aSSujith 	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
35f078f209SLuis R. Rodriguez 		/* Always burst out beacon and CAB traffic. */
36f078f209SLuis R. Rodriguez 		qi.tqi_aifs = 1;
37f078f209SLuis R. Rodriguez 		qi.tqi_cwmin = 0;
38f078f209SLuis R. Rodriguez 		qi.tqi_cwmax = 0;
39f078f209SLuis R. Rodriguez 	} else {
40f078f209SLuis R. Rodriguez 		/* Adhoc mode; important thing is to use 2x cwmin. */
41066dae93SFelix Fietkau 		txq = sc->tx.txq_map[WME_AC_BE];
42066dae93SFelix Fietkau 		ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
4394db2936SVivek Natarajan 		qi.tqi_aifs = qi_be.tqi_aifs;
4494db2936SVivek Natarajan 		qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
4594db2936SVivek Natarajan 		qi.tqi_cwmax = qi_be.tqi_cwmax;
46f078f209SLuis R. Rodriguez 	}
47f078f209SLuis R. Rodriguez 
48b77f483fSSujith 	if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
49*3800276aSJoe Perches 		ath_err(common,
50d8baa939SSujith 			"Unable to update h/w beacon queue parameters\n");
51f078f209SLuis R. Rodriguez 		return 0;
52f078f209SLuis R. Rodriguez 	} else {
539fc9ab0aSSujith 		ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
54f078f209SLuis R. Rodriguez 		return 1;
55f078f209SLuis R. Rodriguez 	}
56f078f209SLuis R. Rodriguez }
57f078f209SLuis R. Rodriguez 
58f078f209SLuis R. Rodriguez /*
59f078f209SLuis R. Rodriguez  *  Associates the beacon frame buffer with a transmit descriptor.  Will set
60f078f209SLuis R. Rodriguez  *  up all required antenna switch parameters, rate codes, and channel flags.
61f078f209SLuis R. Rodriguez  *  Beacons are always sent out at the lowest rate, and are not retried.
62f078f209SLuis R. Rodriguez */
639fc9ab0aSSujith static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
6464b84010SJouni Malinen 			     struct ath_buf *bf, int rateidx)
65f078f209SLuis R. Rodriguez {
66a22be22aSSujith 	struct sk_buff *skb = bf->bf_mpdu;
67cbe61d8aSSujith 	struct ath_hw *ah = sc->sc_ah;
6843c27613SLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
69f078f209SLuis R. Rodriguez 	struct ath_desc *ds;
70980b24daSSujith 	struct ath9k_11n_rate_series series[4];
719fc9ab0aSSujith 	int flags, antenna, ctsrate = 0, ctsduration = 0;
72545750d3SFelix Fietkau 	struct ieee80211_supported_band *sband;
73545750d3SFelix Fietkau 	u8 rate = 0;
74f078f209SLuis R. Rodriguez 
75f078f209SLuis R. Rodriguez 	ds = bf->bf_desc;
76f078f209SLuis R. Rodriguez 	flags = ATH9K_TXDESC_NOACK;
77f078f209SLuis R. Rodriguez 
78f078f209SLuis R. Rodriguez 	ds->ds_link = 0;
79f078f209SLuis R. Rodriguez 	/*
80f078f209SLuis R. Rodriguez 	 * Switch antenna every beacon.
819fc9ab0aSSujith 	 * Should only switch every beacon period, not for every SWBA
829fc9ab0aSSujith 	 * XXX assumes two antennae
83f078f209SLuis R. Rodriguez 	 */
8417d7904dSSujith 	antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1);
85f078f209SLuis R. Rodriguez 
86545750d3SFelix Fietkau 	sband = &sc->sbands[common->hw->conf.channel->band];
8764b84010SJouni Malinen 	rate = sband->bitrates[rateidx].hw_value;
88672840acSSujith 	if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
8964b84010SJouni Malinen 		rate |= sband->bitrates[rateidx].hw_value_short;
90f078f209SLuis R. Rodriguez 
919fc9ab0aSSujith 	ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN,
929fc9ab0aSSujith 			       ATH9K_PKT_TYPE_BEACON,
939fc9ab0aSSujith 			       MAX_RATE_POWER,
949fc9ab0aSSujith 			       ATH9K_TXKEYIX_INVALID,
959fc9ab0aSSujith 			       ATH9K_KEY_TYPE_CLEAR,
969fc9ab0aSSujith 			       flags);
97f078f209SLuis R. Rodriguez 
98f078f209SLuis R. Rodriguez 	/* NB: beacon's BufLen must be a multiple of 4 bytes */
999fc9ab0aSSujith 	ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4),
100cc610ac0SVasanthakumar Thiagarajan 			    true, true, ds, bf->bf_buf_addr,
101cc610ac0SVasanthakumar Thiagarajan 			    sc->beacon.beaconq);
102f078f209SLuis R. Rodriguez 
1030345f37bSLuis R. Rodriguez 	memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
104f078f209SLuis R. Rodriguez 	series[0].Tries = 1;
105f078f209SLuis R. Rodriguez 	series[0].Rate = rate;
106ea066d5aSMohammed Shafi Shajakhan 	series[0].ChSel = ath_txchainmask_reduction(sc,
107ea066d5aSMohammed Shafi Shajakhan 			common->tx_chainmask, series[0].Rate);
108f078f209SLuis R. Rodriguez 	series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
1099fc9ab0aSSujith 	ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration,
1109fc9ab0aSSujith 				     series, 4, 0);
111f078f209SLuis R. Rodriguez }
112f078f209SLuis R. Rodriguez 
11328d16708SFelix Fietkau static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
11428d16708SFelix Fietkau {
11528d16708SFelix Fietkau 	struct ath_wiphy *aphy = hw->priv;
11628d16708SFelix Fietkau 	struct ath_softc *sc = aphy->sc;
11728d16708SFelix Fietkau 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
11828d16708SFelix Fietkau 	struct ath_tx_control txctl;
11928d16708SFelix Fietkau 
12028d16708SFelix Fietkau 	memset(&txctl, 0, sizeof(struct ath_tx_control));
12128d16708SFelix Fietkau 	txctl.txq = sc->beacon.cabq;
12228d16708SFelix Fietkau 
12328d16708SFelix Fietkau 	ath_print(common, ATH_DBG_XMIT,
12428d16708SFelix Fietkau 		  "transmitting CABQ packet, skb: %p\n", skb);
12528d16708SFelix Fietkau 
12628d16708SFelix Fietkau 	if (ath_tx_start(hw, skb, &txctl) != 0) {
12728d16708SFelix Fietkau 		ath_print(common, ATH_DBG_XMIT, "CABQ TX failed\n");
12828d16708SFelix Fietkau 		dev_kfree_skb_any(skb);
12928d16708SFelix Fietkau 	}
13028d16708SFelix Fietkau }
13128d16708SFelix Fietkau 
132c52f33d0SJouni Malinen static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
1332c3db3d5SJouni Malinen 					   struct ieee80211_vif *vif)
134f078f209SLuis R. Rodriguez {
135c52f33d0SJouni Malinen 	struct ath_wiphy *aphy = hw->priv;
136c52f33d0SJouni Malinen 	struct ath_softc *sc = aphy->sc;
137c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
138f078f209SLuis R. Rodriguez 	struct ath_buf *bf;
13917d7904dSSujith 	struct ath_vif *avp;
140f078f209SLuis R. Rodriguez 	struct sk_buff *skb;
141f078f209SLuis R. Rodriguez 	struct ath_txq *cabq;
142147583c0SJouni Malinen 	struct ieee80211_tx_info *info;
143980b24daSSujith 	int cabq_depth;
144980b24daSSujith 
145f0ed85c6SJouni Malinen 	if (aphy->state != ATH_WIPHY_ACTIVE)
146f0ed85c6SJouni Malinen 		return NULL;
147f0ed85c6SJouni Malinen 
1485640b08eSSujith 	avp = (void *)vif->drv_priv;
149b77f483fSSujith 	cabq = sc->beacon.cabq;
150f078f209SLuis R. Rodriguez 
151d8baa939SSujith 	if (avp->av_bcbuf == NULL)
152f078f209SLuis R. Rodriguez 		return NULL;
153980b24daSSujith 
1549fc9ab0aSSujith 	/* Release the old beacon first */
1559fc9ab0aSSujith 
156f078f209SLuis R. Rodriguez 	bf = avp->av_bcbuf;
157a22be22aSSujith 	skb = bf->bf_mpdu;
158a8fff50eSJouni Malinen 	if (skb) {
159c1739eb3SBen Greear 		dma_unmap_single(sc->dev, bf->bf_buf_addr,
1609fc9ab0aSSujith 				 skb->len, DMA_TO_DEVICE);
1613fbb9d95SJouni Malinen 		dev_kfree_skb_any(skb);
1626cf9e995SBen Greear 		bf->bf_buf_addr = 0;
163a8fff50eSJouni Malinen 	}
164f078f209SLuis R. Rodriguez 
1659fc9ab0aSSujith 	/* Get a new beacon from mac80211 */
1669fc9ab0aSSujith 
167c52f33d0SJouni Malinen 	skb = ieee80211_beacon_get(hw, vif);
168a8fff50eSJouni Malinen 	bf->bf_mpdu = skb;
169a8fff50eSJouni Malinen 	if (skb == NULL)
170a8fff50eSJouni Malinen 		return NULL;
1714ed96f04SJouni Malinen 	((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
1724ed96f04SJouni Malinen 		avp->tsf_adjust;
173980b24daSSujith 
174147583c0SJouni Malinen 	info = IEEE80211_SKB_CB(skb);
175147583c0SJouni Malinen 	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
176147583c0SJouni Malinen 		/*
177147583c0SJouni Malinen 		 * TODO: make sure the seq# gets assigned properly (vs. other
178147583c0SJouni Malinen 		 * TX frames)
179147583c0SJouni Malinen 		 */
180147583c0SJouni Malinen 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
181b77f483fSSujith 		sc->tx.seq_no += 0x10;
182147583c0SJouni Malinen 		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
183b77f483fSSujith 		hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
184147583c0SJouni Malinen 	}
185980b24daSSujith 
186c1739eb3SBen Greear 	bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
1879fc9ab0aSSujith 					 skb->len, DMA_TO_DEVICE);
1887da3c55cSGabor Juhos 	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
189f8316df1SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
190f8316df1SLuis R. Rodriguez 		bf->bf_mpdu = NULL;
1916cf9e995SBen Greear 		bf->bf_buf_addr = 0;
192*3800276aSJoe Perches 		ath_err(common, "dma_mapping_error on beaconing\n");
193f8316df1SLuis R. Rodriguez 		return NULL;
194f8316df1SLuis R. Rodriguez 	}
195f078f209SLuis R. Rodriguez 
196c52f33d0SJouni Malinen 	skb = ieee80211_get_buffered_bc(hw, vif);
197f078f209SLuis R. Rodriguez 
198f078f209SLuis R. Rodriguez 	/*
199f078f209SLuis R. Rodriguez 	 * if the CABQ traffic from previous DTIM is pending and the current
200f078f209SLuis R. Rodriguez 	 *  beacon is also a DTIM.
20117d7904dSSujith 	 *  1) if there is only one vif let the cab traffic continue.
20217d7904dSSujith 	 *  2) if there are more than one vif and we are using staggered
203f078f209SLuis R. Rodriguez 	 *     beacons, then drain the cabq by dropping all the frames in
20417d7904dSSujith 	 *     the cabq so that the current vifs cab traffic can be scheduled.
205f078f209SLuis R. Rodriguez 	 */
206f078f209SLuis R. Rodriguez 	spin_lock_bh(&cabq->axq_lock);
207f078f209SLuis R. Rodriguez 	cabq_depth = cabq->axq_depth;
208f078f209SLuis R. Rodriguez 	spin_unlock_bh(&cabq->axq_lock);
209f078f209SLuis R. Rodriguez 
210e022edbdSJouni Malinen 	if (skb && cabq_depth) {
21117d7904dSSujith 		if (sc->nvifs > 1) {
212c46917bbSLuis R. Rodriguez 			ath_print(common, ATH_DBG_BEACON,
2139fc9ab0aSSujith 				  "Flushing previous cabq traffic\n");
2149fc9ab0aSSujith 			ath_draintxq(sc, cabq, false);
215f078f209SLuis R. Rodriguez 		}
216f078f209SLuis R. Rodriguez 	}
217f078f209SLuis R. Rodriguez 
21864b84010SJouni Malinen 	ath_beacon_setup(sc, avp, bf, info->control.rates[0].idx);
219f078f209SLuis R. Rodriguez 
220e022edbdSJouni Malinen 	while (skb) {
221c52f33d0SJouni Malinen 		ath_tx_cabq(hw, skb);
222c52f33d0SJouni Malinen 		skb = ieee80211_get_buffered_bc(hw, vif);
223e022edbdSJouni Malinen 	}
224f078f209SLuis R. Rodriguez 
225f078f209SLuis R. Rodriguez 	return bf;
226f078f209SLuis R. Rodriguez }
227f078f209SLuis R. Rodriguez 
228c52f33d0SJouni Malinen int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
229f078f209SLuis R. Rodriguez {
230c52f33d0SJouni Malinen 	struct ath_softc *sc = aphy->sc;
231c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
23217d7904dSSujith 	struct ath_vif *avp;
233f078f209SLuis R. Rodriguez 	struct ath_buf *bf;
234f078f209SLuis R. Rodriguez 	struct sk_buff *skb;
235459f5f90SSujith 	__le64 tstamp;
236f078f209SLuis R. Rodriguez 
2375640b08eSSujith 	avp = (void *)vif->drv_priv;
238f078f209SLuis R. Rodriguez 
239f078f209SLuis R. Rodriguez 	/* Allocate a beacon descriptor if we haven't done so. */
240f078f209SLuis R. Rodriguez 	if (!avp->av_bcbuf) {
241980b24daSSujith 		/* Allocate beacon state for hostap/ibss.  We know
242980b24daSSujith 		 * a buffer is available. */
243b77f483fSSujith 		avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf,
244f078f209SLuis R. Rodriguez 						 struct ath_buf, list);
245f078f209SLuis R. Rodriguez 		list_del(&avp->av_bcbuf->list);
246f078f209SLuis R. Rodriguez 
2472660b81aSSujith 		if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
248a65e4cb4SFelix Fietkau 		    sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC ||
249a65e4cb4SFelix Fietkau 		    sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
250f078f209SLuis R. Rodriguez 			int slot;
251f078f209SLuis R. Rodriguez 			/*
25217d7904dSSujith 			 * Assign the vif to a beacon xmit slot. As
253f078f209SLuis R. Rodriguez 			 * above, this cannot fail to find one.
254f078f209SLuis R. Rodriguez 			 */
255f078f209SLuis R. Rodriguez 			avp->av_bslot = 0;
256f078f209SLuis R. Rodriguez 			for (slot = 0; slot < ATH_BCBUF; slot++)
2572c3db3d5SJouni Malinen 				if (sc->beacon.bslot[slot] == NULL) {
258f078f209SLuis R. Rodriguez 					avp->av_bslot = slot;
259774610e4SFelix Fietkau 
260f078f209SLuis R. Rodriguez 					/* NB: keep looking for a double slot */
261774610e4SFelix Fietkau 					if (slot == 0 || !sc->beacon.bslot[slot-1])
262774610e4SFelix Fietkau 						break;
263f078f209SLuis R. Rodriguez 				}
2642c3db3d5SJouni Malinen 			BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
2652c3db3d5SJouni Malinen 			sc->beacon.bslot[avp->av_bslot] = vif;
266c52f33d0SJouni Malinen 			sc->beacon.bslot_aphy[avp->av_bslot] = aphy;
26717d7904dSSujith 			sc->nbcnvifs++;
268f078f209SLuis R. Rodriguez 		}
269f078f209SLuis R. Rodriguez 	}
270f078f209SLuis R. Rodriguez 
271f078f209SLuis R. Rodriguez 	/* release the previous beacon frame, if it already exists. */
272f078f209SLuis R. Rodriguez 	bf = avp->av_bcbuf;
273f078f209SLuis R. Rodriguez 	if (bf->bf_mpdu != NULL) {
274a22be22aSSujith 		skb = bf->bf_mpdu;
275c1739eb3SBen Greear 		dma_unmap_single(sc->dev, bf->bf_buf_addr,
2769fc9ab0aSSujith 				 skb->len, DMA_TO_DEVICE);
277f078f209SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
278f078f209SLuis R. Rodriguez 		bf->bf_mpdu = NULL;
2796cf9e995SBen Greear 		bf->bf_buf_addr = 0;
280f078f209SLuis R. Rodriguez 	}
281f078f209SLuis R. Rodriguez 
2829fc9ab0aSSujith 	/* NB: the beacon data buffer must be 32-bit aligned. */
2835640b08eSSujith 	skb = ieee80211_beacon_get(sc->hw, vif);
284f078f209SLuis R. Rodriguez 	if (skb == NULL) {
285c46917bbSLuis R. Rodriguez 		ath_print(common, ATH_DBG_BEACON, "cannot get skb\n");
286f078f209SLuis R. Rodriguez 		return -ENOMEM;
287f078f209SLuis R. Rodriguez 	}
288f078f209SLuis R. Rodriguez 
289459f5f90SSujith 	tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
290b77f483fSSujith 	sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
2914ed96f04SJouni Malinen 	/* Calculate a TSF adjustment factor required for staggered beacons. */
292f078f209SLuis R. Rodriguez 	if (avp->av_bslot > 0) {
293f078f209SLuis R. Rodriguez 		u64 tsfadjust;
294f078f209SLuis R. Rodriguez 		int intval;
295f078f209SLuis R. Rodriguez 
29657c4d7b4SJohannes Berg 		intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
297f078f209SLuis R. Rodriguez 
298f078f209SLuis R. Rodriguez 		/*
2994ed96f04SJouni Malinen 		 * Calculate the TSF offset for this beacon slot, i.e., the
3004ed96f04SJouni Malinen 		 * number of usecs that need to be added to the timestamp field
3014ed96f04SJouni Malinen 		 * in Beacon and Probe Response frames. Beacon slot 0 is
3024ed96f04SJouni Malinen 		 * processed at the correct offset, so it does not require TSF
3034ed96f04SJouni Malinen 		 * adjustment. Other slots are adjusted to get the timestamp
3044ed96f04SJouni Malinen 		 * close to the TBTT for the BSS.
305f078f209SLuis R. Rodriguez 		 */
3064ed96f04SJouni Malinen 		tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
3074ed96f04SJouni Malinen 		avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
308f078f209SLuis R. Rodriguez 
309c46917bbSLuis R. Rodriguez 		ath_print(common, ATH_DBG_BEACON,
310c46917bbSLuis R. Rodriguez 			  "stagger beacons, bslot %d intval "
311c46917bbSLuis R. Rodriguez 			  "%u tsfadjust %llu\n",
312f078f209SLuis R. Rodriguez 			  avp->av_bslot, intval, (unsigned long long)tsfadjust);
313f078f209SLuis R. Rodriguez 
3144ed96f04SJouni Malinen 		((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
3154ed96f04SJouni Malinen 			avp->tsf_adjust;
3164ed96f04SJouni Malinen 	} else
3174ed96f04SJouni Malinen 		avp->tsf_adjust = cpu_to_le64(0);
318f078f209SLuis R. Rodriguez 
319f8316df1SLuis R. Rodriguez 	bf->bf_mpdu = skb;
320c1739eb3SBen Greear 	bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
3219fc9ab0aSSujith 					 skb->len, DMA_TO_DEVICE);
3227da3c55cSGabor Juhos 	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
323f8316df1SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
324f8316df1SLuis R. Rodriguez 		bf->bf_mpdu = NULL;
3256cf9e995SBen Greear 		bf->bf_buf_addr = 0;
326*3800276aSJoe Perches 		ath_err(common, "dma_mapping_error on beacon alloc\n");
327f8316df1SLuis R. Rodriguez 		return -ENOMEM;
328f8316df1SLuis R. Rodriguez 	}
329f078f209SLuis R. Rodriguez 
330f078f209SLuis R. Rodriguez 	return 0;
331f078f209SLuis R. Rodriguez }
332f078f209SLuis R. Rodriguez 
33317d7904dSSujith void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp)
334f078f209SLuis R. Rodriguez {
335f078f209SLuis R. Rodriguez 	if (avp->av_bcbuf != NULL) {
336f078f209SLuis R. Rodriguez 		struct ath_buf *bf;
337f078f209SLuis R. Rodriguez 
338f078f209SLuis R. Rodriguez 		if (avp->av_bslot != -1) {
3392c3db3d5SJouni Malinen 			sc->beacon.bslot[avp->av_bslot] = NULL;
340c52f33d0SJouni Malinen 			sc->beacon.bslot_aphy[avp->av_bslot] = NULL;
34117d7904dSSujith 			sc->nbcnvifs--;
342f078f209SLuis R. Rodriguez 		}
343f078f209SLuis R. Rodriguez 
344f078f209SLuis R. Rodriguez 		bf = avp->av_bcbuf;
345f078f209SLuis R. Rodriguez 		if (bf->bf_mpdu != NULL) {
346a22be22aSSujith 			struct sk_buff *skb = bf->bf_mpdu;
347c1739eb3SBen Greear 			dma_unmap_single(sc->dev, bf->bf_buf_addr,
3489fc9ab0aSSujith 					 skb->len, DMA_TO_DEVICE);
349f078f209SLuis R. Rodriguez 			dev_kfree_skb_any(skb);
350f078f209SLuis R. Rodriguez 			bf->bf_mpdu = NULL;
3516cf9e995SBen Greear 			bf->bf_buf_addr = 0;
352f078f209SLuis R. Rodriguez 		}
353b77f483fSSujith 		list_add_tail(&bf->list, &sc->beacon.bbuf);
354f078f209SLuis R. Rodriguez 
355f078f209SLuis R. Rodriguez 		avp->av_bcbuf = NULL;
356f078f209SLuis R. Rodriguez 	}
357f078f209SLuis R. Rodriguez }
358f078f209SLuis R. Rodriguez 
3599fc9ab0aSSujith void ath_beacon_tasklet(unsigned long data)
360f078f209SLuis R. Rodriguez {
361f078f209SLuis R. Rodriguez 	struct ath_softc *sc = (struct ath_softc *)data;
362cbe61d8aSSujith 	struct ath_hw *ah = sc->sc_ah;
363c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
364f078f209SLuis R. Rodriguez 	struct ath_buf *bf = NULL;
3652c3db3d5SJouni Malinen 	struct ieee80211_vif *vif;
366c52f33d0SJouni Malinen 	struct ath_wiphy *aphy;
3672c3db3d5SJouni Malinen 	int slot;
3689546aae0SSujith 	u32 bfaddr, bc = 0, tsftu;
369f078f209SLuis R. Rodriguez 	u64 tsf;
370f078f209SLuis R. Rodriguez 	u16 intval;
371f078f209SLuis R. Rodriguez 
372f078f209SLuis R. Rodriguez 	/*
373f078f209SLuis R. Rodriguez 	 * Check if the previous beacon has gone out.  If
374f078f209SLuis R. Rodriguez 	 * not don't try to post another, skip this period
375f078f209SLuis R. Rodriguez 	 * and wait for the next.  Missed beacons indicate
376f078f209SLuis R. Rodriguez 	 * a problem and should not occur.  If we miss too
377f078f209SLuis R. Rodriguez 	 * many consecutive beacons reset the device.
378f078f209SLuis R. Rodriguez 	 */
379b77f483fSSujith 	if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
380b77f483fSSujith 		sc->beacon.bmisscnt++;
3819546aae0SSujith 
382b77f483fSSujith 		if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
3836252fcb9SFelix Fietkau 			ath_print(common, ATH_DBG_BSTUCK,
38404bd4638SSujith 				  "missed %u consecutive beacons\n",
385b77f483fSSujith 				  sc->beacon.bmisscnt);
38670cf1533SFelix Fietkau 			ath9k_hw_bstuck_nfcal(ah);
387b77f483fSSujith 		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
3886252fcb9SFelix Fietkau 			ath_print(common, ATH_DBG_BSTUCK,
38904bd4638SSujith 				  "beacon is officially stuck\n");
390b74444f8SJeff Hansen 			sc->sc_flags |= SC_OP_TSF_RESET;
391fac6b6a0SFelix Fietkau 			ath_reset(sc, true);
392f078f209SLuis R. Rodriguez 		}
3939546aae0SSujith 
394f078f209SLuis R. Rodriguez 		return;
395f078f209SLuis R. Rodriguez 	}
396980b24daSSujith 
397b77f483fSSujith 	if (sc->beacon.bmisscnt != 0) {
3986252fcb9SFelix Fietkau 		ath_print(common, ATH_DBG_BSTUCK,
39904bd4638SSujith 			  "resume beacon xmit after %u misses\n",
400b77f483fSSujith 			  sc->beacon.bmisscnt);
401b77f483fSSujith 		sc->beacon.bmisscnt = 0;
402f078f209SLuis R. Rodriguez 	}
403f078f209SLuis R. Rodriguez 
404f078f209SLuis R. Rodriguez 	/*
405f078f209SLuis R. Rodriguez 	 * Generate beacon frames. we are sending frames
406f078f209SLuis R. Rodriguez 	 * staggered so calculate the slot for this frame based
407f078f209SLuis R. Rodriguez 	 * on the tsf to safeguard against missing an swba.
408f078f209SLuis R. Rodriguez 	 */
409f078f209SLuis R. Rodriguez 
41057c4d7b4SJohannes Berg 	intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
411f078f209SLuis R. Rodriguez 
412f078f209SLuis R. Rodriguez 	tsf = ath9k_hw_gettsf64(ah);
413f078f209SLuis R. Rodriguez 	tsftu = TSF_TO_TU(tsf>>32, tsf);
414f078f209SLuis R. Rodriguez 	slot = ((tsftu % intval) * ATH_BCBUF) / intval;
4154ed96f04SJouni Malinen 	/*
4164ed96f04SJouni Malinen 	 * Reverse the slot order to get slot 0 on the TBTT offset that does
4174ed96f04SJouni Malinen 	 * not require TSF adjustment and other slots adding
4184ed96f04SJouni Malinen 	 * slot/ATH_BCBUF * beacon_int to timestamp. For example, with
4194ed96f04SJouni Malinen 	 * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 ..
4204ed96f04SJouni Malinen 	 * and slot 0 is at correct offset to TBTT.
4214ed96f04SJouni Malinen 	 */
4224ed96f04SJouni Malinen 	slot = ATH_BCBUF - slot - 1;
4234ed96f04SJouni Malinen 	vif = sc->beacon.bslot[slot];
4244ed96f04SJouni Malinen 	aphy = sc->beacon.bslot_aphy[slot];
425980b24daSSujith 
426c46917bbSLuis R. Rodriguez 	ath_print(common, ATH_DBG_BEACON,
4272c3db3d5SJouni Malinen 		  "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
4282c3db3d5SJouni Malinen 		  slot, tsf, tsftu, intval, vif);
429980b24daSSujith 
430f078f209SLuis R. Rodriguez 	bfaddr = 0;
4312c3db3d5SJouni Malinen 	if (vif) {
432c52f33d0SJouni Malinen 		bf = ath_beacon_generate(aphy->hw, vif);
433f078f209SLuis R. Rodriguez 		if (bf != NULL) {
434f078f209SLuis R. Rodriguez 			bfaddr = bf->bf_daddr;
435f078f209SLuis R. Rodriguez 			bc = 1;
436f078f209SLuis R. Rodriguez 		}
437f078f209SLuis R. Rodriguez 	}
4389546aae0SSujith 
439f078f209SLuis R. Rodriguez 	/*
440f078f209SLuis R. Rodriguez 	 * Handle slot time change when a non-ERP station joins/leaves
441f078f209SLuis R. Rodriguez 	 * an 11g network.  The 802.11 layer notifies us via callback,
442f078f209SLuis R. Rodriguez 	 * we mark updateslot, then wait one beacon before effecting
443f078f209SLuis R. Rodriguez 	 * the change.  This gives associated stations at least one
444f078f209SLuis R. Rodriguez 	 * beacon interval to note the state change.
445f078f209SLuis R. Rodriguez 	 *
446f078f209SLuis R. Rodriguez 	 * NB: The slot time change state machine is clocked according
447f078f209SLuis R. Rodriguez 	 *     to whether we are bursting or staggering beacons.  We
448f078f209SLuis R. Rodriguez 	 *     recognize the request to update and record the current
449f078f209SLuis R. Rodriguez 	 *     slot then don't transition until that slot is reached
450f078f209SLuis R. Rodriguez 	 *     again.  If we miss a beacon for that slot then we'll be
451f078f209SLuis R. Rodriguez 	 *     slow to transition but we'll be sure at least one beacon
452f078f209SLuis R. Rodriguez 	 *     interval has passed.  When bursting slot is always left
453f078f209SLuis R. Rodriguez 	 *     set to ATH_BCBUF so this check is a noop.
454f078f209SLuis R. Rodriguez 	 */
455b77f483fSSujith 	if (sc->beacon.updateslot == UPDATE) {
456b77f483fSSujith 		sc->beacon.updateslot = COMMIT; /* commit next beacon */
457b77f483fSSujith 		sc->beacon.slotupdate = slot;
458b77f483fSSujith 	} else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) {
4590005baf4SFelix Fietkau 		ah->slottime = sc->beacon.slottime;
4600005baf4SFelix Fietkau 		ath9k_hw_init_global_settings(ah);
461b77f483fSSujith 		sc->beacon.updateslot = OK;
462ff37e337SSujith 	}
463f078f209SLuis R. Rodriguez 	if (bfaddr != 0) {
464f078f209SLuis R. Rodriguez 		/*
465f078f209SLuis R. Rodriguez 		 * Stop any current dma and put the new frame(s) on the queue.
466f078f209SLuis R. Rodriguez 		 * This should never fail since we check above that no frames
467f078f209SLuis R. Rodriguez 		 * are still pending on the queue.
468f078f209SLuis R. Rodriguez 		 */
469b77f483fSSujith 		if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) {
470*3800276aSJoe Perches 			ath_err(common, "beacon queue %u did not stop?\n",
471*3800276aSJoe Perches 				sc->beacon.beaconq);
472f078f209SLuis R. Rodriguez 		}
473f078f209SLuis R. Rodriguez 
474f078f209SLuis R. Rodriguez 		/* NB: cabq traffic should already be queued and primed */
475b77f483fSSujith 		ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
476b77f483fSSujith 		ath9k_hw_txstart(ah, sc->beacon.beaconq);
477f078f209SLuis R. Rodriguez 
47817d7904dSSujith 		sc->beacon.ast_be_xmit += bc;     /* XXX per-vif? */
479f078f209SLuis R. Rodriguez 	}
480f078f209SLuis R. Rodriguez }
481f078f209SLuis R. Rodriguez 
48221526d57SLuis R. Rodriguez static void ath9k_beacon_init(struct ath_softc *sc,
48321526d57SLuis R. Rodriguez 			      u32 next_beacon,
48421526d57SLuis R. Rodriguez 			      u32 beacon_period)
48521526d57SLuis R. Rodriguez {
48621526d57SLuis R. Rodriguez 	if (beacon_period & ATH9K_BEACON_RESET_TSF)
48721526d57SLuis R. Rodriguez 		ath9k_ps_wakeup(sc);
48821526d57SLuis R. Rodriguez 
48921526d57SLuis R. Rodriguez 	ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
49021526d57SLuis R. Rodriguez 
49121526d57SLuis R. Rodriguez 	if (beacon_period & ATH9K_BEACON_RESET_TSF)
49221526d57SLuis R. Rodriguez 		ath9k_ps_restore(sc);
49321526d57SLuis R. Rodriguez }
49421526d57SLuis R. Rodriguez 
495f078f209SLuis R. Rodriguez /*
4965379c8a2SSujith  * For multi-bss ap support beacons are either staggered evenly over N slots or
4975379c8a2SSujith  * burst together.  For the former arrange for the SWBA to be delivered for each
4985379c8a2SSujith  * slot. Slots that are not occupied will generate nothing.
499f078f209SLuis R. Rodriguez  */
5005379c8a2SSujith static void ath_beacon_config_ap(struct ath_softc *sc,
501d31e20afSVasanthakumar Thiagarajan 				 struct ath_beacon_config *conf)
502f078f209SLuis R. Rodriguez {
5033069168cSPavel Roskin 	struct ath_hw *ah = sc->sc_ah;
504980b24daSSujith 	u32 nexttbtt, intval;
505f078f209SLuis R. Rodriguez 
506f078f209SLuis R. Rodriguez 	/* NB: the beacon interval is kept internally in TU's */
5075379c8a2SSujith 	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
508f078f209SLuis R. Rodriguez 	intval /= ATH_BCBUF;    /* for staggered beacons */
5095379c8a2SSujith 	nexttbtt = intval;
510d8728ee9SFelix Fietkau 
511d8728ee9SFelix Fietkau 	if (sc->sc_flags & SC_OP_TSF_RESET)
5125379c8a2SSujith 		intval |= ATH9K_BEACON_RESET_TSF;
5135379c8a2SSujith 
5145379c8a2SSujith 	/*
5155379c8a2SSujith 	 * In AP mode we enable the beacon timers and SWBA interrupts to
5165379c8a2SSujith 	 * prepare beacon frames.
5175379c8a2SSujith 	 */
5185379c8a2SSujith 	intval |= ATH9K_BEACON_ENA;
5193069168cSPavel Roskin 	ah->imask |= ATH9K_INT_SWBA;
5205379c8a2SSujith 	ath_beaconq_config(sc);
5215379c8a2SSujith 
5225379c8a2SSujith 	/* Set the computed AP beacon timers */
5235379c8a2SSujith 
5244df3071eSFelix Fietkau 	ath9k_hw_disable_interrupts(ah);
52521526d57SLuis R. Rodriguez 	ath9k_beacon_init(sc, nexttbtt, intval);
5265379c8a2SSujith 	sc->beacon.bmisscnt = 0;
5273069168cSPavel Roskin 	ath9k_hw_set_interrupts(ah, ah->imask);
528b238e90eSSujith 
529b238e90eSSujith 	/* Clear the reset TSF flag, so that subsequent beacon updation
530b238e90eSSujith 	   will not reset the HW TSF. */
531b238e90eSSujith 
532b238e90eSSujith 	sc->sc_flags &= ~SC_OP_TSF_RESET;
533f078f209SLuis R. Rodriguez }
534f078f209SLuis R. Rodriguez 
5355379c8a2SSujith /*
5365379c8a2SSujith  * This sets up the beacon timers according to the timestamp of the last
5375379c8a2SSujith  * received beacon and the current TSF, configures PCF and DTIM
5385379c8a2SSujith  * handling, programs the sleep registers so the hardware will wakeup in
5395379c8a2SSujith  * time to receive beacons, and configures the beacon miss handling so
5405379c8a2SSujith  * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
5415379c8a2SSujith  * we've associated with.
5425379c8a2SSujith  */
5435379c8a2SSujith static void ath_beacon_config_sta(struct ath_softc *sc,
544d31e20afSVasanthakumar Thiagarajan 				  struct ath_beacon_config *conf)
5455379c8a2SSujith {
5463069168cSPavel Roskin 	struct ath_hw *ah = sc->sc_ah;
5473069168cSPavel Roskin 	struct ath_common *common = ath9k_hw_common(ah);
548f078f209SLuis R. Rodriguez 	struct ath9k_beacon_state bs;
549f078f209SLuis R. Rodriguez 	int dtimperiod, dtimcount, sleepduration;
550f078f209SLuis R. Rodriguez 	int cfpperiod, cfpcount;
5515379c8a2SSujith 	u32 nexttbtt = 0, intval, tsftu;
5525379c8a2SSujith 	u64 tsf;
553267a9012SJouni Malinen 	int num_beacons, offset, dtim_dec_count, cfp_dec_count;
5545379c8a2SSujith 
5551a20034aSSenthil Balasubramanian 	/* No need to configure beacon if we are not associated */
5561a20034aSSenthil Balasubramanian 	if (!common->curaid) {
5571a20034aSSenthil Balasubramanian 		ath_print(common, ATH_DBG_BEACON,
5581a20034aSSenthil Balasubramanian 			 "STA is not yet associated..skipping beacon config\n");
5591a20034aSSenthil Balasubramanian 		return;
5601a20034aSSenthil Balasubramanian 	}
5611a20034aSSenthil Balasubramanian 
5625379c8a2SSujith 	memset(&bs, 0, sizeof(bs));
5635379c8a2SSujith 	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
564f078f209SLuis R. Rodriguez 
565f078f209SLuis R. Rodriguez 	/*
566f078f209SLuis R. Rodriguez 	 * Setup dtim and cfp parameters according to
567f078f209SLuis R. Rodriguez 	 * last beacon we received (which may be none).
568f078f209SLuis R. Rodriguez 	 */
5695379c8a2SSujith 	dtimperiod = conf->dtim_period;
570f078f209SLuis R. Rodriguez 	if (dtimperiod <= 0)		/* NB: 0 if not known */
571f078f209SLuis R. Rodriguez 		dtimperiod = 1;
5725379c8a2SSujith 	dtimcount = conf->dtim_count;
573f078f209SLuis R. Rodriguez 	if (dtimcount >= dtimperiod)	/* NB: sanity check */
574980b24daSSujith 		dtimcount = 0;
575f078f209SLuis R. Rodriguez 	cfpperiod = 1;			/* NB: no PCF support yet */
576f078f209SLuis R. Rodriguez 	cfpcount = 0;
577f078f209SLuis R. Rodriguez 
5785379c8a2SSujith 	sleepduration = conf->listen_interval * intval;
579f078f209SLuis R. Rodriguez 	if (sleepduration <= 0)
580f078f209SLuis R. Rodriguez 		sleepduration = intval;
581f078f209SLuis R. Rodriguez 
582f078f209SLuis R. Rodriguez 	/*
583f078f209SLuis R. Rodriguez 	 * Pull nexttbtt forward to reflect the current
584f078f209SLuis R. Rodriguez 	 * TSF and calculate dtim+cfp state for the result.
585f078f209SLuis R. Rodriguez 	 */
5863069168cSPavel Roskin 	tsf = ath9k_hw_gettsf64(ah);
587f078f209SLuis R. Rodriguez 	tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
588267a9012SJouni Malinen 
589267a9012SJouni Malinen 	num_beacons = tsftu / intval + 1;
590267a9012SJouni Malinen 	offset = tsftu % intval;
591267a9012SJouni Malinen 	nexttbtt = tsftu - offset;
592267a9012SJouni Malinen 	if (offset)
593f078f209SLuis R. Rodriguez 		nexttbtt += intval;
594267a9012SJouni Malinen 
595267a9012SJouni Malinen 	/* DTIM Beacon every dtimperiod Beacon */
596267a9012SJouni Malinen 	dtim_dec_count = num_beacons % dtimperiod;
597267a9012SJouni Malinen 	/* CFP every cfpperiod DTIM Beacon */
598267a9012SJouni Malinen 	cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
599267a9012SJouni Malinen 	if (dtim_dec_count)
600267a9012SJouni Malinen 		cfp_dec_count++;
601267a9012SJouni Malinen 
602267a9012SJouni Malinen 	dtimcount -= dtim_dec_count;
603267a9012SJouni Malinen 	if (dtimcount < 0)
604267a9012SJouni Malinen 		dtimcount += dtimperiod;
605267a9012SJouni Malinen 
606267a9012SJouni Malinen 	cfpcount -= cfp_dec_count;
607267a9012SJouni Malinen 	if (cfpcount < 0)
608267a9012SJouni Malinen 		cfpcount += cfpperiod;
6095379c8a2SSujith 
610f078f209SLuis R. Rodriguez 	bs.bs_intval = intval;
611f078f209SLuis R. Rodriguez 	bs.bs_nexttbtt = nexttbtt;
612f078f209SLuis R. Rodriguez 	bs.bs_dtimperiod = dtimperiod*intval;
613f078f209SLuis R. Rodriguez 	bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
614f078f209SLuis R. Rodriguez 	bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
615f078f209SLuis R. Rodriguez 	bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
616f078f209SLuis R. Rodriguez 	bs.bs_cfpmaxduration = 0;
617980b24daSSujith 
618f078f209SLuis R. Rodriguez 	/*
6195379c8a2SSujith 	 * Calculate the number of consecutive beacons to miss* before taking
6205379c8a2SSujith 	 * a BMISS interrupt. The configuration is specified in TU so we only
6215379c8a2SSujith 	 * need calculate based	on the beacon interval.  Note that we clamp the
622f078f209SLuis R. Rodriguez 	 * result to at most 15 beacons.
623f078f209SLuis R. Rodriguez 	 */
624f078f209SLuis R. Rodriguez 	if (sleepduration > intval) {
6255379c8a2SSujith 		bs.bs_bmissthreshold = conf->listen_interval *
626f078f209SLuis R. Rodriguez 			ATH_DEFAULT_BMISS_LIMIT / 2;
627f078f209SLuis R. Rodriguez 	} else {
6285379c8a2SSujith 		bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval);
629f078f209SLuis R. Rodriguez 		if (bs.bs_bmissthreshold > 15)
630f078f209SLuis R. Rodriguez 			bs.bs_bmissthreshold = 15;
631f078f209SLuis R. Rodriguez 		else if (bs.bs_bmissthreshold <= 0)
632f078f209SLuis R. Rodriguez 			bs.bs_bmissthreshold = 1;
633f078f209SLuis R. Rodriguez 	}
634f078f209SLuis R. Rodriguez 
635f078f209SLuis R. Rodriguez 	/*
6365379c8a2SSujith 	 * Calculate sleep duration. The configuration is given in ms.
6375379c8a2SSujith 	 * We ensure a multiple of the beacon period is used. Also, if the sleep
6385379c8a2SSujith 	 * duration is greater than the DTIM period then it makes senses
639f078f209SLuis R. Rodriguez 	 * to make it a multiple of that.
640f078f209SLuis R. Rodriguez 	 *
641f078f209SLuis R. Rodriguez 	 * XXX fixed at 100ms
642f078f209SLuis R. Rodriguez 	 */
643f078f209SLuis R. Rodriguez 
6445379c8a2SSujith 	bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
645f078f209SLuis R. Rodriguez 	if (bs.bs_sleepduration > bs.bs_dtimperiod)
646f078f209SLuis R. Rodriguez 		bs.bs_sleepduration = bs.bs_dtimperiod;
647f078f209SLuis R. Rodriguez 
6484af9cf4fSSujith 	/* TSF out of range threshold fixed at 1 second */
6494af9cf4fSSujith 	bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
6504af9cf4fSSujith 
651c46917bbSLuis R. Rodriguez 	ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
652c46917bbSLuis R. Rodriguez 	ath_print(common, ATH_DBG_BEACON,
6539fc9ab0aSSujith 		  "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
6549fc9ab0aSSujith 		  bs.bs_bmissthreshold, bs.bs_sleepduration,
6559fc9ab0aSSujith 		  bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
656f078f209SLuis R. Rodriguez 
6575379c8a2SSujith 	/* Set the computed STA beacon timers */
6589fc9ab0aSSujith 
6594df3071eSFelix Fietkau 	ath9k_hw_disable_interrupts(ah);
6603069168cSPavel Roskin 	ath9k_hw_set_sta_beacon_timers(ah, &bs);
6613069168cSPavel Roskin 	ah->imask |= ATH9K_INT_BMISS;
6623069168cSPavel Roskin 	ath9k_hw_set_interrupts(ah, ah->imask);
6635379c8a2SSujith }
6645379c8a2SSujith 
6655379c8a2SSujith static void ath_beacon_config_adhoc(struct ath_softc *sc,
6665379c8a2SSujith 				    struct ath_beacon_config *conf,
6672c3db3d5SJouni Malinen 				    struct ieee80211_vif *vif)
6685379c8a2SSujith {
6693069168cSPavel Roskin 	struct ath_hw *ah = sc->sc_ah;
6703069168cSPavel Roskin 	struct ath_common *common = ath9k_hw_common(ah);
6715379c8a2SSujith 	u64 tsf;
6725379c8a2SSujith 	u32 tsftu, intval, nexttbtt;
6735379c8a2SSujith 
6745379c8a2SSujith 	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
6755379c8a2SSujith 
676546256fbSJouni Malinen 
6779fc9ab0aSSujith 	/* Pull nexttbtt forward to reflect the current TSF */
6785379c8a2SSujith 
6795379c8a2SSujith 	nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
6805379c8a2SSujith 	if (nexttbtt == 0)
6815379c8a2SSujith                 nexttbtt = intval;
6825379c8a2SSujith         else if (intval)
6835379c8a2SSujith                 nexttbtt = roundup(nexttbtt, intval);
6845379c8a2SSujith 
6853069168cSPavel Roskin 	tsf = ath9k_hw_gettsf64(ah);
6865379c8a2SSujith 	tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
687f078f209SLuis R. Rodriguez 	do {
688f078f209SLuis R. Rodriguez 		nexttbtt += intval;
689f078f209SLuis R. Rodriguez 	} while (nexttbtt < tsftu);
6905379c8a2SSujith 
691c46917bbSLuis R. Rodriguez 	ath_print(common, ATH_DBG_BEACON,
69204bd4638SSujith 		  "IBSS nexttbtt %u intval %u (%u)\n",
6935379c8a2SSujith 		  nexttbtt, intval, conf->beacon_interval);
694f078f209SLuis R. Rodriguez 
695f078f209SLuis R. Rodriguez 	/*
6965379c8a2SSujith 	 * In IBSS mode enable the beacon timers but only enable SWBA interrupts
6975379c8a2SSujith 	 * if we need to manually prepare beacon frames.  Otherwise we use a
6985379c8a2SSujith 	 * self-linked tx descriptor and let the hardware deal with things.
699f078f209SLuis R. Rodriguez 	 */
700f078f209SLuis R. Rodriguez 	intval |= ATH9K_BEACON_ENA;
7013069168cSPavel Roskin 	ah->imask |= ATH9K_INT_SWBA;
7029fc9ab0aSSujith 
7035379c8a2SSujith 	ath_beaconq_config(sc);
7045379c8a2SSujith 
7055379c8a2SSujith 	/* Set the computed ADHOC beacon timers */
7065379c8a2SSujith 
7074df3071eSFelix Fietkau 	ath9k_hw_disable_interrupts(ah);
70821526d57SLuis R. Rodriguez 	ath9k_beacon_init(sc, nexttbtt, intval);
709b77f483fSSujith 	sc->beacon.bmisscnt = 0;
7103069168cSPavel Roskin 	ath9k_hw_set_interrupts(ah, ah->imask);
711f078f209SLuis R. Rodriguez }
7125379c8a2SSujith 
7132c3db3d5SJouni Malinen void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
7145379c8a2SSujith {
7156b96f93eSVasanthakumar Thiagarajan 	struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
716c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
7176b96f93eSVasanthakumar Thiagarajan 	enum nl80211_iftype iftype;
7185379c8a2SSujith 
7195379c8a2SSujith 	/* Setup the beacon configuration parameters */
7202c3db3d5SJouni Malinen 	if (vif) {
7216b96f93eSVasanthakumar Thiagarajan 		struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
7225379c8a2SSujith 
7236b96f93eSVasanthakumar Thiagarajan 		iftype = vif->type;
7246b96f93eSVasanthakumar Thiagarajan 
7256b96f93eSVasanthakumar Thiagarajan 		cur_conf->beacon_interval = bss_conf->beacon_int;
7266b96f93eSVasanthakumar Thiagarajan 		cur_conf->dtim_period = bss_conf->dtim_period;
7276b96f93eSVasanthakumar Thiagarajan 		cur_conf->listen_interval = 1;
7286b96f93eSVasanthakumar Thiagarajan 		cur_conf->dtim_count = 1;
7296b96f93eSVasanthakumar Thiagarajan 		cur_conf->bmiss_timeout =
7306b96f93eSVasanthakumar Thiagarajan 			ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
7316b96f93eSVasanthakumar Thiagarajan 	} else {
7326b96f93eSVasanthakumar Thiagarajan 		iftype = sc->sc_ah->opmode;
7336b96f93eSVasanthakumar Thiagarajan 	}
7346b96f93eSVasanthakumar Thiagarajan 
735c4f9f16bSVasanthakumar Thiagarajan 	/*
736c4f9f16bSVasanthakumar Thiagarajan 	 * It looks like mac80211 may end up using beacon interval of zero in
737c4f9f16bSVasanthakumar Thiagarajan 	 * some cases (at least for mesh point). Avoid getting into an
738c4f9f16bSVasanthakumar Thiagarajan 	 * infinite loop by using a bit safer value instead. To be safe,
739c4f9f16bSVasanthakumar Thiagarajan 	 * do sanity check on beacon interval for all operating modes.
740c4f9f16bSVasanthakumar Thiagarajan 	 */
741c4f9f16bSVasanthakumar Thiagarajan 	if (cur_conf->beacon_interval == 0)
742c4f9f16bSVasanthakumar Thiagarajan 		cur_conf->beacon_interval = 100;
7436b96f93eSVasanthakumar Thiagarajan 
7446b96f93eSVasanthakumar Thiagarajan 	switch (iftype) {
7455379c8a2SSujith 	case NL80211_IFTYPE_AP:
7466b96f93eSVasanthakumar Thiagarajan 		ath_beacon_config_ap(sc, cur_conf);
7475379c8a2SSujith 		break;
7485379c8a2SSujith 	case NL80211_IFTYPE_ADHOC:
7499cb5412bSPat Erley 	case NL80211_IFTYPE_MESH_POINT:
7506b96f93eSVasanthakumar Thiagarajan 		ath_beacon_config_adhoc(sc, cur_conf, vif);
7515379c8a2SSujith 		break;
7525379c8a2SSujith 	case NL80211_IFTYPE_STATION:
7536b96f93eSVasanthakumar Thiagarajan 		ath_beacon_config_sta(sc, cur_conf);
7545379c8a2SSujith 		break;
7555379c8a2SSujith 	default:
756c46917bbSLuis R. Rodriguez 		ath_print(common, ATH_DBG_CONFIG,
7575379c8a2SSujith 			  "Unsupported beaconing mode\n");
7585379c8a2SSujith 		return;
759f078f209SLuis R. Rodriguez 	}
760f078f209SLuis R. Rodriguez 
761672840acSSujith 	sc->sc_flags |= SC_OP_BEACONS;
762f078f209SLuis R. Rodriguez }
763