Commit | Line | Data |
---|---|---|
ef1b6cd9 SM |
1 | /* |
2 | * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h" | |
18 | ||
19 | /* | |
20 | * TX polling - checks if the TX engine is stuck somewhere | |
21 | * and issues a chip reset if so. | |
22 | */ | |
23 | void ath_tx_complete_poll_work(struct work_struct *work) | |
24 | { | |
25 | struct ath_softc *sc = container_of(work, struct ath_softc, | |
26 | tx_complete_work.work); | |
27 | struct ath_txq *txq; | |
28 | int i; | |
29 | bool needreset = false; | |
ef1b6cd9 | 30 | |
01d4ab96 FF |
31 | for (i = 0; i < IEEE80211_NUM_ACS; i++) { |
32 | txq = sc->tx.txq_map[i]; | |
33 | ||
34 | ath_txq_lock(sc, txq); | |
35 | if (txq->axq_depth) { | |
36 | if (txq->axq_tx_inprogress) { | |
37 | needreset = true; | |
38 | ath_txq_unlock(sc, txq); | |
39 | break; | |
40 | } else { | |
41 | txq->axq_tx_inprogress = true; | |
ef1b6cd9 | 42 | } |
ef1b6cd9 | 43 | } |
01d4ab96 FF |
44 | ath_txq_unlock_complete(sc, txq); |
45 | } | |
ef1b6cd9 SM |
46 | |
47 | if (needreset) { | |
48 | ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, | |
49 | "tx hung, resetting the chip\n"); | |
124b979b | 50 | ath9k_queue_reset(sc, RESET_TYPE_TX_HANG); |
af68abad | 51 | return; |
ef1b6cd9 SM |
52 | } |
53 | ||
54 | ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, | |
55 | msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); | |
56 | } | |
57 | ||
58 | /* | |
59 | * Checks if the BB/MAC is hung. | |
60 | */ | |
61 | void ath_hw_check(struct work_struct *work) | |
62 | { | |
63 | struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); | |
64 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | |
65 | unsigned long flags; | |
66 | int busy; | |
67 | u8 is_alive, nbeacon = 1; | |
124b979b | 68 | enum ath_reset_type type; |
ef1b6cd9 SM |
69 | |
70 | ath9k_ps_wakeup(sc); | |
71 | is_alive = ath9k_hw_check_alive(sc->sc_ah); | |
72 | ||
73 | if (is_alive && !AR_SREV_9300(sc->sc_ah)) | |
74 | goto out; | |
75 | else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { | |
76 | ath_dbg(common, RESET, | |
77 | "DCU stuck is detected. Schedule chip reset\n"); | |
124b979b | 78 | type = RESET_TYPE_MAC_HANG; |
ef1b6cd9 SM |
79 | goto sched_reset; |
80 | } | |
81 | ||
82 | spin_lock_irqsave(&common->cc_lock, flags); | |
83 | busy = ath_update_survey_stats(sc); | |
84 | spin_unlock_irqrestore(&common->cc_lock, flags); | |
85 | ||
86 | ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", | |
87 | busy, sc->hw_busy_count + 1); | |
88 | if (busy >= 99) { | |
89 | if (++sc->hw_busy_count >= 3) { | |
124b979b | 90 | type = RESET_TYPE_BB_HANG; |
ef1b6cd9 SM |
91 | goto sched_reset; |
92 | } | |
93 | } else if (busy >= 0) { | |
94 | sc->hw_busy_count = 0; | |
95 | nbeacon = 3; | |
96 | } | |
97 | ||
98 | ath_start_rx_poll(sc, nbeacon); | |
99 | goto out; | |
100 | ||
101 | sched_reset: | |
124b979b | 102 | ath9k_queue_reset(sc, type); |
ef1b6cd9 SM |
103 | out: |
104 | ath9k_ps_restore(sc); | |
105 | } | |
106 | ||
107 | /* | |
af68abad | 108 | * PLL-WAR for AR9485/AR9340 |
ef1b6cd9 | 109 | */ |
af68abad | 110 | static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) |
ef1b6cd9 SM |
111 | { |
112 | static int count; | |
113 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | |
114 | ||
115 | if (pll_sqsum >= 0x40000) { | |
116 | count++; | |
117 | if (count == 3) { | |
af68abad | 118 | ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); |
124b979b | 119 | ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG); |
ef1b6cd9 | 120 | count = 0; |
af68abad | 121 | return true; |
ef1b6cd9 | 122 | } |
af68abad | 123 | } else { |
ef1b6cd9 | 124 | count = 0; |
af68abad SM |
125 | } |
126 | ||
127 | return false; | |
ef1b6cd9 SM |
128 | } |
129 | ||
130 | void ath_hw_pll_work(struct work_struct *work) | |
131 | { | |
af68abad | 132 | u32 pll_sqsum; |
ef1b6cd9 SM |
133 | struct ath_softc *sc = container_of(work, struct ath_softc, |
134 | hw_pll_work.work); | |
64bc1239 MSS |
135 | /* |
136 | * ensure that the PLL WAR is executed only | |
137 | * after the STA is associated (or) if the | |
138 | * beaconing had started in interfaces that | |
139 | * uses beacons. | |
140 | */ | |
141 | if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) | |
142 | return; | |
ef1b6cd9 | 143 | |
af68abad SM |
144 | ath9k_ps_wakeup(sc); |
145 | pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); | |
146 | ath9k_ps_restore(sc); | |
147 | if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) | |
148 | return; | |
149 | ||
150 | ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, | |
151 | msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); | |
ef1b6cd9 SM |
152 | } |
153 | ||
154 | /* | |
155 | * RX Polling - monitors baseband hangs. | |
156 | */ | |
157 | void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) | |
158 | { | |
159 | if (!AR_SREV_9300(sc->sc_ah)) | |
160 | return; | |
161 | ||
781b14a3 | 162 | if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) |
ef1b6cd9 SM |
163 | return; |
164 | ||
165 | mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies | |
166 | (nbeacon * sc->cur_beacon_conf.beacon_interval)); | |
167 | } | |
168 | ||
169 | void ath_rx_poll(unsigned long data) | |
170 | { | |
171 | struct ath_softc *sc = (struct ath_softc *)data; | |
172 | ||
7fc03574 LR |
173 | if (!test_bit(SC_OP_INVALID, &sc->sc_flags)) |
174 | ieee80211_queue_work(sc->hw, &sc->hw_check_work); | |
ef1b6cd9 SM |
175 | } |
176 | ||
177 | /* | |
178 | * PA Pre-distortion. | |
179 | */ | |
180 | static void ath_paprd_activate(struct ath_softc *sc) | |
181 | { | |
182 | struct ath_hw *ah = sc->sc_ah; | |
914d0f4d | 183 | struct ath_common *common = ath9k_hw_common(ah); |
ef1b6cd9 SM |
184 | struct ath9k_hw_cal_data *caldata = ah->caldata; |
185 | int chain; | |
186 | ||
914d0f4d SM |
187 | if (!caldata || !caldata->paprd_done) { |
188 | ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n"); | |
ef1b6cd9 | 189 | return; |
914d0f4d | 190 | } |
ef1b6cd9 | 191 | |
ef1b6cd9 SM |
192 | ar9003_paprd_enable(ah, false); |
193 | for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { | |
194 | if (!(ah->txchainmask & BIT(chain))) | |
195 | continue; | |
196 | ||
197 | ar9003_paprd_populate_single_table(ah, caldata, chain); | |
198 | } | |
199 | ||
914d0f4d | 200 | ath_dbg(common, CALIBRATE, "Activating PAPRD\n"); |
ef1b6cd9 | 201 | ar9003_paprd_enable(ah, true); |
ef1b6cd9 SM |
202 | } |
203 | ||
204 | static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) | |
205 | { | |
206 | struct ieee80211_hw *hw = sc->hw; | |
207 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | |
208 | struct ath_hw *ah = sc->sc_ah; | |
209 | struct ath_common *common = ath9k_hw_common(ah); | |
210 | struct ath_tx_control txctl; | |
211 | int time_left; | |
212 | ||
213 | memset(&txctl, 0, sizeof(txctl)); | |
bea843c7 | 214 | txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; |
ef1b6cd9 SM |
215 | |
216 | memset(tx_info, 0, sizeof(*tx_info)); | |
675a0b04 | 217 | tx_info->band = hw->conf.chandef.chan->band; |
ef1b6cd9 SM |
218 | tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; |
219 | tx_info->control.rates[0].idx = 0; | |
220 | tx_info->control.rates[0].count = 1; | |
221 | tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; | |
222 | tx_info->control.rates[1].idx = -1; | |
223 | ||
224 | init_completion(&sc->paprd_complete); | |
225 | txctl.paprd = BIT(chain); | |
226 | ||
227 | if (ath_tx_start(hw, skb, &txctl) != 0) { | |
228 | ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); | |
229 | dev_kfree_skb_any(skb); | |
230 | return false; | |
231 | } | |
232 | ||
233 | time_left = wait_for_completion_timeout(&sc->paprd_complete, | |
234 | msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); | |
235 | ||
236 | if (!time_left) | |
237 | ath_dbg(common, CALIBRATE, | |
238 | "Timeout waiting for paprd training on TX chain %d\n", | |
239 | chain); | |
240 | ||
241 | return !!time_left; | |
242 | } | |
243 | ||
244 | void ath_paprd_calibrate(struct work_struct *work) | |
245 | { | |
246 | struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); | |
247 | struct ieee80211_hw *hw = sc->hw; | |
248 | struct ath_hw *ah = sc->sc_ah; | |
249 | struct ieee80211_hdr *hdr; | |
250 | struct sk_buff *skb = NULL; | |
251 | struct ath9k_hw_cal_data *caldata = ah->caldata; | |
252 | struct ath_common *common = ath9k_hw_common(ah); | |
253 | int ftype; | |
254 | int chain_ok = 0; | |
255 | int chain; | |
256 | int len = 1800; | |
381c726c | 257 | int ret; |
ef1b6cd9 | 258 | |
914d0f4d SM |
259 | if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) { |
260 | ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n"); | |
ef1b6cd9 | 261 | return; |
914d0f4d | 262 | } |
ef1b6cd9 SM |
263 | |
264 | ath9k_ps_wakeup(sc); | |
265 | ||
266 | if (ar9003_paprd_init_table(ah) < 0) | |
267 | goto fail_paprd; | |
268 | ||
269 | skb = alloc_skb(len, GFP_KERNEL); | |
270 | if (!skb) | |
271 | goto fail_paprd; | |
272 | ||
273 | skb_put(skb, len); | |
274 | memset(skb->data, 0, len); | |
275 | hdr = (struct ieee80211_hdr *)skb->data; | |
276 | ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; | |
277 | hdr->frame_control = cpu_to_le16(ftype); | |
278 | hdr->duration_id = cpu_to_le16(10); | |
279 | memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); | |
280 | memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); | |
281 | memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); | |
282 | ||
283 | for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { | |
284 | if (!(ah->txchainmask & BIT(chain))) | |
285 | continue; | |
286 | ||
287 | chain_ok = 0; | |
ef1b6cd9 SM |
288 | ar9003_paprd_setup_gain_table(ah, chain); |
289 | ||
290 | ath_dbg(common, CALIBRATE, | |
291 | "Sending PAPRD training frame on chain %d\n", chain); | |
292 | if (!ath_paprd_send_frame(sc, skb, chain)) | |
293 | goto fail_paprd; | |
294 | ||
295 | if (!ar9003_paprd_is_done(ah)) { | |
296 | ath_dbg(common, CALIBRATE, | |
297 | "PAPRD not yet done on chain %d\n", chain); | |
298 | break; | |
299 | } | |
300 | ||
381c726c FF |
301 | ret = ar9003_paprd_create_curve(ah, caldata, chain); |
302 | if (ret == -EINPROGRESS) { | |
303 | ath_dbg(common, CALIBRATE, | |
304 | "PAPRD curve on chain %d needs to be re-trained\n", | |
305 | chain); | |
306 | break; | |
307 | } else if (ret) { | |
ef1b6cd9 SM |
308 | ath_dbg(common, CALIBRATE, |
309 | "PAPRD create curve failed on chain %d\n", | |
af68abad | 310 | chain); |
ef1b6cd9 SM |
311 | break; |
312 | } | |
313 | ||
314 | chain_ok = 1; | |
315 | } | |
316 | kfree_skb(skb); | |
317 | ||
318 | if (chain_ok) { | |
319 | caldata->paprd_done = true; | |
320 | ath_paprd_activate(sc); | |
321 | } | |
322 | ||
323 | fail_paprd: | |
324 | ath9k_ps_restore(sc); | |
325 | } | |
326 | ||
327 | /* | |
328 | * ANI performs periodic noise floor calibration | |
329 | * that is used to adjust and optimize the chip performance. This | |
330 | * takes environmental changes (location, temperature) into account. | |
331 | * When the task is complete, it reschedules itself depending on the | |
332 | * appropriate interval that was calculated. | |
333 | */ | |
334 | void ath_ani_calibrate(unsigned long data) | |
335 | { | |
336 | struct ath_softc *sc = (struct ath_softc *)data; | |
337 | struct ath_hw *ah = sc->sc_ah; | |
338 | struct ath_common *common = ath9k_hw_common(ah); | |
339 | bool longcal = false; | |
340 | bool shortcal = false; | |
341 | bool aniflag = false; | |
342 | unsigned int timestamp = jiffies_to_msecs(jiffies); | |
343 | u32 cal_interval, short_cal_interval, long_cal_interval; | |
344 | unsigned long flags; | |
345 | ||
346 | if (ah->caldata && ah->caldata->nfcal_interference) | |
347 | long_cal_interval = ATH_LONG_CALINTERVAL_INT; | |
348 | else | |
349 | long_cal_interval = ATH_LONG_CALINTERVAL; | |
350 | ||
351 | short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? | |
352 | ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; | |
353 | ||
354 | /* Only calibrate if awake */ | |
424749c7 RM |
355 | if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { |
356 | if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) { | |
357 | spin_lock_irqsave(&sc->sc_pm_lock, flags); | |
358 | sc->ps_flags |= PS_WAIT_FOR_ANI; | |
359 | spin_unlock_irqrestore(&sc->sc_pm_lock, flags); | |
360 | } | |
ef1b6cd9 | 361 | goto set_timer; |
424749c7 RM |
362 | } |
363 | ah->ani_skip_count = 0; | |
364 | spin_lock_irqsave(&sc->sc_pm_lock, flags); | |
365 | sc->ps_flags &= ~PS_WAIT_FOR_ANI; | |
366 | spin_unlock_irqrestore(&sc->sc_pm_lock, flags); | |
ef1b6cd9 SM |
367 | |
368 | ath9k_ps_wakeup(sc); | |
369 | ||
370 | /* Long calibration runs independently of short calibration. */ | |
371 | if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { | |
372 | longcal = true; | |
373 | common->ani.longcal_timer = timestamp; | |
374 | } | |
375 | ||
376 | /* Short calibration applies only while caldone is false */ | |
377 | if (!common->ani.caldone) { | |
378 | if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { | |
379 | shortcal = true; | |
380 | common->ani.shortcal_timer = timestamp; | |
381 | common->ani.resetcal_timer = timestamp; | |
382 | } | |
383 | } else { | |
384 | if ((timestamp - common->ani.resetcal_timer) >= | |
385 | ATH_RESTART_CALINTERVAL) { | |
386 | common->ani.caldone = ath9k_hw_reset_calvalid(ah); | |
387 | if (common->ani.caldone) | |
388 | common->ani.resetcal_timer = timestamp; | |
389 | } | |
390 | } | |
391 | ||
392 | /* Verify whether we must check ANI */ | |
e323300d | 393 | if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) { |
ef1b6cd9 SM |
394 | aniflag = true; |
395 | common->ani.checkani_timer = timestamp; | |
396 | } | |
397 | ||
398 | /* Call ANI routine if necessary */ | |
399 | if (aniflag) { | |
400 | spin_lock_irqsave(&common->cc_lock, flags); | |
401 | ath9k_hw_ani_monitor(ah, ah->curchan); | |
402 | ath_update_survey_stats(sc); | |
403 | spin_unlock_irqrestore(&common->cc_lock, flags); | |
404 | } | |
405 | ||
406 | /* Perform calibration if necessary */ | |
407 | if (longcal || shortcal) { | |
408 | common->ani.caldone = | |
409 | ath9k_hw_calibrate(ah, ah->curchan, | |
410 | ah->rxchainmask, longcal); | |
411 | } | |
412 | ||
413 | ath_dbg(common, ANI, | |
414 | "Calibration @%lu finished: %s %s %s, caldone: %s\n", | |
415 | jiffies, | |
416 | longcal ? "long" : "", shortcal ? "short" : "", | |
417 | aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); | |
418 | ||
419 | ath9k_ps_restore(sc); | |
420 | ||
421 | set_timer: | |
422 | /* | |
423 | * Set timer interval based on previous results. | |
424 | * The interval must be the shortest necessary to satisfy ANI, | |
425 | * short calibration and long calibration. | |
426 | */ | |
ef1b6cd9 | 427 | cal_interval = ATH_LONG_CALINTERVAL; |
e323300d | 428 | cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval); |
ef1b6cd9 SM |
429 | if (!common->ani.caldone) |
430 | cal_interval = min(cal_interval, (u32)short_cal_interval); | |
431 | ||
432 | mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); | |
19f78422 | 433 | |
0f21ee8d | 434 | if (ar9003_is_paprd_enabled(ah) && ah->caldata) { |
19f78422 | 435 | if (!ah->caldata->paprd_done) { |
ef1b6cd9 | 436 | ieee80211_queue_work(sc->hw, &sc->paprd_work); |
19f78422 SM |
437 | } else if (!ah->paprd_table_write_done) { |
438 | ath9k_ps_wakeup(sc); | |
ef1b6cd9 | 439 | ath_paprd_activate(sc); |
19f78422 SM |
440 | ath9k_ps_restore(sc); |
441 | } | |
ef1b6cd9 SM |
442 | } |
443 | } | |
444 | ||
da0d45f7 | 445 | void ath_start_ani(struct ath_softc *sc) |
ef1b6cd9 | 446 | { |
da0d45f7 SM |
447 | struct ath_hw *ah = sc->sc_ah; |
448 | struct ath_common *common = ath9k_hw_common(ah); | |
ef1b6cd9 | 449 | unsigned long timestamp = jiffies_to_msecs(jiffies); |
ef1b6cd9 | 450 | |
da0d45f7 SM |
451 | if (common->disable_ani || |
452 | !test_bit(SC_OP_ANI_RUN, &sc->sc_flags) || | |
453 | (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) | |
ef1b6cd9 SM |
454 | return; |
455 | ||
456 | common->ani.longcal_timer = timestamp; | |
457 | common->ani.shortcal_timer = timestamp; | |
458 | common->ani.checkani_timer = timestamp; | |
459 | ||
da0d45f7 | 460 | ath_dbg(common, ANI, "Starting ANI\n"); |
ef1b6cd9 SM |
461 | mod_timer(&common->ani.timer, |
462 | jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); | |
463 | } | |
464 | ||
da0d45f7 SM |
465 | void ath_stop_ani(struct ath_softc *sc) |
466 | { | |
467 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | |
468 | ||
469 | ath_dbg(common, ANI, "Stopping ANI\n"); | |
470 | del_timer_sync(&common->ani.timer); | |
471 | } | |
472 | ||
473 | void ath_check_ani(struct ath_softc *sc) | |
474 | { | |
475 | struct ath_hw *ah = sc->sc_ah; | |
476 | struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; | |
477 | ||
478 | /* | |
479 | * Check for the various conditions in which ANI has to | |
480 | * be stopped. | |
481 | */ | |
482 | if (ah->opmode == NL80211_IFTYPE_ADHOC) { | |
483 | if (!cur_conf->enable_beacon) | |
484 | goto stop_ani; | |
485 | } else if (ah->opmode == NL80211_IFTYPE_AP) { | |
486 | if (!cur_conf->enable_beacon) { | |
487 | /* | |
488 | * Disable ANI only when there are no | |
489 | * associated stations. | |
490 | */ | |
491 | if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) | |
492 | goto stop_ani; | |
493 | } | |
494 | } else if (ah->opmode == NL80211_IFTYPE_STATION) { | |
495 | if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) | |
496 | goto stop_ani; | |
497 | } | |
498 | ||
499 | if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags)) { | |
500 | set_bit(SC_OP_ANI_RUN, &sc->sc_flags); | |
501 | ath_start_ani(sc); | |
502 | } | |
503 | ||
504 | return; | |
505 | ||
506 | stop_ani: | |
507 | clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); | |
508 | ath_stop_ani(sc); | |
509 | } | |
510 | ||
ef1b6cd9 SM |
511 | void ath_update_survey_nf(struct ath_softc *sc, int channel) |
512 | { | |
513 | struct ath_hw *ah = sc->sc_ah; | |
514 | struct ath9k_channel *chan = &ah->channels[channel]; | |
515 | struct survey_info *survey = &sc->survey[channel]; | |
516 | ||
517 | if (chan->noisefloor) { | |
518 | survey->filled |= SURVEY_INFO_NOISE_DBM; | |
519 | survey->noise = ath9k_hw_getchan_noise(ah, chan); | |
520 | } | |
521 | } | |
522 | ||
523 | /* | |
524 | * Updates the survey statistics and returns the busy time since last | |
525 | * update in %, if the measurement duration was long enough for the | |
526 | * result to be useful, -1 otherwise. | |
527 | */ | |
528 | int ath_update_survey_stats(struct ath_softc *sc) | |
529 | { | |
530 | struct ath_hw *ah = sc->sc_ah; | |
531 | struct ath_common *common = ath9k_hw_common(ah); | |
532 | int pos = ah->curchan - &ah->channels[0]; | |
533 | struct survey_info *survey = &sc->survey[pos]; | |
534 | struct ath_cycle_counters *cc = &common->cc_survey; | |
535 | unsigned int div = common->clockrate * 1000; | |
536 | int ret = 0; | |
537 | ||
538 | if (!ah->curchan) | |
539 | return -1; | |
540 | ||
541 | if (ah->power_mode == ATH9K_PM_AWAKE) | |
542 | ath_hw_cycle_counters_update(common); | |
543 | ||
544 | if (cc->cycles > 0) { | |
545 | survey->filled |= SURVEY_INFO_CHANNEL_TIME | | |
546 | SURVEY_INFO_CHANNEL_TIME_BUSY | | |
547 | SURVEY_INFO_CHANNEL_TIME_RX | | |
548 | SURVEY_INFO_CHANNEL_TIME_TX; | |
549 | survey->channel_time += cc->cycles / div; | |
550 | survey->channel_time_busy += cc->rx_busy / div; | |
551 | survey->channel_time_rx += cc->rx_frame / div; | |
552 | survey->channel_time_tx += cc->tx_frame / div; | |
553 | } | |
554 | ||
555 | if (cc->cycles < div) | |
556 | return -1; | |
557 | ||
558 | if (cc->cycles > 0) | |
559 | ret = cc->rx_busy * 100 / cc->cycles; | |
560 | ||
561 | memset(cc, 0, sizeof(*cc)); | |
562 | ||
563 | ath_update_survey_nf(sc, pos); | |
564 | ||
565 | return ret; | |
566 | } |