Commit | Line | Data |
---|---|---|
fbbcd146 FF |
1 | /* |
2 | * Copyright (c) 2014 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 | /* Set/change channels. If the channel is really being changed, it's done | |
20 | * by reseting the chip. To accomplish this we must first cleanup any pending | |
21 | * DMA, then restart stuff. | |
22 | */ | |
23 | static int ath_set_channel(struct ath_softc *sc) | |
24 | { | |
25 | struct ath_hw *ah = sc->sc_ah; | |
26 | struct ath_common *common = ath9k_hw_common(ah); | |
27 | struct ieee80211_hw *hw = sc->hw; | |
28 | struct ath9k_channel *hchan; | |
29 | struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef; | |
30 | struct ieee80211_channel *chan = chandef->chan; | |
31 | int pos = chan->hw_value; | |
32 | int old_pos = -1; | |
33 | int r; | |
34 | ||
35 | if (test_bit(ATH_OP_INVALID, &common->op_flags)) | |
36 | return -EIO; | |
37 | ||
38 | if (ah->curchan) | |
39 | old_pos = ah->curchan - &ah->channels[0]; | |
40 | ||
41 | ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", | |
42 | chan->center_freq, chandef->width); | |
43 | ||
44 | /* update survey stats for the old channel before switching */ | |
45 | spin_lock_bh(&common->cc_lock); | |
46 | ath_update_survey_stats(sc); | |
47 | spin_unlock_bh(&common->cc_lock); | |
48 | ||
49 | ath9k_cmn_get_channel(hw, ah, chandef); | |
50 | ||
51 | /* If the operating channel changes, change the survey in-use flags | |
52 | * along with it. | |
53 | * Reset the survey data for the new channel, unless we're switching | |
54 | * back to the operating channel from an off-channel operation. | |
55 | */ | |
56 | if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) { | |
57 | if (sc->cur_survey) | |
58 | sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; | |
59 | ||
60 | sc->cur_survey = &sc->survey[pos]; | |
61 | ||
62 | memset(sc->cur_survey, 0, sizeof(struct survey_info)); | |
63 | sc->cur_survey->filled |= SURVEY_INFO_IN_USE; | |
64 | } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { | |
65 | memset(&sc->survey[pos], 0, sizeof(struct survey_info)); | |
66 | } | |
67 | ||
68 | hchan = &sc->sc_ah->channels[pos]; | |
69 | r = ath_reset_internal(sc, hchan); | |
70 | if (r) | |
71 | return r; | |
72 | ||
73 | /* The most recent snapshot of channel->noisefloor for the old | |
74 | * channel is only available after the hardware reset. Copy it to | |
75 | * the survey stats now. | |
76 | */ | |
77 | if (old_pos >= 0) | |
78 | ath_update_survey_nf(sc, old_pos); | |
79 | ||
80 | /* Enable radar pulse detection if on a DFS channel. Spectral | |
81 | * scanning and radar detection can not be used concurrently. | |
82 | */ | |
83 | if (hw->conf.radar_enabled) { | |
84 | u32 rxfilter; | |
85 | ||
86 | /* set HW specific DFS configuration */ | |
87 | ath9k_hw_set_radar_params(ah); | |
88 | rxfilter = ath9k_hw_getrxfilter(ah); | |
89 | rxfilter |= ATH9K_RX_FILTER_PHYRADAR | | |
90 | ATH9K_RX_FILTER_PHYERR; | |
91 | ath9k_hw_setrxfilter(ah, rxfilter); | |
92 | ath_dbg(common, DFS, "DFS enabled at freq %d\n", | |
93 | chan->center_freq); | |
94 | } else { | |
95 | /* perform spectral scan if requested. */ | |
96 | if (test_bit(ATH_OP_SCANNING, &common->op_flags) && | |
97 | sc->spectral_mode == SPECTRAL_CHANSCAN) | |
98 | ath9k_spectral_scan_trigger(hw); | |
99 | } | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | void ath_chanctx_init(struct ath_softc *sc) | |
105 | { | |
106 | struct ath_chanctx *ctx; | |
107 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | |
108 | struct ieee80211_supported_band *sband; | |
109 | struct ieee80211_channel *chan; | |
110 | int i; | |
111 | ||
112 | sband = &common->sbands[IEEE80211_BAND_2GHZ]; | |
113 | if (!sband->n_channels) | |
114 | sband = &common->sbands[IEEE80211_BAND_5GHZ]; | |
115 | ||
116 | chan = &sband->channels[0]; | |
117 | for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { | |
118 | ctx = &sc->chanctx[i]; | |
119 | cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); | |
120 | INIT_LIST_HEAD(&ctx->vifs); | |
bc7e1be7 | 121 | ctx->txpower = ATH_TXPOWER_MAX; |
fbbcd146 FF |
122 | } |
123 | sc->cur_chan = &sc->chanctx[0]; | |
124 | } | |
125 | ||
126 | int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, | |
127 | struct cfg80211_chan_def *chandef) | |
128 | { | |
129 | memcpy(&ctx->chandef, chandef, sizeof(ctx->chandef)); | |
130 | if (ctx != sc->cur_chan) | |
131 | return 0; | |
132 | ||
133 | return ath_set_channel(sc); | |
134 | } |