htc_drv_beacon.c revision 7f1f5a0060e377ff6a15903487b39223e12b8568
1/* 2 * Copyright (c) 2010 Atheros Communications Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include "htc.h" 18 19#define FUDGE 2 20 21static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, 22 struct htc_beacon_config *bss_conf) 23{ 24 struct ath_common *common = ath9k_hw_common(priv->ah); 25 struct ath9k_beacon_state bs; 26 enum ath9k_int imask = 0; 27 int dtimperiod, dtimcount, sleepduration; 28 int cfpperiod, cfpcount, bmiss_timeout; 29 u32 nexttbtt = 0, intval, tsftu; 30 __be32 htc_imask = 0; 31 u64 tsf; 32 int num_beacons, offset, dtim_dec_count, cfp_dec_count; 33 int ret; 34 u8 cmd_rsp; 35 36 memset(&bs, 0, sizeof(bs)); 37 38 intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; 39 bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); 40 41 /* 42 * Setup dtim and cfp parameters according to 43 * last beacon we received (which may be none). 44 */ 45 dtimperiod = bss_conf->dtim_period; 46 if (dtimperiod <= 0) /* NB: 0 if not known */ 47 dtimperiod = 1; 48 dtimcount = 1; 49 if (dtimcount >= dtimperiod) /* NB: sanity check */ 50 dtimcount = 0; 51 cfpperiod = 1; /* NB: no PCF support yet */ 52 cfpcount = 0; 53 54 sleepduration = intval; 55 if (sleepduration <= 0) 56 sleepduration = intval; 57 58 /* 59 * Pull nexttbtt forward to reflect the current 60 * TSF and calculate dtim+cfp state for the result. 61 */ 62 tsf = ath9k_hw_gettsf64(priv->ah); 63 tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 64 65 num_beacons = tsftu / intval + 1; 66 offset = tsftu % intval; 67 nexttbtt = tsftu - offset; 68 if (offset) 69 nexttbtt += intval; 70 71 /* DTIM Beacon every dtimperiod Beacon */ 72 dtim_dec_count = num_beacons % dtimperiod; 73 /* CFP every cfpperiod DTIM Beacon */ 74 cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; 75 if (dtim_dec_count) 76 cfp_dec_count++; 77 78 dtimcount -= dtim_dec_count; 79 if (dtimcount < 0) 80 dtimcount += dtimperiod; 81 82 cfpcount -= cfp_dec_count; 83 if (cfpcount < 0) 84 cfpcount += cfpperiod; 85 86 bs.bs_intval = intval; 87 bs.bs_nexttbtt = nexttbtt; 88 bs.bs_dtimperiod = dtimperiod*intval; 89 bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 90 bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 91 bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 92 bs.bs_cfpmaxduration = 0; 93 94 /* 95 * Calculate the number of consecutive beacons to miss* before taking 96 * a BMISS interrupt. The configuration is specified in TU so we only 97 * need calculate based on the beacon interval. Note that we clamp the 98 * result to at most 15 beacons. 99 */ 100 if (sleepduration > intval) { 101 bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; 102 } else { 103 bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); 104 if (bs.bs_bmissthreshold > 15) 105 bs.bs_bmissthreshold = 15; 106 else if (bs.bs_bmissthreshold <= 0) 107 bs.bs_bmissthreshold = 1; 108 } 109 110 /* 111 * Calculate sleep duration. The configuration is given in ms. 112 * We ensure a multiple of the beacon period is used. Also, if the sleep 113 * duration is greater than the DTIM period then it makes senses 114 * to make it a multiple of that. 115 * 116 * XXX fixed at 100ms 117 */ 118 119 bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); 120 if (bs.bs_sleepduration > bs.bs_dtimperiod) 121 bs.bs_sleepduration = bs.bs_dtimperiod; 122 123 /* TSF out of range threshold fixed at 1 second */ 124 bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; 125 126 ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); 127 ath_print(common, ATH_DBG_BEACON, 128 "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", 129 bs.bs_bmissthreshold, bs.bs_sleepduration, 130 bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); 131 132 /* Set the computed STA beacon timers */ 133 134 WMI_CMD(WMI_DISABLE_INTR_CMDID); 135 ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); 136 imask |= ATH9K_INT_BMISS; 137 htc_imask = cpu_to_be32(imask); 138 WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); 139} 140 141static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, 142 struct htc_beacon_config *bss_conf) 143{ 144 struct ath_common *common = ath9k_hw_common(priv->ah); 145 enum ath9k_int imask = 0; 146 u32 nexttbtt, intval; 147 __be32 htc_imask = 0; 148 int ret; 149 u8 cmd_rsp; 150 151 intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; 152 nexttbtt = intval; 153 intval |= ATH9K_BEACON_ENA; 154 if (priv->op_flags & OP_ENABLE_BEACON) 155 imask |= ATH9K_INT_SWBA; 156 157 ath_print(common, ATH_DBG_BEACON, 158 "IBSS Beacon config, intval: %d, imask: 0x%x\n", 159 bss_conf->beacon_interval, imask); 160 161 WMI_CMD(WMI_DISABLE_INTR_CMDID); 162 ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); 163 priv->bmiss_cnt = 0; 164 htc_imask = cpu_to_be32(imask); 165 WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); 166} 167 168void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, 169 struct ieee80211_vif *vif) 170{ 171 struct ath_common *common = ath9k_hw_common(priv->ah); 172 173 spin_lock_bh(&priv->beacon_lock); 174 175 if (priv->beacon) 176 dev_kfree_skb_any(priv->beacon); 177 178 priv->beacon = ieee80211_beacon_get(priv->hw, vif); 179 if (!priv->beacon) 180 ath_print(common, ATH_DBG_BEACON, 181 "Unable to allocate beacon\n"); 182 183 spin_unlock_bh(&priv->beacon_lock); 184} 185 186void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) 187{ 188 struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv; 189 struct tx_beacon_header beacon_hdr; 190 struct ath9k_htc_tx_ctl tx_ctl; 191 struct ieee80211_tx_info *info; 192 u8 *tx_fhdr; 193 194 memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); 195 memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); 196 197 /* FIXME: Handle BMISS */ 198 if (beacon_pending != 0) { 199 priv->bmiss_cnt++; 200 return; 201 } 202 203 spin_lock_bh(&priv->beacon_lock); 204 205 if (unlikely(priv->op_flags & OP_SCANNING)) { 206 spin_unlock_bh(&priv->beacon_lock); 207 return; 208 } 209 210 if (unlikely(priv->beacon == NULL)) { 211 spin_unlock_bh(&priv->beacon_lock); 212 return; 213 } 214 215 /* Free the old SKB first */ 216 dev_kfree_skb_any(priv->beacon); 217 218 /* Get a new beacon */ 219 priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif); 220 if (!priv->beacon) { 221 spin_unlock_bh(&priv->beacon_lock); 222 return; 223 } 224 225 info = IEEE80211_SKB_CB(priv->beacon); 226 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 227 struct ieee80211_hdr *hdr = 228 (struct ieee80211_hdr *) priv->beacon->data; 229 priv->seq_no += 0x10; 230 hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 231 hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); 232 } 233 234 tx_ctl.type = ATH9K_HTC_NORMAL; 235 beacon_hdr.vif_index = avp->index; 236 tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr)); 237 memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); 238 239 htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl); 240 241 spin_unlock_bh(&priv->beacon_lock); 242} 243 244 245void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, 246 struct ieee80211_vif *vif) 247{ 248 struct ath_common *common = ath9k_hw_common(priv->ah); 249 struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; 250 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 251 252 cur_conf->beacon_interval = bss_conf->beacon_int; 253 if (cur_conf->beacon_interval == 0) 254 cur_conf->beacon_interval = 100; 255 256 cur_conf->dtim_period = bss_conf->dtim_period; 257 cur_conf->listen_interval = 1; 258 cur_conf->dtim_count = 1; 259 cur_conf->bmiss_timeout = 260 ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 261 262 switch (vif->type) { 263 case NL80211_IFTYPE_STATION: 264 ath9k_htc_beacon_config_sta(priv, cur_conf); 265 break; 266 case NL80211_IFTYPE_ADHOC: 267 ath9k_htc_beacon_config_adhoc(priv, cur_conf); 268 break; 269 default: 270 ath_print(common, ATH_DBG_CONFIG, 271 "Unsupported beaconing mode\n"); 272 return; 273 } 274} 275