1 | /* |
2 | * Copyright (c) 2010-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 "htc.h" |
18 | |
19 | #define FUDGE 2 |
20 | |
21 | void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) |
22 | { |
23 | struct ath_hw *ah = priv->ah; |
24 | struct ath9k_tx_queue_info qi, qi_be; |
25 | |
26 | memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); |
27 | memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info)); |
28 | |
29 | ath9k_hw_get_txq_props(ah, q: priv->beacon.beaconq, qinfo: &qi); |
30 | |
31 | if (priv->ah->opmode == NL80211_IFTYPE_AP || |
32 | priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) { |
33 | qi.tqi_aifs = 1; |
34 | qi.tqi_cwmin = 0; |
35 | qi.tqi_cwmax = 0; |
36 | } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { |
37 | int qnum = priv->hwq_map[IEEE80211_AC_BE]; |
38 | |
39 | ath9k_hw_get_txq_props(ah, q: qnum, qinfo: &qi_be); |
40 | |
41 | qi.tqi_aifs = qi_be.tqi_aifs; |
42 | |
43 | /* |
44 | * For WIFI Beacon Distribution |
45 | * Long slot time : 2x cwmin |
46 | * Short slot time : 4x cwmin |
47 | */ |
48 | if (ah->slottime == 20) |
49 | qi.tqi_cwmin = 2*qi_be.tqi_cwmin; |
50 | else |
51 | qi.tqi_cwmin = 4*qi_be.tqi_cwmin; |
52 | |
53 | qi.tqi_cwmax = qi_be.tqi_cwmax; |
54 | |
55 | } |
56 | |
57 | if (!ath9k_hw_set_txq_props(ah, q: priv->beacon.beaconq, qinfo: &qi)) { |
58 | ath_err(ath9k_hw_common(ah), |
59 | "Unable to update beacon queue %u!\n" , priv->beacon.beaconq); |
60 | } else { |
61 | ath9k_hw_resettxqueue(ah, q: priv->beacon.beaconq); |
62 | } |
63 | } |
64 | |
65 | /* |
66 | * Both nexttbtt and intval have to be in usecs. |
67 | */ |
68 | static void ath9k_htc_beacon_init(struct ath9k_htc_priv *priv, |
69 | struct ath_beacon_config *conf, |
70 | bool reset_tsf) |
71 | { |
72 | struct ath_hw *ah = priv->ah; |
73 | int ret __attribute__ ((unused)); |
74 | __be32 htc_imask = 0; |
75 | u8 cmd_rsp; |
76 | |
77 | if (conf->intval >= TU_TO_USEC(DEFAULT_SWBA_RESPONSE)) |
78 | ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; |
79 | else |
80 | ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; |
81 | |
82 | WMI_CMD(WMI_DISABLE_INTR_CMDID); |
83 | if (reset_tsf) |
84 | ath9k_hw_reset_tsf(ah); |
85 | ath9k_htc_beaconq_config(priv); |
86 | ath9k_hw_beaconinit(ah, next_beacon: conf->nexttbtt, beacon_period: conf->intval); |
87 | priv->beacon.bmisscnt = 0; |
88 | htc_imask = cpu_to_be32(ah->imask); |
89 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); |
90 | } |
91 | |
92 | static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, |
93 | struct ath_beacon_config *bss_conf) |
94 | { |
95 | struct ath9k_beacon_state bs; |
96 | enum ath9k_int imask = 0; |
97 | __be32 htc_imask = 0; |
98 | int ret __attribute__ ((unused)); |
99 | u8 cmd_rsp; |
100 | |
101 | if (ath9k_cmn_beacon_config_sta(ah: priv->ah, conf: bss_conf, bs: &bs) == -EPERM) |
102 | return; |
103 | |
104 | WMI_CMD(WMI_DISABLE_INTR_CMDID); |
105 | ath9k_hw_set_sta_beacon_timers(ah: priv->ah, bs: &bs); |
106 | imask |= ATH9K_INT_BMISS; |
107 | htc_imask = cpu_to_be32(imask); |
108 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); |
109 | } |
110 | |
111 | static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, |
112 | struct ath_beacon_config *conf) |
113 | { |
114 | struct ath_hw *ah = priv->ah; |
115 | ah->imask = 0; |
116 | |
117 | ath9k_cmn_beacon_config_ap(ah, conf, ATH9K_HTC_MAX_BCN_VIF); |
118 | ath9k_htc_beacon_init(priv, conf, reset_tsf: false); |
119 | } |
120 | |
121 | static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, |
122 | struct ath_beacon_config *conf) |
123 | { |
124 | struct ath_hw *ah = priv->ah; |
125 | ah->imask = 0; |
126 | |
127 | ath9k_cmn_beacon_config_adhoc(ah, conf); |
128 | ath9k_htc_beacon_init(priv, conf, reset_tsf: conf->ibss_creator); |
129 | } |
130 | |
131 | void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, |
132 | enum htc_endpoint_id ep_id, bool txok) |
133 | { |
134 | dev_kfree_skb_any(skb); |
135 | } |
136 | |
137 | static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, |
138 | int slot) |
139 | { |
140 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
141 | struct ieee80211_vif *vif; |
142 | struct sk_buff *skb; |
143 | struct ieee80211_hdr *hdr; |
144 | int padpos, padsize, ret, tx_slot; |
145 | |
146 | spin_lock_bh(lock: &priv->beacon_lock); |
147 | |
148 | vif = priv->beacon.bslot[slot]; |
149 | |
150 | skb = ieee80211_get_buffered_bc(hw: priv->hw, vif); |
151 | |
152 | while(skb) { |
153 | hdr = (struct ieee80211_hdr *) skb->data; |
154 | |
155 | padpos = ieee80211_hdrlen(fc: hdr->frame_control); |
156 | padsize = padpos & 3; |
157 | if (padsize && skb->len > padpos) { |
158 | if (skb_headroom(skb) < padsize) { |
159 | dev_kfree_skb_any(skb); |
160 | goto next; |
161 | } |
162 | skb_push(skb, len: padsize); |
163 | memmove(skb->data, skb->data + padsize, padpos); |
164 | } |
165 | |
166 | tx_slot = ath9k_htc_tx_get_slot(priv); |
167 | if (tx_slot < 0) { |
168 | ath_dbg(common, XMIT, "No free CAB slot\n" ); |
169 | dev_kfree_skb_any(skb); |
170 | goto next; |
171 | } |
172 | |
173 | ret = ath9k_htc_tx_start(priv, NULL, skb, slot: tx_slot, is_cab: true); |
174 | if (ret != 0) { |
175 | ath9k_htc_tx_clear_slot(priv, slot: tx_slot); |
176 | dev_kfree_skb_any(skb); |
177 | |
178 | ath_dbg(common, XMIT, "Failed to send CAB frame\n" ); |
179 | } else { |
180 | spin_lock_bh(lock: &priv->tx.tx_lock); |
181 | priv->tx.queued_cnt++; |
182 | spin_unlock_bh(lock: &priv->tx.tx_lock); |
183 | } |
184 | next: |
185 | skb = ieee80211_get_buffered_bc(hw: priv->hw, vif); |
186 | } |
187 | |
188 | spin_unlock_bh(lock: &priv->beacon_lock); |
189 | } |
190 | |
191 | static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv, |
192 | int slot) |
193 | { |
194 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
195 | struct ieee80211_vif *vif; |
196 | struct ath9k_htc_vif *avp; |
197 | struct tx_beacon_header beacon_hdr; |
198 | struct ath9k_htc_tx_ctl *tx_ctl; |
199 | struct ieee80211_tx_info *info; |
200 | struct ieee80211_mgmt *mgmt; |
201 | struct sk_buff *beacon; |
202 | u8 *tx_fhdr; |
203 | int ret; |
204 | |
205 | memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); |
206 | |
207 | spin_lock_bh(lock: &priv->beacon_lock); |
208 | |
209 | vif = priv->beacon.bslot[slot]; |
210 | avp = (struct ath9k_htc_vif *)vif->drv_priv; |
211 | |
212 | if (unlikely(test_bit(ATH_OP_SCANNING, &common->op_flags))) { |
213 | spin_unlock_bh(lock: &priv->beacon_lock); |
214 | return; |
215 | } |
216 | |
217 | /* Get a new beacon */ |
218 | beacon = ieee80211_beacon_get(hw: priv->hw, vif, link_id: 0); |
219 | if (!beacon) { |
220 | spin_unlock_bh(lock: &priv->beacon_lock); |
221 | return; |
222 | } |
223 | |
224 | /* |
225 | * Update the TSF adjust value here, the HW will |
226 | * add this value for every beacon. |
227 | */ |
228 | mgmt = (struct ieee80211_mgmt *)beacon->data; |
229 | mgmt->u.beacon.timestamp = avp->tsfadjust; |
230 | |
231 | info = IEEE80211_SKB_CB(skb: beacon); |
232 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
233 | struct ieee80211_hdr *hdr = |
234 | (struct ieee80211_hdr *) beacon->data; |
235 | avp->seq_no += 0x10; |
236 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); |
237 | hdr->seq_ctrl |= cpu_to_le16(avp->seq_no); |
238 | } |
239 | |
240 | tx_ctl = HTC_SKB_CB(skb: beacon); |
241 | memset(tx_ctl, 0, sizeof(*tx_ctl)); |
242 | |
243 | tx_ctl->type = ATH9K_HTC_BEACON; |
244 | tx_ctl->epid = priv->beacon_ep; |
245 | |
246 | beacon_hdr.vif_index = avp->index; |
247 | tx_fhdr = skb_push(skb: beacon, len: sizeof(beacon_hdr)); |
248 | memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); |
249 | |
250 | ret = htc_send(target: priv->htc, skb: beacon); |
251 | if (ret != 0) { |
252 | if (ret == -ENOMEM) { |
253 | ath_dbg(common, BSTUCK, |
254 | "Failed to send beacon, no free TX buffer\n" ); |
255 | } |
256 | dev_kfree_skb_any(skb: beacon); |
257 | } |
258 | |
259 | spin_unlock_bh(lock: &priv->beacon_lock); |
260 | |
261 | ath9k_htc_csa_is_finished(priv); |
262 | } |
263 | |
264 | static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv, |
265 | struct wmi_event_swba *swba) |
266 | { |
267 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
268 | u64 tsf; |
269 | u32 tsftu; |
270 | u16 intval; |
271 | int slot; |
272 | |
273 | intval = priv->cur_beacon_conf.beacon_interval; |
274 | |
275 | tsf = be64_to_cpu(swba->tsf); |
276 | tsftu = TSF_TO_TU(tsf >> 32, tsf); |
277 | slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval; |
278 | slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1; |
279 | |
280 | ath_dbg(common, BEACON, |
281 | "Choose slot: %d, tsf: %llu, tsftu: %u, intval: %u\n" , |
282 | slot, tsf, tsftu, intval); |
283 | |
284 | return slot; |
285 | } |
286 | |
287 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, |
288 | struct wmi_event_swba *swba) |
289 | { |
290 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
291 | int slot; |
292 | |
293 | if (swba->beacon_pending != 0) { |
294 | priv->beacon.bmisscnt++; |
295 | if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) { |
296 | ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n" ); |
297 | ieee80211_queue_work(hw: priv->hw, |
298 | work: &priv->fatal_work); |
299 | } |
300 | return; |
301 | } |
302 | |
303 | if (priv->beacon.bmisscnt) { |
304 | ath_dbg(common, BSTUCK, |
305 | "Resuming beacon xmit after %u misses\n" , |
306 | priv->beacon.bmisscnt); |
307 | priv->beacon.bmisscnt = 0; |
308 | } |
309 | |
310 | slot = ath9k_htc_choose_bslot(priv, swba); |
311 | spin_lock_bh(lock: &priv->beacon_lock); |
312 | if (priv->beacon.bslot[slot] == NULL) { |
313 | spin_unlock_bh(lock: &priv->beacon_lock); |
314 | return; |
315 | } |
316 | spin_unlock_bh(lock: &priv->beacon_lock); |
317 | |
318 | ath9k_htc_send_buffered(priv, slot); |
319 | ath9k_htc_send_beacon(priv, slot); |
320 | } |
321 | |
322 | void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv, |
323 | struct ieee80211_vif *vif) |
324 | { |
325 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
326 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; |
327 | int i = 0; |
328 | |
329 | spin_lock_bh(lock: &priv->beacon_lock); |
330 | for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) { |
331 | if (priv->beacon.bslot[i] == NULL) { |
332 | avp->bslot = i; |
333 | break; |
334 | } |
335 | } |
336 | |
337 | priv->beacon.bslot[avp->bslot] = vif; |
338 | spin_unlock_bh(lock: &priv->beacon_lock); |
339 | |
340 | ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n" , |
341 | avp->bslot); |
342 | } |
343 | |
344 | void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, |
345 | struct ieee80211_vif *vif) |
346 | { |
347 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
348 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; |
349 | |
350 | spin_lock_bh(lock: &priv->beacon_lock); |
351 | priv->beacon.bslot[avp->bslot] = NULL; |
352 | spin_unlock_bh(lock: &priv->beacon_lock); |
353 | |
354 | ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n" , |
355 | avp->bslot); |
356 | } |
357 | |
358 | /* |
359 | * Calculate the TSF adjustment value for all slots |
360 | * other than zero. |
361 | */ |
362 | void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv, |
363 | struct ieee80211_vif *vif) |
364 | { |
365 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
366 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; |
367 | struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; |
368 | u64 tsfadjust; |
369 | |
370 | if (avp->bslot == 0) |
371 | return; |
372 | |
373 | /* |
374 | * The beacon interval cannot be different for multi-AP mode, |
375 | * and we reach here only for VIF slots greater than zero, |
376 | * so beacon_interval is guaranteed to be set in cur_conf. |
377 | */ |
378 | tsfadjust = cur_conf->beacon_interval * avp->bslot / ATH9K_HTC_MAX_BCN_VIF; |
379 | avp->tsfadjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); |
380 | |
381 | ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n" , |
382 | (unsigned long long)tsfadjust, avp->bslot); |
383 | } |
384 | |
385 | static void ath9k_htc_beacon_iter(void *data, u8 *mac, struct ieee80211_vif *vif) |
386 | { |
387 | bool *beacon_configured = data; |
388 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; |
389 | |
390 | if (vif->type == NL80211_IFTYPE_STATION && |
391 | avp->beacon_configured) |
392 | *beacon_configured = true; |
393 | } |
394 | |
395 | static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, |
396 | struct ieee80211_vif *vif) |
397 | { |
398 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
399 | struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; |
400 | struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; |
401 | bool beacon_configured; |
402 | |
403 | /* |
404 | * Changing the beacon interval when multiple AP interfaces |
405 | * are configured will affect beacon transmission of all |
406 | * of them. |
407 | */ |
408 | if ((priv->ah->opmode == NL80211_IFTYPE_AP) && |
409 | (priv->num_ap_vif > 1) && |
410 | (vif->type == NL80211_IFTYPE_AP) && |
411 | (cur_conf->beacon_interval != bss_conf->beacon_int)) { |
412 | ath_dbg(common, CONFIG, |
413 | "Changing beacon interval of multiple AP interfaces !\n" ); |
414 | return false; |
415 | } |
416 | |
417 | /* |
418 | * If the HW is operating in AP mode, any new station interfaces that |
419 | * are added cannot change the beacon parameters. |
420 | */ |
421 | if (priv->num_ap_vif && |
422 | (vif->type != NL80211_IFTYPE_AP)) { |
423 | ath_dbg(common, CONFIG, |
424 | "HW in AP mode, cannot set STA beacon parameters\n" ); |
425 | return false; |
426 | } |
427 | |
428 | /* |
429 | * The beacon parameters are configured only for the first |
430 | * station interface. |
431 | */ |
432 | if ((priv->ah->opmode == NL80211_IFTYPE_STATION) && |
433 | (priv->num_sta_vif > 1) && |
434 | (vif->type == NL80211_IFTYPE_STATION)) { |
435 | beacon_configured = false; |
436 | ieee80211_iterate_active_interfaces_atomic( |
437 | hw: priv->hw, iter_flags: IEEE80211_IFACE_ITER_RESUME_ALL, |
438 | iterator: ath9k_htc_beacon_iter, data: &beacon_configured); |
439 | |
440 | if (beacon_configured) { |
441 | ath_dbg(common, CONFIG, |
442 | "Beacon already configured for a station interface\n" ); |
443 | return false; |
444 | } |
445 | } |
446 | |
447 | return true; |
448 | } |
449 | |
450 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, |
451 | struct ieee80211_vif *vif) |
452 | { |
453 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
454 | struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; |
455 | struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; |
456 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; |
457 | |
458 | if (!ath9k_htc_check_beacon_config(priv, vif)) |
459 | return; |
460 | |
461 | cur_conf->beacon_interval = bss_conf->beacon_int; |
462 | if (cur_conf->beacon_interval == 0) |
463 | cur_conf->beacon_interval = 100; |
464 | |
465 | cur_conf->dtim_period = bss_conf->dtim_period; |
466 | cur_conf->bmiss_timeout = |
467 | ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; |
468 | |
469 | switch (vif->type) { |
470 | case NL80211_IFTYPE_STATION: |
471 | ath9k_htc_beacon_config_sta(priv, bss_conf: cur_conf); |
472 | avp->beacon_configured = true; |
473 | break; |
474 | case NL80211_IFTYPE_ADHOC: |
475 | ath9k_htc_beacon_config_adhoc(priv, conf: cur_conf); |
476 | break; |
477 | case NL80211_IFTYPE_MESH_POINT: |
478 | case NL80211_IFTYPE_AP: |
479 | ath9k_htc_beacon_config_ap(priv, conf: cur_conf); |
480 | break; |
481 | default: |
482 | ath_dbg(common, CONFIG, "Unsupported beaconing mode\n" ); |
483 | return; |
484 | } |
485 | } |
486 | |
487 | void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) |
488 | { |
489 | struct ath_common *common = ath9k_hw_common(ah: priv->ah); |
490 | struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; |
491 | |
492 | switch (priv->ah->opmode) { |
493 | case NL80211_IFTYPE_STATION: |
494 | ath9k_htc_beacon_config_sta(priv, bss_conf: cur_conf); |
495 | break; |
496 | case NL80211_IFTYPE_ADHOC: |
497 | ath9k_htc_beacon_config_adhoc(priv, conf: cur_conf); |
498 | break; |
499 | case NL80211_IFTYPE_MESH_POINT: |
500 | case NL80211_IFTYPE_AP: |
501 | ath9k_htc_beacon_config_ap(priv, conf: cur_conf); |
502 | break; |
503 | default: |
504 | ath_dbg(common, CONFIG, "Unsupported beaconing mode\n" ); |
505 | return; |
506 | } |
507 | } |
508 | |
509 | bool ath9k_htc_csa_is_finished(struct ath9k_htc_priv *priv) |
510 | { |
511 | struct ieee80211_vif *vif; |
512 | |
513 | vif = priv->csa_vif; |
514 | if (!vif || !vif->bss_conf.csa_active) |
515 | return false; |
516 | |
517 | if (!ieee80211_beacon_cntdwn_is_complete(vif, link_id: 0)) |
518 | return false; |
519 | |
520 | ieee80211_csa_finish(vif, link_id: 0); |
521 | |
522 | priv->csa_vif = NULL; |
523 | return true; |
524 | } |
525 | |