Commit | Line | Data |
---|---|---|
cbbdf2ae OR |
1 | /* |
2 | * Copyright (c) 2008-2011 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 "common.h" | |
18 | ||
19 | #define FUDGE 2 | |
20 | ||
21 | /* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ | |
22 | static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) | |
23 | { | |
24 | u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; | |
25 | ||
26 | tsf_mod = tsf & (BIT(10) - 1); | |
27 | tsf_hi = tsf >> 32; | |
28 | tsf_lo = ((u32) tsf) >> 10; | |
29 | ||
30 | mod_hi = tsf_hi % div_tu; | |
31 | mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; | |
32 | ||
33 | return (mod_lo << 10) | tsf_mod; | |
34 | } | |
35 | ||
36 | static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf, | |
37 | unsigned int interval) | |
38 | { | |
39 | unsigned int offset; | |
40 | ||
41 | tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); | |
42 | offset = ath9k_mod_tsf64_tu(tsf, interval); | |
43 | ||
44 | return (u32) tsf + TU_TO_USEC(interval) - offset; | |
45 | } | |
46 | ||
47 | /* | |
48 | * This sets up the beacon timers according to the timestamp of the last | |
49 | * received beacon and the current TSF, configures PCF and DTIM | |
50 | * handling, programs the sleep registers so the hardware will wakeup in | |
51 | * time to receive beacons, and configures the beacon miss handling so | |
52 | * we'll receive a BMISS interrupt when we stop seeing beacons from the AP | |
53 | * we've associated with. | |
54 | */ | |
55 | int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, | |
56 | struct ath_beacon_config *conf, | |
57 | struct ath9k_beacon_state *bs) | |
58 | { | |
59 | struct ath_common *common = ath9k_hw_common(ah); | |
ae0fd635 | 60 | int dtim_intval; |
cbbdf2ae OR |
61 | u64 tsf; |
62 | ||
63 | /* No need to configure beacon if we are not associated */ | |
64 | if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { | |
65 | ath_dbg(common, BEACON, | |
66 | "STA is not yet associated..skipping beacon config\n"); | |
67 | return -EPERM; | |
68 | } | |
69 | ||
70 | memset(bs, 0, sizeof(*bs)); | |
71 | conf->intval = conf->beacon_interval; | |
72 | ||
73 | /* | |
74 | * Setup dtim parameters according to | |
75 | * last beacon we received (which may be none). | |
76 | */ | |
77 | dtim_intval = conf->intval * conf->dtim_period; | |
78 | ||
79 | /* | |
80 | * Pull nexttbtt forward to reflect the current | |
81 | * TSF and calculate dtim state for the result. | |
82 | */ | |
83 | tsf = ath9k_hw_gettsf64(ah); | |
84 | conf->nexttbtt = ath9k_get_next_tbtt(ah, tsf, conf->intval); | |
85 | ||
86 | bs->bs_intval = TU_TO_USEC(conf->intval); | |
87 | bs->bs_dtimperiod = conf->dtim_period * bs->bs_intval; | |
88 | bs->bs_nexttbtt = conf->nexttbtt; | |
89 | bs->bs_nextdtim = conf->nexttbtt; | |
90 | if (conf->dtim_period > 1) | |
91 | bs->bs_nextdtim = ath9k_get_next_tbtt(ah, tsf, dtim_intval); | |
92 | ||
93 | /* | |
94 | * Calculate the number of consecutive beacons to miss* before taking | |
95 | * a BMISS interrupt. The configuration is specified in TU so we only | |
96 | * need calculate based on the beacon interval. Note that we clamp the | |
97 | * result to at most 15 beacons. | |
98 | */ | |
99 | bs->bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, conf->intval); | |
100 | if (bs->bs_bmissthreshold > 15) | |
101 | bs->bs_bmissthreshold = 15; | |
102 | else if (bs->bs_bmissthreshold <= 0) | |
103 | bs->bs_bmissthreshold = 1; | |
104 | ||
105 | /* | |
106 | * Calculate sleep duration. The configuration is given in ms. | |
107 | * We ensure a multiple of the beacon period is used. Also, if the sleep | |
108 | * duration is greater than the DTIM period then it makes senses | |
109 | * to make it a multiple of that. | |
110 | * | |
111 | * XXX fixed at 100ms | |
112 | */ | |
113 | ||
114 | bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), | |
ae0fd635 | 115 | conf->intval)); |
cbbdf2ae OR |
116 | if (bs->bs_sleepduration > bs->bs_dtimperiod) |
117 | bs->bs_sleepduration = bs->bs_dtimperiod; | |
118 | ||
119 | /* TSF out of range threshold fixed at 1 second */ | |
120 | bs->bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; | |
121 | ||
122 | ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n", | |
123 | bs->bs_bmissthreshold, bs->bs_sleepduration); | |
124 | return 0; | |
125 | } | |
126 | EXPORT_SYMBOL(ath9k_cmn_beacon_config_sta); | |
4c9a1f32 OR |
127 | |
128 | void ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah, | |
129 | struct ath_beacon_config *conf) | |
130 | { | |
131 | struct ath_common *common = ath9k_hw_common(ah); | |
132 | ||
133 | conf->intval = TU_TO_USEC(conf->beacon_interval); | |
134 | ||
135 | if (conf->ibss_creator) | |
136 | conf->nexttbtt = conf->intval; | |
137 | else | |
138 | conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah), | |
139 | conf->beacon_interval); | |
140 | ||
141 | if (conf->enable_beacon) | |
142 | ah->imask |= ATH9K_INT_SWBA; | |
143 | else | |
144 | ah->imask &= ~ATH9K_INT_SWBA; | |
145 | ||
146 | ath_dbg(common, BEACON, | |
147 | "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n", | |
148 | (conf->enable_beacon) ? "Enable" : "Disable", | |
149 | conf->nexttbtt, conf->intval, conf->beacon_interval); | |
150 | } | |
151 | EXPORT_SYMBOL(ath9k_cmn_beacon_config_adhoc); | |
fa7b52fa OR |
152 | |
153 | /* | |
154 | * For multi-bss ap support beacons are either staggered evenly over N slots or | |
155 | * burst together. For the former arrange for the SWBA to be delivered for each | |
156 | * slot. Slots that are not occupied will generate nothing. | |
157 | */ | |
158 | void ath9k_cmn_beacon_config_ap(struct ath_hw *ah, | |
159 | struct ath_beacon_config *conf, | |
160 | unsigned int bc_buf) | |
161 | { | |
162 | struct ath_common *common = ath9k_hw_common(ah); | |
163 | ||
164 | /* NB: the beacon interval is kept internally in TU's */ | |
165 | conf->intval = TU_TO_USEC(conf->beacon_interval); | |
166 | conf->intval /= bc_buf; | |
167 | conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah), | |
168 | conf->beacon_interval); | |
169 | ||
170 | if (conf->enable_beacon) | |
171 | ah->imask |= ATH9K_INT_SWBA; | |
172 | else | |
173 | ah->imask &= ~ATH9K_INT_SWBA; | |
174 | ||
175 | ath_dbg(common, BEACON, | |
176 | "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n", | |
177 | (conf->enable_beacon) ? "Enable" : "Disable", | |
178 | conf->nexttbtt, conf->intval, conf->beacon_interval); | |
179 | } | |
180 | EXPORT_SYMBOL(ath9k_cmn_beacon_config_ap); |