ieee80211_ht.c revision 219606
1149871Sscottl/*- 2149871Sscottl * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting 3136849Sscottl * All rights reserved. 4136849Sscottl * 5136849Sscottl * Redistribution and use in source and binary forms, with or without 6136849Sscottl * modification, are permitted provided that the following conditions 7136849Sscottl * are met: 8136849Sscottl * 1. Redistributions of source code must retain the above copyright 9136849Sscottl * notice, this list of conditions and the following disclaimer. 10136849Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11136849Sscottl * notice, this list of conditions and the following disclaimer in the 12136849Sscottl * documentation and/or other materials provided with the distribution. 13136849Sscottl * 14136849Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15136849Sscottl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16136849Sscottl * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17136849Sscottl * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18136849Sscottl * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19136849Sscottl * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20136849Sscottl * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21136849Sscottl * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22136849Sscottl * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23136849Sscottl * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24136849Sscottl */ 25136849Sscottl 26227912Smarius#include <sys/cdefs.h> 27227912Smarius#ifdef __FreeBSD__ 28227912Smarius__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 219606 2011-03-13 13:05:50Z bschmidt $"); 29149871Sscottl#endif 30136849Sscottl 31136849Sscottl/* 32136849Sscottl * IEEE 802.11n protocol support. 33136849Sscottl */ 34136849Sscottl 35136849Sscottl#include "opt_inet.h" 36136849Sscottl#include "opt_wlan.h" 37136849Sscottl 38136849Sscottl#include <sys/param.h> 39136849Sscottl#include <sys/kernel.h> 40136849Sscottl#include <sys/systm.h> 41136849Sscottl#include <sys/endian.h> 42136849Sscottl 43149871Sscottl#include <sys/socket.h> 44149871Sscottl 45269617Sjhb#include <net/if.h> 46149871Sscottl#include <net/if_media.h> 47136849Sscottl#include <net/ethernet.h> 48136849Sscottl 49136849Sscottl#include <net80211/ieee80211_var.h> 50149871Sscottl#include <net80211/ieee80211_action.h> 51149871Sscottl#include <net80211/ieee80211_input.h> 52149871Sscottl 53149871Sscottl/* define here, used throughout file */ 54136849Sscottl#define MS(_v, _f) (((_v) & _f) >> _f##_S) 55136849Sscottl#define SM(_v, _f) (((_v) << _f##_S) & _f) 56136849Sscottl 57143039Sscottlconst struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = { 58136849Sscottl { 13, 14, 27, 30 }, /* MCS 0 */ 59149871Sscottl { 26, 29, 54, 60 }, /* MCS 1 */ 60136849Sscottl { 39, 43, 81, 90 }, /* MCS 2 */ 61136849Sscottl { 52, 58, 108, 120 }, /* MCS 3 */ 62136849Sscottl { 78, 87, 162, 180 }, /* MCS 4 */ 63149871Sscottl { 104, 116, 216, 240 }, /* MCS 5 */ 64136849Sscottl { 117, 130, 243, 270 }, /* MCS 6 */ 65136849Sscottl { 130, 144, 270, 300 }, /* MCS 7 */ 66136849Sscottl { 26, 29, 54, 60 }, /* MCS 8 */ 67136849Sscottl { 52, 58, 108, 120 }, /* MCS 9 */ 68136849Sscottl { 78, 87, 162, 180 }, /* MCS 10 */ 69149871Sscottl { 104, 116, 216, 240 }, /* MCS 11 */ 70136849Sscottl { 156, 173, 324, 360 }, /* MCS 12 */ 71136849Sscottl { 208, 231, 432, 480 }, /* MCS 13 */ 72136849Sscottl { 234, 260, 486, 540 }, /* MCS 14 */ 73149871Sscottl { 260, 289, 540, 600 }, /* MCS 15 */ 74149871Sscottl { 39, 43, 81, 90 }, /* MCS 16 */ 75149871Sscottl { 78, 87, 162, 180 }, /* MCS 17 */ 76149871Sscottl { 117, 130, 243, 270 }, /* MCS 18 */ 77149871Sscottl { 156, 173, 324, 360 }, /* MCS 19 */ 78149871Sscottl { 234, 260, 486, 540 }, /* MCS 20 */ 79149871Sscottl { 312, 347, 648, 720 }, /* MCS 21 */ 80149871Sscottl { 351, 390, 729, 810 }, /* MCS 22 */ 81149871Sscottl { 390, 433, 810, 900 }, /* MCS 23 */ 82149871Sscottl { 52, 58, 108, 120 }, /* MCS 24 */ 83149871Sscottl { 104, 116, 216, 240 }, /* MCS 25 */ 84149871Sscottl { 156, 173, 324, 360 }, /* MCS 26 */ 85149871Sscottl { 208, 231, 432, 480 }, /* MCS 27 */ 86149871Sscottl { 312, 347, 648, 720 }, /* MCS 28 */ 87149871Sscottl { 416, 462, 864, 960 }, /* MCS 29 */ 88149871Sscottl { 468, 520, 972, 1080 }, /* MCS 30 */ 89190863Sdelphij { 520, 578, 1080, 1200 }, /* MCS 31 */ 90227912Smarius { 0, 0, 12, 13 }, /* MCS 32 */ 91149871Sscottl { 78, 87, 162, 180 }, /* MCS 33 */ 92149871Sscottl { 104, 116, 216, 240 }, /* MCS 34 */ 93149871Sscottl { 130, 144, 270, 300 }, /* MCS 35 */ 94149871Sscottl { 117, 130, 243, 270 }, /* MCS 36 */ 95149871Sscottl { 156, 173, 324, 360 }, /* MCS 37 */ 96149871Sscottl { 195, 217, 405, 450 }, /* MCS 38 */ 97149871Sscottl { 104, 116, 216, 240 }, /* MCS 39 */ 98149871Sscottl { 130, 144, 270, 300 }, /* MCS 40 */ 99149871Sscottl { 130, 144, 270, 300 }, /* MCS 41 */ 100149871Sscottl { 156, 173, 324, 360 }, /* MCS 42 */ 101149871Sscottl { 182, 202, 378, 420 }, /* MCS 43 */ 102149871Sscottl { 182, 202, 378, 420 }, /* MCS 44 */ 103236379Seadler { 208, 231, 432, 480 }, /* MCS 45 */ 104149871Sscottl { 156, 173, 324, 360 }, /* MCS 46 */ 105149871Sscottl { 195, 217, 405, 450 }, /* MCS 47 */ 106149871Sscottl { 195, 217, 405, 450 }, /* MCS 48 */ 107149871Sscottl { 234, 260, 486, 540 }, /* MCS 49 */ 108149871Sscottl { 273, 303, 567, 630 }, /* MCS 50 */ 109149871Sscottl { 273, 303, 567, 630 }, /* MCS 51 */ 110149871Sscottl { 312, 347, 648, 720 }, /* MCS 52 */ 111149871Sscottl { 130, 144, 270, 300 }, /* MCS 53 */ 112149871Sscottl { 156, 173, 324, 360 }, /* MCS 54 */ 113149871Sscottl { 182, 202, 378, 420 }, /* MCS 55 */ 114269617Sjhb { 156, 173, 324, 360 }, /* MCS 56 */ 115136849Sscottl { 182, 202, 378, 420 }, /* MCS 57 */ 116136849Sscottl { 208, 231, 432, 480 }, /* MCS 58 */ 117136849Sscottl { 234, 260, 486, 540 }, /* MCS 59 */ 118136849Sscottl { 208, 231, 432, 480 }, /* MCS 60 */ 119136849Sscottl { 234, 260, 486, 540 }, /* MCS 61 */ 120136849Sscottl { 260, 289, 540, 600 }, /* MCS 62 */ 121136849Sscottl { 260, 289, 540, 600 }, /* MCS 63 */ 122136849Sscottl { 286, 318, 594, 660 }, /* MCS 64 */ 123136849Sscottl { 195, 217, 405, 450 }, /* MCS 65 */ 124136849Sscottl { 234, 260, 486, 540 }, /* MCS 66 */ 125136849Sscottl { 273, 303, 567, 630 }, /* MCS 67 */ 126136849Sscottl { 234, 260, 486, 540 }, /* MCS 68 */ 127136849Sscottl { 273, 303, 567, 630 }, /* MCS 69 */ 128136849Sscottl { 312, 347, 648, 720 }, /* MCS 70 */ 129136849Sscottl { 351, 390, 729, 810 }, /* MCS 71 */ 130136849Sscottl { 312, 347, 648, 720 }, /* MCS 72 */ 131136849Sscottl { 351, 390, 729, 810 }, /* MCS 73 */ 132136849Sscottl { 390, 433, 810, 900 }, /* MCS 74 */ 133136849Sscottl { 390, 433, 810, 900 }, /* MCS 75 */ 134136849Sscottl { 429, 477, 891, 990 }, /* MCS 76 */ 135136849Sscottl}; 136136849Sscottl 137136849Sscottl#ifdef IEEE80211_AMPDU_AGE 138136849Sscottlstatic int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ 139269617SjhbSYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW, 140269617Sjhb &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", 141136849Sscottl "AMPDU max reorder age (ms)"); 142136849Sscottl#endif 143149871Sscottl 144136849Sscottlstatic int ieee80211_recv_bar_ena = 1; 145136849SscottlSYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, 146136849Sscottl 0, "BAR frame processing (ena/dis)"); 147136849Sscottl 148136849Sscottlstatic int ieee80211_addba_timeout = -1;/* timeout for ADDBA response */ 149136849SscottlSYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLTYPE_INT | CTLFLAG_RW, 150136849Sscottl &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", 151136849Sscottl "ADDBA request timeout (ms)"); 152136849Sscottlstatic int ieee80211_addba_backoff = -1;/* backoff after max ADDBA requests */ 153136849SscottlSYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLTYPE_INT | CTLFLAG_RW, 154136849Sscottl &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", 155136849Sscottl "ADDBA request backoff (ms)"); 156136849Sscottlstatic int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */ 157269617SjhbSYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW, 158269617Sjhb &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); 159136849Sscottl 160190809Sdelphijstatic int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */ 161136849Sscottlstatic int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */ 162136849Sscottl 163136849Sscottlstatic ieee80211_recv_action_func ht_recv_action_ba_addba_request; 164136849Sscottlstatic ieee80211_recv_action_func ht_recv_action_ba_addba_response; 165136849Sscottlstatic ieee80211_recv_action_func ht_recv_action_ba_delba; 166136849Sscottlstatic ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave; 167298955Spfgstatic ieee80211_recv_action_func ht_recv_action_ht_txchwidth; 168136849Sscottl 169136849Sscottlstatic ieee80211_send_action_func ht_send_action_ba_addba; 170136849Sscottlstatic ieee80211_send_action_func ht_send_action_ba_delba; 171136849Sscottlstatic ieee80211_send_action_func ht_send_action_ht_txchwidth; 172136849Sscottl 173136849Sscottlstatic void 174136849Sscottlieee80211_ht_init(void) 175136849Sscottl{ 176136849Sscottl /* 177149871Sscottl * Setup HT parameters that depends on the clock frequency. 178136849Sscottl */ 179149871Sscottl#ifdef IEEE80211_AMPDU_AGE 180149871Sscottl ieee80211_ampdu_age = msecs_to_ticks(500); 181149871Sscottl#endif 182149871Sscottl ieee80211_addba_timeout = msecs_to_ticks(250); 183149871Sscottl ieee80211_addba_backoff = msecs_to_ticks(10*1000); 184149871Sscottl ieee80211_bar_timeout = msecs_to_ticks(250); 185149871Sscottl /* 186149871Sscottl * Register action frame handlers. 187149871Sscottl */ 188149871Sscottl ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 189149871Sscottl IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request); 190149871Sscottl ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 191149871Sscottl IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response); 192149871Sscottl ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 193149871Sscottl IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba); 194149871Sscottl ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, 195149871Sscottl IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave); 196136849Sscottl ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, 197149871Sscottl IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth); 198136849Sscottl 199136849Sscottl ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 200136849Sscottl IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba); 201136849Sscottl ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 202136849Sscottl IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba); 203136849Sscottl ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 204136849Sscottl IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba); 205136849Sscottl ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT, 206136849Sscottl IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth); 207136849Sscottl} 208136849SscottlSYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL); 209136849Sscottl 210136849Sscottlstatic int ieee80211_ampdu_enable(struct ieee80211_node *ni, 211136849Sscottl struct ieee80211_tx_ampdu *tap); 212136849Sscottlstatic int ieee80211_addba_request(struct ieee80211_node *ni, 213136849Sscottl struct ieee80211_tx_ampdu *tap, 214136849Sscottl int dialogtoken, int baparamset, int batimeout); 215136849Sscottlstatic int ieee80211_addba_response(struct ieee80211_node *ni, 216149871Sscottl struct ieee80211_tx_ampdu *tap, 217149871Sscottl int code, int baparamset, int batimeout); 218149871Sscottlstatic void ieee80211_addba_stop(struct ieee80211_node *ni, 219136849Sscottl struct ieee80211_tx_ampdu *tap); 220136849Sscottlstatic void ieee80211_bar_response(struct ieee80211_node *ni, 221136849Sscottl struct ieee80211_tx_ampdu *tap, int status); 222136849Sscottlstatic void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap); 223149871Sscottlstatic void bar_stop_timer(struct ieee80211_tx_ampdu *tap); 224149871Sscottlstatic int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *, 225149871Sscottl int baparamset, int batimeout, int baseqctl); 226149871Sscottlstatic void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *); 227149871Sscottl 228149871Sscottlvoid 229149871Sscottlieee80211_ht_attach(struct ieee80211com *ic) 230149871Sscottl{ 231136849Sscottl /* setup default aggregation policy */ 232136849Sscottl ic->ic_recv_action = ieee80211_recv_action; 233136849Sscottl ic->ic_send_action = ieee80211_send_action; 234136849Sscottl ic->ic_ampdu_enable = ieee80211_ampdu_enable; 235136849Sscottl ic->ic_addba_request = ieee80211_addba_request; 236136849Sscottl ic->ic_addba_response = ieee80211_addba_response; 237149871Sscottl ic->ic_addba_stop = ieee80211_addba_stop; 238149871Sscottl ic->ic_bar_response = ieee80211_bar_response; 239149871Sscottl ic->ic_ampdu_rx_start = ampdu_rx_start; 240136849Sscottl ic->ic_ampdu_rx_stop = ampdu_rx_stop; 241136849Sscottl 242136849Sscottl ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; 243136849Sscottl ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; 244136849Sscottl} 245136849Sscottl 246136849Sscottlvoid 247136849Sscottlieee80211_ht_detach(struct ieee80211com *ic) 248136849Sscottl{ 249136849Sscottl} 250136849Sscottl 251136849Sscottlvoid 252136849Sscottlieee80211_ht_vattach(struct ieee80211vap *vap) 253136849Sscottl{ 254190809Sdelphij 255136849Sscottl /* driver can override defaults */ 256136849Sscottl vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; 257136849Sscottl vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; 258136849Sscottl vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; 259149871Sscottl vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU; 260136849Sscottl /* tx aggregation traffic thresholds */ 261136849Sscottl vap->iv_ampdu_mintraffic[WME_AC_BK] = 128; 262136849Sscottl vap->iv_ampdu_mintraffic[WME_AC_BE] = 64; 263136849Sscottl vap->iv_ampdu_mintraffic[WME_AC_VO] = 32; 264136849Sscottl vap->iv_ampdu_mintraffic[WME_AC_VI] = 32; 265149871Sscottl 266149871Sscottl if (vap->iv_htcaps & IEEE80211_HTC_HT) { 267149871Sscottl /* 268136849Sscottl * Device is HT capable; enable all HT-related 269136849Sscottl * facilities by default. 270149871Sscottl * XXX these choices may be too aggressive. 271136849Sscottl */ 272136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_HT 273136849Sscottl | IEEE80211_FHT_HTCOMPAT 274136849Sscottl ; 275190809Sdelphij if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20) 276149871Sscottl vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20; 277149871Sscottl /* XXX infer from channel list? */ 278136849Sscottl if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) { 279136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_USEHT40; 280136849Sscottl if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40) 281136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40; 282190809Sdelphij } 283149871Sscottl /* enable RIFS if capable */ 284149871Sscottl if (vap->iv_htcaps & IEEE80211_HTC_RIFS) 285149871Sscottl vap->iv_flags_ht |= IEEE80211_FHT_RIFS; 286149871Sscottl 287149871Sscottl /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ 288136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX; 289136849Sscottl if (vap->iv_htcaps & IEEE80211_HTC_AMPDU) 290136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX; 291136849Sscottl vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX; 292190809Sdelphij if (vap->iv_htcaps & IEEE80211_HTC_AMSDU) 293149871Sscottl vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX; 294149871Sscottl } 295136849Sscottl /* NB: disable default legacy WDS, too many issues right now */ 296136849Sscottl if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) 297136849Sscottl vap->iv_flags_ht &= ~IEEE80211_FHT_HT; 298136849Sscottl} 299190809Sdelphij 300149871Sscottlvoid 301149871Sscottlieee80211_ht_vdetach(struct ieee80211vap *vap) 302136849Sscottl{ 303136849Sscottl} 304136849Sscottl 305136849Sscottlstatic int 306136849Sscottlht_getrate(struct ieee80211com *ic, int index, int mode, int ratetype) 307136849Sscottl{ 308136849Sscottl int mword, rate; 309136849Sscottl 310149871Sscottl mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode); 311190809Sdelphij if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) 312298955Spfg return (0); 313149871Sscottl switch (ratetype) { 314136849Sscottl case 0: 315136849Sscottl rate = ieee80211_htrates[index].ht20_rate_800ns; 316136849Sscottl break; 317136849Sscottl case 1: 318136849Sscottl rate = ieee80211_htrates[index].ht20_rate_400ns; 319136849Sscottl break; 320136849Sscottl case 2: 321136849Sscottl rate = ieee80211_htrates[index].ht40_rate_800ns; 322136849Sscottl break; 323136849Sscottl default: 324136849Sscottl rate = ieee80211_htrates[index].ht40_rate_400ns; 325149871Sscottl break; 326149871Sscottl } 327136849Sscottl return (rate); 328136849Sscottl} 329149871Sscottl 330149871Sscottlstatic struct printranges { 331149871Sscottl int minmcs; 332136849Sscottl int maxmcs; 333136849Sscottl int txstream; 334136849Sscottl int ratetype; 335149871Sscottl int htcapflags; 336149871Sscottl} ranges[] = { 337136849Sscottl { 0, 7, 1, 0, 0 }, 338136849Sscottl { 8, 15, 2, 0, 0 }, 339149871Sscottl { 16, 23, 3, 0, 0 }, 340149871Sscottl { 24, 31, 4, 0, 0 }, 341149871Sscottl { 32, 0, 1, 2, IEEE80211_HTC_TXMCS32 }, 342136849Sscottl { 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL }, 343136849Sscottl { 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL }, 344136849Sscottl { 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL }, 345149871Sscottl { 0, 0, 0, 0, 0 }, 346149871Sscottl}; 347149871Sscottl 348136849Sscottlstatic void 349136849Sscottlht_rateprint(struct ieee80211com *ic, int mode, int ratetype) 350136849Sscottl{ 351136849Sscottl struct ifnet *ifp = ic->ic_ifp; 352136849Sscottl int minrate, maxrate; 353136849Sscottl struct printranges *range; 354149871Sscottl 355149871Sscottl for (range = ranges; range->txstream != 0; range++) { 356149871Sscottl if (ic->ic_txstream < range->txstream) 357136849Sscottl continue; 358136849Sscottl if (range->htcapflags && 359136849Sscottl (ic->ic_htcaps & range->htcapflags) == 0) 360136849Sscottl continue; 361149871Sscottl if (ratetype < range->ratetype) 362149871Sscottl continue; 363136849Sscottl minrate = ht_getrate(ic, range->minmcs, mode, ratetype); 364136849Sscottl maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype); 365149871Sscottl if (range->maxmcs) { 366149871Sscottl if_printf(ifp, "MCS %d-%d: %d%sMbps - %d%sMbps\n", 367149871Sscottl range->minmcs, range->maxmcs, 368136849Sscottl minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""), 369136849Sscottl maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : "")); 370136849Sscottl } else { 371136849Sscottl if_printf(ifp, "MCS %d: %d%sMbps\n", range->minmcs, 372136849Sscottl minrate/2, ((minrate & 0x1) != 0 ? ".5" : "")); 373136849Sscottl } 374136849Sscottl } 375136849Sscottl} 376136849Sscottl 377149871Sscottlstatic void 378149871Sscottlht_announce(struct ieee80211com *ic, int mode) 379149871Sscottl{ 380136849Sscottl struct ifnet *ifp = ic->ic_ifp; 381136849Sscottl const char *modestr = ieee80211_phymode_name[mode]; 382136849Sscottl 383136849Sscottl if_printf(ifp, "%s MCS 20MHz\n", modestr); 384136849Sscottl ht_rateprint(ic, mode, 0); 385136849Sscottl if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) { 386136849Sscottl if_printf(ifp, "%s MCS 20MHz SGI\n", modestr); 387136849Sscottl ht_rateprint(ic, mode, 1); 388136849Sscottl } 389136849Sscottl if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { 390149871Sscottl if_printf(ifp, "%s MCS 40MHz:\n", modestr); 391136849Sscottl ht_rateprint(ic, mode, 2); 392136849Sscottl } 393136849Sscottl if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && 394149871Sscottl (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) { 395149871Sscottl if_printf(ifp, "%s MCS 40MHz SGI:\n", modestr); 396149871Sscottl ht_rateprint(ic, mode, 3); 397149871Sscottl } 398136849Sscottl} 399136849Sscottl 400149871Sscottlvoid 401136849Sscottlieee80211_ht_announce(struct ieee80211com *ic) 402136849Sscottl{ 403149871Sscottl struct ifnet *ifp = ic->ic_ifp; 404136849Sscottl 405136849Sscottl if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 406136849Sscottl isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) 407149871Sscottl if_printf(ifp, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream); 408136849Sscottl if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) 409136849Sscottl ht_announce(ic, IEEE80211_MODE_11NA); 410136849Sscottl if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) 411149871Sscottl ht_announce(ic, IEEE80211_MODE_11NG); 412136849Sscottl} 413136849Sscottl 414149871Sscottlstatic struct ieee80211_htrateset htrateset; 415149871Sscottl 416149871Sscottlconst struct ieee80211_htrateset * 417149871Sscottlieee80211_get_suphtrates(struct ieee80211com *ic, 418136849Sscottl const struct ieee80211_channel *c) 419149871Sscottl{ 420149871Sscottl#define ADDRATE(x) do { \ 421136849Sscottl htrateset.rs_rates[htrateset.rs_nrates] = x; \ 422136849Sscottl htrateset.rs_nrates++; \ 423136849Sscottl} while (0) 424149871Sscottl int i; 425149871Sscottl 426136849Sscottl memset(&htrateset, 0, sizeof(struct ieee80211_htrateset)); 427149871Sscottl for (i = 0; i < ic->ic_txstream * 8; i++) 428136849Sscottl ADDRATE(i); 429136849Sscottl if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && 430136849Sscottl (ic->ic_htcaps & IEEE80211_HTC_TXMCS32)) 431136849Sscottl ADDRATE(i); 432136849Sscottl if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { 433149871Sscottl if (ic->ic_txstream >= 2) { 434149871Sscottl for (i = 33; i <= 38; i++) 435136849Sscottl ADDRATE(i); 436149871Sscottl } 437149871Sscottl if (ic->ic_txstream >= 3) { 438136849Sscottl for (i = 39; i <= 52; i++) 439136849Sscottl ADDRATE(i); 440136849Sscottl } 441136849Sscottl if (ic->ic_txstream == 4) { 442136849Sscottl for (i = 53; i <= 76; i++) 443136849Sscottl ADDRATE(i); 444136849Sscottl } 445136849Sscottl } 446136849Sscottl return &htrateset; 447136849Sscottl#undef ADDRATE 448136849Sscottl} 449136849Sscottl 450136849Sscottl/* 451136849Sscottl * Receive processing. 452136849Sscottl */ 453149871Sscottl 454149871Sscottl/* 455149871Sscottl * Decap the encapsulated A-MSDU frames and dispatch all but 456136849Sscottl * the last for delivery. The last frame is returned for 457149871Sscottl * delivery via the normal path. 458136849Sscottl */ 459136849Sscottlstruct mbuf * 460136849Sscottlieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) 461136849Sscottl{ 462136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 463136849Sscottl int framelen; 464149871Sscottl struct mbuf *n; 465149871Sscottl 466149871Sscottl /* discard 802.3 header inserted by ieee80211_decap */ 467149871Sscottl m_adj(m, sizeof(struct ether_header)); 468136849Sscottl 469190809Sdelphij vap->iv_stats.is_amsdu_decap++; 470149871Sscottl 471136849Sscottl for (;;) { 472136849Sscottl /* 473136849Sscottl * Decap the first frame, bust it apart from the 474149871Sscottl * remainder and deliver. We leave the last frame 475149871Sscottl * delivery to the caller (for consistency with other 476190809Sdelphij * code paths, could also do it here). 477149871Sscottl */ 478136849Sscottl m = ieee80211_decap1(m, &framelen); 479136849Sscottl if (m == NULL) { 480136849Sscottl IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 481136849Sscottl ni->ni_macaddr, "a-msdu", "%s", "decap failed"); 482149871Sscottl vap->iv_stats.is_amsdu_tooshort++; 483149871Sscottl return NULL; 484149871Sscottl } 485149871Sscottl if (m->m_pkthdr.len == framelen) 486136849Sscottl break; 487149871Sscottl n = m_split(m, framelen, M_NOWAIT); 488149871Sscottl if (n == NULL) { 489190809Sdelphij IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 490149871Sscottl ni->ni_macaddr, "a-msdu", 491136849Sscottl "%s", "unable to split encapsulated frames"); 492136849Sscottl vap->iv_stats.is_amsdu_split++; 493136849Sscottl m_freem(m); /* NB: must reclaim */ 494136849Sscottl return NULL; 495136849Sscottl } 496149871Sscottl vap->iv_deliver_data(vap, ni, m); 497149871Sscottl 498190809Sdelphij /* 499149871Sscottl * Remove frame contents; each intermediate frame 500136849Sscottl * is required to be aligned to a 4-byte boundary. 501136849Sscottl */ 502149871Sscottl m = n; 503149871Sscottl m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ 504190809Sdelphij } 505149871Sscottl return m; /* last delivered by caller */ 506136849Sscottl} 507136849Sscottl 508136849Sscottl/* 509136849Sscottl * Purge all frames in the A-MPDU re-order queue. 510136849Sscottl */ 511149871Sscottlstatic void 512149871Sscottlampdu_rx_purge(struct ieee80211_rx_ampdu *rap) 513190809Sdelphij{ 514149871Sscottl struct mbuf *m; 515136849Sscottl int i; 516149871Sscottl 517149871Sscottl for (i = 0; i < rap->rxa_wnd; i++) { 518149871Sscottl m = rap->rxa_m[i]; 519190809Sdelphij if (m != NULL) { 520149871Sscottl rap->rxa_m[i] = NULL; 521136849Sscottl rap->rxa_qbytes -= m->m_pkthdr.len; 522136849Sscottl m_freem(m); 523136849Sscottl if (--rap->rxa_qframes == 0) 524149871Sscottl break; 525136849Sscottl } 526149871Sscottl } 527149871Sscottl KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 528149871Sscottl ("lost %u data, %u frames on ampdu rx q", 529149871Sscottl rap->rxa_qbytes, rap->rxa_qframes)); 530149871Sscottl} 531149871Sscottl 532149871Sscottl/* 533149871Sscottl * Start A-MPDU rx/re-order processing for the specified TID. 534190809Sdelphij */ 535149871Sscottlstatic int 536136849Sscottlampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, 537136849Sscottl int baparamset, int batimeout, int baseqctl) 538136849Sscottl{ 539190809Sdelphij int bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 540149871Sscottl 541149871Sscottl if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { 542149871Sscottl /* 543149871Sscottl * AMPDU previously setup and not terminated with a DELBA, 544190809Sdelphij * flush the reorder q's in case anything remains. 545149871Sscottl */ 546136849Sscottl ampdu_rx_purge(rap); 547149871Sscottl } 548149871Sscottl memset(rap, 0, sizeof(*rap)); 549149871Sscottl rap->rxa_wnd = (bufsiz == 0) ? 550149871Sscottl IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 551190809Sdelphij rap->rxa_start = MS(baseqctl, IEEE80211_BASEQ_START); 552149871Sscottl rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; 553149871Sscottl 554149871Sscottl return 0; 555149871Sscottl} 556149871Sscottl 557190809Sdelphij/* 558149871Sscottl * Stop A-MPDU rx processing for the specified TID. 559149871Sscottl */ 560149871Sscottlstatic void 561136849Sscottlampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) 562190809Sdelphij{ 563149871Sscottl 564136849Sscottl ampdu_rx_purge(rap); 565136849Sscottl rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND); 566136849Sscottl} 567136849Sscottl 568190809Sdelphij/* 569149871Sscottl * Dispatch a frame from the A-MPDU reorder queue. The 570136849Sscottl * frame is fed back into ieee80211_input marked with an 571149871Sscottl * M_AMPDU_MPDU flag so it doesn't come back to us (it also 572149871Sscottl * permits ieee80211_input to optimize re-processing). 573149871Sscottl */ 574149871Sscottlstatic __inline void 575190809Sdelphijampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) 576149871Sscottl{ 577136849Sscottl m->m_flags |= M_AMPDU_MPDU; /* bypass normal processing */ 578136849Sscottl /* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */ 579136849Sscottl (void) ieee80211_input(ni, m, 0, 0); 580149871Sscottl} 581149871Sscottl 582136849Sscottl/* 583149871Sscottl * Dispatch as many frames as possible from the re-order queue. 584149871Sscottl * Frames will always be "at the front"; we process all frames 585149871Sscottl * up to the first empty slot in the window. On completion we 586136849Sscottl * cleanup state if there are still pending frames in the current 587149871Sscottl * BA window. We assume the frame at slot 0 is already handled 588149871Sscottl * by the caller; we always start at slot 1. 589149871Sscottl */ 590149871Sscottlstatic void 591136849Sscottlampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) 592136849Sscottl{ 593136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 594190809Sdelphij struct mbuf *m; 595149871Sscottl int i; 596136849Sscottl 597136849Sscottl /* flush run of frames */ 598149871Sscottl for (i = 1; i < rap->rxa_wnd; i++) { 599149871Sscottl m = rap->rxa_m[i]; 600149871Sscottl if (m == NULL) 601190809Sdelphij break; 602149871Sscottl rap->rxa_m[i] = NULL; 603136849Sscottl rap->rxa_qbytes -= m->m_pkthdr.len; 604136849Sscottl rap->rxa_qframes--; 605136849Sscottl 606136849Sscottl ampdu_dispatch(ni, m); 607149871Sscottl } 608149871Sscottl /* 609136849Sscottl * If frames remain, copy the mbuf pointers down so 610149871Sscottl * they correspond to the offsets in the new window. 611149871Sscottl */ 612149871Sscottl if (rap->rxa_qframes != 0) { 613136849Sscottl int n = rap->rxa_qframes, j; 614149871Sscottl for (j = i+1; j < rap->rxa_wnd; j++) { 615149871Sscottl if (rap->rxa_m[j] != NULL) { 616149871Sscottl rap->rxa_m[j-i] = rap->rxa_m[j]; 617136849Sscottl rap->rxa_m[j] = NULL; 618149871Sscottl if (--n == 0) 619149871Sscottl break; 620149871Sscottl } 621136849Sscottl } 622149871Sscottl KASSERT(n == 0, ("lost %d frames", n)); 623149871Sscottl vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; 624149871Sscottl } 625136849Sscottl /* 626136849Sscottl * Adjust the start of the BA window to 627136849Sscottl * reflect the frames just dispatched. 628190809Sdelphij */ 629149871Sscottl rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); 630136849Sscottl vap->iv_stats.is_ampdu_rx_oor += i; 631136849Sscottl} 632149871Sscottl 633149871Sscottl#ifdef IEEE80211_AMPDU_AGE 634149871Sscottl/* 635149871Sscottl * Dispatch all frames in the A-MPDU re-order queue. 636190809Sdelphij */ 637149871Sscottlstatic void 638136849Sscottlampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) 639149871Sscottl{ 640136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 641136849Sscottl struct mbuf *m; 642149871Sscottl int i; 643149871Sscottl 644149871Sscottl for (i = 0; i < rap->rxa_wnd; i++) { 645149871Sscottl m = rap->rxa_m[i]; 646149871Sscottl if (m == NULL) 647149871Sscottl continue; 648149871Sscottl rap->rxa_m[i] = NULL; 649149871Sscottl rap->rxa_qbytes -= m->m_pkthdr.len; 650149871Sscottl rap->rxa_qframes--; 651149871Sscottl vap->iv_stats.is_ampdu_rx_oor++; 652149871Sscottl 653149871Sscottl ampdu_dispatch(ni, m); 654149871Sscottl if (rap->rxa_qframes == 0) 655149871Sscottl break; 656149871Sscottl } 657149871Sscottl} 658149871Sscottl#endif /* IEEE80211_AMPDU_AGE */ 659149871Sscottl 660149871Sscottl/* 661149871Sscottl * Dispatch all frames in the A-MPDU re-order queue 662149871Sscottl * preceding the specified sequence number. This logic 663149871Sscottl * handles window moves due to a received MSDU or BAR. 664149871Sscottl */ 665149871Sscottlstatic void 666149871Sscottlampdu_rx_flush_upto(struct ieee80211_node *ni, 667149871Sscottl struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) 668149871Sscottl{ 669149871Sscottl struct ieee80211vap *vap = ni->ni_vap; 670136849Sscottl struct mbuf *m; 671149871Sscottl ieee80211_seq seqno; 672136849Sscottl int i; 673149871Sscottl 674136849Sscottl /* 675149871Sscottl * Flush any complete MSDU's with a sequence number lower 676149871Sscottl * than winstart. Gaps may exist. Note that we may actually 677149871Sscottl * dispatch frames past winstart if a run continues; this is 678149871Sscottl * an optimization that avoids having to do a separate pass 679149871Sscottl * to dispatch frames after moving the BA window start. 680149871Sscottl */ 681149871Sscottl seqno = rap->rxa_start; 682149871Sscottl for (i = 0; i < rap->rxa_wnd; i++) { 683190809Sdelphij m = rap->rxa_m[i]; 684149871Sscottl if (m != NULL) { 685136849Sscottl rap->rxa_m[i] = NULL; 686136849Sscottl rap->rxa_qbytes -= m->m_pkthdr.len; 687136849Sscottl rap->rxa_qframes--; 688190809Sdelphij vap->iv_stats.is_ampdu_rx_oor++; 689149871Sscottl 690136849Sscottl ampdu_dispatch(ni, m); 691149871Sscottl } else { 692149871Sscottl if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) 693190809Sdelphij break; 694149871Sscottl } 695149871Sscottl seqno = IEEE80211_SEQ_INC(seqno); 696149871Sscottl } 697149871Sscottl /* 698149871Sscottl * If frames remain, copy the mbuf pointers down so 699149871Sscottl * they correspond to the offsets in the new window. 700190809Sdelphij */ 701149871Sscottl if (rap->rxa_qframes != 0) { 702149871Sscottl int n = rap->rxa_qframes, j; 703149871Sscottl 704149871Sscottl /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */ 705149871Sscottl KASSERT(rap->rxa_m[0] == NULL, 706190809Sdelphij ("%s: BA window slot 0 occupied", __func__)); 707149871Sscottl for (j = i+1; j < rap->rxa_wnd; j++) { 708149871Sscottl if (rap->rxa_m[j] != NULL) { 709149871Sscottl rap->rxa_m[j-i] = rap->rxa_m[j]; 710136849Sscottl rap->rxa_m[j] = NULL; 711190809Sdelphij if (--n == 0) 712149871Sscottl break; 713149871Sscottl } 714136849Sscottl } 715136849Sscottl KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d " 716136849Sscottl "BA win <%d:%d> winstart %d", 717149871Sscottl __func__, n, rap->rxa_qframes, i, rap->rxa_start, 718190809Sdelphij IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 719149871Sscottl winstart)); 720149871Sscottl vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; 721149871Sscottl } 722149871Sscottl /* 723149871Sscottl * Move the start of the BA window; we use the 724190809Sdelphij * sequence number of the last MSDU that was 725149871Sscottl * passed up the stack+1 or winstart if stopped on 726136849Sscottl * a gap in the reorder buffer. 727136849Sscottl */ 728136849Sscottl rap->rxa_start = seqno; 729136849Sscottl} 730149871Sscottl 731149871Sscottl/* 732190809Sdelphij * Process a received QoS data frame for an HT station. Handle 733149871Sscottl * A-MPDU reordering: if this frame is received out of order 734136849Sscottl * and falls within the BA window hold onto it. Otherwise if 735136849Sscottl * this frame completes a run, flush any pending frames. We 736190809Sdelphij * return 1 if the frame is consumed. A 0 is returned if 737149871Sscottl * the frame should be processed normally by the caller. 738136849Sscottl */ 739136849Sscottlint 740136849Sscottlieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) 741136849Sscottl{ 742136849Sscottl#define IEEE80211_FC0_QOSDATA \ 743136849Sscottl (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) 744136849Sscottl#define PROCESS 0 /* caller should process frame */ 745136849Sscottl#define CONSUMED 1 /* frame consumed, caller does nothing */ 746136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 747136849Sscottl struct ieee80211_qosframe *wh; 748149871Sscottl struct ieee80211_rx_ampdu *rap; 749149871Sscottl ieee80211_seq rxseq; 750149871Sscottl uint8_t tid; 751269617Sjhb int off; 752269617Sjhb 753149871Sscottl KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU, 754149871Sscottl ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags)); 755149871Sscottl KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); 756149871Sscottl 757149871Sscottl /* NB: m_len known to be sufficient */ 758149871Sscottl wh = mtod(m, struct ieee80211_qosframe *); 759149871Sscottl if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) { 760149871Sscottl /* 761149871Sscottl * Not QoS data, shouldn't get here but just 762190809Sdelphij * return it to the caller for processing. 763149871Sscottl */ 764149871Sscottl return PROCESS; 765149871Sscottl } 766149871Sscottl if (IEEE80211_IS_DSTODS(wh)) 767190809Sdelphij tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0]; 768149871Sscottl else 769149871Sscottl tid = wh->i_qos[0]; 770149871Sscottl tid &= IEEE80211_QOS_TID; 771149871Sscottl rap = &ni->ni_rx_ampdu[tid]; 772149871Sscottl if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 773149871Sscottl /* 774149871Sscottl * No ADDBA request yet, don't touch. 775149871Sscottl */ 776190809Sdelphij return PROCESS; 777149871Sscottl } 778149871Sscottl rxseq = le16toh(*(uint16_t *)wh->i_seq); 779149871Sscottl if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) { 780149871Sscottl /* 781149871Sscottl * Fragments are not allowed; toss. 782149871Sscottl */ 783149871Sscottl IEEE80211_DISCARD_MAC(vap, 784149871Sscottl IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, 785149871Sscottl "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid, 786149871Sscottl wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); 787136849Sscottl vap->iv_stats.is_ampdu_rx_drop++; 788149871Sscottl IEEE80211_NODE_STAT(ni, rx_drop); 789149871Sscottl m_freem(m); 790149871Sscottl return CONSUMED; 791149871Sscottl } 792190809Sdelphij rxseq >>= IEEE80211_SEQ_SEQ_SHIFT; 793149871Sscottl rap->rxa_nframes++; 794149871Sscottlagain: 795149871Sscottl if (rxseq == rap->rxa_start) { 796149871Sscottl /* 797149871Sscottl * First frame in window. 798149871Sscottl */ 799149871Sscottl if (rap->rxa_qframes != 0) { 800149871Sscottl /* 801149871Sscottl * Dispatch as many packets as we can. 802149871Sscottl */ 803149871Sscottl KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); 804190809Sdelphij ampdu_dispatch(ni, m); 805149871Sscottl ampdu_rx_dispatch(rap, ni); 806149871Sscottl return CONSUMED; 807149871Sscottl } else { 808136849Sscottl /* 809149871Sscottl * In order; advance window and notify 810149871Sscottl * caller to dispatch directly. 811190809Sdelphij */ 812149871Sscottl rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 813149871Sscottl return PROCESS; 814149871Sscottl } 815136849Sscottl } 816136849Sscottl /* 817149871Sscottl * Frame is out of order; store if in the BA window. 818149871Sscottl */ 819136849Sscottl /* calculate offset in BA window */ 820149871Sscottl off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 821149871Sscottl if (off < rap->rxa_wnd) { 822136849Sscottl /* 823136849Sscottl * Common case (hopefully): in the BA window. 824149871Sscottl * Sec 9.10.7.6.2 a) (p.137) 825136849Sscottl */ 826136849Sscottl#ifdef IEEE80211_AMPDU_AGE 827136849Sscottl /* 828136849Sscottl * Check for frames sitting too long in the reorder queue. 829136849Sscottl * This should only ever happen if frames are not delivered 830136849Sscottl * without the sender otherwise notifying us (e.g. with a 831136849Sscottl * BAR to move the window). Typically this happens because 832136849Sscottl * of vendor bugs that cause the sequence number to jump. 833149871Sscottl * When this happens we get a gap in the reorder queue that 834136849Sscottl * leaves frame sitting on the queue until they get pushed 835136849Sscottl * out due to window moves. When the vendor does not send 836136849Sscottl * BAR this move only happens due to explicit packet sends 837136849Sscottl * 838136849Sscottl * NB: we only track the time of the oldest frame in the 839149871Sscottl * reorder q; this means that if we flush we might push 840136849Sscottl * frames that still "new"; if this happens then subsequent 841136849Sscottl * frames will result in BA window moves which cost something 842136849Sscottl * but is still better than a big throughput dip. 843136849Sscottl */ 844149871Sscottl if (rap->rxa_qframes != 0) { 845136849Sscottl /* XXX honor batimeout? */ 846149871Sscottl if (ticks - rap->rxa_age > ieee80211_ampdu_age) { 847136849Sscottl /* 848149871Sscottl * Too long since we received the first 849136849Sscottl * frame; flush the reorder buffer. 850149871Sscottl */ 851149871Sscottl if (rap->rxa_qframes != 0) { 852149871Sscottl vap->iv_stats.is_ampdu_rx_age += 853136849Sscottl rap->rxa_qframes; 854149871Sscottl ampdu_rx_flush(ni, rap); 855149871Sscottl } 856149871Sscottl rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 857190809Sdelphij return PROCESS; 858149871Sscottl } 859149871Sscottl } else { 860269617Sjhb /* 861149871Sscottl * First frame, start aging timer. 862149871Sscottl */ 863149871Sscottl rap->rxa_age = ticks; 864149871Sscottl } 865190809Sdelphij#endif /* IEEE80211_AMPDU_AGE */ 866149871Sscottl /* save packet */ 867149871Sscottl if (rap->rxa_m[off] == NULL) { 868149871Sscottl rap->rxa_m[off] = m; 869269617Sjhb rap->rxa_qframes++; 870149871Sscottl rap->rxa_qbytes += m->m_pkthdr.len; 871149871Sscottl vap->iv_stats.is_ampdu_rx_reorder++; 872149871Sscottl } else { 873149871Sscottl IEEE80211_DISCARD_MAC(vap, 874149871Sscottl IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 875136849Sscottl ni->ni_macaddr, "a-mpdu duplicate", 876250460Seadler "seqno %u tid %u BA win <%u:%u>", 877250460Seadler rxseq, tid, rap->rxa_start, 878149871Sscottl IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); 879149871Sscottl vap->iv_stats.is_rx_dup++; 880149871Sscottl IEEE80211_NODE_STAT(ni, rx_dup); 881149871Sscottl m_freem(m); 882149871Sscottl } 883149871Sscottl return CONSUMED; 884190809Sdelphij } 885149871Sscottl if (off < IEEE80211_SEQ_BA_RANGE) { 886149871Sscottl /* 887149871Sscottl * Outside the BA window, but within range; 888190809Sdelphij * flush the reorder q and move the window. 889149871Sscottl * Sec 9.10.7.6.2 b) (p.138) 890149871Sscottl */ 891136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, 892136849Sscottl "move BA win <%u:%u> (%u frames) rxseq %u tid %u", 893136849Sscottl rap->rxa_start, 894136849Sscottl IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 895149871Sscottl rap->rxa_qframes, rxseq, tid); 896136849Sscottl vap->iv_stats.is_ampdu_rx_move++; 897136849Sscottl 898149871Sscottl /* 899149871Sscottl * The spec says to flush frames up to but not including: 900149871Sscottl * WinStart_B = rxseq - rap->rxa_wnd + 1 901149871Sscottl * Then insert the frame or notify the caller to process 902190809Sdelphij * it immediately. We can safely do this by just starting 903149871Sscottl * over again because we know the frame will now be within 904136849Sscottl * the BA window. 905136849Sscottl */ 906149871Sscottl /* NB: rxa_wnd known to be >0 */ 907149871Sscottl ampdu_rx_flush_upto(ni, rap, 908136849Sscottl IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); 909149871Sscottl goto again; 910149871Sscottl } else { 911149871Sscottl /* 912136849Sscottl * Outside the BA window and out of range; toss. 913149871Sscottl * Sec 9.10.7.6.2 c) (p.138) 914136849Sscottl */ 915149871Sscottl IEEE80211_DISCARD_MAC(vap, 916149871Sscottl IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, 917149871Sscottl "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", 918190809Sdelphij rap->rxa_start, 919149871Sscottl IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 920149871Sscottl rap->rxa_qframes, rxseq, tid, 921136849Sscottl wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); 922136849Sscottl vap->iv_stats.is_ampdu_rx_drop++; 923136849Sscottl IEEE80211_NODE_STAT(ni, rx_drop); 924149871Sscottl m_freem(m); 925149871Sscottl return CONSUMED; 926149871Sscottl } 927149871Sscottl#undef CONSUMED 928190809Sdelphij#undef PROCESS 929149871Sscottl#undef IEEE80211_FC0_QOSDATA 930149871Sscottl} 931136849Sscottl 932136849Sscottl/* 933149871Sscottl * Process a BAR ctl frame. Dispatch all frames up to 934149871Sscottl * the sequence number of the frame. If this frame is 935136849Sscottl * out of range it's discarded. 936149871Sscottl */ 937149871Sscottlvoid 938149871Sscottlieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) 939149871Sscottl{ 940149871Sscottl struct ieee80211vap *vap = ni->ni_vap; 941136849Sscottl struct ieee80211_frame_bar *wh; 942149871Sscottl struct ieee80211_rx_ampdu *rap; 943149871Sscottl ieee80211_seq rxseq; 944149871Sscottl int tid, off; 945250460Seadler 946149871Sscottl if (!ieee80211_recv_bar_ena) { 947149871Sscottl#if 0 948149871Sscottl IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N, 949136849Sscottl ni->ni_macaddr, "BAR", "%s", "processing disabled"); 950136849Sscottl#endif 951136849Sscottl vap->iv_stats.is_ampdu_bar_bad++; 952136849Sscottl return; 953136849Sscottl } 954136849Sscottl wh = mtod(m0, struct ieee80211_frame_bar *); 955136849Sscottl /* XXX check basic BAR */ 956136849Sscottl tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); 957149871Sscottl rap = &ni->ni_rx_ampdu[tid]; 958149871Sscottl if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 959136849Sscottl /* 960136849Sscottl * No ADDBA request yet, don't touch. 961136849Sscottl */ 962136849Sscottl IEEE80211_DISCARD_MAC(vap, 963136849Sscottl IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 964136849Sscottl ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); 965136849Sscottl vap->iv_stats.is_ampdu_bar_bad++; 966149871Sscottl return; 967136849Sscottl } 968136849Sscottl vap->iv_stats.is_ampdu_bar_rx++; 969136849Sscottl rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 970136849Sscottl if (rxseq == rap->rxa_start) 971136849Sscottl return; 972136849Sscottl /* calculate offset in BA window */ 973136849Sscottl off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 974136849Sscottl if (off < IEEE80211_SEQ_BA_RANGE) { 975136849Sscottl /* 976136849Sscottl * Flush the reorder q up to rxseq and move the window. 977136849Sscottl * Sec 9.10.7.6.3 a) (p.138) 978136849Sscottl */ 979136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, 980136849Sscottl "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", 981136849Sscottl rap->rxa_start, 982136849Sscottl IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 983136849Sscottl rap->rxa_qframes, rxseq, tid); 984136849Sscottl vap->iv_stats.is_ampdu_bar_move++; 985136849Sscottl 986136849Sscottl ampdu_rx_flush_upto(ni, rap, rxseq); 987136849Sscottl if (off >= rap->rxa_wnd) { 988136849Sscottl /* 989136849Sscottl * BAR specifies a window start to the right of BA 990149871Sscottl * window; we must move it explicitly since 991136849Sscottl * ampdu_rx_flush_upto will not. 992136849Sscottl */ 993136849Sscottl rap->rxa_start = rxseq; 994136849Sscottl } 995136849Sscottl } else { 996136849Sscottl /* 997136849Sscottl * Out of range; toss. 998149871Sscottl * Sec 9.10.7.6.3 b) (p.138) 999136849Sscottl */ 1000136849Sscottl IEEE80211_DISCARD_MAC(vap, 1001136849Sscottl IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, 1002149871Sscottl "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", 1003149871Sscottl rap->rxa_start, 1004136849Sscottl IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 1005149871Sscottl rap->rxa_qframes, rxseq, tid, 1006149871Sscottl wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); 1007136849Sscottl vap->iv_stats.is_ampdu_bar_oow++; 1008136849Sscottl IEEE80211_NODE_STAT(ni, rx_drop); 1009136849Sscottl } 1010136849Sscottl} 1011136849Sscottl 1012136849Sscottl/* 1013149871Sscottl * Setup HT-specific state in a node. Called only 1014149871Sscottl * when HT use is negotiated so we don't do extra 1015149871Sscottl * work for temporary and/or legacy sta's. 1016136849Sscottl */ 1017136849Sscottlvoid 1018136849Sscottlieee80211_ht_node_init(struct ieee80211_node *ni) 1019136849Sscottl{ 1020136849Sscottl struct ieee80211_tx_ampdu *tap; 1021136849Sscottl int ac; 1022136849Sscottl 1023136849Sscottl if (ni->ni_flags & IEEE80211_NODE_HT) { 1024136849Sscottl /* 1025136849Sscottl * Clean AMPDU state on re-associate. This handles the case 1026136849Sscottl * where a station leaves w/o notifying us and then returns 1027149871Sscottl * before node is reaped for inactivity. 1028190809Sdelphij */ 1029190809Sdelphij ieee80211_ht_node_cleanup(ni); 1030136849Sscottl } 1031136849Sscottl for (ac = 0; ac < WME_NUM_AC; ac++) { 1032136849Sscottl tap = &ni->ni_tx_ampdu[ac]; 1033149871Sscottl tap->txa_ac = ac; 1034149871Sscottl tap->txa_ni = ni; 1035136849Sscottl /* NB: further initialization deferred */ 1036136849Sscottl } 1037136849Sscottl ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; 1038149871Sscottl} 1039136849Sscottl 1040136849Sscottl/* 1041149871Sscottl * Cleanup HT-specific state in a node. Called only 1042149871Sscottl * when HT use has been marked. 1043149871Sscottl */ 1044149871Sscottlvoid 1045149871Sscottlieee80211_ht_node_cleanup(struct ieee80211_node *ni) 1046149871Sscottl{ 1047136849Sscottl struct ieee80211com *ic = ni->ni_ic; 1048136849Sscottl int i; 1049136849Sscottl 1050136849Sscottl KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); 1051136849Sscottl 1052136849Sscottl /* XXX optimize this */ 1053136849Sscottl for (i = 0; i < WME_NUM_AC; i++) { 1054136849Sscottl struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; 1055136849Sscottl if (tap->txa_flags & IEEE80211_AGGR_SETUP) 1056136849Sscottl ampdu_tx_stop(tap); 1057136849Sscottl } 1058136849Sscottl for (i = 0; i < WME_NUM_TID; i++) 1059136849Sscottl ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]); 1060136849Sscottl 1061136849Sscottl ni->ni_htcap = 0; 1062136849Sscottl ni->ni_flags &= ~IEEE80211_NODE_HT_ALL; 1063149871Sscottl} 1064149871Sscottl 1065149871Sscottl/* 1066136849Sscottl * Age out HT resources for a station. 1067136849Sscottl */ 1068136849Sscottlvoid 1069136849Sscottlieee80211_ht_node_age(struct ieee80211_node *ni) 1070136849Sscottl{ 1071136849Sscottl#ifdef IEEE80211_AMPDU_AGE 1072136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 1073136849Sscottl uint8_t tid; 1074136849Sscottl#endif 1075136849Sscottl 1076136849Sscottl KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); 1077136849Sscottl 1078136849Sscottl#ifdef IEEE80211_AMPDU_AGE 1079136849Sscottl for (tid = 0; tid < WME_NUM_TID; tid++) { 1080136849Sscottl struct ieee80211_rx_ampdu *rap; 1081136849Sscottl 1082136849Sscottl rap = &ni->ni_rx_ampdu[tid]; 1083136849Sscottl if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) 1084149871Sscottl continue; 1085149871Sscottl if (rap->rxa_qframes == 0) 1086149871Sscottl continue; 1087136849Sscottl /* 1088136849Sscottl * Check for frames sitting too long in the reorder queue. 1089136849Sscottl * See above for more details on what's happening here. 1090136849Sscottl */ 1091136849Sscottl /* XXX honor batimeout? */ 1092136849Sscottl if (ticks - rap->rxa_age > ieee80211_ampdu_age) { 1093136849Sscottl /* 1094190809Sdelphij * Too long since we received the first 1095190809Sdelphij * frame; flush the reorder buffer. 1096190809Sdelphij */ 1097190809Sdelphij vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; 1098190809Sdelphij ampdu_rx_flush(ni, rap); 1099190809Sdelphij } 1100190809Sdelphij } 1101190809Sdelphij#endif /* IEEE80211_AMPDU_AGE */ 1102190809Sdelphij} 1103190809Sdelphij 1104190809Sdelphijstatic struct ieee80211_channel * 1105190809Sdelphijfindhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) 1106190809Sdelphij{ 1107190809Sdelphij return ieee80211_find_channel(ic, c->ic_freq, 1108190809Sdelphij (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags); 1109190809Sdelphij} 1110190809Sdelphij 1111190809Sdelphij/* 1112190809Sdelphij * Adjust a channel to be HT/non-HT according to the vap's configuration. 1113190809Sdelphij */ 1114190809Sdelphijstruct ieee80211_channel * 1115190809Sdelphijieee80211_ht_adjust_channel(struct ieee80211com *ic, 1116190809Sdelphij struct ieee80211_channel *chan, int flags) 1117190809Sdelphij{ 1118190809Sdelphij struct ieee80211_channel *c; 1119190809Sdelphij 1120190809Sdelphij if (flags & IEEE80211_FHT_HT) { 1121190809Sdelphij /* promote to HT if possible */ 1122190809Sdelphij if (flags & IEEE80211_FHT_USEHT40) { 1123190809Sdelphij if (!IEEE80211_IS_CHAN_HT40(chan)) { 1124190809Sdelphij /* NB: arbitrarily pick ht40+ over ht40- */ 1125190809Sdelphij c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); 1126190809Sdelphij if (c == NULL) 1127190809Sdelphij c = findhtchan(ic, chan, 1128190809Sdelphij IEEE80211_CHAN_HT40D); 1129190809Sdelphij if (c == NULL) 1130190809Sdelphij c = findhtchan(ic, chan, 1131190809Sdelphij IEEE80211_CHAN_HT20); 1132190809Sdelphij if (c != NULL) 1133190809Sdelphij chan = c; 1134190809Sdelphij } 1135190809Sdelphij } else if (!IEEE80211_IS_CHAN_HT20(chan)) { 1136190809Sdelphij c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); 1137190809Sdelphij if (c != NULL) 1138190809Sdelphij chan = c; 1139190809Sdelphij } 1140190809Sdelphij } else if (IEEE80211_IS_CHAN_HT(chan)) { 1141190809Sdelphij /* demote to legacy, HT use is disabled */ 1142190809Sdelphij c = ieee80211_find_channel(ic, chan->ic_freq, 1143190809Sdelphij chan->ic_flags &~ IEEE80211_CHAN_HT); 1144190809Sdelphij if (c != NULL) 1145190809Sdelphij chan = c; 1146190809Sdelphij } 1147190809Sdelphij return chan; 1148190809Sdelphij} 1149190809Sdelphij 1150190809Sdelphij/* 1151190809Sdelphij * Setup HT-specific state for a legacy WDS peer. 1152190809Sdelphij */ 1153190809Sdelphijvoid 1154190809Sdelphijieee80211_ht_wds_init(struct ieee80211_node *ni) 1155190809Sdelphij{ 1156190809Sdelphij struct ieee80211vap *vap = ni->ni_vap; 1157190809Sdelphij struct ieee80211_tx_ampdu *tap; 1158190809Sdelphij int ac; 1159190809Sdelphij 1160190809Sdelphij KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested")); 1161190809Sdelphij 1162190809Sdelphij /* XXX check scan cache in case peer has an ap and we have info */ 1163190809Sdelphij /* 1164190809Sdelphij * If setup with a legacy channel; locate an HT channel. 1165190809Sdelphij * Otherwise if the inherited channel (from a companion 1166190809Sdelphij * AP) is suitable use it so we use the same location 1167190809Sdelphij * for the extension channel). 1168190809Sdelphij */ 1169190809Sdelphij ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic, 1170190809Sdelphij ni->ni_chan, ieee80211_htchanflags(ni->ni_chan)); 1171190809Sdelphij 1172190809Sdelphij ni->ni_htcap = 0; 1173190809Sdelphij if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) 1174190809Sdelphij ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; 1175190809Sdelphij if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { 1176190809Sdelphij ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; 1177190809Sdelphij ni->ni_chw = 40; 1178190809Sdelphij if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) 1179190809Sdelphij ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; 1180190809Sdelphij else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) 1181190809Sdelphij ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; 1182190809Sdelphij if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) 1183190809Sdelphij ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; 1184190809Sdelphij } else { 1185190809Sdelphij ni->ni_chw = 20; 1186190809Sdelphij ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; 1187190809Sdelphij } 1188190809Sdelphij ni->ni_htctlchan = ni->ni_chan->ic_ieee; 1189190809Sdelphij if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) 1190190809Sdelphij ni->ni_flags |= IEEE80211_NODE_RIFS; 1191190809Sdelphij /* XXX does it make sense to enable SMPS? */ 1192190809Sdelphij 1193190809Sdelphij ni->ni_htopmode = 0; /* XXX need protection state */ 1194190809Sdelphij ni->ni_htstbc = 0; /* XXX need info */ 1195190809Sdelphij 1196190809Sdelphij for (ac = 0; ac < WME_NUM_AC; ac++) { 1197190809Sdelphij tap = &ni->ni_tx_ampdu[ac]; 1198190809Sdelphij tap->txa_ac = ac; 1199190809Sdelphij } 1200190809Sdelphij /* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */ 1201190809Sdelphij ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; 1202190809Sdelphij} 1203190809Sdelphij 1204190809Sdelphij/* 1205190809Sdelphij * Notify hostap vaps of a change in the HTINFO ie. 1206190809Sdelphij */ 1207190809Sdelphijstatic void 1208190809Sdelphijhtinfo_notify(struct ieee80211com *ic) 1209190809Sdelphij{ 1210190809Sdelphij struct ieee80211vap *vap; 1211190809Sdelphij int first = 1; 1212190809Sdelphij 1213190809Sdelphij IEEE80211_LOCK_ASSERT(ic); 1214190809Sdelphij 1215136849Sscottl TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1216136849Sscottl if (vap->iv_opmode != IEEE80211_M_HOSTAP) 1217149871Sscottl continue; 1218136849Sscottl if (vap->iv_state != IEEE80211_S_RUN || 1219136849Sscottl !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan)) 1220136849Sscottl continue; 1221136849Sscottl if (first) { 1222136849Sscottl IEEE80211_NOTE(vap, 1223136849Sscottl IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, 1224149871Sscottl vap->iv_bss, 1225136849Sscottl "HT bss occupancy change: %d sta, %d ht, " 1226136849Sscottl "%d ht40%s, HT protmode now 0x%x" 1227136849Sscottl , ic->ic_sta_assoc 1228136849Sscottl , ic->ic_ht_sta_assoc 1229136849Sscottl , ic->ic_ht40_sta_assoc 1230136849Sscottl , (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) ? 1231136849Sscottl ", non-HT sta present" : "" 1232149871Sscottl , ic->ic_curhtprotmode); 1233149871Sscottl first = 0; 1234149871Sscottl } 1235149871Sscottl ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO); 1236149871Sscottl } 1237149871Sscottl} 1238149871Sscottl 1239149871Sscottl/* 1240149871Sscottl * Calculate HT protection mode from current 1241149871Sscottl * state and handle updates. 1242149871Sscottl */ 1243149871Sscottlstatic void 1244149871Sscottlhtinfo_update(struct ieee80211com *ic) 1245149871Sscottl{ 1246149871Sscottl uint8_t protmode; 1247149871Sscottl 1248136849Sscottl if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { 1249136849Sscottl protmode = IEEE80211_HTINFO_OPMODE_MIXED 1250136849Sscottl | IEEE80211_HTINFO_NONHT_PRESENT; 1251136849Sscottl } else if (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) { 1252136849Sscottl protmode = IEEE80211_HTINFO_OPMODE_PROTOPT 1253136849Sscottl | IEEE80211_HTINFO_NONHT_PRESENT; 1254149871Sscottl } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 1255136849Sscottl IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && 1256136849Sscottl ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { 1257136849Sscottl protmode = IEEE80211_HTINFO_OPMODE_HT20PR; 1258269617Sjhb } else { 1259269617Sjhb protmode = IEEE80211_HTINFO_OPMODE_PURE; 1260269617Sjhb } 1261190809Sdelphij if (protmode != ic->ic_curhtprotmode) { 1262269617Sjhb ic->ic_curhtprotmode = protmode; 1263136849Sscottl htinfo_notify(ic); 1264136849Sscottl } 1265149871Sscottl} 1266136849Sscottl 1267136849Sscottl/* 1268149871Sscottl * Handle an HT station joining a BSS. 1269149871Sscottl */ 1270136849Sscottlvoid 1271136849Sscottlieee80211_ht_node_join(struct ieee80211_node *ni) 1272136849Sscottl{ 1273269617Sjhb struct ieee80211com *ic = ni->ni_ic; 1274136849Sscottl 1275136849Sscottl IEEE80211_LOCK_ASSERT(ic); 1276136849Sscottl 1277136849Sscottl if (ni->ni_flags & IEEE80211_NODE_HT) { 1278136849Sscottl ic->ic_ht_sta_assoc++; 1279136849Sscottl if (ni->ni_chw == 40) 1280136849Sscottl ic->ic_ht40_sta_assoc++; 1281232854Sscottl } 1282149871Sscottl htinfo_update(ic); 1283149871Sscottl} 1284149871Sscottl 1285149871Sscottl/* 1286149871Sscottl * Handle an HT station leaving a BSS. 1287149871Sscottl */ 1288149871Sscottlvoid 1289149871Sscottlieee80211_ht_node_leave(struct ieee80211_node *ni) 1290149871Sscottl{ 1291149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1292269617Sjhb 1293149871Sscottl IEEE80211_LOCK_ASSERT(ic); 1294149871Sscottl 1295201758Smbr if (ni->ni_flags & IEEE80211_NODE_HT) { 1296136849Sscottl ic->ic_ht_sta_assoc--; 1297136849Sscottl if (ni->ni_chw == 40) 1298136849Sscottl ic->ic_ht40_sta_assoc--; 1299149871Sscottl } 1300149871Sscottl htinfo_update(ic); 1301190809Sdelphij} 1302149871Sscottl 1303136849Sscottl/* 1304136849Sscottl * Public version of htinfo_update; used for processing 1305136849Sscottl * beacon frames from overlapping bss. 1306136849Sscottl * 1307296135Sjhibbits * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED 1308296135Sjhibbits * (on receipt of a beacon that advertises MIXED) or 1309149871Sscottl * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon 1310149871Sscottl * from an overlapping legacy bss). We treat MIXED with 1311149871Sscottl * a higher precedence than PROTOPT (i.e. we will not change 1312190809Sdelphij * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This 1313149871Sscottl * corresponds to how we handle things in htinfo_update. 1314149871Sscottl */ 1315136849Sscottlvoid 1316149871Sscottlieee80211_htprot_update(struct ieee80211com *ic, int protmode) 1317149871Sscottl{ 1318190809Sdelphij#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE) 1319149871Sscottl IEEE80211_LOCK(ic); 1320136849Sscottl 1321136849Sscottl /* track non-HT station presence */ 1322136849Sscottl KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT, 1323149871Sscottl ("protmode 0x%x", protmode)); 1324136849Sscottl ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR; 1325149871Sscottl ic->ic_lastnonht = ticks; 1326190809Sdelphij 1327136849Sscottl if (protmode != ic->ic_curhtprotmode && 1328136849Sscottl (OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED || 1329136849Sscottl OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) { 1330136849Sscottl /* push beacon update */ 1331136849Sscottl ic->ic_curhtprotmode = protmode; 1332136849Sscottl htinfo_notify(ic); 1333136849Sscottl } 1334136849Sscottl IEEE80211_UNLOCK(ic); 1335136849Sscottl#undef OPMODE 1336149871Sscottl} 1337149871Sscottl 1338190809Sdelphij/* 1339149871Sscottl * Time out presence of an overlapping bss with non-HT 1340149871Sscottl * stations. When operating in hostap mode we listen for 1341149871Sscottl * beacons from other stations and if we identify a non-HT 1342149871Sscottl * station is present we update the opmode field of the 1343149871Sscottl * HTINFO ie. To identify when all non-HT stations are 1344136849Sscottl * gone we time out this condition. 1345136849Sscottl */ 1346136849Sscottlvoid 1347136849Sscottlieee80211_ht_timeout(struct ieee80211com *ic) 1348136849Sscottl{ 1349136849Sscottl IEEE80211_LOCK_ASSERT(ic); 1350136849Sscottl 1351136849Sscottl if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) && 1352136849Sscottl time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { 1353136849Sscottl#if 0 1354149871Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, 1355149871Sscottl "%s", "time out non-HT STA present on channel"); 1356149871Sscottl#endif 1357149871Sscottl ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR; 1358149871Sscottl htinfo_update(ic); 1359149871Sscottl } 1360149871Sscottl} 1361136849Sscottl 1362149871Sscottl/* unalligned little endian access */ 1363149871Sscottl#define LE_READ_2(p) \ 1364149871Sscottl ((uint16_t) \ 1365136849Sscottl ((((const uint8_t *)(p))[0] ) | \ 1366149871Sscottl (((const uint8_t *)(p))[1] << 8))) 1367149871Sscottl 1368149871Sscottl/* 1369149871Sscottl * Process an 802.11n HT capabilities ie. 1370149871Sscottl */ 1371149871Sscottlvoid 1372136849Sscottlieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) 1373136849Sscottl{ 1374149871Sscottl if (ie[0] == IEEE80211_ELEMID_VENDOR) { 1375149871Sscottl /* 1376149871Sscottl * Station used Vendor OUI ie to associate; 1377149871Sscottl * mark the node so when we respond we'll use 1378149871Sscottl * the Vendor OUI's and not the standard ie's. 1379149871Sscottl */ 1380149871Sscottl ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; 1381149871Sscottl ie += 4; 1382149871Sscottl } else 1383149871Sscottl ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; 1384149871Sscottl 1385149871Sscottl ni->ni_htcap = LE_READ_2(ie + 1386149871Sscottl __offsetof(struct ieee80211_ie_htcap, hc_cap)); 1387269617Sjhb ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; 1388149871Sscottl} 1389136849Sscottl 1390136849Sscottlstatic void 1391136849Sscottlhtinfo_parse(struct ieee80211_node *ni, 1392149871Sscottl const struct ieee80211_ie_htinfo *htinfo) 1393149871Sscottl{ 1394149871Sscottl uint16_t w; 1395149871Sscottl 1396136862Sscottl ni->ni_htctlchan = htinfo->hi_ctrlchannel; 1397136849Sscottl ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); 1398136849Sscottl w = LE_READ_2(&htinfo->hi_byte2); 1399136849Sscottl ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); 1400136849Sscottl w = LE_READ_2(&htinfo->hi_byte45); 1401149871Sscottl ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); 1402149871Sscottl} 1403149871Sscottl 1404149871Sscottl/* 1405149871Sscottl * Parse an 802.11n HT info ie and save useful information 1406149871Sscottl * to the node state. Note this does not effect any state 1407149871Sscottl * changes such as for channel width change. 1408149871Sscottl */ 1409149871Sscottlvoid 1410136849Sscottlieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) 1411136849Sscottl{ 1412136849Sscottl if (ie[0] == IEEE80211_ELEMID_VENDOR) 1413136849Sscottl ie += 4; 1414136849Sscottl htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie); 1415149871Sscottl} 1416149871Sscottl 1417136849Sscottl/* 1418136849Sscottl * Handle 11n channel switch. Use the received HT ie's to 1419149871Sscottl * identify the right channel to use. If we cannot locate it 1420149871Sscottl * in the channel table then fallback to legacy operation. 1421190809Sdelphij * Note that we use this information to identify the node's 1422149871Sscottl * channel only; the caller is responsible for insuring any 1423136849Sscottl * required channel change is done (e.g. in sta mode when 1424149871Sscottl * parsing the contents of a beacon frame). 1425149871Sscottl */ 1426149871Sscottlstatic void 1427149871Sscottlhtinfo_update_chw(struct ieee80211_node *ni, int htflags) 1428190809Sdelphij{ 1429149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1430149871Sscottl struct ieee80211_channel *c; 1431149871Sscottl int chanflags; 1432149871Sscottl 1433149871Sscottl chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; 1434149871Sscottl if (chanflags != ni->ni_chan->ic_flags) { 1435149871Sscottl /* XXX not right for ht40- */ 1436190809Sdelphij c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); 1437149871Sscottl if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) { 1438149871Sscottl /* 1439149871Sscottl * No HT40 channel entry in our table; fall back 1440149871Sscottl * to HT20 operation. This should not happen. 1441149871Sscottl */ 1442149871Sscottl c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); 1443149871Sscottl#if 0 1444149871Sscottl IEEE80211_NOTE(ni->ni_vap, 1445149871Sscottl IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, 1446136849Sscottl "no HT40 channel (freq %u), falling back to HT20", 1447136849Sscottl ni->ni_chan->ic_freq); 1448136849Sscottl#endif 1449136849Sscottl /* XXX stat */ 1450136849Sscottl } 1451136849Sscottl if (c != NULL && c != ni->ni_chan) { 1452136849Sscottl IEEE80211_NOTE(ni->ni_vap, 1453136849Sscottl IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, 1454136849Sscottl "switch station to HT%d channel %u/0x%x", 1455136849Sscottl IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, 1456136849Sscottl c->ic_freq, c->ic_flags); 1457136849Sscottl ni->ni_chan = c; 1458136849Sscottl } 1459136849Sscottl /* NB: caller responsible for forcing any channel change */ 1460136849Sscottl } 1461149871Sscottl /* update node's tx channel width */ 1462136849Sscottl ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20; 1463136849Sscottl} 1464136849Sscottl 1465136849Sscottl/* 1466136849Sscottl * Update 11n MIMO PS state according to received htcap. 1467136849Sscottl */ 1468136849Sscottlstatic __inline int 1469136849Sscottlhtcap_update_mimo_ps(struct ieee80211_node *ni) 1470136849Sscottl{ 1471136849Sscottl uint16_t oflags = ni->ni_flags; 1472136849Sscottl 1473136849Sscottl switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { 1474136849Sscottl case IEEE80211_HTCAP_SMPS_DYNAMIC: 1475136849Sscottl ni->ni_flags |= IEEE80211_NODE_MIMO_PS; 1476136849Sscottl ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; 1477136849Sscottl break; 1478136849Sscottl case IEEE80211_HTCAP_SMPS_ENA: 1479136849Sscottl ni->ni_flags |= IEEE80211_NODE_MIMO_PS; 1480136849Sscottl ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; 1481136849Sscottl break; 1482190809Sdelphij case IEEE80211_HTCAP_SMPS_OFF: 1483136849Sscottl default: /* disable on rx of reserved value */ 1484136849Sscottl ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; 1485136849Sscottl ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; 1486136849Sscottl break; 1487136849Sscottl } 1488136849Sscottl return (oflags ^ ni->ni_flags); 1489136849Sscottl} 1490136849Sscottl 1491136849Sscottl/* 1492149871Sscottl * Update short GI state according to received htcap 1493136849Sscottl * and local settings. 1494136849Sscottl */ 1495136849Sscottlstatic __inline void 1496136849Sscottlhtcap_update_shortgi(struct ieee80211_node *ni) 1497136849Sscottl{ 1498136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 1499149871Sscottl 1500149871Sscottl ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40); 1501190809Sdelphij if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) && 1502149871Sscottl (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)) 1503149871Sscottl ni->ni_flags |= IEEE80211_NODE_SGI20; 1504136849Sscottl if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && 1505136849Sscottl (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)) 1506136849Sscottl ni->ni_flags |= IEEE80211_NODE_SGI40; 1507136849Sscottl} 1508149871Sscottl 1509149871Sscottl/* 1510190809Sdelphij * Parse and update HT-related state extracted from 1511149871Sscottl * the HT cap and info ie's. 1512136849Sscottl */ 1513136849Sscottlvoid 1514136849Sscottlieee80211_ht_updateparams(struct ieee80211_node *ni, 1515136849Sscottl const uint8_t *htcapie, const uint8_t *htinfoie) 1516149871Sscottl{ 1517149871Sscottl struct ieee80211vap *vap = ni->ni_vap; 1518190809Sdelphij const struct ieee80211_ie_htinfo *htinfo; 1519149871Sscottl int htflags; 1520136849Sscottl 1521136849Sscottl ieee80211_parse_htcap(ni, htcapie); 1522149871Sscottl if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) 1523149871Sscottl htcap_update_mimo_ps(ni); 1524149871Sscottl htcap_update_shortgi(ni); 1525149871Sscottl 1526149871Sscottl if (htinfoie[0] == IEEE80211_ELEMID_VENDOR) 1527149871Sscottl htinfoie += 4; 1528149871Sscottl htinfo = (const struct ieee80211_ie_htinfo *) htinfoie; 1529149871Sscottl htinfo_parse(ni, htinfo); 1530149871Sscottl 1531149871Sscottl htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? 1532149871Sscottl IEEE80211_CHAN_HT20 : 0; 1533149871Sscottl /* NB: honor operating mode constraint */ 1534149871Sscottl if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && 1535149871Sscottl (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { 1536149871Sscottl if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) 1537149871Sscottl htflags = IEEE80211_CHAN_HT40U; 1538149871Sscottl else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) 1539149871Sscottl htflags = IEEE80211_CHAN_HT40D; 1540149871Sscottl } 1541149871Sscottl htinfo_update_chw(ni, htflags); 1542136849Sscottl 1543136849Sscottl if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) && 1544149871Sscottl (vap->iv_flags_ht & IEEE80211_FHT_RIFS)) 1545149871Sscottl ni->ni_flags |= IEEE80211_NODE_RIFS; 1546149871Sscottl else 1547149871Sscottl ni->ni_flags &= ~IEEE80211_NODE_RIFS; 1548149871Sscottl} 1549149871Sscottl 1550149871Sscottl/* 1551149871Sscottl * Parse and update HT-related state extracted from the HT cap ie 1552149871Sscottl * for a station joining an HT BSS. 1553136849Sscottl */ 1554136849Sscottlvoid 1555136849Sscottlieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) 1556136849Sscottl{ 1557136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 1558149871Sscottl int htflags; 1559149871Sscottl 1560136849Sscottl ieee80211_parse_htcap(ni, htcapie); 1561136849Sscottl if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) 1562136849Sscottl htcap_update_mimo_ps(ni); 1563136849Sscottl htcap_update_shortgi(ni); 1564136849Sscottl 1565136849Sscottl /* NB: honor operating mode constraint */ 1566136849Sscottl /* XXX 40 MHz intolerant */ 1567136849Sscottl htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? 1568136849Sscottl IEEE80211_CHAN_HT20 : 0; 1569136849Sscottl if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && 1570136849Sscottl (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { 1571149871Sscottl if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan)) 1572136849Sscottl htflags = IEEE80211_CHAN_HT40U; 1573136849Sscottl else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan)) 1574149871Sscottl htflags = IEEE80211_CHAN_HT40D; 1575149871Sscottl } 1576136849Sscottl htinfo_update_chw(ni, htflags); 1577136849Sscottl} 1578136849Sscottl 1579136849Sscottl/* 1580149871Sscottl * Install received HT rate set by parsing the HT cap ie. 1581136849Sscottl */ 1582136849Sscottlint 1583136849Sscottlieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) 1584136849Sscottl{ 1585149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1586136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 1587136849Sscottl const struct ieee80211_ie_htcap *htcap; 1588136849Sscottl struct ieee80211_htrateset *rs; 1589136849Sscottl int i, maxequalmcs, maxunequalmcs; 1590136849Sscottl 1591136849Sscottl maxequalmcs = ic->ic_txstream * 8 - 1; 1592136849Sscottl if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { 1593136849Sscottl if (ic->ic_txstream >= 2) 1594136849Sscottl maxunequalmcs = 38; 1595136849Sscottl if (ic->ic_txstream >= 3) 1596149871Sscottl maxunequalmcs = 52; 1597136849Sscottl if (ic->ic_txstream >= 4) 1598149871Sscottl maxunequalmcs = 76; 1599149871Sscottl } else 1600136849Sscottl maxunequalmcs = 0; 1601149871Sscottl 1602136849Sscottl rs = &ni->ni_htrates; 1603136849Sscottl memset(rs, 0, sizeof(*rs)); 1604149871Sscottl if (ie != NULL) { 1605136849Sscottl if (ie[0] == IEEE80211_ELEMID_VENDOR) 1606136849Sscottl ie += 4; 1607149871Sscottl htcap = (const struct ieee80211_ie_htcap *) ie; 1608149871Sscottl for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 1609136849Sscottl if (isclr(htcap->hc_mcsset, i)) 1610136849Sscottl continue; 1611136849Sscottl if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { 1612136849Sscottl IEEE80211_NOTE(vap, 1613136849Sscottl IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 1614136849Sscottl "WARNING, HT rate set too large; only " 1615136849Sscottl "using %u rates", IEEE80211_HTRATE_MAXSIZE); 1616136849Sscottl vap->iv_stats.is_rx_rstoobig++; 1617136849Sscottl break; 1618136849Sscottl } 1619136849Sscottl if (i <= 31 && i > maxequalmcs) 1620136862Sscottl continue; 1621136849Sscottl if (i == 32 && 1622136849Sscottl (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0) 1623136849Sscottl continue; 1624136849Sscottl if (i > 32 && i > maxunequalmcs) 1625136849Sscottl continue; 1626136849Sscottl rs->rs_rates[rs->rs_nrates++] = i; 1627136849Sscottl } 1628136849Sscottl } 1629136849Sscottl return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); 1630136849Sscottl} 1631136849Sscottl 1632136849Sscottl/* 1633136849Sscottl * Mark rates in a node's HT rate set as basic according 1634136849Sscottl * to the information in the supplied HT info ie. 1635136849Sscottl */ 1636136849Sscottlvoid 1637149871Sscottlieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) 1638136849Sscottl{ 1639136849Sscottl const struct ieee80211_ie_htinfo *htinfo; 1640136849Sscottl struct ieee80211_htrateset *rs; 1641136849Sscottl int i, j; 1642136849Sscottl 1643136849Sscottl if (ie[0] == IEEE80211_ELEMID_VENDOR) 1644149871Sscottl ie += 4; 1645149871Sscottl htinfo = (const struct ieee80211_ie_htinfo *) ie; 1646149871Sscottl rs = &ni->ni_htrates; 1647149871Sscottl if (rs->rs_nrates == 0) { 1648149871Sscottl IEEE80211_NOTE(ni->ni_vap, 1649149871Sscottl IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 1650149871Sscottl "%s", "WARNING, empty HT rate set"); 1651136849Sscottl return; 1652136849Sscottl } 1653136849Sscottl for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 1654136849Sscottl if (isclr(htinfo->hi_basicmcsset, i)) 1655136849Sscottl continue; 1656149871Sscottl for (j = 0; j < rs->rs_nrates; j++) 1657136849Sscottl if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) 1658149871Sscottl rs->rs_rates[j] |= IEEE80211_RATE_BASIC; 1659149871Sscottl } 1660136849Sscottl} 1661136849Sscottl 1662136849Sscottlstatic void 1663136849Sscottlampdu_tx_setup(struct ieee80211_tx_ampdu *tap) 1664136849Sscottl{ 1665136849Sscottl callout_init(&tap->txa_timer, CALLOUT_MPSAFE); 1666136849Sscottl tap->txa_flags |= IEEE80211_AGGR_SETUP; 1667149871Sscottl} 1668149871Sscottl 1669136849Sscottlstatic void 1670136849Sscottlampdu_tx_stop(struct ieee80211_tx_ampdu *tap) 1671149871Sscottl{ 1672149871Sscottl struct ieee80211_node *ni = tap->txa_ni; 1673149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1674149871Sscottl 1675149871Sscottl KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP, 1676149871Sscottl ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac)); 1677149871Sscottl 1678149871Sscottl /* 1679149871Sscottl * Stop BA stream if setup so driver has a chance 1680136849Sscottl * to reclaim any resources it might have allocated. 1681149871Sscottl */ 1682149871Sscottl ic->ic_addba_stop(ni, tap); 1683136849Sscottl /* 1684149871Sscottl * Stop any pending BAR transmit. 1685149871Sscottl */ 1686136849Sscottl bar_stop_timer(tap); 1687136849Sscottl 1688136849Sscottl tap->txa_lastsample = 0; 1689136849Sscottl tap->txa_avgpps = 0; 1690136849Sscottl /* NB: clearing NAK means we may re-send ADDBA */ 1691136849Sscottl tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); 1692136849Sscottl} 1693136849Sscottl 1694136849Sscottlstatic void 1695149871Sscottladdba_timeout(void *arg) 1696136849Sscottl{ 1697136849Sscottl struct ieee80211_tx_ampdu *tap = arg; 1698136849Sscottl 1699136849Sscottl /* XXX ? */ 1700136849Sscottl tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 1701136849Sscottl tap->txa_attempts++; 1702136849Sscottl} 1703136849Sscottl 1704136849Sscottlstatic void 1705136849Sscottladdba_start_timeout(struct ieee80211_tx_ampdu *tap) 1706136849Sscottl{ 1707149871Sscottl /* XXX use CALLOUT_PENDING instead? */ 1708136849Sscottl callout_reset(&tap->txa_timer, ieee80211_addba_timeout, 1709136849Sscottl addba_timeout, tap); 1710149871Sscottl tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; 1711149871Sscottl tap->txa_nextrequest = ticks + ieee80211_addba_timeout; 1712190809Sdelphij} 1713149871Sscottl 1714149871Sscottlstatic void 1715136849Sscottladdba_stop_timeout(struct ieee80211_tx_ampdu *tap) 1716136849Sscottl{ 1717149871Sscottl /* XXX use CALLOUT_PENDING instead? */ 1718149871Sscottl if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { 1719149871Sscottl callout_stop(&tap->txa_timer); 1720190809Sdelphij tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 1721149871Sscottl } 1722149871Sscottl} 1723149871Sscottl 1724136849Sscottl/* 1725136849Sscottl * Default method for requesting A-MPDU tx aggregation. 1726136849Sscottl * We setup the specified state block and start a timer 1727136849Sscottl * to wait for an ADDBA response frame. 1728136849Sscottl */ 1729136849Sscottlstatic int 1730136849Sscottlieee80211_addba_request(struct ieee80211_node *ni, 1731136849Sscottl struct ieee80211_tx_ampdu *tap, 1732136849Sscottl int dialogtoken, int baparamset, int batimeout) 1733136849Sscottl{ 1734136849Sscottl int bufsiz; 1735136849Sscottl 1736136849Sscottl /* XXX locking */ 1737136849Sscottl tap->txa_token = dialogtoken; 1738149871Sscottl tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; 1739136849Sscottl bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1740136849Sscottl tap->txa_wnd = (bufsiz == 0) ? 1741136849Sscottl IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 1742136849Sscottl addba_start_timeout(tap); 1743136849Sscottl return 1; 1744136849Sscottl} 1745136849Sscottl 1746136849Sscottl/* 1747190809Sdelphij * Default method for processing an A-MPDU tx aggregation 1748190809Sdelphij * response. We shutdown any pending timer and update the 1749190809Sdelphij * state block according to the reply. 1750190809Sdelphij */ 1751190809Sdelphijstatic int 1752190809Sdelphijieee80211_addba_response(struct ieee80211_node *ni, 1753136849Sscottl struct ieee80211_tx_ampdu *tap, 1754149871Sscottl int status, int baparamset, int batimeout) 1755149871Sscottl{ 1756136849Sscottl int bufsiz, tid; 1757136849Sscottl 1758136849Sscottl /* XXX locking */ 1759136849Sscottl addba_stop_timeout(tap); 1760136849Sscottl if (status == IEEE80211_STATUS_SUCCESS) { 1761136849Sscottl bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1762136849Sscottl /* XXX override our request? */ 1763136849Sscottl tap->txa_wnd = (bufsiz == 0) ? 1764136849Sscottl IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 1765136849Sscottl /* XXX AC/TID */ 1766136849Sscottl tid = MS(baparamset, IEEE80211_BAPS_TID); 1767136849Sscottl tap->txa_flags |= IEEE80211_AGGR_RUNNING; 1768136849Sscottl tap->txa_attempts = 0; 1769136849Sscottl } else { 1770136849Sscottl /* mark tid so we don't try again */ 1771136849Sscottl tap->txa_flags |= IEEE80211_AGGR_NAK; 1772136849Sscottl } 1773136849Sscottl return 1; 1774136849Sscottl} 1775149871Sscottl 1776149871Sscottl/* 1777149871Sscottl * Default method for stopping A-MPDU tx aggregation. 1778149871Sscottl * Any timer is cleared and we drain any pending frames. 1779136849Sscottl */ 1780149871Sscottlstatic void 1781149871Sscottlieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) 1782149871Sscottl{ 1783149871Sscottl /* XXX locking */ 1784149871Sscottl addba_stop_timeout(tap); 1785149871Sscottl if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { 1786149871Sscottl /* XXX clear aggregation queue */ 1787149871Sscottl tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; 1788149871Sscottl } 1789149871Sscottl tap->txa_attempts = 0; 1790149871Sscottl} 1791149871Sscottl 1792149871Sscottl/* 1793149871Sscottl * Process a received action frame using the default aggregation 1794136849Sscottl * policy. We intercept ADDBA-related frames and use them to 1795149871Sscottl * update our aggregation state. All other frames are passed up 1796149871Sscottl * for processing by ieee80211_recv_action. 1797149871Sscottl */ 1798149871Sscottlstatic int 1799149871Sscottlht_recv_action_ba_addba_request(struct ieee80211_node *ni, 1800149871Sscottl const struct ieee80211_frame *wh, 1801149871Sscottl const uint8_t *frm, const uint8_t *efrm) 1802149871Sscottl{ 1803149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1804149871Sscottl struct ieee80211vap *vap = ni->ni_vap; 1805149871Sscottl struct ieee80211_rx_ampdu *rap; 1806149871Sscottl uint8_t dialogtoken; 1807149871Sscottl uint16_t baparamset, batimeout, baseqctl; 1808149871Sscottl uint16_t args[5]; 1809149871Sscottl int tid; 1810149871Sscottl 1811149871Sscottl dialogtoken = frm[2]; 1812149871Sscottl baparamset = LE_READ_2(frm+3); 1813149871Sscottl batimeout = LE_READ_2(frm+5); 1814149871Sscottl baseqctl = LE_READ_2(frm+7); 1815149871Sscottl 1816149871Sscottl tid = MS(baparamset, IEEE80211_BAPS_TID); 1817149871Sscottl 1818149871Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1819149871Sscottl "recv ADDBA request: dialogtoken %u baparamset 0x%x " 1820136849Sscottl "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d", 1821149871Sscottl dialogtoken, baparamset, 1822149871Sscottl tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ), 1823149871Sscottl batimeout, 1824149871Sscottl MS(baseqctl, IEEE80211_BASEQ_START), 1825149871Sscottl MS(baseqctl, IEEE80211_BASEQ_FRAG)); 1826149871Sscottl 1827136849Sscottl rap = &ni->ni_rx_ampdu[tid]; 1828136849Sscottl 1829136849Sscottl /* Send ADDBA response */ 1830149871Sscottl args[0] = dialogtoken; 1831136862Sscottl /* 1832136849Sscottl * NB: We ack only if the sta associated with HT and 1833136849Sscottl * the ap is configured to do AMPDU rx (the latter 1834149871Sscottl * violates the 11n spec and is mostly for testing). 1835136849Sscottl */ 1836136849Sscottl if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && 1837136849Sscottl (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) { 1838136849Sscottl /* XXX handle ampdu_rx_start failure */ 1839149871Sscottl ic->ic_ampdu_rx_start(ni, rap, 1840149871Sscottl baparamset, batimeout, baseqctl); 1841136849Sscottl 1842136849Sscottl args[1] = IEEE80211_STATUS_SUCCESS; 1843136849Sscottl } else { 1844136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1845136849Sscottl ni, "reject ADDBA request: %s", 1846149871Sscottl ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? 1847136849Sscottl "administratively disabled" : 1848149871Sscottl "not negotiated for station"); 1849149871Sscottl vap->iv_stats.is_addba_reject++; 1850136849Sscottl args[1] = IEEE80211_STATUS_UNSPECIFIED; 1851136849Sscottl } 1852136849Sscottl /* XXX honor rap flags? */ 1853136849Sscottl args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE 1854136849Sscottl | SM(tid, IEEE80211_BAPS_TID) 1855149871Sscottl | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) 1856136849Sscottl ; 1857149871Sscottl args[3] = 0; 1858149871Sscottl args[4] = 0; 1859136849Sscottl ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 1860149871Sscottl IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); 1861149871Sscottl return 0; 1862136849Sscottl} 1863149871Sscottl 1864149871Sscottlstatic int 1865136849Sscottlht_recv_action_ba_addba_response(struct ieee80211_node *ni, 1866136849Sscottl const struct ieee80211_frame *wh, 1867136849Sscottl const uint8_t *frm, const uint8_t *efrm) 1868149871Sscottl{ 1869149871Sscottl struct ieee80211com *ic = ni->ni_ic; 1870149871Sscottl struct ieee80211vap *vap = ni->ni_vap; 1871136849Sscottl struct ieee80211_tx_ampdu *tap; 1872136849Sscottl uint8_t dialogtoken, policy; 1873136849Sscottl uint16_t baparamset, batimeout, code; 1874136849Sscottl int tid, ac, bufsiz; 1875149871Sscottl 1876149871Sscottl dialogtoken = frm[2]; 1877149871Sscottl code = LE_READ_2(frm+3); 1878136849Sscottl baparamset = LE_READ_2(frm+5); 1879136849Sscottl tid = MS(baparamset, IEEE80211_BAPS_TID); 1880136849Sscottl bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1881149871Sscottl policy = MS(baparamset, IEEE80211_BAPS_POLICY); 1882149871Sscottl batimeout = LE_READ_2(frm+7); 1883149871Sscottl 1884136849Sscottl ac = TID_TO_WME_AC(tid); 1885136849Sscottl tap = &ni->ni_tx_ampdu[ac]; 1886136849Sscottl if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 1887149871Sscottl IEEE80211_DISCARD_MAC(vap, 1888149871Sscottl IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1889136849Sscottl ni->ni_macaddr, "ADDBA response", 1890136849Sscottl "no pending ADDBA, tid %d dialogtoken %u " 1891136849Sscottl "code %d", tid, dialogtoken, code); 1892149871Sscottl vap->iv_stats.is_addba_norequest++; 1893149871Sscottl return 0; 1894136849Sscottl } 1895136849Sscottl if (dialogtoken != tap->txa_token) { 1896136849Sscottl IEEE80211_DISCARD_MAC(vap, 1897136849Sscottl IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1898136849Sscottl ni->ni_macaddr, "ADDBA response", 1899136849Sscottl "dialogtoken mismatch: waiting for %d, " 1900136849Sscottl "received %d, tid %d code %d", 1901136849Sscottl tap->txa_token, dialogtoken, tid, code); 1902136849Sscottl vap->iv_stats.is_addba_badtoken++; 1903136849Sscottl return 0; 1904136849Sscottl } 1905136849Sscottl /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */ 1906136849Sscottl if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) { 1907136849Sscottl IEEE80211_DISCARD_MAC(vap, 1908136849Sscottl IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1909149871Sscottl ni->ni_macaddr, "ADDBA response", 1910136849Sscottl "policy mismatch: expecting %s, " 1911149871Sscottl "received %s, tid %d code %d", 1912136849Sscottl tap->txa_flags & IEEE80211_AGGR_IMMEDIATE, 1913136849Sscottl policy, tid, code); 1914149871Sscottl vap->iv_stats.is_addba_badpolicy++; 1915136849Sscottl return 0; 1916136849Sscottl } 1917149871Sscottl#if 0 1918149871Sscottl /* XXX we take MIN in ieee80211_addba_response */ 1919136849Sscottl if (bufsiz > IEEE80211_AGGR_BAWMAX) { 1920136849Sscottl IEEE80211_DISCARD_MAC(vap, 1921136849Sscottl IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1922136849Sscottl ni->ni_macaddr, "ADDBA response", 1923149871Sscottl "BA window too large: max %d, " 1924149871Sscottl "received %d, tid %d code %d", 1925149871Sscottl bufsiz, IEEE80211_AGGR_BAWMAX, tid, code); 1926149871Sscottl vap->iv_stats.is_addba_badbawinsize++; 1927149871Sscottl return 0; 1928149871Sscottl } 1929149871Sscottl#endif 1930149871Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1931149871Sscottl "recv ADDBA response: dialogtoken %u code %d " 1932149871Sscottl "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", 1933149871Sscottl dialogtoken, code, baparamset, tid, bufsiz, 1934149871Sscottl batimeout); 1935149871Sscottl ic->ic_addba_response(ni, tap, code, baparamset, batimeout); 1936149871Sscottl return 0; 1937149871Sscottl} 1938136849Sscottl 1939136849Sscottlstatic int 1940136849Sscottlht_recv_action_ba_delba(struct ieee80211_node *ni, 1941136849Sscottl const struct ieee80211_frame *wh, 1942136849Sscottl const uint8_t *frm, const uint8_t *efrm) 1943136849Sscottl{ 1944136849Sscottl struct ieee80211com *ic = ni->ni_ic; 1945136849Sscottl struct ieee80211_rx_ampdu *rap; 1946136849Sscottl struct ieee80211_tx_ampdu *tap; 1947136849Sscottl uint16_t baparamset, code; 1948136849Sscottl int tid, ac; 1949136849Sscottl 1950136849Sscottl baparamset = LE_READ_2(frm+2); 1951136849Sscottl code = LE_READ_2(frm+4); 1952136849Sscottl 1953136849Sscottl tid = MS(baparamset, IEEE80211_DELBAPS_TID); 1954136849Sscottl 1955136849Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1956136849Sscottl "recv DELBA: baparamset 0x%x (tid %d initiator %d) " 1957136849Sscottl "code %d", baparamset, tid, 1958136849Sscottl MS(baparamset, IEEE80211_DELBAPS_INIT), code); 1959136849Sscottl 1960149871Sscottl if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { 1961149871Sscottl ac = TID_TO_WME_AC(tid); 1962136849Sscottl tap = &ni->ni_tx_ampdu[ac]; 1963136849Sscottl ic->ic_addba_stop(ni, tap); 1964267368Sdelphij } else { 1965136849Sscottl rap = &ni->ni_rx_ampdu[tid]; 1966136849Sscottl ic->ic_ampdu_rx_stop(ni, rap); 1967136849Sscottl } 1968136849Sscottl return 0; 1969136849Sscottl} 1970136849Sscottl 1971136849Sscottlstatic int 1972136849Sscottlht_recv_action_ht_txchwidth(struct ieee80211_node *ni, 1973136849Sscottl const struct ieee80211_frame *wh, 1974136849Sscottl const uint8_t *frm, const uint8_t *efrm) 1975136849Sscottl{ 1976136849Sscottl int chw; 1977136849Sscottl 1978149871Sscottl chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ? 40 : 20; 1979136849Sscottl 1980136849Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1981136849Sscottl "%s: HT txchwidth, width %d%s", 1982136849Sscottl __func__, chw, ni->ni_chw != chw ? "*" : ""); 1983136849Sscottl if (chw != ni->ni_chw) { 1984269617Sjhb ni->ni_chw = chw; 1985136849Sscottl /* XXX notify on change */ 1986136849Sscottl } 1987136849Sscottl return 0; 1988136849Sscottl} 1989136849Sscottl 1990136849Sscottlstatic int 1991136849Sscottlht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni, 1992136849Sscottl const struct ieee80211_frame *wh, 1993295790Sjhibbits const uint8_t *frm, const uint8_t *efrm) 1994149871Sscottl{ 1995136849Sscottl const struct ieee80211_action_ht_mimopowersave *mps = 1996136849Sscottl (const struct ieee80211_action_ht_mimopowersave *) frm; 1997136849Sscottl 1998136849Sscottl /* XXX check iv_htcaps */ 1999269617Sjhb if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA) 2000269617Sjhb ni->ni_flags |= IEEE80211_NODE_MIMO_PS; 2001190809Sdelphij else 2002149871Sscottl ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; 2003136849Sscottl if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE) 2004136849Sscottl ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; 2005136849Sscottl else 2006136849Sscottl ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; 2007136849Sscottl /* XXX notify on change */ 2008149871Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 2009149871Sscottl "%s: HT MIMO PS (%s%s)", __func__, 2010149871Sscottl (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ? "on" : "off", 2011149871Sscottl (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ? "+rts" : "" 2012136849Sscottl ); 2013136849Sscottl return 0; 2014149871Sscottl} 2015149871Sscottl 2016149871Sscottl/* 2017136849Sscottl * Transmit processing. 2018136849Sscottl */ 2019136849Sscottl 2020136849Sscottl/* 2021136849Sscottl * Check if A-MPDU should be requested/enabled for a stream. 2022149871Sscottl * We require a traffic rate above a per-AC threshold and we 2023149871Sscottl * also handle backoff from previous failed attempts. 2024136849Sscottl * 2025136849Sscottl * Drivers may override this method to bring in information 2026136849Sscottl * such as link state conditions in making the decision. 2027136849Sscottl */ 2028136849Sscottlstatic int 2029136849Sscottlieee80211_ampdu_enable(struct ieee80211_node *ni, 2030136849Sscottl struct ieee80211_tx_ampdu *tap) 2031190809Sdelphij{ 2032269617Sjhb struct ieee80211vap *vap = ni->ni_vap; 2033269617Sjhb 2034190809Sdelphij if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac]) 2035136849Sscottl return 0; 2036136849Sscottl /* XXX check rssi? */ 2037136849Sscottl if (tap->txa_attempts >= ieee80211_addba_maxtries && 2038136849Sscottl ticks < tap->txa_nextrequest) { 2039269617Sjhb /* 2040190809Sdelphij * Don't retry too often; txa_nextrequest is set 2041149871Sscottl * to the minimum interval we'll retry after 2042136849Sscottl * ieee80211_addba_maxtries failed attempts are made. 2043269617Sjhb */ 2044136849Sscottl return 0; 2045136849Sscottl } 2046136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, 2047136849Sscottl "enable AMPDU on %s, avgpps %d pkts %d", 2048136849Sscottl ieee80211_wme_acnames[tap->txa_ac], tap->txa_avgpps, tap->txa_pkts); 2049149871Sscottl return 1; 2050149871Sscottl} 2051149871Sscottl 2052136849Sscottl/* 2053136849Sscottl * Request A-MPDU tx aggregation. Setup local state and 2054269617Sjhb * issue an ADDBA request. BA use will only happen after 2055136849Sscottl * the other end replies with ADDBA response. 2056136849Sscottl */ 2057136849Sscottlint 2058269617Sjhbieee80211_ampdu_request(struct ieee80211_node *ni, 2059136849Sscottl struct ieee80211_tx_ampdu *tap) 2060136849Sscottl{ 2061136849Sscottl struct ieee80211com *ic = ni->ni_ic; 2062136849Sscottl uint16_t args[5]; 2063136849Sscottl int tid, dialogtoken; 2064136849Sscottl static int tokens = 0; /* XXX */ 2065136849Sscottl 2066136849Sscottl /* XXX locking */ 2067136849Sscottl if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { 2068190863Sdelphij /* do deferred setup of state */ 2069190810Sdelphij ampdu_tx_setup(tap); 2070190810Sdelphij } 2071139044Snjl /* XXX hack for not doing proper locking */ 2072136849Sscottl tap->txa_flags &= ~IEEE80211_AGGR_NAK; 2073136849Sscottl 2074136849Sscottl dialogtoken = (tokens+1) % 63; /* XXX */ 2075136849Sscottl tid = WME_AC_TO_TID(tap->txa_ac); 2076136849Sscottl tap->txa_start = ni->ni_txseqs[tid]; 2077136849Sscottl 2078149871Sscottl args[0] = dialogtoken; 2079136849Sscottl args[1] = 0; /* NB: status code not used */ 2080136849Sscottl args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE 2081136849Sscottl | SM(tid, IEEE80211_BAPS_TID) 2082149871Sscottl | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) 2083136849Sscottl ; 2084136849Sscottl args[3] = 0; /* batimeout */ 2085136849Sscottl /* NB: do first so there's no race against reply */ 2086136849Sscottl if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) { 2087136849Sscottl /* unable to setup state, don't make request */ 2088136849Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, 2089136849Sscottl ni, "%s: could not setup BA stream for AC %d", 2090136849Sscottl __func__, tap->txa_ac); 2091269617Sjhb /* defer next try so we don't slam the driver with requests */ 2092269617Sjhb tap->txa_attempts = ieee80211_addba_maxtries; 2093269617Sjhb /* NB: check in case driver wants to override */ 2094269617Sjhb if (tap->txa_nextrequest <= ticks) 2095269617Sjhb tap->txa_nextrequest = ticks + ieee80211_addba_backoff; 2096136849Sscottl return 0; 2097136849Sscottl } 2098136849Sscottl tokens = dialogtoken; /* allocate token */ 2099136849Sscottl /* NB: after calling ic_addba_request so driver can set txa_start */ 2100136849Sscottl args[4] = SM(tap->txa_start, IEEE80211_BASEQ_START) 2101136849Sscottl | SM(0, IEEE80211_BASEQ_FRAG) 2102136849Sscottl ; 2103136849Sscottl return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 2104136849Sscottl IEEE80211_ACTION_BA_ADDBA_REQUEST, args); 2105269617Sjhb} 2106269617Sjhb 2107269617Sjhb/* 2108269617Sjhb * Terminate an AMPDU tx stream. State is reclaimed 2109269617Sjhb * and the peer notified with a DelBA Action frame. 2110269617Sjhb */ 2111269617Sjhbvoid 2112269617Sjhbieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, 2113269617Sjhb int reason) 2114269617Sjhb{ 2115269617Sjhb struct ieee80211com *ic = ni->ni_ic; 2116269617Sjhb struct ieee80211vap *vap = ni->ni_vap; 2117269617Sjhb uint16_t args[4]; 2118136849Sscottl 2119149871Sscottl /* XXX locking */ 2120149871Sscottl tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; 2121136849Sscottl if (IEEE80211_AMPDU_RUNNING(tap)) { 2122136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 2123136849Sscottl ni, "%s: stop BA stream for AC %d (reason %d)", 2124136849Sscottl __func__, tap->txa_ac, reason); 2125136849Sscottl vap->iv_stats.is_ampdu_stop++; 2126136849Sscottl 2127136849Sscottl ic->ic_addba_stop(ni, tap); 2128136849Sscottl args[0] = WME_AC_TO_TID(tap->txa_ac); 2129136849Sscottl args[1] = IEEE80211_DELBAPS_INIT; 2130136849Sscottl args[2] = reason; /* XXX reason code */ 2131136849Sscottl ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 2132136849Sscottl IEEE80211_ACTION_BA_DELBA, args); 2133136849Sscottl } else { 2134136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 2135136849Sscottl ni, "%s: BA stream for AC %d not running (reason %d)", 2136136849Sscottl __func__, tap->txa_ac, reason); 2137136849Sscottl vap->iv_stats.is_ampdu_stop_failed++; 2138136849Sscottl } 2139136849Sscottl} 2140136849Sscottl 2141136849Sscottlstatic void 2142136849Sscottlbar_timeout(void *arg) 2143136849Sscottl{ 2144136849Sscottl struct ieee80211_tx_ampdu *tap = arg; 2145136849Sscottl struct ieee80211_node *ni = tap->txa_ni; 2146136849Sscottl 2147136849Sscottl KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0, 2148136849Sscottl ("bar/addba collision, flags 0x%x", tap->txa_flags)); 2149136849Sscottl 2150136849Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, 2151136849Sscottl ni, "%s: tid %u flags 0x%x attempts %d", __func__, 2152136849Sscottl tap->txa_ac, tap->txa_flags, tap->txa_attempts); 2153136849Sscottl 2154136849Sscottl /* guard against race with bar_tx_complete */ 2155136849Sscottl if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) 2156136849Sscottl return; 2157136849Sscottl /* XXX ? */ 2158149871Sscottl if (tap->txa_attempts >= ieee80211_bar_maxtries) 2159136849Sscottl ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT); 2160136849Sscottl else 2161136849Sscottl ieee80211_send_bar(ni, tap, tap->txa_seqpending); 2162136849Sscottl} 2163136849Sscottl 2164136849Sscottlstatic void 2165149871Sscottlbar_start_timer(struct ieee80211_tx_ampdu *tap) 2166149871Sscottl{ 2167149871Sscottl callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap); 2168136849Sscottl} 2169149871Sscottl 2170269617Sjhbstatic void 2171149871Sscottlbar_stop_timer(struct ieee80211_tx_ampdu *tap) 2172269617Sjhb{ 2173149871Sscottl callout_stop(&tap->txa_timer); 2174149871Sscottl} 2175149871Sscottl 2176149871Sscottlstatic void 2177136849Sscottlbar_tx_complete(struct ieee80211_node *ni, void *arg, int status) 2178136849Sscottl{ 2179136849Sscottl struct ieee80211_tx_ampdu *tap = arg; 2180136849Sscottl 2181136849Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, 2182136849Sscottl ni, "%s: tid %u flags 0x%x pending %d status %d", 2183136849Sscottl __func__, tap->txa_ac, tap->txa_flags, 2184136849Sscottl callout_pending(&tap->txa_timer), status); 2185136849Sscottl 2186136849Sscottl /* XXX locking */ 2187149871Sscottl if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) && 2188149871Sscottl callout_pending(&tap->txa_timer)) { 2189136849Sscottl struct ieee80211com *ic = ni->ni_ic; 2190149871Sscottl 2191149871Sscottl if (status) /* ACK'd */ 2192149871Sscottl bar_stop_timer(tap); 2193149871Sscottl ic->ic_bar_response(ni, tap, status); 2194149871Sscottl /* NB: just let timer expire so we pace requests */ 2195149871Sscottl } 2196149871Sscottl} 2197149871Sscottl 2198136849Sscottlstatic void 2199136849Sscottlieee80211_bar_response(struct ieee80211_node *ni, 2200136849Sscottl struct ieee80211_tx_ampdu *tap, int status) 2201136849Sscottl{ 2202136849Sscottl 2203149871Sscottl if (status != 0) { /* got ACK */ 2204149871Sscottl IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, 2205136849Sscottl ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u", 2206149871Sscottl tap->txa_start, 2207136849Sscottl IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1), 2208136849Sscottl tap->txa_qframes, tap->txa_seqpending, 2209136849Sscottl WME_AC_TO_TID(tap->txa_ac)); 2210136849Sscottl 2211136849Sscottl /* NB: timer already stopped in bar_tx_complete */ 2212136849Sscottl tap->txa_start = tap->txa_seqpending; 2213136849Sscottl tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; 2214136849Sscottl } 2215149871Sscottl} 2216149871Sscottl 2217149871Sscottl/* 2218136849Sscottl * Transmit a BAR frame to the specified node. The 2219149871Sscottl * BAR contents are drawn from the supplied aggregation 2220136849Sscottl * state associated with the node. 2221136849Sscottl * 2222136849Sscottl * NB: we only handle immediate ACK w/ compressed bitmap. 2223136849Sscottl */ 2224149871Sscottlint 2225149871Sscottlieee80211_send_bar(struct ieee80211_node *ni, 2226136849Sscottl struct ieee80211_tx_ampdu *tap, ieee80211_seq seq) 2227136849Sscottl{ 2228269617Sjhb#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) 2229136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 2230136849Sscottl struct ieee80211com *ic = ni->ni_ic; 2231136849Sscottl struct ieee80211_frame_bar *bar; 2232136849Sscottl struct mbuf *m; 2233136849Sscottl uint16_t barctl, barseqctl; 2234136849Sscottl uint8_t *frm; 2235136849Sscottl int tid, ret; 2236136849Sscottl 2237136849Sscottl if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) { 2238136849Sscottl /* no ADDBA response, should not happen */ 2239136849Sscottl /* XXX stat+msg */ 2240136849Sscottl return EINVAL; 2241136849Sscottl } 2242136849Sscottl /* XXX locking */ 2243149871Sscottl bar_stop_timer(tap); 2244136849Sscottl 2245136849Sscottl ieee80211_ref_node(ni); 2246269617Sjhb 2247149878Sscottl m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar)); 2248149871Sscottl if (m == NULL) 2249136849Sscottl senderr(ENOMEM, is_tx_nobuf); 2250149871Sscottl 2251149871Sscottl if (!ieee80211_add_callback(m, bar_tx_complete, tap)) { 2252149871Sscottl m_freem(m); 2253149871Sscottl senderr(ENOMEM, is_tx_nobuf); /* XXX */ 2254149871Sscottl /* NOTREACHED */ 2255136849Sscottl } 2256149871Sscottl 2257149871Sscottl bar = mtod(m, struct ieee80211_frame_bar *); 2258149871Sscottl bar->i_fc[0] = IEEE80211_FC0_VERSION_0 | 2259149871Sscottl IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; 2260149871Sscottl bar->i_fc[1] = 0; 2261149871Sscottl IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr); 2262149871Sscottl IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr); 2263149871Sscottl 2264149871Sscottl tid = WME_AC_TO_TID(tap->txa_ac); 2265149871Sscottl barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 2266149871Sscottl 0 : IEEE80211_BAR_NOACK) 2267136849Sscottl | IEEE80211_BAR_COMP 2268149871Sscottl | SM(tid, IEEE80211_BAR_TID) 2269149871Sscottl ; 2270136849Sscottl barseqctl = SM(seq, IEEE80211_BAR_SEQ_START); 2271149871Sscottl /* NB: known to have proper alignment */ 2272149871Sscottl bar->i_ctl = htole16(barctl); 2273149871Sscottl bar->i_seq = htole16(barseqctl); 2274149871Sscottl m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar); 2275136849Sscottl 2276149871Sscottl M_WME_SETAC(m, WME_AC_VO); 2277149871Sscottl 2278149871Sscottl IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ 2279149871Sscottl 2280136849Sscottl /* XXX locking */ 2281149871Sscottl /* init/bump attempts counter */ 2282149871Sscottl if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) 2283149871Sscottl tap->txa_attempts = 1; 2284136849Sscottl else 2285149871Sscottl tap->txa_attempts++; 2286149871Sscottl tap->txa_seqpending = seq; 2287149871Sscottl tap->txa_flags |= IEEE80211_AGGR_BARPEND; 2288149871Sscottl 2289149871Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, 2290136849Sscottl ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)", 2291149871Sscottl tid, barctl, seq, tap->txa_attempts); 2292149871Sscottl 2293149871Sscottl ret = ic->ic_raw_xmit(ni, m, NULL); 2294149871Sscottl if (ret != 0) { 2295149871Sscottl /* xmit failed, clear state flag */ 2296149871Sscottl tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; 2297149871Sscottl goto bad; 2298149871Sscottl } 2299149871Sscottl /* XXX hack against tx complete happening before timer is started */ 2300149871Sscottl if (tap->txa_flags & IEEE80211_AGGR_BARPEND) 2301149871Sscottl bar_start_timer(tap); 2302136849Sscottl return 0; 2303149871Sscottlbad: 2304149871Sscottl ieee80211_free_node(ni); 2305149871Sscottl return ret; 2306149871Sscottl#undef senderr 2307149871Sscottl} 2308149871Sscottl 2309136849Sscottlstatic int 2310149871Sscottlht_action_output(struct ieee80211_node *ni, struct mbuf *m) 2311227912Smarius{ 2312149871Sscottl struct ieee80211_bpf_params params; 2313149871Sscottl 2314136849Sscottl memset(¶ms, 0, sizeof(params)); 2315149871Sscottl params.ibp_pri = WME_AC_VO; 2316149871Sscottl params.ibp_rate0 = ni->ni_txparms->mgmtrate; 2317149871Sscottl /* NB: we know all frames are unicast */ 2318136849Sscottl params.ibp_try0 = ni->ni_txparms->maxretry; 2319149871Sscottl params.ibp_power = ni->ni_txpower; 2320149871Sscottl return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, 2321149871Sscottl ¶ms); 2322149871Sscottl} 2323149871Sscottl 2324149871Sscottl#define ADDSHORT(frm, v) do { \ 2325136849Sscottl frm[0] = (v) & 0xff; \ 2326149871Sscottl frm[1] = (v) >> 8; \ 2327149871Sscottl frm += 2; \ 2328149871Sscottl} while (0) 2329136849Sscottl 2330149871Sscottl/* 2331149871Sscottl * Send an action management frame. The arguments are stuff 2332149871Sscottl * into a frame without inspection; the caller is assumed to 2333149871Sscottl * prepare them carefully (e.g. based on the aggregation state). 2334149871Sscottl */ 2335149871Sscottlstatic int 2336209341Smavht_send_action_ba_addba(struct ieee80211_node *ni, 2337209341Smav int category, int action, void *arg0) 2338209341Smav{ 2339209341Smav struct ieee80211vap *vap = ni->ni_vap; 2340149871Sscottl struct ieee80211com *ic = ni->ni_ic; 2341149871Sscottl uint16_t *args = arg0; 2342149871Sscottl struct mbuf *m; 2343149871Sscottl uint8_t *frm; 2344136849Sscottl 2345149871Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 2346149871Sscottl "send ADDBA %s: dialogtoken %d status %d " 2347149871Sscottl "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", 2348149871Sscottl (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ? 2349149871Sscottl "request" : "response", 2350136849Sscottl args[0], args[1], args[2], MS(args[2], IEEE80211_BAPS_TID), 2351136849Sscottl args[3], args[4]); 2352136849Sscottl 2353136849Sscottl IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2354136849Sscottl "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2355136849Sscottl ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2356136849Sscottl ieee80211_ref_node(ni); 2357136849Sscottl 2358136849Sscottl m = ieee80211_getmgtframe(&frm, 2359136849Sscottl ic->ic_headroom + sizeof(struct ieee80211_frame), 2360136849Sscottl sizeof(uint16_t) /* action+category */ 2361136849Sscottl /* XXX may action payload */ 2362136849Sscottl + sizeof(struct ieee80211_action_ba_addbaresponse) 2363136849Sscottl ); 2364136849Sscottl if (m != NULL) { 2365136849Sscottl *frm++ = category; 2366136849Sscottl *frm++ = action; 2367136849Sscottl *frm++ = args[0]; /* dialog token */ 2368136849Sscottl if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE) 2369136849Sscottl ADDSHORT(frm, args[1]); /* status code */ 2370136849Sscottl ADDSHORT(frm, args[2]); /* baparamset */ 2371136849Sscottl ADDSHORT(frm, args[3]); /* batimeout */ 2372136849Sscottl if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) 2373136849Sscottl ADDSHORT(frm, args[4]); /* baseqctl */ 2374136849Sscottl m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2375136849Sscottl return ht_action_output(ni, m); 2376149871Sscottl } else { 2377136849Sscottl vap->iv_stats.is_tx_nobuf++; 2378136849Sscottl ieee80211_free_node(ni); 2379136849Sscottl return ENOMEM; 2380136849Sscottl } 2381136849Sscottl} 2382136849Sscottl 2383136849Sscottlstatic int 2384136849Sscottlht_send_action_ba_delba(struct ieee80211_node *ni, 2385136849Sscottl int category, int action, void *arg0) 2386136849Sscottl{ 2387136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 2388136849Sscottl struct ieee80211com *ic = ni->ni_ic; 2389136849Sscottl uint16_t *args = arg0; 2390136849Sscottl struct mbuf *m; 2391136849Sscottl uint16_t baparamset; 2392136849Sscottl uint8_t *frm; 2393136849Sscottl 2394136849Sscottl baparamset = SM(args[0], IEEE80211_DELBAPS_TID) 2395136849Sscottl | args[1] 2396136849Sscottl ; 2397136849Sscottl IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 2398136849Sscottl "send DELBA action: tid %d, initiator %d reason %d", 2399269617Sjhb args[0], args[1], args[2]); 2400136849Sscottl 2401136849Sscottl IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2402136849Sscottl "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2403136849Sscottl ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2404136849Sscottl ieee80211_ref_node(ni); 2405136849Sscottl 2406269617Sjhb m = ieee80211_getmgtframe(&frm, 2407136849Sscottl ic->ic_headroom + sizeof(struct ieee80211_frame), 2408136849Sscottl sizeof(uint16_t) /* action+category */ 2409269617Sjhb /* XXX may action payload */ 2410269617Sjhb + sizeof(struct ieee80211_action_ba_addbaresponse) 2411136849Sscottl ); 2412149871Sscottl if (m != NULL) { 2413136849Sscottl *frm++ = category; 2414136849Sscottl *frm++ = action; 2415136849Sscottl ADDSHORT(frm, baparamset); 2416136849Sscottl ADDSHORT(frm, args[2]); /* reason code */ 2417136849Sscottl m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2418136849Sscottl return ht_action_output(ni, m); 2419136849Sscottl } else { 2420269617Sjhb vap->iv_stats.is_tx_nobuf++; 2421269617Sjhb ieee80211_free_node(ni); 2422136849Sscottl return ENOMEM; 2423149871Sscottl } 2424149871Sscottl} 2425149871Sscottl 2426149871Sscottlstatic int 2427149871Sscottlht_send_action_ht_txchwidth(struct ieee80211_node *ni, 2428149871Sscottl int category, int action, void *arg0) 2429149871Sscottl{ 2430269617Sjhb struct ieee80211vap *vap = ni->ni_vap; 2431269617Sjhb struct ieee80211com *ic = ni->ni_ic; 2432149871Sscottl struct mbuf *m; 2433149871Sscottl uint8_t *frm; 2434149871Sscottl 2435269617Sjhb IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 2436149871Sscottl "send HT txchwidth: width %d", 2437149871Sscottl IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20); 2438149871Sscottl 2439149871Sscottl IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2440149871Sscottl "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2441149871Sscottl ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2442149871Sscottl ieee80211_ref_node(ni); 2443149871Sscottl 2444149871Sscottl m = ieee80211_getmgtframe(&frm, 2445149871Sscottl ic->ic_headroom + sizeof(struct ieee80211_frame), 2446149871Sscottl sizeof(uint16_t) /* action+category */ 2447149871Sscottl /* XXX may action payload */ 2448136849Sscottl + sizeof(struct ieee80211_action_ba_addbaresponse) 2449269617Sjhb ); 2450149871Sscottl if (m != NULL) { 2451136849Sscottl *frm++ = category; 2452269617Sjhb *frm++ = action; 2453136849Sscottl *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 2454269617Sjhb IEEE80211_A_HT_TXCHWIDTH_2040 : 2455136849Sscottl IEEE80211_A_HT_TXCHWIDTH_20; 2456269617Sjhb m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2457136849Sscottl return ht_action_output(ni, m); 2458149871Sscottl } else { 2459149871Sscottl vap->iv_stats.is_tx_nobuf++; 2460136849Sscottl ieee80211_free_node(ni); 2461136849Sscottl return ENOMEM; 2462167086Sjhb } 2463136849Sscottl} 2464149871Sscottl#undef ADDSHORT 2465172836Sjulian 2466269617Sjhb/* 2467136849Sscottl * Construct the MCS bit mask for inclusion in an HT capabilities 2468136849Sscottl * information element. 2469136849Sscottl */ 2470136849Sscottlstatic void 2471136849Sscottlieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm) 2472136849Sscottl{ 2473136849Sscottl int i; 2474136849Sscottl uint8_t txparams; 2475136849Sscottl 2476136849Sscottl KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4), 2477149871Sscottl ("ic_rxstream %d out of range", ic->ic_rxstream)); 2478136849Sscottl KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4), 2479136849Sscottl ("ic_txstream %d out of range", ic->ic_txstream)); 2480136849Sscottl 2481136849Sscottl for (i = 0; i < ic->ic_rxstream * 8; i++) 2482136849Sscottl setbit(frm, i); 2483136849Sscottl if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && 2484136849Sscottl (ic->ic_htcaps & IEEE80211_HTC_RXMCS32)) 2485269617Sjhb setbit(frm, 32); 2486136849Sscottl if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) { 2487136849Sscottl if (ic->ic_rxstream >= 2) { 2488136849Sscottl for (i = 33; i <= 38; i++) 2489136849Sscottl setbit(frm, i); 2490136849Sscottl } 2491136849Sscottl if (ic->ic_rxstream >= 3) { 2492136849Sscottl for (i = 39; i <= 52; i++) 2493136849Sscottl setbit(frm, i); 2494136849Sscottl } 2495149871Sscottl if (ic->ic_txstream >= 4) { 2496149871Sscottl for (i = 53; i <= 76; i++) 2497149871Sscottl setbit(frm, i); 2498149871Sscottl } 2499149871Sscottl } 2500136849Sscottl 2501269617Sjhb if (ic->ic_rxstream != ic->ic_txstream) { 2502136849Sscottl txparams = 0x1; /* TX MCS set defined */ 2503136849Sscottl txparams |= 0x2; /* TX RX MCS not equal */ 2504149871Sscottl txparams |= (ic->ic_txstream - 1) << 2; /* num TX streams */ 2505136849Sscottl if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) 2506249849Smav txparams |= 0x16; /* TX unequal modulation sup */ 2507249849Smav } else 2508136849Sscottl txparams = 0; 2509149871Sscottl frm[12] = txparams; 2510149871Sscottl} 2511149871Sscottl 2512136849Sscottl/* 2513149871Sscottl * Add body of an HTCAP information element. 2514136849Sscottl */ 2515136849Sscottlstatic uint8_t * 2516136849Sscottlieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) 2517149871Sscottl{ 2518136849Sscottl#define ADDSHORT(frm, v) do { \ 2519267368Sdelphij frm[0] = (v) & 0xff; \ 2520267368Sdelphij frm[1] = (v) >> 8; \ 2521267368Sdelphij frm += 2; \ 2522267368Sdelphij} while (0) 2523267368Sdelphij struct ieee80211vap *vap = ni->ni_vap; 2524267368Sdelphij uint16_t caps, extcaps; 2525267368Sdelphij int rxmax, density; 2526267368Sdelphij 2527149871Sscottl /* HT capabilities */ 2528149871Sscottl caps = vap->iv_htcaps & 0xffff; 2529149871Sscottl /* 2530136849Sscottl * Note channel width depends on whether we are operating as 2531136849Sscottl * a sta or not. When operating as a sta we are generating 2532136849Sscottl * a request based on our desired configuration. Otherwise 2533136849Sscottl * we are operational and the channel attributes identify 2534136849Sscottl * how we've been setup (which might be different if a fixed 2535136849Sscottl * channel is specified). 2536136849Sscottl */ 2537136849Sscottl if (vap->iv_opmode == IEEE80211_M_STA) { 2538136849Sscottl /* override 20/40 use based on config */ 2539136849Sscottl if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40) 2540149871Sscottl caps |= IEEE80211_HTCAP_CHWIDTH40; 2541149871Sscottl else 2542136849Sscottl caps &= ~IEEE80211_HTCAP_CHWIDTH40; 2543136849Sscottl /* use advertised setting (XXX locally constraint) */ 2544136849Sscottl rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); 2545136849Sscottl density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); 2546136849Sscottl } else { 2547136849Sscottl /* override 20/40 use based on current channel */ 2548136849Sscottl if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) 2549136849Sscottl caps |= IEEE80211_HTCAP_CHWIDTH40; 2550149871Sscottl else 2551149871Sscottl caps &= ~IEEE80211_HTCAP_CHWIDTH40; 2552136849Sscottl rxmax = vap->iv_ampdu_rxmax; 2553136849Sscottl density = vap->iv_ampdu_density; 2554136849Sscottl } 2555136849Sscottl /* adjust short GI based on channel and config */ 2556136849Sscottl if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0) 2557136849Sscottl caps &= ~IEEE80211_HTCAP_SHORTGI20; 2558136849Sscottl if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 || 2559136849Sscottl (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) 2560136849Sscottl caps &= ~IEEE80211_HTCAP_SHORTGI40; 2561136849Sscottl ADDSHORT(frm, caps); 2562136849Sscottl 2563149871Sscottl /* HT parameters */ 2564136849Sscottl *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) 2565136849Sscottl | SM(density, IEEE80211_HTCAP_MPDUDENSITY) 2566136849Sscottl ; 2567149871Sscottl frm++; 2568149871Sscottl 2569149871Sscottl /* pre-zero remainder of ie */ 2570136849Sscottl memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - 2571136849Sscottl __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); 2572136849Sscottl 2573136849Sscottl /* supported MCS set */ 2574136849Sscottl /* 2575136849Sscottl * XXX: For sta mode the rate set should be restricted based 2576136849Sscottl * on the AP's capabilities, but ni_htrates isn't setup when 2577149871Sscottl * we're called to form an AssocReq frame so for now we're 2578149871Sscottl * restricted to the device capabilities. 2579149871Sscottl */ 2580149871Sscottl ieee80211_set_mcsset(ni->ni_ic, frm); 2581136849Sscottl 2582136849Sscottl frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) - 2583136849Sscottl __offsetof(struct ieee80211_ie_htcap, hc_mcsset); 2584190809Sdelphij 2585136849Sscottl /* HT extended capabilities */ 2586149871Sscottl extcaps = vap->iv_htextcaps & 0xffff; 2587136849Sscottl 2588149871Sscottl ADDSHORT(frm, extcaps); 2589149871Sscottl 2590149871Sscottl frm += sizeof(struct ieee80211_ie_htcap) - 2591136849Sscottl __offsetof(struct ieee80211_ie_htcap, hc_txbf); 2592149871Sscottl 2593136849Sscottl return frm; 2594136849Sscottl#undef ADDSHORT 2595149871Sscottl} 2596149871Sscottl 2597149871Sscottl/* 2598136849Sscottl * Add 802.11n HT capabilities information element 2599149871Sscottl */ 2600136849Sscottluint8_t * 2601136849Sscottlieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) 2602136849Sscottl{ 2603136849Sscottl frm[0] = IEEE80211_ELEMID_HTCAP; 2604136849Sscottl frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; 2605136849Sscottl return ieee80211_add_htcap_body(frm + 2, ni); 2606136849Sscottl} 2607136849Sscottl 2608136849Sscottl/* 2609136849Sscottl * Add Broadcom OUI wrapped standard HTCAP ie; this is 2610136849Sscottl * used for compatibility w/ pre-draft implementations. 2611136849Sscottl */ 2612136849Sscottluint8_t * 2613136849Sscottlieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) 2614136849Sscottl{ 2615136849Sscottl frm[0] = IEEE80211_ELEMID_VENDOR; 2616136849Sscottl frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; 2617269617Sjhb frm[2] = (BCM_OUI >> 0) & 0xff; 2618269617Sjhb frm[3] = (BCM_OUI >> 8) & 0xff; 2619269617Sjhb frm[4] = (BCM_OUI >> 16) & 0xff; 2620269617Sjhb frm[5] = BCM_OUI_HTCAP; 2621269617Sjhb return ieee80211_add_htcap_body(frm + 6, ni); 2622136849Sscottl} 2623136849Sscottl 2624136849Sscottl/* 2625149871Sscottl * Construct the MCS bit mask of basic rates 2626149871Sscottl * for inclusion in an HT information element. 2627149871Sscottl */ 2628149871Sscottlstatic void 2629149871Sscottlieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 2630149871Sscottl{ 2631149871Sscottl int i; 2632149871Sscottl 2633149871Sscottl for (i = 0; i < rs->rs_nrates; i++) { 2634149871Sscottl int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 2635149871Sscottl if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && 2636149871Sscottl r < IEEE80211_HTRATE_MAXSIZE) { 2637149871Sscottl /* NB: this assumes a particular implementation */ 2638149871Sscottl setbit(frm, r); 2639149871Sscottl } 2640246713Skib } 2641149871Sscottl} 2642149871Sscottl 2643149871Sscottl/* 2644149871Sscottl * Update the HTINFO ie for a beacon frame. 2645246713Skib */ 2646246713Skibvoid 2647246713Skibieee80211_ht_update_beacon(struct ieee80211vap *vap, 2648246713Skib struct ieee80211_beacon_offsets *bo) 2649246713Skib{ 2650246713Skib#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) 2651246713Skib const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan; 2652246713Skib struct ieee80211com *ic = vap->iv_ic; 2653246713Skib struct ieee80211_ie_htinfo *ht = 2654246713Skib (struct ieee80211_ie_htinfo *) bo->bo_htinfo; 2655246713Skib 2656246713Skib /* XXX only update on channel change */ 2657246713Skib ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan); 2658246713Skib if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) 2659246713Skib ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM; 2660246713Skib else 2661246713Skib ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; 2662149871Sscottl if (IEEE80211_IS_CHAN_HT40U(bsschan)) 2663149871Sscottl ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 2664269617Sjhb else if (IEEE80211_IS_CHAN_HT40D(bsschan)) 2665149871Sscottl ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; 2666149871Sscottl else 2667149871Sscottl ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; 2668149871Sscottl if (IEEE80211_IS_CHAN_HT40(bsschan)) 2669149871Sscottl ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; 2670149871Sscottl 2671136849Sscottl /* protection mode */ 2672136849Sscottl ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; 2673136849Sscottl 2674149871Sscottl /* XXX propagate to vendor ie's */ 2675149871Sscottl#undef PROTMODE 2676149871Sscottl} 2677149871Sscottl 2678149871Sscottl/* 2679136849Sscottl * Add body of an HTINFO information element. 2680149871Sscottl * 2681136849Sscottl * NB: We don't use struct ieee80211_ie_htinfo because we can 2682136849Sscottl * be called to fillin both a standard ie and a compat ie that 2683136849Sscottl * has a vendor OUI at the front. 2684136849Sscottl */ 2685136849Sscottlstatic uint8_t * 2686136849Sscottlieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) 2687136849Sscottl{ 2688136849Sscottl struct ieee80211vap *vap = ni->ni_vap; 2689136849Sscottl struct ieee80211com *ic = ni->ni_ic; 2690136849Sscottl 2691136849Sscottl /* pre-zero remainder of ie */ 2692136849Sscottl memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); 2693136849Sscottl 2694136849Sscottl /* primary/control channel center */ 2695136849Sscottl *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); 2696136849Sscottl 2697149871Sscottl if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) 2698149871Sscottl frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM; 2699149871Sscottl else 2700149871Sscottl frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; 2701149871Sscottl if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) 2702149871Sscottl frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 2703136849Sscottl else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) 2704149871Sscottl frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; 2705149871Sscottl else 2706149871Sscottl frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; 2707149871Sscottl if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) 2708149871Sscottl frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; 2709136849Sscottl 2710149871Sscottl frm[1] = ic->ic_curhtprotmode; 2711149871Sscottl 2712190809Sdelphij frm += 5; 2713190809Sdelphij 2714190809Sdelphij /* basic MCS set */ 2715190809Sdelphij ieee80211_set_basic_htrates(frm, &ni->ni_htrates); 2716190809Sdelphij frm += sizeof(struct ieee80211_ie_htinfo) - 2717190809Sdelphij __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); 2718190809Sdelphij return frm; 2719190809Sdelphij} 2720190809Sdelphij 2721190809Sdelphij/* 2722190809Sdelphij * Add 802.11n HT information information element. 2723190809Sdelphij */ 2724190809Sdelphijuint8_t * 2725149871Sscottlieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) 2726190809Sdelphij{ 2727190809Sdelphij frm[0] = IEEE80211_ELEMID_HTINFO; 2728190809Sdelphij frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; 2729190809Sdelphij return ieee80211_add_htinfo_body(frm + 2, ni); 2730190809Sdelphij} 2731149871Sscottl 2732149871Sscottl/* 2733136849Sscottl * Add Broadcom OUI wrapped standard HTINFO ie; this is 2734136849Sscottl * used for compatibility w/ pre-draft implementations. 2735190809Sdelphij */ 2736190809Sdelphijuint8_t * 2737190809Sdelphijieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) 2738190809Sdelphij{ 2739190809Sdelphij frm[0] = IEEE80211_ELEMID_VENDOR; 2740190809Sdelphij frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; 2741190809Sdelphij frm[2] = (BCM_OUI >> 0) & 0xff; 2742190809Sdelphij frm[3] = (BCM_OUI >> 8) & 0xff; 2743190809Sdelphij frm[4] = (BCM_OUI >> 16) & 0xff; 2744190809Sdelphij frm[5] = BCM_OUI_HTINFO; 2745190809Sdelphij return ieee80211_add_htinfo_body(frm + 6, ni); 2746190809Sdelphij} 2747190809Sdelphij