sync 11n support with vap code base; many changes based on interop

testing with all major vendors

MFC after:	1 week
This commit is contained in:
Sam Leffler 2007-11-02 05:22:25 +00:00
parent cd9a2dab49
commit 1b6167d239
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=173273
16 changed files with 1037 additions and 273 deletions

View File

@ -169,6 +169,10 @@ struct ieee80211_qosframe_addr4 {
#define IEEE80211_SEQ_SUB(a, b) \
(((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE-1))
#define IEEE80211_SEQ_BA_RANGE 2048 /* 2^11 */
#define IEEE80211_SEQ_BA_BEFORE(a, b) \
(IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1)
#define IEEE80211_NWID_LEN 32
#define IEEE80211_QOS_TXOP 0x00ff
@ -546,20 +550,20 @@ struct ieee80211_ie_htcap {
/* HT parameters (hc_param) */
#define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */
#define IEEE80211_HTCAP_MAXRXAMPDU_S 0
#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0x00
#define IEEE80211_HTCAP_MAXRXAMPDU_16K 0x01
#define IEEE80211_HTCAP_MAXRXAMPDU_32K 0x02
#define IEEE80211_HTCAP_MAXRXAMPDU_64K 0x03
#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0
#define IEEE80211_HTCAP_MAXRXAMPDU_16K 1
#define IEEE80211_HTCAP_MAXRXAMPDU_32K 2
#define IEEE80211_HTCAP_MAXRXAMPDU_64K 3
#define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */
#define IEEE80211_HTCAP_MPDUDENSITY_S 2
#define IEEE80211_HTCAP_MPDUDENSITY_NA 0x00 /* no time restriction */
#define IEEE80211_HTCAP_MPDUDENSITY_025 0x04 /* 1/4 us */
#define IEEE80211_HTCAP_MPDUDENSITY_05 0x08 /* 1/2 us */
#define IEEE80211_HTCAP_MPDUDENSITY_1 0x0c /* 1 us */
#define IEEE80211_HTCAP_MPDUDENSITY_2 0x10 /* 2 us */
#define IEEE80211_HTCAP_MPDUDENSITY_4 0x14 /* 4 us */
#define IEEE80211_HTCAP_MPDUDENSITY_8 0x18 /* 8 us */
#define IEEE80211_HTCAP_MPDUDENSITY_16 0x1c /* 16 us */
#define IEEE80211_HTCAP_MPDUDENSITY_NA 0 /* no time restriction */
#define IEEE80211_HTCAP_MPDUDENSITY_025 1 /* 1/4 us */
#define IEEE80211_HTCAP_MPDUDENSITY_05 2 /* 1/2 us */
#define IEEE80211_HTCAP_MPDUDENSITY_1 3 /* 1 us */
#define IEEE80211_HTCAP_MPDUDENSITY_2 4 /* 2 us */
#define IEEE80211_HTCAP_MPDUDENSITY_4 5 /* 4 us */
#define IEEE80211_HTCAP_MPDUDENSITY_8 6 /* 8 us */
#define IEEE80211_HTCAP_MPDUDENSITY_16 7 /* 16 us */
/* HT extended capabilities (hc_extcap) */
#define IEEE80211_HTCAP_PCO 0x0001 /* PCO capable */
@ -686,7 +690,7 @@ struct ieee80211_country_ie {
uint8_t schan; /* starting channel */
uint8_t nchan; /* number channels */
uint8_t maxtxpwr; /* tx power cap */
} __packed band[4]; /* up to 4 sub bands */
} __packed band[10]; /* sub bands */
} __packed;
/*

View File

@ -54,6 +54,27 @@ int ieee80211_debug = 0;
SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
0, "debugging printfs");
#endif
extern int ieee80211_recv_bar_ena;
SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
0, "BAR frame processing (ena/dis)");
#ifdef IEEE80211_AMPDU_AGE
static int
ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
{
extern int ieee80211_ampdu_age;
int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
int error;
error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
if (error || !req->newptr)
return error;
ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
return 0;
}
SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
#endif
static int
ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)

View File

@ -125,6 +125,13 @@ typedef struct mtx ieee80211_scan_lock_t;
(_qlen) = ++(_ni)->ni_savedq.ifq_len; \
} while (0)
#define IEEE80211_TAPQ_INIT(_tap) do { \
mtx_init(&(tap)->txa_q.ifq_mtx, "ampdu tx queue", NULL, MTX_DEF); \
(_tap)->txa_q.ifq_maxlen = IEEE80211_AGGR_BAWMAX; \
} while (0)
#define IEEE80211_TAPQ_DESTROY(_tap) \
mtx_destroy(&(_tap)->txa_q.ifq_mtx)
#ifndef IF_PREPEND_LIST
#define _IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do { \
(mtail)->m_nextpkt = (ifq)->ifq_head; \
@ -178,6 +185,7 @@ struct ifqueue;
void ieee80211_drain_ifq(struct ifqueue *);
#define msecs_to_ticks(ms) (((ms)*hz)/1000)
#define ticks_to_msecs(t) ((t) / hz)
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)

File diff suppressed because it is too large Load Diff

View File

@ -31,10 +31,9 @@
* 802.11n protocol implementation definitions.
*/
#define IEEE80211_SEND_ACTION(_ni,_cat, _act, _args) \
((*(_ic)->ic_send_action)(_ni, _cat, _act, _args))
#define IEEE80211_AGGR_BAWMAX 64 /* max block ack window size */
/* threshold for aging overlapping non-HT bss */
#define IEEE80211_NONHT_PRESENT_AGE msecs_to_ticks(60*1000)
typedef uint16_t ieee80211_seq;
@ -44,6 +43,7 @@ struct ieee80211_tx_ampdu {
#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */
#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */
#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
uint8_t txa_ac;
uint8_t txa_token; /* dialog token */
int txa_qbytes; /* data queued (bytes) */
@ -64,7 +64,7 @@ struct ieee80211_tx_ampdu {
/* return non-zero if AMPDU tx for the TID is running or started */
#define IEEE80211_AMPDU_REQUESTED(tap) \
(((tap)->txa_flags & \
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND)) != 0)
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
struct ieee80211_rx_ampdu {
int rxa_flags;
@ -72,8 +72,9 @@ struct ieee80211_rx_ampdu {
short rxa_qframes; /* data queued (frames) */
ieee80211_seq rxa_seqstart;
ieee80211_seq rxa_start; /* start of current BA window */
ieee80211_seq rxa_nxt; /* next seq# in BA window */
uint16_t rxa_wnd; /* BA window size */
int rxa_age; /* age of oldest frame in window */
int rxa_nframes; /* frames since ADDBA */
struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX];
};
@ -96,12 +97,21 @@ int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
void ieee80211_ht_node_cleanup(struct ieee80211_node *);
struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
struct ieee80211_channel *, int);
void ieee80211_ht_wds_init(struct ieee80211_node *);
void ieee80211_ht_node_join(struct ieee80211_node *);
void ieee80211_ht_node_leave(struct ieee80211_node *);
void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
void ieee80211_ht_timeout(struct ieee80211com *);
void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
void ieee80211_recv_action(struct ieee80211_node *,
const uint8_t *, const uint8_t *);
int ieee80211_ampdu_request(struct ieee80211_node *,
struct ieee80211_tx_ampdu *);
void ieee80211_ampdu_stop(struct ieee80211_node *,
struct ieee80211_tx_ampdu *);
int ieee80211_send_bar(struct ieee80211_node *,
const struct ieee80211_tx_ampdu *);
int ieee80211_send_action(struct ieee80211_node *,

View File

@ -2037,6 +2037,19 @@ capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
ic->ic_stats.is_rx_assoc_capmismatch++;
}
static void
htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
int reassoc, int resp)
{
struct ieee80211com *ic = ni->ni_ic;
IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
"deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
/* XXX no better code */
IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
ieee80211_node_leave(ic, ni);
}
void
ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
struct ieee80211_node *ni,
@ -2046,7 +2059,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
int reassoc, resp, allocbs;
uint8_t rate;
@ -2311,8 +2324,17 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_parse_athparams(ni, scan.ath, wh);
if (scan.htcap != NULL)
ieee80211_parse_htcap(ni, scan.htcap);
if (scan.htinfo != NULL)
if (scan.htinfo != NULL) {
ieee80211_parse_htinfo(ni, scan.htinfo);
if (ni->ni_chan != ic->ic_bsschan) {
/*
* Channel has been adjusted based on
* negotiated HT parameters; force the
* channel state to follow.
*/
ieee80211_setbsschan(ic, ni->ni_chan);
}
}
if (scan.tim != NULL) {
struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *) scan.tim;
@ -2789,6 +2811,37 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_ht_node_init(ni, htcap);
} else if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_ht_node_cleanup(ni);
/*
* Allow AMPDU operation only with unencrypted traffic
* or AES-CCM; the 11n spec only specifies these ciphers
* so permitting any others is undefined and can lead
* to interoperability problems.
*
* NB: We check for AES by looking at the GTK cipher
* since the WPA/11i specs say the PTK cipher has
* to be "as good or better".
*/
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
(((ic->ic_flags & IEEE80211_F_WPA) &&
rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
(ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
IEEE80211_NOTE(ic,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"disallow HT use because WEP or TKIP requested, "
"capinfo 0x%x mcastcipher %d", capinfo,
rsnparms.rsn_mcastcipher);
ieee80211_ht_node_cleanup(ni);
ic->ic_stats.is_ht_assoc_downgrade++;
}
/*
* If constrained to 11n-only stations reject legacy stations.
*/
if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
(ni->ni_flags & IEEE80211_NODE_HT) == 0) {
htcapmismatch(ni, wh, reassoc, resp);
ic->ic_stats.is_ht_assoc_nohtcap++;
return;
}
ni->ni_rssi = rssi;
ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
@ -2884,6 +2937,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] extended supported rates
* [tlv] WME
* [tlv] HT capabilities
* [tlv] HT info
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
ni = ic->ic_bss;
@ -2904,7 +2958,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
associd = le16toh(*(uint16_t *)frm);
frm += 2;
rates = xrates = wme = htcap = NULL;
rates = xrates = wme = htcap = htinfo = NULL;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
@ -2917,9 +2971,25 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_ELEMID_HTCAP:
htcap = frm;
break;
case IEEE80211_ELEMID_HTINFO:
htinfo = frm;
break;
case IEEE80211_ELEMID_VENDOR:
if (iswmeoui(frm))
wme = frm;
else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
/*
* Accept pre-draft HT ie's if the
* standard ones have not been seen.
*/
if (ishtcapoui(frm)) {
if (htcap == NULL)
htcap = frm;
} else if (ishtinfooui(frm)) {
if (htinfo == NULL)
htcap = frm;
}
}
/* XXX Atheros OUI support */
break;
}
@ -2955,6 +3025,25 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_wme_updateparams(ic);
} else
ni->ni_flags &= ~IEEE80211_NODE_QOS;
/*
* Setup HT state according to the negotiation.
*/
if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
htcap != NULL && htinfo != NULL) {
ieee80211_ht_node_init(ni, htcap);
ieee80211_parse_htinfo(ni, htinfo);
ieee80211_setup_htrates(ni,
htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
ieee80211_setup_basic_htrates(ni, htinfo);
if (ni->ni_chan != ic->ic_bsschan) {
/*
* Channel has been adjusted based on
* negotiated HT parameters; force the
* channel state to follow.
*/
ieee80211_setbsschan(ic, ni->ni_chan);
}
}
/*
* Configure state now that we are associated.
*
@ -2989,6 +3078,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
", fast-frames" : "",
IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?

View File

@ -1108,6 +1108,17 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_INACTIVITY:
ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
break;
case IEEE80211_IOC_HTPROTMODE:
ireq->i_val = ic->ic_htprotmode;
break;
case IEEE80211_IOC_HTCONF:
if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
ireq->i_val = 1;
if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
ireq->i_val |= 2;
} else
ireq->i_val = 0;
break;
default:
error = EINVAL;
break;
@ -2479,6 +2490,27 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
else
ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
break;
case IEEE80211_IOC_HTPROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
return EINVAL;
ic->ic_htprotmode = ireq->i_val ?
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
/* NB: if not operating in 11n this can wait */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
error = ERESTART;
break;
case IEEE80211_IOC_HTCONF:
if (ireq->i_val & 1)
ic->ic_flags_ext |= IEEE80211_FEXT_HT;
else
ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
if (ireq->i_val & 2)
ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
else
ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
error = ENETRESET;
break;
default:
error = EINVAL;
break;

View File

@ -60,6 +60,7 @@ struct ieee80211_nodestats {
uint32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
uint32_t ns_rx_unauth; /* rx on unauthorized port */
uint32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
uint32_t ns_rx_drop; /* rx discard other reason */
uint32_t ns_tx_data; /* tx data frames */
uint32_t ns_tx_mgmt; /* tx management frames */
@ -186,11 +187,28 @@ struct ieee80211_stats {
uint32_t is_amsdu_encap; /* A-MSDU encap'd for tx */
uint32_t is_ampdu_bar_bad; /* A-MPDU BAR out of window */
uint32_t is_ampdu_bar_oow; /* A-MPDU BAR before ADDBA */
uint32_t is_ampdu_bar_move; /* A-MPDU BAR moved window */
uint32_t is_ampdu_bar_rx; /* A-MPDU BAR frames handled */
uint32_t is_ampdu_rx_flush; /* A-MPDU frames flushed */
uint32_t is_ampdu_rx_oor; /* A-MPDU frames out-of-order */
uint32_t is_ampdu_rx_copy; /* A-MPDU frames copied down */
uint32_t is_spare[32];
uint32_t is_ampdu_rx_drop; /* A-MPDU frames dropped */
uint32_t is_tx_badstate; /* tx discard state != RUN */
uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
uint32_t is_tx_classify; /* tx classification failed */
uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
uint32_t is_ampdu_rx_age; /* A-MPDU sent up 'cuz of age */
uint32_t is_ampdu_rx_move; /* A-MPDU MSDU moved window */
uint32_t is_addba_reject; /* ADDBA reject 'cuz disabled */
uint32_t is_addba_norequest; /* ADDBA response w/o ADDBA */
uint32_t is_addba_badtoken; /* ADDBA response w/ wrong
dialogtoken */
uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
uint32_t is_spare[16];
};
/*
@ -208,7 +226,7 @@ struct ieee80211_stats {
* Otherwise a unicast/pairwise key is specified by the bssid
* (on a station) or mac address (on an ap). They key length
* must include any MIC key data; otherwise it should be no
more than IEEE80211_KEYBUF_SIZE.
* more than IEEE80211_KEYBUF_SIZE.
*/
struct ieee80211req_key {
uint8_t ik_type; /* key/cipher type */
@ -493,6 +511,8 @@ struct ieee80211req {
#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */
#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.

View File

@ -535,6 +535,18 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
return ieee80211_sta_join1(ieee80211_ref_node(ni));
}
/*
* Change the bss channel.
*/
void
ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
{
ic->ic_bsschan = c;
ic->ic_curchan = ic->ic_bsschan;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_set_channel(ic);
}
/*
* Join the specified IBSS/BSS network. The node is assumed to
* be passed in with a held reference.
@ -584,10 +596,7 @@ ieee80211_sta_join1(struct ieee80211_node *selbs)
ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
IEEE80211_F_DODEL | IEEE80211_F_JOIN);
ic->ic_bsschan = selbs->ni_chan;
ic->ic_curchan = ic->ic_bsschan;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_set_channel(ic);
ieee80211_setbsschan(ic, selbs->ni_chan);
/*
* Set the erp state (mostly the slot time) to deal with
* the auto-select case; this should be redundant if the
@ -637,7 +646,6 @@ ieee80211_sta_join(struct ieee80211com *ic,
ni->ni_tstamp.tsf = se->se_tstamp.tsf;
ni->ni_intval = se->se_intval;
ni->ni_capinfo = se->se_capinfo;
/* XXX shift to 11n channel if htinfo present */
ni->ni_chan = se->se_chan;
ni->ni_timoff = se->se_timoff;
ni->ni_fhdwell = se->se_fhdwell;
@ -646,9 +654,7 @@ ieee80211_sta_join(struct ieee80211com *ic,
ni->ni_rssi = se->se_rssi;
ni->ni_noise = se->se_noise;
if (se->se_htcap_ie != NULL)
ieee80211_ht_node_init(ni, se->se_htcap_ie);
if (se->se_htinfo_ie != NULL)
ieee80211_parse_htinfo(ni, se->se_htinfo_ie);
ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
if (se->se_wpa_ie != NULL)
ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
if (se->se_rsn_ie != NULL)
@ -664,10 +670,6 @@ ieee80211_sta_join(struct ieee80211com *ic,
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
IEEE80211_F_DOSORT);
if (se->se_htcap_ie != NULL)
ieee80211_setup_htrates(ni, se->se_htcap_ie, IEEE80211_F_JOIN);
if (se->se_htinfo_ie != NULL)
ieee80211_setup_basic_htrates(ni, se->se_htinfo_ie);
return ieee80211_sta_join1(ieee80211_ref_node(ni));
}
@ -715,6 +717,11 @@ node_cleanup(struct ieee80211_node *ni)
"[%s] power save mode off, %u sta's in ps mode\n",
ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
}
/*
* Cleanup any HT-related state.
*/
if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_ht_node_cleanup(ni);
/*
* Clear AREF flag that marks the authorization refcnt bump
* has happened. This is probably not needed as the node
@ -1577,6 +1584,7 @@ ieee80211_node_timeout(void *arg)
IEEE80211_LOCK(ic);
ieee80211_erp_timeout(ic);
ieee80211_ht_timeout(ic);
IEEE80211_UNLOCK(ic);
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
@ -1659,6 +1667,8 @@ static void
ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
{
IEEE80211_LOCK_ASSERT(ic);
/*
* Station isn't capable of short slot time. Bump
* the count of long slot time stations and disable
@ -1724,6 +1734,7 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
if (ni->ni_associd == 0) {
uint16_t aid;
IEEE80211_LOCK(ic);
/*
* It would be good to search the bitmap
* more efficiently, but this will do for now.
@ -1734,23 +1745,30 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
break;
}
if (aid >= ic->ic_max_aid) {
IEEE80211_UNLOCK(ic);
IEEE80211_SEND_MGMT(ic, ni, resp,
IEEE80211_REASON_ASSOC_TOOMANY);
ieee80211_node_leave(ic, ni);
return;
}
ni->ni_associd = aid | 0xc000;
ni->ni_jointime = time_uptime;
IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
ic->ic_sta_assoc++;
newassoc = 1;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_join(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
ieee80211_node_join_11g(ic, ni);
IEEE80211_UNLOCK(ic);
newassoc = 1;
} else
newassoc = 0;
IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station %sassociated at aid %d: %s preamble, %s slot time%s%s%s%s%s",
"station %sassociated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
newassoc ? "" : "re",
IEEE80211_NODE_AID(ni),
ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
@ -1759,6 +1777,7 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
", fast-frames" : "",
IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
@ -1799,6 +1818,8 @@ static void
ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
{
IEEE80211_LOCK_ASSERT(ic);
KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
ic->ic_bsschan->ic_flags, ic->ic_curmode));
@ -1900,13 +1921,18 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if (ic->ic_auth->ia_node_leave != NULL)
ic->ic_auth->ia_node_leave(ic, ni);
IEEE80211_LOCK(ic);
IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
ni->ni_associd = 0;
ic->ic_sta_assoc--;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_leave(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
ieee80211_node_leave_11g(ic, ni);
IEEE80211_UNLOCK(ic);
/*
* Cleanup station state. In particular clear various
* state that might otherwise be reused if the node

View File

@ -111,10 +111,13 @@ struct ieee80211_node {
#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
uint16_t ni_ath_defkeyix;/* Atheros def key index */
uint16_t ni_associd; /* assoc response */
uint16_t ni_txpower; /* current transmit power */
uint16_t ni_vlan; /* vlan tag */
uint32_t ni_jointime; /* time of join (secs) */
uint32_t *ni_challenge; /* shared-key challenge */
uint8_t *ni_wpa_ie; /* captured WPA ie */
uint8_t *ni_rsn_ie; /* captured RSN ie */
@ -157,6 +160,7 @@ struct ieee80211_node {
uint8_t ni_dtim_count; /* DTIM count for last bcn */
/* 11n state */
uint8_t *ni_htcap_ie; /* captured HTCAP ie */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
uint8_t ni_htctlchan; /* HT control channel */
@ -180,6 +184,8 @@ struct ieee80211_node {
MALLOC_DECLARE(M_80211_NODE);
#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
#define IEEE80211_NODE_AMPDU \
(IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd)
@ -219,6 +225,7 @@ void ieee80211_node_unauthorize(struct ieee80211_node *);
void ieee80211_probe_curchan(struct ieee80211com *, int);
void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
void ieee80211_reset_bss(struct ieee80211com *);
void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
int ieee80211_sta_join(struct ieee80211com *,

View File

@ -819,7 +819,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* in case the receiver NAK's us or we are otherwise
* unable to establish a BA stream.
*/
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
(ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
@ -1583,7 +1583,9 @@ int
ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
int type, int arg)
{
#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
const struct ieee80211_rateset *rs;
struct mbuf *m;
uint8_t *frm;
uint16_t capinfo;
@ -1657,7 +1659,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
ic->ic_bss->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
rs = ieee80211_get_suprates(ic, ic->ic_curchan);
frm = ieee80211_add_rates(frm, rs);
if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
*frm++ = IEEE80211_ELEMID_FHPARMS;
@ -1684,16 +1687,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_wpa(frm, ic);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
frm = ieee80211_add_erp(frm, ic);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
if (ic->ic_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
frm = ieee80211_add_xrates(frm, rs);
/*
* NB: legacy 11b clients do not get certain ie's.
* The caller identifies such clients by passing
* a token in arg to us. Could expand this to be
* any legacy client for stuff like HT ie's.
*/
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
arg != IEEE80211_SEND_LEGACY_11B) {
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
}
if (ic->ic_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
(ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
arg != IEEE80211_SEND_LEGACY_11B) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
if (ni->ni_ath_ie != NULL)
frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
@ -1828,6 +1840,9 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
(ic->ic_flags & IEEE80211_F_DOTH))
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@ -1845,13 +1860,16 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
ni->ni_htcap_ie != NULL &&
ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
frm = ieee80211_add_htcap(frm, ni);
if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
frm = ieee80211_add_htcap(frm, ni);
if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)
frm = ieee80211_add_htcap_vendor(frm, ni);
}
if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
ni->ni_htcap_ie != NULL &&
ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
frm = ieee80211_add_htcap_vendor(frm, ni);
if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
@ -1914,17 +1932,16 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_rates(frm, &ni->ni_rates);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
/* NB: respond according to what we received */
if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) {
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
}
if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
/* NB: respond according to what we received */
if (ni->ni_flags & IEEE80211_NODE_HTCOMPAT) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
} else {
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
}
if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
@ -1965,6 +1982,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ieee80211_free_node(ni);
return ret;
#undef senderr
#undef HTFLAGS
}
static void
@ -2072,6 +2090,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
return NULL;
}
memset(bo, 0, sizeof(*bo));
memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */
frm += 8;
*(uint16_t *)frm = htole16(ni->ni_intval);
@ -2115,29 +2135,27 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
if (ic->ic_flags & IEEE80211_F_DOTH)
frm = ieee80211_add_countryie(frm, ic,
ic->ic_countrycode, ic->ic_location);
if (ic->ic_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
} else
bo->bo_wme = NULL;
if (ic->ic_flags & IEEE80211_F_WPA)
frm = ieee80211_add_wpa(frm, ic);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
} else
bo->bo_erp = NULL;
}
frm = ieee80211_add_xrates(frm, rs);
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
frm = ieee80211_add_htcap(frm, ni);
bo->bo_htinfo = frm;
frm = ieee80211_add_htinfo(frm, ni);
if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
} else
bo->bo_htinfo = NULL;
}
if (ic->ic_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
}
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
(ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);

