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