mirror of
https://github.com/proski/madwifi
synced 2024-11-22 14:31:22 +03:00
4d5de4b5a2
time in ms, or the number of beacons for the beacon miss alarm. If we haven't had a beacon in this period, the alarm is sounded and action (i.e. roaming) is taken. This patch changes the storage of the beacon miss threshold to integral beacon count but corrects the previous intent. The default beacon miss alarm is going to be 850ms which is half way between the two hard coded limits that were there before. The old hard coded limits assumed that the beacon interval was 100ms and set the limit to 10 (1000ms) for software beacon miss timer and 7 for hardware beacon miss timer. The new default is 850ms (the midpoint between the two previous defaults). This value is rounded up to the next nearest beacon interval. There is no upper bound on the beacon miss threshold specified, since it may need to be wildly inflated with 25ms beacon interval, or wildly reduced with 1000ms beacon interval. The minimum is still 2 beacons, regardless of the beacon interval. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3314 0192ed92-7a03-0410-a25b-9323aeb14dbd
1913 lines
55 KiB
C
1913 lines
55 KiB
C
/*-
|
|
* Copyright (c) 2001 Atsushi Onoe
|
|
* 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$
|
|
*/
|
|
#ifndef EXPORT_SYMTAB
|
|
#define EXPORT_SYMTAB
|
|
#endif
|
|
|
|
/*
|
|
* IEEE 802.11 generic handler
|
|
*/
|
|
#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/rtnetlink.h> /* XXX for rtnl_lock */
|
|
|
|
#include "if_media.h"
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/if_athproto.h>
|
|
|
|
const char *ieee80211_phymode_name[] = {
|
|
"auto", /* IEEE80211_MODE_AUTO */
|
|
"11a", /* IEEE80211_MODE_11A */
|
|
"11b", /* IEEE80211_MODE_11B */
|
|
"11g", /* IEEE80211_MODE_11G */
|
|
"FH", /* IEEE80211_MODE_FH */
|
|
"turboA", /* IEEE80211_MODE_TURBO_A */
|
|
"turboG", /* IEEE80211_MODE_TURBO_G */
|
|
"staticTurboA", /* IEEE80211_MODE_TURBO_G */
|
|
};
|
|
EXPORT_SYMBOL(ieee80211_phymode_name);
|
|
|
|
static void ieee80211com_media_status(struct net_device*, struct ifmediareq *);
|
|
static int ieee80211com_media_change(struct net_device *);
|
|
static struct net_device_stats *ieee80211_getstats(struct net_device *);
|
|
static int ieee80211_change_mtu(struct net_device *, int);
|
|
static void ieee80211_set_multicast_list(struct net_device *);
|
|
static void ieee80211_expire_dfs_excl_timer(unsigned long);
|
|
|
|
MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state");
|
|
|
|
/*
|
|
* Country Code Table for code-to-string conversion.
|
|
*/
|
|
struct country_code_to_string {
|
|
u_int16_t iso_code;
|
|
const char *iso_name;
|
|
};
|
|
|
|
/*
|
|
* XXX: Ugly, since these must match defines in other modules.
|
|
*/
|
|
#define CTRY_DEBUG 0x1ff /* debug */
|
|
#define CTRY_DEFAULT 0 /* default */
|
|
|
|
static const struct country_code_to_string country_strings[] = {
|
|
{CTRY_DEBUG, "DB"},
|
|
{CTRY_DEFAULT, "NA"},
|
|
{CTRY_ALBANIA, "AL"},
|
|
{CTRY_ALGERIA, "DZ"},
|
|
{CTRY_ARGENTINA, "AR"},
|
|
{CTRY_ARMENIA, "AM"},
|
|
{CTRY_AUSTRALIA, "AU"},
|
|
{CTRY_AUSTRIA, "AT"},
|
|
{CTRY_AZERBAIJAN, "AZ"},
|
|
{CTRY_BAHRAIN, "BH"},
|
|
{CTRY_BELARUS, "BY"},
|
|
{CTRY_BELGIUM, "BE"},
|
|
{CTRY_BELIZE, "BZ"},
|
|
{CTRY_BOLIVIA, "BO"},
|
|
{CTRY_BRAZIL, "BR"},
|
|
{CTRY_BRUNEI_DARUSSALAM, "BN"},
|
|
{CTRY_BULGARIA, "BG"},
|
|
{CTRY_CANADA, "CA"},
|
|
{CTRY_CHILE, "CL"},
|
|
{CTRY_CHINA, "CN"},
|
|
{CTRY_COLOMBIA, "CO"},
|
|
{CTRY_COSTA_RICA, "CR"},
|
|
{CTRY_CROATIA, "HR"},
|
|
{CTRY_CYPRUS, "CY"},
|
|
{CTRY_CZECH, "CZ"},
|
|
{CTRY_DENMARK, "DK"},
|
|
{CTRY_DOMINICAN_REPUBLIC, "DO"},
|
|
{CTRY_ECUADOR, "EC"},
|
|
{CTRY_EGYPT, "EG"},
|
|
{CTRY_EL_SALVADOR, "SV"},
|
|
{CTRY_ESTONIA, "EE"},
|
|
{CTRY_FINLAND, "FI"},
|
|
{CTRY_FRANCE, "FR"},
|
|
{CTRY_FRANCE2, "F2"},
|
|
{CTRY_GEORGIA, "GE"},
|
|
{CTRY_GERMANY, "DE"},
|
|
{CTRY_GREECE, "GR"},
|
|
{CTRY_GUATEMALA, "GT"},
|
|
{CTRY_HONDURAS, "HN"},
|
|
{CTRY_HONG_KONG, "HK"},
|
|
{CTRY_HUNGARY, "HU"},
|
|
{CTRY_ICELAND, "IS"},
|
|
{CTRY_INDIA, "IN"},
|
|
{CTRY_INDONESIA, "ID"},
|
|
{CTRY_IRAN, "IR"},
|
|
{CTRY_IRELAND, "IE"},
|
|
{CTRY_ISRAEL, "IL"},
|
|
{CTRY_ITALY, "IT"},
|
|
{CTRY_JAPAN, "JP"},
|
|
{CTRY_JAPAN1, "J1"},
|
|
{CTRY_JAPAN2, "J2"},
|
|
{CTRY_JAPAN3, "J3"},
|
|
{CTRY_JAPAN4, "J4"},
|
|
{CTRY_JAPAN5, "J5"},
|
|
{CTRY_JAPAN7, "JP"},
|
|
{CTRY_JAPAN6, "JP"},
|
|
{CTRY_JAPAN8, "JP"},
|
|
{CTRY_JAPAN9, "JP"},
|
|
{CTRY_JAPAN10, "JP"},
|
|
{CTRY_JAPAN11, "JP"},
|
|
{CTRY_JAPAN12, "JP"},
|
|
{CTRY_JAPAN13, "JP"},
|
|
{CTRY_JAPAN14, "JP"},
|
|
{CTRY_JAPAN15, "JP"},
|
|
{CTRY_JAPAN16, "JP"},
|
|
{CTRY_JAPAN17, "JP"},
|
|
{CTRY_JAPAN18, "JP"},
|
|
{CTRY_JAPAN19, "JP"},
|
|
{CTRY_JAPAN20, "JP"},
|
|
{CTRY_JAPAN21, "JP"},
|
|
{CTRY_JAPAN22, "JP"},
|
|
{CTRY_JAPAN23, "JP"},
|
|
{CTRY_JAPAN24, "JP"},
|
|
{CTRY_JAPAN25, "JP"},
|
|
{CTRY_JAPAN26, "JP"},
|
|
{CTRY_JAPAN27, "JP"},
|
|
{CTRY_JAPAN28, "JP"},
|
|
{CTRY_JAPAN29, "JP"},
|
|
{CTRY_JAPAN30, "JP"},
|
|
{CTRY_JAPAN31, "JP"},
|
|
{CTRY_JAPAN32, "JP"},
|
|
{CTRY_JAPAN33, "JP"},
|
|
{CTRY_JAPAN34, "JP"},
|
|
{CTRY_JAPAN35, "JP"},
|
|
{CTRY_JAPAN36, "JP"},
|
|
{CTRY_JAPAN37, "JP"},
|
|
{CTRY_JAPAN38, "JP"},
|
|
{CTRY_JAPAN39, "JP"},
|
|
{CTRY_JAPAN40, "JP"},
|
|
{CTRY_JAPAN41, "JP"},
|
|
{CTRY_JAPAN42, "JP"},
|
|
{CTRY_JAPAN43, "JP"},
|
|
{CTRY_JAPAN44, "JP"},
|
|
{CTRY_JAPAN45, "JP"},
|
|
{CTRY_JAPAN46, "JP"},
|
|
{CTRY_JAPAN47, "JP"},
|
|
{CTRY_JAPAN48, "JP"},
|
|
{CTRY_JORDAN, "JO"},
|
|
{CTRY_KAZAKHSTAN, "KZ"},
|
|
{CTRY_KOREA_NORTH, "KP"},
|
|
{CTRY_KOREA_ROC, "KR"},
|
|
{CTRY_KOREA_ROC2, "K2"},
|
|
{CTRY_KUWAIT, "KW"},
|
|
{CTRY_LATVIA, "LV"},
|
|
{CTRY_LEBANON, "LB"},
|
|
{CTRY_LIECHTENSTEIN, "LI"},
|
|
{CTRY_LITHUANIA, "LT"},
|
|
{CTRY_LUXEMBOURG, "LU"},
|
|
{CTRY_MACAU, "MO"},
|
|
{CTRY_MACEDONIA, "MK"},
|
|
{CTRY_MALAYSIA, "MY"},
|
|
{CTRY_MEXICO, "MX"},
|
|
{CTRY_MONACO, "MC"},
|
|
{CTRY_MOROCCO, "MA"},
|
|
{CTRY_NETHERLANDS, "NL"},
|
|
{CTRY_NEW_ZEALAND, "NZ"},
|
|
{CTRY_NORWAY, "NO"},
|
|
{CTRY_OMAN, "OM"},
|
|
{CTRY_PAKISTAN, "PK"},
|
|
{CTRY_PANAMA, "PA"},
|
|
{CTRY_PERU, "PE"},
|
|
{CTRY_PHILIPPINES, "PH"},
|
|
{CTRY_POLAND, "PL"},
|
|
{CTRY_PORTUGAL, "PT"},
|
|
{CTRY_PUERTO_RICO, "PR"},
|
|
{CTRY_QATAR, "QA"},
|
|
{CTRY_ROMANIA, "RO"},
|
|
{CTRY_RUSSIA, "RU"},
|
|
{CTRY_SAUDI_ARABIA, "SA"},
|
|
{CTRY_SINGAPORE, "SG"},
|
|
{CTRY_SLOVAKIA, "SK"},
|
|
{CTRY_SLOVENIA, "SI"},
|
|
{CTRY_SOUTH_AFRICA, "ZA"},
|
|
{CTRY_SPAIN, "ES"},
|
|
{CTRY_SWEDEN, "SE"},
|
|
{CTRY_SWITZERLAND, "CH"},
|
|
{CTRY_SYRIA, "SY"},
|
|
{CTRY_TAIWAN, "TW"},
|
|
{CTRY_THAILAND, "TH"},
|
|
{CTRY_TRINIDAD_Y_TOBAGO, "TT"},
|
|
{CTRY_TUNISIA, "TN"},
|
|
{CTRY_TURKEY, "TR"},
|
|
{CTRY_UKRAINE, "UA"},
|
|
{CTRY_UAE, "AE"},
|
|
{CTRY_UNITED_KINGDOM, "GB"},
|
|
{CTRY_UNITED_STATES, "US"},
|
|
{CTRY_UNITED_STATES_FCC49, "US"},
|
|
{CTRY_URUGUAY, "UY"},
|
|
{CTRY_UZBEKISTAN, "UZ"},
|
|
{CTRY_VENEZUELA, "VE"},
|
|
{CTRY_VIET_NAM, "VN"},
|
|
{CTRY_YEMEN, "YE"},
|
|
{CTRY_ZIMBABWE, "ZW"}
|
|
};
|
|
|
|
int
|
|
ieee80211_ifattach(struct ieee80211com *ic)
|
|
{
|
|
struct net_device *dev = ic->ic_dev;
|
|
struct ieee80211_channel *c;
|
|
struct ifmediareq imr;
|
|
int i;
|
|
|
|
_MOD_INC_USE(THIS_MODULE, return -ENODEV);
|
|
|
|
/*
|
|
* Pick an initial operating mode until we have a vap
|
|
* created to lock it down correctly. This is only
|
|
* drivers have something defined for configuring the
|
|
* hardware at startup.
|
|
*/
|
|
ic->ic_opmode = IEEE80211_M_STA; /* everyone supports this */
|
|
|
|
/*
|
|
* Fill in 802.11 available channel set, mark
|
|
* all available channels as active, and pick
|
|
* a default channel if not already specified.
|
|
*/
|
|
KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
|
|
("invalid number of channels specified: %u", ic->ic_nchans));
|
|
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
|
|
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
KASSERT(c->ic_flags != 0, ("channel with no flags"));
|
|
KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX,
|
|
("channel with bogus ieee number %u", c->ic_ieee));
|
|
setbit(ic->ic_chan_avail, c->ic_ieee);
|
|
|
|
/* Identify mode capabilities. */
|
|
if (IEEE80211_IS_CHAN_A(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_11A;
|
|
if (IEEE80211_IS_CHAN_B(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_11B;
|
|
if (IEEE80211_IS_CHAN_PUREG(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_11G;
|
|
if (IEEE80211_IS_CHAN_FHSS(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_FH;
|
|
if (IEEE80211_IS_CHAN_108A(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
|
|
if (IEEE80211_IS_CHAN_108G(c))
|
|
ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
|
|
}
|
|
/* Initialize candidate channels to all available */
|
|
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
|
|
sizeof(ic->ic_chan_avail));
|
|
/* update Supported Channels information element */
|
|
ieee80211_build_sc_ie(ic);
|
|
/* Validate ic->ic_curmode */
|
|
if ((ic->ic_modecaps & (1 << ic->ic_curmode)) == 0)
|
|
ic->ic_curmode = IEEE80211_MODE_AUTO;
|
|
/*
|
|
* When 11g is supported, force the rate set to
|
|
* include basic rates suitable for a mixed b/g bss.
|
|
*/
|
|
if (ic->ic_modecaps & (1 << IEEE80211_MODE_11G))
|
|
ieee80211_set11gbasicrates(
|
|
&ic->ic_sup_rates[IEEE80211_MODE_11G],
|
|
IEEE80211_MODE_11G);
|
|
|
|
/* Setup initial channel settings */
|
|
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
|
|
/* Arbitrarily pick the first channel */
|
|
ic->ic_curchan = &ic->ic_channels[0];
|
|
|
|
/* Enable marking of dfs by default */
|
|
ic->ic_flags_ext |= IEEE80211_FEXT_MARKDFS;
|
|
|
|
/* Enable WME by default, if we're capable. */
|
|
if (ic->ic_caps & IEEE80211_C_WME)
|
|
ic->ic_flags |= IEEE80211_F_WME;
|
|
(void) ieee80211_setmode(ic, ic->ic_curmode);
|
|
|
|
/* Store default beacon interval, as nec. */
|
|
if (ic->ic_lintval == 0)
|
|
ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT;
|
|
|
|
/* We store the beacon miss threshold in integral number of beacons,
|
|
* to keep the calculations on the critical path simple. */
|
|
if (ic->ic_bmissthreshold == 0) {
|
|
ic->ic_bmissthreshold = howmany(roundup(
|
|
IEEE80211_MS_TO_TU(IEEE80211_BMISSTHRESH_DEFAULT_MS),
|
|
ic->ic_lintval), ic->ic_lintval);
|
|
}
|
|
|
|
IEEE80211_LOCK_INIT(ic, "ieee80211com");
|
|
IEEE80211_VAPS_LOCK_INIT(ic, "ieee80211com_vaps");
|
|
TAILQ_INIT(&ic->ic_vaps);
|
|
|
|
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
|
|
ic->ic_txpowlimit = IEEE80211_TXPOWER_MIN;
|
|
ic->ic_newtxpowlimit = IEEE80211_TXPOWER_MAX;
|
|
|
|
init_timer(&ic->ic_dfs_excl_timer);
|
|
ic->ic_dfs_excl_timer.function =
|
|
ieee80211_expire_dfs_excl_timer;
|
|
ic->ic_dfs_excl_timer.data = (unsigned long) ic;
|
|
|
|
ieee80211_crypto_attach(ic);
|
|
ieee80211_node_attach(ic);
|
|
ieee80211_power_attach(ic);
|
|
ieee80211_proto_attach(ic);
|
|
ieee80211_scan_attach(ic);
|
|
|
|
ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps,
|
|
ieee80211com_media_change, ieee80211com_media_status);
|
|
ieee80211com_media_status(dev, &imr);
|
|
ifmedia_set(&ic->ic_media, imr.ifm_active);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_ifattach);
|
|
|
|
void
|
|
ieee80211_ifdetach(struct ieee80211com *ic)
|
|
{
|
|
struct ieee80211vap *vap;
|
|
|
|
rtnl_lock();
|
|
while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
|
|
ic->ic_vap_delete(vap);
|
|
rtnl_unlock();
|
|
|
|
del_timer(&ic->ic_dfs_excl_timer);
|
|
ieee80211_scan_detach(ic);
|
|
ieee80211_proto_detach(ic);
|
|
ieee80211_crypto_detach(ic);
|
|
ieee80211_power_detach(ic);
|
|
ieee80211_node_detach(ic);
|
|
ifmedia_removeall(&ic->ic_media);
|
|
|
|
IEEE80211_VAPS_LOCK_DESTROY(ic);
|
|
IEEE80211_LOCK_DESTROY(ic);
|
|
|
|
_MOD_DEC_USE(THIS_MODULE);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_ifdetach);
|
|
|
|
int
|
|
ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
|
|
const char *name, int opmode, int flags)
|
|
{
|
|
#define IEEE80211_C_OPMODE \
|
|
(IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
|
|
IEEE80211_C_MONITOR)
|
|
struct ieee80211vap *vap = dev->priv;
|
|
struct net_device *parent = ic->ic_dev;
|
|
int err;
|
|
|
|
if (name != NULL) { /* XXX */
|
|
if (strchr(name, '%')) {
|
|
if ((err = dev_alloc_name(dev, name)) < 0) {
|
|
printk(KERN_ERR "can't alloc name %s\n", name);
|
|
return err;
|
|
}
|
|
} else
|
|
strncpy(dev->name, name, sizeof(dev->name));
|
|
}
|
|
|
|
dev->get_stats = ieee80211_getstats;
|
|
dev->open = ieee80211_open;
|
|
dev->stop = ieee80211_stop;
|
|
dev->hard_start_xmit = ieee80211_hardstart;
|
|
dev->set_multicast_list = ieee80211_set_multicast_list;
|
|
#if 0
|
|
dev->set_mac_address = ieee80211_set_mac_address;
|
|
#endif
|
|
dev->change_mtu = ieee80211_change_mtu;
|
|
dev->tx_queue_len = 0; /* NB: bypass queuing */
|
|
dev->hard_header_len = parent->hard_header_len;
|
|
/*
|
|
* The caller is assumed to allocate the device with
|
|
* alloc_etherdev or similar so we arrange for the
|
|
* space to be reclaimed accordingly.
|
|
*/
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
/* In 2.4 things are done differently... */
|
|
dev->features |= NETIF_F_DYNALLOC;
|
|
#else
|
|
dev->destructor = free_netdev;
|
|
#endif
|
|
|
|
vap->iv_ic = ic;
|
|
vap->iv_dev = dev; /* back pointer */
|
|
vap->iv_xrvap = NULL;
|
|
vap->iv_flags = ic->ic_flags; /* propagate common flags */
|
|
vap->iv_flags_ext = ic->ic_flags_ext;
|
|
vap->iv_ath_cap = ic->ic_ath_cap;
|
|
vap->iv_mcast_rate = 1000; /* Default multicast traffic to lowest rate of 1Mbps */
|
|
|
|
#ifdef ATH_SUPERG_XR
|
|
/*
|
|
* Setup XR VAP specific flags.
|
|
* link the XR VAP to its normal val.
|
|
*/
|
|
if (flags & IEEE80211_VAP_XR) {
|
|
vap->iv_flags |= IEEE80211_F_XR; /* propagate common flags and add XR flag */
|
|
vap->iv_mcast_rate = 256; /* Default multicast rate to lowest possible 256 kbps */
|
|
}
|
|
#endif
|
|
|
|
vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
|
|
switch (opmode) {
|
|
case IEEE80211_M_STA:
|
|
/* WDS/Repeater */
|
|
if (flags & IEEE80211_NO_STABEACONS)
|
|
vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS;
|
|
break;
|
|
case IEEE80211_M_IBSS:
|
|
vap->iv_caps |= IEEE80211_C_IBSS;
|
|
vap->iv_ath_cap &= ~IEEE80211_ATHC_XR;
|
|
break;
|
|
case IEEE80211_M_AHDEMO:
|
|
vap->iv_caps |= IEEE80211_C_AHDEMO;
|
|
vap->iv_ath_cap &= ~IEEE80211_ATHC_XR;
|
|
break;
|
|
case IEEE80211_M_HOSTAP:
|
|
vap->iv_caps |= IEEE80211_C_HOSTAP;
|
|
vap->iv_ath_cap &= ~IEEE80211_ATHC_TURBOP;
|
|
if ((vap->iv_flags & IEEE80211_VAP_XR) == 0)
|
|
vap->iv_ath_cap &= ~IEEE80211_ATHC_XR;
|
|
break;
|
|
case IEEE80211_M_MONITOR:
|
|
vap->iv_caps |= IEEE80211_C_MONITOR;
|
|
vap->iv_ath_cap &= ~(IEEE80211_ATHC_XR | IEEE80211_ATHC_TURBOP);
|
|
break;
|
|
case IEEE80211_M_WDS:
|
|
vap->iv_caps |= IEEE80211_C_WDS;
|
|
vap->iv_ath_cap &= ~(IEEE80211_ATHC_XR | IEEE80211_ATHC_TURBOP);
|
|
vap->iv_flags_ext |= IEEE80211_FEXT_WDS;
|
|
break;
|
|
}
|
|
vap->iv_opmode = opmode;
|
|
IEEE80211_INIT_TQUEUE(&vap->iv_stajoin1tq, ieee80211_sta_join1_tasklet, vap);
|
|
|
|
vap->iv_chanchange_count = 0;
|
|
|
|
/* Enable various functionality by default, if we're capable. */
|
|
#ifdef ATH_WME /* Not yet the default */
|
|
if (vap->iv_caps & IEEE80211_C_WME)
|
|
vap->iv_flags |= IEEE80211_F_WME;
|
|
#endif
|
|
if (vap->iv_caps & IEEE80211_C_FF)
|
|
vap->iv_flags |= IEEE80211_F_FF;
|
|
/* NB: Background scanning only makes sense for station mode right now */
|
|
if (ic->ic_opmode == IEEE80211_M_STA &&
|
|
(vap->iv_caps & IEEE80211_C_BGSCAN))
|
|
vap->iv_flags |= IEEE80211_F_BGSCAN;
|
|
|
|
vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT;
|
|
vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is OK */
|
|
|
|
vap->iv_monitor_crc_errors = 0;
|
|
vap->iv_monitor_phy_errors = 0;
|
|
|
|
IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr);
|
|
IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr);
|
|
/* NB: Defer setting dev_addr so driver can override */
|
|
|
|
ieee80211_crypto_vattach(vap);
|
|
ieee80211_node_vattach(vap);
|
|
ieee80211_power_vattach(vap);
|
|
ieee80211_proto_vattach(vap);
|
|
ieee80211_scan_vattach(vap);
|
|
ieee80211_vlan_vattach(vap);
|
|
ieee80211_ioctl_vattach(vap);
|
|
|
|
return 1;
|
|
#undef IEEE80211_C_OPMODE
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_vap_setup);
|
|
|
|
int
|
|
ieee80211_vap_attach(struct ieee80211vap *vap,
|
|
ifm_change_cb_t media_change, ifm_stat_cb_t media_status)
|
|
{
|
|
struct net_device *dev = vap->iv_dev;
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct ifmediareq imr;
|
|
|
|
ieee80211_node_latevattach(vap); /* XXX: move into vattach */
|
|
ieee80211_power_latevattach(vap); /* XXX: move into vattach */
|
|
|
|
memset(vap->wds_mac, 0x00, IEEE80211_ADDR_LEN);
|
|
|
|
(void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps,
|
|
media_change, media_status);
|
|
ieee80211_media_status(dev, &imr);
|
|
ifmedia_set(&vap->iv_media, imr.ifm_active);
|
|
|
|
IEEE80211_LOCK_IRQ(ic);
|
|
TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
|
|
IEEE80211_UNLOCK_IRQ(ic);
|
|
|
|
IEEE80211_ADDR_COPY(dev->dev_addr, vap->iv_myaddr);
|
|
|
|
#ifdef ATH_SUPERG_XR
|
|
/* Do not register XR VAP device with OS. */
|
|
if (vap->iv_flags & IEEE80211_F_XR)
|
|
return 0;
|
|
#endif
|
|
|
|
ieee80211_scanner_get(vap->iv_opmode, 1);
|
|
|
|
/* NB: rtnl_lock is held on entry, so don't use register_netdev */
|
|
if (register_netdevice(dev)) {
|
|
printk(KERN_ERR "%s: unable to register device\n", dev->name);
|
|
return 0;
|
|
}
|
|
|
|
/* SysFS needs to be initialised after the device, as it uses the
|
|
* device koject */
|
|
ieee80211_virtfs_latevattach(vap);
|
|
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_vap_attach);
|
|
|
|
void
|
|
ieee80211_vap_detach(struct ieee80211vap *vap)
|
|
{
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct net_device *dev = vap->iv_dev;
|
|
|
|
IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
|
|
IEEE80211_LOCK_IRQ(ic);
|
|
TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
|
|
if (TAILQ_EMPTY(&ic->ic_vaps)) /* reset to supported mode */
|
|
ic->ic_opmode = IEEE80211_M_STA;
|
|
IEEE80211_UNLOCK_IRQ(ic);
|
|
|
|
ifmedia_removeall(&vap->iv_media);
|
|
|
|
ieee80211_virtfs_vdetach(vap);
|
|
ieee80211_proc_cleanup(vap);
|
|
ieee80211_ioctl_vdetach(vap);
|
|
ieee80211_vlan_vdetach(vap);
|
|
ieee80211_scan_vdetach(vap);
|
|
ieee80211_proto_vdetach(vap);
|
|
ieee80211_crypto_vdetach(vap);
|
|
ieee80211_power_vdetach(vap);
|
|
ieee80211_node_vdetach(vap);
|
|
|
|
#ifdef ATH_SUPERG_XR
|
|
/* XR VAP is not registered. */
|
|
if (!(vap->iv_flags & IEEE80211_F_XR))
|
|
#endif
|
|
/* NB: rtnl_lock is held on entry so don't use unregister_netdev */
|
|
unregister_netdevice(dev);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_vap_detach);
|
|
|
|
/*
|
|
* Convert MHz frequency to IEEE channel number.
|
|
*/
|
|
u_int
|
|
ieee80211_mhz2ieee(u_int freq, u_int flags)
|
|
{
|
|
if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
|
|
if (freq == 2484) /* Japan */
|
|
return 14;
|
|
if ((freq >= 2412) && (freq < 2484)) /* don't number non-IEEE channels */
|
|
return (freq - 2407) / 5;
|
|
return 0;
|
|
} else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */
|
|
if ((freq >= 5150) && (freq <= 5825)) /* don't number non-IEEE channels */
|
|
return (freq - 5000) / 5;
|
|
return 0;
|
|
} else {
|
|
/* Something is fishy, don't do anything */
|
|
return 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_mhz2ieee);
|
|
|
|
/*
|
|
* Convert channel to IEEE channel number.
|
|
*/
|
|
u_int
|
|
ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c)
|
|
{
|
|
if (c == NULL) {
|
|
if_printf(ic->ic_dev, "invalid channel (NULL)\n");
|
|
return 0; /* XXX */
|
|
}
|
|
return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_chan2ieee);
|
|
|
|
/*
|
|
* Convert IEEE channel number to MHz frequency.
|
|
*/
|
|
u_int
|
|
ieee80211_ieee2mhz(u_int chan, u_int flags)
|
|
{
|
|
if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
|
|
if (chan == 14)
|
|
return 2484;
|
|
if (chan < 14)
|
|
return 2407 + chan * 5;
|
|
else
|
|
return 2512 + ((chan - 15) * 20);
|
|
} else if (flags & IEEE80211_CHAN_5GHZ) /* 5Ghz band */
|
|
return 5000 + (chan * 5);
|
|
else { /* either, guess */
|
|
if (chan == 14)
|
|
return 2484;
|
|
if (chan < 14) /* 0-13 */
|
|
return 2407 + chan * 5;
|
|
if (chan < 27) /* 15-26 */
|
|
return 2512 + ((chan - 15) * 20);
|
|
return 5000 + (chan * 5);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_ieee2mhz);
|
|
|
|
/*
|
|
* Locate a channel given a frequency+flags. We cache
|
|
* the previous lookup to optimize swithing between two
|
|
* channels--as happens with dynamic turbo.
|
|
*/
|
|
struct ieee80211_channel *
|
|
ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
|
|
{
|
|
struct ieee80211_channel *c;
|
|
int i;
|
|
|
|
/* Brute force search */
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
if (c->ic_freq == freq &&
|
|
(flags == 0 ||
|
|
(c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags))
|
|
return c;
|
|
}
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_find_channel);
|
|
|
|
/*
|
|
* Setup the media data structures according to the channel and
|
|
* rate tables. This must be called by the driver after
|
|
* ieee80211_attach and before most anything else.
|
|
*/
|
|
int
|
|
ieee80211_media_setup(struct ieee80211com *ic,
|
|
struct ifmedia *media, u_int32_t caps,
|
|
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
|
|
{
|
|
#define ADD(_media, _s, _o) \
|
|
ifmedia_add(_media, IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
|
|
int i, j, mode, rate, maxrate, mword, mopt, r;
|
|
struct ieee80211_rateset *rs;
|
|
struct ieee80211_rateset allrates;
|
|
|
|
/* Fill in media characteristics. */
|
|
ifmedia_init(media, 0, media_change, media_stat);
|
|
maxrate = 0;
|
|
memset(&allrates, 0, sizeof(allrates));
|
|
|
|
for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
|
|
static const u_int mopts[] = {
|
|
IFM_AUTO,
|
|
IFM_IEEE80211_11A,
|
|
IFM_IEEE80211_11B,
|
|
IFM_IEEE80211_11G,
|
|
IFM_IEEE80211_FH,
|
|
IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
|
|
IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
|
|
};
|
|
if ((ic->ic_modecaps & (1 << mode)) == 0)
|
|
continue;
|
|
mopt = mopts[mode];
|
|
ADD(media, IFM_AUTO, mopt); /* e.g. 11a auto */
|
|
if (caps & IEEE80211_C_IBSS)
|
|
ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
|
|
if (caps & IEEE80211_C_HOSTAP)
|
|
ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
|
|
if (caps & IEEE80211_C_AHDEMO)
|
|
ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
|
|
if (caps & IEEE80211_C_MONITOR)
|
|
ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
|
|
if (caps & IEEE80211_C_WDS)
|
|
ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_WDS);
|
|
if (mode == IEEE80211_MODE_AUTO)
|
|
continue;
|
|
rs = &ic->ic_sup_rates[mode];
|
|
|
|
for (i = 0; i < rs->rs_nrates; i++) {
|
|
rate = rs->rs_rates[i];
|
|
mword = ieee80211_rate2media(ic, rate, mode);
|
|
if (mword == 0)
|
|
continue;
|
|
ADD(media, mword, mopt);
|
|
if (caps & IEEE80211_C_IBSS)
|
|
ADD(media, mword, mopt | IFM_IEEE80211_ADHOC);
|
|
if (caps & IEEE80211_C_HOSTAP)
|
|
ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP);
|
|
if (caps & IEEE80211_C_AHDEMO)
|
|
ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
|
|
if (caps & IEEE80211_C_MONITOR)
|
|
ADD(media, mword, mopt | IFM_IEEE80211_MONITOR);
|
|
if (caps & IEEE80211_C_WDS)
|
|
ADD(media, mword, mopt | IFM_IEEE80211_WDS);
|
|
/* Add rate to the collection of all rates. */
|
|
r = rate & IEEE80211_RATE_VAL;
|
|
for (j = 0; j < allrates.rs_nrates; j++)
|
|
if (allrates.rs_rates[j] == r)
|
|
break;
|
|
if (j == allrates.rs_nrates) {
|
|
/* Unique, add to the set */
|
|
allrates.rs_rates[j] = r;
|
|
allrates.rs_nrates++;
|
|
}
|
|
rate = (rate & IEEE80211_RATE_VAL) / 2;
|
|
if (rate > maxrate)
|
|
maxrate = rate;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < allrates.rs_nrates; i++) {
|
|
mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
|
|
IEEE80211_MODE_AUTO);
|
|
if (mword == 0)
|
|
continue;
|
|
mword = IFM_SUBTYPE(mword); /* remove media options */
|
|
ADD(media, mword, 0);
|
|
if (caps & IEEE80211_C_IBSS)
|
|
ADD(media, mword, IFM_IEEE80211_ADHOC);
|
|
if (caps & IEEE80211_C_HOSTAP)
|
|
ADD(media, mword, IFM_IEEE80211_HOSTAP);
|
|
if (caps & IEEE80211_C_AHDEMO)
|
|
ADD(media, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
|
|
if (caps & IEEE80211_C_MONITOR)
|
|
ADD(media, mword, IFM_IEEE80211_MONITOR);
|
|
if (caps & IEEE80211_C_WDS)
|
|
ADD(media, mword, IFM_IEEE80211_WDS);
|
|
}
|
|
|
|
return maxrate;
|
|
#undef ADD
|
|
}
|
|
|
|
/*
|
|
* Perform the dfs action (channel switch) using scan cache or a randomly
|
|
* chosen channel. The choice of the fallback random channel is done in
|
|
* ieee80211_scan_dfs_action, when there are no scan cache results.
|
|
*
|
|
* This was moved out of ieee80211_mark_dfs, because the same functionality is
|
|
* used also in ieee80211_ioctl_chanswitch.
|
|
*/
|
|
void
|
|
ieee80211_dfs_action(struct ieee80211com *ic) {
|
|
struct ieee80211vap *vap;
|
|
|
|
/* Get an AP mode VAP */
|
|
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
|
if (vap->iv_state == IEEE80211_S_RUN) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vap == NULL) {
|
|
/*
|
|
* No running VAP was found, check
|
|
* if any one is scanning.
|
|
*/
|
|
|
|
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
|
if (vap->iv_state == IEEE80211_S_SCAN) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* No running/scanning VAP was found, so they're all in
|
|
* INIT state, no channel change needed. */
|
|
if (vap == NULL)
|
|
return;
|
|
/* Is it really Scanning */
|
|
/* XXX: Race condition? */
|
|
if (ic->ic_flags & IEEE80211_F_SCAN)
|
|
return;
|
|
/* It is not scanning, but waiting for ath driver to move the
|
|
* vap to RUN. */
|
|
}
|
|
/* Check the scan results using only cached results */
|
|
if (!(ieee80211_check_scan(vap, IEEE80211_SCAN_NOSSID |
|
|
IEEE80211_SCAN_KEEPMODE |
|
|
IEEE80211_SCAN_USECACHE, 0,
|
|
vap->iv_des_nssid, vap->iv_des_ssid,
|
|
ieee80211_scan_dfs_action))) {
|
|
/* No channel was found, so call the scan action with no
|
|
* result. */
|
|
ieee80211_scan_dfs_action(vap, NULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
ieee80211_expire_excl_restrictions(struct ieee80211com *ic)
|
|
{
|
|
struct ieee80211_channel* c = NULL;
|
|
struct net_device *dev = ic->ic_dev;
|
|
struct timeval tv_now;
|
|
int i;
|
|
|
|
do_gettimeofday(&tv_now);
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
if (c->ic_flags & IEEE80211_CHAN_RADAR) {
|
|
if (timeval_compare(&ic->ic_chan_non_occupy[i],
|
|
&tv_now) < 0) {
|
|
if_printf(dev,
|
|
"Returning channel %3d (%4d MHz) "
|
|
"radar avoidance marker expired. "
|
|
"Channel now available again. -- "
|
|
"Time: %10ld.%06ld\n",
|
|
c->ic_ieee, c->ic_freq, tv_now.tv_sec,
|
|
tv_now.tv_usec);
|
|
c->ic_flags &= ~IEEE80211_CHAN_RADAR;
|
|
} else {
|
|
if_printf(dev,
|
|
"Channel %3d (%4d MHz) is still "
|
|
"marked for radar. Channel will "
|
|
"become usable in %u seconds at "
|
|
"Time: %10ld.%06ld\n",
|
|
c->ic_ieee, c->ic_freq,
|
|
ic->ic_chan_non_occupy[i].tv_sec -
|
|
tv_now.tv_sec,
|
|
ic->ic_chan_non_occupy[i].tv_sec,
|
|
ic->ic_chan_non_occupy[i].tv_usec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_expire_excl_restrictions);
|
|
|
|
/* Update the Non-Occupancy Period timer with the first Non-Occupancy Period
|
|
* that will expire */
|
|
static void
|
|
ieee80211_update_dfs_excl_timer(struct ieee80211com *ic)
|
|
{
|
|
struct ieee80211_channel * chan;
|
|
struct timeval tv_now, tv_next;
|
|
int i;
|
|
unsigned long jiffies_tmp;
|
|
|
|
do_gettimeofday(&tv_now);
|
|
jiffies_tmp = jiffies;
|
|
|
|
tv_next.tv_sec = 0;
|
|
tv_next.tv_usec = 0;
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
chan = &ic->ic_channels[i];
|
|
if (chan->ic_flags & IEEE80211_CHAN_RADAR) {
|
|
if ((tv_next.tv_sec == 0) &&
|
|
(tv_next.tv_usec == 0)) {
|
|
tv_next = ic->ic_chan_non_occupy[i];
|
|
}
|
|
if (timeval_compare(&ic->ic_chan_non_occupy[i],
|
|
&tv_next) < 0) {
|
|
tv_next = ic->ic_chan_non_occupy[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((tv_next.tv_sec == 0) &&
|
|
(tv_next.tv_usec == 0)) {
|
|
del_timer(&ic->ic_dfs_excl_timer);
|
|
} else {
|
|
mod_timer(&ic->ic_dfs_excl_timer,
|
|
jiffies_tmp +
|
|
(tv_next.tv_sec - tv_now.tv_sec + 1) * HZ);
|
|
}
|
|
}
|
|
|
|
/* Periodically expire radar avoidance marks. */
|
|
static void
|
|
ieee80211_expire_dfs_excl_timer(unsigned long data)
|
|
{
|
|
struct ieee80211com *ic = (struct ieee80211com *) data;
|
|
struct ieee80211vap *vap;
|
|
|
|
printk(KERN_INFO "%s: %s: expiring Non-Occupancy Period\n",
|
|
DEV_NAME(ic->ic_dev), __func__);
|
|
|
|
if (ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS) {
|
|
/* Make sure there are no channels that have just become
|
|
* available. */
|
|
ieee80211_expire_excl_restrictions(ic);
|
|
/* Go through and clear any interference flag we have, if we
|
|
* just got it cleared up for us */
|
|
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
|
/* We need to check for the special value
|
|
IEEE80211_CHAN_ANYC before using vap->iv_des_chan
|
|
since it will cause a kernel panic */
|
|
if ((vap->iv_state == IEEE80211_S_RUN) &&
|
|
((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
|
|
(vap->iv_opmode == IEEE80211_M_IBSS)) &&
|
|
/* Operating on channel other than desired. */
|
|
(vap->iv_des_chan != IEEE80211_CHAN_ANYC) &&
|
|
(vap->iv_des_chan->ic_freq > 0) &&
|
|
(vap->iv_des_chan->ic_freq !=
|
|
ic->ic_bsschan->ic_freq)) {
|
|
struct ieee80211_channel *des_chan =
|
|
ieee80211_find_channel(ic,
|
|
vap->iv_des_chan->
|
|
ic_freq,
|
|
vap->iv_des_chan->
|
|
ic_flags);
|
|
/* Can we switch to it? */
|
|
if (NULL == des_chan) {
|
|
IEEE80211_DPRINTF(vap,
|
|
IEEE80211_MSG_DOTH,
|
|
"%s: Desired channel "
|
|
"not found: %u/%x\n",
|
|
__func__,
|
|
vap->iv_des_chan->
|
|
ic_freq,
|
|
vap->iv_des_chan->
|
|
ic_flags);
|
|
} else if (!(des_chan->ic_flags &
|
|
IEEE80211_CHAN_RADAR)) {
|
|
IEEE80211_DPRINTF(vap,
|
|
IEEE80211_MSG_DOTH,
|
|
"%s: Desired channel "
|
|
"found and available. "
|
|
"Switching to %u/%x\n",
|
|
__func__,
|
|
vap->iv_des_chan->
|
|
ic_freq,
|
|
vap->iv_des_chan->
|
|
ic_flags);
|
|
ic->ic_chanchange_chan =
|
|
des_chan->ic_ieee;
|
|
ic->ic_chanchange_tbtt =
|
|
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
|
|
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
|
|
} else if (ieee80211_msg_is_reported(vap,
|
|
IEEE80211_MSG_DOTH)) {
|
|
/* Find the desired channel in
|
|
* ic_channels, so we can find the
|
|
* index into ic_chan_non_occupy. */
|
|
int i_des_chan = -1, i = 0;
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
if (&ic->ic_channels[i] ==
|
|
des_chan) {
|
|
i_des_chan = i;
|
|
break;
|
|
}
|
|
}
|
|
IEEE80211_DPRINTF(vap,
|
|
IEEE80211_MSG_DOTH,
|
|
"%s: Desired channel "
|
|
"found and not "
|
|
"available until Time: "
|
|
"%10ld.%06ld\n",
|
|
__func__,
|
|
ic->ic_chan_non_occupy
|
|
[i_des_chan].
|
|
tv_sec,
|
|
ic->ic_chan_non_occupy
|
|
[i_des_chan].
|
|
tv_usec
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* update the timer */
|
|
ieee80211_update_dfs_excl_timer(ic);
|
|
}
|
|
|
|
/* This function is called whenever a radar is detected on channel ichan */
|
|
|
|
void
|
|
ieee80211_mark_dfs(struct ieee80211com *ic, struct ieee80211_channel *ichan)
|
|
{
|
|
struct ieee80211_channel *c=NULL;
|
|
struct net_device *dev = ic->ic_dev;
|
|
struct timeval tv_now;
|
|
unsigned int excl_period = ic->ic_get_dfs_excl_period(ic);
|
|
int i;
|
|
|
|
do_gettimeofday(&tv_now);
|
|
if_printf(dev, "Radar found on channel %3d (%4d MHz) -- "
|
|
"Time: %ld.%06ld\n",
|
|
ichan->ic_ieee, ichan->ic_freq,
|
|
tv_now.tv_sec, tv_now.tv_usec);
|
|
if (IEEE80211_IS_MODE_DFS_MASTER(ic->ic_opmode)) {
|
|
/* Mark the channel in the ic_chan list */
|
|
if (ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS) {
|
|
if_printf(dev, "Marking channel %3d (%4d MHz) in "
|
|
"ic_chan list -- Time: %ld.%06ld\n",
|
|
ichan->ic_ieee, ichan->ic_freq,
|
|
tv_now.tv_sec, tv_now.tv_usec);
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
if (c->ic_freq == ichan->ic_freq) {
|
|
c->ic_flags |= IEEE80211_CHAN_RADAR;
|
|
ic->ic_chan_non_occupy[i].tv_sec =
|
|
tv_now.tv_sec + excl_period;
|
|
ic->ic_chan_non_occupy[i].tv_usec =
|
|
tv_now.tv_usec;
|
|
|
|
if_printf(dev, "Channel %3d (%4d MHz) "
|
|
"will become usable in %u "
|
|
"seconds. Suspending use of "
|
|
"the channel until: "
|
|
"%ld.%06ld\n",
|
|
ichan->ic_ieee, ichan->ic_freq,
|
|
excl_period,
|
|
ic->ic_chan_non_occupy[i].tv_sec,
|
|
ic->ic_chan_non_occupy[i].tv_usec);
|
|
}
|
|
}
|
|
|
|
/* Recompute the next time a Non-Occupancy Period
|
|
* expires. */
|
|
ieee80211_update_dfs_excl_timer(ic);
|
|
|
|
c = ieee80211_find_channel(ic, ichan->ic_freq,
|
|
ichan->ic_flags);
|
|
if (c == NULL) {
|
|
if_printf(dev, "%s: Couldn't find matching "
|
|
"channel for dfs chanchange "
|
|
"(%d, 0x%x)\n",
|
|
__func__, ichan->ic_freq,
|
|
ichan->ic_flags);
|
|
return;
|
|
}
|
|
if (ic->ic_curchan->ic_freq == c->ic_freq) {
|
|
if_printf(dev, "%s: Invoking "
|
|
"ieee80211_dfs_action "
|
|
"(%d, 0x%x)\n",
|
|
__func__, ichan->ic_freq,
|
|
ichan->ic_flags);
|
|
/* The current channel has been marked. We
|
|
* need to move away from it. */
|
|
ieee80211_dfs_action(ic);
|
|
} else
|
|
if_printf(dev,
|
|
"Unexpected channel frequency! "
|
|
"ichan=%3d (%4d MHz) "
|
|
"ic_curchan=%3d (%4d MHz). "
|
|
"Not invoking "
|
|
"ieee80211_dfs_action.\n",
|
|
ichan->ic_ieee,
|
|
ichan->ic_freq,
|
|
ic->ic_curchan->ic_ieee,
|
|
ic->ic_curchan->ic_freq);
|
|
} else {
|
|
/* Change to a radar free 11a channel for dfstesttime
|
|
* seconds. */
|
|
ic->ic_chanchange_chan = IEEE80211_RADAR_TEST_MUTE_CHAN;
|
|
ic->ic_chanchange_tbtt =
|
|
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
|
|
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
|
|
if_printf(dev,
|
|
"Mute test - markdfs is off, we are "
|
|
"in hostap mode, found radar on "
|
|
"channel %3d (%4d MHz) "
|
|
"ic->ic_curchan=%3d (%4d MHz). "
|
|
"Not invoking ieee80211_dfs_action.\n",
|
|
ichan->ic_ieee, ichan->ic_freq,
|
|
ic->ic_curchan->ic_ieee,
|
|
ic->ic_curchan->ic_freq);
|
|
}
|
|
} else {
|
|
/* XXX: Are we in STA mode? If so, send an action msg. to AP
|
|
* saying we found a radar? */
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_mark_dfs);
|
|
|
|
void
|
|
ieee80211_dfs_test_return(struct ieee80211com *ic, u_int8_t ieeeChan)
|
|
{
|
|
struct net_device *dev = ic->ic_dev;
|
|
|
|
/* Return to the original channel we were on before the test mute */
|
|
if_printf(dev, "Returning to channel %d\n", ieeeChan);
|
|
printk(KERN_DEBUG "Returning to chan %d\n", ieeeChan);
|
|
ic->ic_chanchange_chan = ieeeChan;
|
|
ic->ic_chanchange_tbtt = IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
|
|
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_dfs_test_return);
|
|
|
|
void
|
|
ieee80211_announce(struct ieee80211com *ic)
|
|
{
|
|
struct net_device *dev = ic->ic_dev;
|
|
int i, mode, rate, mword;
|
|
struct ieee80211_rateset *rs;
|
|
|
|
for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
|
|
if ((ic->ic_modecaps & (1 << mode)) == 0)
|
|
continue;
|
|
if_printf(dev, "%s rates: ", ieee80211_phymode_name[mode]);
|
|
rs = &ic->ic_sup_rates[mode];
|
|
for (i = 0; i < rs->rs_nrates; i++) {
|
|
rate = rs->rs_rates[i];
|
|
mword = ieee80211_rate2media(ic, rate, mode);
|
|
if (mword == 0)
|
|
continue;
|
|
printk("%s%d%sMbps", (i != 0 ? " " : ""),
|
|
(rate & IEEE80211_RATE_VAL) / 2,
|
|
((rate & 0x1) != 0 ? ".5" : ""));
|
|
}
|
|
printk("\n");
|
|
}
|
|
if_printf(dev, "H/W encryption support:");
|
|
if (ic->ic_caps & IEEE80211_C_WEP)
|
|
printk(" WEP");
|
|
if (ic->ic_caps & IEEE80211_C_AES)
|
|
printk(" AES");
|
|
if (ic->ic_caps & IEEE80211_C_AES_CCM)
|
|
printk(" AES_CCM");
|
|
if (ic->ic_caps & IEEE80211_C_CKIP)
|
|
printk(" CKIP");
|
|
if (ic->ic_caps & IEEE80211_C_TKIP)
|
|
printk(" TKIP");
|
|
printk("\n");
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_announce);
|
|
|
|
void
|
|
ieee80211_announce_channels(struct ieee80211com *ic)
|
|
{
|
|
const struct ieee80211_channel *c;
|
|
char type;
|
|
int i;
|
|
|
|
printk(KERN_INFO "Chan Freq RegPwr MinPwr MaxPwr\n");
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
if (IEEE80211_IS_CHAN_ST(c))
|
|
type = 'S';
|
|
else if (IEEE80211_IS_CHAN_108A(c))
|
|
type = 'T';
|
|
else if (IEEE80211_IS_CHAN_108G(c))
|
|
type = 'G';
|
|
else if (IEEE80211_IS_CHAN_A(c))
|
|
type = 'a';
|
|
else if (IEEE80211_IS_CHAN_ANYG(c))
|
|
type = 'g';
|
|
else if (IEEE80211_IS_CHAN_B(c))
|
|
type = 'b';
|
|
else
|
|
type = 'f';
|
|
printk(KERN_INFO "%4d %4d%c %6d %6d %6d\n",
|
|
c->ic_ieee, c->ic_freq, type,
|
|
c->ic_maxregpower,
|
|
c->ic_minpower, c->ic_maxpower
|
|
);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_announce_channels);
|
|
|
|
/*
|
|
* Common code to calculate the media status word
|
|
* from the operating mode and channel state.
|
|
*/
|
|
static int
|
|
media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
|
|
{
|
|
int status;
|
|
|
|
status = IFM_IEEE80211;
|
|
switch (opmode) {
|
|
case IEEE80211_M_STA:
|
|
break;
|
|
case IEEE80211_M_AHDEMO:
|
|
status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
|
|
break;
|
|
case IEEE80211_M_IBSS:
|
|
status |= IFM_IEEE80211_ADHOC;
|
|
break;
|
|
case IEEE80211_M_HOSTAP:
|
|
status |= IFM_IEEE80211_HOSTAP;
|
|
break;
|
|
case IEEE80211_M_MONITOR:
|
|
status |= IFM_IEEE80211_MONITOR;
|
|
break;
|
|
case IEEE80211_M_WDS:
|
|
status |= IFM_IEEE80211_WDS;
|
|
break;
|
|
}
|
|
if (IEEE80211_IS_CHAN_A(chan)) {
|
|
status |= IFM_IEEE80211_11A;
|
|
if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
status |= IFM_IEEE80211_TURBO;
|
|
} else if (IEEE80211_IS_CHAN_B(chan)) {
|
|
status |= IFM_IEEE80211_11B;
|
|
} else if (IEEE80211_IS_CHAN_ANYG(chan)) {
|
|
status |= IFM_IEEE80211_11G;
|
|
if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
status |= IFM_IEEE80211_TURBO;
|
|
} else if (IEEE80211_IS_CHAN_FHSS(chan)) {
|
|
status |= IFM_IEEE80211_FH;
|
|
}
|
|
/* XXX: Otherwise complain? */
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Handle a media requests on the base interface.
|
|
*/
|
|
static void
|
|
ieee80211com_media_status(struct net_device *dev, struct ifmediareq *imr)
|
|
{
|
|
struct ieee80211com *ic = dev->priv; /* XXX */
|
|
|
|
imr->ifm_status = IFM_AVALID;
|
|
if (!TAILQ_EMPTY(&ic->ic_vaps))
|
|
imr->ifm_status |= IFM_ACTIVE;
|
|
imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
|
|
}
|
|
|
|
/*
|
|
* Convert a media specification to an 802.11 PHY mode.
|
|
*/
|
|
static int
|
|
media2mode(const struct ifmedia_entry *ime, enum ieee80211_phymode *mode)
|
|
{
|
|
|
|
switch (IFM_MODE(ime->ifm_media)) {
|
|
case IFM_IEEE80211_11A:
|
|
*mode = IEEE80211_MODE_11A;
|
|
break;
|
|
case IFM_IEEE80211_11B:
|
|
*mode = IEEE80211_MODE_11B;
|
|
break;
|
|
case IFM_IEEE80211_11G:
|
|
*mode = IEEE80211_MODE_11G;
|
|
break;
|
|
case IFM_IEEE80211_FH:
|
|
*mode = IEEE80211_MODE_FH;
|
|
break;
|
|
case IFM_AUTO:
|
|
*mode = IEEE80211_MODE_AUTO;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
/*
|
|
* Turbo mode is an 'option'.
|
|
* XXX: Turbo currently does not apply to AUTO
|
|
*/
|
|
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
|
|
if (*mode == IEEE80211_MODE_11A)
|
|
*mode = IEEE80211_MODE_TURBO_A;
|
|
else if (*mode == IEEE80211_MODE_11G)
|
|
*mode = IEEE80211_MODE_TURBO_G;
|
|
else
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ieee80211com_media_change(struct net_device *dev)
|
|
{
|
|
struct ieee80211com *ic = dev->priv; /* XXX */
|
|
struct ieee80211vap *vap;
|
|
struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
|
|
enum ieee80211_phymode newphymode;
|
|
int j, error = 0;
|
|
|
|
/* XXX: Is rtnl_lock held here? */
|
|
/* First, identify the phy mode. */
|
|
if (!media2mode(ime, &newphymode))
|
|
return -EINVAL;
|
|
|
|
/* NB: Mode must be supported, no need to check */
|
|
/*
|
|
* Autoselect doesn't make sense when operating as an AP.
|
|
* If no phy mode has been selected, pick one and lock it
|
|
* down so rate tables can be used in forming beacon frames
|
|
* and the like.
|
|
*/
|
|
if ((ic->ic_opmode == IEEE80211_M_HOSTAP) &&
|
|
(newphymode == IEEE80211_MODE_AUTO)) {
|
|
for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
|
|
if (ic->ic_modecaps & (1 << j)) {
|
|
newphymode = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Handle PHY mode change. */
|
|
IEEE80211_LOCK_IRQ(ic);
|
|
if (ic->ic_curmode != newphymode) { /* change PHY mode */
|
|
error = ieee80211_setmode(ic, newphymode);
|
|
if (error != 0) {
|
|
IEEE80211_UNLOCK_IRQ_EARLY(ic);
|
|
return error;
|
|
}
|
|
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
|
|
/* Reset WME state */
|
|
ieee80211_wme_initparams_locked(vap);
|
|
/*
|
|
* Setup an initial rate set according to the
|
|
* current/default channel selected above. This
|
|
* will be changed when scanning but must exist
|
|
* now so drivers have a consistent state.
|
|
*/
|
|
KASSERT(vap->iv_bss != NULL, ("no bss node"));
|
|
vap->iv_bss->ni_rates = ic->ic_sup_rates[newphymode];
|
|
}
|
|
error = -ENETRESET;
|
|
}
|
|
IEEE80211_UNLOCK_IRQ(ic);
|
|
|
|
#ifdef notdef
|
|
if (error == 0)
|
|
ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
|
|
#endif
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
|
|
{
|
|
#define IEEERATE(_ic,_m,_i) \
|
|
((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
|
|
int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
|
|
for (i = 0; i < nrates; i++)
|
|
if (IEEERATE(ic, mode, i) == rate)
|
|
return i;
|
|
return -1;
|
|
#undef IEEERATE
|
|
}
|
|
|
|
/*
|
|
* Convert a media specification to a rate index and possibly a mode
|
|
* (if the rate is fixed and the mode is specified as 'auto' then
|
|
* we need to lock down the mode so the index is meaningful).
|
|
*/
|
|
static int
|
|
checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
|
|
{
|
|
|
|
/* Check the rate table for the specified/current PHY. */
|
|
if (mode == IEEE80211_MODE_AUTO) {
|
|
int i;
|
|
/* In autoselect mode search for the rate. */
|
|
for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
|
|
if ((ic->ic_modecaps & (1 << i)) &&
|
|
findrate(ic, i, rate) != -1)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
} else {
|
|
/* Mode is fixed; check for rate. */
|
|
return (findrate(ic, mode, rate) != -1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle a media change request; the only per-vap
|
|
* information that is meaningful is the fixed rate
|
|
* and desired PHY mode.
|
|
*/
|
|
int
|
|
ieee80211_media_change(struct net_device *dev)
|
|
{
|
|
struct ieee80211vap *vap = dev->priv;
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
|
|
enum ieee80211_phymode newmode;
|
|
int newrate, error;
|
|
|
|
/* First, identify the desired PHY mode. */
|
|
if (!media2mode(ime, &newmode))
|
|
return -EINVAL;
|
|
/* Check for fixed/variable rate. */
|
|
if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
|
|
/*
|
|
* Convert media subtype to rate and potentially
|
|
* lock down the mode.
|
|
*/
|
|
newrate = ieee80211_media2rate(ime->ifm_media);
|
|
if ((newrate == 0) || !checkrate(ic, newmode, newrate))
|
|
return -EINVAL;
|
|
} else
|
|
newrate = IEEE80211_FIXED_RATE_NONE;
|
|
|
|
/* Install the rate & mode settings. */
|
|
error = 0;
|
|
if (vap->iv_fixed_rate != newrate) {
|
|
vap->iv_fixed_rate = newrate; /* fixed TX rate */
|
|
error = -ENETRESET;
|
|
}
|
|
if (vap->iv_des_mode != newmode) {
|
|
vap->iv_des_mode = newmode; /* desired PHY mode */
|
|
error = -ENETRESET;
|
|
}
|
|
return error;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_media_change);
|
|
|
|
void
|
|
ieee80211_media_status(struct net_device *dev, struct ifmediareq *imr)
|
|
{
|
|
struct ieee80211vap *vap = dev->priv;
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
enum ieee80211_phymode mode;
|
|
struct ieee80211_rateset *rs;
|
|
|
|
imr->ifm_status = IFM_AVALID;
|
|
/*
|
|
* NB: use the current channel's mode to lock down a xmit
|
|
* rate only when running; otherwise we may have a mismatch
|
|
* in which case the rate will not be convertible.
|
|
*/
|
|
if (vap->iv_state == IEEE80211_S_RUN) {
|
|
imr->ifm_status |= IFM_ACTIVE;
|
|
mode = ieee80211_chan2mode(ic->ic_curchan);
|
|
} else
|
|
mode = IEEE80211_MODE_AUTO;
|
|
imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan);
|
|
/* Calculate a current rate, if possible. */
|
|
if (vap->iv_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
|
|
/* A fixed rate is set, report that. */
|
|
imr->ifm_active |= ieee80211_rate2media(ic,
|
|
vap->iv_fixed_rate, mode);
|
|
} else if (vap->iv_opmode == IEEE80211_M_STA) {
|
|
/* In station mode, report the current transmit rate. */
|
|
rs = &vap->iv_bss->ni_rates;
|
|
imr->ifm_active |= ieee80211_rate2media(ic,
|
|
rs->rs_rates[vap->iv_bss->ni_txrate], mode);
|
|
} else
|
|
imr->ifm_active |= IFM_AUTO;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_media_status);
|
|
|
|
/*
|
|
* Set the current PHY mode.
|
|
*/
|
|
int
|
|
ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
|
|
{
|
|
|
|
#if 0
|
|
/* Potentially invalidate the BSS channel. */
|
|
/* XXX not right/too conservative */
|
|
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
|
|
mode != ieee80211_chan2mode(ic->ic_bsschan))
|
|
ic->ic_bsschan = IEEE80211_CHAN_ANYC; /* invalidate */
|
|
#endif
|
|
ieee80211_reset_erp(ic, mode); /* reset ERP state */
|
|
ic->ic_curmode = mode; /* NB: must do post reset_erp */
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_setmode);
|
|
|
|
/*
|
|
* Return the PHY mode for with the specified channel.
|
|
*/
|
|
enum ieee80211_phymode
|
|
ieee80211_chan2mode(const struct ieee80211_channel *chan)
|
|
{
|
|
/*
|
|
* Callers should handle this case properly, rather than
|
|
* just relying that this function returns a sane value.
|
|
* XXX: Probably needs to be revised.
|
|
*/
|
|
KASSERT(chan != IEEE80211_CHAN_ANYC, ("channel not setup"));
|
|
|
|
if (IEEE80211_IS_CHAN_108G(chan))
|
|
return IEEE80211_MODE_TURBO_G;
|
|
else if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
return IEEE80211_MODE_TURBO_A;
|
|
else if (IEEE80211_IS_CHAN_A(chan))
|
|
return IEEE80211_MODE_11A;
|
|
else if (IEEE80211_IS_CHAN_ANYG(chan))
|
|
return IEEE80211_MODE_11G;
|
|
else if (IEEE80211_IS_CHAN_B(chan))
|
|
return IEEE80211_MODE_11B;
|
|
else if (IEEE80211_IS_CHAN_FHSS(chan))
|
|
return IEEE80211_MODE_FH;
|
|
|
|
/* NB: Should not get here */
|
|
printk(KERN_ERR "%s: cannot map channel to mode; freq %u flags 0x%x\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
return IEEE80211_MODE_11B;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_chan2mode);
|
|
|
|
/*
|
|
* Convert IEEE80211 rate value to ifmedia subtype.
|
|
* ieee80211 rate is in unit of 0.5Mbps.
|
|
*/
|
|
int
|
|
ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
|
|
{
|
|
static const struct {
|
|
u_int m; /* rate & mode */
|
|
u_int r; /* if_media rate */
|
|
} rates[] = {
|
|
{ 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
|
|
{ 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
|
|
{ 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
|
|
{ 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 },
|
|
{ 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 },
|
|
{ 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 },
|
|
{ 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 },
|
|
{ 3 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM1_50 },
|
|
{ 4 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM2_25 },
|
|
{ 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 },
|
|
{ 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4_50 },
|
|
{ 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 },
|
|
{ 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 },
|
|
{ 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 },
|
|
{ 27 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM13_5 },
|
|
{ 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 },
|
|
{ 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 },
|
|
{ 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 },
|
|
{ 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 },
|
|
{ 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 },
|
|
{ 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 },
|
|
{ 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 },
|
|
{ 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 },
|
|
{ 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 },
|
|
{ 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 },
|
|
{ 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 },
|
|
{ 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 },
|
|
{ 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 },
|
|
{ 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 },
|
|
{ 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 },
|
|
{ 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
|
|
{ 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
|
|
{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
|
|
/* NB: OFDM72 doesn't really exist so we don't handle it */
|
|
};
|
|
|
|
u_int mask, i;
|
|
mask = rate & IEEE80211_RATE_VAL;
|
|
switch (mode) {
|
|
case IEEE80211_MODE_11A:
|
|
case IEEE80211_MODE_TURBO_A:
|
|
mask |= IFM_IEEE80211_11A;
|
|
break;
|
|
case IEEE80211_MODE_11B:
|
|
mask |= IFM_IEEE80211_11B;
|
|
break;
|
|
case IEEE80211_MODE_FH:
|
|
mask |= IFM_IEEE80211_FH;
|
|
break;
|
|
case IEEE80211_MODE_AUTO:
|
|
/* NB: ic may be NULL for some drivers */
|
|
if (ic && (ic->ic_phytype == IEEE80211_T_FH)) {
|
|
mask |= IFM_IEEE80211_FH;
|
|
break;
|
|
}
|
|
/* NB: Hack, 11g matches both 11b & 11a rates */
|
|
/* *Fall through* */
|
|
case IEEE80211_MODE_11G:
|
|
case IEEE80211_MODE_TURBO_G:
|
|
mask |= IFM_IEEE80211_11G;
|
|
break;
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(rates); i++)
|
|
if (rates[i].m == mask)
|
|
return rates[i].r;
|
|
return IFM_AUTO;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rate2media);
|
|
|
|
int
|
|
ieee80211_media2rate(int mword)
|
|
{
|
|
static const int ieeerates[] = {
|
|
-1, /* IFM_AUTO */
|
|
0, /* IFM_MANUAL */
|
|
0, /* IFM_NONE */
|
|
2, /* IFM_IEEE80211_FH1 */
|
|
4, /* IFM_IEEE80211_FH2 */
|
|
2, /* IFM_IEEE80211_DS1 */
|
|
4, /* IFM_IEEE80211_DS2 */
|
|
11, /* IFM_IEEE80211_DS5 */
|
|
22, /* IFM_IEEE80211_DS11 */
|
|
44, /* IFM_IEEE80211_DS22 */
|
|
3, /* IFM_IEEE80211_OFDM1_50 */
|
|
4, /* IFM_IEEE80211_OFDM2_25 */
|
|
6, /* IFM_IEEE80211_OFDM3 */
|
|
9, /* IFM_IEEE80211_OFDM4_50 */
|
|
12, /* IFM_IEEE80211_OFDM6 */
|
|
18, /* IFM_IEEE80211_OFDM9 */
|
|
24, /* IFM_IEEE80211_OFDM12 */
|
|
27, /* IFM_IEEE80211_OFDM13_5 */
|
|
36, /* IFM_IEEE80211_OFDM18 */
|
|
48, /* IFM_IEEE80211_OFDM24 */
|
|
54, /* IFM_IEEE80211_OFDM27 */
|
|
72, /* IFM_IEEE80211_OFDM36 */
|
|
96, /* IFM_IEEE80211_OFDM48 */
|
|
108, /* IFM_IEEE80211_OFDM54 */
|
|
144, /* IFM_IEEE80211_OFDM72 */
|
|
};
|
|
return IFM_SUBTYPE(mword) < ARRAY_SIZE(ieeerates) ?
|
|
ieeerates[IFM_SUBTYPE(mword)] : 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_media2rate);
|
|
|
|
/*
|
|
* Return netdevice statistics.
|
|
*/
|
|
static struct net_device_stats *
|
|
ieee80211_getstats(struct net_device *dev)
|
|
{
|
|
struct ieee80211vap *vap = dev->priv;
|
|
struct net_device_stats *stats = &vap->iv_devstats;
|
|
|
|
/* XXX: Total guess as to what to count where */
|
|
/* Update according to private statistics */
|
|
stats->tx_errors = vap->iv_stats.is_tx_nodefkey
|
|
+ vap->iv_stats.is_tx_noheadroom
|
|
+ vap->iv_stats.is_crypto_enmicfail;
|
|
stats->tx_dropped = vap->iv_stats.is_tx_nobuf
|
|
+ vap->iv_stats.is_tx_nonode
|
|
+ vap->iv_stats.is_tx_unknownmgt
|
|
+ vap->iv_stats.is_tx_badcipher
|
|
+ vap->iv_stats.is_tx_nodefkey;
|
|
stats->rx_errors = vap->iv_stats.is_rx_tooshort
|
|
+ vap->iv_stats.is_rx_wepfail
|
|
+ vap->iv_stats.is_rx_decap
|
|
+ vap->iv_stats.is_rx_nobuf
|
|
+ vap->iv_stats.is_rx_decryptcrc
|
|
+ vap->iv_stats.is_rx_ccmpmic
|
|
+ vap->iv_stats.is_rx_tkipmic
|
|
+ vap->iv_stats.is_rx_tkipicv;
|
|
stats->rx_crc_errors = 0;
|
|
|
|
return stats;
|
|
}
|
|
|
|
static int
|
|
ieee80211_change_mtu(struct net_device *dev, int mtu)
|
|
{
|
|
if ((IEEE80211_MTU_MIN >= mtu) || (mtu > IEEE80211_MTU_MAX))
|
|
return -EINVAL;
|
|
dev->mtu = mtu;
|
|
/* XXX: Coordinate with parent device */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ieee80211_set_multicast_list(struct net_device *dev)
|
|
{
|
|
struct ieee80211vap *vap = dev->priv;
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct net_device *parent = ic->ic_dev;
|
|
|
|
IEEE80211_LOCK_IRQ(ic);
|
|
if (dev->flags & IFF_PROMISC) {
|
|
if ((vap->iv_flags & IEEE80211_F_PROMISC) == 0) {
|
|
vap->iv_flags |= IEEE80211_F_PROMISC;
|
|
ic->ic_promisc++;
|
|
parent->flags |= IFF_PROMISC;
|
|
}
|
|
} else {
|
|
if (vap->iv_flags & IEEE80211_F_PROMISC) {
|
|
vap->iv_flags &= ~IEEE80211_F_PROMISC;
|
|
ic->ic_promisc--;
|
|
parent->flags &= ~IFF_PROMISC;
|
|
}
|
|
}
|
|
if (dev->flags & IFF_ALLMULTI) {
|
|
if ((vap->iv_flags & IEEE80211_F_ALLMULTI) == 0) {
|
|
vap->iv_flags |= IEEE80211_F_ALLMULTI;
|
|
ic->ic_allmulti++;
|
|
parent->flags |= IFF_ALLMULTI;
|
|
}
|
|
} else {
|
|
if (vap->iv_flags & IEEE80211_F_ALLMULTI) {
|
|
vap->iv_flags &= ~IEEE80211_F_ALLMULTI;
|
|
ic->ic_allmulti--;
|
|
parent->flags &= ~IFF_ALLMULTI;
|
|
}
|
|
}
|
|
IEEE80211_UNLOCK_IRQ(ic);
|
|
|
|
/* XXX: Merge multicast list into parent device */
|
|
parent->set_multicast_list(ic->ic_dev);
|
|
}
|
|
|
|
void
|
|
ieee80211_build_countryie(struct ieee80211com *ic)
|
|
{
|
|
int i, found;
|
|
struct net_device *dev = ic->ic_dev;
|
|
struct ieee80211_channel *c;
|
|
u_int8_t *cur_runlen, *cur_chan, *cur_pow, prevchan;
|
|
|
|
/* Fill in country IE. */
|
|
memset(&ic->ic_country_ie, 0, sizeof(ic->ic_country_ie));
|
|
ic->ic_country_ie.country_id = IEEE80211_ELEMID_COUNTRY;
|
|
|
|
/* Initialize country IE */
|
|
found = 0;
|
|
for (i = 0; i < ARRAY_SIZE(country_strings); i++) {
|
|
if (country_strings[i].iso_code == ic->ic_country_code) {
|
|
ic->ic_country_ie.country_str[0] = country_strings[i].iso_name[0];
|
|
ic->ic_country_ie.country_str[1] = country_strings[i].iso_name[1];
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
if_printf(dev, "bad country string ignored: %d\n",
|
|
ic->ic_country_code);
|
|
ic->ic_country_ie.country_str[0] = ' ';
|
|
ic->ic_country_ie.country_str[1] = ' ';
|
|
}
|
|
|
|
/*
|
|
* Indoor/Outdoor portion if country string.
|
|
* NB: this is not quite right, since we should have one of:
|
|
* 'I': indoor only
|
|
* 'O': outdoor only
|
|
* ' ': all enviroments
|
|
* we currently can only provide 'I' or ' '.
|
|
*/
|
|
ic->ic_country_ie.country_str[2] = 'I';
|
|
if (ic->ic_country_outdoor)
|
|
ic->ic_country_ie.country_str[2] = ' ';
|
|
/* Runlength encoded channel max. TX power info. */
|
|
cur_runlen = &ic->ic_country_ie.country_triplet[1];
|
|
cur_chan = &ic->ic_country_ie.country_triplet[0];
|
|
cur_pow = &ic->ic_country_ie.country_triplet[2];
|
|
prevchan = 0;
|
|
ic->ic_country_ie.country_len = 3; /* invalid, but just initialize */
|
|
|
|
if ((ic->ic_flags_ext & IEEE80211_FEXT_REGCLASS) && ic->ic_nregclass) {
|
|
/* Add regulatory triplets.
|
|
* chan/no_of_chans/tx power triplet is overridden as
|
|
* as follows:
|
|
* cur_chan == REGULATORY EXTENSION ID.
|
|
* cur_runlen = Regulatory class.
|
|
* cur_pow = coverage class.
|
|
*/
|
|
for (i = 0; i < ic->ic_nregclass; i++) {
|
|
*cur_chan = IEEE80211_REG_EXT_ID;
|
|
*cur_runlen = ic->ic_regclassids[i];
|
|
*cur_pow = ic->ic_coverageclass;
|
|
|
|
cur_runlen += 3;
|
|
cur_chan += 3;
|
|
cur_pow += 3;
|
|
ic->ic_country_ie.country_len += 3;
|
|
}
|
|
} else {
|
|
u_int16_t curmode_noturbo = ic->ic_curmode;
|
|
/* advertise only non-turbo channels */
|
|
/* XXX: shouldn't turbo channels be included as well? */
|
|
switch (curmode_noturbo) {
|
|
case IEEE80211_MODE_TURBO_A:
|
|
curmode_noturbo = IEEE80211_MODE_11A;
|
|
break;
|
|
case IEEE80211_MODE_TURBO_G:
|
|
curmode_noturbo = IEEE80211_MODE_11G;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
|
|
/* Does channel belong to current operation mode */
|
|
if (ieee80211_chan2mode(c) != curmode_noturbo)
|
|
continue;
|
|
|
|
/* Skip half/quarter rate channels */
|
|
if (IEEE80211_IS_CHAN_HALF(c) ||
|
|
IEEE80211_IS_CHAN_QUARTER(c))
|
|
continue;
|
|
|
|
if (*cur_runlen == 0) {
|
|
(*cur_runlen)++;
|
|
*cur_pow = c->ic_maxregpower;
|
|
*cur_chan = c->ic_ieee;
|
|
prevchan = c->ic_ieee;
|
|
ic->ic_country_ie.country_len += 3;
|
|
} else if (*cur_pow == c->ic_maxregpower &&
|
|
c->ic_ieee == prevchan +
|
|
(IEEE80211_IS_CHAN_5GHZ(c) ? 4 : 1)) {
|
|
(*cur_runlen)++;
|
|
prevchan = c->ic_ieee;
|
|
} else {
|
|
cur_runlen +=3;
|
|
cur_chan += 3;
|
|
cur_pow += 3;
|
|
(*cur_runlen)++;
|
|
*cur_pow = c->ic_maxregpower;
|
|
*cur_chan = c->ic_ieee;
|
|
prevchan = c->ic_ieee;
|
|
ic->ic_country_ie.country_len += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Pad */
|
|
if (ic->ic_country_ie.country_len & 1)
|
|
ic->ic_country_ie.country_len++;
|
|
}
|
|
|
|
void
|
|
ieee80211_build_sc_ie(struct ieee80211com *ic)
|
|
{
|
|
struct ieee80211_ie_sc *ie = &ic->ic_sc_ie;
|
|
int i, j;
|
|
struct ieee80211_channel *c;
|
|
u_int8_t prevchan;
|
|
|
|
/* Fill in Supported Channels IE. */
|
|
memset(ie, 0, sizeof(*ie));
|
|
ie->sc_id = IEEE80211_ELEMID_SUPPCHAN;
|
|
|
|
prevchan = 0;
|
|
|
|
j = 0;
|
|
for (i = 0; i < ic->ic_nchans; i++) {
|
|
c = &ic->ic_channels[i];
|
|
|
|
/* Skip disabled channels */
|
|
if (isclr(ic->ic_chan_active, c->ic_ieee))
|
|
continue;
|
|
|
|
/* XXX Skip turbo channels */
|
|
if (IEEE80211_IS_CHAN_TURBO(c))
|
|
continue;
|
|
|
|
/* Skip half/quarter rate channels */
|
|
if (IEEE80211_IS_CHAN_HALF(c) ||
|
|
IEEE80211_IS_CHAN_QUARTER(c))
|
|
continue;
|
|
|
|
/* Skip duplicate frequencies (separate b/g channels) */
|
|
if (c->ic_ieee == prevchan)
|
|
continue;
|
|
|
|
if (ie->sc_subband[j].sc_number == 0) {
|
|
ie->sc_subband[j].sc_first = c->ic_ieee;
|
|
} else if (c->ic_ieee != prevchan +
|
|
/* XXX: see 802.11d-2001-4-05-03-interp,
|
|
* but what about .11j, turbo, etc.? */
|
|
(IEEE80211_IS_CHAN_5GHZ(c) ? 4 : 1)) {
|
|
j++;
|
|
ie->sc_subband[j].sc_first = c->ic_ieee;
|
|
}
|
|
ie->sc_subband[j].sc_number++;
|
|
prevchan = c->ic_ieee;
|
|
}
|
|
ie->sc_len = (j+1) * 2;
|
|
}
|
|
|
|
int ath_debug_global = 0;
|
|
EXPORT_SYMBOL(ath_debug_global);
|
|
|