1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
@@ -384,6 +384,7 @@ static u_int32_t ath_get_real_maxtxpower
static void ath_poll_disable(struct net_device *dev);
static void ath_poll_enable(struct net_device *dev);
+static void ath_fetch_idle_time(struct ath_softc *sc);
/* calibrate every 30 secs in steady state but check every second at first. */
static int ath_calinterval = ATH_SHORT_CALINTERVAL;
@@ -2580,6 +2581,7 @@ ath_init(struct net_device *dev)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
+ ath_fetch_idle_time(sc);
sc->sc_curchan.channel = ic->ic_curchan->ic_freq;
sc->sc_curchan.channelFlags = ath_chan2flags(ic->ic_curchan);
if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
@@ -2913,6 +2915,48 @@ ath_hw_check_atim(struct ath_softc *sc,
return 0;
}
+#define AR5K_MIBC 0x0040
+#define AR5K_MIBC_FREEZE (1 << 1)
+#define AR5K_TXFC 0x80ec
+#define AR5K_RXFC 0x80f0
+#define AR5K_RXCLEAR 0x80f4
+#define AR5K_CYCLES 0x80f8
+static void
+ath_fetch_idle_time(struct ath_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t cc, rx;
+ u_int32_t time = 0;
+
+ if (sc->sc_ah->ah_macType < 5212)
+ return;
+
+ if (!ic->ic_curchan || (ic->ic_curchan == IEEE80211_CHAN_ANYC))
+ return;
+
+ OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
+ rx = OS_REG_READ(ah, AR5K_RXCLEAR);
+ cc = OS_REG_READ(ah, AR5K_CYCLES);
+
+ if (!cc)
+ return;
+
+ if (rx > cc)
+ return; /* should not happen */
+
+ if (sc->sc_last_chan)
+ sc->sc_last_chan->ic_idletime = 100 * (cc - rx) / cc;
+ sc->sc_last_chan = ic->ic_curchan;
+
+ OS_REG_WRITE(ah, AR5K_RXCLEAR, 0);
+ OS_REG_WRITE(ah, AR5K_CYCLES, 0);
+ OS_REG_WRITE(ah, AR5K_TXFC, 0);
+ OS_REG_WRITE(ah, AR5K_RXFC, 0);
+ OS_REG_WRITE(ah, AR5K_MIBC, 0);
+}
+#undef AR5K_RXCLEAR
+#undef AR5K_CYCLES
/*
* Reset the hardware w/o losing operational state. This is
@@ -2940,6 +2984,7 @@ ath_reset(struct net_device *dev)
* Convert to a HAL channel description with the flags
* constrained to reflect the current operating mode.
*/
+ ath_fetch_idle_time(sc);
c = ic->ic_curchan;
sc->sc_curchan.channel = c->ic_freq;
sc->sc_curchan.channelFlags = ath_chan2flags(c);
@@ -9022,6 +9067,7 @@ ath_chan_set(struct ath_softc *sc, struc
u_int8_t channel_change_required = 0;
struct timeval tv;
+
/*
* Convert to a HAL channel description with
* the flags constrained to reflect the current
@@ -9030,6 +9076,14 @@ ath_chan_set(struct ath_softc *sc, struc
memset(&hchan, 0, sizeof(HAL_CHANNEL));
hchan.channel = chan->ic_freq;
hchan.channelFlags = ath_chan2flags(chan);
+
+ /* don't do duplicate channel changes, but do
+ * store the available idle time */
+ ath_fetch_idle_time(sc);
+ if ((sc->sc_curchan.channel == hchan.channel) &&
+ (sc->sc_curchan.channelFlags == hchan.channelFlags))
+ return 0;
+
KASSERT(hchan.channel != 0,
("bogus channel %u/0x%x", hchan.channel, hchan.channelFlags));
do_gettimeofday(&tv);
--- a/ath/if_athvar.h
+++ b/ath/if_athvar.h
@@ -773,6 +773,7 @@ struct ath_softc {
struct ieee80211vap **sc_bslot; /* beacon xmit slots */
int sc_bnext; /* next slot for beacon xmit */
+ struct ieee80211_channel *sc_last_chan;
int sc_beacon_cal; /* use beacon timer for calibration */
u_int64_t sc_lastcal; /* last time the calibration was performed */
struct timer_list sc_cal_ch; /* calibration timer */
--- a/net80211/_ieee80211.h
+++ b/net80211/_ieee80211.h
@@ -148,6 +148,7 @@ struct ieee80211_channel {
int8_t ic_maxpower; /* maximum tx power in dBm */
int8_t ic_minpower; /* minimum tx power in dBm */
u_int8_t ic_scanflags;
+ u_int8_t ic_idletime; /* phy idle time in % */
};
#define IEEE80211_CHAN_MAX 255
--- a/net80211/ieee80211_scan_ap.c
+++ b/net80211/ieee80211_scan_ap.c
@@ -417,6 +417,19 @@ pc_cmp_rssi(struct ap_state *as, struct
/* This function must be invoked with locks acquired */
static int
+pc_cmp_idletime(struct ieee80211_channel *a,
+ struct ieee80211_channel *b)
+{
+ if (!a->ic_idletime || !b->ic_idletime)
+ return 0;
+
+ /* a is better than b (return < 0) when a has more idle time than b */
+ return b->ic_idletime - a->ic_idletime;
+}
+
+
+/* This function must be invoked with locks acquired */
+static int
pc_cmp_samechan(struct ieee80211com *ic, struct ieee80211_channel *a,
struct ieee80211_channel *b)
{
@@ -451,6 +464,7 @@ pc_cmp(const void *_a, const void *_b)
EVALUATE_CRITERION(radar, a, b);
EVALUATE_CRITERION(keepmode, params, a, b);
+ EVALUATE_CRITERION(idletime, a, b);
EVALUATE_CRITERION(sc, ic, a, b);
/* XXX: rssi useless? pick_channel evaluates it anyway */
EVALUATE_CRITERION(rssi, params->ss->ss_priv, a, b);
@@ -519,16 +533,9 @@ pick_channel(struct ieee80211_scan_state
#endif
best = NULL;
- best_rssi = 0xff; /* If signal is bigger than 0xff, we'd be melting. */
for (i = 0; i < ss_last; i++) {
c = &chans[i];
- benefit = best_rssi - as->as_maxrssi[c->chan->ic_ieee];
- sta_assoc = ic->ic_sta_assoc;
-
- /* Don't switch... */
- if (benefit <= 0)
- continue;
/* Verify channel is not marked for non-occupancy */
if (IEEE80211_IS_CHAN_RADAR(c->chan))
@@ -546,31 +553,8 @@ pick_channel(struct ieee80211_scan_state
break;
}
- if (sta_assoc != 0) {
- int sl = ic->ic_cn_total -
- ic->ic_chan_nodes[c->chan->ic_ieee]; /* count */
- if (ic->ic_sc_algorithm == IEEE80211_SC_LOOSE) {
- int sl_max = ic->ic_sc_sldg * benefit;
- sl = 1000 * sl / sta_assoc; /* permil */
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: chan %d, dB gained: %d, "
- "STAs lost: %d permil (max %d)\n",
- __func__, c->chan->ic_ieee,
- benefit, sl, sl_max);
- if (sl > sl_max)
- continue;
- } else if (((ic->ic_sc_algorithm ==
- IEEE80211_SC_TIGHT) ||
- (ic->ic_sc_algorithm ==
- IEEE80211_SC_STRICT)) &&
- (sl > 0)) {
- /* Break the loop as the subsequent chans
- * won't be better. */
- break;
- }
- }
best = c->chan;
- best_rssi = as->as_maxrssi[best->ic_ieee];
+ break;
}
if (best != NULL) {
@@ -599,6 +583,9 @@ ap_end(struct ieee80211_scan_state *ss,
("wrong opmode %u", vap->iv_opmode));
ic = vap->iv_ic;
+
+ /* record stats for the channel that was scanned last */
+ ic->ic_set_channel(ic);
bestchan = pick_channel(ss, vap, flags);
if (bestchan == NULL) {
if (ss->ss_last > 0) {
--- a/net80211/ieee80211_scan.c
+++ b/net80211/ieee80211_scan.c
@@ -1002,20 +1002,34 @@ ieee80211_scan_add_channels(struct ieee8
{
struct ieee80211_channel *c, *cg;
u_int modeflags;
+ int has_non_turbo = 0;
int i;
KASSERT(mode < ARRAY_SIZE(chanflags), ("Unexpected mode %u", mode));
modeflags = chanflags[mode];
for (i = 0; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
+ if (c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO))
+ continue;
+
+ has_non_turbo = 1;
+ break;
+ }
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
if (c == NULL || isclr(ic->ic_chan_active, c->ic_ieee))
continue;
if (c->ic_scanflags & IEEE80211_NOSCAN_SET)
continue;
- if (modeflags &&
- ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
- (modeflags & IEEE80211_CHAN_ALLTURBO)))
- continue;
+ if (modeflags) {
+ if ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+ (modeflags & IEEE80211_CHAN_ALLTURBO))
+ continue;
+ } else if (has_non_turbo) {
+ if ((ss->ss_vap->iv_opmode == IEEE80211_M_HOSTAP) &&
+ (c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)))
+ continue;
+ }
if (mode == IEEE80211_MODE_AUTO) {
/*
* XXX special-case 11b/g channels so we select
|