From 1b6167d23905ef392a200c3cf17c0b59d5461bb9 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Fri, 2 Nov 2007 05:22:25 +0000 Subject: [PATCH] sync 11n support with vap code base; many changes based on interop testing with all major vendors MFC after: 1 week --- sys/net80211/ieee80211.h | 30 +- sys/net80211/ieee80211_freebsd.c | 21 + sys/net80211/ieee80211_freebsd.h | 8 + sys/net80211/ieee80211_ht.c | 881 +++++++++++++++++++++++------- sys/net80211/ieee80211_ht.h | 20 +- sys/net80211/ieee80211_input.c | 98 +++- sys/net80211/ieee80211_ioctl.c | 32 ++ sys/net80211/ieee80211_ioctl.h | 24 +- sys/net80211/ieee80211_node.c | 54 +- sys/net80211/ieee80211_node.h | 7 + sys/net80211/ieee80211_output.c | 92 ++-- sys/net80211/ieee80211_proto.c | 4 +- sys/net80211/ieee80211_proto.h | 10 + sys/net80211/ieee80211_scan_ap.c | 3 +- sys/net80211/ieee80211_scan_sta.c | 3 +- sys/net80211/ieee80211_var.h | 23 +- 16 files changed, 1037 insertions(+), 273 deletions(-) diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 14c7cd0ee520..0fc7078b40d0 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -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; /* diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index f9c1abd70c33..1cb78137e972 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -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, &du_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) diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 005f61dadb0a..0052dfe79915 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -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) diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index b96e3d9d3698..0a8341ad7b51 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -79,6 +79,12 @@ static const struct ieee80211_htrateset ieee80211_rateset_11n = 10, 11, 12, 13, 14, 15 } }; +#ifdef IEEE80211_AMPDU_AGE +/* XXX public for sysctl hookup */ +int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ +#endif +int ieee80211_recv_bar_ena = 1; + #define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250) #define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000) #define IEEE80211_AGGR_MAXTRIES 3 @@ -97,12 +103,10 @@ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni, void ieee80211_ht_attach(struct ieee80211com *ic) { - - ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; - ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; - ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; - - ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; +#ifdef IEEE80211_AMPDU_AGE + if (ieee80211_ampdu_age == -1) + ieee80211_ampdu_age = msecs_to_ticks(500); +#endif /* setup default aggregation policy */ ic->ic_recv_action = ieee80211_aggr_recv_action; @@ -111,11 +115,19 @@ ieee80211_ht_attach(struct ieee80211com *ic) ic->ic_addba_response = ieee80211_addba_response; ic->ic_addba_stop = ieee80211_addba_stop; - if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || - isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { + ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; + ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; + + /* XXX get from driver */ + ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; + ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; + ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; + ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; + + if (ic->ic_htcaps & IEEE80211_HTC_HT) { /* - * There are HT channels in the channel list; enable - * all HT-related facilities by default. + * Device is HT capable; enable all HT-related + * facilities by default. * XXX these choices may be too aggressive. */ ic->ic_flags_ext |= IEEE80211_FEXT_HT @@ -123,7 +135,7 @@ ieee80211_ht_attach(struct ieee80211com *ic) ; if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; - /* XXX infer from channel list */ + /* XXX infer from channel list? */ if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) @@ -136,8 +148,6 @@ ieee80211_ht_attach(struct ieee80211com *ic) ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; - - ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; } } @@ -179,11 +189,7 @@ const struct ieee80211_htrateset * ieee80211_get_suphtrates(struct ieee80211com *ic, const struct ieee80211_channel *c) { - if (IEEE80211_IS_CHAN_HT(c)) - return &ieee80211_rateset_11n; - /* XXX what's the right thing to do here? */ - return (const struct ieee80211_htrateset *) - ieee80211_get_suprates(ic, c); + return &ieee80211_rateset_11n; } /* @@ -255,7 +261,6 @@ ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); rap->rxa_start = start; - rap->rxa_nxt = rap->rxa_start; rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; } @@ -333,13 +338,6 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) ampdu_dispatch(ni, m); } - /* - * Adjust the start of the BA window to - * reflect the frames just dispatched. - */ - rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); - rap->rxa_nxt = rap->rxa_start; - ic->ic_stats.is_ampdu_rx_oor += i; /* * If frames remain, copy the mbuf pointers down so * they correspond to the offsets in the new window. @@ -357,36 +355,113 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) KASSERT(n == 0, ("lost %d frames", n)); ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; } + /* + * Adjust the start of the BA window to + * reflect the frames just dispatched. + */ + rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); + ic->ic_stats.is_ampdu_rx_oor += i; } +#ifdef IEEE80211_AMPDU_AGE /* - * Dispatch all frames in the A-MPDU - * re-order queue up to the specified slot. + * Dispatch all frames in the A-MPDU re-order queue. */ static void -ampdu_rx_flush(struct ieee80211_node *ni, - struct ieee80211_rx_ampdu *rap, int limit) +ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { + struct ieee80211com *ic = ni->ni_ic; struct mbuf *m; int i; - for (i = 0; i < limit; i++) { + for (i = 0; i < rap->rxa_wnd; i++) { m = rap->rxa_m[i]; if (m == NULL) continue; rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + ic->ic_stats.is_ampdu_rx_oor++; + ampdu_dispatch(ni, m); - if (--rap->rxa_qframes == 0) + if (rap->rxa_qframes == 0) break; } } +#endif /* IEEE80211_AMPDU_AGE */ + +/* + * Dispatch all frames in the A-MPDU re-order queue + * preceding the specified sequence number. This logic + * handles window moves due to a received MSDU or BAR. + */ +static void +ampdu_rx_flush_upto(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + ieee80211_seq seqno; + int i; + + /* + * Flush any complete MSDU's with a sequence number lower + * than winstart. Gaps may exist. Note that we may actually + * dispatch frames past winstart if a run continues; this is + * an optimization that avoids having to do a separate pass + * to dispatch frames after moving the BA window start. + */ + seqno = rap->rxa_start; + for (i = 0; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m != NULL) { + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + ic->ic_stats.is_ampdu_rx_oor++; + + ampdu_dispatch(ni, m); + } else { + if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) + break; + } + seqno = IEEE80211_SEQ_INC(seqno); + } + /* + * If frames remain, copy the mbuf pointers down so + * they correspond to the offsets in the new window. + */ + if (rap->rxa_qframes != 0) { + int n = rap->rxa_qframes, j; + for (j = i+1; j < rap->rxa_wnd; j++) { + if (rap->rxa_m[j] != NULL) { + rap->rxa_m[j-i] = rap->rxa_m[j]; + rap->rxa_m[j] = NULL; + if (--n == 0) + break; + } + } + KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d " + "BA win <%d:%d> winstart %d", + __func__, n, rap->rxa_qframes, i, rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + winstart)); + ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + } + /* + * Move the start of the BA window; we use the + * sequence number of the last MSDU that was + * passed up the stack+1 or winstart if stopped on + * a gap in the reorder buffer. + */ + rap->rxa_start = seqno; +} /* * Process a received QoS data frame for an HT station. Handle * A-MPDU reordering: if this frame is received out of order * and falls within the BA window hold onto it. Otherwise if - * this frame completes a run flush any pending frames. We + * this frame completes a run, flush any pending frames. We * return 1 if the frame is consumed. A 0 is returned if * the frame should be processed normally by the caller. */ @@ -395,6 +470,8 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) { #define IEEE80211_FC0_QOSDATA \ (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) +#define PROCESS 0 /* caller should process frame */ +#define CONSUMED 1 /* frame consumed, caller does nothing */ struct ieee80211com *ic = ni->ni_ic; struct ieee80211_qosframe *wh; struct ieee80211_rx_ampdu *rap; @@ -408,16 +485,21 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) wh = mtod(m, struct ieee80211_qosframe *); KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data")); - /* XXX 4-address frame */ - tid = wh->i_qos[0] & IEEE80211_QOS_TID; + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0]; + else + tid = wh->i_qos[0]; + tid &= IEEE80211_QOS_TID; rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { /* * No ADDBA request yet, don't touch. */ - return 0; + return PROCESS; } rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + rap->rxa_nframes++; +again: if (rxseq == rap->rxa_start) { /* * First frame in window. @@ -429,97 +511,136 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); ampdu_dispatch(ni, m); ampdu_rx_dispatch(rap, ni); - return 1; /* NB: consumed */ + return CONSUMED; } else { /* * In order; advance window and notify * caller to dispatch directly. */ rap->rxa_start = IEEE80211_SEQ_INC(rxseq); - rap->rxa_nxt = rap->rxa_start; - return 0; /* NB: process packet */ + return PROCESS; } } /* - * This packet is out of order; store it - * if it's in the BA window. + * Frame is out of order; store if in the BA window. */ /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); - if (off >= rap->rxa_wnd) { + if (off < rap->rxa_wnd) { /* - * Outside the window, clear the q and start over. + * Common case (hopefully): in the BA window. + * Sec 9.10.7.6 a) (D2.04 p.118 line 47) + */ +#ifdef IEEE80211_AMPDU_AGE + /* + * Check for frames sitting too long in the reorder queue. + * This should only ever happen if frames are not delivered + * without the sender otherwise notifying us (e.g. with a + * BAR to move the window). Typically this happens because + * of vendor bugs that cause the sequence number to jump. + * When this happens we get a gap in the reorder queue that + * leaves frame sitting on the queue until they get pushed + * out due to window moves. When the vendor does not send + * BAR this move only happens due to explicit packet sends * - * NB: this handles the case where rxseq is before - * rxa_start because our max BA window is 64 - * and the sequence number range is 4096. + * NB: we only track the time of the oldest frame in the + * reorder q; this means that if we flush we might push + * frames that still "new"; if this happens then subsequent + * frames will result in BA window moves which cost something + * but is still better than a big throughput dip. + */ + if (rap->rxa_qframes != 0) { + /* XXX honor batimeout? */ + if (ticks - rap->rxa_age > ieee80211_ampdu_age) { + /* + * Too long since we received the first + * frame; flush the reorder buffer. + */ + if (rap->rxa_qframes != 0) { + ic->ic_stats.is_ampdu_rx_age += + rap->rxa_qframes; + ampdu_rx_flush(ni, rap); + } + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + return PROCESS; + } + } else { + /* + * First frame, start aging timer. + */ + rap->rxa_age = ticks; + } +#endif /* IEEE80211_AMPDU_AGE */ + /* save packet */ + if (rap->rxa_m[off] == NULL) { + rap->rxa_m[off] = m; + rap->rxa_qframes++; + rap->rxa_qbytes += m->m_pkthdr.len; + ic->ic_stats.is_ampdu_rx_reorder++; + } else { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, + ni->ni_macaddr, "a-mpdu duplicate", + "seqno %u tid %u BA win <%u:%u>", + rxseq, tid, rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); + ic->ic_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + m_freem(m); + } + return CONSUMED; + } + if (off < IEEE80211_SEQ_BA_RANGE) { + /* + * Outside the BA window, but within range; + * flush the reorder q and move the window. + * Sec 9.10.7.6 b) (D2.04 p.118 line 60) */ IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, - "flush BA win <%u:%u> (%u frames) rxseq %u tid %u", + "move BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, - IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); + ic->ic_stats.is_ampdu_rx_move++; - if (rap->rxa_qframes != 0) { - ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; - ampdu_rx_flush(ni, rap, rap->rxa_wnd); - KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, - ("lost %u data, %u frames on ampdu rx q", - rap->rxa_qbytes, rap->rxa_qframes)); - } - rap->rxa_start = IEEE80211_SEQ_INC(rxseq); - rap->rxa_nxt = rap->rxa_start; - return 0; /* NB: process packet */ - } - if (rap->rxa_qframes != 0) { -#if 0 - /* XXX honor batimeout? */ - if (ticks - mn->mn_age[tid] > 50) { - /* - * Too long since we received the first frame; flush. - */ - if (rap->rxa_qframes != 0) { - ic->ic_stats.is_ampdu_rx_oor += - rap->rxa_qframes; - ampdu_rx_flush(ni, rap, rap->rxa_wnd); - } - rap->rxa_start = IEEE80211_SEQ_INC(rxseq); - rap->rxa_nxt = rap->rxa_start; - return 0; /* NB: process packet */ - } -#endif - rap->rxa_nxt = rxseq; + /* + * The spec says to flush frames up to but not including: + * WinStart_B = rxseq - rap->rxa_wnd + 1 + * Then insert the frame or notify the caller to process + * it immediately. We can safely do this by just starting + * over again because we know the frame will now be within + * the BA window. + */ + /* NB: rxa_wnd known to be >0 */ + ampdu_rx_flush_upto(ni, rap, + IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); + goto again; } else { /* - * First frame, start aging timer. + * Outside the BA window and out of range; toss. + * Sec 9.10.7.6 c) (D2.04 p.119 line 16) */ -#if 0 - mn->mn_age[tid] = ticks; -#endif - } - /* save packet */ - if (rap->rxa_m[off] == NULL) { - rap->rxa_m[off] = m; - rap->rxa_qframes++; - rap->rxa_qbytes += m->m_pkthdr.len; - } else { IEEE80211_DISCARD_MAC(ic, - IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, - ni->ni_macaddr, "a-mpdu duplicate", - "seqno %u tid %u BA win <%u:%u>", - rxseq, tid, rap->rxa_start, rap->rxa_wnd); - ic->ic_stats.is_rx_dup++; - IEEE80211_NODE_STAT(ni, rx_dup); + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, + "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid, + wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); + ic->ic_stats.is_ampdu_rx_drop++; + IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); + return CONSUMED; } - return 1; /* NB: consumed */ +#undef CONSUMED +#undef PROCESS #undef IEEE80211_FC0_QOSDATA } /* * Process a BAR ctl frame. Dispatch all frames up to * the sequence number of the frame. If this frame is - * out of the window it's discarded. + * out of range it's discarded. */ void ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) @@ -530,6 +651,14 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) ieee80211_seq rxseq; int tid, off; + if (!ieee80211_recv_bar_ena) { +#if 0 + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N, + ni->ni_macaddr, "BAR", "%s", "processing disabled"); +#endif + ic->ic_stats.is_ampdu_bar_bad++; + return; + } wh = mtod(m0, struct ieee80211_frame_bar *); /* XXX check basic BAR */ tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); @@ -546,58 +675,46 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) } ic->ic_stats.is_ampdu_bar_rx++; rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + if (rxseq == rap->rxa_start) + return; /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); - if (off >= rap->rxa_wnd) { + if (off < IEEE80211_SEQ_BA_RANGE) { /* - * Outside the window, flush the reorder q if - * not pulling the sequence # backward. The - * latter is typically caused by a dropped BA. + * Flush the reorder q up to rxseq and move the window. + * Sec 9.10.7.6 a) (D2.04 p.119 line 22) */ - IEEE80211_NOTE(ic, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni, - "recv BAR outside BA win <%u:%u> rxseq %u tid %u", + IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, - IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), - rxseq, tid); - ic->ic_stats.is_ampdu_bar_oow++; - if (rxseq < rap->rxa_start) { - /* XXX stat? */ - return; - } - if (rap->rxa_qframes != 0) { - ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; - ampdu_rx_flush(ni, rap, rap->rxa_wnd); - KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, - ("lost %u data, %u frames on ampdu rx q", - rap->rxa_qbytes, rap->rxa_qframes)); - } - } else if (rap->rxa_qframes != 0) { - /* - * Dispatch packets up to rxseq. - */ - ampdu_rx_flush(ni, rap, off); - ic->ic_stats.is_ampdu_rx_oor += off; + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid); + ic->ic_stats.is_ampdu_bar_move++; - /* - * If frames remain, copy the mbuf pointers down so - * they correspond to the offsets in the new window. - */ - if (rap->rxa_qframes != 0) { - int n = rap->rxa_qframes, j; - for (j = off+1; j < rap->rxa_wnd; j++) { - if (rap->rxa_m[j] != NULL) { - rap->rxa_m[j-off] = rap->rxa_m[j]; - rap->rxa_m[j] = NULL; - if (--n == 0) - break; - } - } - KASSERT(n == 0, ("lost %d frames", n)); - ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + ampdu_rx_flush_upto(ni, rap, rxseq); + if (off >= rap->rxa_wnd) { + /* + * BAR specifies a window start to the right of BA + * window; we must move it explicitly since + * ampdu_rx_flush_upto will not. + */ + rap->rxa_start = rxseq; } + } else { + /* + * Out of range; toss. + * Sec 9.10.7.6 b) (D2.04 p.119 line 41) + */ + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, + "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid, + wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); + ic->ic_stats.is_ampdu_bar_oow++; + IEEE80211_NODE_STAT(ni, rx_drop); } - rap->rxa_start = rxseq; - rap->rxa_nxt = rap->rxa_start; } /* @@ -611,12 +728,21 @@ ieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap) struct ieee80211_tx_ampdu *tap; int ac; + if (ni->ni_flags & IEEE80211_NODE_HT) { + /* + * Clean AMPDU state on re-associate. This handles the case + * where a station leaves w/o notifying us and then returns + * before node is reaped for inactivity. + */ + ieee80211_ht_node_cleanup(ni); + } ieee80211_parse_htcap(ni, htcap); for (ac = 0; ac < WME_NUM_AC; ac++) { tap = &ni->ni_tx_ampdu[ac]; tap->txa_ac = ac; + /* NB: further initialization deferred */ } - ni->ni_flags |= IEEE80211_NODE_HT; + ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; } /* @@ -634,14 +760,243 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni) /* XXX optimize this */ for (i = 0; i < WME_NUM_AC; i++) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; - if (IEEE80211_AMPDU_REQUESTED(tap)) + if (tap->txa_flags & IEEE80211_AGGR_SETUP) { + /* + * Stop BA stream if setup so driver has a chance + * to reclaim any resources it might have allocated. + */ ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]); + IEEE80211_TAPQ_DESTROY(tap); + /* NB: clearing NAK means we may re-send ADDBA */ + tap->txa_flags &= + ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); + } } for (i = 0; i < WME_NUM_TID; i++) ampdu_rx_stop(&ni->ni_rx_ampdu[i]); ni->ni_htcap = 0; - ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT); + ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT | + IEEE80211_NODE_AMPDU); +} + +static struct ieee80211_channel * +findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) +{ + return ieee80211_find_channel(ic, c->ic_freq, + (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags); +} + +/* + * Adjust a channel to be HT/non-HT according to the vap's configuration. + */ +struct ieee80211_channel * +ieee80211_ht_adjust_channel(struct ieee80211com *ic, + struct ieee80211_channel *chan, int flags) +{ + struct ieee80211_channel *c; + + if (flags & IEEE80211_FEXT_HT) { + /* promote to HT if possible */ + if (flags & IEEE80211_FEXT_USEHT40) { + if (!IEEE80211_IS_CHAN_HT40(chan)) { + /* NB: arbitrarily pick ht40+ over ht40- */ + c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); + if (c == NULL) + c = findhtchan(ic, chan, + IEEE80211_CHAN_HT40D); + if (c == NULL) + c = findhtchan(ic, chan, + IEEE80211_CHAN_HT20); + if (c != NULL) + chan = c; + } + } else if (!IEEE80211_IS_CHAN_HT20(chan)) { + c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); + if (c != NULL) + chan = c; + } + } else if (IEEE80211_IS_CHAN_HT(chan)) { + /* demote to legacy, HT use is disabled */ + c = ieee80211_find_channel(ic, chan->ic_freq, + chan->ic_flags &~ IEEE80211_CHAN_HT); + if (c != NULL) + chan = c; + } + return chan; +} + +/* + * Setup HT-specific state for a legacy WDS peer. + */ +void +ieee80211_ht_wds_init(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_tx_ampdu *tap; + int ac; + + KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested")); + + /* XXX check scan cache in case peer has an ap and we have info */ + /* + * If setup with a legacy channel; locate an HT channel. + * Otherwise if the inherited channel (from a companion + * AP) is suitable use it so we use the same location + * for the extension channel). + */ + ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan, + ic->ic_flags_ext); + + ni->ni_htcap = 0; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) + ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; + ni->ni_chw = 40; + if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; + else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; + if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) + ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; + } else { + ni->ni_chw = 20; + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; + } + ni->ni_htctlchan = ni->ni_chan->ic_ieee; + + ni->ni_htopmode = 0; /* XXX need protection state */ + ni->ni_htstbc = 0; /* XXX need info */ + + for (ac = 0; ac < WME_NUM_AC; ac++) { + tap = &ni->ni_tx_ampdu[ac]; + tap->txa_ac = ac; + } + /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */ + ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; +} + +/* + * Notify hostap vaps of a change in the HTINFO ie. + */ +static void +htinfo_notify(struct ieee80211com *ic) +{ + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return; + IEEE80211_NOTE(ic, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, + ic->ic_bss, + "HT bss occupancy change: %d sta, %d ht, " + "%d ht40%s, HT protmode now 0x%x" + , ic->ic_sta_assoc + , ic->ic_ht_sta_assoc + , ic->ic_ht40_sta_assoc + , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ? + ", non-HT sta present" : "" + , ic->ic_curhtprotmode); + ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO); +} + +/* + * Calculate HT protection mode from current + * state and handle updates. + */ +static void +htinfo_update(struct ieee80211com *ic) +{ + uint8_t protmode; + + if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) { + protmode = IEEE80211_HTINFO_OPMODE_PROTOPT + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { + protmode = IEEE80211_HTINFO_OPMODE_MIXED + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && + ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { + protmode = IEEE80211_HTINFO_OPMODE_HT20PR; + } else { + protmode = IEEE80211_HTINFO_OPMODE_PURE; + } + if (protmode != ic->ic_curhtprotmode) { + ic->ic_curhtprotmode = protmode; + htinfo_notify(ic); + } +} + +/* + * Handle an HT station joining a BSS. + */ +void +ieee80211_ht_node_join(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + if (ni->ni_flags & IEEE80211_NODE_HT) { + ic->ic_ht_sta_assoc++; + if (ni->ni_chw == 40) + ic->ic_ht40_sta_assoc++; + } + htinfo_update(ic); +} + +/* + * Handle an HT station leaving a BSS. + */ +void +ieee80211_ht_node_leave(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + if (ni->ni_flags & IEEE80211_NODE_HT) { + ic->ic_ht_sta_assoc--; + if (ni->ni_chw == 40) + ic->ic_ht40_sta_assoc--; + } + htinfo_update(ic); +} + +/* + * Public version of htinfo_update; used for processing + * beacon frames from overlapping bss in hostap_recv_mgmt. + */ +void +ieee80211_htinfo_update(struct ieee80211com *ic, int protmode) +{ + if (protmode != ic->ic_curhtprotmode) { + ic->ic_curhtprotmode = protmode; + htinfo_notify(ic); + } +} + +/* + * Time out presence of an overlapping bss with non-HT + * stations. When operating in hostap mode we listen for + * beacons from other stations and if we identify a non-HT + * station is present we update the opmode field of the + * HTINFO ie. To identify when all non-HT stations are + * gone we time out this condition. + */ +void +ieee80211_ht_timeout(struct ieee80211com *ic) +{ + IEEE80211_LOCK_ASSERT(ic); + + if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) && + time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { +#if 0 + IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + "%s", "time out non-HT STA present on channel"); +#endif + ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR; + htinfo_update(ic); + } } /* unalligned little endian access */ @@ -671,28 +1026,28 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) ni->ni_htcap = LE_READ_2(ie + __offsetof(struct ieee80211_ie_htcap, hc_cap)); - if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0) - ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI40; - if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) - ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI20; - ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) ? 40 : 20; ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; -#if 0 - ni->ni_maxampdu = - (8*1024) << MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); - ni->ni_mpdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); -#endif + /* XXX needed or will ieee80211_parse_htinfo always be called? */ + ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && + (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20; } /* - * Process an 802.11n HT info ie. + * Process an 802.11n HT info ie and update the node state. + * Note that we handle use this information to identify the + * correct channel (HT20, HT40+, HT40-, legacy). The caller + * is responsible for insuring any required channel change is + * done (e.g. in sta mode when parsing the contents of a + * beacon frame). */ void ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) { + struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_ie_htinfo *htinfo; + struct ieee80211_channel *c; uint16_t w; - int chw; + int htflags, chanflags; if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; @@ -703,12 +1058,48 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); w = LE_READ_2(&htinfo->hi_byte45); ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); - /* update node's recommended tx channel width */ - chw = (htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) ? 40 : 20; - if (chw != ni->ni_chw) { - ni->ni_chw = chw; - ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; + /* + * Handle 11n channel switch. Use the received HT ie's to + * identify the right channel to use. If we cannot locate it + * in the channel table then fallback to legacy operation. + */ + htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ? + IEEE80211_CHAN_HT20 : 0; + /* NB: honor operating mode constraint */ + if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && + (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) { + if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) + htflags = IEEE80211_CHAN_HT40U; + else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) + htflags = IEEE80211_CHAN_HT40D; } + chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; + if (chanflags != ni->ni_chan->ic_flags) { + c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); + if (c == NULL && htflags != IEEE80211_CHAN_HT20) { + /* + * No HT40 channel entry in our table; fall back + * to HT20 operation. This should not happen. + */ + c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); + IEEE80211_NOTE(ni->ni_ic, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "no HT40 channel (freq %u), falling back to HT20", + ni->ni_chan->ic_freq); + /* XXX stat */ + } + if (c != NULL && c != ni->ni_chan) { + IEEE80211_NOTE(ni->ni_ic, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "switch station to HT%d channel %u/0x%x", + IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, + c->ic_freq, c->ic_flags); + ni->ni_chan = c; + } + /* NB: caller responsible for forcing any channel change */ + } + /* update node's tx channel width */ + ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20; } /* @@ -848,6 +1239,9 @@ ieee80211_addba_response(struct ieee80211_node *ni, tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); tap->txa_flags |= IEEE80211_AGGR_RUNNING; + } else { + /* mark tid so we don't try again */ + tap->txa_flags |= IEEE80211_AGGR_NAK; } return 1; } @@ -905,21 +1299,36 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA request: dialogtoken %u " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " - "baseqctl %d", - dialogtoken, baparamset, tid, bufsiz, - batimeout, baseqctl); + "baseqctl %d:%d", + dialogtoken, baparamset, tid, bufsiz, batimeout, + MS(baseqctl, IEEE80211_BASEQ_START), + MS(baseqctl, IEEE80211_BASEQ_FRAG)); rap = &ni->ni_rx_ampdu[tid]; /* Send ADDBA response */ args[0] = dialogtoken; - if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) { + /* + * NB: We ack only if the sta associated with HT and + * the ap is configured to do AMPDU rx (the latter + * violates the 11n spec and is mostly for testing). + */ + if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && + (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) { ampdu_rx_start(rap, bufsiz, MS(baseqctl, IEEE80211_BASEQ_START)); args[1] = IEEE80211_STATUS_SUCCESS; - } else + } else { + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "reject ADDBA request: %s", + ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? + "administratively disabled" : + "not negotiated for station"); + ic->ic_stats.is_addba_reject++; args[1] = IEEE80211_STATUS_UNSPECIFIED; + } /* XXX honor rap flags? */ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE | SM(tid, IEEE80211_BAPS_TID) @@ -938,16 +1347,34 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); batimeout = LE_READ_2(frm+7); + ac = TID_TO_WME_AC(tid); + tap = &ni->ni_tx_ampdu[ac]; + if ((tap->txa_flags & ~IEEE80211_AGGR_XCHGPEND) == 0) { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "no pending ADDBA, tid %d dialogtoken %u " + "code %d", tid, dialogtoken, code); + ic->ic_stats.is_addba_norequest++; + return; + } + if (dialogtoken != tap->txa_token) { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "dialogtoken mismatch: waiting for %d, " + "received %d, tid %d code %d", + tap->txa_token, dialogtoken, tid, code); + ic->ic_stats.is_addba_badtoken++; + return; + } + IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA response: dialogtoken %u code %d " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", dialogtoken, code, baparamset, tid, bufsiz, batimeout); - - ac = TID_TO_WME_AC(tid); - tap = &ni->ni_tx_ampdu[ac]; - ic->ic_addba_response(ni, tap, code, baparamset, batimeout); return; @@ -976,7 +1403,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, } break; } - return ieee80211_recv_action(ni, frm, efrm); + ieee80211_recv_action(ni, frm, efrm); } /* @@ -1012,11 +1439,16 @@ ieee80211_recv_action(struct ieee80211_node *ni, } IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "%s: HT txchwidth. width %d (%s)", + "%s: HT txchwidth, width %d (%s)", __func__, chw, ni->ni_flags & IEEE80211_NODE_CHWUPDATE ? "new" : "no change"); break; + case IEEE80211_ACTION_HT_MIMOPWRSAVE: + IEEE80211_NOTE(ic, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: HT MIMO PS", __func__); + break; default: IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, @@ -1057,7 +1489,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { /* do deferred setup of state */ - /* XXX tap->txa_q */ + IEEE80211_TAPQ_INIT(tap); callout_init(&tap->txa_timer, CALLOUT_MPSAFE); tap->txa_flags |= IEEE80211_AGGR_SETUP; } @@ -1071,6 +1503,9 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, */ return 0; } + /* XXX hack for not doing proper locking */ + tap->txa_flags &= ~IEEE80211_AGGR_NAK; + dialogtoken = (tokens+1) % 63; /* XXX */ tid = WME_AC_TO_TID(tap->txa_ac); @@ -1086,6 +1521,12 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, /* NB: do first so there's no race against reply */ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { /* unable to setup state, don't make request */ + IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N, + ni, "%s: could not setup BA stream for AC %d", + __func__, tap->txa_ac); + /* defer next try so we don't slam the driver with requests */ + tap->txa_attempts = IEEE80211_AGGR_MAXTRIES; + tap->txa_lastrequest = ticks; return 0; } tokens = dialogtoken; /* allocate token */ @@ -1093,6 +1534,36 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, IEEE80211_ACTION_BA_ADDBA_REQUEST, args); } +/* + * Terminate an AMPDU tx stream. State is reclaimed + * and the peer notified with a DelBA Action frame. + */ +void +ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t args[4]; + + /* XXX locking */ + if (IEEE80211_AMPDU_RUNNING(tap)) { + IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac); + ic->ic_stats.is_ampdu_stop++; + + ic->ic_addba_stop(ni, tap); + args[0] = WME_AC_TO_TID(tap->txa_ac); + args[1] = IEEE80211_DELBAPS_INIT; + args[2] = 1; /* XXX reason code */ + ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_DELBA, args); + } else { + IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "%s: BA stream for AC %d not running", + __func__, tap->txa_ac); + ic->ic_stats.is_ampdu_stop_failed++; + } +} + /* * Transmit a BAR frame to the specified node. The * BAR contents are drawn from the supplied aggregation @@ -1148,14 +1619,13 @@ ieee80211_send_bar(struct ieee80211_node *ni, IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "[%s] send bar frame (tid %u start %u) on channel %u\n", - ether_sprintf(ni->ni_macaddr), tid, tap->txa_start, - ieee80211_chan2ieee(ic, ic->ic_curchan)); + IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + ni, "send bar frame (tid %u start %u) on channel %u", + tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan)); m->m_pkthdr.rcvif = (void *)ni; IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ - (*ifp->if_start)(ifp); + if_start(ifp); return 0; bad: @@ -1217,8 +1687,10 @@ ieee80211_send_action(struct ieee80211_node *ni, case IEEE80211_ACTION_BA_ADDBA_REQUEST: IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "send ADDBA request: tid %d, baparamset 0x%x", - args[0], args[1]); + "send ADDBA request: dialogtoken %d " + "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", + args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), + args[2], args[3]); *frm++ = args[0]; /* dialog token */ ADDSHORT(frm, args[1]); /* baparamset */ @@ -1261,7 +1733,7 @@ ieee80211_send_action(struct ieee80211_node *ni, IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send HT txchwidth: width %d", - IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 + IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 ); *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? IEEE80211_A_HT_TXCHWIDTH_2040 : @@ -1327,12 +1799,27 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) /* HT capabilities */ caps = ic->ic_htcaps & 0xffff; - /* override 20/40 use based on channel and config */ - if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && - (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) - caps |= IEEE80211_HTCAP_CHWIDTH40; - else - caps &= ~IEEE80211_HTCAP_CHWIDTH40; + /* + * Note channel width depends on whether we are operating as + * a sta or not. When operating as a sta we are generating + * a request based on our desired configuration. Otherwise + * we are operational and the channel attributes identify + * how we've been setup (which might be different if a fixed + * channel is specified). + */ + if (ic->ic_opmode == IEEE80211_M_STA) { + /* override 20/40 use based on config */ + if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + } else { + /* override 20/40 use based on current channel */ + if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + } /* adjust short GI based on channel and config */ if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; @@ -1342,13 +1829,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) ADDSHORT(frm, caps); /* HT parameters */ - switch (ic->ic_ampdu_rxmax / 1024) { - case 8: *frm = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; - case 16: *frm = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; - case 32: *frm = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; - default: *frm = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; - } - *frm |= SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY); + *frm = SM(ic->ic_ampdu_rxmax, IEEE80211_HTCAP_MAXRXAMPDU) + | SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY) + ; frm++; /* pre-zero remainder of ie */ @@ -1356,7 +1839,13 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); /* supported MCS set */ - ieee80211_set_htrates(frm, &ni->ni_htrates); + /* + * XXX it would better to get the rate set from ni_htrates + * so we can restrict it but for sta mode ni_htrates isn't + * setup when we're called to form an AssocReq frame so for + * now we're restricted to the default HT rate set. + */ + ieee80211_set_htrates(frm, &ieee80211_rateset_11n); frm += sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset); @@ -1442,6 +1931,10 @@ ieee80211_ht_update_beacon(struct ieee80211com *ic, /* * Add body of an HTINFO information element. + * + * NB: We don't use struct ieee80211_ie_htinfo because we can + * be called to fillin both a standard ie and a compat ie that + * has a vendor OUI at the front. */ static uint8_t * ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index 5f2689b6ab66..e876449ea6e0 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -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 *, diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index ba57da1e849e..ad6b24ff337f 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -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) ? diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 3fe85e97322b..83627e83bd44 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -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; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 6fe168ed7a26..e96ab521f3b6 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -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. diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 8e59b503518d..976651786fe0 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -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 diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 070890df55a5..2799ed4c814c 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -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 *, diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index b6850ed695e8..a20a3c971ffa 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -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 *); diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 8e4df65c1725..b7747be876f9 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -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... */ diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 41ed3ba9bf7c..9f94f1cfc77b 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -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[]; diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c index 9aa09ede78e0..300f4407b407 100644 --- a/sys/net80211/ieee80211_scan_ap.c +++ b/sys/net80211/ieee80211_scan_ap.c @@ -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; } } diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index 4fbaf6408f01..7a8ad0694670 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -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) { diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index d66198b310b1..cd8403074b3d 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -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 */