diff options
Diffstat (limited to 'freebsd/sys/net80211/ieee80211_ht.c')
-rw-r--r-- | freebsd/sys/net80211/ieee80211_ht.c | 417 |
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 } |