View File

@ -1306,7 +1306,9 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg
* of this explicitly sync the scanner state.
*/
ieee80211_scan_update(ic);
ieee80211_create_ibss(ic, ic->ic_curchan);
ieee80211_create_ibss(ic,
ieee80211_ht_adjust_channel(ic,
ic->ic_curchan, ic->ic_flags_ext));
break;
}
/* fall thru... */

View File

@ -47,6 +47,16 @@ enum ieee80211_state {
#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
/*
* The formation of some management frames requires guidance to
* deal with legacy clients. When the client is identified as
* "legacy 11b" this parameter can be passed in the arg param of a
* IEEE80211_SEND_MGMT call.
*/
#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[];

View File

@ -325,7 +325,8 @@ ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
return 0;
}
}
ieee80211_create_ibss(ic, c);
ieee80211_create_ibss(ic,
ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
return 1;
}
}

View File

@ -1352,7 +1352,8 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* specified, try to select a channel.
*/
if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
chan = adhoc_pick_channel(ss);
chan = ieee80211_ht_adjust_channel(ic,
adhoc_pick_channel(ss), ic->ic_flags_ext);
else
chan = ic->ic_des_chan;
if (chan != NULL) {

View File

@ -105,7 +105,9 @@ struct ieee80211com {
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
uint32_t ic_flags_ven; /* vendor state flags */
uint32_t ic_caps; /* capabilities */
uint32_t ic_htcaps; /* HT capabilities */
uint8_t ic_modecaps[2]; /* set of mode capabilities */
uint16_t ic_curmode; /* current mode */
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
@ -113,7 +115,6 @@ struct ieee80211com {
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
uint32_t ic_htcaps; /* HT capabilities */
int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
int ic_ampdu_density;/* A-MPDU density */
int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
@ -175,8 +176,12 @@ struct ieee80211com {
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
uint16_t ic_sta_assoc; /* stations associated */
uint16_t ic_ht_sta_assoc;/* HT stations associated */
uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */
uint8_t ic_curhtprotmode;/* HTINFO bss state */
enum ieee80211_protmode ic_htprotmode; /* HT protection mode */
int ic_lastnonerp; /* last time non-ERP sta noted*/
int ic_lastnonht; /* last time non-HT sta noted */
struct ifqueue ic_mgtq;
enum ieee80211_state ic_state; /* 802.11 state */
@ -334,7 +339,7 @@ struct ieee80211com {
((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
/* ic_flags_ext */
#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */
#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
@ -391,6 +396,8 @@ struct ieee80211com {
*/
#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */
#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */
/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */
#define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
@ -472,6 +479,16 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what)
ic->ic_update_beacon(ic, what);
}
/*
* Debugging facilities compiled in when IEEE80211_DEBUG is defined.
*
* The intent is that any problem in the net80211 layer can be
* diagnosed by inspecting the statistics (dumped by the wlanstats
* program) and/or the msgs generated by net80211. Messages are
* broken into functional classes and can be controlled with the
* wlandebug program. Certain of these msg groups are for facilities
* that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1X).
*/
#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */
#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */
@ -500,6 +517,8 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what)
#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */
#define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */
#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */
#define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */
#define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */
#define IEEE80211_MSG_ANY 0xffffffff /* anything */