18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "common.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define FUDGE 2
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf,
228c2ecf20Sopenharmony_ci			       unsigned int interval)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	unsigned int offset, divisor;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
278c2ecf20Sopenharmony_ci	divisor = TU_TO_USEC(interval);
288c2ecf20Sopenharmony_ci	div_u64_rem(tsf, divisor, &offset);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return (u32) tsf + divisor - offset;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * This sets up the beacon timers according to the timestamp of the last
358c2ecf20Sopenharmony_ci * received beacon and the current TSF, configures PCF and DTIM
368c2ecf20Sopenharmony_ci * handling, programs the sleep registers so the hardware will wakeup in
378c2ecf20Sopenharmony_ci * time to receive beacons, and configures the beacon miss handling so
388c2ecf20Sopenharmony_ci * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
398c2ecf20Sopenharmony_ci * we've associated with.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ciint ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
428c2ecf20Sopenharmony_ci				 struct ath_beacon_config *conf,
438c2ecf20Sopenharmony_ci				 struct ath9k_beacon_state *bs)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
468c2ecf20Sopenharmony_ci	int dtim_intval;
478c2ecf20Sopenharmony_ci	u64 tsf;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* No need to configure beacon if we are not associated */
508c2ecf20Sopenharmony_ci	if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
518c2ecf20Sopenharmony_ci		ath_dbg(common, BEACON,
528c2ecf20Sopenharmony_ci			"STA is not yet associated..skipping beacon config\n");
538c2ecf20Sopenharmony_ci		return -EPERM;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	memset(bs, 0, sizeof(*bs));
578c2ecf20Sopenharmony_ci	conf->intval = conf->beacon_interval;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/*
608c2ecf20Sopenharmony_ci	 * Setup dtim parameters according to
618c2ecf20Sopenharmony_ci	 * last beacon we received (which may be none).
628c2ecf20Sopenharmony_ci	 */
638c2ecf20Sopenharmony_ci	dtim_intval = conf->intval * conf->dtim_period;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/*
668c2ecf20Sopenharmony_ci	 * Pull nexttbtt forward to reflect the current
678c2ecf20Sopenharmony_ci	 * TSF and calculate dtim state for the result.
688c2ecf20Sopenharmony_ci	 */
698c2ecf20Sopenharmony_ci	tsf = ath9k_hw_gettsf64(ah);
708c2ecf20Sopenharmony_ci	conf->nexttbtt = ath9k_get_next_tbtt(ah, tsf, conf->intval);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	bs->bs_intval = TU_TO_USEC(conf->intval);
738c2ecf20Sopenharmony_ci	bs->bs_dtimperiod = conf->dtim_period * bs->bs_intval;
748c2ecf20Sopenharmony_ci	bs->bs_nexttbtt = conf->nexttbtt;
758c2ecf20Sopenharmony_ci	bs->bs_nextdtim = conf->nexttbtt;
768c2ecf20Sopenharmony_ci	if (conf->dtim_period > 1)
778c2ecf20Sopenharmony_ci		bs->bs_nextdtim = ath9k_get_next_tbtt(ah, tsf, dtim_intval);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * Calculate the number of consecutive beacons to miss* before taking
818c2ecf20Sopenharmony_ci	 * a BMISS interrupt. The configuration is specified in TU so we only
828c2ecf20Sopenharmony_ci	 * need calculate based	on the beacon interval.  Note that we clamp the
838c2ecf20Sopenharmony_ci	 * result to at most 15 beacons.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	bs->bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, conf->intval);
868c2ecf20Sopenharmony_ci	if (bs->bs_bmissthreshold > 15)
878c2ecf20Sopenharmony_ci		bs->bs_bmissthreshold = 15;
888c2ecf20Sopenharmony_ci	else if (bs->bs_bmissthreshold <= 0)
898c2ecf20Sopenharmony_ci		bs->bs_bmissthreshold = 1;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/*
928c2ecf20Sopenharmony_ci	 * Calculate sleep duration. The configuration is given in ms.
938c2ecf20Sopenharmony_ci	 * We ensure a multiple of the beacon period is used. Also, if the sleep
948c2ecf20Sopenharmony_ci	 * duration is greater than the DTIM period then it makes senses
958c2ecf20Sopenharmony_ci	 * to make it a multiple of that.
968c2ecf20Sopenharmony_ci	 *
978c2ecf20Sopenharmony_ci	 * XXX fixed at 100ms
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
1018c2ecf20Sopenharmony_ci						  conf->intval));
1028c2ecf20Sopenharmony_ci	if (bs->bs_sleepduration > bs->bs_dtimperiod)
1038c2ecf20Sopenharmony_ci		bs->bs_sleepduration = bs->bs_dtimperiod;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* TSF out of range threshold fixed at 1 second */
1068c2ecf20Sopenharmony_ci	bs->bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n",
1098c2ecf20Sopenharmony_ci		bs->bs_bmissthreshold, bs->bs_sleepduration);
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_sta);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_civoid ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah,
1158c2ecf20Sopenharmony_ci				   struct ath_beacon_config *conf)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	conf->intval = TU_TO_USEC(conf->beacon_interval);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (conf->ibss_creator)
1228c2ecf20Sopenharmony_ci		conf->nexttbtt = conf->intval;
1238c2ecf20Sopenharmony_ci	else
1248c2ecf20Sopenharmony_ci		conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
1258c2ecf20Sopenharmony_ci					       conf->beacon_interval);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (conf->enable_beacon)
1288c2ecf20Sopenharmony_ci		ah->imask |= ATH9K_INT_SWBA;
1298c2ecf20Sopenharmony_ci	else
1308c2ecf20Sopenharmony_ci		ah->imask &= ~ATH9K_INT_SWBA;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ath_dbg(common, BEACON,
1338c2ecf20Sopenharmony_ci		"IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
1348c2ecf20Sopenharmony_ci		(conf->enable_beacon) ? "Enable" : "Disable",
1358c2ecf20Sopenharmony_ci		conf->nexttbtt, conf->intval, conf->beacon_interval);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_adhoc);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * For multi-bss ap support beacons are either staggered evenly over N slots or
1418c2ecf20Sopenharmony_ci * burst together.  For the former arrange for the SWBA to be delivered for each
1428c2ecf20Sopenharmony_ci * slot. Slots that are not occupied will generate nothing.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_civoid ath9k_cmn_beacon_config_ap(struct ath_hw *ah,
1458c2ecf20Sopenharmony_ci				struct ath_beacon_config *conf,
1468c2ecf20Sopenharmony_ci				unsigned int bc_buf)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* NB: the beacon interval is kept internally in TU's */
1518c2ecf20Sopenharmony_ci	conf->intval = TU_TO_USEC(conf->beacon_interval);
1528c2ecf20Sopenharmony_ci	conf->intval /= bc_buf;
1538c2ecf20Sopenharmony_ci	conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
1548c2ecf20Sopenharmony_ci				       conf->beacon_interval);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (conf->enable_beacon)
1578c2ecf20Sopenharmony_ci		ah->imask |= ATH9K_INT_SWBA;
1588c2ecf20Sopenharmony_ci	else
1598c2ecf20Sopenharmony_ci		ah->imask &= ~ATH9K_INT_SWBA;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ath_dbg(common, BEACON,
1628c2ecf20Sopenharmony_ci		"AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
1638c2ecf20Sopenharmony_ci		(conf->enable_beacon) ? "Enable" : "Disable",
1648c2ecf20Sopenharmony_ci		conf->nexttbtt, conf->intval, conf->beacon_interval);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_beacon_config_ap);
167