| 1 | /* |
| 2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
| 3 | * Copyright (c) 2002-2006 Atheros Communications, Inc. |
| 4 | * |
| 5 | * Permission to use, copy, modify, and/or distribute this software for any |
| 6 | * purpose with or without fee is hereby granted, provided that the above |
| 7 | * copyright notice and this permission notice appear in all copies. |
| 8 | * |
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 16 | * |
| 17 | * $Id: ar5211_beacon.c,v 1.2 2009/01/06 06:03:57 mrg Exp $ |
| 18 | */ |
| 19 | #include "opt_ah.h" |
| 20 | |
| 21 | #include "ah.h" |
| 22 | #include "ah_internal.h" |
| 23 | |
| 24 | #include "ar5211/ar5211.h" |
| 25 | #include "ar5211/ar5211reg.h" |
| 26 | #include "ar5211/ar5211desc.h" |
| 27 | |
| 28 | /* |
| 29 | * Routines used to initialize and generated beacons for the AR5211/AR5311. |
| 30 | */ |
| 31 | |
| 32 | /* |
| 33 | * Initialize all of the hardware registers used to send beacons. |
| 34 | */ |
| 35 | void |
| 36 | ar5211SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) |
| 37 | { |
| 38 | |
| 39 | OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt); |
| 40 | OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba); |
| 41 | OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba); |
| 42 | OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim); |
| 43 | /* |
| 44 | * Set the Beacon register after setting all timers. |
| 45 | */ |
| 46 | OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval); |
| 47 | } |
| 48 | |
| 49 | /* |
| 50 | * Legacy api to initialize all of the beacon registers. |
| 51 | */ |
| 52 | void |
| 53 | ar5211BeaconInit(struct ath_hal *ah, |
| 54 | uint32_t next_beacon, uint32_t beacon_period) |
| 55 | { |
| 56 | HAL_BEACON_TIMERS bt; |
| 57 | |
| 58 | bt.bt_nextdba = 0; |
| 59 | bt.bt_nextswba = 0; |
| 60 | bt.bt_nexttbtt = next_beacon; |
| 61 | /* |
| 62 | * TIMER1: in AP/adhoc mode this controls the DMA beacon |
| 63 | * alert timer; otherwise it controls the next wakeup time. |
| 64 | * TIMER2: in AP mode, it controls the SBA beacon alert |
| 65 | * interrupt; otherwise it sets the start of the next CFP. |
| 66 | */ |
| 67 | switch (AH_PRIVATE(ah)->ah_opmode) { |
| 68 | case HAL_M_STA: |
| 69 | case HAL_M_MONITOR: |
| 70 | bt.bt_nextdba = 0xffff; |
| 71 | bt.bt_nextswba = 0x7ffff; |
| 72 | break; |
| 73 | case HAL_M_IBSS: |
| 74 | case HAL_M_HOSTAP: |
| 75 | bt.bt_nextdba = (next_beacon - |
| 76 | ath_hal_dma_beacon_response_time) << 3; /* 1/8 TU */ |
| 77 | bt.bt_nextswba = (next_beacon - |
| 78 | ath_hal_sw_beacon_response_time) << 3; /* 1/8 TU */ |
| 79 | break; |
| 80 | } |
| 81 | /* |
| 82 | * Set the ATIM window |
| 83 | * Our hardware does not support an ATIM window of 0 |
| 84 | * (beacons will not work). If the ATIM windows is 0, |
| 85 | * force it to 1. |
| 86 | */ |
| 87 | bt.bt_nextatim = next_beacon + 1; |
| 88 | bt.bt_intval = beacon_period & |
| 89 | (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN); |
| 90 | ar5211SetBeaconTimers(ah, &bt); |
| 91 | } |
| 92 | |
| 93 | void |
| 94 | ar5211ResetStaBeaconTimers(struct ath_hal *ah) |
| 95 | { |
| 96 | uint32_t val; |
| 97 | |
| 98 | OS_REG_WRITE(ah, AR_TIMER0, 0); /* no beacons */ |
| 99 | val = OS_REG_READ(ah, AR_STA_ID1); |
| 100 | val |= AR_STA_ID1_PWR_SAV; /* XXX */ |
| 101 | /* tell the h/w that the associated AP is not PCF capable */ |
| 102 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 103 | val & ~(AR_STA_ID1_DEFAULT_ANTENNA | AR_STA_ID1_PCF)); |
| 104 | OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_PERIOD); |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | * Set all the beacon related bits on the h/w for stations |
| 109 | * i.e. initializes the corresponding h/w timers; |
| 110 | * also tells the h/w whether to anticipate PCF beacons |
| 111 | */ |
| 112 | void |
| 113 | ar5211SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) |
| 114 | { |
| 115 | struct ath_hal_5211 *ahp = AH5211(ah); |
| 116 | |
| 117 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: setting beacon timers\n" , __func__); |
| 118 | |
| 119 | HALASSERT(bs->bs_intval != 0); |
| 120 | /* if the AP will do PCF */ |
| 121 | if (bs->bs_cfpmaxduration != 0) { |
| 122 | /* tell the h/w that the associated AP is PCF capable */ |
| 123 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 124 | OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PCF); |
| 125 | |
| 126 | /* set CFP_PERIOD(1.024ms) register */ |
| 127 | OS_REG_WRITE(ah, AR_CFP_PERIOD, bs->bs_cfpperiod); |
| 128 | |
| 129 | /* set CFP_DUR(1.024ms) register to max cfp duration */ |
| 130 | OS_REG_WRITE(ah, AR_CFP_DUR, bs->bs_cfpmaxduration); |
| 131 | |
| 132 | /* set TIMER2(128us) to anticipated time of next CFP */ |
| 133 | OS_REG_WRITE(ah, AR_TIMER2, bs->bs_cfpnext << 3); |
| 134 | } else { |
| 135 | /* tell the h/w that the associated AP is not PCF capable */ |
| 136 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 137 | OS_REG_READ(ah, AR_STA_ID1) &~ AR_STA_ID1_PCF); |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | * Set TIMER0(1.024ms) to the anticipated time of the next beacon. |
| 142 | */ |
| 143 | OS_REG_WRITE(ah, AR_TIMER0, bs->bs_nexttbtt); |
| 144 | |
| 145 | /* |
| 146 | * Start the beacon timers by setting the BEACON register |
| 147 | * to the beacon interval; also write the tim offset which |
| 148 | * we should know by now. The code, in ar5211WriteAssocid, |
| 149 | * also sets the tim offset once the AID is known which can |
| 150 | * be left as such for now. |
| 151 | */ |
| 152 | OS_REG_WRITE(ah, AR_BEACON, |
| 153 | (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_PERIOD|AR_BEACON_TIM)) |
| 154 | | SM(bs->bs_intval, AR_BEACON_PERIOD) |
| 155 | | SM(bs->bs_timoffset ? bs->bs_timoffset + 4 : 0, AR_BEACON_TIM) |
| 156 | ); |
| 157 | |
| 158 | /* |
| 159 | * Configure the BMISS interrupt. Note that we |
| 160 | * assume the caller blocks interrupts while enabling |
| 161 | * the threshold. |
| 162 | */ |
| 163 | HALASSERT(bs->bs_bmissthreshold <= MS(0xffffffff, AR_RSSI_THR_BM_THR)); |
| 164 | ahp->ah_rssiThr = (ahp->ah_rssiThr &~ AR_RSSI_THR_BM_THR) |
| 165 | | SM(bs->bs_bmissthreshold, AR_RSSI_THR_BM_THR); |
| 166 | OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); |
| 167 | |
| 168 | /* |
| 169 | * Set the sleep duration in 1/8 TU's. |
| 170 | */ |
| 171 | #define SLEEP_SLOP 3 |
| 172 | OS_REG_RMW_FIELD(ah, AR_SCR, AR_SCR_SLDUR, |
| 173 | (bs->bs_sleepduration - SLEEP_SLOP) << 3); |
| 174 | #undef SLEEP_SLOP |
| 175 | } |
| 176 | |