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