| 1 | --- a/net80211/ieee80211_scan.c |
| 2 | +++ b/net80211/ieee80211_scan.c |
| 3 | @@ -97,6 +97,123 @@ struct scan_state { |
| 4 | static void scan_restart_pwrsav(unsigned long); |
| 5 | static void scan_next(unsigned long); |
| 6 | |
| 7 | +spinlock_t channel_lock = SPIN_LOCK_UNLOCKED; |
| 8 | +static LIST_HEAD(channels_inuse); |
| 9 | + |
| 10 | +struct channel_inuse { |
| 11 | + struct list_head list; |
| 12 | + struct ieee80211com *ic; |
| 13 | + u16 freq; |
| 14 | + u8 bw; |
| 15 | +}; |
| 16 | + |
| 17 | +static inline u32 |
| 18 | +get_signal(u8 bw, u8 distance) |
| 19 | +{ |
| 20 | + u32 v; |
| 21 | + |
| 22 | + /* signal = 1 - (distance / bw)^2 [scale: 100] */ |
| 23 | + v = 100 * distance / bw; |
| 24 | + v = (100 - ((v * v) / 100)); |
| 25 | + return v; |
| 26 | +} |
| 27 | + |
| 28 | +static u32 |
| 29 | +get_overlap(u16 f1, u16 f2, u8 b1, u8 b2) |
| 30 | +{ |
| 31 | + u32 v; |
| 32 | + u16 d, c; |
| 33 | + |
| 34 | + /* add offsets for sidechannel interference */ |
| 35 | + b1 += (b1 / 5); |
| 36 | + b2 += (b2 / 5); |
| 37 | + |
| 38 | + /* use only one direction */ |
| 39 | + b1 /= 2; |
| 40 | + b2 /= 2; |
| 41 | + |
| 42 | + if (f1 + b1 < f2 - b2) |
| 43 | + return 0; |
| 44 | + |
| 45 | + d = f2 - f1; |
| 46 | + c = d * b1 / (b1 + b2); |
| 47 | + v = get_signal(b1, c); |
| 48 | + |
| 49 | + return v * v / 100; |
| 50 | +} |
| 51 | + |
| 52 | +static u8 |
| 53 | +get_channel_bw(struct ieee80211_channel *c) |
| 54 | +{ |
| 55 | + switch(c->ic_flags & ( |
| 56 | + IEEE80211_CHAN_HALF | |
| 57 | + IEEE80211_CHAN_QUARTER | |
| 58 | + IEEE80211_CHAN_TURBO | |
| 59 | + IEEE80211_CHAN_STURBO)) { |
| 60 | + case IEEE80211_CHAN_QUARTER: |
| 61 | + return 5; |
| 62 | + case IEEE80211_CHAN_HALF: |
| 63 | + return 10; |
| 64 | + case IEEE80211_CHAN_TURBO: |
| 65 | + case IEEE80211_CHAN_STURBO: |
| 66 | + return 40; |
| 67 | + default: |
| 68 | + return 20; |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +/* must be called with channel_lock held */ |
| 73 | +u32 |
| 74 | +ieee80211_scan_get_bias(struct ieee80211_channel *c) |
| 75 | +{ |
| 76 | + struct channel_inuse *ch; |
| 77 | + u8 bw = get_channel_bw(c); |
| 78 | + u32 bias = 0; |
| 79 | + |
| 80 | + list_for_each_entry(ch, &channels_inuse, list) { |
| 81 | + if (ch->freq == c->ic_freq) { |
| 82 | + bias += 50; |
| 83 | + continue; |
| 84 | + } |
| 85 | + if (c->ic_freq < ch->freq) |
| 86 | + bias += get_overlap(c->ic_freq, ch->freq, bw, ch->bw); |
| 87 | + else |
| 88 | + bias += get_overlap(ch->freq, c->ic_freq, ch->bw, bw); |
| 89 | + } |
| 90 | + return bias; |
| 91 | +} |
| 92 | +EXPORT_SYMBOL(ieee80211_scan_get_bias); |
| 93 | + |
| 94 | +/* must be called with channel_lock held */ |
| 95 | +void |
| 96 | +ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c) |
| 97 | +{ |
| 98 | + unsigned long flags; |
| 99 | + struct channel_inuse *ch; |
| 100 | + |
| 101 | + list_for_each_entry(ch, &channels_inuse, list) { |
| 102 | + if (ch->ic == ic) |
| 103 | + goto found; |
| 104 | + } |
| 105 | + ch = NULL; |
| 106 | +found: |
| 107 | + if (c && (c != IEEE80211_CHAN_ANYC)) { |
| 108 | + if (!ch) { |
| 109 | + ch = kmalloc(sizeof(struct channel_inuse), GFP_ATOMIC); |
| 110 | + ch->ic = ic; |
| 111 | + INIT_LIST_HEAD(&ch->list); |
| 112 | + list_add(&ch->list, &channels_inuse); |
| 113 | + } |
| 114 | + ch->freq = c->ic_freq; |
| 115 | + ch->bw = get_channel_bw(c); |
| 116 | + } else if (ch) { |
| 117 | + list_del(&ch->list); |
| 118 | + kfree(ch); |
| 119 | + } |
| 120 | +} |
| 121 | +EXPORT_SYMBOL(ieee80211_scan_set_bss_channel); |
| 122 | + |
| 123 | + |
| 124 | void |
| 125 | ieee80211_scan_attach(struct ieee80211com *ic) |
| 126 | { |
| 127 | @@ -1169,7 +1286,7 @@ ieee80211_scan_dfs_action(struct ieee802 |
| 128 | IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT; |
| 129 | ic->ic_flags |= IEEE80211_F_CHANSWITCH; |
| 130 | } else { |
| 131 | - |
| 132 | + unsigned long flags; |
| 133 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| 134 | "%s: directly switching to channel " |
| 135 | "%3d (%4d MHz)\n", __func__, |
| 136 | @@ -1180,6 +1297,9 @@ ieee80211_scan_dfs_action(struct ieee802 |
| 137 | * change the channel here. */ |
| 138 | change_channel(ic, new_channel); |
| 139 | ic->ic_bsschan = new_channel; |
| 140 | + spin_lock_irqsave(&channel_lock, flags); |
| 141 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 142 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 143 | if (vap->iv_bss) |
| 144 | vap->iv_bss->ni_chan = new_channel; |
| 145 | } |
| 146 | --- a/net80211/ieee80211_scan.h |
| 147 | +++ b/net80211/ieee80211_scan.h |
| 148 | @@ -35,6 +35,7 @@ |
| 149 | |
| 150 | #define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX |
| 151 | |
| 152 | +extern spinlock_t channel_lock; |
| 153 | struct ieee80211_scanner; |
| 154 | struct ieee80211_scan_entry; |
| 155 | |
| 156 | @@ -116,6 +117,8 @@ void ieee80211_scan_flush(struct ieee802 |
| 157 | struct ieee80211_scan_entry; |
| 158 | typedef int ieee80211_scan_iter_func(void *, const struct ieee80211_scan_entry *); |
| 159 | int ieee80211_scan_iterate(struct ieee80211com *, ieee80211_scan_iter_func *, void *); |
| 160 | +u32 ieee80211_scan_get_bias(struct ieee80211_channel *c); |
| 161 | +void ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c); |
| 162 | |
| 163 | /* |
| 164 | * Parameters supplied when adding/updating an entry in a |
| 165 | --- a/net80211/ieee80211.c |
| 166 | +++ b/net80211/ieee80211.c |
| 167 | @@ -373,8 +373,16 @@ void |
| 168 | ieee80211_ifdetach(struct ieee80211com *ic) |
| 169 | { |
| 170 | struct ieee80211vap *vap; |
| 171 | + unsigned long flags; |
| 172 | int count; |
| 173 | |
| 174 | + /* mark the channel as no longer in use */ |
| 175 | + ic->ic_bsschan = IEEE80211_CHAN_ANYC; |
| 176 | + spin_lock_irqsave(&channel_lock, flags); |
| 177 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 178 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 179 | + |
| 180 | + |
| 181 | /* bring down all vaps */ |
| 182 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 183 | ieee80211_stop(vap->iv_dev); |
| 184 | --- a/net80211/ieee80211_input.c |
| 185 | +++ b/net80211/ieee80211_input.c |
| 186 | @@ -2775,6 +2775,7 @@ static void |
| 187 | ieee80211_doth_switch_channel(struct ieee80211vap *vap) |
| 188 | { |
| 189 | struct ieee80211com *ic = vap->iv_ic; |
| 190 | + unsigned long flags; |
| 191 | |
| 192 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| 193 | "%s: Channel switch to %3d (%4d MHz) NOW!\n", |
| 194 | @@ -2797,6 +2798,9 @@ ieee80211_doth_switch_channel(struct iee |
| 195 | |
| 196 | ic->ic_curchan = ic->ic_bsschan = vap->iv_csa_chan; |
| 197 | ic->ic_set_channel(ic); |
| 198 | + spin_lock_irqsave(&channel_lock, flags); |
| 199 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 200 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 201 | } |
| 202 | |
| 203 | static void |
| 204 | --- a/net80211/ieee80211_node.c |
| 205 | +++ b/net80211/ieee80211_node.c |
| 206 | @@ -308,6 +308,7 @@ ieee80211_create_ibss(struct ieee80211va |
| 207 | { |
| 208 | struct ieee80211com *ic = vap->iv_ic; |
| 209 | struct ieee80211_node *ni; |
| 210 | + unsigned long flags; |
| 211 | |
| 212 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| 213 | "%s: creating ibss on channel %u\n", __func__, |
| 214 | @@ -386,6 +387,9 @@ ieee80211_create_ibss(struct ieee80211va |
| 215 | ic->ic_bsschan = chan; |
| 216 | ieee80211_node_set_chan(ic, ni); |
| 217 | ic->ic_curmode = ieee80211_chan2mode(chan); |
| 218 | + spin_lock_irqsave(&channel_lock, flags); |
| 219 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 220 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 221 | |
| 222 | /* Update country ie information */ |
| 223 | ieee80211_build_countryie(ic); |
| 224 | @@ -622,6 +626,7 @@ ieee80211_sta_join1(struct ieee80211_nod |
| 225 | struct ieee80211vap *vap = selbs->ni_vap; |
| 226 | struct ieee80211com *ic = selbs->ni_ic; |
| 227 | struct ieee80211_node *obss; |
| 228 | + unsigned long flags; |
| 229 | int canreassoc; |
| 230 | |
| 231 | if (vap->iv_opmode == IEEE80211_M_IBSS) { |
| 232 | @@ -650,6 +655,9 @@ ieee80211_sta_join1(struct ieee80211_nod |
| 233 | ic->ic_curchan = ic->ic_bsschan; |
| 234 | ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); |
| 235 | ic->ic_set_channel(ic); |
| 236 | + spin_lock_irqsave(&channel_lock, flags); |
| 237 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 238 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 239 | /* |
| 240 | * Set the erp state (mostly the slot time) to deal with |
| 241 | * the auto-select case; this should be redundant if the |
| 242 | --- a/net80211/ieee80211_proto.c |
| 243 | +++ b/net80211/ieee80211_proto.c |
| 244 | @@ -1231,6 +1231,7 @@ ieee80211_dturbo_switch(struct ieee80211 |
| 245 | struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
| 246 | #endif |
| 247 | struct ieee80211_channel *chan; |
| 248 | + unsigned long flags; |
| 249 | |
| 250 | chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); |
| 251 | if (chan == NULL) { /* XXX should not happen */ |
| 252 | @@ -1249,6 +1250,9 @@ ieee80211_dturbo_switch(struct ieee80211 |
| 253 | ic->ic_bsschan = chan; |
| 254 | ic->ic_curchan = chan; |
| 255 | ic->ic_set_channel(ic); |
| 256 | + spin_lock_irqsave(&channel_lock, flags); |
| 257 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 258 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 259 | /* NB: do not need to reset ERP state because in sta mode */ |
| 260 | } |
| 261 | EXPORT_SYMBOL(ieee80211_dturbo_switch); |
| 262 | --- a/net80211/ieee80211_wireless.c |
| 263 | +++ b/net80211/ieee80211_wireless.c |
| 264 | @@ -4076,8 +4076,13 @@ ieee80211_ioctl_setchanlist(struct net_d |
| 265 | if (nchan == 0) /* no valid channels, disallow */ |
| 266 | return -EINVAL; |
| 267 | if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ |
| 268 | - isclr(chanlist, ic->ic_bsschan->ic_ieee)) |
| 269 | + isclr(chanlist, ic->ic_bsschan->ic_ieee)) { |
| 270 | + unsigned long flags; |
| 271 | ic->ic_bsschan = IEEE80211_CHAN_ANYC; /* invalidate */ |
| 272 | + spin_lock_irqsave(&channel_lock, flags); |
| 273 | + ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan); |
| 274 | + spin_unlock_irqrestore(&channel_lock, flags); |
| 275 | + } |
| 276 | |
| 277 | memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); |
| 278 | /* update Supported Channels information element */ |
| 279 | --- a/net80211/ieee80211_scan_ap.c |
| 280 | +++ b/net80211/ieee80211_scan_ap.c |
| 281 | @@ -208,9 +208,15 @@ ap_start(struct ieee80211_scan_state *ss |
| 282 | struct ieee80211com *ic = NULL; |
| 283 | int i; |
| 284 | unsigned int mode = 0; |
| 285 | + unsigned long sflags; |
| 286 | |
| 287 | SCAN_AP_LOCK_IRQ(as); |
| 288 | ic = vap->iv_ic; |
| 289 | + |
| 290 | + spin_lock_irqsave(&channel_lock, sflags); |
| 291 | + ieee80211_scan_set_bss_channel(ic, NULL); |
| 292 | + spin_unlock_irqrestore(&channel_lock, sflags); |
| 293 | + |
| 294 | /* Determine mode flags to match, or leave zero for auto mode */ |
| 295 | ss->ss_last = 0; |
| 296 | ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode); |
| 297 | @@ -423,8 +429,10 @@ pc_cmp_idletime(struct ieee80211_channel |
| 298 | if (!a->ic_idletime || !b->ic_idletime) |
| 299 | return 0; |
| 300 | |
| 301 | - /* a is better than b (return < 0) when a has more idle time than b */ |
| 302 | - return b->ic_idletime - a->ic_idletime; |
| 303 | + /* a is better than b (return < 0) when a has more idle and less bias time than b */ |
| 304 | + return |
| 305 | + ((100 - (u32) a->ic_idletime) + ieee80211_scan_get_bias(a)) - |
| 306 | + ((100 - (u32) b->ic_idletime) + ieee80211_scan_get_bias(b)); |
| 307 | } |
| 308 | |
| 309 | |
| 310 | @@ -575,6 +583,7 @@ ap_end(struct ieee80211_scan_state *ss, |
| 311 | struct ap_state *as = ss->ss_priv; |
| 312 | struct ieee80211_channel *bestchan = NULL; |
| 313 | struct ieee80211com *ic = NULL; |
| 314 | + unsigned long sflags; |
| 315 | int res = 1; |
| 316 | |
| 317 | SCAN_AP_LOCK_IRQ(as); |
| 318 | @@ -586,8 +595,11 @@ ap_end(struct ieee80211_scan_state *ss, |
| 319 | |
| 320 | /* record stats for the channel that was scanned last */ |
| 321 | ic->ic_set_channel(ic); |
| 322 | + spin_lock_irqsave(&channel_lock, sflags); |
| 323 | + ieee80211_scan_set_bss_channel(ic, NULL); |
| 324 | bestchan = pick_channel(ss, vap, flags); |
| 325 | if (bestchan == NULL) { |
| 326 | + spin_unlock_irqrestore(&channel_lock, sflags); |
| 327 | if (ss->ss_last > 0) { |
| 328 | /* no suitable channel, should not happen */ |
| 329 | printk(KERN_ERR "%s: %s: no suitable channel! " |
| 330 | @@ -606,6 +618,7 @@ ap_end(struct ieee80211_scan_state *ss, |
| 331 | bestchan->ic_freq, bestchan->ic_flags & |
| 332 | ~IEEE80211_CHAN_TURBO)) == NULL) { |
| 333 | /* should never happen ?? */ |
| 334 | + spin_unlock_irqrestore(&channel_lock, sflags); |
| 335 | SCAN_AP_UNLOCK_IRQ_EARLY(as); |
| 336 | return 0; |
| 337 | } |
| 338 | @@ -618,6 +631,9 @@ ap_end(struct ieee80211_scan_state *ss, |
| 339 | as->as_action = action; |
| 340 | as->as_selbss = se; |
| 341 | |
| 342 | + ieee80211_scan_set_bss_channel(ic, bestchan); |
| 343 | + spin_unlock_irqrestore(&channel_lock, sflags); |
| 344 | + |
| 345 | /* Must defer action to avoid possible recursive call through |
| 346 | * 80211 state machine, which would result in recursive |
| 347 | * locking. */ |
| 348 | |