162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "common.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define FUDGE 2
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf,
2262306a36Sopenharmony_ci			       unsigned int interval)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	unsigned int offset, divisor;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
2762306a36Sopenharmony_ci	divisor = TU_TO_USEC(interval);
2862306a36Sopenharmony_ci	div_u64_rem(tsf, divisor, &offset);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	return (u32) tsf + divisor - offset;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * This sets up the beacon timers according to the timestamp of the last
3562306a36Sopenharmony_ci * received beacon and the current TSF, configures PCF and DTIM
3662306a36Sopenharmony_ci * handling, programs the sleep registers so the hardware will wakeup in
3762306a36Sopenharmony_ci * time to receive beacons, and configures the beacon miss handling so
3862306a36Sopenharmony_ci * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
3962306a36Sopenharmony_ci * we've associated with.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ciint ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
4262306a36Sopenharmony_ci				 struct ath_beacon_config *conf,
4362306a36Sopenharmony_ci				 struct ath9k_beacon_state *bs)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
4662306a36Sopenharmony_ci	int dtim_intval;
4762306a36Sopenharmony_ci	u64 tsf;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* No need to configure beacon if we are not associated */
5062306a36Sopenharmony_ci	if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
5162306a36Sopenharmony_ci		ath_dbg(common, BEACON,
5262306a36Sopenharmony_ci			"STA is not yet associated..skipping beacon config\n");
5362306a36Sopenharmony_ci		return -EPERM;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	memset(bs, 0, sizeof(*bs));
5762306a36Sopenharmony_ci	conf->intval = conf->beacon_interval;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/*
6062306a36Sopenharmony_ci	 * Setup dtim parameters according to
6162306a36Sopenharmony_ci	 * last beacon we received (which may be none).
6262306a36Sopenharmony_ci	 */
6362306a36Sopenharmony_ci	dtim_intval = conf->intval * conf->dtim_period;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * Pull nexttbtt forward to reflect the current
6762306a36Sopenharmony_ci	 * TSF and calculate dtim state for the result.
6862306a36Sopenharmony_ci	 */
6962306a36Sopenharmony_ci	tsf = ath9k_hw_gettsf64(ah);
7062306a36Sopenharmony_ci	conf->nexttbtt = ath9k_get_next_tbtt(ah, tsf, conf->intval);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	bs->bs_intval = TU_TO_USEC(conf->intval);
7362306a36Sopenharmony_ci	bs->bs_dtimperiod = conf->dtim_period * bs->bs_intval;
7462306a36Sopenharmony_ci	bs->bs_nexttbtt = conf->nexttbtt;
7562306a36Sopenharmony_ci	bs->bs_nextdtim = conf->nexttbtt;
7662306a36Sopenharmony_ci	if (conf->dtim_period > 1)
7762306a36Sopenharmony_ci		bs->bs_nextdtim = ath9k_get_next_tbtt(ah, tsf, dtim_intval);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * Calculate the number of consecutive beacons to miss* before taking
8162306a36Sopenharmony_ci	 * a BMISS interrupt. The configuration is specified in TU so we only
8262306a36Sopenharmony_ci	 * need calculate based	on the beacon interval.  Note that we clamp the
8362306a36Sopenharmony_ci	 * result to at most 15 beacons.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	bs->bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, conf->intval);
8662306a36Sopenharmony_ci	if (bs->bs_bmissthreshold > 15)
8762306a36Sopenharmony_ci		bs->bs_bmissthreshold = 15;
8862306a36Sopenharmony_ci	else if (bs->bs_bmissthreshold <= 0)
8962306a36Sopenharmony_ci		bs->bs_bmissthreshold = 1;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * Calculate sleep duration. The configuration is given in ms.
9362306a36Sopenharmony_ci	 * We ensure a multiple of the beacon period is used. Also, if the sleep
9462306a36Sopenharmony_ci	 * duration is greater than the DTIM period then it makes senses
9562306a36Sopenharmony_ci	 * to make it a multiple of that.
9662306a36Sopenharmony_ci	 *
9762306a36Sopenharmony_ci	 * XXX fixed at 100ms
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
10162306a36Sopenharmony_ci						  conf->intval));
10262306a36Sopenharmony_ci	if (bs->bs_sleepduration > bs->bs_dtimperiod)
10362306a36Sopenharmony_ci		bs->bs_sleepduration = bs->bs_dtimperiod;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* TSF out of range threshold fixed at 1 second */
10662306a36Sopenharmony_ci	bs->bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n",
10962306a36Sopenharmony_ci		bs->bs_bmissthreshold, bs->bs_sleepduration);
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_sta);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_civoid ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah,
11562306a36Sopenharmony_ci				   struct ath_beacon_config *conf)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	conf->intval = TU_TO_USEC(conf->beacon_interval);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (conf->ibss_creator)
12262306a36Sopenharmony_ci		conf->nexttbtt = conf->intval;
12362306a36Sopenharmony_ci	else
12462306a36Sopenharmony_ci		conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
12562306a36Sopenharmony_ci					       conf->beacon_interval);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (conf->enable_beacon)
12862306a36Sopenharmony_ci		ah->imask |= ATH9K_INT_SWBA;
12962306a36Sopenharmony_ci	else
13062306a36Sopenharmony_ci		ah->imask &= ~ATH9K_INT_SWBA;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ath_dbg(common, BEACON,
13362306a36Sopenharmony_ci		"IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
13462306a36Sopenharmony_ci		(conf->enable_beacon) ? "Enable" : "Disable",
13562306a36Sopenharmony_ci		conf->nexttbtt, conf->intval, conf->beacon_interval);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_adhoc);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * For multi-bss ap support beacons are either staggered evenly over N slots or
14162306a36Sopenharmony_ci * burst together.  For the former arrange for the SWBA to be delivered for each
14262306a36Sopenharmony_ci * slot. Slots that are not occupied will generate nothing.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_civoid ath9k_cmn_beacon_config_ap(struct ath_hw *ah,
14562306a36Sopenharmony_ci				struct ath_beacon_config *conf,
14662306a36Sopenharmony_ci				unsigned int bc_buf)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* NB: the beacon interval is kept internally in TU's */
15162306a36Sopenharmony_ci	conf->intval = TU_TO_USEC(conf->beacon_interval);
15262306a36Sopenharmony_ci	conf->intval /= bc_buf;
15362306a36Sopenharmony_ci	conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
15462306a36Sopenharmony_ci				       conf->beacon_interval);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (conf->enable_beacon)
15762306a36Sopenharmony_ci		ah->imask |= ATH9K_INT_SWBA;
15862306a36Sopenharmony_ci	else
15962306a36Sopenharmony_ci		ah->imask &= ~ATH9K_INT_SWBA;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ath_dbg(common, BEACON,
16262306a36Sopenharmony_ci		"AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
16362306a36Sopenharmony_ci		(conf->enable_beacon) ? "Enable" : "Disable",
16462306a36Sopenharmony_ci		conf->nexttbtt, conf->intval, conf->beacon_interval);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_ap);
167