ath_ieee80211.c revision 7a1306a70fee0e017a445bde1dcfd1997f691cf4
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 2002, 2003 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,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
*
*/
/*
* IEEE 802.11 generic handler
*
* This code is derived from NetBSD code; their copyright notice follows.
*/
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Atsushi Onoe.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/stream.h>
#include <sys/termio.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/strtty.h>
#include <sys/kbio.h>
#include <sys/cred.h>
#include <sys/stat.h>
#include <sys/consdev.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/pci.h>
#include <sys/errno.h>
#include <sys/gld.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/byteorder.h>
#include <sys/proc.h>
#include <sys/note.h>
#include <sys/strsun.h>
#include <sys/list.h>
#include <sys/byteorder.h>
#include <inet/common.h>
#include <inet/nd.h>
#include <inet/mi.h>
#include <inet/wifi_ioctl.h>
#include "ath_ieee80211.h"
#include "ath_impl.h"
#define list_empty(a) ((a)->list_head.list_next == &(a)->list_head)
static const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
"beacon", "atim", "disassoc", "auth",
"deauth", "reserved#13", "reserved#14", "reserved#15"
};
extern pri_t minclsyspri;
static void
ieee80211_unref_node(struct ieee80211_node **in)
{
*in = NULL;
}
static uint8_t
ieee80211_get_rssi(struct ieee80211_node *in)
{
/* no recent samples, use last known value */
return (in->in_recv_hist[in->in_hist_cur].irh_rssi);
}
static void
ieee80211_reset_recvhist(struct ieee80211_node *in)
{
int i;
for (i = 0; i < IEEE80211_RECV_HIST_LEN; ++i) {
in->in_recv_hist[i].irh_jiffies = IEEE80211_JIFFIES_NONE;
in->in_recv_hist[i].irh_rssi = 0;
in->in_recv_hist[i].irh_rstamp = 0;
in->in_recv_hist[i].irh_rantenna = 0;
}
in->in_hist_cur = IEEE80211_RECV_HIST_LEN - 1;
}
static void
ieee80211_add_recvhist(struct ieee80211_node *in, uint8_t rssi,
uint32_t rstamp, uint8_t rantenna)
{
if (++in->in_hist_cur >= IEEE80211_RECV_HIST_LEN)
in->in_hist_cur = 0;
in->in_recv_hist[in->in_hist_cur].irh_rssi = rssi;
in->in_recv_hist[in->in_hist_cur].irh_rstamp = rstamp;
in->in_recv_hist[in->in_hist_cur].irh_rantenna = rantenna;
}
/*
* Convert MHz frequency to IEEE channel number.
*/
uint32_t
ieee80211_mhz2ieee(uint32_t freq, uint32_t flags)
{
if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
if (freq == 2484)
return (14);
if (freq < 2484)
return ((freq - 2407) / 5);
else
return (15 + ((freq - 2512) / 20));
} else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */
return ((freq - 5000) / 5);
} else { /* either, guess */
if (freq == 2484)
return (14);
if (freq < 2484)
return ((freq - 2407) / 5);
if (freq < 5000)
return (15 + ((freq - 2512) / 20));
return ((freq - 5000) / 5);
}
}
/*
* Convert channel to IEEE channel number.
*/
uint32_t
ieee80211_chan2ieee(ieee80211com_t *isc, struct ieee80211channel *ch)
{
if (isc->isc_channels <= ch &&
ch <= &isc->isc_channels[IEEE80211_CHAN_MAX])
return (ch - isc->isc_channels);
else if (ch == IEEE80211_CHAN_ANYC)
return (IEEE80211_CHAN_ANY);
else {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_chan2ieee(): "
"invalid channel freq %u flags %x\n",
ch->ich_freq, ch->ich_flags));
return (0);
}
}
/*
* Convert IEEE channel number to MHz frequency.
*/
uint32_t
ieee80211_ieee2mhz(uint32_t chan, uint32_t 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));
}
}
static void
ieee80211_free_node(ieee80211com_t *isc, struct ieee80211_node *in)
{
int32_t i, done;
struct ieee80211_node *in1;
/* remove in from list of isc->isc_in_list */
list_remove(&isc->isc_in_list, in);
/* remove in from list of isc->isc_inhash_list */
done = 0;
for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) {
in1 = list_head(&isc->isc_inhash_list[i]);
while (in1 != NULL) {
if (in1 == in) {
list_remove(&isc->isc_inhash_list[i], in);
done = 1;
break;
}
in1 = list_next(&isc->isc_inhash_list[i], in1);
}
if (done)
break;
}
if (list_empty(&isc->isc_in_list))
isc->isc_inact_timeout = 0;
if (isc->isc_node_free != NULL)
(*isc->isc_node_free)(isc, in);
}
/*
* This function is only running in software interrupt thread,
* and it will probably call back to LLD by isc_mgmt_send() or isc_new_state(),
* to avoid recursive mutex entry, we must make sure that
* the callers have release all LLD mutexs before calling ieee80211_input().
*/
void
ieee80211_input(ieee80211com_t *isc, mblk_t *mp,
int32_t rssi, uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_node *in;
gld_mac_info_t *gld_p = isc->isc_dev;
struct ieee80211_frame *wh;
void (*rh)(ieee80211com_t *, mblk_t *, int32_t, uint32_t, uint32_t);
uint8_t dir, subtype;
uint8_t *bssid;
uint16_t rxseq;
wh = (struct ieee80211_frame *)mp->b_rptr;
if ((wh->ifrm_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
IEEE80211_FC0_VERSION_0) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_input(): "
"discard pkt with wrong version"));
goto out;
}
dir = wh->ifrm_fc[1] & IEEE80211_FC1_DIR_MASK;
mutex_enter(&isc->isc_genlock);
if (isc->isc_state != IEEE80211_S_SCAN) {
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
in = isc->isc_bss;
if (!IEEE80211_ADDR_EQ(wh->ifrm_addr2, in->in_bssid))
goto out_with_mutex;
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
case IEEE80211_M_HOSTAP:
if (dir == IEEE80211_FC1_DIR_NODS)
bssid = wh->ifrm_addr3;
else
bssid = wh->ifrm_addr1;
if (!IEEE80211_ADDR_EQ(bssid, isc->isc_bss->in_bssid) &&
!IEEE80211_ADDR_EQ(bssid,
gld_p->gldm_broadcast_addr)) {
/* not interested in */
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_input(): other bss %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr3)));
goto out_with_mutex;
}
in = ieee80211_find_node(isc, wh->ifrm_addr2);
if (in == NULL) {
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_input(): unknown src %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2)));
/*
* NB: Node allocation is handled in the
* management handling routines. Just fake
* up a reference to the hosts's node to do
* the stuff below.
*/
in = isc->isc_bss;
}
break;
default:
/* catch bad values */
break;
}
ieee80211_add_recvhist(in, rssi, rstamp, rantenna);
rxseq = in->in_rxseq;
in->in_rxseq = LE_16(*(uint16_t *)wh->ifrm_seq)
>> IEEE80211_SEQ_SEQ_SHIFT;
/* fragment */
if ((wh->ifrm_fc[1] & IEEE80211_FC1_RETRY) &&
rxseq == in->in_rxseq) {
/* duplicate, silently discarded */
goto out_with_mutex;
}
in->in_inact = 0;
}
switch (wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_DATA:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
if (dir != IEEE80211_FC1_DIR_FROMDS) {
ATH_DEBUG((ATH_DBG_80211, "ath: ",
"ieee80211_input(): discard frame "
"with invalid direction %x\n", dir));
goto out_with_mutex;
}
if (IEEE80211_IS_MULTICAST(wh->ifrm_addr1) &&
IEEE80211_ADDR_EQ(wh->ifrm_addr3,
gld_p->gldm_vendor_addr)) {
/*
* In IEEE802.11 network, multicast packet
* sent from me is broadcasted from AP.
* It should be silently discarded for
* SIMPLEX interface.
*/
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_input(): "
"discard multicast echo\n"));
goto out_with_mutex;
}
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
if (dir != IEEE80211_FC1_DIR_NODS)
goto out_with_mutex;
break;
case IEEE80211_M_HOSTAP:
/* need more work to support HOSTAP */
break;
}
mutex_exit(&isc->isc_genlock);
/* copy to listener after decrypt */
mp = ieee80211_decap(mp);
if (mp == NULL) {
ATH_DEBUG((ATH_DBG_80211, "ath: ",
"ieee80211_input(): decapsulation failed\n"));
goto out;
}
gld_recv(gld_p, mp);
return;
case IEEE80211_FC0_TYPE_MGT:
if (dir != IEEE80211_FC1_DIR_NODS)
goto out_with_mutex;
if (isc->isc_opmode == IEEE80211_M_AHDEMO)
goto out_with_mutex;
subtype = wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
/* drop uninteresting frames */
if (isc->isc_state == IEEE80211_S_SCAN) {
if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
goto out_with_mutex;
} else {
if (isc->isc_opmode != IEEE80211_M_IBSS &&
subtype == IEEE80211_FC0_SUBTYPE_BEACON)
goto out_with_mutex;
}
rh = isc->isc_recv_mgmt[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT];
if (rh != NULL)
(*rh)(isc, mp, rssi, rstamp, rantenna);
goto out_with_mutex;
case IEEE80211_FC0_TYPE_CTL:
default:
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_input(): "
"bad type %x\n", wh->ifrm_fc[0]));
break;
}
out_with_mutex:
mutex_exit(&isc->isc_genlock);
out:
if (mp != NULL)
freemsg(mp); /* free the buffer alloced in ath_rx_handler */
}
static void
ieee80211_free_allnodes(ieee80211com_t *isc)
{
struct ieee80211_node *in;
in = list_head(&isc->isc_in_list);
while (in != NULL) {
ieee80211_free_node(isc, in);
in = list_head(&isc->isc_in_list);
}
}
/*
* Begin an active scan.
*/
static void
ieee80211_begin_scan(ieee80211com_t *isc, struct ieee80211_node *in)
{
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_begin_scan(): begin %s scan\n",
isc->isc_opmode != IEEE80211_M_HOSTAP ? "active" : "passive"));
/*
* Initialize the active channel set based on the set
* of available channels and the current PHY mode.
*/
bcopy(isc->isc_chan_active, isc->isc_chan_scan,
sizeof (isc->isc_chan_active));
/*
* Flush any previously seen AP's. Note that this
* assumes we don't act as both an AP and a station,
* otherwise we'll potentially flush state of stations
* associated with us.
*/
ieee80211_free_allnodes(isc);
clrbit(isc->isc_chan_scan, ieee80211_chan2ieee(isc, in->in_chan));
if (isc->isc_opmode != IEEE80211_M_HOSTAP) {
isc->isc_flags |= IEEE80211_F_ASCAN;
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
}
}
static void
ieee80211_create_ibss(ieee80211com_t *isc, struct ieee80211channel *chan)
{
struct ieee80211_node *in;
in = isc->isc_bss;
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_create_ibss(): creating ibss\n"));
isc->isc_flags |= IEEE80211_F_SIBSS;
in->in_chan = chan;
in->in_rates = isc->isc_sup_rates[
ieee80211_chan2mode(isc, in->in_chan)];
IEEE80211_ADDR_COPY(in->in_macaddr, isc->isc_macaddr);
IEEE80211_ADDR_COPY(in->in_bssid, isc->isc_macaddr);
if (isc->isc_opmode == IEEE80211_M_IBSS)
in->in_bssid[0] |= 0x02; /* local bit for IBSS */
in->in_esslen = isc->isc_des_esslen;
bcopy(isc->isc_des_essid, in->in_essid, in->in_esslen);
ieee80211_reset_recvhist(in);
bzero(in->in_tstamp, sizeof (in->in_tstamp));
in->in_intval = isc->isc_lintval;
in->in_capinfo = IEEE80211_CAPINFO_IBSS;
if (isc->isc_flags & IEEE80211_F_WEPON)
in->in_capinfo |= IEEE80211_CAPINFO_PRIVACY;
if (isc->isc_phytype == IEEE80211_T_FH) {
in->in_fhdwell = 200;
in->in_fhindex = 1;
}
(void) _ieee80211_new_state(isc, IEEE80211_S_RUN, -1);
}
/*
* The difference between _ieee80211_new_state() and ieee80211_new_state()
* is the former asserts isc_genlock is already held.
* _ieee80211_new_state() is called from Multi-func thread and GLD thread,
* because ic_genlock is already owned at its entry in those 2 types of thread.
* ieee80211_new_state() is just for software interrupt thread in LLD.
*
* Because of the reason to avoid recursive mutex entry, the caller can't hold
* any other LLD mutexs before calling ieee80211_new_state().
*/
int
_ieee80211_new_state(ieee80211com_t *isc, enum ieee80211_state nstate,
int32_t mgt)
{
gld_mac_info_t *gld_p = isc->isc_dev;
struct ieee80211_node *in;
int error, ostate;
ASSERT(mutex_owned(&isc->isc_genlock));
ostate = isc->isc_state;
if (isc->isc_new_state) {
error = (*isc->isc_new_state)(isc, nstate);
if (error == EINPROGRESS)
return (0);
if (error != 0)
return (error);
}
isc->isc_state = nstate;
in = isc->isc_bss;
/* state transition */
switch (nstate) {
case IEEE80211_S_INIT:
switch (ostate) {
case IEEE80211_S_INIT:
break;
case IEEE80211_S_RUN:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_DISASSOC,
IEEE80211_REASON_ASSOC_LEAVE);
break;
default:
break;
}
case IEEE80211_S_ASSOC:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_LEAVE);
break;
default:
break;
}
case IEEE80211_S_AUTH:
case IEEE80211_S_SCAN:
break;
}
isc->isc_mgt_timeout = 0;
isc->isc_inact_timeout = 0;
break;
case IEEE80211_S_SCAN:
isc->isc_flags &= ~IEEE80211_F_SIBSS;
/* initialize bss for probe request */
IEEE80211_ADDR_COPY(in->in_macaddr, gld_p->gldm_broadcast_addr);
IEEE80211_ADDR_COPY(in->in_bssid, gld_p->gldm_broadcast_addr);
in->in_rates = isc->isc_sup_rates[
ieee80211_chan2mode(isc, in->in_chan)];
in->in_associd = 0;
ieee80211_reset_recvhist(in);
switch (ostate) {
case IEEE80211_S_INIT:
if ((isc->isc_opmode == IEEE80211_M_HOSTAP ||
isc->isc_opmode == IEEE80211_M_IBSS) &&
isc->isc_des_chan != IEEE80211_CHAN_ANYC) {
/*
* AP operation and we already have a channel;
* bypass the scan and startup immediately.
* Same applies to ad-hoc mode.
*/
ieee80211_create_ibss(isc, isc->isc_des_chan);
} else {
ieee80211_begin_scan(isc, in);
}
break;
case IEEE80211_S_SCAN:
/* scan next */
if (isc->isc_flags & IEEE80211_F_ASCAN) {
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
}
break;
case IEEE80211_S_RUN:
/* beacon miss */
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): no recent beacons"
" from %s; rescanning\n",
ieee80211_ether_sprintf(isc->isc_bss->in_bssid)));
ieee80211_free_allnodes(isc);
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
/* timeout, restart scan */
in = ieee80211_find_node(isc, isc->isc_bss->in_macaddr);
if (in != NULL) {
in->in_fails++;
}
ieee80211_begin_scan(isc, isc->isc_bss);
break;
default:
break;
}
break;
case IEEE80211_S_AUTH:
switch (ostate) {
case IEEE80211_S_INIT:
ATH_DEBUG((ATH_DBG_80211, "ath(): "
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_SCAN:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_AUTH, 1);
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
switch (mgt) {
case IEEE80211_FC0_SUBTYPE_AUTH:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_AUTH, 2);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
/* ignore and retry scan on timeout */
break;
}
break;
case IEEE80211_S_RUN:
switch (mgt) {
case IEEE80211_FC0_SUBTYPE_AUTH:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_AUTH, 2);
isc->isc_state = ostate; /* stay RUN */
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
/* try to reauth */
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_AUTH, 1);
break;
}
break;
}
break;
case IEEE80211_S_ASSOC:
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
case IEEE80211_S_ASSOC:
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_AUTH:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
break;
case IEEE80211_S_RUN:
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
break;
}
break;
case IEEE80211_S_RUN:
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_AUTH:
case IEEE80211_S_RUN:
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_SCAN: /* adhoc/hostap mode */
case IEEE80211_S_ASSOC: /* infra mode */
if (isc->isc_opmode == IEEE80211_M_STA)
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): "
"associated with %s\n",
ieee80211_ether_sprintf(in->in_bssid)));
else
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): "
"asynchronized with %s\n",
ieee80211_ether_sprintf(in->in_bssid)));
ATH_DEBUG((ATH_DBG_80211, "ath: "
"_ieee80211_new_state(): "
"essid %s, channel %d, start %uMb\n",
ieee80211_essid_sprintf(in->in_essid,
in->in_esslen),
ieee80211_chan2ieee(isc, in->in_chan),
IEEE80211_RATE2MBS(in->in_rates.ir_rates[
in->in_txrate])));
isc->isc_mgt_timeout = 0;
break;
}
break;
}
return (0);
}
int
ieee80211_new_state(ieee80211com_t *isc, enum ieee80211_state nstate,
int32_t mgt)
{
int result;
mutex_enter(&isc->isc_genlock);
result = _ieee80211_new_state(isc, nstate, mgt);
mutex_exit(&isc->isc_genlock);
return (result);
}
static void
ieee80211_timeout_nodes(ieee80211com_t *isc)
{
struct ieee80211_node *in, *nextbs;
for (in = list_head(&isc->isc_in_list); in != NULL; ) {
if (++in->in_inact <= IEEE80211_INACT_MAX) {
in = list_next(&isc->isc_in_list, in);
continue;
}
ATH_DEBUG((ATH_DBG_80211, "ath: "
"station %s timed out due to inactivity"
" (%u secs)\n", ieee80211_ether_sprintf(in->in_macaddr),
in->in_inact));
nextbs = list_next(&isc->isc_in_list, in);
IEEE80211_SEND_MGMT(isc, in,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_EXPIRE);
ieee80211_free_node(isc, in);
in = nextbs;
}
if (!list_empty(&isc->isc_in_list))
isc->isc_inact_timeout = IEEE80211_INACT_WAIT;
}
int
ieee80211_mgmt_output(ieee80211com_t *isc, struct ieee80211_node *in,
mblk_t *mp, int type)
{
struct ieee80211_frame *wh;
ASSERT(mutex_owned(&isc->isc_genlock));
ASSERT(in != NULL);
in->in_inact = 0;
mp->b_rptr -= sizeof (struct ieee80211_frame);
wh = (struct ieee80211_frame *)mp->b_rptr;
wh->ifrm_fc[0] = IEEE80211_FC0_VERSION_0 |
IEEE80211_FC0_TYPE_MGT | type;
wh->ifrm_fc[1] = IEEE80211_FC1_DIR_NODS;
*(uint16_t *)wh->ifrm_dur = 0;
*(uint16_t *)wh->ifrm_seq =
LE_16(in->in_txseq << IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseq++;
IEEE80211_ADDR_COPY(wh->ifrm_addr1, in->in_macaddr);
IEEE80211_ADDR_COPY(wh->ifrm_addr2, isc->isc_macaddr);
IEEE80211_ADDR_COPY(wh->ifrm_addr3, in->in_bssid);
(void) (*isc->isc_mgmt_send)(isc, mp);
return (0);
}
mblk_t *
ieee80211_fill_header(ieee80211com_t *isc, mblk_t *mp_gld,
int32_t wep_txkey, struct ieee80211_node *in)
{
struct ieee80211_frame *wh;
struct ieee80211_llc *llc;
struct ether_header *eh;
mblk_t *mp_header;
uint32_t iv;
uint8_t *ivp;
ASSERT(mutex_owned(&isc->isc_genlock));
/*
* Alloc a new mblk struct for the whole IEEE80211 header.
*/
if ((mp_header = allocb(HEADERSPACE, BPRI_MED)) == NULL) {
ath_problem("ath: ieee80211_encap(): can't alloc mblk!\n");
return (NULL);
}
mp_header->b_wptr = mp_header->b_rptr +
sizeof (struct ieee80211_frame);
eh = (struct ether_header *)mp_gld->b_rptr;
/*
* Fill 802.11 field.
*/
wh = (struct ieee80211_frame *)mp_header->b_rptr;
wh->ifrm_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(uint16_t *)wh->ifrm_dur = 0;
*(uint16_t *)wh->ifrm_seq =
LE_16(in->in_txseq << IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseq++;
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
wh->ifrm_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->ifrm_addr1, in->in_bssid);
IEEE80211_ADDR_COPY(wh->ifrm_addr2,
eh->ether_shost.ether_addr_octet);
IEEE80211_ADDR_COPY(wh->ifrm_addr3,
eh->ether_dhost.ether_addr_octet);
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
wh->ifrm_fc[1] = IEEE80211_FC1_DIR_NODS;
IEEE80211_ADDR_COPY(wh->ifrm_addr1,
eh->ether_dhost.ether_addr_octet);
IEEE80211_ADDR_COPY(wh->ifrm_addr2,
eh->ether_shost.ether_addr_octet);
IEEE80211_ADDR_COPY(wh->ifrm_addr3, in->in_bssid);
break;
case IEEE80211_M_HOSTAP:
wh->ifrm_fc[1] = IEEE80211_FC1_DIR_FROMDS;
IEEE80211_ADDR_COPY(wh->ifrm_addr1,
eh->ether_dhost.ether_addr_octet);
IEEE80211_ADDR_COPY(wh->ifrm_addr2, in->in_bssid);
IEEE80211_ADDR_COPY(wh->ifrm_addr3,
eh->ether_shost.ether_addr_octet);
break;
}
if (isc->isc_flags & IEEE80211_F_WEPON) {
wh->ifrm_fc[1] |= IEEE80211_FC1_WEP;
ivp = mp_header->b_rptr + sizeof (struct ieee80211_frame);
/*
* IV must not duplicate during the lifetime of the key.
* But no mechanism to renew keys is defined in IEEE 802.11
* WEP. And IV may be duplicated between other stations
* because of the session key itself is shared.
* So we use pseudo random IV for now, though it is not the
* right way.
*/
iv = isc->isc_iv;
/*
* Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
* (B, 255, N) with 3 <= B < 8
*/
if ((iv & 0xff00) == 0xff00) {
int B = (iv & 0xff0000) >> 16;
if (3 <= B && B < 16)
iv = (B+1) << 16;
}
isc->isc_iv = iv + 1;
#ifdef ATH_HOST_BIG_ENDIAN
ivp[0] = iv >> 0;
ivp[1] = iv >> 8;
ivp[2] = iv >> 16;
#else
ivp[2] = iv >> 0;
ivp[1] = iv >> 8;
ivp[0] = iv >> 16;
#endif /* ATH_HOST_BIG_ENDIAN */
/* Key ID and pad */
ivp[IEEE80211_WEP_IVLEN] = wep_txkey << 6;
/*
* The ICV length must be included into hdrlen and pktlen.
*/
mp_header->b_wptr +=
IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
}
/*
* CRC are added by H/W, not encaped by driver,
* but we must count it in pkt length.
*/
/*
* fill LLC and SNAP fields.
*/
llc = (struct ieee80211_llc *)mp_header->b_wptr;
llc->illc_dsap = LLC_DSAP;
llc->illc_ssap = LLC_SSAP;
llc->illc_control = LLC_CONTROL;
llc->illc_oc[0] = SNAP_OC0;
llc->illc_oc[1] = SNAP_OC1;
llc->illc_oc[2] = SNAP_OC2;
llc->illc_ether_type = eh->ether_type;
mp_header->b_wptr += sizeof (struct ieee80211_llc);
return (mp_header);
}
mblk_t *
ieee80211_decap(mblk_t *mp)
{
struct ether_header *eh;
struct ieee80211_frame wh;
struct ieee80211_llc *llc;
if ((mp->b_wptr - mp->b_rptr) < (sizeof (wh) + sizeof (*llc))) {
freemsg(mp);
return (NULL);
}
bcopy(mp->b_rptr, &wh, sizeof (struct ieee80211_frame));
mp->b_rptr += sizeof (struct ieee80211_frame);
llc = (struct ieee80211_llc *)mp->b_rptr;
if (llc->illc_dsap == LLC_DSAP && llc->illc_ssap == LLC_SSAP &&
llc->illc_control == LLC_CONTROL && llc->illc_oc[0] == SNAP_OC0 &&
llc->illc_oc[1] == SNAP_OC1 && llc->illc_oc[2] == SNAP_OC2) {
mp->b_rptr += sizeof (struct ieee80211_llc);
llc = NULL;
}
/*
* we are sure that the size of ieee80211_frame plus llc is
* larger than the size of ether_header,
* so there has enough space to encap ether_header in this mblk.
*/
mp->b_rptr -= sizeof (struct ether_header);
eh = (struct ether_header *)mp->b_rptr;
switch (wh.ifrm_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
IEEE80211_ADDR_COPY(eh->ether_dhost.ether_addr_octet,
wh.ifrm_addr1);
IEEE80211_ADDR_COPY(eh->ether_shost.ether_addr_octet,
wh.ifrm_addr2);
break;
case IEEE80211_FC1_DIR_TODS:
IEEE80211_ADDR_COPY(eh->ether_dhost.ether_addr_octet,
wh.ifrm_addr3);
IEEE80211_ADDR_COPY(eh->ether_shost.ether_addr_octet,
wh.ifrm_addr2);
break;
case IEEE80211_FC1_DIR_FROMDS:
IEEE80211_ADDR_COPY(eh->ether_dhost.ether_addr_octet,
wh.ifrm_addr1);
IEEE80211_ADDR_COPY(eh->ether_shost.ether_addr_octet,
wh.ifrm_addr3);
break;
case IEEE80211_FC1_DIR_DSTODS:
/* not yet supported */
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_decap(): DS to DS\n"));
freemsg(mp);
return (NULL);
}
if (llc != NULL)
eh->ether_type = htons(mp->b_wptr - mp->b_rptr - sizeof (*eh));
return (mp);
}
/*
* This function doesn't need mutex protection.
*/
void
ieee80211_dump_pkt(uint8_t *buf, int32_t len, int32_t rate, int32_t rssi)
{
struct ieee80211_frame *wh;
int32_t i;
int8_t buf1[100], buf2[25];
bzero(buf1, sizeof (buf1));
bzero(buf2, sizeof (buf2));
wh = (struct ieee80211_frame *)buf;
switch (wh->ifrm_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
(void) sprintf(buf2, "NODS %s",
ieee80211_ether_sprintf(wh->ifrm_addr2));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "->%s",
ieee80211_ether_sprintf(wh->ifrm_addr1));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "(%s)",
ieee80211_ether_sprintf(wh->ifrm_addr3));
(void) strcat(buf1, buf2);
break;
case IEEE80211_FC1_DIR_TODS:
(void) sprintf(buf2, "TODS %s",
ieee80211_ether_sprintf(wh->ifrm_addr2));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "->%s",
ieee80211_ether_sprintf(wh->ifrm_addr3));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "(%s)",
ieee80211_ether_sprintf(wh->ifrm_addr1));
(void) strcat(buf1, buf2);
break;
case IEEE80211_FC1_DIR_FROMDS:
(void) sprintf(buf2, "FRDS %s",
ieee80211_ether_sprintf(wh->ifrm_addr3));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "->%s",
ieee80211_ether_sprintf(wh->ifrm_addr1));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "(%s)",
ieee80211_ether_sprintf(wh->ifrm_addr2));
(void) strcat(buf1, buf2);
break;
case IEEE80211_FC1_DIR_DSTODS:
(void) sprintf(buf2, "DSDS %s",
ieee80211_ether_sprintf((uint8_t *)&wh[1]));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "->%s ",
ieee80211_ether_sprintf(wh->ifrm_addr3));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "%s",
ieee80211_ether_sprintf(wh->ifrm_addr2));
(void) strcat(buf1, buf2);
(void) sprintf(buf2, "->%s",
ieee80211_ether_sprintf(wh->ifrm_addr1));
(void) strcat(buf1, buf2);
break;
}
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_dump_pkt(): %s", buf1));
bzero(buf1, sizeof (buf1));
switch (wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_DATA:
(void) sprintf(buf2, "data");
break;
case IEEE80211_FC0_TYPE_MGT:
(void) sprintf(buf2, "%s",
ieee80211_mgt_subtype_name[
(wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
>> IEEE80211_FC0_SUBTYPE_SHIFT]);
break;
default:
(void) sprintf(buf2, "type#%d",
wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK);
break;
}
(void) strcat(buf1, buf2);
if (wh->ifrm_fc[1] & IEEE80211_FC1_WEP) {
(void) sprintf(buf2, " WEP");
(void) strcat(buf1, buf2);
}
if (rate >= 0) {
(void) sprintf(buf2, " %dM", rate / 2);
(void) strcat(buf1, buf2);
}
if (rssi >= 0) {
(void) sprintf(buf2, " +%d", rssi);
(void) strcat(buf1, buf2);
}
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_dump_pkt(): %s", buf1));
bzero(buf1, sizeof (buf1));
if (len > 0) {
for (i = 0; i < (len > 40 ? 40 : len); i++) {
if ((i & 0x03) == 0)
(void) strcat(buf1, " ");
(void) sprintf(buf2, "%02x", buf[i]);
(void) strcat(buf1, buf2);
}
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_dump_pkt(): %s",
buf1));
}
}
/*
* Mark the basic rates for the 11g rate table based on the
* operating mode. For real 11g we mark all the 11b rates
* and 6, 12, and 24 OFDM. For 11b compatibility we mark only
* 11b rates. There's also a pseudo 11a-mode used to mark only
* the basic OFDM rates.
*/
static void
ieee80211_set11gbasicrates(struct ieee80211_rateset *rs,
enum ieee80211_phymode mode)
{
static const struct ieee80211_rateset basic[] = {
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */
{ 7, { 2, 4, 11, 22, 12, 24, 48 } }, /* IEEE80211_MODE_11G */
{ 0 }, /* IEEE80211_MODE_TURBO */
};
int32_t i, j;
for (i = 0; i < rs->ir_nrates; i++) {
rs->ir_rates[i] &= IEEE80211_RATE_VAL;
for (j = 0; j < basic[mode].ir_nrates; j++)
if (basic[mode].ir_rates[j] == rs->ir_rates[i]) {
rs->ir_rates[i] |= IEEE80211_RATE_BASIC;
break;
}
}
}
/*
* Set the current phy mode and recalculate the active channel
* set based on the available channels for this mode. Also
* select a new default/current channel if the current one is
* inappropriate for this mode.
*/
static int
ieee80211_setmode(ieee80211com_t *isc, enum ieee80211_phymode mode)
{
static const uint32_t chanflags[] = {
0, /* IEEE80211_MODE_AUTO */
IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */
};
struct ieee80211channel *ch;
uint32_t modeflags;
int32_t i;
/* validate new mode */
if ((isc->isc_modecaps & (1 << mode)) == 0) {
ath_problem("ath: ieee80211_setmode(): mode %u not supported"
" (caps 0x%x)\n", mode, isc->isc_modecaps);
return (EINVAL);
}
/*
* Verify at least one channel is present in the available
* channel list before committing to the new mode.
*/
ASSERT(mode < ATH_N(chanflags));
modeflags = chanflags[mode];
/* isc_channels size is IEEE80211_CHAN_MAX + 1, so no problem */
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
ch = &isc->isc_channels[i];
if (mode == IEEE80211_MODE_AUTO) {
/* ignore turbo channels for autoselect */
if ((ch->ich_flags & ~IEEE80211_CHAN_TURBO) != 0)
break;
} else {
if ((ch->ich_flags & modeflags) == modeflags)
break;
}
}
if (i > IEEE80211_CHAN_MAX) {
ath_problem("ath: ieee80211_setmode(): "
"no channel found for mode %u\n", mode);
return (EINVAL);
}
/*
* Calculate the active channel set.
*/
bzero(isc->isc_chan_active, sizeof (isc->isc_chan_active));
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
ch = &isc->isc_channels[i];
if (mode == IEEE80211_MODE_AUTO) {
/* take anything but pure turbo channels */
if ((ch->ich_flags & ~IEEE80211_CHAN_TURBO) != 0)
setbit(isc->isc_chan_active, i);
} else {
if ((ch->ich_flags & modeflags) == modeflags)
setbit(isc->isc_chan_active, i);
}
}
/*
* If no current/default channel is setup or the current
* channel is wrong for the mode then pick the first
* available channel from the active list. This is likely
* not the right one.
*/
if (isc->isc_ibss_chan == NULL ||
isclr(isc->isc_chan_active,
ieee80211_chan2ieee(isc, isc->isc_ibss_chan))) {
for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
if (isset(isc->isc_chan_active, i)) {
isc->isc_ibss_chan = &isc->isc_channels[i];
break;
}
}
/*
* Set/reset state flags that influence beacon contents, etc.
*/
if (isc->isc_caps & IEEE80211_C_SHPREAMBLE)
isc->isc_flags |= IEEE80211_F_SHPREAMBLE;
if (mode == IEEE80211_MODE_11G) {
if (isc->isc_caps & IEEE80211_C_SHSLOT)
isc->isc_flags |= IEEE80211_F_SHSLOT;
ieee80211_set11gbasicrates(&isc->isc_sup_rates[mode],
IEEE80211_MODE_11G);
} else {
isc->isc_flags &= ~(IEEE80211_F_SHSLOT);
}
/*
* Setup an initial rate set according to the
* current/default channel. This will be changed
* when scanning but must exist now so drivers have
* consistent state of ic_ibss_chan.
*/
if (isc->isc_bss)
isc->isc_bss->in_rates = isc->isc_sup_rates[mode];
isc->isc_curmode = mode;
return (0);
}
/*
* If (its return value & IEEE80211_RATE_BASIC != 0),
* the rate negotiation or fix rate is failed.
*/
static int
ieee80211_fix_rate(ieee80211com_t *isc,
struct ieee80211_node *in, int32_t flags)
{
int32_t i, j, ignore, error;
int32_t okrate, badrate;
struct ieee80211_rateset *srs, *nrs;
uint8_t r;
error = 0;
okrate = badrate = 0;
srs = &isc->isc_sup_rates[ieee80211_chan2mode(isc, in->in_chan)];
nrs = &in->in_rates;
for (i = 0; i < in->in_rates.ir_nrates; ) {
ignore = 0;
if (flags & IEEE80211_F_DOSORT) {
/*
* Sort rates.
*/
for (j = i + 1; j < nrs->ir_nrates; j++) {
if (IEEE80211_RV(nrs->ir_rates[i]) >
IEEE80211_RV(nrs->ir_rates[j])) {
r = nrs->ir_rates[i];
nrs->ir_rates[i] = nrs->ir_rates[j];
nrs->ir_rates[j] = r;
}
}
}
r = nrs->ir_rates[i] & IEEE80211_RATE_VAL;
badrate = r;
if (flags & IEEE80211_F_DOFRATE) {
/*
* Apply fixed rate constraint. Note that we do
* not apply the constraint to basic rates as
* otherwise we may not be able to associate if
* the rate set we submit to the AP is invalid
* (e.g. fix rate at 36Mb/s which is not a basic
* rate for 11a operation).
*/
if ((nrs->ir_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
isc->isc_fixed_rate >= 0 &&
r != IEEE80211_RV(srs->ir_rates
[isc->isc_fixed_rate]))
ignore++;
}
if (flags & IEEE80211_F_DONEGO) {
/*
* Check against supported rates.
*/
for (j = 0; j < srs->ir_nrates; j++) {
if (r == IEEE80211_RV(srs->ir_rates[j]))
break;
}
if (j == srs->ir_nrates) {
if (nrs->ir_rates[i] & IEEE80211_RATE_BASIC)
error++;
ignore++;
}
}
if (flags & IEEE80211_F_DODEL) {
/*
* Delete unacceptable rates.
*/
if (ignore) {
nrs->ir_nrates--;
for (j = i; j < nrs->ir_nrates; j++)
nrs->ir_rates[j] = nrs->ir_rates[j + 1];
nrs->ir_rates[j] = 0;
continue;
}
}
if (!ignore)
okrate = nrs->ir_rates[i];
i++;
}
if (okrate == 0 || error != 0)
return (badrate | IEEE80211_RATE_BASIC);
else
return (IEEE80211_RV(okrate));
}
/*
* Complete a scan of potential channels.
*/
static void
ieee80211_end_scan(ieee80211com_t *isc)
{
struct ieee80211_node *in, *selbs;
uint8_t rate;
int32_t fail;
ASSERT(isc->isc_state == IEEE80211_S_SCAN);
ASSERT(mutex_owned(&isc->isc_genlock));
isc->isc_flags &= ~IEEE80211_F_ASCAN;
cv_broadcast(&isc->isc_scan_cv);
in = list_head(&isc->isc_in_list);
if (in == NULL) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_end_scan(): "
"no scan candidate\n"));
notfound:
if (isc->isc_opmode == IEEE80211_M_IBSS &&
(isc->isc_flags & IEEE80211_F_IBSSON) &&
isc->isc_des_esslen != 0) {
ieee80211_create_ibss(isc, isc->isc_ibss_chan);
return;
}
mutex_exit(&isc->isc_genlock);
delay(drv_usectohz(200000));
mutex_enter(&isc->isc_genlock);
ieee80211_begin_scan(isc, isc->isc_bss);
return;
}
selbs = NULL;
for (; in != NULL; in = list_next(&isc->isc_in_list, in)) {
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_end_scan(): isc_bss->in_bssid=%s",
ieee80211_ether_sprintf(in->in_bssid)));
if (in->in_fails) {
/*
* The configuration of the access points may change
* during my scan. So delete the entry for the AP
* and retry to associate if there is another beacon.
*/
if (in->in_fails++ > 2)
ieee80211_free_node(isc, in);
continue;
}
fail = 0;
if (in->in_chan == NULL || in->in_chan->ich_flags == 0)
fail |= 0x01;
if (isc->isc_des_chan !=
(struct ieee80211channel *)IEEE80211_CHAN_ANY &&
in->in_chan != isc->isc_des_chan)
fail |= 0x01;
if (isc->isc_opmode == IEEE80211_M_IBSS) {
if ((in->in_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= 0x02;
} else {
if ((in->in_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= 0x02;
}
if (isc->isc_flags & IEEE80211_F_WEPON) {
if ((in->in_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= 0x04;
} else {
if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= 0x04;
}
rate = ieee80211_fix_rate(isc, in, IEEE80211_F_DONEGO);
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
if (isc->isc_des_esslen != 0 &&
(in->in_esslen != isc->isc_des_esslen ||
bcmp(in->in_essid, isc->isc_des_essid,
isc->isc_des_esslen) != 0))
fail |= 0x10;
if ((isc->isc_flags & IEEE80211_F_DESBSSID) &&
!IEEE80211_ADDR_EQ(isc->isc_des_bssid, in->in_bssid))
fail |= 0x20;
if (!fail) {
if (selbs == NULL)
selbs = in;
else if (ieee80211_get_rssi(in) >
ieee80211_get_rssi(selbs))
selbs = in;
}
}
if (selbs == NULL)
goto notfound;
bcopy(selbs, isc->isc_bss, sizeof (struct ieee80211_node));
if (isc->isc_opmode == IEEE80211_M_IBSS) {
(void) ieee80211_fix_rate(isc, isc->isc_bss,
IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
IEEE80211_F_DODEL);
if (isc->isc_bss->in_rates.ir_nrates == 0) {
selbs->in_fails++;
goto notfound;
}
(void) _ieee80211_new_state(isc, IEEE80211_S_RUN, -1);
} else
(void) _ieee80211_new_state(isc, IEEE80211_S_AUTH, -1);
}
/*
* Switch to the next channel marked for scanning.
* This one is only called by multi-func thread.
*/
static void
ieee80211_next_scan(ieee80211com_t *isc)
{
struct ieee80211channel *chan;
ASSERT(isc->isc_state == IEEE80211_S_SCAN);
ASSERT(mutex_owned(&isc->isc_genlock));
chan = isc->isc_bss->in_chan;
for (;;) {
if (++chan > &isc->isc_channels[IEEE80211_CHAN_MAX])
chan = &isc->isc_channels[0];
if (isset(isc->isc_chan_scan, ieee80211_chan2ieee(isc, chan))) {
/*
* Honor channels marked passive-only
* during an active scan.
*/
if ((isc->isc_flags & IEEE80211_F_ASCAN) == 0 ||
(chan->ich_flags & IEEE80211_CHAN_PASSIVE) == 0)
break;
}
if (chan == isc->isc_bss->in_chan) {
ieee80211_end_scan(isc);
return;
}
}
clrbit(isc->isc_chan_scan, ieee80211_chan2ieee(isc, chan));
isc->isc_bss->in_chan = chan;
(void) _ieee80211_new_state(isc, IEEE80211_S_SCAN, -1);
}
static void
ieee80211_setup_node(ieee80211com_t *isc,
struct ieee80211_node *in, uint8_t *macaddr)
{
int32_t hash;
ASSERT(mutex_owned(&isc->isc_genlock));
IEEE80211_ADDR_COPY(in->in_macaddr, macaddr);
hash = IEEE80211_NODE_HASH(macaddr);
list_insert_tail(&isc->isc_in_list, in);
list_insert_tail(&isc->isc_inhash_list[hash], in);
/*
* Note we don't enable the inactive timer when acting
* as a station. Nodes created in this mode represent
* AP's identified while scanning. If we time them out
* then several things happen: we can't return the data
* to users to show the list of AP's we encountered, and
* more importantly, we'll incorrectly deauthenticate
* ourself because the inactivity timer will kick us off.
*/
if (isc->isc_opmode != IEEE80211_M_STA)
isc->isc_inact_timeout = IEEE80211_INACT_WAIT;
}
static struct ieee80211_node *
ieee80211_alloc_node(ieee80211com_t *isc, uint8_t *macaddr)
{
struct ieee80211_node *in = (*isc->isc_node_alloc)(isc);
bzero(in, sizeof (struct ieee80211_node));
ieee80211_setup_node(isc, in, macaddr);
return (in);
}
static struct ieee80211_node *
ieee80211_dup_bss(ieee80211com_t *isc, uint8_t *macaddr)
{
struct ieee80211_node *in;
in = kmem_zalloc(sizeof (struct ieee80211_node), KM_SLEEP);
ieee80211_setup_node(isc, in, macaddr);
return (in);
}
/*
* Find a node state block given the mac address. Note that
* this returns the first node found with the mac address.
*/
struct ieee80211_node *
ieee80211_find_node(ieee80211com_t *isc, uint8_t *macaddr)
{
struct ieee80211_node *in;
int32_t hash;
hash = IEEE80211_NODE_HASH(macaddr);
in = list_head(&isc->isc_inhash_list[hash]);
while (in != NULL) {
if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr))
break;
in = list_next(&isc->isc_inhash_list[hash], in);
}
return (in);
}
/*
* Like find but search based on the channel too.
*/
struct ieee80211_node *
ieee80211_lookup_node(ieee80211com_t *isc, uint8_t *macaddr,
struct ieee80211channel *chan)
{
struct ieee80211_node *in;
int32_t hash;
hash = IEEE80211_NODE_HASH(macaddr);
in = list_head(&isc->isc_inhash_list[hash]);
while (in != NULL) {
if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr) &&
(in->in_chan == chan))
break;
in = list_next(&isc->isc_inhash_list[hash], in);
}
return (in);
}
/*
* Install received rate set information in the node's state block.
* If (its return value & IEEE80211_RATE_BASIC != 0),
* the rate negotiation or fix rate is failed.
*/
static int32_t
ieee80211_setup_rates(ieee80211com_t *isc, struct ieee80211_node *in,
uint8_t *rates, uint8_t *xrates, int32_t flags)
{
struct ieee80211_rateset *rs = &in->in_rates;
bzero(rs, sizeof (*rs));
rs->ir_nrates = rates[1];
bcopy(rates + 2, rs->ir_rates, rs->ir_nrates);
if (xrates != NULL) {
uint8_t nxrates;
/*
* Tack on 11g extended supported rate element.
*/
nxrates = xrates[1];
if (rs->ir_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->ir_nrates;
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_setup_rates(): extended rate set"
" too large; only using %u of %u rates\n",
nxrates, xrates[1]));
}
bcopy(xrates + 2, rs->ir_rates + rs->ir_nrates, nxrates);
rs->ir_nrates += nxrates;
}
return (ieee80211_fix_rate(isc, in, flags));
}
/*
* Misc management frame encapsulation functions.
*/
static uint8_t *
ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
int32_t nrates;
*frm++ = IEEE80211_ELEMID_RATES;
nrates = rs->ir_nrates;
if (nrates > IEEE80211_RATE_SIZE)
nrates = IEEE80211_RATE_SIZE;
*frm++ = nrates;
bcopy(rs->ir_rates, frm, nrates);
return (frm + nrates);
}
static uint8_t *
ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
/*
* Add an extended supported rates element if operating in 11g mode.
*/
if (rs->ir_nrates > IEEE80211_RATE_SIZE) {
int32_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE;
*frm++ = IEEE80211_ELEMID_XRATES;
*frm++ = nrates;
bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates);
frm += nrates;
}
return (frm);
}
static uint8_t *
ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len)
{
*frm++ = IEEE80211_ELEMID_SSID;
*frm++ = len;
bcopy(ssid, frm, len);
return (frm + len);
}
/*
* Following functions are responsible for management frame encapsulation.
*/
static int32_t
ieee80211_send_prreq(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t dummy)
{
int32_t ret, pktlen;
mblk_t *mp;
uint8_t *frm;
enum ieee80211_phymode mode;
_NOTE(ARGUNUSED(dummy));
ASSERT(mutex_owned(&isc->isc_genlock));
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
2 + isc->isc_des_esslen +
2 + IEEE80211_RATE_SIZE +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_prreq(): no space\n");
return (ENOMEM);
}
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = mp->b_rptr;
frm = ieee80211_add_ssid(frm, isc->isc_des_essid, isc->isc_des_esslen);
mode = ieee80211_chan2mode(isc, in->in_chan);
frm = ieee80211_add_rates(frm, &isc->isc_sup_rates[mode]);
frm = ieee80211_add_xrates(frm, &isc->isc_sup_rates[mode]);
mp->b_wptr = frm;
ret = ieee80211_mgmt_output(isc, in, mp, type);
isc->isc_mgt_timeout = IEEE80211_TRANS_WAIT;
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_send_prreq() "
"channel=%u\n", ieee80211_chan2ieee(isc, in->in_chan)));
return (ret);
}
static int32_t
ieee80211_send_prresp(ieee80211com_t *isc, struct ieee80211_node *bs0,
int32_t type, int32_t dummy)
{
mblk_t *mp;
uint8_t *frm;
struct ieee80211_node *in = isc->isc_bss;
uint16_t capinfo;
int32_t pktlen;
_NOTE(ARGUNUSED(dummy));
ASSERT(mutex_owned(&isc->isc_genlock));
/*
* probe response frame format
* [8] time stamp
* [2] beacon interval
* [2] cabability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] parameter set (IBSS)
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
8 + 2 + 2 + 2 +
2 + in->in_esslen +
2 + IEEE80211_RATE_SIZE +
6 +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_prresp(): alloc failed.\n");
return (ENOMEM);
}
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = mp->b_rptr;
bzero(frm, 8); /* timestamp */
frm += 8;
*(uint16_t *)frm = LE_16(in->in_intval);
frm += 2;
if (isc->isc_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = IEEE80211_CAPINFO_ESS;
if (isc->isc_flags & IEEE80211_F_WEPON)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
*(uint16_t *)frm = LE_16(capinfo);
frm += 2;
frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
frm = ieee80211_add_rates(frm, &in->in_rates);
if (isc->isc_opmode == IEEE80211_M_IBSS) {
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* ATIM window */
} else { /* IEEE80211_M_HOSTAP */
/* TIM */
*frm++ = IEEE80211_ELEMID_TIM;
*frm++ = 4; /* length */
*frm++ = 0; /* DTIM count */
*frm++ = 1; /* DTIM period */
*frm++ = 0; /* bitmap control */
*frm++ = 0; /* Partial Virtual Bitmap (variable length) */
}
frm = ieee80211_add_xrates(frm, &in->in_rates);
mp->b_wptr = frm;
return (ieee80211_mgmt_output(isc, bs0, mp, type));
}
static int32_t
ieee80211_send_auth(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t seq)
{
mblk_t *mp;
uint16_t *frm;
int32_t ret, pktlen;
ASSERT(mutex_owned(&isc->isc_genlock));
pktlen = sizeof (struct ieee80211_frame) + 3 * sizeof (uint16_t);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_auth(): allocb failed\n");
return (ENOMEM);
}
mp->b_wptr = mp->b_rptr + pktlen;
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = (uint16_t *)mp->b_rptr;
/* shared key auth */
frm[0] = LE_16(IEEE80211_AUTH_ALG_OPEN);
frm[1] = LE_16(seq);
frm[2] = 0; /* status */
ret = ieee80211_mgmt_output(isc, in, mp, type);
if (isc->isc_opmode == IEEE80211_M_STA)
isc->isc_mgt_timeout = IEEE80211_TRANS_WAIT;
return (ret);
}
static int32_t
ieee80211_send_deauth(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t reason)
{
mblk_t *mp;
uint16_t *frm;
int32_t pktlen;
ASSERT(mutex_owned(&isc->isc_genlock));
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_send_deauth(): station %s deauthenticate",
" (reason %d)\n", ieee80211_ether_sprintf(in->in_macaddr), reason));
pktlen = sizeof (struct ieee80211_frame) + sizeof (uint16_t);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_deauth(): allocb failed\n");
return (ENOMEM);
}
mp->b_wptr = mp->b_rptr + pktlen;
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = (uint16_t *)mp->b_rptr;
frm[0] = LE_16(reason);
return (ieee80211_mgmt_output(isc, in, mp, type));
}
static int32_t
ieee80211_send_asreq(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t dummy)
{
mblk_t *mp;
uint8_t *frm;
uint16_t capinfo = 0;
int32_t ret, pktlen;
_NOTE(ARGUNUSED(dummy));
ASSERT(mutex_owned(&isc->isc_genlock));
/*
* asreq frame format
* [2] capability information
* [2] listen interval
* [6*] current AP address (reassoc only)
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
sizeof (capinfo) + sizeof (uint16_t) + IEEE80211_ADDR_LEN +
2 + in->in_esslen + 2 + IEEE80211_RATE_SIZE +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_asreq: allocb failed\n");
return (ENOMEM);
}
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = (uint8_t *)mp->b_rptr;
capinfo = 0;
if (isc->isc_opmode == IEEE80211_M_IBSS)
capinfo |= IEEE80211_CAPINFO_IBSS;
else /* IEEE80211_M_STA */
capinfo |= IEEE80211_CAPINFO_ESS;
if (isc->isc_flags & IEEE80211_F_WEPON)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if (isc->isc_flags & IEEE80211_F_SHPREAMBLE)
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (isc->isc_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
capinfo |= 0x0020;
*(uint16_t *)frm = LE_16(capinfo);
frm += 2;
*(uint16_t *)frm = LE_16(isc->isc_lintval);
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
IEEE80211_ADDR_COPY(frm, isc->isc_bss->in_bssid);
frm += IEEE80211_ADDR_LEN;
}
frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
frm = ieee80211_add_rates(frm, &in->in_rates);
frm = ieee80211_add_xrates(frm, &in->in_rates);
mp->b_wptr = frm;
ret = ieee80211_mgmt_output(isc, in, mp, type);
isc->isc_mgt_timeout = IEEE80211_TRANS_WAIT;
return (ret);
}
static int32_t
ieee80211_send_asresp(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t status)
{
mblk_t *mp;
uint8_t *frm;
uint16_t capinfo;
int32_t pktlen;
ASSERT(mutex_owned(&isc->isc_genlock));
/*
* asreq frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
sizeof (capinfo) +
sizeof (uint16_t) +
sizeof (uint16_t) +
2 + IEEE80211_RATE_SIZE +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_asresp: allocb failed\n");
return (ENOMEM);
}
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = (uint8_t *)mp->b_rptr;
capinfo = IEEE80211_CAPINFO_ESS;
if (isc->isc_flags & IEEE80211_F_WEPON)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
*(uint16_t *)frm = LE_16(capinfo);
frm += 2;
*(uint16_t *)frm = LE_16(status);
frm += 2;
if (status == IEEE80211_STATUS_SUCCESS && in != NULL)
*(uint16_t *)frm = LE_16(in->in_associd);
else
*(uint16_t *)frm = LE_16(0);
frm += 2;
if (in != NULL) {
frm = ieee80211_add_rates(frm, &in->in_rates);
frm = ieee80211_add_xrates(frm, &in->in_rates);
} else {
frm = ieee80211_add_rates(frm, &isc->isc_bss->in_rates);
frm = ieee80211_add_xrates(frm, &isc->isc_bss->in_rates);
}
mp->b_wptr = frm;
return (ieee80211_mgmt_output(isc, in, mp, type));
}
static int32_t
ieee80211_send_disassoc(ieee80211com_t *isc, struct ieee80211_node *in,
int32_t type, int32_t reason)
{
mblk_t *mp;
uint16_t *frm;
int32_t pktlen;
_NOTE(ARGUNUSED(type))
ASSERT(mutex_owned(&isc->isc_genlock));
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_send_disassoc(): station %s disassociate",
" (reason %d)\n", ieee80211_ether_sprintf(in->in_macaddr), reason));
pktlen = sizeof (struct ieee80211_frame) + sizeof (uint16_t);
mp = allocb(pktlen, BPRI_MED);
if (mp == NULL) {
ath_problem("ath: ieee80211_send_asresp: allocb failed\n");
return (ENOMEM);
}
mp->b_wptr = mp->b_rptr + pktlen;
mp->b_rptr += sizeof (struct ieee80211_frame);
frm = (uint16_t *)mp->b_rptr;
frm[0] = LE_16(reason);
return (ieee80211_mgmt_output(isc, in, mp,
IEEE80211_FC0_SUBTYPE_DISASSOC));
}
/*
* This handles both beacon and probe response frames.
*/
static void
ieee80211_recv_beacon(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
uint8_t *frm, *efrm, *tstamp, *bintval, *capinfo, *ssid;
uint8_t *rates, *xrates;
uint8_t chan, bchan, fhindex, erp;
uint16_t fhdwell;
ASSERT(mutex_owned(&isc->isc_genlock));
if (isc->isc_opmode != IEEE80211_M_IBSS &&
isc->isc_state != IEEE80211_S_SCAN) {
return;
}
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* beacon frame format
* [8] time stamp
* [2] beacon interval
* [2] cabability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] country information
* [tlv] parameter set (FH/DS)
* [tlv] erp information
* [tlv] extended supported rates
*/
tstamp = frm;
frm += 8;
bintval = frm;
frm += 2;
capinfo = frm;
frm += 2;
ssid = rates = xrates = NULL;
bchan = ieee80211_chan2ieee(isc, isc->isc_bss->in_chan);
chan = bchan;
fhdwell = 0;
fhindex = 0;
erp = 0;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_SSID:
ssid = frm;
break;
case IEEE80211_ELEMID_RATES:
rates = frm;
break;
case IEEE80211_ELEMID_COUNTRY:
/*
* don't care 'country', otherwise,
* just do:
* country = frm;
*/
break;
case IEEE80211_ELEMID_FHPARMS:
if (isc->isc_phytype == IEEE80211_T_FH) {
fhdwell = (frm[3] << 8) | frm[2];
chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
fhindex = frm[6];
}
break;
case IEEE80211_ELEMID_DSPARMS:
/*
* hack this since depending on phytype
* is problematic for multi-mode devices.
*/
if (isc->isc_phytype != IEEE80211_T_FH)
chan = frm[2];
break;
case IEEE80211_ELEMID_TIM:
break;
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
case IEEE80211_ELEMID_ERP:
if (frm[1] != 1) {
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_recv_beacon(): "
"%s: invalid ERP element; "
"length %u, expecting 1\n",
"ieee80211_recv_beacon", frm[1]));
break;
}
erp = frm[2];
break;
default:
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE, wh);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, wh);
if (isclr(isc->isc_chan_active, chan)) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_beacon(): "
"ignore %s with invalid channel %u\n",
IEEE80211_ISPROBE(wh) ? "probe response" : "beacon", chan));
return;
}
if (chan != bchan && isc->isc_phytype != IEEE80211_T_FH) {
/*
* Frame was received on a channel different from the
* one indicated in the DS/FH params element id; silently
* discard it.
*
* NB: this can happen due to signal leakage.
*/
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_beacon(): "
"ignore %s phytype %u on channel %u marked for %u\n",
IEEE80211_ISPROBE(wh) ? "probe response" : "beacon",
isc->isc_phytype,
bchan, chan));
return;
}
/*
* Use mac and channel for lookup so we collect all
* potential AP's when scanning. Otherwise we may
* see the same AP on multiple channels and will only
* record the last one. We could filter APs here based
* on rssi, etc. but leave that to the end of the scan
* so we can keep the selection criteria in one spot.
* This may result in a bloat of the scanned AP list but
* it shouldn't be too much.
*/
in = ieee80211_lookup_node(isc, wh->ifrm_addr2,
&isc->isc_channels[chan]);
if (in == NULL || isc->isc_state == IEEE80211_S_SCAN) {
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_recv_beacon(): essid = %s\n",
ieee80211_essid_sprintf(ssid + 2, ssid[1])));
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_beacon(): "
"from %s\n", ieee80211_ether_sprintf(wh->ifrm_addr2)));
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_recv_beacon(): to %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr1)));
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_beacon(): "
"caps 0x%x bintval %u erp 0x%x\n",
LE_16(*(uint16_t *)capinfo),
LE_16(*(uint16_t *)bintval), erp));
}
if (in == NULL) {
in = ieee80211_alloc_node(isc, wh->ifrm_addr2);
if (in == NULL)
return;
in->in_esslen = ssid[1];
bzero(in->in_essid, sizeof (in->in_essid));
bcopy(ssid + 2, in->in_essid, ssid[1]);
} else if (ssid[1] != 0 && IEEE80211_ISPROBE(wh)) {
/*
* Update ESSID at probe response to adopt hidden AP by
* Lucent/Cisco, which announces null ESSID in beacon.
*/
in->in_esslen = ssid[1];
bzero(in->in_essid, sizeof (in->in_essid));
bcopy(ssid + 2, in->in_essid, ssid[1]);
}
IEEE80211_ADDR_COPY(in->in_bssid, wh->ifrm_addr3);
ieee80211_add_recvhist(in, rssi, rstamp, rantenna);
bcopy(tstamp, in->in_tstamp, sizeof (in->in_tstamp));
in->in_intval = LE_16(*(uint16_t *)bintval);
in->in_capinfo = LE_16(*(uint16_t *)capinfo);
in->in_chan = &isc->isc_channels[chan];
in->in_fhdwell = fhdwell;
in->in_fhindex = fhindex;
in->in_erp = erp;
/* in_chan must have been setup */
(void) ieee80211_setup_rates(isc, in, rates, xrates,
IEEE80211_F_DOSORT);
}
static void
ieee80211_recv_prreq(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
uint8_t *frm, *efrm, *ssid, *rates, *xrates;
uint8_t rate;
int32_t allocbs;
ASSERT(mutex_owned(&isc->isc_genlock));
if (isc->isc_opmode == IEEE80211_M_STA)
return;
if (isc->isc_state != IEEE80211_S_RUN)
return;
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
ssid = rates = xrates = NULL;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_SSID:
ssid = frm;
break;
case IEEE80211_ELEMID_RATES:
rates = frm;
break;
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE, wh);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, wh);
if (ssid[1] != 0 &&
(ssid[1] != isc->isc_bss->in_esslen ||
bcmp(ssid + 2, isc->isc_bss->in_essid,
isc->isc_bss->in_esslen) != 0)) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_prreq(): "
"ssid unmatch %s from %s",
ieee80211_essid_sprintf(ssid + 2, ssid[1]),
ieee80211_ether_sprintf(wh->ifrm_addr2)));
return;
}
in = ieee80211_find_node(isc, wh->ifrm_addr2);
if (in == NULL) {
in = ieee80211_dup_bss(isc, wh->ifrm_addr2);
if (in == NULL) {
ath_problem("ath: ieee80211_recv_prreq(): "
"dup bss failed\n");
return;
}
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_recv_prreq: new req from %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2)));
allocbs = 1;
} else
allocbs = 0;
ieee80211_add_recvhist(in, rssi, rstamp, rantenna);
rate = ieee80211_setup_rates(isc, in, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
if (rate & IEEE80211_RATE_BASIC) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_prreq(): "
"rate negotiation fail: %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2)));
} else {
IEEE80211_SEND_MGMT(isc, in, IEEE80211_FC0_SUBTYPE_PROBE_RESP,
0);
}
if (allocbs && isc->isc_opmode == IEEE80211_M_HOSTAP)
ieee80211_free_node(isc, in);
else
ieee80211_unref_node(&in);
}
static void
ieee80211_recv_auth(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
uint8_t *frm, *efrm;
uint16_t algo, seq, status;
_NOTE(ARGUNUSED(rssi))
_NOTE(ARGUNUSED(rstamp))
_NOTE(ARGUNUSED(rantenna))
_NOTE(ARGUNUSED(mp))
ASSERT(mutex_owned(&isc->isc_genlock));
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* auth frame format
* [2] algorithm
* [2] sequence
* [2] status
* [tlv*] challenge
*/
if (frm + 6 > efrm) {
ATH_DEBUG((ATH_DBG_80211, "ath: "
"ieee80211_recv_auth: too short from %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2)));
return;
}
algo = LE_16(*(uint16_t *)frm);
seq = LE_16(*(uint16_t *)(frm + 2));
status = LE_16(*(uint16_t *)(frm + 4));
if (algo != IEEE80211_AUTH_ALG_OPEN) {
/* shared key auth */
ath_problem("ath: ieee80211_recv_auth(): "
"unsupported auth %d from %s\n",
algo, ieee80211_ether_sprintf(wh->ifrm_addr2));
return;
}
switch (isc->isc_opmode) {
case IEEE80211_M_IBSS:
if (isc->isc_state != IEEE80211_S_RUN || seq != 1)
return;
(void) _ieee80211_new_state(isc, IEEE80211_S_AUTH,
wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
case IEEE80211_M_AHDEMO:
break;
case IEEE80211_M_HOSTAP:
break;
case IEEE80211_M_STA:
if (isc->isc_state != IEEE80211_S_AUTH || seq != 2)
return;
if (status != 0) {
ath_log("ath: ieee80211_recv_auth(): "
"authentication failed (reason %d) for %s\n",
status, ieee80211_ether_sprintf(wh->ifrm_addr3));
in = ieee80211_find_node(isc, wh->ifrm_addr2);
if (in != NULL)
in->in_fails++;
return;
}
(void) _ieee80211_new_state(isc, IEEE80211_S_ASSOC,
wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
}
}
static void
ieee80211_recv_asreq(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
_NOTE(ARGUNUSED(isc))
_NOTE(ARGUNUSED(rssi))
_NOTE(ARGUNUSED(rstamp))
_NOTE(ARGUNUSED(rantenna))
_NOTE(ARGUNUSED(mp))
/* doesn't support HOST-AP mode yet */
}
static void
ieee80211_recv_asresp(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
uint8_t *frm, *efrm, *rates, *xrates;
int32_t status;
_NOTE(ARGUNUSED(rssi))
_NOTE(ARGUNUSED(rstamp))
_NOTE(ARGUNUSED(rantenna))
_NOTE(ARGUNUSED(mp))
ASSERT(mutex_owned(&isc->isc_genlock));
if (isc->isc_opmode != IEEE80211_M_STA ||
isc->isc_state != IEEE80211_S_ASSOC)
return;
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* asresp frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
*/
if (frm + 6 > efrm) {
ath_log("ath: ieee80211_recv_asresp(): too short from %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2));
return;
}
in = isc->isc_bss;
in->in_capinfo = LE_16(*(uint16_t *)frm);
frm += 2;
status = LE_16(*(uint16_t *)frm);
frm += 2;
if (status != 0) {
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_asresp(): "
"association failed (reason %d)\n", status));
in = ieee80211_find_node(isc, wh->ifrm_addr2);
if (in != NULL) {
in->in_fails++;
}
return;
}
in->in_associd = LE_16(*(uint16_t *)frm);
frm += 2;
rates = xrates = NULL;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_RATES:
rates = frm;
break;
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_SIZE, wh);
(void) ieee80211_setup_rates(isc, in, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
if (in->in_rates.ir_nrates != 0)
(void) _ieee80211_new_state(isc, IEEE80211_S_RUN,
wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
}
static void
ieee80211_recv_disassoc(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
uint16_t reason;
_NOTE(ARGUNUSED(rssi))
_NOTE(ARGUNUSED(rstamp))
_NOTE(ARGUNUSED(rantenna))
_NOTE(ARGUNUSED(mp))
ASSERT(mutex_owned(&isc->isc_genlock));
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* disassoc frame format
* [2] reason
*/
if (frm + 2 > efrm) {
ath_log("ath: ieee80211_recv_disassoc(): too short from %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2));
return;
}
reason = LE_16(*(uint16_t *)frm);
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_disassoc(): "
"disassociation packet, reason:0x%x\n", reason));
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
(void) _ieee80211_new_state(isc, IEEE80211_S_ASSOC,
wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
case IEEE80211_M_HOSTAP:
/* don't support HOSTAP */
break;
default:
break;
}
}
static void
ieee80211_recv_deauth(ieee80211com_t *isc, mblk_t *mp, int32_t rssi,
uint32_t rstamp, uint32_t rantenna)
{
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
uint16_t reason;
_NOTE(ARGUNUSED(rssi))
_NOTE(ARGUNUSED(rstamp))
_NOTE(ARGUNUSED(rantenna))
_NOTE(ARGUNUSED(mp))
ASSERT(mutex_owned(&isc->isc_genlock));
wh = (struct ieee80211_frame *)mp->b_rptr;
frm = (uint8_t *)&wh[1];
efrm = mp->b_wptr;
/*
* dauth frame format
* [2] reason
*/
if (frm + 2 > efrm) {
ath_log("ath: ieee80211_recv_deauth(): too short from %s\n",
ieee80211_ether_sprintf(wh->ifrm_addr2));
return;
}
reason = LE_16(*(uint16_t *)frm);
ATH_DEBUG((ATH_DBG_80211, "ath: ieee80211_recv_deauth(): "
"deauthentication packet, reason: 0x%x\n", reason));
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
(void) _ieee80211_new_state(isc, IEEE80211_S_AUTH,
wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
case IEEE80211_M_HOSTAP:
break;
default:
break;
}
}
/*
* Return the phy mode for with the specified channel so the
* caller can select a rate set. This is problematic and the
* work here assumes how things work elsewhere in this code.
*/
enum ieee80211_phymode
ieee80211_chan2mode(ieee80211com_t *isc, struct ieee80211channel *chan)
{
/*
* NB: this assumes the channel would not be supplied to us
* unless it was already compatible with the current mode.
*/
if (isc->isc_curmode != IEEE80211_MODE_AUTO)
return (isc->isc_curmode);
/*
* In autoselect mode; deduce a mode based on the channel
* characteristics. We assume that turbo-only channels
* are not considered when the channel set is constructed.
*/
if (IEEE80211_IS_CHAN_5GHZ(chan))
return (IEEE80211_MODE_11A);
else if (chan->ich_flags & (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN))
return (IEEE80211_MODE_11G);
else
return (IEEE80211_MODE_11B);
}
/*
* Format an Ethernet MAC for printing,
* and this function adds NULL byte at the end of string.
*/
const char *
ieee80211_ether_sprintf(const uint8_t *mac)
{
static char etherbuf[18];
(void) snprintf(etherbuf, sizeof (etherbuf),
"%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return (etherbuf);
}
/*
* Format an essid for printing,
* and this function adds NULL byte at the end of string.
*/
const int8_t *
ieee80211_essid_sprintf(uint8_t *essid, uint32_t len)
{
static int8_t essidbuf[IEEE80211_NWID_LEN * 2 + 1];
uint32_t i;
uint8_t *p;
int8_t tmp[3];
bzero(essidbuf, sizeof (essidbuf));
if (len > IEEE80211_NWID_LEN)
len = IEEE80211_NWID_LEN;
/* determine printable or not */
for (i = 0, p = essid; i < len; i++, p++) {
if (*p < ' ' || *p > 0x7e)
break;
}
if (i == len) {
for (i = 0; i < len; i++)
essidbuf[i] = essid[i];
essidbuf[i] = '\0';
} else {
for (i = 0; i < len; i++) {
(void) sprintf(tmp, "%02x", essid[i]);
(void) strcat(essidbuf, tmp);
}
}
return (essidbuf);
}
/*
* Following fucntions are registerd to GLD and intercepting
* the function calls from GLD to LLD to add appropriate ic_genlock protection.
*
* We have to protect ieee80211_gld_send() by isc_genlock,
* because there have many references to isc struct on this transimit path,
* and this may affect performace.
*/
static int32_t
ieee80211_gld_send(gld_mac_info_t *gld_p, mblk_t *mp)
{
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
int32_t result;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_send)(gld_p, mp);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int32_t
ieee80211_gld_reset(gld_mac_info_t *gld_p)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_reset)(gld_p);
mutex_exit(&isc->isc_genlock);
return (result);
}
/*
* Following function is for Multi_func thread.
* The smallest timing unit is 100ms.
* It is created in ieee80211_gld_start(),
* and destroy in ieee80211_gld_stop().
*/
void
ieee80211_mf_thread(ieee80211com_t *isc)
{
struct ieee80211_node *in;
uint32_t scan_ticks = 0;
uint32_t ratectl_ticks = 0;
uint32_t cali_ticks = 0;
uint32_t mgt_ticks = 0;
enum ieee80211_state ostate = IEEE80211_S_INIT;
mutex_enter(&isc->isc_genlock);
while (isc->isc_mfthread_switch) {
if (isc->isc_state == IEEE80211_S_SCAN &&
ostate != IEEE80211_S_SCAN)
scan_ticks = 0;
if (isc->isc_state == IEEE80211_S_SCAN) {
if (scan_ticks >= (isc->isc_scan_interval/100 - 1)) {
ieee80211_next_scan(isc);
scan_ticks = 0;
} else
scan_ticks++;
}
if (ratectl_ticks >= (isc->isc_ratectl_interval/100 - 1)) {
if (isc->isc_opmode == IEEE80211_M_STA)
(*isc->isc_rate_ctl)(isc, isc->isc_bss);
else {
in = list_head(&isc->isc_in_list);
while (in != NULL) {
(*isc->isc_rate_ctl)(isc, in);
in = list_next(&isc->isc_in_list, in);
}
}
ratectl_ticks = 0;
} else
ratectl_ticks++;
if (cali_ticks >= (isc->isc_cali_interval/100 - 1)) {
(*isc->isc_calibrate)(isc);
cali_ticks = 0;
} else
cali_ticks++;
if (mgt_ticks >= 10) { /* one second */
if (isc->isc_mgt_timeout &&
--isc->isc_mgt_timeout == 0) {
(void) _ieee80211_new_state(isc,
IEEE80211_S_SCAN, -1);
}
if (isc->isc_inact_timeout &&
--isc->isc_inact_timeout == 0)
ieee80211_timeout_nodes(isc);
mgt_ticks = 0;
} else
mgt_ticks++;
ostate = isc->isc_state;
mutex_exit(&isc->isc_genlock);
delay(drv_usectohz(100000)); /* delay 100ms */
mutex_enter(&isc->isc_genlock);
}
isc->isc_mf_thread = NULL;
cv_broadcast(&isc->isc_mfthread_cv);
mutex_exit(&isc->isc_genlock);
thread_exit();
}
/*
* This function is responsible for creating multi-func thread.
*/
static int32_t
ieee80211_gld_start(gld_mac_info_t *gld_p)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
isc->isc_mfthread_switch = 1;
if (isc->isc_mf_thread == NULL)
isc->isc_mf_thread = thread_create((caddr_t)NULL, 0,
ieee80211_mf_thread, isc, 0, &p0, TS_RUN, minclsyspri);
result = (*isc->isc_gld_start)(gld_p);
mutex_exit(&isc->isc_genlock);
return (result);
}
/*
* This function is responsible for destory multi-func thread.
*/
static int32_t
ieee80211_gld_stop(gld_mac_info_t *gld_p)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
isc->isc_mfthread_switch = 0;
while (isc->isc_mf_thread != NULL) {
if (cv_wait_sig(&isc->isc_mfthread_cv, &isc->isc_genlock) == 0)
break;
}
result = (*isc->isc_gld_stop)(gld_p);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int32_t
ieee80211_gld_saddr(gld_mac_info_t *gld_p, uint8_t *macaddr)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_saddr)(gld_p, macaddr);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int
ieee80211_gld_set_promiscuous(gld_mac_info_t *gld_p, int mode)
{
int result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_set_promiscuous)(gld_p, mode);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int32_t
ieee80211_gld_gstat(gld_mac_info_t *gld_p, struct gld_stats *glds_p)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_gstat)(gld_p, glds_p);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int32_t
ieee80211_gld_ioctl(gld_mac_info_t *gld_p, queue_t *wq, mblk_t *mp)
{
int32_t result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_ioctl)(gld_p, wq, mp);
mutex_exit(&isc->isc_genlock);
return (result);
}
static int
ieee80211_gld_set_multicast(gld_mac_info_t *gld_p, uint8_t *eth_p, int flag)
{
int result;
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
mutex_enter(&isc->isc_genlock);
result = (*isc->isc_gld_set_multicast)(gld_p, eth_p, flag);
mutex_exit(&isc->isc_genlock);
return (result);
}
static uint32_t
ieee80211_gld_intr(gld_mac_info_t *gld_p)
{
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
return ((*isc->isc_gld_intr)(gld_p));
}
int32_t
ieee80211_ifattach(gld_mac_info_t *gld_p)
{
ieee80211com_t *isc =
(ieee80211com_t *)gld_p->gldm_private;
struct ieee80211channel *ch;
int32_t i;
mutex_init(&isc->isc_genlock, NULL, MUTEX_DRIVER, NULL);
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
* a default channel if not already specified.
*/
bzero(isc->isc_chan_avail, sizeof (isc->isc_chan_avail));
isc->isc_modecaps |= 1 << IEEE80211_MODE_AUTO;
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
ch = &isc->isc_channels[i];
if (ch->ich_flags) {
setbit(isc->isc_chan_avail, i);
/*
* Identify mode capabilities.
*/
if (IEEE80211_IS_CHAN_A(ch))
isc->isc_modecaps |= 1 << IEEE80211_MODE_11A;
if (IEEE80211_IS_CHAN_B(ch))
isc->isc_modecaps |= 1 << IEEE80211_MODE_11B;
if (IEEE80211_IS_CHAN_PUREG(ch))
isc->isc_modecaps |= 1 << IEEE80211_MODE_11G;
if (IEEE80211_IS_CHAN_T(ch))
isc->isc_modecaps |= 1 << IEEE80211_MODE_TURBO;
}
}
/* Start from auto mode */
(void) ieee80211_setmode(isc, IEEE80211_MODE_AUTO);
/* Initialize WEP related variable */
isc->isc_wep_txkey = 0;
isc->isc_iv = (int32_t)(gethrtime() & 0x00000000ffffffff);
/* Initialize some config variables */
isc->isc_rtsthreshold = IEEE80211_RTS_MAX;
isc->isc_fragthreshold = 2346;
isc->isc_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
isc->isc_fixed_rate = -1; /* no fixed rate */
if (isc->isc_lintval == 0)
isc->isc_lintval = 100; /* default sleep */
isc->isc_txpower = IEEE80211_TXPOWER_MAX; /* default tx power */
isc->isc_bmisstimeout = 7 * isc->isc_lintval; /* default 7 beacons */
list_create(&isc->isc_in_list,
sizeof (struct ieee80211_node),
offsetof(struct ieee80211_node, in_node));
for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
list_create(&isc->isc_inhash_list[i],
sizeof (struct ieee80211_node),
offsetof(struct ieee80211_node, in_hash_node));
/* Initialize management frame handlers */
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_BEACON
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_prreq;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_auth;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_deauth;
isc->isc_recv_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_disassoc;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prreq;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prresp;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_auth;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_deauth;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
isc->isc_send_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_disassoc;
cv_init(&isc->isc_scan_cv, NULL, CV_DRIVER, NULL);
cv_init(&isc->isc_mfthread_cv, NULL, CV_DRIVER, NULL);
isc->isc_mf_thread = NULL;
isc->isc_mfthread_switch = 0;
ASSERT(isc->isc_node_alloc != NULL);
isc->isc_bss = (*isc->isc_node_alloc)(isc);
isc->isc_bss->in_chan = IEEE80211_CHAN_ANYC;
isc->isc_bss->in_txpower = IEEE80211_TXPOWER_MAX;
isc->isc_scan_interval = 200;
isc->isc_cali_interval = 30000;
isc->isc_ratectl_interval = 500;
gld_p->gldm_reset = ieee80211_gld_reset;
gld_p->gldm_start = ieee80211_gld_start;
gld_p->gldm_stop = ieee80211_gld_stop;
gld_p->gldm_set_mac_addr = ieee80211_gld_saddr;
gld_p->gldm_send = ieee80211_gld_send;
gld_p->gldm_set_promiscuous = ieee80211_gld_set_promiscuous;
gld_p->gldm_get_stats = ieee80211_gld_gstat;
gld_p->gldm_ioctl = ieee80211_gld_ioctl;
gld_p->gldm_set_multicast = ieee80211_gld_set_multicast;
gld_p->gldm_intr = ieee80211_gld_intr;
return (0);
}
void
ieee80211_ifdetach(gld_mac_info_t *gld_p)
{
ieee80211com_t *isc = (ieee80211com_t *)gld_p->gldm_private;
(*isc->isc_node_free)(isc, isc->isc_bss);
ieee80211_free_allnodes(isc);
cv_destroy(&isc->isc_mfthread_cv);
cv_destroy(&isc->isc_scan_cv);
mutex_destroy(&isc->isc_genlock);
}