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