]>
Commit | Line | Data |
---|---|---|
fb9987d0 S |
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 | ||
21 | static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, | |
1c3652a5 | 22 | struct htc_beacon_config *bss_conf) |
fb9987d0 S |
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; | |
7f1f5a00 S |
29 | u32 nexttbtt = 0, intval, tsftu; |
30 | __be32 htc_imask = 0; | |
fb9987d0 S |
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 | ||
1c3652a5 VN |
38 | intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; |
39 | bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); | |
fb9987d0 S |
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 | ||
141 | static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, | |
1c3652a5 | 142 | struct htc_beacon_config *bss_conf) |
fb9987d0 S |
143 | { |
144 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
145 | enum ath9k_int imask = 0; | |
7f1f5a00 S |
146 | u32 nexttbtt, intval; |
147 | __be32 htc_imask = 0; | |
fb9987d0 S |
148 | int ret; |
149 | u8 cmd_rsp; | |
150 | ||
1c3652a5 | 151 | intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; |
fb9987d0 S |
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", | |
1c3652a5 | 159 | bss_conf->beacon_interval, imask); |
fb9987d0 S |
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 | ||
9c6dda4e S |
168 | void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, |
169 | enum htc_endpoint_id ep_id, bool txok) | |
fb9987d0 | 170 | { |
9c6dda4e | 171 | dev_kfree_skb_any(skb); |
fb9987d0 S |
172 | } |
173 | ||
174 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | |
175 | { | |
176 | struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv; | |
177 | struct tx_beacon_header beacon_hdr; | |
178 | struct ath9k_htc_tx_ctl tx_ctl; | |
179 | struct ieee80211_tx_info *info; | |
9c6dda4e | 180 | struct sk_buff *beacon; |
fb9987d0 S |
181 | u8 *tx_fhdr; |
182 | ||
183 | memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); | |
184 | memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); | |
185 | ||
186 | /* FIXME: Handle BMISS */ | |
187 | if (beacon_pending != 0) { | |
188 | priv->bmiss_cnt++; | |
189 | return; | |
190 | } | |
191 | ||
192 | spin_lock_bh(&priv->beacon_lock); | |
193 | ||
194 | if (unlikely(priv->op_flags & OP_SCANNING)) { | |
195 | spin_unlock_bh(&priv->beacon_lock); | |
196 | return; | |
197 | } | |
198 | ||
fb9987d0 | 199 | /* Get a new beacon */ |
9c6dda4e S |
200 | beacon = ieee80211_beacon_get(priv->hw, priv->vif); |
201 | if (!beacon) { | |
fb9987d0 S |
202 | spin_unlock_bh(&priv->beacon_lock); |
203 | return; | |
204 | } | |
205 | ||
9c6dda4e | 206 | info = IEEE80211_SKB_CB(beacon); |
fb9987d0 S |
207 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
208 | struct ieee80211_hdr *hdr = | |
9c6dda4e | 209 | (struct ieee80211_hdr *) beacon->data; |
fb9987d0 S |
210 | priv->seq_no += 0x10; |
211 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); | |
212 | hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); | |
213 | } | |
214 | ||
215 | tx_ctl.type = ATH9K_HTC_NORMAL; | |
216 | beacon_hdr.vif_index = avp->index; | |
9c6dda4e | 217 | tx_fhdr = skb_push(beacon, sizeof(beacon_hdr)); |
fb9987d0 S |
218 | memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); |
219 | ||
9c6dda4e | 220 | htc_send(priv->htc, beacon, priv->beacon_ep, &tx_ctl); |
fb9987d0 S |
221 | |
222 | spin_unlock_bh(&priv->beacon_lock); | |
223 | } | |
224 | ||
764580f5 S |
225 | /* Currently, only for IBSS */ |
226 | void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) | |
227 | { | |
228 | struct ath_hw *ah = priv->ah; | |
229 | struct ath9k_tx_queue_info qi, qi_be; | |
e8c35a77 | 230 | int qnum = priv->hwq_map[WME_AC_BE]; |
764580f5 S |
231 | |
232 | memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); | |
233 | memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info)); | |
234 | ||
235 | ath9k_hw_get_txq_props(ah, qnum, &qi_be); | |
236 | ||
237 | qi.tqi_aifs = qi_be.tqi_aifs; | |
9cf13668 RM |
238 | /* For WIFI Beacon Distribution |
239 | * Long slot time : 2x cwmin | |
240 | * Short slot time : 4x cwmin | |
241 | */ | |
242 | if (ah->slottime == ATH9K_SLOT_TIME_20) | |
243 | qi.tqi_cwmin = 2*qi_be.tqi_cwmin; | |
244 | else | |
245 | qi.tqi_cwmin = 4*qi_be.tqi_cwmin; | |
764580f5 S |
246 | qi.tqi_cwmax = qi_be.tqi_cwmax; |
247 | ||
248 | if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) { | |
249 | ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, | |
250 | "Unable to update beacon queue %u!\n", qnum); | |
251 | } else { | |
252 | ath9k_hw_resettxqueue(ah, priv->beaconq); | |
253 | } | |
254 | } | |
1c3652a5 | 255 | |
fb9987d0 | 256 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, |
1c3652a5 | 257 | struct ieee80211_vif *vif) |
fb9987d0 S |
258 | { |
259 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1c3652a5 | 260 | struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; |
fcb9392f | 261 | struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; |
1c3652a5 | 262 | |
fcb9392f | 263 | cur_conf->beacon_interval = bss_conf->beacon_int; |
1c3652a5 VN |
264 | if (cur_conf->beacon_interval == 0) |
265 | cur_conf->beacon_interval = 100; | |
266 | ||
fcb9392f S |
267 | cur_conf->dtim_period = bss_conf->dtim_period; |
268 | cur_conf->listen_interval = 1; | |
269 | cur_conf->dtim_count = 1; | |
270 | cur_conf->bmiss_timeout = | |
271 | ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; | |
272 | ||
273 | switch (vif->type) { | |
fb9987d0 | 274 | case NL80211_IFTYPE_STATION: |
1c3652a5 | 275 | ath9k_htc_beacon_config_sta(priv, cur_conf); |
fb9987d0 S |
276 | break; |
277 | case NL80211_IFTYPE_ADHOC: | |
1c3652a5 | 278 | ath9k_htc_beacon_config_adhoc(priv, cur_conf); |
fb9987d0 S |
279 | break; |
280 | default: | |
281 | ath_print(common, ATH_DBG_CONFIG, | |
282 | "Unsupported beaconing mode\n"); | |
283 | return; | |
284 | } | |
285 | } |