PERFORCE change 119566 for review
Sam Leffler
sam at FreeBSD.org
Wed May 9 16:30:20 UTC 2007
http://perforce.freebsd.org/chv.cgi?CH=119566
Change 119566 by sam at sam_ebb on 2007/05/09 16:30:02
Minimal 802.11n support; mostly tested in ap mode, sta
mode is known to be incomplete; a-msdu is untested; a-mpdu
is known to work but BAR handling needs fixups.
This grows per-sta state significantly; we may want to
make support conditional.
This work is derived from code provided by Atheros.
Sponsored by: Marvelle
Reviewed by: sephe
Affected files ...
.. //depot/projects/wifi/sys/net80211/_ieee80211.h#19 edit
.. //depot/projects/wifi/sys/net80211/ieee80211.c#51 edit
.. //depot/projects/wifi/sys/net80211/ieee80211.h#20 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_freebsd.h#26 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_ht.c#1 add
.. //depot/projects/wifi/sys/net80211/ieee80211_ht.h#1 add
.. //depot/projects/wifi/sys/net80211/ieee80211_input.c#102 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_ioctl.c#77 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_ioctl.h#38 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_node.c#92 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_node.h#44 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_output.c#73 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_proto.c#55 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_proto.h#35 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_radiotap.h#10 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_regdomain.c#3 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_regdomain.h#2 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_scan.c#12 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_scan.h#7 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_scan_ap.c#6 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_scan_sta.c#18 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_var.h#56 edit
Differences ...
==== //depot/projects/wifi/sys/net80211/_ieee80211.h#19 (text+ko) ====
@@ -29,7 +29,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/sys/net80211/_ieee80211.h,v 1.8 2007/03/07 04:35:07 sam Exp $
+ * $FreeBSD: src/sys/net80211/_ieee80211.h,v 1.7 2007/02/02 02:45:33 sam Exp $
*/
#ifndef _NET80211__IEEE80211_H_
#define _NET80211__IEEE80211_H_
@@ -39,6 +39,7 @@
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
+ IEEE80211_T_HT, /* high throughput, full GI */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
@@ -52,8 +53,10 @@
IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */
IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */
IEEE80211_MODE_STURBO_A = 7, /* 5GHz, OFDM, 2x clock, static */
+ IEEE80211_MODE_11NA = 8, /* 5GHz, w/ HT */
+ IEEE80211_MODE_11NG = 9, /* 2GHz, w/ HT */
};
-#define IEEE80211_MODE_MAX (IEEE80211_MODE_STURBO_A+1)
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1)
enum ieee80211_opmode {
IEEE80211_M_STA = 1, /* infrastructure station */
@@ -66,7 +69,7 @@
#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
/*
- * 802.11g protection mode.
+ * 802.11g/802.11n protection mode.
*/
enum ieee80211_protmode {
IEEE80211_PROT_NONE = 0, /* no protection */
@@ -135,7 +138,13 @@
#define IEEE80211_CHAN_STURBO 0x02000 /* 11a static turbo channel only */
#define IEEE80211_CHAN_HALF 0x04000 /* Half rate channel */
#define IEEE80211_CHAN_QUARTER 0x08000 /* Quarter rate channel */
+#define IEEE80211_CHAN_HT20 0x10000 /* HT 20 channel */
+#define IEEE80211_CHAN_HT40U 0x20000 /* HT 40 channel w/ ext above */
+#define IEEE80211_CHAN_HT40D 0x40000 /* HT 40 channel w/ ext below */
+#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
+#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
+
/*
* Useful combinations of channel characteristics.
*/
@@ -158,7 +167,8 @@
#define IEEE80211_CHAN_ALL \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \
- IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN)
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
+ IEEE80211_CHAN_HT)
#define IEEE80211_CHAN_ALLTURBO \
(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
@@ -208,6 +218,22 @@
(((_c)->ic_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF)) == 0)
#define IEEE80211_IS_CHAN_GSM(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_GSM) != 0)
+#define IEEE80211_IS_CHAN_HT(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define IEEE80211_IS_CHAN_HT20(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT20) != 0)
+#define IEEE80211_IS_CHAN_HT40(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40) != 0)
+#define IEEE80211_IS_CHAN_HT40U(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40U) != 0)
+#define IEEE80211_IS_CHAN_HT40D(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40D) != 0)
+#define IEEE80211_IS_CHAN_HTA(_c) \
+ (IEEE80211_IS_CHAN_5GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define IEEE80211_IS_CHAN_HTG(_c) \
+ (IEEE80211_IS_CHAN_2GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
/* ni_chan encoding for FH phy */
#define IEEE80211_FH_CHANMOD 80
@@ -226,6 +252,26 @@
u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
+/*
+ * 802.11n variant of ieee80211_rateset. Instead
+ * legacy rates the entries are MCS rates. We define
+ * the structure such that it can be used interchangeably
+ * with an ieee80211_rateset (modulo structure size).
+ */
+#define IEEE80211_HTRATE_MAXSIZE 127
+
+struct ieee80211_htrateset {
+ u_int8_t rs_nrates;
+ u_int8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+};
+
+/*
+ * Roaming state visible to user space. There are two
+ * thresholds that control whether roaming is considered;
+ * when either is exceeded the 802.11 layer will check
+ * the scan cache for another AP. If the cache is stale
+ * then a scan may be triggered.
+ */
struct ieee80211_roam {
int8_t rssi11a; /* rssi thresh for 11a bss */
int8_t rssi11b; /* for 11g sta in 11b bss */
==== //depot/projects/wifi/sys/net80211/ieee80211.c#51 (text+ko) ====
@@ -60,6 +60,8 @@
"turboA", /* IEEE80211_MODE_TURBO_A */
"turboG", /* IEEE80211_MODE_TURBO_G */
"sturboA", /* IEEE80211_MODE_STURBO_A */
+ "11na", /* IEEE80211_MODE_11NA */
+ "11ng", /* IEEE80211_MODE_11NG */
};
/*
@@ -181,6 +183,10 @@
setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G);
if (IEEE80211_IS_CHAN_ST(c))
setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A);
+ if (IEEE80211_IS_CHAN_HTA(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11NA);
+ if (IEEE80211_IS_CHAN_HTG(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11NG);
}
/* initialize candidate channels to all available */
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
@@ -216,6 +222,19 @@
bpfattach2(ifp, DLT_IEEE802_11,
sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
+ /* XXX no way to recalculate on ifdetach */
+ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+ /* XXX sanity check... */
+ max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ }
+
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
@@ -232,6 +251,7 @@
#endif
if (ic->ic_caps & IEEE80211_C_BURST)
ic->ic_flags |= IEEE80211_F_BURST;
+ ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
@@ -246,6 +266,7 @@
ieee80211_node_attach(ic);
ieee80211_power_attach(ic);
ieee80211_proto_attach(ic);
+ ieee80211_ht_attach(ic);
ieee80211_scan_attach(ic);
ieee80211_add_vap(ic);
@@ -272,6 +293,7 @@
ieee80211_sysctl_detach(ic);
ieee80211_scan_detach(ic);
+ ieee80211_ht_detach(ic);
/* NB: must be called before ieee80211_node_detach */
ieee80211_proto_detach(ic);
ieee80211_crypto_detach(ic);
@@ -439,6 +461,8 @@
TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */
TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */
TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */
+ IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */
+ IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */
};
u_int mopt;
@@ -501,7 +525,7 @@
* Add media for legacy operating modes.
*/
memset(&allrates, 0, sizeof(allrates));
- for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
+ for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
addmedia(ic, mode, IFM_AUTO);
@@ -539,6 +563,27 @@
/* NB: remove media options from mword */
addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
}
+ /*
+ * Add HT/11n media. Note that we do not have enough
+ * bits in the media subtype to express the MCS so we
+ * use a "placeholder" media subtype and any fixed MCS
+ * must be specified with a different mechanism.
+ */
+ for (; mode < IEEE80211_MODE_MAX; mode++) {
+ if (isclr(ic->ic_modecaps, mode))
+ continue;
+ addmedia(ic, mode, IFM_AUTO);
+ addmedia(ic, mode, IFM_IEEE80211_MCS);
+ }
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
+ isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
+ addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+ /* XXX could walk htrates */
+ /* XXX known array size */
+ if (ieee80211_htrates[15] > maxrate)
+ maxrate = ieee80211_htrates[15];
+ }
+
/* NB: strip explicit mode; we're actually in autoselect */
ifmedia_set(&ic->ic_media,
media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
@@ -554,6 +599,12 @@
return &ieee80211_rateset_half;
if (IEEE80211_IS_CHAN_QUARTER(c))
return &ieee80211_rateset_quarter;
+ if (IEEE80211_IS_CHAN_HTA(c))
+ return &ic->ic_sup_rates[IEEE80211_MODE_11A];
+ if (IEEE80211_IS_CHAN_HTG(c)) {
+ /* XXX does this work for basic rates? */
+ return &ic->ic_sup_rates[IEEE80211_MODE_11G];
+ }
return &ic->ic_sup_rates[ieee80211_chan2mode(c)];
}
@@ -564,7 +615,7 @@
int i, mode, rate, mword;
const struct ieee80211_rateset *rs;
- for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
+ for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
@@ -579,6 +630,7 @@
}
printf("\n");
}
+ ieee80211_ht_announce(ic);
}
void
@@ -597,6 +649,8 @@
type = 'T';
else if (IEEE80211_IS_CHAN_108G(c))
type = 'G';
+ else if (IEEE80211_IS_CHAN_HT(c))
+ type = 'n';
else if (IEEE80211_IS_CHAN_A(c))
type = 'a';
else if (IEEE80211_IS_CHAN_ANYG(c))
@@ -605,15 +659,19 @@
type = 'b';
else
type = 'f';
- if (IEEE80211_IS_CHAN_HALF(c))
+ if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c))
+ cw = 40;
+ else if (IEEE80211_IS_CHAN_HALF(c))
cw = 10;
else if (IEEE80211_IS_CHAN_QUARTER(c))
cw = 5;
else
cw = 20;
- printf("%4d %4d%c %2d %6d %4d.%d %4d.%d\n"
+ printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n"
, c->ic_ieee, c->ic_freq, type
, cw
+ , IEEE80211_IS_CHAN_HT40U(c) ? '+' :
+ IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' '
, c->ic_maxregpower
, c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0
, c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0
@@ -727,6 +785,12 @@
case IFM_IEEE80211_FH:
newphymode = IEEE80211_MODE_FH;
break;
+ case IFM_IEEE80211_11NA:
+ newphymode = IEEE80211_MODE_11NA;
+ break;
+ case IFM_IEEE80211_11NG:
+ newphymode = IEEE80211_MODE_11NG;
+ break;
case IFM_AUTO:
newphymode = IEEE80211_MODE_AUTO;
break;
@@ -748,6 +812,7 @@
else
return EINVAL;
}
+ /* XXX HT40 +/- */
/*
* Next, the fixed/variable rate.
*/
@@ -855,21 +920,28 @@
/* should not come here */
break;
}
- if (IEEE80211_IS_CHAN_A(chan)) {
+ if (IEEE80211_IS_CHAN_HTA(chan)) {
+ status |= IFM_IEEE80211_11NA;
+ } else if (IEEE80211_IS_CHAN_HTG(chan)) {
+ status |= IFM_IEEE80211_11NG;
+ } else if (IEEE80211_IS_CHAN_A(chan)) {
status |= IFM_IEEE80211_11A;
- if (IEEE80211_IS_CHAN_TURBO(chan))
- status |= IFM_IEEE80211_TURBO;
} else if (IEEE80211_IS_CHAN_B(chan)) {
status |= IFM_IEEE80211_11B;
} else if (IEEE80211_IS_CHAN_ANYG(chan)) {
status |= IFM_IEEE80211_11G;
- if (IEEE80211_IS_CHAN_TURBO(chan))
- status |= IFM_IEEE80211_TURBO;
} else if (IEEE80211_IS_CHAN_FHSS(chan)) {
status |= IFM_IEEE80211_FH;
}
/* XXX else complain? */
+ if (IEEE80211_IS_CHAN_TURBO(chan))
+ status |= IFM_IEEE80211_TURBO;
+ if (IEEE80211_IS_CHAN_HT40U(chan))
+ status |= IFM_IEEE80211_HT40PLUS;
+ if (IEEE80211_IS_CHAN_HT40D(chan))
+ status |= IFM_IEEE80211_HT40MINUS;
+
return status;
}
@@ -909,6 +981,7 @@
} else if (ic->ic_opmode == IEEE80211_M_STA) {
/*
* In station mode report the current transmit rate.
+ * XXX HT rate
*/
rs = &ic->ic_bss->ni_rates;
imr->ifm_active |= ieee80211_rate2media(ic,
@@ -949,7 +1022,11 @@
ieee80211_chan2mode(const struct ieee80211_channel *chan)
{
- if (IEEE80211_IS_CHAN_108G(chan))
+ if (IEEE80211_IS_CHAN_HTA(chan))
+ return IEEE80211_MODE_11NA;
+ else if (IEEE80211_IS_CHAN_HTG(chan))
+ return IEEE80211_MODE_11NG;
+ else if (IEEE80211_IS_CHAN_108G(chan))
return IEEE80211_MODE_TURBO_G;
else if (IEEE80211_IS_CHAN_ST(chan))
return IEEE80211_MODE_STURBO_A;
@@ -988,7 +1065,8 @@
/*
* Convert IEEE80211 rate value to ifmedia subtype.
- * Rate is a legacy rate in units of 0.5Mbps.
+ * Rate is either a legacy rate in units of 0.5Mbps
+ * or an MCS index.
*/
int
ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
@@ -1027,10 +1105,47 @@
{ 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 },
/* NB: OFDM72 doesn't realy exist so we don't handle it */
};
+ static const struct ratemedia htrates[] = {
+ { 0, IFM_IEEE80211_MCS },
+ { 1, IFM_IEEE80211_MCS },
+ { 2, IFM_IEEE80211_MCS },
+ { 3, IFM_IEEE80211_MCS },
+ { 4, IFM_IEEE80211_MCS },
+ { 5, IFM_IEEE80211_MCS },
+ { 6, IFM_IEEE80211_MCS },
+ { 7, IFM_IEEE80211_MCS },
+ { 8, IFM_IEEE80211_MCS },
+ { 9, IFM_IEEE80211_MCS },
+ { 10, IFM_IEEE80211_MCS },
+ { 11, IFM_IEEE80211_MCS },
+ { 12, IFM_IEEE80211_MCS },
+ { 13, IFM_IEEE80211_MCS },
+ { 14, IFM_IEEE80211_MCS },
+ { 15, IFM_IEEE80211_MCS },
+ };
+ int m;
+ /*
+ * Check 11n rates first for match as an MCS.
+ */
+ if (mode == IEEE80211_MODE_11NA) {
+ if ((rate & IEEE80211_RATE_BASIC) == 0) {
+ m = findmedia(htrates, N(htrates), rate);
+ if (m != IFM_AUTO)
+ return m | IFM_IEEE80211_11NA;
+ }
+ } else if (mode == IEEE80211_MODE_11NG) {
+ /* NB: 12 is ambiguous, it will be treated as an MCS */
+ if ((rate & IEEE80211_RATE_BASIC) == 0) {
+ m = findmedia(htrates, N(htrates), rate);
+ if (m != IFM_AUTO)
+ return m | IFM_IEEE80211_11NG;
+ }
+ }
rate &= IEEE80211_RATE_VAL;
switch (mode) {
case IEEE80211_MODE_11A:
+ case IEEE80211_MODE_11NA:
case IEEE80211_MODE_TURBO_A:
case IEEE80211_MODE_STURBO_A:
return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
@@ -1046,6 +1161,7 @@
/* NB: hack, 11g matches both 11b+11a rates */
/* fall thru... */
case IEEE80211_MODE_11G:
+ case IEEE80211_MODE_11NG:
case IEEE80211_MODE_TURBO_G:
return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G);
}
@@ -1082,6 +1198,7 @@
6, /* IFM_IEEE80211_OFDM3 */
9, /* IFM_IEEE80211_OFDM4 */
54, /* IFM_IEEE80211_OFDM27 */
+ -1, /* IFM_IEEE80211_MCS */
};
return IFM_SUBTYPE(mword) < N(ieeerates) ?
ieeerates[IFM_SUBTYPE(mword)] : 0;
==== //depot/projects/wifi/sys/net80211/ieee80211.h#20 (text+ko) ====
==== //depot/projects/wifi/sys/net80211/ieee80211_freebsd.h#26 (text+ko) ====
@@ -191,6 +191,8 @@
#define M_MORE_DATA M_PROTO5 /* more data frames to follow */
#define M_FF 0x20000 /* fast frame */
#define M_TXCB 0x40000 /* do tx complete callback */
+/* rx path usage */
+#define M_AMPDU M_PROTO5 /* A-MPDU processing done */
/*
* Encode WME access control bits in the PROTO flags.
* This is safe since it's passed directly in to the
==== //depot/projects/wifi/sys/net80211/ieee80211_input.c#102 (text+ko) ====
@@ -108,10 +108,28 @@
struct ieee80211_key *key;
struct ether_header *eh;
int hdrspace, need_tap;
- u_int8_t dir, type, subtype;
+ u_int8_t dir, type, subtype, qos;
u_int8_t *bssid;
u_int16_t rxseq;
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ need_tap = 0;
+ goto resubmit_ampdu;
+ }
+
KASSERT(ni != NULL, ("null node"));
ni->ni_inact = ni->ni_inact_reload;
@@ -234,7 +252,8 @@
} else
tid = IEEE80211_NONQOS_TID;
rxseq = le16toh(*(u_int16_t *)wh->i_seq);
- if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
/* duplicate, discard */
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
@@ -328,6 +347,7 @@
/*
* Check for power save state change.
+ * XXX out-of-order A-MPDU frames?
*/
if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
(ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
@@ -340,6 +360,23 @@
}
/*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
* we (potentially) call ieee80211_crypto_demic;
@@ -371,6 +408,16 @@
}
/*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -448,8 +495,13 @@
goto out;
}
}
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
- if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
struct llc *llc;
@@ -547,6 +599,9 @@
case IEEE80211_FC0_SUBTYPE_PS_POLL:
ieee80211_recv_pspoll(ic, ni, m);
break;
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(ni, m);
+ break;
}
}
goto out;
@@ -828,7 +883,7 @@
}
/*
- * Decap a frame encapsulated in a fast-frame.
+ * Decap a frame encapsulated in a fast-frame/A-MSDU.
*/
struct mbuf *
ieee80211_decap1(struct mbuf *m, int *framelen)
@@ -1438,6 +1493,18 @@
return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
}
+static __inline int
+ishtcapoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
+}
+
/*
* Convert a WPA cipher selector OUI to an internal
* cipher algorithm. Where appropriate we also
@@ -1986,7 +2053,7 @@
#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
u_int8_t *frm, *efrm;
- u_int8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath;
+ u_int8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
int reassoc, resp, allocbs;
u_int8_t rate;
@@ -2025,6 +2092,8 @@
* [tlv] extended supported rates
* [tlv] WME
* [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
* [tlv] Atheros capabilities
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
@@ -2082,9 +2151,15 @@
}
scan.erp = frm[2];
break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan.htcap = frm;
+ break;
case IEEE80211_ELEMID_RSN:
scan.rsn = frm;
break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan.htinfo = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan.wpa = frm;
@@ -2092,6 +2167,19 @@
scan.wme = frm;
else if (isatherosoui(frm))
scan.ath = 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 (scan.htcap == NULL)
+ scan.htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan.htinfo == NULL)
+ scan.htcap = frm;
+ }
+ }
break;
default:
IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
@@ -2147,6 +2235,25 @@
ic->ic_stats.is_rx_badbintval++;
return;
}
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan.htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htcap[1],
+ scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ scan.htcap = NULL);
+ }
+ if (scan.htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
+ scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+ sizeof(struct ieee80211_ie_htinfo)-2,
+ scan.htinfo = NULL);
+ }
/*
* Count frame now that we know it's to be processed.
@@ -2208,6 +2315,10 @@
ieee80211_wme_updateparams(ic);
if (scan.ath != NULL)
ieee80211_parse_athparams(ni, scan.ath, wh);
+ if (scan.htcap != NULL)
+ ieee80211_parse_htcap(ni, scan.htcap);
+ if (scan.htinfo != NULL)
+ ieee80211_parse_htinfo(ni, scan.htinfo);
if (scan.tim != NULL) {
struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *) scan.tim;
@@ -2497,6 +2608,7 @@
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WPA or RSN
+ * [tlv] HT capabilities
* [tlv] Atheros capabilities
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
@@ -2512,7 +2624,7 @@
lintval = le16toh(*(u_int16_t *)frm); frm += 2;
if (reassoc)
frm += 6; /* ignore current AP info */
- ssid = rates = xrates = wpa = rsn = wme = ath = NULL;
+ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
@@ -2529,6 +2641,9 @@
case IEEE80211_ELEMID_RSN:
rsn = frm;
break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
wpa = frm;
@@ -2536,6 +2651,10 @@
wme = frm;
else if (isatherosoui(frm))
ath = frm;
+ else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ if (ishtcapoui(frm) && htcap == NULL)
+ htcap = frm;
+ }
break;
}
frm += frm[1] + 2;
@@ -2546,6 +2665,13 @@
IEEE80211_RATE_MAXSIZE - rates[1]);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+ if (htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(htcap[1],
+ htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ return); /* XXX just NULL out? */
+ }
if (ni == ic->ic_bss) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
@@ -2654,6 +2780,21 @@
ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
return;
}
+ /* XXX enforce PUREN */
+ /* 802.11n-specific rateset handling */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
+ rate = ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
+ IEEE80211_F_DOBRS);
+ if (rate & IEEE80211_RATE_BASIC) {
+ /* XXX 11n-specific stat */
+ ratesetmismatch(ni, wh, reassoc, resp,
+ "HT", rate);
+ return;
+ }
+ ieee80211_ht_node_init(ni, htcap);
+ } else if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
ni->ni_rssi = rssi;
ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
@@ -2748,6 +2889,7 @@
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
+ * [tlv] HT capabilities
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
ni = ic->ic_bss;
@@ -2768,7 +2910,7 @@
associd = le16toh(*(u_int16_t *)frm);
frm += 2;
- rates = xrates = wme = NULL;
+ rates = xrates = wme = htcap = NULL;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
@@ -2778,6 +2920,9 @@
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswmeoui(frm))
wme = frm;
@@ -2939,6 +3084,65 @@
}
break;
}
+
+ case IEEE80211_FC0_SUBTYPE_ACTION: {
+ const struct ieee80211_action *ia;
+
+ if (ic->ic_state != IEEE80211_S_RUN &&
+ ic->ic_state != IEEE80211_S_ASSOC &&
+ ic->ic_state != IEEE80211_S_AUTH) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return);
+ ia = (const struct ieee80211_action *) frm;
+
+ ic->ic_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ }
+
default:
IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
wh, "mgt", "subtype 0x%x not handled", subtype);
==== //depot/projects/wifi/sys/net80211/ieee80211_ioctl.c#77 (text+ko) ====
@@ -461,6 +461,7 @@
cp = copyie(cp, se->se_rsn_ie);
cp = copyie(cp, se->se_wme_ie);
cp = copyie(cp, se->se_ath_ie);
+ cp = copyie(cp, se->se_htcap_ie);
}
req->space -= len;
@@ -1066,6 +1067,45 @@
case IEEE80211_IOC_CURCHAN:
error = ieee80211_ioctl_getcurchan(ic, ireq);
break;
+ case IEEE80211_IOC_SHORTGI:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
+ break;
+ case IEEE80211_IOC_AMPDU:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+ ireq->i_val |= 1;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+ ireq->i_val |= 2;
+ break;
+ case IEEE80211_IOC_AMPDU_LIMIT:
+ ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */
+ break;
+ case IEEE80211_IOC_AMPDU_DENSITY:
+ ireq->i_val = ic->ic_ampdu_density;
+ break;
+ case IEEE80211_IOC_AMSDU:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+ ireq->i_val |= 1;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+ ireq->i_val |= 2;
+ break;
+ case IEEE80211_IOC_AMSDU_LIMIT:
+ ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */
+ break;
+ case IEEE80211_IOC_PUREN:
+ ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+ break;
+ case IEEE80211_IOC_DOTH:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+ break;
+ case IEEE80211_IOC_HTCOMPAT:
+ ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ break;
default:
error = EINVAL;
break;
@@ -1606,6 +1646,9 @@
IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
IEEE80211_CHAN_STURBO, /* IEEE80211_MODE_STURBO_A */
+ /* NB: handled specially below */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
};
u_int modeflags;
int i;
@@ -1627,11 +1670,17 @@
* XXX special-case 11b/g channels so we
* always select the g channel if both
* are present.
+ * XXX prefer HT to non-HT?
*/
if (!IEEE80211_IS_CHAN_B(c) ||
!find11gchannel(ic, i, c->ic_freq))
return c;
} else {
+ /* must check HT specially */
+ if ((mode == IEEE80211_MODE_11NA ||
+ mode == IEEE80211_MODE_11NG) &&
+ !IEEE80211_IS_CHAN_HT(c))
+ continue;
if ((c->ic_flags & modeflags) == modeflags)
return c;
}
@@ -1653,11 +1702,15 @@
case IEEE80211_MODE_11B:
return (IEEE80211_IS_CHAN_B(c));
case IEEE80211_MODE_11G:
- return (IEEE80211_IS_CHAN_ANYG(c));
+ return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
case IEEE80211_MODE_11A:
- return (IEEE80211_IS_CHAN_A(c));
+ return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
case IEEE80211_MODE_STURBO_A:
return (IEEE80211_IS_CHAN_STURBO(c));
+ case IEEE80211_MODE_11NA:
+ return (IEEE80211_IS_CHAN_HTA(c));
+ case IEEE80211_MODE_11NG:
+ return (IEEE80211_IS_CHAN_HTG(c));
}
return 1;
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list