ieee80211_ht.c revision 170530
1170530Ssam/*- 2170530Ssam * Copyright (c) 2007 Sam Leffler, Errno Consulting 3170530Ssam * All rights reserved. 4170530Ssam * 5170530Ssam * Redistribution and use in source and binary forms, with or without 6170530Ssam * modification, are permitted provided that the following conditions 7170530Ssam * are met: 8170530Ssam * 1. Redistributions of source code must retain the above copyright 9170530Ssam * notice, this list of conditions and the following disclaimer. 10170530Ssam * 2. Redistributions in binary form must reproduce the above copyright 11170530Ssam * notice, this list of conditions and the following disclaimer in the 12170530Ssam * documentation and/or other materials provided with the distribution. 13170530Ssam * 14170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24170530Ssam */ 25170530Ssam 26170530Ssam#include <sys/cdefs.h> 27170530Ssam#ifdef __FreeBSD__ 28170530Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 170530 2007-06-11 03:36:55Z sam $"); 29170530Ssam#endif 30170530Ssam 31170530Ssam/* 32170530Ssam * IEEE 802.11n protocol support. 33170530Ssam */ 34170530Ssam 35170530Ssam#include "opt_inet.h" 36170530Ssam 37170530Ssam#include <sys/param.h> 38170530Ssam#include <sys/kernel.h> 39170530Ssam#include <sys/systm.h> 40170530Ssam#include <sys/endian.h> 41170530Ssam 42170530Ssam#include <sys/socket.h> 43170530Ssam 44170530Ssam#include <net/if.h> 45170530Ssam#include <net/if_media.h> 46170530Ssam#include <net/ethernet.h> 47170530Ssam 48170530Ssam#include <net80211/ieee80211_var.h> 49170530Ssam 50170530Ssam/* define here, used throughout file */ 51170530Ssam#define MS(_v, _f) (((_v) & _f) >> _f##_S) 52170530Ssam#define SM(_v, _f) (((_v) << _f##_S) & _f) 53170530Ssam 54170530Ssam/* XXX need max array size */ 55170530Ssamconst int ieee80211_htrates[16] = { 56170530Ssam 13, /* IFM_IEEE80211_MCS0 */ 57170530Ssam 26, /* IFM_IEEE80211_MCS1 */ 58170530Ssam 39, /* IFM_IEEE80211_MCS2 */ 59170530Ssam 52, /* IFM_IEEE80211_MCS3 */ 60170530Ssam 78, /* IFM_IEEE80211_MCS4 */ 61170530Ssam 104, /* IFM_IEEE80211_MCS5 */ 62170530Ssam 117, /* IFM_IEEE80211_MCS6 */ 63170530Ssam 130, /* IFM_IEEE80211_MCS7 */ 64170530Ssam 26, /* IFM_IEEE80211_MCS8 */ 65170530Ssam 52, /* IFM_IEEE80211_MCS9 */ 66170530Ssam 78, /* IFM_IEEE80211_MCS10 */ 67170530Ssam 104, /* IFM_IEEE80211_MCS11 */ 68170530Ssam 156, /* IFM_IEEE80211_MCS12 */ 69170530Ssam 208, /* IFM_IEEE80211_MCS13 */ 70170530Ssam 234, /* IFM_IEEE80211_MCS14 */ 71170530Ssam 260, /* IFM_IEEE80211_MCS15 */ 72170530Ssam}; 73170530Ssam 74170530Ssamstatic const struct ieee80211_htrateset ieee80211_rateset_11n = 75170530Ssam { 16, { 76170530Ssam /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */ 77170530Ssam 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 78170530Ssam /* 39 52 78 104 117, 130 */ 79170530Ssam 10, 11, 12, 13, 14, 15 } 80170530Ssam }; 81170530Ssam 82170530Ssam#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250) 83170530Ssam#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000) 84170530Ssam#define IEEE80211_AGGR_MAXTRIES 3 85170530Ssam 86170530Ssamstatic int ieee80211_addba_request(struct ieee80211_node *ni, 87170530Ssam struct ieee80211_tx_ampdu *tap, 88170530Ssam int dialogtoken, int baparamset, int batimeout); 89170530Ssamstatic int ieee80211_addba_response(struct ieee80211_node *ni, 90170530Ssam struct ieee80211_tx_ampdu *tap, 91170530Ssam int code, int baparamset, int batimeout); 92170530Ssamstatic void ieee80211_addba_stop(struct ieee80211_node *ni, 93170530Ssam struct ieee80211_tx_ampdu *tap); 94170530Ssamstatic void ieee80211_aggr_recv_action(struct ieee80211_node *ni, 95170530Ssam const uint8_t *frm, const uint8_t *efrm); 96170530Ssam 97170530Ssamvoid 98170530Ssamieee80211_ht_attach(struct ieee80211com *ic) 99170530Ssam{ 100170530Ssam 101170530Ssam ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; 102170530Ssam ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; 103170530Ssam ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; 104170530Ssam 105170530Ssam ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; 106170530Ssam 107170530Ssam /* setup default aggregation policy */ 108170530Ssam ic->ic_recv_action = ieee80211_aggr_recv_action; 109170530Ssam ic->ic_send_action = ieee80211_send_action; 110170530Ssam ic->ic_addba_request = ieee80211_addba_request; 111170530Ssam ic->ic_addba_response = ieee80211_addba_response; 112170530Ssam ic->ic_addba_stop = ieee80211_addba_stop; 113170530Ssam 114170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 115170530Ssam isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 116170530Ssam /* 117170530Ssam * There are HT channels in the channel list; enable 118170530Ssam * all HT-related facilities by default. 119170530Ssam * XXX these choices may be too aggressive. 120170530Ssam */ 121170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_HT 122170530Ssam | IEEE80211_FEXT_HTCOMPAT 123170530Ssam ; 124170530Ssam if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) 125170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; 126170530Ssam /* XXX infer from channel list */ 127170530Ssam if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { 128170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; 129170530Ssam if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) 130170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; 131170530Ssam } 132170530Ssam /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ 133170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; 134170530Ssam if (ic->ic_htcaps & IEEE80211_HTC_AMPDU) 135170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; 136170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; 137170530Ssam if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) 138170530Ssam ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; 139170530Ssam } 140170530Ssam} 141170530Ssam 142170530Ssamvoid 143170530Ssamieee80211_ht_detach(struct ieee80211com *ic) 144170530Ssam{ 145170530Ssam} 146170530Ssam 147170530Ssamstatic void 148170530Ssamht_announce(struct ieee80211com *ic, int mode, 149170530Ssam const struct ieee80211_htrateset *rs) 150170530Ssam{ 151170530Ssam struct ifnet *ifp = ic->ic_ifp; 152170530Ssam int i, rate, mword; 153170530Ssam 154170530Ssam if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); 155170530Ssam for (i = 0; i < rs->rs_nrates; i++) { 156170530Ssam mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 157170530Ssam if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) 158170530Ssam continue; 159170530Ssam rate = ieee80211_htrates[rs->rs_rates[i]]; 160170530Ssam printf("%s%d%sMbps", (i != 0 ? " " : ""), 161170530Ssam rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 162170530Ssam } 163170530Ssam printf("\n"); 164170530Ssam} 165170530Ssam 166170530Ssamvoid 167170530Ssamieee80211_ht_announce(struct ieee80211com *ic) 168170530Ssam{ 169170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) 170170530Ssam ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); 171170530Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) 172170530Ssam ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); 173170530Ssam} 174170530Ssam 175170530Ssamconst struct ieee80211_htrateset * 176170530Ssamieee80211_get_suphtrates(struct ieee80211com *ic, 177170530Ssam const struct ieee80211_channel *c) 178170530Ssam{ 179170530Ssam if (IEEE80211_IS_CHAN_HT(c)) 180170530Ssam return &ieee80211_rateset_11n; 181170530Ssam /* XXX what's the right thing to do here? */ 182170530Ssam return (const struct ieee80211_htrateset *) 183170530Ssam ieee80211_get_suprates(ic, c); 184170530Ssam} 185170530Ssam 186170530Ssam/* 187170530Ssam * Receive processing. 188170530Ssam */ 189170530Ssam 190170530Ssam/* 191170530Ssam * Decap the encapsulated A-MSDU frames and dispatch all but 192170530Ssam * the last for delivery. The last frame is returned for 193170530Ssam * delivery via the normal path. 194170530Ssam */ 195170530Ssamstruct mbuf * 196170530Ssamieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) 197170530Ssam{ 198170530Ssam struct ieee80211com *ic = ni->ni_ic; 199170530Ssam int totallen, framelen; 200170530Ssam struct mbuf *n; 201170530Ssam 202170530Ssam /* discard 802.3 header inserted by ieee80211_decap */ 203170530Ssam m_adj(m, sizeof(struct ether_header)); 204170530Ssam 205170530Ssam ic->ic_stats.is_amsdu_decap++; 206170530Ssam 207170530Ssam totallen = m->m_pkthdr.len; 208170530Ssam for (;;) { 209170530Ssam /* 210170530Ssam * Decap the first frame, bust it apart from the 211170530Ssam * remainder and deliver. We leave the last frame 212170530Ssam * delivery to the caller (for consistency with other 213170530Ssam * code paths, could also do it here). 214170530Ssam */ 215170530Ssam m = ieee80211_decap1(m, &framelen); 216170530Ssam if (m == NULL) { 217170530Ssam IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 218170530Ssam ni->ni_macaddr, "a-msdu", "%s", "first decap failed"); 219170530Ssam ic->ic_stats.is_amsdu_tooshort++; 220170530Ssam return NULL; 221170530Ssam } 222170530Ssam if (framelen == totallen) 223170530Ssam break; 224170530Ssam n = m_split(m, framelen, M_NOWAIT); 225170530Ssam if (n == NULL) { 226170530Ssam IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 227170530Ssam ni->ni_macaddr, "a-msdu", 228170530Ssam "%s", "unable to split encapsulated frames"); 229170530Ssam ic->ic_stats.is_amsdu_split++; 230170530Ssam m_freem(m); /* NB: must reclaim */ 231170530Ssam return NULL; 232170530Ssam } 233170530Ssam ieee80211_deliver_data(ic, ni, m); 234170530Ssam 235170530Ssam /* 236170530Ssam * Remove frame contents; each intermediate frame 237170530Ssam * is required to be aligned to a 4-byte boundary. 238170530Ssam */ 239170530Ssam m = n; 240170530Ssam m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ 241170530Ssam } 242170530Ssam return m; /* last delivered by caller */ 243170530Ssam} 244170530Ssam 245170530Ssam/* 246170530Ssam * Start A-MPDU rx/re-order processing for the specified TID. 247170530Ssam */ 248170530Ssamstatic void 249170530Ssamampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) 250170530Ssam{ 251170530Ssam memset(rap, 0, sizeof(*rap)); 252170530Ssam rap->rxa_wnd = (bufsiz == 0) ? 253170530Ssam IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 254170530Ssam rap->rxa_start = start; 255170530Ssam rap->rxa_nxt = rap->rxa_start; 256170530Ssam rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; 257170530Ssam} 258170530Ssam 259170530Ssam/* 260170530Ssam * Purge all frames in the A-MPDU re-order queue. 261170530Ssam */ 262170530Ssamstatic void 263170530Ssamampdu_rx_purge(struct ieee80211_rx_ampdu *rap) 264170530Ssam{ 265170530Ssam struct mbuf *m; 266170530Ssam int i; 267170530Ssam 268170530Ssam for (i = 0; i < rap->rxa_wnd; i++) { 269170530Ssam m = rap->rxa_m[i]; 270170530Ssam if (m != NULL) { 271170530Ssam rap->rxa_m[i] = NULL; 272170530Ssam rap->rxa_qbytes -= m->m_pkthdr.len; 273170530Ssam m_freem(m); 274170530Ssam if (--rap->rxa_qframes == 0) 275170530Ssam break; 276170530Ssam } 277170530Ssam } 278170530Ssam KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 279170530Ssam ("lost %u data, %u frames on ampdu rx q", 280170530Ssam rap->rxa_qbytes, rap->rxa_qframes)); 281170530Ssam} 282170530Ssam 283170530Ssam/* 284170530Ssam * Stop A-MPDU rx processing for the specified TID. 285170530Ssam */ 286170530Ssamstatic void 287170530Ssamampdu_rx_stop(struct ieee80211_rx_ampdu *rap) 288170530Ssam{ 289170530Ssam rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND; 290170530Ssam ampdu_rx_purge(rap); 291170530Ssam} 292170530Ssam 293170530Ssam/* 294170530Ssam * Dispatch a frame from the A-MPDU reorder queue. The 295170530Ssam * frame is fed back into ieee80211_input marked with an 296170530Ssam * M_AMPDU flag so it doesn't come back to us (it also 297170530Ssam * permits ieee80211_input to optimize re-processing). 298170530Ssam */ 299170530Ssamstatic __inline void 300170530Ssamampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) 301170530Ssam{ 302170530Ssam m->m_flags |= M_AMPDU; /* bypass normal processing */ 303170530Ssam /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */ 304170530Ssam (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0); 305170530Ssam} 306170530Ssam 307170530Ssam/* 308170530Ssam * Dispatch as many frames as possible from the re-order queue. 309170530Ssam * Frames will always be "at the front"; we process all frames 310170530Ssam * up to the first empty slot in the window. On completion we 311170530Ssam * cleanup state if there are still pending frames in the current 312170530Ssam * BA window. We assume the frame at slot 0 is already handled 313170530Ssam * by the caller; we always start at slot 1. 314170530Ssam */ 315170530Ssamstatic void 316170530Ssamampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) 317170530Ssam{ 318170530Ssam struct ieee80211com *ic = ni->ni_ic; 319170530Ssam struct mbuf *m; 320170530Ssam int i; 321170530Ssam 322170530Ssam /* flush run of frames */ 323170530Ssam for (i = 1; i < rap->rxa_wnd; i++) { 324170530Ssam m = rap->rxa_m[i]; 325170530Ssam if (m == NULL) 326170530Ssam break; 327170530Ssam rap->rxa_m[i] = NULL; 328170530Ssam rap->rxa_qbytes -= m->m_pkthdr.len; 329170530Ssam rap->rxa_qframes--; 330170530Ssam 331170530Ssam ampdu_dispatch(ni, m); 332170530Ssam } 333170530Ssam /* 334170530Ssam * Adjust the start of the BA window to 335170530Ssam * reflect the frames just dispatched. 336170530Ssam */ 337170530Ssam rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); 338170530Ssam rap->rxa_nxt = rap->rxa_start; 339170530Ssam ic->ic_stats.is_ampdu_rx_oor += i; 340170530Ssam /* 341170530Ssam * If frames remain, copy the mbuf pointers down so 342170530Ssam * they correspond to the offsets in the new window. 343170530Ssam */ 344170530Ssam if (rap->rxa_qframes != 0) { 345170530Ssam int n = rap->rxa_qframes, j; 346170530Ssam for (j = i+1; j < rap->rxa_wnd; j++) { 347170530Ssam if (rap->rxa_m[j] != NULL) { 348170530Ssam rap->rxa_m[j-i] = rap->rxa_m[j]; 349170530Ssam rap->rxa_m[j] = NULL; 350170530Ssam if (--n == 0) 351170530Ssam break; 352170530Ssam } 353170530Ssam } 354170530Ssam KASSERT(n == 0, ("lost %d frames", n)); 355170530Ssam ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; 356170530Ssam } 357170530Ssam} 358170530Ssam 359170530Ssam/* 360170530Ssam * Dispatch all frames in the A-MPDU 361170530Ssam * re-order queue up to the specified slot. 362170530Ssam */ 363170530Ssamstatic void 364170530Ssamampdu_rx_flush(struct ieee80211_node *ni, 365170530Ssam struct ieee80211_rx_ampdu *rap, int limit) 366170530Ssam{ 367170530Ssam struct mbuf *m; 368170530Ssam int i; 369170530Ssam 370170530Ssam for (i = 0; i < limit; i++) { 371170530Ssam m = rap->rxa_m[i]; 372170530Ssam if (m == NULL) 373170530Ssam continue; 374170530Ssam rap->rxa_m[i] = NULL; 375170530Ssam rap->rxa_qbytes -= m->m_pkthdr.len; 376170530Ssam ampdu_dispatch(ni, m); 377170530Ssam if (--rap->rxa_qframes == 0) 378170530Ssam break; 379170530Ssam } 380170530Ssam} 381170530Ssam 382170530Ssam/* 383170530Ssam * Process a received QoS data frame for an HT station. Handle 384170530Ssam * A-MPDU reordering: if this frame is received out of order 385170530Ssam * and falls within the BA window hold onto it. Otherwise if 386170530Ssam * this frame completes a run flush any pending frames. We 387170530Ssam * return 1 if the frame is consumed. A 0 is returned if 388170530Ssam * the frame should be processed normally by the caller. 389170530Ssam */ 390170530Ssamint 391170530Ssamieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) 392170530Ssam{ 393170530Ssam#define IEEE80211_FC0_QOSDATA \ 394170530Ssam (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) 395170530Ssam struct ieee80211com *ic = ni->ni_ic; 396170530Ssam struct ieee80211_qosframe *wh; 397170530Ssam struct ieee80211_rx_ampdu *rap; 398170530Ssam ieee80211_seq rxseq; 399170530Ssam uint8_t tid; 400170530Ssam int off; 401170530Ssam 402170530Ssam KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); 403170530Ssam 404170530Ssam /* NB: m_len known to be sufficient */ 405170530Ssam wh = mtod(m, struct ieee80211_qosframe *); 406170530Ssam KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data")); 407170530Ssam 408170530Ssam /* XXX 4-address frame */ 409170530Ssam tid = wh->i_qos[0] & IEEE80211_QOS_TID; 410170530Ssam rap = &ni->ni_rx_ampdu[tid]; 411170530Ssam if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 412170530Ssam /* 413170530Ssam * No ADDBA request yet, don't touch. 414170530Ssam */ 415170530Ssam return 0; 416170530Ssam } 417170530Ssam rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 418170530Ssam if (rxseq == rap->rxa_start) { 419170530Ssam /* 420170530Ssam * First frame in window. 421170530Ssam */ 422170530Ssam if (rap->rxa_qframes != 0) { 423170530Ssam /* 424170530Ssam * Dispatch as many packets as we can. 425170530Ssam */ 426170530Ssam KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); 427170530Ssam ampdu_dispatch(ni, m); 428170530Ssam ampdu_rx_dispatch(rap, ni); 429170530Ssam return 1; /* NB: consumed */ 430170530Ssam } else { 431170530Ssam /* 432170530Ssam * In order; advance window and notify 433170530Ssam * caller to dispatch directly. 434170530Ssam */ 435170530Ssam rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 436170530Ssam rap->rxa_nxt = rap->rxa_start; 437170530Ssam return 0; /* NB: process packet */ 438170530Ssam } 439170530Ssam } 440170530Ssam /* 441170530Ssam * This packet is out of order; store it 442170530Ssam * if it's in the BA window. 443170530Ssam */ 444170530Ssam /* calculate offset in BA window */ 445170530Ssam off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 446170530Ssam if (off >= rap->rxa_wnd) { 447170530Ssam /* 448170530Ssam * Outside the window, clear the q and start over. 449170530Ssam * 450170530Ssam * NB: this handles the case where rxseq is before 451170530Ssam * rxa_start because our max BA window is 64 452170530Ssam * and the sequence number range is 4096. 453170530Ssam */ 454170530Ssam IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, 455170530Ssam "flush BA win <%u:%u> (%u frames) rxseq %u tid %u", 456170530Ssam rap->rxa_start, 457170530Ssam IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), 458170530Ssam rap->rxa_qframes, rxseq, tid); 459170530Ssam 460170530Ssam if (rap->rxa_qframes != 0) { 461170530Ssam ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; 462170530Ssam ampdu_rx_flush(ni, rap, rap->rxa_wnd); 463170530Ssam KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 464170530Ssam ("lost %u data, %u frames on ampdu rx q", 465170530Ssam rap->rxa_qbytes, rap->rxa_qframes)); 466170530Ssam } 467170530Ssam rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 468170530Ssam rap->rxa_nxt = rap->rxa_start; 469170530Ssam return 0; /* NB: process packet */ 470170530Ssam } 471170530Ssam if (rap->rxa_qframes != 0) { 472170530Ssam#if 0 473170530Ssam /* XXX honor batimeout? */ 474170530Ssam if (ticks - mn->mn_age[tid] > 50) { 475170530Ssam /* 476170530Ssam * Too long since we received the first frame; flush. 477170530Ssam */ 478170530Ssam if (rap->rxa_qframes != 0) { 479170530Ssam ic->ic_stats.is_ampdu_rx_oor += 480170530Ssam rap->rxa_qframes; 481170530Ssam ampdu_rx_flush(ni, rap, rap->rxa_wnd); 482170530Ssam } 483170530Ssam rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 484170530Ssam rap->rxa_nxt = rap->rxa_start; 485170530Ssam return 0; /* NB: process packet */ 486170530Ssam } 487170530Ssam#endif 488170530Ssam rap->rxa_nxt = rxseq; 489170530Ssam } else { 490170530Ssam /* 491170530Ssam * First frame, start aging timer. 492170530Ssam */ 493170530Ssam#if 0 494170530Ssam mn->mn_age[tid] = ticks; 495170530Ssam#endif 496170530Ssam } 497170530Ssam /* save packet */ 498170530Ssam if (rap->rxa_m[off] == NULL) { 499170530Ssam rap->rxa_m[off] = m; 500170530Ssam rap->rxa_qframes++; 501170530Ssam rap->rxa_qbytes += m->m_pkthdr.len; 502170530Ssam } else { 503170530Ssam IEEE80211_DISCARD_MAC(ic, 504170530Ssam IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 505170530Ssam ni->ni_macaddr, "a-mpdu duplicate", 506170530Ssam "seqno %u tid %u BA win <%u:%u>", 507170530Ssam rxseq, tid, rap->rxa_start, rap->rxa_wnd); 508170530Ssam ic->ic_stats.is_rx_dup++; 509170530Ssam IEEE80211_NODE_STAT(ni, rx_dup); 510170530Ssam m_freem(m); 511170530Ssam } 512170530Ssam return 1; /* NB: consumed */ 513170530Ssam#undef IEEE80211_FC0_QOSDATA 514170530Ssam} 515170530Ssam 516170530Ssam/* 517170530Ssam * Process a BAR ctl frame. Dispatch all frames up to 518170530Ssam * the sequence number of the frame. If this frame is 519170530Ssam * out of the window it's discarded. 520170530Ssam */ 521170530Ssamvoid 522170530Ssamieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) 523170530Ssam{ 524170530Ssam struct ieee80211com *ic = ni->ni_ic; 525170530Ssam struct ieee80211_frame_bar *wh; 526170530Ssam struct ieee80211_rx_ampdu *rap; 527170530Ssam ieee80211_seq rxseq; 528170530Ssam int tid, off; 529170530Ssam 530170530Ssam wh = mtod(m0, struct ieee80211_frame_bar *); 531170530Ssam /* XXX check basic BAR */ 532170530Ssam tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); 533170530Ssam rap = &ni->ni_rx_ampdu[tid]; 534170530Ssam if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 535170530Ssam /* 536170530Ssam * No ADDBA request yet, don't touch. 537170530Ssam */ 538170530Ssam IEEE80211_DISCARD_MAC(ic, 539170530Ssam IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 540170530Ssam ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); 541170530Ssam ic->ic_stats.is_ampdu_bar_bad++; 542170530Ssam return; 543170530Ssam } 544170530Ssam ic->ic_stats.is_ampdu_bar_rx++; 545170530Ssam rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 546170530Ssam /* calculate offset in BA window */ 547170530Ssam off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 548170530Ssam if (off >= rap->rxa_wnd) { 549170530Ssam /* 550170530Ssam * Outside the window, flush the reorder q if 551170530Ssam * not pulling the sequence # backward. The 552170530Ssam * latter is typically caused by a dropped BA. 553170530Ssam */ 554170530Ssam IEEE80211_NOTE(ic, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni, 555170530Ssam "recv BAR outside BA win <%u:%u> rxseq %u tid %u", 556170530Ssam rap->rxa_start, 557170530Ssam IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), 558170530Ssam rxseq, tid); 559170530Ssam ic->ic_stats.is_ampdu_bar_oow++; 560170530Ssam if (rxseq < rap->rxa_start) { 561170530Ssam /* XXX stat? */ 562170530Ssam return; 563170530Ssam } 564170530Ssam if (rap->rxa_qframes != 0) { 565170530Ssam ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; 566170530Ssam ampdu_rx_flush(ni, rap, rap->rxa_wnd); 567170530Ssam KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 568170530Ssam ("lost %u data, %u frames on ampdu rx q", 569170530Ssam rap->rxa_qbytes, rap->rxa_qframes)); 570170530Ssam } 571170530Ssam } else if (rap->rxa_qframes != 0) { 572170530Ssam /* 573170530Ssam * Dispatch packets up to rxseq. 574170530Ssam */ 575170530Ssam ampdu_rx_flush(ni, rap, off); 576170530Ssam ic->ic_stats.is_ampdu_rx_oor += off; 577170530Ssam 578170530Ssam /* 579170530Ssam * If frames remain, copy the mbuf pointers down so 580170530Ssam * they correspond to the offsets in the new window. 581170530Ssam */ 582170530Ssam if (rap->rxa_qframes != 0) { 583170530Ssam int n = rap->rxa_qframes, j; 584170530Ssam for (j = off+1; j < rap->rxa_wnd; j++) { 585170530Ssam if (rap->rxa_m[j] != NULL) { 586170530Ssam rap->rxa_m[j-off] = rap->rxa_m[j]; 587170530Ssam rap->rxa_m[j] = NULL; 588170530Ssam if (--n == 0) 589170530Ssam break; 590170530Ssam } 591170530Ssam } 592170530Ssam KASSERT(n == 0, ("lost %d frames", n)); 593170530Ssam ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; 594170530Ssam } 595170530Ssam } 596170530Ssam rap->rxa_start = rxseq; 597170530Ssam rap->rxa_nxt = rap->rxa_start; 598170530Ssam} 599170530Ssam 600170530Ssam/* 601170530Ssam * Setup HT-specific state in a node. Called only 602170530Ssam * when HT use is negotiated so we don't do extra 603170530Ssam * work for temporary and/or legacy sta's. 604170530Ssam */ 605170530Ssamvoid 606170530Ssamieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap) 607170530Ssam{ 608170530Ssam struct ieee80211_tx_ampdu *tap; 609170530Ssam int ac; 610170530Ssam 611170530Ssam ieee80211_parse_htcap(ni, htcap); 612170530Ssam for (ac = 0; ac < WME_NUM_AC; ac++) { 613170530Ssam tap = &ni->ni_tx_ampdu[ac]; 614170530Ssam tap->txa_ac = ac; 615170530Ssam } 616170530Ssam ni->ni_flags |= IEEE80211_NODE_HT; 617170530Ssam} 618170530Ssam 619170530Ssam/* 620170530Ssam * Cleanup HT-specific state in a node. Called only 621170530Ssam * when HT use has been marked. 622170530Ssam */ 623170530Ssamvoid 624170530Ssamieee80211_ht_node_cleanup(struct ieee80211_node *ni) 625170530Ssam{ 626170530Ssam struct ieee80211com *ic = ni->ni_ic; 627170530Ssam int i; 628170530Ssam 629170530Ssam KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); 630170530Ssam 631170530Ssam /* XXX optimize this */ 632170530Ssam for (i = 0; i < WME_NUM_AC; i++) { 633170530Ssam struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; 634170530Ssam if (IEEE80211_AMPDU_REQUESTED(tap)) 635170530Ssam ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]); 636170530Ssam } 637170530Ssam for (i = 0; i < WME_NUM_TID; i++) 638170530Ssam ampdu_rx_stop(&ni->ni_rx_ampdu[i]); 639170530Ssam 640170530Ssam ni->ni_htcap = 0; 641170530Ssam ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT); 642170530Ssam} 643170530Ssam 644170530Ssam/* unalligned little endian access */ 645170530Ssam#define LE_READ_2(p) \ 646170530Ssam ((uint16_t) \ 647170530Ssam ((((const uint8_t *)(p))[0] ) | \ 648170530Ssam (((const uint8_t *)(p))[1] << 8))) 649170530Ssam 650170530Ssam/* 651170530Ssam * Process an 802.11n HT capabilities ie. 652170530Ssam */ 653170530Ssamvoid 654170530Ssamieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) 655170530Ssam{ 656170530Ssam struct ieee80211com *ic = ni->ni_ic; 657170530Ssam 658170530Ssam if (ie[0] == IEEE80211_ELEMID_VENDOR) { 659170530Ssam /* 660170530Ssam * Station used Vendor OUI ie to associate; 661170530Ssam * mark the node so when we respond we'll use 662170530Ssam * the Vendor OUI's and not the standard ie's. 663170530Ssam */ 664170530Ssam ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; 665170530Ssam ie += 4; 666170530Ssam } else 667170530Ssam ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; 668170530Ssam 669170530Ssam ni->ni_htcap = LE_READ_2(ie + 670170530Ssam __offsetof(struct ieee80211_ie_htcap, hc_cap)); 671170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0) 672170530Ssam ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI40; 673170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) 674170530Ssam ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI20; 675170530Ssam ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) ? 40 : 20; 676170530Ssam ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; 677170530Ssam#if 0 678170530Ssam ni->ni_maxampdu = 679170530Ssam (8*1024) << MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); 680170530Ssam ni->ni_mpdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); 681170530Ssam#endif 682170530Ssam} 683170530Ssam 684170530Ssam/* 685170530Ssam * Process an 802.11n HT info ie. 686170530Ssam */ 687170530Ssamvoid 688170530Ssamieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) 689170530Ssam{ 690170530Ssam const struct ieee80211_ie_htinfo *htinfo; 691170530Ssam uint16_t w; 692170530Ssam int chw; 693170530Ssam 694170530Ssam if (ie[0] == IEEE80211_ELEMID_VENDOR) 695170530Ssam ie += 4; 696170530Ssam htinfo = (const struct ieee80211_ie_htinfo *) ie; 697170530Ssam ni->ni_htctlchan = htinfo->hi_ctrlchannel; 698170530Ssam ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); 699170530Ssam w = LE_READ_2(&htinfo->hi_byte23); 700170530Ssam ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); 701170530Ssam w = LE_READ_2(&htinfo->hi_byte45); 702170530Ssam ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); 703170530Ssam /* update node's recommended tx channel width */ 704170530Ssam chw = (htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) ? 40 : 20; 705170530Ssam if (chw != ni->ni_chw) { 706170530Ssam ni->ni_chw = chw; 707170530Ssam ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; 708170530Ssam } 709170530Ssam} 710170530Ssam 711170530Ssam/* 712170530Ssam * Install received HT rate set by parsing the HT cap ie. 713170530Ssam */ 714170530Ssamint 715170530Ssamieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) 716170530Ssam{ 717170530Ssam struct ieee80211com *ic = ni->ni_ic; 718170530Ssam const struct ieee80211_ie_htcap *htcap; 719170530Ssam struct ieee80211_htrateset *rs; 720170530Ssam int i; 721170530Ssam 722170530Ssam rs = &ni->ni_htrates; 723170530Ssam memset(rs, 0, sizeof(*rs)); 724170530Ssam if (ie != NULL) { 725170530Ssam if (ie[0] == IEEE80211_ELEMID_VENDOR) 726170530Ssam ie += 4; 727170530Ssam htcap = (const struct ieee80211_ie_htcap *) ie; 728170530Ssam for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 729170530Ssam if (isclr(htcap->hc_mcsset, i)) 730170530Ssam continue; 731170530Ssam if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { 732170530Ssam IEEE80211_NOTE(ic, 733170530Ssam IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 734170530Ssam "WARNING, HT rate set too large; only " 735170530Ssam "using %u rates", IEEE80211_HTRATE_MAXSIZE); 736170530Ssam ic->ic_stats.is_rx_rstoobig++; 737170530Ssam break; 738170530Ssam } 739170530Ssam rs->rs_rates[rs->rs_nrates++] = i; 740170530Ssam } 741170530Ssam } 742170530Ssam return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); 743170530Ssam} 744170530Ssam 745170530Ssam/* 746170530Ssam * Mark rates in a node's HT rate set as basic according 747170530Ssam * to the information in the supplied HT info ie. 748170530Ssam */ 749170530Ssamvoid 750170530Ssamieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) 751170530Ssam{ 752170530Ssam const struct ieee80211_ie_htinfo *htinfo; 753170530Ssam struct ieee80211_htrateset *rs; 754170530Ssam int i, j; 755170530Ssam 756170530Ssam if (ie[0] == IEEE80211_ELEMID_VENDOR) 757170530Ssam ie += 4; 758170530Ssam htinfo = (const struct ieee80211_ie_htinfo *) ie; 759170530Ssam rs = &ni->ni_htrates; 760170530Ssam if (rs->rs_nrates == 0) { 761170530Ssam IEEE80211_NOTE(ni->ni_ic, 762170530Ssam IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 763170530Ssam "%s", "WARNING, empty HT rate set"); 764170530Ssam return; 765170530Ssam } 766170530Ssam for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 767170530Ssam if (isclr(htinfo->hi_basicmcsset, i)) 768170530Ssam continue; 769170530Ssam for (j = 0; j < rs->rs_nrates; j++) 770170530Ssam if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) 771170530Ssam rs->rs_rates[j] |= IEEE80211_RATE_BASIC; 772170530Ssam } 773170530Ssam} 774170530Ssam 775170530Ssamstatic void 776170530Ssamaddba_timeout(void *arg) 777170530Ssam{ 778170530Ssam struct ieee80211_tx_ampdu *tap = arg; 779170530Ssam 780170530Ssam /* XXX ? */ 781170530Ssam tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 782170530Ssam tap->txa_attempts++; 783170530Ssam} 784170530Ssam 785170530Ssamstatic void 786170530Ssamaddba_start_timeout(struct ieee80211_tx_ampdu *tap) 787170530Ssam{ 788170530Ssam /* XXX use CALLOUT_PENDING instead? */ 789170530Ssam callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT, 790170530Ssam addba_timeout, tap); 791170530Ssam tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; 792170530Ssam tap->txa_lastrequest = ticks; 793170530Ssam} 794170530Ssam 795170530Ssamstatic void 796170530Ssamaddba_stop_timeout(struct ieee80211_tx_ampdu *tap) 797170530Ssam{ 798170530Ssam /* XXX use CALLOUT_PENDING instead? */ 799170530Ssam if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { 800170530Ssam callout_stop(&tap->txa_timer); 801170530Ssam tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 802170530Ssam } 803170530Ssam} 804170530Ssam 805170530Ssam/* 806170530Ssam * Default method for requesting A-MPDU tx aggregation. 807170530Ssam * We setup the specified state block and start a timer 808170530Ssam * to wait for an ADDBA response frame. 809170530Ssam */ 810170530Ssamstatic int 811170530Ssamieee80211_addba_request(struct ieee80211_node *ni, 812170530Ssam struct ieee80211_tx_ampdu *tap, 813170530Ssam int dialogtoken, int baparamset, int batimeout) 814170530Ssam{ 815170530Ssam int bufsiz; 816170530Ssam 817170530Ssam /* XXX locking */ 818170530Ssam tap->txa_token = dialogtoken; 819170530Ssam tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; 820170530Ssam tap->txa_start = tap->txa_seqstart = 0; 821170530Ssam bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 822170530Ssam tap->txa_wnd = (bufsiz == 0) ? 823170530Ssam IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 824170530Ssam addba_start_timeout(tap); 825170530Ssam return 1; 826170530Ssam} 827170530Ssam 828170530Ssam/* 829170530Ssam * Default method for processing an A-MPDU tx aggregation 830170530Ssam * response. We shutdown any pending timer and update the 831170530Ssam * state block according to the reply. 832170530Ssam */ 833170530Ssamstatic int 834170530Ssamieee80211_addba_response(struct ieee80211_node *ni, 835170530Ssam struct ieee80211_tx_ampdu *tap, 836170530Ssam int status, int baparamset, int batimeout) 837170530Ssam{ 838170530Ssam int bufsiz; 839170530Ssam 840170530Ssam /* XXX locking */ 841170530Ssam addba_stop_timeout(tap); 842170530Ssam if (status == IEEE80211_STATUS_SUCCESS) { 843170530Ssam bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 844170530Ssam /* XXX override our request? */ 845170530Ssam tap->txa_wnd = (bufsiz == 0) ? 846170530Ssam IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 847170530Ssam tap->txa_flags |= IEEE80211_AGGR_RUNNING; 848170530Ssam } 849170530Ssam return 1; 850170530Ssam} 851170530Ssam 852170530Ssam/* 853170530Ssam * Default method for stopping A-MPDU tx aggregation. 854170530Ssam * Any timer is cleared and we drain any pending frames. 855170530Ssam */ 856170530Ssamstatic void 857170530Ssamieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) 858170530Ssam{ 859170530Ssam /* XXX locking */ 860170530Ssam addba_stop_timeout(tap); 861170530Ssam if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { 862170530Ssam /* clear aggregation queue */ 863170530Ssam ieee80211_drain_ifq(&tap->txa_q); 864170530Ssam tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; 865170530Ssam } 866170530Ssam tap->txa_attempts = 0; 867170530Ssam} 868170530Ssam 869170530Ssam/* 870170530Ssam * Process a received action frame using the default aggregation 871170530Ssam * policy. We intercept ADDBA-related frames and use them to 872170530Ssam * update our aggregation state. All other frames are passed up 873170530Ssam * for processing by ieee80211_recv_action. 874170530Ssam */ 875170530Ssamstatic void 876170530Ssamieee80211_aggr_recv_action(struct ieee80211_node *ni, 877170530Ssam const uint8_t *frm, const uint8_t *efrm) 878170530Ssam{ 879170530Ssam struct ieee80211com *ic = ni->ni_ic; 880170530Ssam const struct ieee80211_action *ia; 881170530Ssam struct ieee80211_rx_ampdu *rap; 882170530Ssam struct ieee80211_tx_ampdu *tap; 883170530Ssam uint8_t dialogtoken; 884170530Ssam uint16_t baparamset, batimeout, baseqctl, code; 885170530Ssam uint16_t args[4]; 886170530Ssam int tid, ac, bufsiz; 887170530Ssam 888170530Ssam ia = (const struct ieee80211_action *) frm; 889170530Ssam switch (ia->ia_category) { 890170530Ssam case IEEE80211_ACTION_CAT_BA: 891170530Ssam switch (ia->ia_action) { 892170530Ssam case IEEE80211_ACTION_BA_ADDBA_REQUEST: 893170530Ssam dialogtoken = frm[2]; 894170530Ssam baparamset = LE_READ_2(frm+3); 895170530Ssam batimeout = LE_READ_2(frm+5); 896170530Ssam baseqctl = LE_READ_2(frm+7); 897170530Ssam 898170530Ssam tid = MS(baparamset, IEEE80211_BAPS_TID); 899170530Ssam bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 900170530Ssam 901170530Ssam IEEE80211_NOTE(ic, 902170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 903170530Ssam "recv ADDBA request: dialogtoken %u " 904170530Ssam "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " 905170530Ssam "baseqctl %d", 906170530Ssam dialogtoken, baparamset, tid, bufsiz, 907170530Ssam batimeout, baseqctl); 908170530Ssam 909170530Ssam rap = &ni->ni_rx_ampdu[tid]; 910170530Ssam 911170530Ssam /* Send ADDBA response */ 912170530Ssam args[0] = dialogtoken; 913170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) { 914170530Ssam ampdu_rx_start(rap, bufsiz, 915170530Ssam MS(baseqctl, IEEE80211_BASEQ_START)); 916170530Ssam 917170530Ssam args[1] = IEEE80211_STATUS_SUCCESS; 918170530Ssam } else 919170530Ssam args[1] = IEEE80211_STATUS_UNSPECIFIED; 920170530Ssam /* XXX honor rap flags? */ 921170530Ssam args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE 922170530Ssam | SM(tid, IEEE80211_BAPS_TID) 923170530Ssam | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) 924170530Ssam ; 925170530Ssam args[3] = 0; 926170530Ssam ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 927170530Ssam IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); 928170530Ssam return; 929170530Ssam 930170530Ssam case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 931170530Ssam dialogtoken = frm[2]; 932170530Ssam code = LE_READ_2(frm+3); 933170530Ssam baparamset = LE_READ_2(frm+5); 934170530Ssam tid = MS(baparamset, IEEE80211_BAPS_TID); 935170530Ssam bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 936170530Ssam batimeout = LE_READ_2(frm+7); 937170530Ssam 938170530Ssam IEEE80211_NOTE(ic, 939170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 940170530Ssam "recv ADDBA response: dialogtoken %u code %d " 941170530Ssam "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", 942170530Ssam dialogtoken, code, baparamset, tid, bufsiz, 943170530Ssam batimeout); 944170530Ssam 945170530Ssam ac = TID_TO_WME_AC(tid); 946170530Ssam tap = &ni->ni_tx_ampdu[ac]; 947170530Ssam 948170530Ssam ic->ic_addba_response(ni, tap, 949170530Ssam code, baparamset, batimeout); 950170530Ssam return; 951170530Ssam 952170530Ssam case IEEE80211_ACTION_BA_DELBA: 953170530Ssam baparamset = LE_READ_2(frm+2); 954170530Ssam code = LE_READ_2(frm+4); 955170530Ssam 956170530Ssam tid = MS(baparamset, IEEE80211_DELBAPS_TID); 957170530Ssam 958170530Ssam IEEE80211_NOTE(ic, 959170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 960170530Ssam "recv DELBA: baparamset 0x%x (tid %d initiator %d) " 961170530Ssam "code %d", baparamset, tid, 962170530Ssam MS(baparamset, IEEE80211_DELBAPS_INIT), code); 963170530Ssam 964170530Ssam if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { 965170530Ssam ac = TID_TO_WME_AC(tid); 966170530Ssam tap = &ni->ni_tx_ampdu[ac]; 967170530Ssam ic->ic_addba_stop(ni, tap); 968170530Ssam } else { 969170530Ssam rap = &ni->ni_rx_ampdu[tid]; 970170530Ssam ampdu_rx_stop(rap); 971170530Ssam } 972170530Ssam return; 973170530Ssam } 974170530Ssam break; 975170530Ssam } 976170530Ssam return ieee80211_recv_action(ni, frm, efrm); 977170530Ssam} 978170530Ssam 979170530Ssam/* 980170530Ssam * Process a received 802.11n action frame. 981170530Ssam * Aggregation-related frames are assumed to be handled 982170530Ssam * already; we handle any other frames we can, otherwise 983170530Ssam * complain about being unsupported (with debugging). 984170530Ssam */ 985170530Ssamvoid 986170530Ssamieee80211_recv_action(struct ieee80211_node *ni, 987170530Ssam const uint8_t *frm, const uint8_t *efrm) 988170530Ssam{ 989170530Ssam struct ieee80211com *ic = ni->ni_ic; 990170530Ssam const struct ieee80211_action *ia; 991170530Ssam int chw; 992170530Ssam 993170530Ssam ia = (const struct ieee80211_action *) frm; 994170530Ssam switch (ia->ia_category) { 995170530Ssam case IEEE80211_ACTION_CAT_BA: 996170530Ssam IEEE80211_NOTE(ic, 997170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 998170530Ssam "%s: BA action %d not implemented", __func__, 999170530Ssam ia->ia_action); 1000170530Ssam ic->ic_stats.is_rx_mgtdiscard++; 1001170530Ssam break; 1002170530Ssam case IEEE80211_ACTION_CAT_HT: 1003170530Ssam switch (ia->ia_action) { 1004170530Ssam case IEEE80211_ACTION_HT_TXCHWIDTH: 1005170530Ssam chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20; 1006170530Ssam if (chw != ni->ni_chw) { 1007170530Ssam ni->ni_chw = chw; 1008170530Ssam ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; 1009170530Ssam } 1010170530Ssam IEEE80211_NOTE(ic, 1011170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1012170530Ssam "%s: HT txchwidth. width %d (%s)", 1013170530Ssam __func__, chw, 1014170530Ssam ni->ni_flags & IEEE80211_NODE_CHWUPDATE ? 1015170530Ssam "new" : "no change"); 1016170530Ssam break; 1017170530Ssam default: 1018170530Ssam IEEE80211_NOTE(ic, 1019170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1020170530Ssam "%s: HT action %d not implemented", __func__, 1021170530Ssam ia->ia_action); 1022170530Ssam ic->ic_stats.is_rx_mgtdiscard++; 1023170530Ssam break; 1024170530Ssam } 1025170530Ssam break; 1026170530Ssam default: 1027170530Ssam IEEE80211_NOTE(ic, 1028170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1029170530Ssam "%s: category %d not implemented", __func__, 1030170530Ssam ia->ia_category); 1031170530Ssam ic->ic_stats.is_rx_mgtdiscard++; 1032170530Ssam break; 1033170530Ssam } 1034170530Ssam} 1035170530Ssam 1036170530Ssam/* 1037170530Ssam * Transmit processing. 1038170530Ssam */ 1039170530Ssam 1040170530Ssam/* 1041170530Ssam * Request A-MPDU tx aggregation. Setup local state and 1042170530Ssam * issue an ADDBA request. BA use will only happen after 1043170530Ssam * the other end replies with ADDBA response. 1044170530Ssam */ 1045170530Ssamint 1046170530Ssamieee80211_ampdu_request(struct ieee80211_node *ni, 1047170530Ssam struct ieee80211_tx_ampdu *tap) 1048170530Ssam{ 1049170530Ssam struct ieee80211com *ic = ni->ni_ic; 1050170530Ssam uint16_t args[4]; 1051170530Ssam int tid, dialogtoken; 1052170530Ssam static int tokens = 0; /* XXX */ 1053170530Ssam 1054170530Ssam /* XXX locking */ 1055170530Ssam if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { 1056170530Ssam /* do deferred setup of state */ 1057170530Ssam /* XXX tap->txa_q */ 1058170530Ssam callout_init(&tap->txa_timer, CALLOUT_MPSAFE); 1059170530Ssam tap->txa_flags |= IEEE80211_AGGR_SETUP; 1060170530Ssam } 1061170530Ssam if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES && 1062170530Ssam (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) { 1063170530Ssam /* 1064170530Ssam * Don't retry too often; IEEE80211_AGGR_MINRETRY 1065170530Ssam * defines the minimum interval we'll retry after 1066170530Ssam * IEEE80211_AGGR_MAXTRIES failed attempts to 1067170530Ssam * negotiate use. 1068170530Ssam */ 1069170530Ssam return 0; 1070170530Ssam } 1071170530Ssam dialogtoken = (tokens+1) % 63; /* XXX */ 1072170530Ssam 1073170530Ssam tid = WME_AC_TO_TID(tap->txa_ac); 1074170530Ssam args[0] = dialogtoken; 1075170530Ssam args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE 1076170530Ssam | SM(tid, IEEE80211_BAPS_TID) 1077170530Ssam | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) 1078170530Ssam ; 1079170530Ssam args[2] = 0; /* batimeout */ 1080170530Ssam args[3] = SM(0, IEEE80211_BASEQ_START) 1081170530Ssam | SM(0, IEEE80211_BASEQ_FRAG) 1082170530Ssam ; 1083170530Ssam /* NB: do first so there's no race against reply */ 1084170530Ssam if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { 1085170530Ssam /* unable to setup state, don't make request */ 1086170530Ssam return 0; 1087170530Ssam } 1088170530Ssam tokens = dialogtoken; /* allocate token */ 1089170530Ssam return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 1090170530Ssam IEEE80211_ACTION_BA_ADDBA_REQUEST, args); 1091170530Ssam} 1092170530Ssam 1093170530Ssam/* 1094170530Ssam * Transmit a BAR frame to the specified node. The 1095170530Ssam * BAR contents are drawn from the supplied aggregation 1096170530Ssam * state associated with the node. 1097170530Ssam */ 1098170530Ssamint 1099170530Ssamieee80211_send_bar(struct ieee80211_node *ni, 1100170530Ssam const struct ieee80211_tx_ampdu *tap) 1101170530Ssam{ 1102170530Ssam#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 1103170530Ssam#define ADDSHORT(frm, v) do { \ 1104170530Ssam frm[0] = (v) & 0xff; \ 1105170530Ssam frm[1] = (v) >> 8; \ 1106170530Ssam frm += 2; \ 1107170530Ssam} while (0) 1108170530Ssam struct ieee80211com *ic = ni->ni_ic; 1109170530Ssam struct ifnet *ifp = ic->ic_ifp; 1110170530Ssam struct ieee80211_frame_min *wh; 1111170530Ssam struct mbuf *m; 1112170530Ssam uint8_t *frm; 1113170530Ssam uint16_t barctl, barseqctl; 1114170530Ssam int tid, ret; 1115170530Ssam 1116170530Ssam ieee80211_ref_node(ni); 1117170530Ssam 1118170530Ssam m = ieee80211_getmgtframe(&frm, 1119170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame_min), 1120170530Ssam sizeof(struct ieee80211_ba_request) 1121170530Ssam ); 1122170530Ssam if (m == NULL) 1123170530Ssam senderr(ENOMEM, is_tx_nobuf); 1124170530Ssam 1125170530Ssam wh = mtod(m, struct ieee80211_frame_min *); 1126170530Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | 1127170530Ssam IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; 1128170530Ssam wh->i_fc[1] = 0; 1129170530Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 1130170530Ssam IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 1131170530Ssam 1132170530Ssam tid = WME_AC_TO_TID(tap->txa_ac); 1133170530Ssam barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 1134170530Ssam IEEE80211_BAPS_POLICY_IMMEDIATE : 1135170530Ssam IEEE80211_BAPS_POLICY_DELAYED) 1136170530Ssam | SM(tid, IEEE80211_BAPS_TID) 1137170530Ssam | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ) 1138170530Ssam ; 1139170530Ssam barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START) 1140170530Ssam | SM(0, IEEE80211_BASEQ_FRAG) 1141170530Ssam ; 1142170530Ssam ADDSHORT(frm, barctl); 1143170530Ssam ADDSHORT(frm, barseqctl); 1144170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1145170530Ssam 1146170530Ssam IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ 1147170530Ssam 1148170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 1149170530Ssam "[%s] send bar frame (tid %u start %u) on channel %u\n", 1150170530Ssam ether_sprintf(ni->ni_macaddr), tid, tap->txa_start, 1151170530Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 1152170530Ssam 1153170530Ssam m->m_pkthdr.rcvif = (void *)ni; 1154170530Ssam IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ 1155170530Ssam (*ifp->if_start)(ifp); 1156170530Ssam 1157170530Ssam return 0; 1158170530Ssambad: 1159170530Ssam ieee80211_free_node(ni); 1160170530Ssam return ret; 1161170530Ssam#undef ADDSHORT 1162170530Ssam#undef senderr 1163170530Ssam} 1164170530Ssam 1165170530Ssam/* 1166170530Ssam * Send an action management frame. The arguments are stuff 1167170530Ssam * into a frame without inspection; the caller is assumed to 1168170530Ssam * prepare them carefully (e.g. based on the aggregation state). 1169170530Ssam */ 1170170530Ssamint 1171170530Ssamieee80211_send_action(struct ieee80211_node *ni, 1172170530Ssam int category, int action, uint16_t args[4]) 1173170530Ssam{ 1174170530Ssam#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 1175170530Ssam#define ADDSHORT(frm, v) do { \ 1176170530Ssam frm[0] = (v) & 0xff; \ 1177170530Ssam frm[1] = (v) >> 8; \ 1178170530Ssam frm += 2; \ 1179170530Ssam} while (0) 1180170530Ssam struct ieee80211com *ic = ni->ni_ic; 1181170530Ssam struct mbuf *m; 1182170530Ssam uint8_t *frm; 1183170530Ssam uint16_t baparamset; 1184170530Ssam int ret; 1185170530Ssam 1186170530Ssam KASSERT(ni != NULL, ("null node")); 1187170530Ssam 1188170530Ssam /* 1189170530Ssam * Hold a reference on the node so it doesn't go away until after 1190170530Ssam * the xmit is complete all the way in the driver. On error we 1191170530Ssam * will remove our reference. 1192170530Ssam */ 1193170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 1194170530Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1195170530Ssam __func__, __LINE__, 1196170530Ssam ni, ether_sprintf(ni->ni_macaddr), 1197170530Ssam ieee80211_node_refcnt(ni)+1); 1198170530Ssam ieee80211_ref_node(ni); 1199170530Ssam 1200170530Ssam m = ieee80211_getmgtframe(&frm, 1201170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1202170530Ssam sizeof(uint16_t) /* action+category */ 1203170530Ssam /* XXX may action payload */ 1204170530Ssam + sizeof(struct ieee80211_action_ba_addbaresponse) 1205170530Ssam ); 1206170530Ssam if (m == NULL) 1207170530Ssam senderr(ENOMEM, is_tx_nobuf); 1208170530Ssam 1209170530Ssam *frm++ = category; 1210170530Ssam *frm++ = action; 1211170530Ssam switch (category) { 1212170530Ssam case IEEE80211_ACTION_CAT_BA: 1213170530Ssam switch (action) { 1214170530Ssam case IEEE80211_ACTION_BA_ADDBA_REQUEST: 1215170530Ssam IEEE80211_NOTE(ic, 1216170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1217170530Ssam "send ADDBA request: tid %d, baparamset 0x%x", 1218170530Ssam args[0], args[1]); 1219170530Ssam 1220170530Ssam *frm++ = args[0]; /* dialog token */ 1221170530Ssam ADDSHORT(frm, args[1]); /* baparamset */ 1222170530Ssam ADDSHORT(frm, args[2]); /* batimeout */ 1223170530Ssam ADDSHORT(frm, args[3]); /* baseqctl */ 1224170530Ssam break; 1225170530Ssam case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 1226170530Ssam IEEE80211_NOTE(ic, 1227170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1228170530Ssam "send ADDBA response: dialogtoken %d status %d " 1229170530Ssam "baparamset 0x%x (tid %d) batimeout %d", 1230170530Ssam args[0], args[1], args[2], 1231170530Ssam MS(args[2], IEEE80211_BAPS_TID), args[3]); 1232170530Ssam 1233170530Ssam *frm++ = args[0]; /* dialog token */ 1234170530Ssam ADDSHORT(frm, args[1]); /* statuscode */ 1235170530Ssam ADDSHORT(frm, args[2]); /* baparamset */ 1236170530Ssam ADDSHORT(frm, args[3]); /* batimeout */ 1237170530Ssam break; 1238170530Ssam case IEEE80211_ACTION_BA_DELBA: 1239170530Ssam /* XXX */ 1240170530Ssam baparamset = SM(args[0], IEEE80211_DELBAPS_TID) 1241170530Ssam | SM(args[1], IEEE80211_DELBAPS_INIT) 1242170530Ssam ; 1243170530Ssam ADDSHORT(frm, baparamset); 1244170530Ssam ADDSHORT(frm, args[2]); /* reason code */ 1245170530Ssam 1246170530Ssam IEEE80211_NOTE(ic, 1247170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1248170530Ssam "send DELBA action: tid %d, initiator %d reason %d", 1249170530Ssam args[0], args[1], args[2]); 1250170530Ssam break; 1251170530Ssam default: 1252170530Ssam goto badaction; 1253170530Ssam } 1254170530Ssam break; 1255170530Ssam case IEEE80211_ACTION_CAT_HT: 1256170530Ssam switch (action) { 1257170530Ssam case IEEE80211_ACTION_HT_TXCHWIDTH: 1258170530Ssam IEEE80211_NOTE(ic, 1259170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1260170530Ssam ni, "send HT txchwidth: width %d", 1261170530Ssam IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 1262170530Ssam ); 1263170530Ssam *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 1264170530Ssam IEEE80211_A_HT_TXCHWIDTH_2040 : 1265170530Ssam IEEE80211_A_HT_TXCHWIDTH_20; 1266170530Ssam break; 1267170530Ssam default: 1268170530Ssam goto badaction; 1269170530Ssam } 1270170530Ssam break; 1271170530Ssam default: 1272170530Ssam badaction: 1273170530Ssam IEEE80211_NOTE(ic, 1274170530Ssam IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1275170530Ssam "%s: unsupported category %d action %d", __func__, 1276170530Ssam category, action); 1277170530Ssam senderr(EINVAL, is_tx_unknownmgt); 1278170530Ssam /* NOTREACHED */ 1279170530Ssam } 1280170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1281170530Ssam 1282170530Ssam ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION); 1283170530Ssam if (ret != 0) 1284170530Ssam goto bad; 1285170530Ssam return 0; 1286170530Ssambad: 1287170530Ssam ieee80211_free_node(ni); 1288170530Ssam return ret; 1289170530Ssam#undef ADDSHORT 1290170530Ssam#undef senderr 1291170530Ssam} 1292170530Ssam 1293170530Ssam/* 1294170530Ssam * Construct the MCS bit mask for inclusion 1295170530Ssam * in an HT information element. 1296170530Ssam */ 1297170530Ssamstatic void 1298170530Ssamieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1299170530Ssam{ 1300170530Ssam int i; 1301170530Ssam 1302170530Ssam for (i = 0; i < rs->rs_nrates; i++) { 1303170530Ssam int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1304170530Ssam if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ 1305170530Ssam /* NB: this assumes a particular implementation */ 1306170530Ssam setbit(frm, r); 1307170530Ssam } 1308170530Ssam } 1309170530Ssam} 1310170530Ssam 1311170530Ssam/* 1312170530Ssam * Add body of an HTCAP information element. 1313170530Ssam */ 1314170530Ssamstatic uint8_t * 1315170530Ssamieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) 1316170530Ssam{ 1317170530Ssam#define ADDSHORT(frm, v) do { \ 1318170530Ssam frm[0] = (v) & 0xff; \ 1319170530Ssam frm[1] = (v) >> 8; \ 1320170530Ssam frm += 2; \ 1321170530Ssam} while (0) 1322170530Ssam struct ieee80211com *ic = ni->ni_ic; 1323170530Ssam uint16_t caps; 1324170530Ssam 1325170530Ssam /* HT capabilities */ 1326170530Ssam caps = ic->ic_htcaps & 0xffff; 1327170530Ssam /* override 20/40 use based on channel and config */ 1328170530Ssam if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && 1329170530Ssam (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) 1330170530Ssam caps |= IEEE80211_HTCAP_CHWIDTH40; 1331170530Ssam else 1332170530Ssam caps &= ~IEEE80211_HTCAP_CHWIDTH40; 1333170530Ssam /* adjust short GI based on channel and config */ 1334170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) 1335170530Ssam caps &= ~IEEE80211_HTCAP_SHORTGI20; 1336170530Ssam if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || 1337170530Ssam (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) 1338170530Ssam caps &= ~IEEE80211_HTCAP_SHORTGI40; 1339170530Ssam ADDSHORT(frm, caps); 1340170530Ssam 1341170530Ssam /* HT parameters */ 1342170530Ssam switch (ic->ic_ampdu_rxmax / 1024) { 1343170530Ssam case 8: *frm = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; 1344170530Ssam case 16: *frm = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; 1345170530Ssam case 32: *frm = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; 1346170530Ssam default: *frm = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; 1347170530Ssam } 1348170530Ssam *frm |= SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY); 1349170530Ssam frm++; 1350170530Ssam 1351170530Ssam /* pre-zero remainder of ie */ 1352170530Ssam memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - 1353170530Ssam __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); 1354170530Ssam 1355170530Ssam /* supported MCS set */ 1356170530Ssam ieee80211_set_htrates(frm, &ni->ni_htrates); 1357170530Ssam 1358170530Ssam frm += sizeof(struct ieee80211_ie_htcap) - 1359170530Ssam __offsetof(struct ieee80211_ie_htcap, hc_mcsset); 1360170530Ssam return frm; 1361170530Ssam#undef ADDSHORT 1362170530Ssam} 1363170530Ssam 1364170530Ssam/* 1365170530Ssam * Add 802.11n HT capabilities information element 1366170530Ssam */ 1367170530Ssamuint8_t * 1368170530Ssamieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) 1369170530Ssam{ 1370170530Ssam frm[0] = IEEE80211_ELEMID_HTCAP; 1371170530Ssam frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; 1372170530Ssam return ieee80211_add_htcap_body(frm + 2, ni); 1373170530Ssam} 1374170530Ssam 1375170530Ssam/* 1376170530Ssam * Add Broadcom OUI wrapped standard HTCAP ie; this is 1377170530Ssam * used for compatibility w/ pre-draft implementations. 1378170530Ssam */ 1379170530Ssamuint8_t * 1380170530Ssamieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) 1381170530Ssam{ 1382170530Ssam frm[0] = IEEE80211_ELEMID_VENDOR; 1383170530Ssam frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; 1384170530Ssam frm[2] = (BCM_OUI >> 0) & 0xff; 1385170530Ssam frm[3] = (BCM_OUI >> 8) & 0xff; 1386170530Ssam frm[4] = (BCM_OUI >> 16) & 0xff; 1387170530Ssam frm[5] = BCM_OUI_HTCAP; 1388170530Ssam return ieee80211_add_htcap_body(frm + 6, ni); 1389170530Ssam} 1390170530Ssam 1391170530Ssam/* 1392170530Ssam * Construct the MCS bit mask of basic rates 1393170530Ssam * for inclusion in an HT information element. 1394170530Ssam */ 1395170530Ssamstatic void 1396170530Ssamieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1397170530Ssam{ 1398170530Ssam int i; 1399170530Ssam 1400170530Ssam for (i = 0; i < rs->rs_nrates; i++) { 1401170530Ssam int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1402170530Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && 1403170530Ssam r < IEEE80211_HTRATE_MAXSIZE) { 1404170530Ssam /* NB: this assumes a particular implementation */ 1405170530Ssam setbit(frm, r); 1406170530Ssam } 1407170530Ssam } 1408170530Ssam} 1409170530Ssam 1410170530Ssam/* 1411170530Ssam * Add body of an HTINFO information element. 1412170530Ssam */ 1413170530Ssamstatic uint8_t * 1414170530Ssamieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) 1415170530Ssam{ 1416170530Ssam struct ieee80211com *ic = ni->ni_ic; 1417170530Ssam 1418170530Ssam /* pre-zero remainder of ie */ 1419170530Ssam memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); 1420170530Ssam 1421170530Ssam /* primary/control channel center */ 1422170530Ssam *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); 1423170530Ssam 1424170530Ssam frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; 1425170530Ssam if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) 1426170530Ssam frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 1427170530Ssam else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) 1428170530Ssam frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; 1429170530Ssam else 1430170530Ssam frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; 1431170530Ssam if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) 1432170530Ssam frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; 1433170530Ssam 1434170530Ssam frm[1] = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) ? 1435170530Ssam IEEE80211_HTINFO_OPMODE_PURE : IEEE80211_HTINFO_OPMODE_MIXED; 1436170530Ssam /* XXX IEEE80211_HTINFO_NONHT_PRESENT */ 1437170530Ssam 1438170530Ssam frm += 5; 1439170530Ssam 1440170530Ssam /* basic MCS set */ 1441170530Ssam ieee80211_set_basic_htrates(frm, &ni->ni_htrates); 1442170530Ssam frm += sizeof(struct ieee80211_ie_htinfo) - 1443170530Ssam __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); 1444170530Ssam return frm; 1445170530Ssam} 1446170530Ssam 1447170530Ssam/* 1448170530Ssam * Add 802.11n HT information information element. 1449170530Ssam */ 1450170530Ssamuint8_t * 1451170530Ssamieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) 1452170530Ssam{ 1453170530Ssam frm[0] = IEEE80211_ELEMID_HTINFO; 1454170530Ssam frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; 1455170530Ssam return ieee80211_add_htinfo_body(frm + 2, ni); 1456170530Ssam} 1457170530Ssam 1458170530Ssam/* 1459170530Ssam * Add Broadcom OUI wrapped standard HTINFO ie; this is 1460170530Ssam * used for compatibility w/ pre-draft implementations. 1461170530Ssam */ 1462170530Ssamuint8_t * 1463170530Ssamieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) 1464170530Ssam{ 1465170530Ssam frm[0] = IEEE80211_ELEMID_VENDOR; 1466170530Ssam frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; 1467170530Ssam frm[2] = (BCM_OUI >> 0) & 0xff; 1468170530Ssam frm[3] = (BCM_OUI >> 8) & 0xff; 1469170530Ssam frm[4] = (BCM_OUI >> 16) & 0xff; 1470170530Ssam frm[5] = BCM_OUI_HTINFO; 1471170530Ssam return ieee80211_add_htinfo_body(frm + 6, ni); 1472170530Ssam} 1473