blob: b9b8744c64483e398da31ee78b9f8385de3a3aa9 [file] [log] [blame]
/*-
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: ieee80211_scan.c 1849 2006-12-08 17:20:08Z proski $
*/
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
/*
* IEEE 802.11 scanning support.
*/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/random.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <qtn/qtn_debug.h>
#include <qtn/shared_defs.h>
#include <qtn/shared_params.h>
#include "net80211/if_media.h"
#include "net80211/ieee80211_var.h"
#include "net80211/ieee80211_scan.h"
struct scan_state {
struct ieee80211_scan_state base; /* public state */
u_int ss_iflags; /* flags used internally */
#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */
#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */
#define ISCAN_CANCEL 0x0004 /* cancel current scan */
#define ISCAN_START 0x0008 /* 1st time through next_scan */
unsigned long ss_chanmindwell; /* min dwell on curchan */
unsigned long ss_scanend; /* time scan must stop */
u_int ss_duration; /* duration for next scan */
struct tasklet_struct ss_pwrsav; /* sta ps ena tasklet */
struct timer_list ss_scan_timer; /* scan timer */
struct timer_list ss_probe_timer; /* start sending probe requests timer */
};
#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)
/*
* Amount of time to go off-channel during a background
* scan. This value should be large enough to catch most
* ap's but short enough that we can return on-channel
* before our listen interval expires.
*
* XXX tunable
* XXX check against configured listen interval
*/
#define IEEE80211_SCAN_OFFCHANNEL msecs_to_jiffies(150)
/*
* Roaming-related defaults. RSSI thresholds are as returned by the
* driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e
* .5M units).
*/
#define SCAN_VALID_DEFAULT 60 /* scan cache valid age (secs) */
#define ROAM_RSSI_11A_DEFAULT 24 /* rssi threshold for 11a bss */
#define ROAM_RSSI_11B_DEFAULT 24 /* rssi threshold for 11b bss */
#define ROAM_RSSI_11BONLY_DEFAULT 24 /* rssi threshold for 11b-only bss */
#define ROAM_RATE_11A_DEFAULT 2*24 /* tx rate threshold for 11a bss */
#define ROAM_RATE_11B_DEFAULT 2*9 /* tx rate threshold for 11b bss */
#define ROAM_RATE_11BONLY_DEFAULT 2*5 /* tx rate threshold for 11b-only bss */
static u_int32_t txpow_rxgain_count = 0;
static u_int32_t txpow_rxgain_state = 1;
static void scan_restart_pwrsav(unsigned long);
static void scan_next(unsigned long);
static void send_probes(unsigned long);
static void scan_saveie(u_int8_t **iep, const u_int8_t *ie);
#ifdef QSCS_ENABLED
int ieee80211_scs_init_ranking_stats(struct ieee80211com *ic)
{
struct ap_state *as;
int i;
MALLOC(as, struct ap_state *, sizeof(struct ap_state),
M_SCANCACHE, M_NOWAIT | M_ZERO);
if (as == NULL) {
printk("Failed to alloc scs ranking stats\n");
return -1;
}
if (ic->ic_scan != NULL) {
as->as_age = AP_PURGE_SCS;
ic->ic_scan->ss_scs_priv = as;
spin_lock_init(&as->asl_lock);
for (i = 0; i < IEEE80211_CHAN_MAX; i++)
TAILQ_INIT(&as->as_scan_list[i].asl_head);
} else {
FREE(as, M_SCANCACHE);
return -1;
}
ieee80211_scs_clean_stats(ic, IEEE80211_SCS_STATE_RESET, 0);
return 0;
}
void ieee80211_scs_deinit_ranking_stats(struct ieee80211com *ic)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
struct ap_state *as;
struct ap_state *as_bak;
const struct ieee80211_scanner *scan;
as = (struct ap_state *)ss->ss_scs_priv;
if (as != NULL) {
scan = ieee80211_scanner_get(IEEE80211_M_HOSTAP, 0);
if (scan == NULL) {
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
"%s: no scanner support for AP mode\n", __func__);
} else {
as_bak = ss->ss_priv;
ss->ss_priv = as;
scan->scan_detach(ss);
ss->ss_priv = as_bak;
}
FREE(as, M_SCANCACHE);
}
ss->ss_scs_priv = NULL;
}
#endif
void
ieee80211_scan_attach(struct ieee80211com *ic)
{
struct scan_state *ss;
ic->ic_roaming = IEEE80211_ROAMING_AUTO;
MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
M_80211_SCAN, M_NOWAIT | M_ZERO);
if (ss != NULL) {
init_timer(&ss->ss_scan_timer);
ss->ss_scan_timer.function = scan_next;
ss->ss_scan_timer.data = (unsigned long) ss;
/* Init the send probe timer for active scans */
init_timer(&ss->ss_probe_timer);
ss->ss_probe_timer.function = send_probes;
ss->ss_probe_timer.data = (unsigned long) ss;
tasklet_init(&ss->ss_pwrsav, scan_restart_pwrsav,
(unsigned long) ss);
ss->base.ss_pick_flags = IEEE80211_PICK_DEFAULT;
ss->base.is_scan_valid = 0;
ic->ic_scan = &ss->base;
} else
ic->ic_scan = NULL;
#ifdef QSCS_ENABLED
ieee80211_scs_init_ranking_stats(ic);
#endif
}
void
ieee80211_scan_detach(struct ieee80211com *ic)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss != NULL) {
#ifdef QSCS_ENABLED
ieee80211_scs_deinit_ranking_stats(ic);
#endif
del_timer(&SCAN_PRIVATE(ss)->ss_scan_timer);
del_timer(&SCAN_PRIVATE(ss)->ss_probe_timer);
tasklet_kill(&SCAN_PRIVATE(ss)->ss_pwrsav);
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
ss->ss_ops = NULL;
}
ic->ic_flags &= ~IEEE80211_F_SCAN;
#ifdef QTN_BG_SCAN
ic->ic_flags_qtn &= ~IEEE80211_QTN_BGSCAN;
#endif /* QTN_BG_SCAN */
ic->ic_scan = NULL;
FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
}
}
void
ieee80211_scan_vattach(struct ieee80211vap *vap)
{
vap->iv_bgscanidle = msecs_to_jiffies(IEEE80211_BGSCAN_IDLE_DEFAULT);
vap->iv_bgscanintvl = vap->iv_ic->ic_extender_bgscanintvl;
vap->iv_scanvalid = SCAN_VALID_DEFAULT * HZ;
vap->iv_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
vap->iv_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
vap->iv_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
vap->iv_roam.rate11a = ROAM_RATE_11A_DEFAULT;
vap->iv_roam.rate11b = ROAM_RATE_11B_DEFAULT;
vap->iv_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
txpow_rxgain_count = 0;
txpow_rxgain_state = 1;
}
void
ieee80211_scan_vdetach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_IRQ(ic);
if (ss->ss_vap == vap) {
if ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
) {
del_timer(&SCAN_PRIVATE(ss)->ss_scan_timer);
del_timer(&SCAN_PRIVATE(ss)->ss_probe_timer);
ic->ic_flags &= ~IEEE80211_F_SCAN;
#ifdef QTN_BG_SCAN
ic->ic_flags_qtn &= ~IEEE80211_QTN_BGSCAN;
#endif /* QTN_BG_SCAN */
}
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
ss->ss_ops = NULL;
}
}
IEEE80211_UNLOCK_IRQ(ic);
}
/*
* Simple-minded scanner module support.
*/
#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)
static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
[IEEE80211_M_IBSS] = "wlan_scan_sta",
[IEEE80211_M_STA] = "wlan_scan_sta",
[IEEE80211_M_AHDEMO] = "wlan_scan_sta",
[IEEE80211_M_HOSTAP] = "wlan_scan_ap",
};
static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
const struct ieee80211_scanner *
ieee80211_scanner_get(enum ieee80211_opmode mode, int tryload)
{
int err;
if (mode >= IEEE80211_SCANNER_MAX)
return NULL;
if (scan_modnames[mode] == NULL)
return NULL;
if (scanners[mode] == NULL && tryload) {
err = ieee80211_load_module(scan_modnames[mode]);
if (scanners[mode] == NULL || err)
printk(KERN_WARNING "unable to load %s\n", scan_modnames[mode]);
}
return scanners[mode];
}
EXPORT_SYMBOL(ieee80211_scanner_get);
void
ieee80211_scanner_register(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
if (mode >= IEEE80211_SCANNER_MAX)
return;
scanners[mode] = scan;
}
EXPORT_SYMBOL(ieee80211_scanner_register);
void
ieee80211_scanner_unregister(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
if (mode >= IEEE80211_SCANNER_MAX)
return;
if (scanners[mode] == scan)
scanners[mode] = NULL;
}
EXPORT_SYMBOL(ieee80211_scanner_unregister);
void
ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
{
int m;
for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
if (scanners[m] == scan)
scanners[m] = NULL;
}
EXPORT_SYMBOL(ieee80211_scanner_unregister_all);
u_int8_t g_channel_fixed = 0;
static void
change_channel(struct ieee80211com *ic,
struct ieee80211_channel *chan)
{
#if 1
/* If channel is fixed using iwconfig then don't do anything */
if(!g_channel_fixed)
{
ic->ic_prevchan = ic->ic_curchan;
ic->ic_curchan = chan;
//printk("Curr chan : %d\n", ic->ic_curchan->ic_ieee);
ic->ic_set_channel(ic);
}
#else
ic->ic_curchan = chan;
#endif
}
static char
channel_type(const struct ieee80211_channel *c)
{
if (IEEE80211_IS_CHAN_ST(c))
return 'S';
if (IEEE80211_IS_CHAN_108A(c))
return 'T';
if (IEEE80211_IS_CHAN_108G(c))
return 'G';
if (IEEE80211_IS_CHAN_A(c))
return 'a';
if (IEEE80211_IS_CHAN_ANYG(c))
return 'g';
if (IEEE80211_IS_CHAN_B(c))
return 'b';
return 'f';
}
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
struct ieee80211com *ic = ss->ss_vap->iv_ic;
const char *sep;
int i;
sep = "";
for (i = ss->ss_next; i < ss->ss_last; i++) {
const struct ieee80211_channel *c = ss->ss_chans[i];
printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c),
channel_type(c));
sep = ", ";
}
}
EXPORT_SYMBOL(ieee80211_scan_dump_channels);
/*
* Enable station power save mode and start/restart the scanning thread.
*/
static void
scan_restart_pwrsav(unsigned long arg)
{
struct scan_state *ss = (struct scan_state *) arg;
struct ieee80211vap *vap = ss->base.ss_vap;
struct ieee80211com *ic = vap->iv_ic;
int delay;
ieee80211_sta_pwrsave(vap, 1);
/*
* Use an initial 1ms delay to ensure the null
* data frame has a chance to go out.
* XXX 1ms is a lot, better to trigger scan
* on tx complete.
*/
delay = msecs_to_jiffies(1);
if (delay < 1)
delay = 1;
ic->ic_setparam(vap->iv_bss, IEEE80211_PARAM_BEACON_ALLOW,
1, NULL, 0);
ic->ic_scan_start(ic); /* notify driver */
ss->ss_scanend = jiffies + delay + ss->ss_duration;
ss->ss_iflags |= ISCAN_START;
mod_timer(&ss->ss_scan_timer, jiffies + delay);
/*
* FIXME: Note, we are not delaying probes at the start here so there
* may be issues with probe requests not being on the correct
* channel for the first channel scanned.
*/
}
/*
* Start/restart scanning. If we're operating in station mode
* and associated notify the ap we're going into power save mode
* and schedule a callback to initiate the work (where there's a
* better context for doing the work). Otherwise, start the scan
* directly.
*/
static int
scan_restart(struct scan_state *ss, u_int duration)
{
struct ieee80211vap *vap = ss->base.ss_vap;
struct ieee80211com *ic = vap->iv_ic;
int defer = 0;
if (ss->base.ss_next == ss->base.ss_last) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
return 0;
}
if (vap->iv_opmode == IEEE80211_M_STA &&
#ifdef QTN_BG_SCAN
/* qtn bgscan sends pwrsav frame in MuC, or use large NAV */
(ss->base.ss_flags & IEEE80211_SCAN_QTN_BGSCAN) == 0 &&
#endif /* QTN_BG_SCAN */
vap->iv_state == IEEE80211_S_RUN &&
(ss->base.ss_flags & IEEE80211_SCAN_OPCHAN) == 0) {
if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/*
* Initiate power save before going off-channel.
* Note that we cannot do this directly because
* of locking issues; instead we defer it to a
* tasklet.
*/
ss->ss_duration = duration;
tasklet_schedule(&ss->ss_pwrsav);
defer = 1;
}
}
if (!defer) {
if (vap->iv_opmode == IEEE80211_M_STA &&
#ifdef QTN_BG_SCAN
!(ss->base.ss_flags & IEEE80211_SCAN_QTN_BGSCAN) &&
#endif
vap->iv_state == IEEE80211_S_RUN) {
ic->ic_setparam(vap->iv_bss, IEEE80211_PARAM_BEACON_ALLOW,
1, NULL, 0);
}
ic->ic_scan_start(ic); /* notify driver */
ss->ss_scanend = jiffies + duration;
ss->ss_iflags |= ISCAN_START;
mod_timer(&ss->ss_scan_timer, jiffies);
/*
* FIXME: Note, we are not delaying probes at the start here so there
* may be issues with probe requests not being on the correct
* channel for the first channel scanned.
*/
}
return 1;
}
static void
copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
if (nssid > IEEE80211_SCAN_MAX_SSID) {
/* XXX printf */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: too many ssid %d, ignoring all of them\n",
__func__, nssid);
return;
}
memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0]));
ss->ss_nssid = nssid;
}
/*
* Start a scan unless one is already going.
*/
int
ieee80211_start_scan(struct ieee80211vap *vap, int flags, u_int duration,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_scanner *scan;
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ic->sta_dfs_info.sta_dfs_strict_mode) {
if ((ic->ic_bsschan != IEEE80211_CHAN_ANYC) &&
IEEE80211_IS_CHAN_CAC_IN_PROGRESS(ic->ic_bsschan)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: Ignored-CAC in progress\n", __func__);
return 0;
}
}
scan = ieee80211_scanner_get(vap->iv_opmode, 0);
if (scan == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scanner support for mode %u\n",
__func__, vap->iv_opmode);
/* XXX stat */
return 0;
}
if (ic->ic_flags_qtn & IEEE80211_QTN_MONITOR) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: not scanning - monitor mode enabled\n", __func__);
return 0;
}
IEEE80211_LOCK_IRQ(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0
#ifdef QTN_BG_SCAN
&& (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0
#endif /* QTN_BG_SCAN */
) {
if (flags & IEEE80211_SCAN_BW40)
ss->ss_scan_bw = BW_HT40;
else if (flags & IEEE80211_SCAN_BW80)
ss->ss_scan_bw = BW_HT80;
else if (flags & IEEE80211_SCAN_BW160)
ss->ss_scan_bw = BW_HT160;
else
ss->ss_scan_bw = BW_HT20;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan, bw %s, duration %lu, desired mode %s, %s%s%s%s%s\n",
__func__,
flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
ieee80211_bw2str(ss->ss_scan_bw),
duration,
ieee80211_phymode_name[ic->ic_des_mode],
flags & IEEE80211_SCAN_FLUSH ? "flush" : "append",
flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "",
flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "",
flags & IEEE80211_SCAN_ONCE ? ", once" : "",
flags & IEEE80211_SCAN_OPCHAN ? ", operating channel only" : "");
ss->ss_vap = vap;
if (ss->ss_ops != scan) {
/* switch scanners; detach old, attach new */
if (ss->ss_ops != NULL)
ss->ss_ops->scan_detach(ss);
if (!scan->scan_attach(ss)) {
/* XXX attach failure */
/* XXX stat+msg */
ss->ss_ops = NULL;
} else
ss->ss_ops = scan;
}
if (ss->ss_ops != NULL) {
if ((flags & IEEE80211_SCAN_NOSSID) == 0)
copy_ssid(vap, ss, nssid, ssids);
/* NB: top 4 bits for internal use */
ss->ss_flags = flags & 0xfff;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
vap->iv_stats.is_scan_active++;
else
vap->iv_stats.is_scan_passive++;
if (flags & IEEE80211_SCAN_FLUSH)
ss->ss_ops->scan_flush(ss);
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ss->ss_ops->scan_start(ss, vap);
if (scan_restart(SCAN_PRIVATE(ss), duration)) {
#ifdef QTN_BG_SCAN
if (flags & IEEE80211_SCAN_QTN_BGSCAN)
ic->ic_flags_qtn |= IEEE80211_QTN_BGSCAN;
else
#endif /*QTN_BG_SCAN */
ic->ic_flags |= IEEE80211_F_SCAN;
ieee80211_scan_scs_sample_cancel(vap);
#if defined(QBMPS_ENABLE)
if ((ic->ic_flags_qtn & IEEE80211_QTN_BMPS) &&
(vap->iv_opmode == IEEE80211_M_STA)) {
/* exit power-saving */
ic->ic_pm_reason = IEEE80211_PM_LEVEL_SCAN_START;
ieee80211_pm_queue_work(ic);
}
#endif
}
#ifdef QTN_BG_SCAN
if (ic->ic_qtn_bgscan.debug_flags >= 3) {
printk("BG_SCAN: start %s scanning...\n",
(ic->ic_flags & IEEE80211_F_SCAN)?"regular":"background");
}
#endif /*QTN_BG_SCAN */
}
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
IEEE80211_UNLOCK_IRQ(ic);
/* Don't transmit beacons while scanning */
if (vap->iv_opmode == IEEE80211_M_HOSTAP
#ifdef QTN_BG_SCAN
&& !(flags & IEEE80211_SCAN_QTN_BGSCAN)
#endif /*QTN_BG_SCAN */
) {
ic->ic_beacon_stop(vap);
}
/* NB: racey, does it matter? */
return (ic->ic_flags & IEEE80211_F_SCAN);
}
EXPORT_SYMBOL(ieee80211_start_scan);
/*
* Under repeater mode, when the AP interface is not in RUN state,
* hold off scanning procedure on STA interface
*/
int ieee80211_should_scan(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *first_ap = NULL;
struct ieee80211vap *vap_each;
struct ieee80211vap *vap_tmp;
int ret = 1;
if (vap->iv_opmode != IEEE80211_M_STA || !(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER))
return 1;
IEEE80211_VAPS_LOCK_BH(ic);
TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) {
if (vap_each->iv_opmode == IEEE80211_M_HOSTAP) {
first_ap = vap_each;
break;
}
}
KASSERT((first_ap != NULL), ("Repeater mode must have an AP interface"));
if (first_ap->iv_state != IEEE80211_S_RUN)
/*
* Do not initiate scan for repeater STA if AP interface hasn't
* been properly running yet
*/
ret = 0;
IEEE80211_VAPS_UNLOCK_BH(ic);
return ret;
}
/*
* Check the scan cache for an ap/channel to use; if that
* fails then kick off a new scan.
*/
int
ieee80211_check_scan(struct ieee80211vap *vap, int flags, u_int duration,
u_int nssid, const struct ieee80211_scan_ssid ssids[],
int (*action)(struct ieee80211vap *, const struct ieee80211_scan_entry *))
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
#ifdef SCAN_CACHE_ENABLE
int checkscanlist = 0;
#endif
/*
* Check if there's a list of scan candidates already.
* XXX want more than the ap we're currently associated with
*/
IEEE80211_LOCK_IRQ(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan, duration %lu, desired mode %s, %s%s%s%s\n",
__func__,
flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
duration,
ieee80211_phymode_name[ic->ic_des_mode],
flags & IEEE80211_SCAN_FLUSH ? "flush" : "append",
flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "",
flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "",
flags & IEEE80211_SCAN_ONCE ? ", once" : "",
flags & IEEE80211_SCAN_USECACHE ? ", usecache" : "");
if (ss->ss_ops != NULL) {
/* XXX verify ss_ops matches vap->iv_opmode */
if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
/*
* Update the ssid list and mark flags so if
* we call start_scan it doesn't duplicate work.
*/
copy_ssid(vap, ss, nssid, ssids);
flags |= IEEE80211_SCAN_NOSSID;
}
#ifdef SCAN_CACHE_ENABLE
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
#ifdef QTN_BG_SCAN
(ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0 &&
#endif /* QTN_BG_SCAN */
time_before(jiffies, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* We're not currently scanning and the cache is
* deemed hot enough to consult. Lock out others
* by marking IEEE80211_F_SCAN while we decide if
* something is already in the scan cache we can
* use. Also discard any frames that might come
* in while temporarily marked as scanning.
*/
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ic->ic_flags |= IEEE80211_F_SCAN;
checkscanlist = 1;
}
#endif
}
IEEE80211_UNLOCK_IRQ(ic);
#ifdef SCAN_CACHE_ENABLE
if (checkscanlist) {
/*
* ss must be filled out so scan may be restarted "outside"
* of the current callstack.
*/
ss->ss_flags = flags;
ss->ss_duration = duration;
if (ss->ss_ops->scan_end(ss, ss->ss_vap, action, flags & IEEE80211_SCAN_KEEPMODE)) {
/* found an ap, just clear the flag */
ic->ic_flags &= ~IEEE80211_F_SCAN;
return 1;
}
/* no ap, clear the flag before starting a scan */
ic->ic_flags &= ~IEEE80211_F_SCAN;
}
#endif
if ((flags & IEEE80211_SCAN_USECACHE) == 0 &&
ieee80211_should_scan(vap)) {
return ieee80211_start_scan(vap, flags, duration, nssid, ssids);
} else {
/* If we *must* use the cache and no ap was found, return failure */
return 0;
}
}
/*
* Restart a previous scan. If the previous scan completed
* then we start again using the existing channel list.
*/
int
ieee80211_bg_scan(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_IRQ(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0
#ifdef QTN_BG_SCAN
&& (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0
#endif /* QTN_BG_SCAN */
) {
u_int duration;
/*
* Go off-channel for a fixed interval that is large
* enough to catch most ap's but short enough that
* we can return on-channel before our listen interval
* expires.
*/
duration = IEEE80211_SCAN_OFFCHANNEL;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan, jiffies %lu duration %lu\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
jiffies, duration);
if (ss->ss_ops != NULL) {
ss->ss_vap = vap;
/*
* A background scan does not select a new sta; it
* just refreshes the scan cache. Also, indicate
* the scan logic should follow the beacon schedule:
* we go off-channel and scan for a while, then
* return to the bss channel to receive a beacon,
* then go off-channel again. All during this time
* we notify the ap we're in power save mode. When
* the scan is complete we leave power save mode.
* If any beacon indicates there are frames pending
*for us then we drop out of power save mode
* (and background scan) automatically by way of the
* usual sta power save logic.
*/
ss->ss_flags |= IEEE80211_SCAN_NOPICK |
IEEE80211_SCAN_BGSCAN;
if (ic->ic_scan_opchan_enable && vap->iv_opmode == IEEE80211_M_STA) {
ss->ss_flags |= IEEE80211_SCAN_OPCHAN | IEEE80211_SCAN_ACTIVE;
ss->ss_ops->scan_start(ss, vap);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: force a new active bgscan", __func__);
}
/* if previous scan completed, restart */
if (ss->ss_next >= ss->ss_last) {
ss->ss_next = 0;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
vap->iv_stats.is_scan_active++;
else
vap->iv_stats.is_scan_passive++;
ss->ss_ops->scan_restart(ss, vap);
}
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ss->ss_mindwell = duration;
if (scan_restart(SCAN_PRIVATE(ss), duration)) {
ic->ic_flags |= IEEE80211_F_SCAN;
ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
}
} else {
/* XXX msg+stat */
}
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
IEEE80211_UNLOCK_IRQ(ic);
/* NB: racey, does it matter? */
return (ic->ic_flags & IEEE80211_F_SCAN);
}
EXPORT_SYMBOL(ieee80211_bg_scan);
static void
_ieee80211_cancel_scan(struct ieee80211vap *vap, int no_wait)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_IRQ(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: cancel %s scan\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
/* clear bg scan NOPICK and mark cancel request */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
ss->ss_ops->scan_cancel(ss, vap);
if (no_wait) {
/* force it to fire immediately */
del_timer(&SCAN_PRIVATE(ss)->ss_scan_timer);
(SCAN_PRIVATE(ss)->ss_scan_timer).function((SCAN_PRIVATE(ss)->ss_scan_timer).data);
} else {
/* force it to fire asap */
mod_timer(&SCAN_PRIVATE(ss)->ss_scan_timer, jiffies);
}
/*
* The probe timer is not cleared, so there may be some
* probe requests sent after the scan, but that should not
* cause any issues.
*/
}
IEEE80211_UNLOCK_IRQ(ic);
}
/*
* Cancel any scan currently going on.
*/
void
ieee80211_cancel_scan(struct ieee80211vap *vap)
{
_ieee80211_cancel_scan(vap, 0);
}
/*
* Cancel any scan currently going on immediately
*/
void
ieee80211_cancel_scan_no_wait(struct ieee80211vap *vap)
{
_ieee80211_cancel_scan(vap, 1);
}
/*
* Process a beacon or probe response frame for SCS off channel sampling
*/
void ieee80211_add_scs_off_chan(struct ieee80211vap *vap,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int rstamp)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
struct ap_state *as;
struct ap_state *as_bak;
if (ic->ic_opmode != IEEE80211_M_HOSTAP)
return;
as = (struct ap_state *)ss->ss_scs_priv;
if (as && ss->ss_ops && ss->ss_ops->scan_add) {
as_bak = ss->ss_priv;
ss->ss_priv = as;
ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, rstamp);
ss->ss_priv = as_bak;
}
}
void
ieee80211_scan_scs_sample_cancel(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_opmode != IEEE80211_M_HOSTAP)
return;
ic->ic_sample_channel_cancel(vap);
}
/*
* Sample the state of an off-channel for Interference Mitigation
*/
void
ieee80211_scan_scs_sample(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
int scanning;
int16_t scs_chan = ic->ic_scs.scs_last_smpl_chan;
int16_t chan_count = 0;
struct ieee80211_channel *chan;
const struct ieee80211_scanner *scan;
struct ieee80211_scan_state *ss = ic->ic_scan;
int cur_bw;
struct ieee80211vap *tmp_vap;
int wds_basic_pure = 0;
IEEE80211_LOCK_IRQ(ic);
scanning = ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
);
IEEE80211_UNLOCK_IRQ(ic);
if (scanning) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: not sampling - scan in progress\n", __func__);
IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_IN_SCAN);
return;
}
if (!ic->ic_scs.scs_stats_on) {
SCSDBG(SCSLOG_INFO, "not sampling - scs stats is disabled\n");
return;
}
if (vap->iv_state != IEEE80211_S_RUN) {
SCSDBG(SCSLOG_INFO, "not sampling - vap is not in running status\n");
return;
}
if (ic->ic_ocac.ocac_running) {
SCSDBG(SCSLOG_INFO, "not sampling - Seamless DFS is ongoing\n");
return;
}
TAILQ_FOREACH(tmp_vap, &ic->ic_vaps, iv_next) {
if (IEEE80211_VAP_WDS_IS_MBS(tmp_vap)) {
wds_basic_pure = 0;
break;
} else if (IEEE80211_VAP_WDS_IS_RBS(tmp_vap)) {
SCSDBG(SCSLOG_INFO, "not sampling - RBS mode\n");
return;
} else if (IEEE80211_VAP_WDS_BASIC(tmp_vap)) {
wds_basic_pure = 1;
}
}
if (wds_basic_pure) {
SCSDBG(SCSLOG_INFO, "not sampling - basic WDS mode\n");
return;
}
cur_bw = ieee80211_get_bw(ic);
scan = ieee80211_scanner_get(IEEE80211_M_HOSTAP, 0);
if (scan == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scanner support for AP mode\n", __func__);
return;
}
ss->ss_vap = vap;
if (ss->ss_ops != scan) {
if (ss->ss_ops != NULL)
ss->ss_ops->scan_detach(ss);
if (!scan->scan_attach(ss)) {
ss->ss_ops = NULL;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: scanner attach failed\n", __func__);
return;
} else {
ss->ss_ops = scan;
}
}
scan_next_chan:
chan_count++;
scs_chan += IEEE80211_SUBCHANNELS_OF_20MHZ;
if (cur_bw >= BW_HT40)
scs_chan += IEEE80211_SUBCHANNELS_OF_40MHZ - IEEE80211_SUBCHANNELS_OF_20MHZ;
if (cur_bw >= BW_HT80)
scs_chan += IEEE80211_SUBCHANNELS_OF_80MHZ - IEEE80211_SUBCHANNELS_OF_40MHZ;
if (chan_count > ic->ic_nchans) {
SCSDBG(SCSLOG_INFO, "no available off channel for sampling\n");
return;
}
if (scs_chan >= ic->ic_nchans) {
if (cur_bw > BW_HT20)
ic->ic_scs.scs_smpl_chan_offset++;
if (cur_bw == BW_HT40 && ic->ic_scs.scs_smpl_chan_offset >
IEEE80211_SUBCHANNELS_OF_40MHZ - 1)
ic->ic_scs.scs_smpl_chan_offset = 0;
else if (cur_bw == BW_HT80 && ic->ic_scs.scs_smpl_chan_offset >
IEEE80211_SUBCHANNELS_OF_80MHZ - 1)
ic->ic_scs.scs_smpl_chan_offset = 0;
scs_chan = 0;
scs_chan += ic->ic_scs.scs_smpl_chan_offset;
}
chan = &ic->ic_channels[scs_chan];
if (isclr(ic->ic_chan_active, chan->ic_ieee)) {
goto scan_next_chan;
}
/* do not scan current working channel */
if (chan->ic_ieee == ic->ic_curchan->ic_ieee) {
goto scan_next_chan;
}
if (cur_bw == BW_HT40) {
if (!(chan->ic_flags & IEEE80211_CHAN_HT40) ||
(chan->ic_ieee == ieee80211_find_sec_chan(ic->ic_curchan))) {
goto scan_next_chan;
}
}
if (cur_bw >= BW_HT80) {
if (!(chan->ic_flags & IEEE80211_CHAN_VHT80) ||
(chan->ic_center_f_80MHz == ic->ic_curchan->ic_center_f_80MHz)) {
goto scan_next_chan;
}
}
SCSDBG(SCSLOG_INFO, "choose sampling channel: %u\n", chan->ic_ieee);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: sampling channel %u freq=%u\n", __func__,
chan->ic_ieee, chan->ic_freq);
/* don't move to next until muc finish sampling */
ic->ic_scs.scs_des_smpl_chan = scs_chan;
ic->ic_sample_channel(vap, chan);
}
EXPORT_SYMBOL(ieee80211_scan_scs_sample);
int
ap_list_asl_table(struct ieee80211_scan_state *ss)
{
struct ap_state *as = ss->ss_priv;
struct ap_scan_entry *se, *next;
int i;
printk(KERN_ERR "CHINUSE_START\n");
for (i = 0; i < IEEE80211_CHAN_MAX; i++) {
TAILQ_FOREACH_SAFE(se, &as->as_scan_list[i].asl_head, ase_list, next) {
printk(KERN_EMERG "Channel %d : %d Mhz\n",
se->base.se_chan->ic_ieee, se->base.se_chan->ic_freq);
break;
}
}
printk(KERN_ERR "CHINUSE_END\n");
return 0;
}
/*
* Getting maximum and minimum dwell time for scanning
*/
static void
get_max_min_dwell(struct ieee80211_scan_state *ss, struct ieee80211_node *ni,
int is_passive, int is_obss_scan, int *mindwell, int *maxdwell)
{
if (is_passive) {
if (is_obss_scan) {
*mindwell = msecs_to_jiffies(ni->ni_obss_ie.obss_passive_dwell);
if (*mindwell > ss->ss_maxdwell_passive)
*maxdwell = *mindwell;
else
*maxdwell = ss->ss_maxdwell_passive;
} else {
*mindwell = ss->ss_mindwell_passive;
*maxdwell = ss->ss_maxdwell_passive;
}
} else {
if (is_obss_scan) {
*mindwell = msecs_to_jiffies(ni->ni_obss_ie.obss_active_dwell);
if (*mindwell > ss->ss_maxdwell)
*maxdwell = *mindwell;
else
*maxdwell = ss->ss_maxdwell;
} else {
*mindwell = ss->ss_mindwell;
*maxdwell = ss->ss_maxdwell;
}
}
}
/*
* Switch to the next channel marked for scanning.
*/
static void
scan_next(unsigned long arg)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
struct ieee80211vap *vap = ss->ss_vap;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
int scanning, scandone;
/* Make passive channels special */
int is_passive;
int is_obss = 0;
int maxdwell_used;
int mindwell_used;
#ifdef QTN_BG_SCAN
int bgscan_dwell = 0;
#endif /* QTN_BG_SCAN */
if (ss->ss_flags & IEEE80211_SCAN_OBSS) {
if (ni && IEEE80211_AID(ni->ni_associd))
is_obss = 1;
else
return;
}
IEEE80211_LOCK_IRQ(ic);
scanning = ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
);
IEEE80211_UNLOCK_IRQ(ic);
if (!scanning) /* canceled */
return;
again:
scandone = (ss->ss_next >= ss->ss_last) ||
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
scanend = SCAN_PRIVATE(ss)->ss_scanend;
if ((vap->iv_opmode == IEEE80211_M_STA) && (ss->ss_next == 0)) {
/*
* Periodically scan using low Rx gain and Tx power in case
* association is failing because the AP is too close.
* More suitable power settings will be determined after association.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s Starting a scan (Low power %s, count %d)\n",
__func__, txpow_rxgain_state ? "on" : "off", txpow_rxgain_count);
if ((ic->ic_pwr_adjust_scancnt > 0) && (ss->is_scan_valid) &&
#ifdef QTN_BG_SCAN
!(ss->ss_flags & IEEE80211_SCAN_QTN_BGSCAN) &&
#endif /* QTN_BG_SCAN */
(txpow_rxgain_count) && ((txpow_rxgain_count % ic->ic_pwr_adjust_scancnt) == 0)) {
ieee80211_pwr_adjust(vap, txpow_rxgain_state);
txpow_rxgain_state = !txpow_rxgain_state;
}
txpow_rxgain_count++;
#ifdef QTN_BG_SCAN
if ((ss->ss_flags & IEEE80211_SCAN_QTN_BGSCAN) &&
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START)) {
ic->ic_bgscan_start(ic);
}
#endif /* QTN_BG_SCAN */
}
/* Work out the previous channel (to use passive vs. active mindwell) */
chan = (ss->ss_next) ? ss->ss_chans[ss->ss_next-1] : ss->ss_chans[0];
is_passive = (!(ss->ss_flags & IEEE80211_SCAN_ACTIVE) ||
(chan->ic_flags & IEEE80211_CHAN_PASSIVE));
get_max_min_dwell(ss, ni, is_passive, is_obss, &mindwell_used, &maxdwell_used);
if (!scandone &&
(ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 &&
((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) ||
time_before(jiffies + mindwell_used, scanend))) {
chan = ss->ss_chans[ss->ss_next++];
is_passive = (!(ss->ss_flags & IEEE80211_SCAN_ACTIVE) ||
(chan->ic_flags & IEEE80211_CHAN_PASSIVE));
ic->ic_scanchan = chan;
#ifdef QTN_BG_SCAN
if (ss->ss_flags & IEEE80211_SCAN_QTN_BGSCAN) {
uint32_t scan_mode = ss->ss_pick_flags & IEEE80211_PICK_BG_MODE_MASK;
if (!is_passive) {
scan_mode = IEEE80211_PICK_BG_ACTIVE;
}
if (chan->ic_ieee == ic->ic_bsschan->ic_ieee) {
if (vap->iv_opmode != IEEE80211_M_STA) {
scan_mode = IEEE80211_PICK_BG_ACTIVE;
} else if (scan_mode & IEEE80211_PICK_BG_ACTIVE) {
scan_mode = 0;
}
}
if (!scan_mode) {
/*
* Auto passive mode selection:
* 1) if FAT is larger than the threshold for fast mode
* which is 60% by default, will pick passive fast mode
* 2) else if FAT is larger than the threshold for normal mode
* which is 30% by default, will pick passive normal mode
* 3) else pick passive slow mode.
*/
if (ic->ic_scs.scs_cca_idle_smthed >=
ic->ic_qtn_bgscan.thrshld_fat_passive_fast) {
scan_mode = IEEE80211_PICK_BG_PASSIVE_FAST;
} else if (ic->ic_scs.scs_cca_idle_smthed >=
ic->ic_qtn_bgscan.thrshld_fat_passive_normal) {
scan_mode = IEEE80211_PICK_BG_PASSIVE_NORMAL;
} else {
scan_mode = IEEE80211_PICK_BG_PASSIVE_SLOW;
}
}
if (scan_mode & IEEE80211_PICK_BG_ACTIVE) {
maxdwell_used = msecs_to_jiffies(ic->ic_qtn_bgscan.duration_msecs_active);
} else if (scan_mode & IEEE80211_PICK_BG_PASSIVE_FAST) {
maxdwell_used = msecs_to_jiffies(ic->ic_qtn_bgscan.duration_msecs_passive_fast);
} else if (scan_mode & IEEE80211_PICK_BG_PASSIVE_NORMAL) {
maxdwell_used = msecs_to_jiffies(ic->ic_qtn_bgscan.duration_msecs_passive_normal);
} else {
maxdwell_used = msecs_to_jiffies(ic->ic_qtn_bgscan.duration_msecs_passive_slow);
}
maxdwell = mindwell_used = maxdwell_used;
if (scan_mode & IEEE80211_PICK_BG_ACTIVE) {
bgscan_dwell = ic->ic_qtn_bgscan.dwell_msecs_passive;
} else {
bgscan_dwell = ic->ic_qtn_bgscan.dwell_msecs_active;
}
/*
* Workaround: in STA mode, don't send probe request frame
* directly because the probe response frame from other AP
* may mess up the txalert timer
*/
if (chan->ic_ieee == ic->ic_bsschan->ic_ieee &&
vap->iv_opmode != IEEE80211_M_STA) {
ieee80211_send_probereq(vap->iv_bss,
vap->iv_myaddr, vap->iv_dev->broadcast,
vap->iv_dev->broadcast,
(u_int8_t *)"", 0,
vap->iv_opt_ie, vap->iv_opt_ie_len);
} else {
ic->ic_bgscan_channel(vap, chan, scan_mode, bgscan_dwell);
}
} else {
#endif /* QTN_BG_SCAN */
/* Reset mindwell and maxdwell as the new channel could be passive */
get_max_min_dwell(ss, ni, is_passive, is_obss, &mindwell_used, &maxdwell_used);
/*
* Watch for truncation due to the scan end time.
*/
if (time_after(jiffies + maxdwell_used, scanend))
maxdwell = scanend - jiffies;
else
maxdwell = maxdwell_used;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
channel_type(ic->ic_curchan),
ieee80211_chan2ieee(ic, chan), channel_type(chan),
(ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
"active" : "passive",
mindwell_used, maxdwell);
/*
* Potentially change channel and phy mode.
*/
/* Channel change done with 20MHz wide channels unless in 40MHz only mode */
if (!((ss->ss_flags & IEEE80211_SCAN_BGSCAN) &&
chan->ic_ieee == ic->ic_curchan->ic_ieee)) {
if (ss->ss_scan_bw == BW_HT20)
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_20;
else if ((ss->ss_scan_bw == BW_HT40) || (ic->ic_11n_40_only_mode))
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_40;
change_channel(ic, chan);
/* Clear the flag */
if (ss->ss_scan_bw == BW_HT20)
ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_20;
else if ((ss->ss_scan_bw == BW_HT40) || (ic->ic_11n_40_only_mode))
ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_40;
}
#ifdef QTN_BG_SCAN
}
#endif /* QTN_BG_SCAN */
/*
* If doing an active scan and the channel is not
* marked passive-only then send a probe request.
* Otherwise just listen for traffic on the channel.
*/
if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
#ifdef QTN_BG_SCAN
/* qtn bgscan sends probe request in MuC */
(ss->ss_flags & IEEE80211_SCAN_QTN_BGSCAN) == 0 &&
#endif /* QTN_BG_SCAN */
(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
/*
* Delay sending the probe requests so we are on
* the new channel. Current delay is half of maxdwell
* to make sure it is well within the dwell time,
* this can be fine tuned later if necessary.
*/
mod_timer(&SCAN_PRIVATE(ss)->ss_probe_timer,
jiffies + (maxdwell / 2));
}
SCAN_PRIVATE(ss)->ss_chanmindwell = jiffies + mindwell_used;
mod_timer(&SCAN_PRIVATE(ss)->ss_scan_timer, jiffies + maxdwell);
/* clear mindwell lock and initial channel change flush */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
} else {
ic->ic_scan_end(ic); /* notify driver */
/*
* Record scan complete time. Note that we also do
* this when canceled so any background scan will
* not be restarted for a while.
*/
if (scandone)
ic->ic_lastscan = jiffies;
/* return to the bss channel */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) {
#ifdef QTN_BG_SCAN
if (ss->ss_flags & IEEE80211_SCAN_QTN_BGSCAN) {
struct ieee80211vap *tmp_vap;
/*
* Need to update beacon because beacon may have been
* updated when AP is on off channel, and in this case,
* the channel offset in beacon's retry table setting
* may not be correct.
*/
TAILQ_FOREACH(tmp_vap, &ic->ic_vaps, iv_next) {
if (tmp_vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (tmp_vap->iv_state != IEEE80211_S_RUN)
continue;
ic->ic_beacon_update(tmp_vap);
}
}
else
#endif /* QTN_BG_SCAN */
change_channel(ic, ic->ic_bsschan);
}
/* clear internal flags and any indication of a pick */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
ieee80211_check_type_of_neighborhood(ic);
#ifdef QSCS_ENABLED
ieee80211_scs_update_ranking_table_by_scan(ic);
ieee80211_scs_adjust_cca_threshold(ic);
#endif
}
/*
* If not canceled and scan completed, do post-processing.
* If the callback function returns 0, then it wants to
* continue/restart scanning. Unfortunately we needed to
* notify the driver to end the scan above to avoid having
* rx frames alter the scan candidate list.
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
!ss->ss_ops->scan_end(ss, vap, NULL, 0) &&
(ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
time_before(jiffies + mindwell_used, scanend)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: done, restart "
"[jiffies %lu, dwell min %lu scanend %lu]\n",
__func__,
jiffies, mindwell_used, scanend);
ss->ss_next = 0; /* reset to beginning */
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
vap->iv_stats.is_scan_active++;
else
vap->iv_stats.is_scan_passive++;
// ic->ic_scan_start(ic); /* notify driver */
goto again;
} else {
/* past here, scandone is ``true'' if not in bg mode */
if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
scandone = 1;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s, "
"[jiffies %lu, dwell min %lu scanend %lu]\n",
__func__, scandone ? "done" : "stopped",
jiffies, mindwell_used, scanend);
/* don't care about bgscan case */
if ((ic->ic_flags & IEEE80211_F_SCAN)
#ifdef QTN_BG_SCAN
|| (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN)
#endif /* QTN_BG_SCAN */
) {
wake_up_interruptible_all(&ic->ic_scan_comp);
}
/*
* Clear the SCAN bit first in case frames are
* pending on the station power save queue. If
* we defer this then the dispatch of the frames
* may generate a request to cancel scanning.
*/
ic->ic_flags &= ~IEEE80211_F_SCAN;
#ifdef QTN_BG_SCAN
ic->ic_flags_qtn &= ~IEEE80211_QTN_BGSCAN;
#endif /* QTN_BG_SCAN */
#if defined(QBMPS_ENABLE)
if ((ic->ic_flags_qtn & IEEE80211_QTN_BMPS) &&
(vap->iv_opmode == IEEE80211_M_STA)) {
/* re-enter power-saving if possible */
ic->ic_pm_reason = IEEE80211_PM_LEVEL_SCAN_STOP;
ieee80211_pm_queue_work(ic);
}
#endif
/*
* Drop out of power save mode when a scan has
* completed. If this scan was prematurely terminated
* because it is a background scan then don't notify
* the ap; we'll either return to scanning after we
* receive the beacon frame or we'll drop out of power
* save mode because the beacon indicates we have frames
* waiting for us.
*/
if (scandone) {
ieee80211_sta_pwrsave(vap, 0);
if ((vap->iv_state == IEEE80211_S_RUN) &&
(vap->iv_opmode == IEEE80211_M_STA)) {
ic->ic_setparam(vap->iv_bss, IEEE80211_PARAM_BEACON_ALLOW,
0, NULL, 0);
}
if (ss->ss_next >= ss->ss_last) {
ieee80211_notify_scan_done(vap);
ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
if (IEEE80211_IS_11NG_40(ic) &&
(ic->ic_opmode == IEEE80211_M_STA))
ieee80211_send_20_40_bss_coex(vap);
}
}
if ((ic->ic_flags_qtn & IEEE80211_QTN_PRINT_CH_INUSE) &&
(ic->ic_opmode == IEEE80211_M_HOSTAP))
ap_list_asl_table(ss);
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;
ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
if (!ieee80211_chanset_scan_finished(ic))
ieee80211_start_chanset_scan(vap, ic->ic_autochan_scan_flags);
}
}
#undef ISCAN_REP
}
/*
* Timer handler to send probe requests after a delay when doing an active
* scan. This is done to allow time for the channel change to complete first.
*/
static void
send_probes(unsigned long arg)
{
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
struct ieee80211vap *vap = ss->ss_vap;
struct net_device *dev = vap->iv_dev;
int i;
/*
* Send a broadcast probe request followed by
* any specified directed probe requests.
* XXX suppress broadcast probe req?
* XXX remove dependence on vap/vap->iv_bss
* XXX move to policy code?
*/
if (vap->iv_bss) {
ieee80211_send_probereq(vap->iv_bss,
vap->iv_myaddr, dev->broadcast,
dev->broadcast,
(u_int8_t *)"", 0,
vap->iv_opt_ie, vap->iv_opt_ie_len);
for (i = 0; i < ss->ss_nssid; i++)
ieee80211_send_probereq(vap->iv_bss,
vap->iv_myaddr, dev->broadcast,
dev->broadcast,
ss->ss_ssid[i].ssid,
ss->ss_ssid[i].len,
vap->iv_opt_ie, vap->iv_opt_ie_len);
}
}
#ifdef IEEE80211_DEBUG
static void
dump_probe_beacon(u_int8_t subtype,
const u_int8_t mac[IEEE80211_ADDR_LEN],
const struct ieee80211_scanparams *sp)
{
printf("[%s] %02x ", ether_sprintf(mac), subtype);
if (sp) {
printf("on chan %u (bss chan %u) ", sp->chan, sp->bchan);
ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
}
printf("\n");
if (sp) {
printf("[%s] caps 0x%x bintval %u erp 0x%x",
ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
if (sp->country != NULL) {
#ifdef __FreeBSD__
printf(" country info %*D",
sp->country[1], sp->country + 2, " ");
#else
int i;
printf(" country info");
for (i = 0; i < sp->country[1]; i++)
printf(" %02x", sp->country[i + 2]);
#endif
}
printf("\n");
}
}
#endif /* IEEE80211_DEBUG */
/*
* Process a beacon or probe response frame.
*/
void
ieee80211_add_scan(struct ieee80211vap *vap,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int rstamp)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
/*
* Frames received during startup are discarded to avoid
* using scan state setup on the initial entry to the timer
* callback. This can occur because the device may enable
* rx prior to our doing the initial channel change in the
* timer routine (we defer the channel change to the timer
* code to simplify locking on linux).
*/
if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
return;
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN) && sp)
dump_probe_beacon(subtype, wh->i_addr2, sp);
#endif
if ((ic->ic_opmode == IEEE80211_M_STA) &&
(ic->ic_flags & IEEE80211_F_SCAN) &&
#ifdef QTN_BG_SCAN
/* For qtn bgscan, probereq is sent by MuC */
((ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0) &&
#endif /* QTN_BG_SCAN */
(subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
(ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS) &&
(ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE)) {
/* Beacon received on a DFS channel, OK to send probe */
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_scan(vap))
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: sending a probe req on DFS channel %3d%c\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
channel_type(ic->ic_curchan));
#endif
if (ic->sta_dfs_info.sta_dfs_strict_mode) {
if ((ss->ss_ops != NULL) &&
ieee80211_is_chan_cac_required(ic->ic_curchan)) {
ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, rstamp);
}
}
send_probes((unsigned long)ss);
} else if (ss->ss_ops != NULL &&
ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, rstamp)) {
#ifdef QTN_BG_SCAN
if (ic->ic_qtn_bgscan.debug_flags >= 4) {
u_int8_t *mac = (u_int8_t *)wh->i_addr2;
u_int8_t ssid[IEEE80211_NWID_LEN+1] = {0};
if (sp->ssid[1] && sp->ssid[1] <= IEEE80211_NWID_LEN) {
memcpy(ssid, sp->ssid + 2, sp->ssid[1]);
}
printk("==> Add scan entry -- chan: %u,"
" mac: %02x:%02x:%02x:%02x:%02x:%02x,"
" ssid: %s <==\n",
sp->chan,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
(char *)ssid);
}
#endif /*QTN_BG_SCAN */
/*
* If we've reached the min dwell time terminate
* the timer so we'll switch to the next channel.
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
#ifdef QTN_BG_SCAN
((ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0) &&
#endif /* QTN_BG_SCAN */
time_after_eq(jiffies, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c min dwell met (%lu > %lu)\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
channel_type(ic->ic_curchan),
jiffies, SCAN_PRIVATE(ss)->ss_chanmindwell);
/*
* XXX
* We want to just kick the timer and still
* process frames until it fires but linux
* will livelock unless we discard frames.
*/
#if 0
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
#else
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
#endif
/* NB: trigger at next clock tick */
mod_timer(&SCAN_PRIVATE(ss)->ss_scan_timer, jiffies);
}
}
}
EXPORT_SYMBOL(ieee80211_add_scan);
/*
* Remove a particular scan entry
*/
void
ieee80211_scan_remove(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_remove(ss, ni);
}
}
/*
* Timeout/age scan cache entries; called from sta timeout
* timer (XXX should be self-contained).
*/
void
_ieee80211_scan_timeout(struct ieee80211com *ic)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL)
ss->ss_ops->scan_age(ss);
}
void ieee80211_scan_timeout(unsigned long arg)
{
struct ieee80211com *ic = (struct ieee80211com *) arg;
if (ic != NULL) {
_ieee80211_scan_timeout(ic);
ic->ic_scan_results_expire.expires = jiffies + ic->ic_scan_results_check * HZ;
add_timer(&ic->ic_scan_results_expire);
}
}
/*
* Mark a scan cache entry after a successful associate.
*/
void
ieee80211_scan_assoc_success(struct ieee80211com *ic, const u_int8_t mac[])
{
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL) {
IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
mac, "%s", __func__);
ss->ss_ops->scan_assoc_success(ss, mac);
}
}
/*
* Demerit a scan cache entry after failing to associate.
*/
void
ieee80211_scan_assoc_fail(struct ieee80211com *ic,
const u_int8_t mac[], int reason)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL) {
IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN, mac,
"%s: reason %u", __func__, reason);
ss->ss_ops->scan_assoc_fail(ss, mac, reason);
}
}
/*
* Iterate over the contents of the scan cache.
*/
int
ieee80211_scan_iterate(struct ieee80211com *ic,
ieee80211_scan_iter_func *f, void *arg)
{
int res = 0;
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL) {
res = ss->ss_ops->scan_iterate(ss, f, arg);
}
return res;
}
static void
scan_saveie(u_int8_t **iep, const u_int8_t *ie)
{
if (ie == NULL) {
if (*iep) {
FREE(*iep, M_DEVBUF);
}
*iep = NULL;
} else {
ieee80211_saveie(iep, ie);
}
}
void
ieee80211_add_scan_entry(struct ieee80211_scan_entry *ise,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int rstamp)
{
if (sp->ssid[1] != 0 &&
(ISPROBE(subtype) || ise->se_ssid[1] == 0)) {
memcpy(ise->se_ssid, sp->ssid, 2 + sp->ssid[1]);
}
memcpy(ise->se_rates, sp->rates,
2 + IEEE80211_SANITISE_RATESIZE(sp->rates[1]));
if (sp->xrates != NULL) {
memcpy(ise->se_xrates, sp->xrates,
2 + IEEE80211_SANITISE_RATESIZE(sp->xrates[1]));
} else {
ise->se_xrates[1] = 0;
}
IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
ise->se_rstamp = rstamp;
memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp));
ise->se_intval = sp->bintval;
ise->se_capinfo = sp->capinfo;
ise->se_chan = sp->rxchan;
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
ise->se_timoff = sp->timoff;
if (sp->tim != NULL) {
const struct ieee80211_tim_ie *tim =
(const struct ieee80211_tim_ie *) sp->tim;
ise->se_dtimperiod = tim->tim_period;
}
scan_saveie(&ise->se_wme_ie, sp->wme);
scan_saveie(&ise->se_wpa_ie, sp->wpa);
scan_saveie(&ise->se_rsn_ie, sp->rsn);
scan_saveie(&ise->se_wsc_ie, sp->wsc);
scan_saveie(&ise->se_ath_ie, sp->ath);
scan_saveie(&ise->se_qtn_ie, sp->qtn);
if (sp->qtn != NULL) {
ise->se_qtn_ie_flags = ((struct ieee80211_ie_qtn *)sp->qtn)->qtn_ie_flags;
ise->se_is_qtn_dev = 1;
} else {
ise->se_qtn_ie_flags = 0;
ise->se_is_qtn_dev = 0;
}
scan_saveie(&ise->se_htcap_ie, sp->htcap);
scan_saveie(&ise->se_htinfo_ie, sp->htinfo);
scan_saveie(&ise->se_vhtcap_ie, sp->vhtcap);
scan_saveie(&ise->se_vhtop_ie, sp->vhtop);
scan_saveie(&ise->se_pairing_ie, sp->pairing_ie);
scan_saveie(&ise->se_bss_load_ie, sp->bssload);
ise->se_ext_role = sp->extender_role;
scan_saveie(&ise->se_ext_bssid_ie, sp->ext_bssid_ie);
ise->local_max_txpwr = sp->local_max_txpwr;
scan_saveie(&ise->se_md_ie, sp->mdie);
}
EXPORT_SYMBOL(ieee80211_add_scan_entry);
static void
ieee80211_scan_set_channel_obssflag(struct ieee80211_scan_state *ss, uint8_t ch, int flag)
{
struct ieee80211com *ic = ss->ss_vap->iv_ic;
struct ap_state *as = ss->ss_priv;
struct ieee80211_channel *chan;
chan = ieee80211_find_channel_by_ieee(ic, ch);
if (chan == NULL)
return;
as->as_obss_chanlayout[ch] |= flag;
}
int
ieee80211_scan_check_secondary_channel(struct ieee80211_scan_state *ss,
struct ieee80211_scan_entry *ise)
{
int bss_bw = ieee80211_get_max_ap_bw(ise);
struct ieee80211vap *vap = ss->ss_vap;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
uint8_t chan_pri;
uint8_t chan_sec;
if (bss_bw <= BW_HT20)
return 0;
ieee80211_find_ht_pri_sec_chan(vap, ise, &chan_pri, &chan_sec);
if (chan_pri == 0)
return 0;
ieee80211_scan_set_channel_obssflag(ss, chan_pri, IEEE80211_OBSS_CHAN_PRI20);
ieee80211_scan_set_channel_obssflag(ss, chan_sec, IEEE80211_OBSS_CHAN_SEC20);
if (bss_bw >= BW_HT80) {
ieee80211_scan_set_channel_obssflag(ss, chan_pri, IEEE80211_OBSS_CHAN_PRI40);
ieee80211_scan_set_channel_obssflag(ss, chan_sec, IEEE80211_OBSS_CHAN_PRI40);
chan = ieee80211_find_channel_by_ieee(ic, chan_pri);
if (chan) {
chan_sec = ieee80211_find_sec40u_chan(chan);
if (chan_sec != 0)
ieee80211_scan_set_channel_obssflag(ss, chan_sec, IEEE80211_OBSS_CHAN_SEC40);
chan_sec = ieee80211_find_sec40l_chan(chan);
if (chan_sec != 0)
ieee80211_scan_set_channel_obssflag(ss, chan_sec, IEEE80211_OBSS_CHAN_SEC40);
}
}
return 0;
}
EXPORT_SYMBOL(ieee80211_scan_check_secondary_channel);
static int
ieee80211_prichan_check_newchan(struct ieee80211_scan_state *ss,
struct ieee80211_channel *chan,
int32_t *max_bsscnt)
{
struct ap_state *as = ss->ss_priv;
struct ieee80211vap *vap = ss->ss_vap;
struct ieee80211com *ic = vap->iv_ic;
uint32_t ch;
int32_t cur_bss;
if (!chan)
return -1;
ch = ieee80211_chan2ieee(ic, chan);
if (!is_channel_valid(ch))
return -1;
if (isset(ic->ic_chan_pri_inactive, ch) || isclr(ic->ic_chan_active, ch))
return -1;
cur_bss = (int32_t)as->as_numbeacons[ch];
if (!IEEE80211_IS_OBSS_CHAN_SECONDARY(as->as_obss_chanlayout[ch])) {
if (cur_bss > *max_bsscnt) {
*max_bsscnt = cur_bss;
return 1;
}
return 0;
}
return -1;
}
/*
* Channel selection methods for a VHT BSS,
* as per IEEE Std 802.11ac 10.39.2, IEEE Std 802.11ac 10.5.3.
* New BSS's primary channel shall not overlap other BSSs' secondary channels.
*/
struct ieee80211_channel *
ieee80211_scan_switch_pri_chan(struct ieee80211_scan_state *ss,
struct ieee80211_channel *chan_pri)
{
struct ieee80211vap *vap = NULL;
struct ieee80211com *ic = NULL;
int cur_bw = BW_INVALID;
struct ieee80211_channel *chan_sec;
uint32_t ch_pri = 0;
uint32_t ch_sec;
int32_t bsscnt = -1;
if (!chan_pri || !ss || !ss->ss_vap || !ss->ss_vap->iv_ic) {
return NULL;
}
vap = ss->ss_vap;
ic = vap->iv_ic;
cur_bw = ieee80211_get_bw(ic);
if (cur_bw >= BW_HT20) {
ch_pri = chan_pri->ic_ieee;
if (ieee80211_prichan_check_newchan(ss, chan_pri, &bsscnt) < 0)
ch_pri = 0;
}
if (cur_bw >= BW_HT40) {
/* we look up operating class to follow different primary channel layouts, esp. 2.4G */
ch_sec = ieee80211_find_sec_chan_by_operating_class(ic,
chan_pri->ic_ieee,
IEEE80211_OC_BEHAV_CHAN_UPPER);
chan_sec = ieee80211_find_channel_by_ieee(ic, ch_sec);
if (ieee80211_prichan_check_newchan(ss, chan_sec, &bsscnt) > 0)
ch_pri = ch_sec;
ch_sec = ieee80211_find_sec_chan_by_operating_class(ic,
chan_pri->ic_ieee,
IEEE80211_OC_BEHAV_CHAN_LOWWER);
chan_sec = ieee80211_find_channel_by_ieee(ic, ch_sec);
if (ieee80211_prichan_check_newchan(ss, chan_sec, &bsscnt) > 0)
ch_pri = ch_sec;
}
if (cur_bw >= BW_HT80) {
ch_sec = ieee80211_find_sec40u_chan(chan_pri);
chan_sec = ieee80211_find_channel_by_ieee(ic, ch_sec);
if (ieee80211_prichan_check_newchan(ss, chan_sec, &bsscnt) > 0)
ch_pri = ch_sec;
ch_sec = ieee80211_find_sec40l_chan(chan_pri);
chan_sec = ieee80211_find_channel_by_ieee(ic, ch_sec);
if (ieee80211_prichan_check_newchan(ss, chan_sec, &bsscnt) > 0)
ch_pri = ch_sec;
}
return ieee80211_find_channel_by_ieee(ic, ch_pri);
}
EXPORT_SYMBOL(ieee80211_scan_switch_pri_chan);
struct ieee80211_channel *
ieee80211_scs_switch_pri_chan(struct ieee80211_scan_state *ss,
struct ieee80211_channel *chan_pri)
{
struct ieee80211_channel *chan;
struct ap_state *as;
struct ap_state *as_bak;
as = (struct ap_state *)ss->ss_scs_priv;
as_bak = ss->ss_priv;
ss->ss_priv = as;
chan = ieee80211_scan_switch_pri_chan(ss, chan_pri);
ss->ss_priv = as_bak;
return chan;
}
EXPORT_SYMBOL(ieee80211_scs_switch_pri_chan);
int
ieee80211_wps_active(uint8_t *wsc_ie)
{
#define IEEE80211_WPS_SELECTED_REGISTRAR 0x1041
uint16_t type;
uint16_t len;
uint8_t *pos;
uint8_t *end;
if (!wsc_ie)
return 0;
pos = wsc_ie;
end = wsc_ie + wsc_ie[1];
pos += (2 + 4);
while (pos < end) {
if (end - pos < 4)
break;
type = get_unaligned_be16(pos);
pos += 2;
len = get_unaligned_be16(pos);
pos += 2;
if (len > end - pos)
break;
if ((type == IEEE80211_WPS_SELECTED_REGISTRAR) && (len == 1))
return 1;
pos += len;
}
return 0;
}
EXPORT_SYMBOL(ieee80211_wps_active);
void
ieee80211_dump_scan_res(struct ieee80211_scan_state *ss)
{
#define IEEE80211_BSS_CAPA_STR_LEN 30
struct ieee80211vap *vap;
struct sta_table *st;
struct sta_entry *se, *next;
struct ieee80211_scan_entry *ise;
char ssid[IEEE80211_NWID_LEN + 1];
char bss_capa[IEEE80211_BSS_CAPA_STR_LEN];
char *pos;
char *end;
int len;
if (!ss)
return;
vap = ss->ss_vap;
st = ss->ss_priv;
if (!ieee80211_msg(vap, IEEE80211_MSG_SCAN))
return;
printk("%-18s %-33s %-7s %-25s %-5s\n",
"BSSID", "SSID", "Channel", "BSS Capabilities", "RSSI");
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
ise = &se->base;
memset(ssid, 0, sizeof(ssid));
memcpy(ssid, &ise->se_ssid[2], MIN(sizeof(ssid), ise->se_ssid[1]));
len = 0;
pos = bss_capa;
end = bss_capa + IEEE80211_BSS_CAPA_STR_LEN;
memset(bss_capa, 0, sizeof(bss_capa));
if (ise->se_capinfo & IEEE80211_CAPINFO_IBSS) {
len = snprintf(pos, end - pos, "IBSS");
pos += len;
} else if (ise->se_capinfo & IEEE80211_CAPINFO_ESS) {
len = snprintf(pos, end - pos, "ESS");
pos += len;
}
if (ise->se_wpa_ie) {
len = snprintf(pos, end - pos, "|WPA");
pos += len;
}
if (ise->se_rsn_ie) {
len = snprintf(pos, end - pos, "|RSN");
pos += len;
}
if (ieee80211_wps_active(ise->se_wsc_ie))
snprintf(pos, end - pos, "|WPS_ACTIVE");
else if (ise->se_wsc_ie)
snprintf(pos, end - pos, "|WPS");
printk("%-18pM %-33s %-7u %-25s %-5d\n",
ise->se_bssid,
ssid,
ise->se_chan->ic_ieee,
bss_capa,
ise->se_rssi);
}
}
EXPORT_SYMBOL(ieee80211_dump_scan_res);
/*
* Flush the contents of the scan cache.
*/
void
ieee80211_scan_flush(struct ieee80211com *ic)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss->ss_ops != NULL) {
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
"%s\n", __func__);
ss->ss_ops->scan_flush(ss);
}
}
EXPORT_SYMBOL(ieee80211_scan_flush);
/*
* Refresh scan module channel list
* In cases where ieee80211_scan_pickchannel is called
* without initiating proper scan from ap scan module,
* the channel list can be out of sync between QDRV and scan_ap modules
*/
void ieee80211_scan_refresh_scan_module_chan_list(struct ieee80211com *ic, struct ieee80211vap *vap)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_ASSERT(ic);
if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
printk(KERN_WARNING "scan state structure not attached or not initialized\n");
return;
}
if (ss->ss_ops->scan_start == NULL) {
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
"%s: scan module does not scan start, "
"opmode %s\n", __func__, ss->ss_vap->iv_opmode);
return;
}
ss->ss_ops->scan_start(ss, vap);
}
EXPORT_SYMBOL(ieee80211_scan_refresh_scan_module_chan_list);
/*
* Check the scan cache for an ap/channel to use
*/
struct ieee80211_channel *
ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_ASSERT(ic);
if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
printk(KERN_WARNING "scan state structure not attached or not initialized\n");
return NULL;
}
if (ss->ss_ops->scan_pickchan == NULL) {
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
"%s: scan module does not support picking a channel, "
"opmode %s\n", __func__, ss->ss_vap->iv_opmode);
return NULL;
}
return ss->ss_ops->scan_pickchan(ic, ss, flags);
}
EXPORT_SYMBOL(ieee80211_scan_pickchannel);
int ieee80211_get_type_of_neighborhood(struct ieee80211com *ic)
{
if (ic->ic_neighbor_count < 0)
return IEEE80211_NEIGHBORHOOD_TYPE_UNKNOWN;
else if (ic->ic_neighbor_count <= ic->ic_neighbor_cnt_sparse)
return IEEE80211_NEIGHBORHOOD_TYPE_SPARSE;
else if (ic->ic_neighbor_count <= ic->ic_neighbor_cnt_dense)
return IEEE80211_NEIGHBORHOOD_TYPE_DENSE;
else
return IEEE80211_NEIGHBORHOOD_TYPE_VERY_DENSE;
}
char * ieee80211_neighborhood_type2str(int type)
{
char *str = "Unknown";
switch (type) {
case IEEE80211_NEIGHBORHOOD_TYPE_SPARSE:
str = "Sparse";
break;
case IEEE80211_NEIGHBORHOOD_TYPE_DENSE:
str = "Dense";
break;
case IEEE80211_NEIGHBORHOOD_TYPE_VERY_DENSE:
str = "Very dense";
break;
default:
break;
}
return str;
}
void ieee80211_check_type_of_neighborhood(struct ieee80211com *ic)
{
struct ieee80211_scan_state *ss = ic->ic_scan;
struct ap_state *as = ss->ss_priv;
struct ap_scan_entry *apse;
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
int i;
ic->ic_neighbor_count = 0;
if (ss->ss_vap->iv_opmode == IEEE80211_M_HOSTAP) {
for (i = 0; i < IEEE80211_CHAN_MAX; i++) {
TAILQ_FOREACH(apse, &as->as_scan_list[i].asl_head, ase_list) {
ic->ic_neighbor_count++;
}
}
} else if (ss->ss_vap->iv_opmode == IEEE80211_M_STA) {
TAILQ_FOREACH(se, &st->st_entry, se_list) {
ic->ic_neighbor_count++;
}
}
IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
"%s: found %d neighborhood APs\n", __func__, ic->ic_neighbor_count);
}