summaryrefslogtreecommitdiff
path: root/freebsd/sys/net80211/ieee80211_ht.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/net80211/ieee80211_ht.c')
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.c417
1 files changed, 339 insertions, 78 deletions
diff --git a/freebsd/sys/net80211/ieee80211_ht.c b/freebsd/sys/net80211/ieee80211_ht.c
index da1bb26b..69e64a02 100644
--- a/freebsd/sys/net80211/ieee80211_ht.c
+++ b/freebsd/sys/net80211/ieee80211_ht.c
@@ -56,31 +56,86 @@ __FBSDID("$FreeBSD$");
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
#define SM(_v, _f) (((_v) << _f##_S) & _f)
-const struct ieee80211_mcs_rates ieee80211_htrates[16] = {
- { 13, 14, 27, 30 }, /* MCS 0 */
- { 26, 29, 54, 60 }, /* MCS 1 */
- { 39, 43, 81, 90 }, /* MCS 2 */
- { 52, 58, 108, 120 }, /* MCS 3 */
- { 78, 87, 162, 180 }, /* MCS 4 */
- { 104, 116, 216, 240 }, /* MCS 5 */
- { 117, 130, 243, 270 }, /* MCS 6 */
- { 130, 144, 270, 300 }, /* MCS 7 */
- { 26, 29, 54, 60 }, /* MCS 8 */
- { 52, 58, 108, 120 }, /* MCS 9 */
- { 78, 87, 162, 180 }, /* MCS 10 */
- { 104, 116, 216, 240 }, /* MCS 11 */
- { 156, 173, 324, 360 }, /* MCS 12 */
- { 208, 231, 432, 480 }, /* MCS 13 */
- { 234, 260, 486, 540 }, /* MCS 14 */
- { 260, 289, 540, 600 } /* MCS 15 */
+const struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = {
+ { 13, 14, 27, 30 }, /* MCS 0 */
+ { 26, 29, 54, 60 }, /* MCS 1 */
+ { 39, 43, 81, 90 }, /* MCS 2 */
+ { 52, 58, 108, 120 }, /* MCS 3 */
+ { 78, 87, 162, 180 }, /* MCS 4 */
+ { 104, 116, 216, 240 }, /* MCS 5 */
+ { 117, 130, 243, 270 }, /* MCS 6 */
+ { 130, 144, 270, 300 }, /* MCS 7 */
+ { 26, 29, 54, 60 }, /* MCS 8 */
+ { 52, 58, 108, 120 }, /* MCS 9 */
+ { 78, 87, 162, 180 }, /* MCS 10 */
+ { 104, 116, 216, 240 }, /* MCS 11 */
+ { 156, 173, 324, 360 }, /* MCS 12 */
+ { 208, 231, 432, 480 }, /* MCS 13 */
+ { 234, 260, 486, 540 }, /* MCS 14 */
+ { 260, 289, 540, 600 }, /* MCS 15 */
+ { 39, 43, 81, 90 }, /* MCS 16 */
+ { 78, 87, 162, 180 }, /* MCS 17 */
+ { 117, 130, 243, 270 }, /* MCS 18 */
+ { 156, 173, 324, 360 }, /* MCS 19 */
+ { 234, 260, 486, 540 }, /* MCS 20 */
+ { 312, 347, 648, 720 }, /* MCS 21 */
+ { 351, 390, 729, 810 }, /* MCS 22 */
+ { 390, 433, 810, 900 }, /* MCS 23 */
+ { 52, 58, 108, 120 }, /* MCS 24 */
+ { 104, 116, 216, 240 }, /* MCS 25 */
+ { 156, 173, 324, 360 }, /* MCS 26 */
+ { 208, 231, 432, 480 }, /* MCS 27 */
+ { 312, 347, 648, 720 }, /* MCS 28 */
+ { 416, 462, 864, 960 }, /* MCS 29 */
+ { 468, 520, 972, 1080 }, /* MCS 30 */
+ { 520, 578, 1080, 1200 }, /* MCS 31 */
+ { 0, 0, 12, 13 }, /* MCS 32 */
+ { 78, 87, 162, 180 }, /* MCS 33 */
+ { 104, 116, 216, 240 }, /* MCS 34 */
+ { 130, 144, 270, 300 }, /* MCS 35 */
+ { 117, 130, 243, 270 }, /* MCS 36 */
+ { 156, 173, 324, 360 }, /* MCS 37 */
+ { 195, 217, 405, 450 }, /* MCS 38 */
+ { 104, 116, 216, 240 }, /* MCS 39 */
+ { 130, 144, 270, 300 }, /* MCS 40 */
+ { 130, 144, 270, 300 }, /* MCS 41 */
+ { 156, 173, 324, 360 }, /* MCS 42 */
+ { 182, 202, 378, 420 }, /* MCS 43 */
+ { 182, 202, 378, 420 }, /* MCS 44 */
+ { 208, 231, 432, 480 }, /* MCS 45 */
+ { 156, 173, 324, 360 }, /* MCS 46 */
+ { 195, 217, 405, 450 }, /* MCS 47 */
+ { 195, 217, 405, 450 }, /* MCS 48 */
+ { 234, 260, 486, 540 }, /* MCS 49 */
+ { 273, 303, 567, 630 }, /* MCS 50 */
+ { 273, 303, 567, 630 }, /* MCS 51 */
+ { 312, 347, 648, 720 }, /* MCS 52 */
+ { 130, 144, 270, 300 }, /* MCS 53 */
+ { 156, 173, 324, 360 }, /* MCS 54 */
+ { 182, 202, 378, 420 }, /* MCS 55 */
+ { 156, 173, 324, 360 }, /* MCS 56 */
+ { 182, 202, 378, 420 }, /* MCS 57 */
+ { 208, 231, 432, 480 }, /* MCS 58 */
+ { 234, 260, 486, 540 }, /* MCS 59 */
+ { 208, 231, 432, 480 }, /* MCS 60 */
+ { 234, 260, 486, 540 }, /* MCS 61 */
+ { 260, 289, 540, 600 }, /* MCS 62 */
+ { 260, 289, 540, 600 }, /* MCS 63 */
+ { 286, 318, 594, 660 }, /* MCS 64 */
+ { 195, 217, 405, 450 }, /* MCS 65 */
+ { 234, 260, 486, 540 }, /* MCS 66 */
+ { 273, 303, 567, 630 }, /* MCS 67 */
+ { 234, 260, 486, 540 }, /* MCS 68 */
+ { 273, 303, 567, 630 }, /* MCS 69 */
+ { 312, 347, 648, 720 }, /* MCS 70 */
+ { 351, 390, 729, 810 }, /* MCS 71 */
+ { 312, 347, 648, 720 }, /* MCS 72 */
+ { 351, 390, 729, 810 }, /* MCS 73 */
+ { 390, 433, 810, 900 }, /* MCS 74 */
+ { 390, 433, 810, 900 }, /* MCS 75 */
+ { 429, 477, 891, 990 }, /* MCS 76 */
};
-static const struct ieee80211_htrateset ieee80211_rateset_11n =
- { 16, {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15 }
- };
-
#ifdef IEEE80211_AMPDU_AGE
static int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */
SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW,
@@ -164,6 +219,9 @@ static int ieee80211_addba_response(struct ieee80211_node *ni,
int code, int baparamset, int batimeout);
static void ieee80211_addba_stop(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap);
+static void null_addba_response_timeout(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
+
static void ieee80211_bar_response(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap, int status);
static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
@@ -181,6 +239,7 @@ ieee80211_ht_attach(struct ieee80211com *ic)
ic->ic_ampdu_enable = ieee80211_ampdu_enable;
ic->ic_addba_request = ieee80211_addba_request;
ic->ic_addba_response = ieee80211_addba_response;
+ ic->ic_addba_response_timeout = null_addba_response_timeout;
ic->ic_addba_stop = ieee80211_addba_stop;
ic->ic_bar_response = ieee80211_bar_response;
ic->ic_ampdu_rx_start = ampdu_rx_start;
@@ -249,40 +308,150 @@ ieee80211_ht_vdetach(struct ieee80211vap *vap)
{
}
+static int
+ht_getrate(struct ieee80211com *ic, int index, enum ieee80211_phymode mode,
+ int ratetype)
+{
+ int mword, rate;
+
+ mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode);
+ if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
+ return (0);
+ switch (ratetype) {
+ case 0:
+ rate = ieee80211_htrates[index].ht20_rate_800ns;
+ break;
+ case 1:
+ rate = ieee80211_htrates[index].ht20_rate_400ns;
+ break;
+ case 2:
+ rate = ieee80211_htrates[index].ht40_rate_800ns;
+ break;
+ default:
+ rate = ieee80211_htrates[index].ht40_rate_400ns;
+ break;
+ }
+ return (rate);
+}
+
+static struct printranges {
+ int minmcs;
+ int maxmcs;
+ int txstream;
+ int ratetype;
+ int htcapflags;
+} ranges[] = {
+ { 0, 7, 1, 0, 0 },
+ { 8, 15, 2, 0, 0 },
+ { 16, 23, 3, 0, 0 },
+ { 24, 31, 4, 0, 0 },
+ { 32, 0, 1, 2, IEEE80211_HTC_TXMCS32 },
+ { 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL },
+ { 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL },
+ { 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL },
+ { 0, 0, 0, 0, 0 },
+};
+
static void
-ht_announce(struct ieee80211com *ic, int mode,
- const struct ieee80211_htrateset *rs)
+ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype)
{
struct ifnet *ifp = ic->ic_ifp;
- int i, rate, mword;
+ int minrate, maxrate;
+ struct printranges *range;
- if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]);
- for (i = 0; i < rs->rs_nrates; i++) {
- mword = ieee80211_rate2media(ic,
- rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
- if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
+ for (range = ranges; range->txstream != 0; range++) {
+ if (ic->ic_txstream < range->txstream)
+ continue;
+ if (range->htcapflags &&
+ (ic->ic_htcaps & range->htcapflags) == 0)
continue;
- rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns;
- printf("%s%d%sMbps", (i != 0 ? " " : ""),
- rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
+ if (ratetype < range->ratetype)
+ continue;
+ minrate = ht_getrate(ic, range->minmcs, mode, ratetype);
+ maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype);
+ if (range->maxmcs) {
+ if_printf(ifp, "MCS %d-%d: %d%sMbps - %d%sMbps\n",
+ range->minmcs, range->maxmcs,
+ minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""),
+ maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : ""));
+ } else {
+ if_printf(ifp, "MCS %d: %d%sMbps\n", range->minmcs,
+ minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""));
+ }
+ }
+}
+
+static void
+ht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ const char *modestr = ieee80211_phymode_name[mode];
+
+ if_printf(ifp, "%s MCS 20MHz\n", modestr);
+ ht_rateprint(ic, mode, 0);
+ if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) {
+ if_printf(ifp, "%s MCS 20MHz SGI\n", modestr);
+ ht_rateprint(ic, mode, 1);
+ }
+ if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ if_printf(ifp, "%s MCS 40MHz:\n", modestr);
+ ht_rateprint(ic, mode, 2);
+ }
+ if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
+ (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) {
+ if_printf(ifp, "%s MCS 40MHz SGI:\n", modestr);
+ ht_rateprint(ic, mode, 3);
}
- printf("\n");
}
void
ieee80211_ht_announce(struct ieee80211com *ic)
{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
+ isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+ if_printf(ifp, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
- ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
+ ht_announce(ic, IEEE80211_MODE_11NA);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
- ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
+ ht_announce(ic, IEEE80211_MODE_11NG);
}
+static struct ieee80211_htrateset htrateset;
+
const struct ieee80211_htrateset *
ieee80211_get_suphtrates(struct ieee80211com *ic,
- const struct ieee80211_channel *c)
+ const struct ieee80211_channel *c)
{
- return &ieee80211_rateset_11n;
+#define ADDRATE(x) do { \
+ htrateset.rs_rates[htrateset.rs_nrates] = x; \
+ htrateset.rs_nrates++; \
+} while (0)
+ int i;
+
+ memset(&htrateset, 0, sizeof(struct ieee80211_htrateset));
+ for (i = 0; i < ic->ic_txstream * 8; i++)
+ ADDRATE(i);
+ if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
+ (ic->ic_htcaps & IEEE80211_HTC_TXMCS32))
+ ADDRATE(32);
+ if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
+ if (ic->ic_txstream >= 2) {
+ for (i = 33; i <= 38; i++)
+ ADDRATE(i);
+ }
+ if (ic->ic_txstream >= 3) {
+ for (i = 39; i <= 52; i++)
+ ADDRATE(i);
+ }
+ if (ic->ic_txstream == 4) {
+ for (i = 53; i <= 76; i++)
+ ADDRATE(i);
+ }
+ }
+ return &htrateset;
+#undef ADDRATE
}
/*
@@ -398,6 +567,7 @@ ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
static void
ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
{
+
ampdu_rx_purge(rap);
rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND);
}
@@ -658,7 +828,7 @@ again:
if (off < rap->rxa_wnd) {
/*
* Common case (hopefully): in the BA window.
- * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
+ * Sec 9.10.7.6.2 a) (p.137)
*/
#ifdef IEEE80211_AMPDU_AGE
/*
@@ -723,7 +893,7 @@ again:
/*
* 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)
+ * Sec 9.10.7.6.2 b) (p.138)
*/
IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"move BA win <%u:%u> (%u frames) rxseq %u tid %u",
@@ -747,7 +917,7 @@ again:
} else {
/*
* Outside the BA window and out of range; toss.
- * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
+ * Sec 9.10.7.6.2 c) (p.138)
*/
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
@@ -811,7 +981,7 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
if (off < IEEE80211_SEQ_BA_RANGE) {
/*
* Flush the reorder q up to rxseq and move the window.
- * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
+ * Sec 9.10.7.6.3 a) (p.138)
*/
IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
@@ -832,7 +1002,7 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
} else {
/*
* Out of range; toss.
- * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
+ * Sec 9.10.7.6.3 b) (p.138)
*/
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
@@ -1400,7 +1570,7 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
htcap_update_shortgi(ni);
/* NB: honor operating mode constraint */
- /* XXX 40 MHZ intolerant */
+ /* XXX 40 MHz intolerant */
htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
IEEE80211_CHAN_HT20 : 0;
if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
@@ -1419,10 +1589,22 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
int
ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
{
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htcap *htcap;
struct ieee80211_htrateset *rs;
- int i;
+ int i, maxequalmcs, maxunequalmcs;
+
+ maxequalmcs = ic->ic_txstream * 8 - 1;
+ if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
+ if (ic->ic_txstream >= 2)
+ maxunequalmcs = 38;
+ if (ic->ic_txstream >= 3)
+ maxunequalmcs = 52;
+ if (ic->ic_txstream >= 4)
+ maxunequalmcs = 76;
+ } else
+ maxunequalmcs = 0;
rs = &ni->ni_htrates;
memset(rs, 0, sizeof(*rs));
@@ -1441,6 +1623,13 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
vap->iv_stats.is_rx_rstoobig++;
break;
}
+ if (i <= 31 && i > maxequalmcs)
+ continue;
+ if (i == 32 &&
+ (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0)
+ continue;
+ if (i > 32 && i > maxunequalmcs)
+ continue;
rs->rs_rates[rs->rs_nrates++] = i;
}
}
@@ -1509,14 +1698,23 @@ ampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
}
+/*
+ * ADDBA response timeout.
+ *
+ * If software aggregation and per-TID queue management was done here,
+ * that queue would be unpaused after the ADDBA timeout occurs.
+ */
static void
addba_timeout(void *arg)
{
struct ieee80211_tx_ampdu *tap = arg;
+ struct ieee80211_node *ni = tap->txa_ni;
+ struct ieee80211com *ic = ni->ni_ic;
/* XXX ? */
tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
tap->txa_attempts++;
+ ic->ic_addba_response_timeout(ni, tap);
}
static void
@@ -1539,6 +1737,12 @@ addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
}
}
+static void
+null_addba_response_timeout(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap)
+{
+}
+
/*
* Default method for requesting A-MPDU tx aggregation.
* We setup the specified state block and start a timer
@@ -1623,7 +1827,7 @@ ht_recv_action_ba_addba_request(struct ieee80211_node *ni,
struct ieee80211_rx_ampdu *rap;
uint8_t dialogtoken;
uint16_t baparamset, batimeout, baseqctl;
- uint16_t args[4];
+ uint16_t args[5];
int tid;
dialogtoken = frm[2];
@@ -1673,6 +1877,7 @@ ht_recv_action_ba_addba_request(struct ieee80211_node *ni,
| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
;
args[3] = 0;
+ args[4] = 0;
ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
return 0;
@@ -1876,7 +2081,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap)
{
struct ieee80211com *ic = ni->ni_ic;
- uint16_t args[4];
+ uint16_t args[5];
int tid, dialogtoken;
static int tokens = 0; /* XXX */
@@ -1893,13 +2098,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
tap->txa_start = ni->ni_txseqs[tid];
args[0] = dialogtoken;
- args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
+ args[1] = 0; /* NB: status code not used */
+ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
| SM(tid, IEEE80211_BAPS_TID)
| SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
;
- args[2] = 0; /* batimeout */
+ args[3] = 0; /* batimeout */
/* NB: do first so there's no race against reply */
- if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
+ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) {
/* unable to setup state, don't make request */
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
ni, "%s: could not setup BA stream for AC %d",
@@ -1913,7 +2119,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
}
tokens = dialogtoken; /* allocate token */
/* NB: after calling ic_addba_request so driver can set txa_start */
- args[3] = SM(tap->txa_start, IEEE80211_BASEQ_START)
+ args[4] = SM(tap->txa_start, IEEE80211_BASEQ_START)
| SM(0, IEEE80211_BASEQ_FRAG)
;
return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
@@ -2004,7 +2210,7 @@ bar_tx_complete(struct ieee80211_node *ni, void *arg, int status)
callout_pending(&tap->txa_timer)) {
struct ieee80211com *ic = ni->ni_ic;
- if (status) /* ACK'd */
+ if (status == 0) /* ACK'd */
bar_stop_timer(tap);
ic->ic_bar_response(ni, tap, status);
/* NB: just let timer expire so we pace requests */
@@ -2016,7 +2222,7 @@ ieee80211_bar_response(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap, int status)
{
- if (status != 0) { /* got ACK */
+ if (status == 0) { /* got ACK */
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
tap->txa_start,
@@ -2106,11 +2312,15 @@ ieee80211_send_bar(struct ieee80211_node *ni,
ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)",
tid, barctl, seq, tap->txa_attempts);
+ /*
+ * ic_raw_xmit will free the node reference
+ * regardless of queue/TX success or failure.
+ */
ret = ic->ic_raw_xmit(ni, m, NULL);
if (ret != 0) {
/* xmit failed, clear state flag */
tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
- goto bad;
+ return ret;
}
/* XXX hack against tx complete happening before timer is started */
if (tap->txa_flags & IEEE80211_AGGR_BARPEND)
@@ -2159,12 +2369,12 @@ ht_send_action_ba_addba(struct ieee80211_node *ni,
uint8_t *frm;
IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
- "send ADDBA %s: dialogtoken %d "
+ "send ADDBA %s: dialogtoken %d status %d "
"baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
(action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ?
"request" : "response",
- args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
- args[2], args[3]);
+ args[0], args[1], args[2], MS(args[2], IEEE80211_BAPS_TID),
+ args[3], args[4]);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
@@ -2181,10 +2391,12 @@ ht_send_action_ba_addba(struct ieee80211_node *ni,
*frm++ = category;
*frm++ = action;
*frm++ = args[0]; /* dialog token */
- ADDSHORT(frm, args[1]); /* baparamset */
- ADDSHORT(frm, args[2]); /* batimeout */
+ if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE)
+ ADDSHORT(frm, args[1]); /* status code */
+ ADDSHORT(frm, args[2]); /* baparamset */
+ ADDSHORT(frm, args[3]); /* batimeout */
if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST)
- ADDSHORT(frm, args[3]); /* baseqctl */
+ ADDSHORT(frm, args[4]); /* baseqctl */
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
return ht_action_output(ni, m);
} else {
@@ -2278,21 +2490,49 @@ ht_send_action_ht_txchwidth(struct ieee80211_node *ni,
#undef ADDSHORT
/*
- * Construct the MCS bit mask for inclusion
- * in an HT information element.
+ * Construct the MCS bit mask for inclusion in an HT capabilities
+ * information element.
*/
-static void
-ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+static void
+ieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm)
{
int i;
-
- for (i = 0; i < rs->rs_nrates; i++) {
- int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
- if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */
- /* NB: this assumes a particular implementation */
- setbit(frm, r);
+ uint8_t txparams;
+
+ KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4),
+ ("ic_rxstream %d out of range", ic->ic_rxstream));
+ KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4),
+ ("ic_txstream %d out of range", ic->ic_txstream));
+
+ for (i = 0; i < ic->ic_rxstream * 8; i++)
+ setbit(frm, i);
+ if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
+ (ic->ic_htcaps & IEEE80211_HTC_RXMCS32))
+ setbit(frm, 32);
+ if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) {
+ if (ic->ic_rxstream >= 2) {
+ for (i = 33; i <= 38; i++)
+ setbit(frm, i);
+ }
+ if (ic->ic_rxstream >= 3) {
+ for (i = 39; i <= 52; i++)
+ setbit(frm, i);
+ }
+ if (ic->ic_txstream >= 4) {
+ for (i = 53; i <= 76; i++)
+ setbit(frm, i);
}
}
+
+ if (ic->ic_rxstream != ic->ic_txstream) {
+ txparams = 0x1; /* TX MCS set defined */
+ txparams |= 0x2; /* TX RX MCS not equal */
+ txparams |= (ic->ic_txstream - 1) << 2; /* num TX streams */
+ if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL)
+ txparams |= 0x16; /* TX unequal modulation sup */
+ } else
+ txparams = 0;
+ frm[12] = txparams;
}
/*
@@ -2306,8 +2546,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
- uint16_t caps;
+ uint16_t caps, extcaps;
int rxmax, density;
/* HT capabilities */
@@ -2329,6 +2570,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
/* use advertised setting (XXX locally constraint) */
rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+
+ /*
+ * NB: Hardware might support HT40 on some but not all
+ * channels. We can't determine this earlier because only
+ * after association the channel is upgraded to HT based
+ * on the negotiated capabilities.
+ */
+ if (ni->ni_chan != IEEE80211_CHAN_ANYC &&
+ findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40U) == NULL &&
+ findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40D) == NULL)
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
} else {
/* override 20/40 use based on current channel */
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
@@ -2358,15 +2610,24 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
/* supported MCS set */
/*
- * 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.
+ * XXX: For sta mode the rate set should be restricted based
+ * on the AP's capabilities, but ni_htrates isn't setup when
+ * we're called to form an AssocReq frame so for now we're
+ * restricted to the device capabilities.
*/
- ieee80211_set_htrates(frm, &ieee80211_rateset_11n);
+ ieee80211_set_mcsset(ni->ni_ic, frm);
- frm += sizeof(struct ieee80211_ie_htcap) -
+ frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) -
__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
+
+ /* HT extended capabilities */
+ extcaps = vap->iv_htextcaps & 0xffff;
+
+ ADDSHORT(frm, extcaps);
+
+ frm += sizeof(struct ieee80211_ie_htcap) -
+ __offsetof(struct ieee80211_ie_htcap, hc_txbf);
+
return frm;
#undef ADDSHORT
}