ieee80211_ht.c revision 172211
1193323Sed/*- 2193323Sed * Copyright (c) 2007 Sam Leffler, Errno Consulting 3193323Sed * All rights reserved. 4193323Sed * 5193323Sed * Redistribution and use in source and binary forms, with or without 6193323Sed * modification, are permitted provided that the following conditions 7193323Sed * are met: 8193323Sed * 1. Redistributions of source code must retain the above copyright 9193323Sed * notice, this list of conditions and the following disclaimer. 10193323Sed * 2. Redistributions in binary form must reproduce the above copyright 11193323Sed * notice, this list of conditions and the following disclaimer in the 12193323Sed * documentation and/or other materials provided with the distribution. 13194612Sed * 14198090Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15198090Srdivacky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16204642Srdivacky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17198090Srdivacky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18198090Srdivacky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19198090Srdivacky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20198090Srdivacky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21198090Srdivacky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22198090Srdivacky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23198090Srdivacky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24198090Srdivacky */ 25198090Srdivacky 26198090Srdivacky#include <sys/cdefs.h> 27198090Srdivacky#ifdef __FreeBSD__ 28198090Srdivacky__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 172211 2007-09-17 19:07:24Z sam $"); 29194612Sed#endif 30194612Sed 31194612Sed/* 32195098Sed * IEEE 802.11n protocol support. 33194612Sed */ 34195098Sed 35194612Sed#include "opt_inet.h" 36194612Sed 37194612Sed#include <sys/param.h> 38194754Sed#include <sys/kernel.h> 39194754Sed#include <sys/systm.h> 40198090Srdivacky#include <sys/endian.h> 41194612Sed 42194612Sed#include <sys/socket.h> 43194754Sed 44194754Sed#include <net/if.h> 45198090Srdivacky#include <net/if_media.h> 46194754Sed#include <net/ethernet.h> 47194612Sed 48194754Sed#include <net80211/ieee80211_var.h> 49194754Sed 50194754Sed/* define here, used throughout file */ 51194754Sed#define MS(_v, _f) (((_v) & _f) >> _f##_S) 52199989Srdivacky#define SM(_v, _f) (((_v) << _f##_S) & _f) 53199989Srdivacky 54194754Sed/* XXX need max array size */ 55194754Sedconst int ieee80211_htrates[16] = { 56204642Srdivacky 13, /* IFM_IEEE80211_MCS0 */ 57198090Srdivacky 26, /* IFM_IEEE80211_MCS1 */ 58194612Sed 39, /* IFM_IEEE80211_MCS2 */ 59204642Srdivacky 52, /* IFM_IEEE80211_MCS3 */ 60194754Sed 78, /* IFM_IEEE80211_MCS4 */ 61194754Sed 104, /* IFM_IEEE80211_MCS5 */ 62194754Sed 117, /* IFM_IEEE80211_MCS6 */ 63198090Srdivacky 130, /* IFM_IEEE80211_MCS7 */ 64198090Srdivacky 26, /* IFM_IEEE80211_MCS8 */ 65194612Sed 52, /* IFM_IEEE80211_MCS9 */ 66194754Sed 78, /* IFM_IEEE80211_MCS10 */ 67194754Sed 104, /* IFM_IEEE80211_MCS11 */ 68194754Sed 156, /* IFM_IEEE80211_MCS12 */ 69198090Srdivacky 208, /* IFM_IEEE80211_MCS13 */ 70198090Srdivacky 234, /* IFM_IEEE80211_MCS14 */ 71194754Sed 260, /* IFM_IEEE80211_MCS15 */ 72198396Srdivacky}; 73198396Srdivacky 74198396Srdivackystatic const struct ieee80211_htrateset ieee80211_rateset_11n = 75198396Srdivacky { 16, { 76198396Srdivacky /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */ 77198396Srdivacky 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 78198396Srdivacky /* 39 52 78 104 117, 130 */ 79198396Srdivacky 10, 11, 12, 13, 14, 15 } 80198396Srdivacky }; 81198396Srdivacky 82198396Srdivacky#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250) 83198396Srdivacky#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000) 84198396Srdivacky#define IEEE80211_AGGR_MAXTRIES 3 85198396Srdivacky 86198396Srdivackystatic int ieee80211_addba_request(struct ieee80211_node *ni, 87198396Srdivacky struct ieee80211_tx_ampdu *tap, 88198396Srdivacky int dialogtoken, int baparamset, int batimeout); 89198396Srdivackystatic int ieee80211_addba_response(struct ieee80211_node *ni, 90198396Srdivacky struct ieee80211_tx_ampdu *tap, 91199989Srdivacky int code, int baparamset, int batimeout); 92199989Srdivackystatic void ieee80211_addba_stop(struct ieee80211_node *ni, 93199989Srdivacky struct ieee80211_tx_ampdu *tap); 94199989Srdivackystatic void ieee80211_aggr_recv_action(struct ieee80211_node *ni, 95199989Srdivacky const uint8_t *frm, const uint8_t *efrm); 96199989Srdivacky 97199989Srdivackyvoid 98199989Srdivackyieee80211_ht_attach(struct ieee80211com *ic) 99199989Srdivacky{ 100199989Srdivacky 101199989Srdivacky ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; 102199989Srdivacky ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; 103199989Srdivacky ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; 104199989Srdivacky 105199989Srdivacky ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; 106195098Sed 107195098Sed /* setup default aggregation policy */ 108195098Sed ic->ic_recv_action = ieee80211_aggr_recv_action; 109195098Sed ic->ic_send_action = ieee80211_send_action; 110195098Sed ic->ic_addba_request = ieee80211_addba_request; 111194754Sed ic->ic_addba_response = ieee80211_addba_response; 112198090Srdivacky ic->ic_addba_stop = ieee80211_addba_stop; 113198090Srdivacky 114194754Sed if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || 115194754Sed isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { 116194754Sed /* 117204642Srdivacky * There are HT channels in the channel list; enable 118204642Srdivacky * all HT-related facilities by default. 119204642Srdivacky * XXX these choices may be too aggressive. 120194612Sed */ 121198090Srdivacky ic->ic_flags_ext |= IEEE80211_FEXT_HT 122198090Srdivacky | IEEE80211_FEXT_HTCOMPAT 123204642Srdivacky ; 124194612Sed if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) 125195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; 126195340Sed /* XXX infer from channel list */ 127195340Sed if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { 128195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; 129195340Sed if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) 130195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; 131195340Sed } 132195340Sed /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ 133195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; 134204792Srdivacky if (ic->ic_htcaps & IEEE80211_HTC_AMPDU) 135195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; 136195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; 137195340Sed if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) 138195340Sed ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; 139195340Sed 140195340Sed ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; 141195340Sed } 142195340Sed} 143195340Sed 144195340Sedvoid 145195340Sedieee80211_ht_detach(struct ieee80211com *ic) 146198090Srdivacky{ 147195340Sed} 148195340Sed 149198090Srdivackystatic void 150195340Sedht_announce(struct ieee80211com *ic, int mode, 151195340Sed const struct ieee80211_htrateset *rs) 152195340Sed{ 153205218Srdivacky struct ifnet *ifp = ic->ic_ifp; 154205218Srdivacky int i, rate, mword; 155205218Srdivacky 156205218Srdivacky if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); 157198090Srdivacky for (i = 0; i < rs->rs_nrates; i++) { 158195340Sed mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); 159195340Sed if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) 160195340Sed continue; 161195340Sed rate = ieee80211_htrates[rs->rs_rates[i]]; 162195340Sed printf("%s%d%sMbps", (i != 0 ? " " : ""), 163195340Sed rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 164195340Sed } 165194612Sed printf("\n"); 166195098Sed} 167194612Sed 168194612Sedvoid 169195098Sedieee80211_ht_announce(struct ieee80211com *ic) 170195098Sed{ 171195098Sed if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) 172201360Srdivacky ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); 173201360Srdivacky if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) 174194612Sed ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); 175198090Srdivacky} 176198892Srdivacky 177195098Sedconst struct ieee80211_htrateset * 178195098Sedieee80211_get_suphtrates(struct ieee80211com *ic, 179195098Sed const struct ieee80211_channel *c) 180201360Srdivacky{ 181201360Srdivacky if (IEEE80211_IS_CHAN_HT(c)) 182201360Srdivacky return &ieee80211_rateset_11n; 183201360Srdivacky /* XXX what's the right thing to do here? */ 184201360Srdivacky return (const struct ieee80211_htrateset *) 185201360Srdivacky ieee80211_get_suprates(ic, c); 186195098Sed} 187195098Sed 188198090Srdivacky/* 189198892Srdivacky * Receive processing. 190201360Srdivacky */ 191201360Srdivacky 192201360Srdivacky/* 193201360Srdivacky * Decap the encapsulated A-MSDU frames and dispatch all but 194201360Srdivacky * the last for delivery. The last frame is returned for 195201360Srdivacky * delivery via the normal path. 196201360Srdivacky */ 197201360Srdivackystruct mbuf * 198201360Srdivackyieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) 199201360Srdivacky{ 200194612Sed struct ieee80211com *ic = ni->ni_ic; 201198090Srdivacky int totallen, framelen; 202198892Srdivacky struct mbuf *n; 203201360Srdivacky 204201360Srdivacky /* discard 802.3 header inserted by ieee80211_decap */ 205201360Srdivacky m_adj(m, sizeof(struct ether_header)); 206201360Srdivacky 207201360Srdivacky ic->ic_stats.is_amsdu_decap++; 208201360Srdivacky 209201360Srdivacky totallen = m->m_pkthdr.len; 210194612Sed for (;;) { 211194612Sed /* 212195098Sed * Decap the first frame, bust it apart from the 213195098Sed * remainder and deliver. We leave the last frame 214195098Sed * delivery to the caller (for consistency with other 215204642Srdivacky * code paths, could also do it here). 216198090Srdivacky */ 217195098Sed m = ieee80211_decap1(m, &framelen); 218198090Srdivacky if (m == NULL) { 219198892Srdivacky IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 220201360Srdivacky ni->ni_macaddr, "a-msdu", "%s", "first decap failed"); 221201360Srdivacky ic->ic_stats.is_amsdu_tooshort++; 222201360Srdivacky return NULL; 223201360Srdivacky } 224201360Srdivacky if (framelen == totallen) 225201360Srdivacky break; 226201360Srdivacky n = m_split(m, framelen, M_NOWAIT); 227195098Sed if (n == NULL) { 228198090Srdivacky IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, 229198892Srdivacky ni->ni_macaddr, "a-msdu", 230195098Sed "%s", "unable to split encapsulated frames"); 231195098Sed ic->ic_stats.is_amsdu_split++; 232201360Srdivacky m_freem(m); /* NB: must reclaim */ 233201360Srdivacky return NULL; 234201360Srdivacky } 235201360Srdivacky ieee80211_deliver_data(ic, ni, m); 236201360Srdivacky 237201360Srdivacky /* 238201360Srdivacky * Remove frame contents; each intermediate frame 239195098Sed * is required to be aligned to a 4-byte boundary. 240195098Sed */ 241198090Srdivacky m = n; 242198892Srdivacky m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ 243201360Srdivacky } 244201360Srdivacky return m; /* last delivered by caller */ 245201360Srdivacky} 246201360Srdivacky 247201360Srdivacky/* 248201360Srdivacky * Start A-MPDU rx/re-order processing for the specified TID. 249195098Sed */ 250195098Sedstatic void 251198090Srdivackyampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) 252198090Srdivacky{ 253201360Srdivacky memset(rap, 0, sizeof(*rap)); 254201360Srdivacky rap->rxa_wnd = (bufsiz == 0) ? 255201360Srdivacky IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 256198090Srdivacky rap->rxa_start = start; 257195098Sed rap->rxa_nxt = rap->rxa_start; 258195098Sed rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; 259195098Sed} 260201360Srdivacky 261194754Sed/* 262198090Srdivacky * Purge all frames in the A-MPDU re-order queue. 263198892Srdivacky */ 264201360Srdivackystatic void 265201360Srdivackyampdu_rx_purge(struct ieee80211_rx_ampdu *rap) 266201360Srdivacky{ 267201360Srdivacky struct mbuf *m; 268201360Srdivacky int i; 269201360Srdivacky 270201360Srdivacky for (i = 0; i < rap->rxa_wnd; i++) { 271194754Sed m = rap->rxa_m[i]; 272198090Srdivacky if (m != NULL) { 273198892Srdivacky rap->rxa_m[i] = NULL; 274201360Srdivacky rap->rxa_qbytes -= m->m_pkthdr.len; 275201360Srdivacky m_freem(m); 276201360Srdivacky if (--rap->rxa_qframes == 0) 277201360Srdivacky break; 278201360Srdivacky } 279201360Srdivacky } 280194754Sed KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 281194754Sed ("lost %u data, %u frames on ampdu rx q", 282195098Sed rap->rxa_qbytes, rap->rxa_qframes)); 283194612Sed} 284194612Sed 285201360Srdivacky/* 286201360Srdivacky * Stop A-MPDU rx processing for the specified TID. 287194612Sed */ 288198090Srdivackystatic void 289198892Srdivackyampdu_rx_stop(struct ieee80211_rx_ampdu *rap) 290201360Srdivacky{ 291201360Srdivacky rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND; 292201360Srdivacky ampdu_rx_purge(rap); 293201360Srdivacky} 294201360Srdivacky 295201360Srdivacky/* 296201360Srdivacky * Dispatch a frame from the A-MPDU reorder queue. The 297195098Sed * frame is fed back into ieee80211_input marked with an 298198090Srdivacky * M_AMPDU flag so it doesn't come back to us (it also 299198892Srdivacky * permits ieee80211_input to optimize re-processing). 300195098Sed */ 301195098Sedstatic __inline void 302201360Srdivackyampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) 303201360Srdivacky{ 304201360Srdivacky m->m_flags |= M_AMPDU; /* bypass normal processing */ 305201360Srdivacky /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */ 306201360Srdivacky (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0); 307201360Srdivacky} 308201360Srdivacky 309195098Sed/* 310194754Sed * Dispatch as many frames as possible from the re-order queue. 311198090Srdivacky * Frames will always be "at the front"; we process all frames 312198892Srdivacky * up to the first empty slot in the window. On completion we 313201360Srdivacky * cleanup state if there are still pending frames in the current 314201360Srdivacky * BA window. We assume the frame at slot 0 is already handled 315201360Srdivacky * by the caller; we always start at slot 1. 316201360Srdivacky */ 317201360Srdivackystatic void 318201360Srdivackyampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) 319194754Sed{ 320194754Sed struct ieee80211com *ic = ni->ni_ic; 321194612Sed struct mbuf *m; 322195098Sed int i; 323195098Sed 324201360Srdivacky /* flush run of frames */ 325201360Srdivacky for (i = 1; i < rap->rxa_wnd; i++) { 326194754Sed m = rap->rxa_m[i]; 327198090Srdivacky if (m == NULL) 328198892Srdivacky break; 329201360Srdivacky rap->rxa_m[i] = NULL; 330201360Srdivacky rap->rxa_qbytes -= m->m_pkthdr.len; 331201360Srdivacky rap->rxa_qframes--; 332201360Srdivacky 333201360Srdivacky ampdu_dispatch(ni, m); 334201360Srdivacky } 335201360Srdivacky /* 336201360Srdivacky * Adjust the start of the BA window to 337195098Sed * reflect the frames just dispatched. 338204961Srdivacky */ 339204961Srdivacky rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); 340204961Srdivacky rap->rxa_nxt = rap->rxa_start; 341201360Srdivacky ic->ic_stats.is_ampdu_rx_oor += i; 342201360Srdivacky /* 343201360Srdivacky * If frames remain, copy the mbuf pointers down so 344201360Srdivacky * they correspond to the offsets in the new window. 345201360Srdivacky */ 346201360Srdivacky if (rap->rxa_qframes != 0) { 347201360Srdivacky int n = rap->rxa_qframes, j; 348195098Sed for (j = i+1; j < rap->rxa_wnd; j++) { 349198090Srdivacky if (rap->rxa_m[j] != NULL) { 350198892Srdivacky rap->rxa_m[j-i] = rap->rxa_m[j]; 351195098Sed rap->rxa_m[j] = NULL; 352195098Sed if (--n == 0) 353201360Srdivacky break; 354201360Srdivacky } 355201360Srdivacky } 356201360Srdivacky KASSERT(n == 0, ("lost %d frames", n)); 357201360Srdivacky ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; 358201360Srdivacky } 359201360Srdivacky} 360201360Srdivacky 361195098Sed/* 362194612Sed * Dispatch all frames in the A-MPDU 363198090Srdivacky * re-order queue up to the specified slot. 364198892Srdivacky */ 365201360Srdivackystatic void 366201360Srdivackyampdu_rx_flush(struct ieee80211_node *ni, 367202375Srdivacky struct ieee80211_rx_ampdu *rap, int limit) 368201360Srdivacky{ 369201360Srdivacky struct mbuf *m; 370201360Srdivacky int i; 371201360Srdivacky 372194612Sed for (i = 0; i < limit; i++) { 373194612Sed m = rap->rxa_m[i]; 374199989Srdivacky if (m == NULL) 375204642Srdivacky continue; 376199989Srdivacky rap->rxa_m[i] = NULL; 377195098Sed rap->rxa_qbytes -= m->m_pkthdr.len; 378204642Srdivacky ampdu_dispatch(ni, m); 379204642Srdivacky if (--rap->rxa_qframes == 0) 380194754Sed break; 381198090Srdivacky } 382198892Srdivacky} 383195098Sed 384204642Srdivacky/* 385201360Srdivacky * Process a received QoS data frame for an HT station. Handle 386201360Srdivacky * A-MPDU reordering: if this frame is received out of order 387201360Srdivacky * and falls within the BA window hold onto it. Otherwise if 388201360Srdivacky * this frame completes a run flush any pending frames. We 389201360Srdivacky * return 1 if the frame is consumed. A 0 is returned if 390201360Srdivacky * the frame should be processed normally by the caller. 391195098Sed */ 392198090Srdivackyint 393198892Srdivackyieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) 394195098Sed{ 395204642Srdivacky#define IEEE80211_FC0_QOSDATA \ 396195098Sed (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) 397201360Srdivacky struct ieee80211com *ic = ni->ni_ic; 398201360Srdivacky struct ieee80211_qosframe *wh; 399201360Srdivacky struct ieee80211_rx_ampdu *rap; 400201360Srdivacky ieee80211_seq rxseq; 401201360Srdivacky uint8_t tid; 402201360Srdivacky int off; 403201360Srdivacky 404195098Sed KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); 405194754Sed 406198090Srdivacky /* NB: m_len known to be sufficient */ 407198892Srdivacky wh = mtod(m, struct ieee80211_qosframe *); 408195098Sed KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data")); 409204642Srdivacky 410201360Srdivacky /* XXX 4-address frame */ 411201360Srdivacky tid = wh->i_qos[0] & IEEE80211_QOS_TID; 412201360Srdivacky rap = &ni->ni_rx_ampdu[tid]; 413201360Srdivacky if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 414201360Srdivacky /* 415204642Srdivacky * No ADDBA request yet, don't touch. 416204642Srdivacky */ 417204642Srdivacky return 0; 418204642Srdivacky } 419204642Srdivacky rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 420204642Srdivacky if (rxseq == rap->rxa_start) { 421195098Sed /* 422204642Srdivacky * First frame in window. 423204642Srdivacky */ 424204642Srdivacky if (rap->rxa_qframes != 0) { 425204642Srdivacky /* 426201360Srdivacky * Dispatch as many packets as we can. 427201360Srdivacky */ 428201360Srdivacky KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); 429201360Srdivacky ampdu_dispatch(ni, m); 430201360Srdivacky ampdu_rx_dispatch(rap, ni); 431201360Srdivacky return 1; /* NB: consumed */ 432195098Sed } else { 433204642Srdivacky /* 434204642Srdivacky * In order; advance window and notify 435204642Srdivacky * caller to dispatch directly. 436204642Srdivacky */ 437201360Srdivacky rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 438201360Srdivacky rap->rxa_nxt = rap->rxa_start; 439201360Srdivacky return 0; /* NB: process packet */ 440201360Srdivacky } 441201360Srdivacky } 442201360Srdivacky /* 443201360Srdivacky * This packet is out of order; store it 444201360Srdivacky * if it's in the BA window. 445195098Sed */ 446195098Sed /* calculate offset in BA window */ 447204642Srdivacky off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 448204642Srdivacky if (off >= rap->rxa_wnd) { 449204642Srdivacky /* 450204642Srdivacky * Outside the window, clear the q and start over. 451201360Srdivacky * 452201360Srdivacky * NB: this handles the case where rxseq is before 453201360Srdivacky * rxa_start because our max BA window is 64 454201360Srdivacky * and the sequence number range is 4096. 455195098Sed */ 456194754Sed IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, 457195098Sed "flush BA win <%u:%u> (%u frames) rxseq %u tid %u", 458204642Srdivacky rap->rxa_start, 459194754Sed IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), 460198090Srdivacky rap->rxa_qframes, rxseq, tid); 461195098Sed 462201360Srdivacky if (rap->rxa_qframes != 0) { 463194754Sed ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; 464195098Sed ampdu_rx_flush(ni, rap, rap->rxa_wnd); 465198090Srdivacky KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 466198892Srdivacky ("lost %u data, %u frames on ampdu rx q", 467201360Srdivacky rap->rxa_qbytes, rap->rxa_qframes)); 468201360Srdivacky } 469201360Srdivacky rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 470201360Srdivacky rap->rxa_nxt = rap->rxa_start; 471201360Srdivacky return 0; /* NB: process packet */ 472201360Srdivacky } 473201360Srdivacky if (rap->rxa_qframes != 0) { 474194612Sed#if 0 475195098Sed /* XXX honor batimeout? */ 476198090Srdivacky if (ticks - mn->mn_age[tid] > 50) { 477198892Srdivacky /* 478201360Srdivacky * Too long since we received the first frame; flush. 479201360Srdivacky */ 480201360Srdivacky if (rap->rxa_qframes != 0) { 481201360Srdivacky ic->ic_stats.is_ampdu_rx_oor += 482201360Srdivacky rap->rxa_qframes; 483201360Srdivacky ampdu_rx_flush(ni, rap, rap->rxa_wnd); 484194612Sed } 485194612Sed rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 486194612Sed rap->rxa_nxt = rap->rxa_start; 487195098Sed return 0; /* NB: process packet */ 488195098Sed } 489201360Srdivacky#endif 490195098Sed rap->rxa_nxt = rxseq; 491198090Srdivacky } else { 492198892Srdivacky /* 493201360Srdivacky * First frame, start aging timer. 494201360Srdivacky */ 495201360Srdivacky#if 0 496201360Srdivacky mn->mn_age[tid] = ticks; 497201360Srdivacky#endif 498201360Srdivacky } 499195098Sed /* save packet */ 500198090Srdivacky if (rap->rxa_m[off] == NULL) { 501198892Srdivacky rap->rxa_m[off] = m; 502201360Srdivacky rap->rxa_qframes++; 503201360Srdivacky rap->rxa_qbytes += m->m_pkthdr.len; 504201360Srdivacky } else { 505201360Srdivacky IEEE80211_DISCARD_MAC(ic, 506201360Srdivacky IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 507201360Srdivacky ni->ni_macaddr, "a-mpdu duplicate", 508201360Srdivacky "seqno %u tid %u BA win <%u:%u>", 509195098Sed rxseq, tid, rap->rxa_start, rap->rxa_wnd); 510194754Sed ic->ic_stats.is_rx_dup++; 511201360Srdivacky IEEE80211_NODE_STAT(ni, rx_dup); 512195098Sed m_freem(m); 513194754Sed } 514198090Srdivacky return 1; /* NB: consumed */ 515201360Srdivacky#undef IEEE80211_FC0_QOSDATA 516194754Sed} 517198090Srdivacky 518198892Srdivacky/* 519201360Srdivacky * Process a BAR ctl frame. Dispatch all frames up to 520201360Srdivacky * the sequence number of the frame. If this frame is 521201360Srdivacky * out of the window it's discarded. 522201360Srdivacky */ 523201360Srdivackyvoid 524201360Srdivackyieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) 525201360Srdivacky{ 526201360Srdivacky struct ieee80211com *ic = ni->ni_ic; 527195098Sed struct ieee80211_frame_bar *wh; 528198090Srdivacky struct ieee80211_rx_ampdu *rap; 529198892Srdivacky ieee80211_seq rxseq; 530201360Srdivacky int tid, off; 531201360Srdivacky 532201360Srdivacky wh = mtod(m0, struct ieee80211_frame_bar *); 533201360Srdivacky /* XXX check basic BAR */ 534201360Srdivacky tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); 535201360Srdivacky rap = &ni->ni_rx_ampdu[tid]; 536201360Srdivacky if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 537201360Srdivacky /* 538201360Srdivacky * No ADDBA request yet, don't touch. 539201360Srdivacky */ 540194754Sed IEEE80211_DISCARD_MAC(ic, 541198090Srdivacky IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, 542198892Srdivacky ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); 543201360Srdivacky ic->ic_stats.is_ampdu_bar_bad++; 544201360Srdivacky return; 545201360Srdivacky } 546201360Srdivacky ic->ic_stats.is_ampdu_bar_rx++; 547201360Srdivacky rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 548201360Srdivacky /* calculate offset in BA window */ 549201360Srdivacky off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 550194754Sed if (off >= rap->rxa_wnd) { 551194754Sed /* 552194754Sed * Outside the window, flush the reorder q if 553195340Sed * not pulling the sequence # backward. The 554201360Srdivacky * latter is typically caused by a dropped BA. 555198090Srdivacky */ 556198892Srdivacky IEEE80211_NOTE(ic, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni, 557201360Srdivacky "recv BAR outside BA win <%u:%u> rxseq %u tid %u", 558201360Srdivacky rap->rxa_start, 559201360Srdivacky IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd), 560201360Srdivacky rxseq, tid); 561201360Srdivacky ic->ic_stats.is_ampdu_bar_oow++; 562201360Srdivacky if (rxseq < rap->rxa_start) { 563201360Srdivacky /* XXX stat? */ 564201360Srdivacky return; 565198090Srdivacky } 566198892Srdivacky if (rap->rxa_qframes != 0) { 567201360Srdivacky ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes; 568201360Srdivacky ampdu_rx_flush(ni, rap, rap->rxa_wnd); 569201360Srdivacky KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, 570201360Srdivacky ("lost %u data, %u frames on ampdu rx q", 571201360Srdivacky rap->rxa_qbytes, rap->rxa_qframes)); 572201360Srdivacky } 573201360Srdivacky } else if (rap->rxa_qframes != 0) { 574201360Srdivacky /* 575201360Srdivacky * Dispatch packets up to rxseq. 576201360Srdivacky */ 577201360Srdivacky ampdu_rx_flush(ni, rap, off); 578201360Srdivacky ic->ic_stats.is_ampdu_rx_oor += off; 579198090Srdivacky 580198892Srdivacky /* 581201360Srdivacky * If frames remain, copy the mbuf pointers down so 582201360Srdivacky * they correspond to the offsets in the new window. 583201360Srdivacky */ 584201360Srdivacky if (rap->rxa_qframes != 0) { 585201360Srdivacky int n = rap->rxa_qframes, j; 586201360Srdivacky for (j = off+1; j < rap->rxa_wnd; j++) { 587201360Srdivacky if (rap->rxa_m[j] != NULL) { 588201360Srdivacky rap->rxa_m[j-off] = rap->rxa_m[j]; 589201360Srdivacky rap->rxa_m[j] = NULL; 590198090Srdivacky if (--n == 0) 591198892Srdivacky break; 592198892Srdivacky } 593198892Srdivacky } 594201360Srdivacky KASSERT(n == 0, ("lost %d frames", n)); 595201360Srdivacky ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; 596201360Srdivacky } 597201360Srdivacky } 598201360Srdivacky rap->rxa_start = rxseq; 599201360Srdivacky rap->rxa_nxt = rap->rxa_start; 600201360Srdivacky} 601198892Srdivacky 602195340Sed/* 603195340Sed * Setup HT-specific state in a node. Called only 604195340Sed * when HT use is negotiated so we don't do extra 605201360Srdivacky * work for temporary and/or legacy sta's. 606198090Srdivacky */ 607198892Srdivackyvoid 608201360Srdivackyieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap) 609201360Srdivacky{ 610201360Srdivacky struct ieee80211_tx_ampdu *tap; 611201360Srdivacky int ac; 612201360Srdivacky 613201360Srdivacky ieee80211_parse_htcap(ni, htcap); 614198090Srdivacky for (ac = 0; ac < WME_NUM_AC; ac++) { 615198892Srdivacky tap = &ni->ni_tx_ampdu[ac]; 616201360Srdivacky tap->txa_ac = ac; 617201360Srdivacky } 618201360Srdivacky ni->ni_flags |= IEEE80211_NODE_HT; 619201360Srdivacky} 620201360Srdivacky 621201360Srdivacky/* 622201360Srdivacky * Cleanup HT-specific state in a node. Called only 623201360Srdivacky * when HT use has been marked. 624201360Srdivacky */ 625201360Srdivackyvoid 626198090Srdivackyieee80211_ht_node_cleanup(struct ieee80211_node *ni) 627198892Srdivacky{ 628201360Srdivacky struct ieee80211com *ic = ni->ni_ic; 629201360Srdivacky int i; 630201360Srdivacky 631201360Srdivacky KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); 632201360Srdivacky 633201360Srdivacky /* XXX optimize this */ 634201360Srdivacky for (i = 0; i < WME_NUM_AC; i++) { 635195340Sed struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; 636195340Sed if (IEEE80211_AMPDU_REQUESTED(tap)) 637195340Sed ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]); 638195340Sed } 639201360Srdivacky for (i = 0; i < WME_NUM_TID; i++) 640198090Srdivacky ampdu_rx_stop(&ni->ni_rx_ampdu[i]); 641198892Srdivacky 642201360Srdivacky ni->ni_htcap = 0; 643201360Srdivacky ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT); 644201360Srdivacky} 645201360Srdivacky 646201360Srdivacky/* unalligned little endian access */ 647201360Srdivacky#define LE_READ_2(p) \ 648201360Srdivacky ((uint16_t) \ 649201360Srdivacky ((((const uint8_t *)(p))[0] ) | \ 650201360Srdivacky (((const uint8_t *)(p))[1] << 8))) 651198090Srdivacky 652198892Srdivacky/* 653201360Srdivacky * Process an 802.11n HT capabilities ie. 654201360Srdivacky */ 655201360Srdivackyvoid 656201360Srdivackyieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) 657201360Srdivacky{ 658201360Srdivacky struct ieee80211com *ic = ni->ni_ic; 659201360Srdivacky 660201360Srdivacky if (ie[0] == IEEE80211_ELEMID_VENDOR) { 661201360Srdivacky /* 662195340Sed * Station used Vendor OUI ie to associate; 663195340Sed * mark the node so when we respond we'll use 664204792Srdivacky * the Vendor OUI's and not the standard ie's. 665204792Srdivacky */ 666204792Srdivacky ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; 667204792Srdivacky ie += 4; 668204792Srdivacky } else 669204792Srdivacky ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; 670204792Srdivacky 671204792Srdivacky ni->ni_htcap = LE_READ_2(ie + 672204792Srdivacky __offsetof(struct ieee80211_ie_htcap, hc_cap)); 673204792Srdivacky if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0) 674204792Srdivacky ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI40; 675204792Srdivacky if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) 676204792Srdivacky ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI20; 677204792Srdivacky ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) ? 40 : 20; 678204792Srdivacky ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; 679204792Srdivacky#if 0 680204792Srdivacky ni->ni_maxampdu = 681204792Srdivacky (8*1024) << MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); 682204792Srdivacky ni->ni_mpdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); 683204792Srdivacky#endif 684204792Srdivacky} 685204792Srdivacky 686204792Srdivacky/* 687204792Srdivacky * Process an 802.11n HT info ie. 688204792Srdivacky */ 689204792Srdivackyvoid 690204642Srdivackyieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) 691204642Srdivacky{ 692204642Srdivacky const struct ieee80211_ie_htinfo *htinfo; 693204642Srdivacky uint16_t w; 694204642Srdivacky int chw; 695204642Srdivacky 696204642Srdivacky if (ie[0] == IEEE80211_ELEMID_VENDOR) 697204642Srdivacky ie += 4; 698204642Srdivacky htinfo = (const struct ieee80211_ie_htinfo *) ie; 699204642Srdivacky ni->ni_htctlchan = htinfo->hi_ctrlchannel; 700204642Srdivacky ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); 701204642Srdivacky w = LE_READ_2(&htinfo->hi_byte2); 702204642Srdivacky ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); 703204642Srdivacky w = LE_READ_2(&htinfo->hi_byte45); 704204642Srdivacky ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); 705204642Srdivacky /* update node's recommended tx channel width */ 706204642Srdivacky chw = (htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) ? 40 : 20; 707204642Srdivacky if (chw != ni->ni_chw) { 708204642Srdivacky ni->ni_chw = chw; 709204642Srdivacky ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; 710204642Srdivacky } 711204642Srdivacky} 712204642Srdivacky 713204642Srdivacky/* 714204642Srdivacky * Install received HT rate set by parsing the HT cap ie. 715195340Sed */ 716195340Sedint 717201360Srdivackyieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) 718198090Srdivacky{ 719198892Srdivacky struct ieee80211com *ic = ni->ni_ic; 720201360Srdivacky const struct ieee80211_ie_htcap *htcap; 721201360Srdivacky struct ieee80211_htrateset *rs; 722201360Srdivacky int i; 723201360Srdivacky 724201360Srdivacky rs = &ni->ni_htrates; 725201360Srdivacky memset(rs, 0, sizeof(*rs)); 726201360Srdivacky if (ie != NULL) { 727201360Srdivacky if (ie[0] == IEEE80211_ELEMID_VENDOR) 728195340Sed ie += 4; 729198892Srdivacky htcap = (const struct ieee80211_ie_htcap *) ie; 730195340Sed for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 731201360Srdivacky if (isclr(htcap->hc_mcsset, i)) 732201360Srdivacky continue; 733201360Srdivacky if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { 734201360Srdivacky IEEE80211_NOTE(ic, 735201360Srdivacky IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 736201360Srdivacky "WARNING, HT rate set too large; only " 737201360Srdivacky "using %u rates", IEEE80211_HTRATE_MAXSIZE); 738201360Srdivacky ic->ic_stats.is_rx_rstoobig++; 739195340Sed break; 740195340Sed } 741204642Srdivacky rs->rs_rates[rs->rs_nrates++] = i; 742204642Srdivacky } 743204642Srdivacky } 744204642Srdivacky return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); 745204642Srdivacky} 746204642Srdivacky 747204642Srdivacky/* 748204642Srdivacky * Mark rates in a node's HT rate set as basic according 749204642Srdivacky * to the information in the supplied HT info ie. 750204642Srdivacky */ 751204642Srdivackyvoid 752204642Srdivackyieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) 753204642Srdivacky{ 754204642Srdivacky const struct ieee80211_ie_htinfo *htinfo; 755204642Srdivacky struct ieee80211_htrateset *rs; 756204642Srdivacky int i, j; 757204642Srdivacky 758204642Srdivacky if (ie[0] == IEEE80211_ELEMID_VENDOR) 759204642Srdivacky ie += 4; 760204642Srdivacky htinfo = (const struct ieee80211_ie_htinfo *) ie; 761204642Srdivacky rs = &ni->ni_htrates; 762204642Srdivacky if (rs->rs_nrates == 0) { 763204642Srdivacky IEEE80211_NOTE(ni->ni_ic, 764194612Sed IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 765195098Sed "%s", "WARNING, empty HT rate set"); 766195098Sed return; 767195098Sed } 768195098Sed for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 769195098Sed if (isclr(htinfo->hi_basicmcsset, i)) 770194612Sed continue; 771194612Sed for (j = 0; j < rs->rs_nrates; j++) 772195098Sed if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) 773195098Sed rs->rs_rates[j] |= IEEE80211_RATE_BASIC; 774198090Srdivacky } 775201360Srdivacky} 776201360Srdivacky 777201360Srdivackystatic void 778201360Srdivackyaddba_timeout(void *arg) 779201360Srdivacky{ 780201360Srdivacky struct ieee80211_tx_ampdu *tap = arg; 781201360Srdivacky 782201360Srdivacky /* XXX ? */ 783201360Srdivacky tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 784195098Sed tap->txa_attempts++; 785198090Srdivacky} 786201360Srdivacky 787201360Srdivackystatic void 788201360Srdivackyaddba_start_timeout(struct ieee80211_tx_ampdu *tap) 789201360Srdivacky{ 790201360Srdivacky /* XXX use CALLOUT_PENDING instead? */ 791201360Srdivacky callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT, 792201360Srdivacky addba_timeout, tap); 793201360Srdivacky tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; 794201360Srdivacky tap->txa_lastrequest = ticks; 795195098Sed} 796198090Srdivacky 797198090Srdivackystatic void 798201360Srdivackyaddba_stop_timeout(struct ieee80211_tx_ampdu *tap) 799201360Srdivacky{ 800201360Srdivacky /* XXX use CALLOUT_PENDING instead? */ 801201360Srdivacky if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { 802201360Srdivacky callout_stop(&tap->txa_timer); 803201360Srdivacky tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 804201360Srdivacky } 805201360Srdivacky} 806204642Srdivacky 807201360Srdivacky/* 808201360Srdivacky * Default method for requesting A-MPDU tx aggregation. 809201360Srdivacky * We setup the specified state block and start a timer 810201360Srdivacky * to wait for an ADDBA response frame. 811201360Srdivacky */ 812201360Srdivackystatic int 813201360Srdivackyieee80211_addba_request(struct ieee80211_node *ni, 814201360Srdivacky struct ieee80211_tx_ampdu *tap, 815195098Sed int dialogtoken, int baparamset, int batimeout) 816198090Srdivacky{ 817198090Srdivacky int bufsiz; 818201360Srdivacky 819201360Srdivacky /* XXX locking */ 820201360Srdivacky tap->txa_token = dialogtoken; 821201360Srdivacky tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; 822201360Srdivacky tap->txa_start = tap->txa_seqstart = 0; 823201360Srdivacky bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 824201360Srdivacky tap->txa_wnd = (bufsiz == 0) ? 825201360Srdivacky IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 826195098Sed addba_start_timeout(tap); 827198090Srdivacky return 1; 828198090Srdivacky} 829201360Srdivacky 830201360Srdivacky/* 831201360Srdivacky * Default method for processing an A-MPDU tx aggregation 832201360Srdivacky * response. We shutdown any pending timer and update the 833201360Srdivacky * state block according to the reply. 834201360Srdivacky */ 835201360Srdivackystatic int 836201360Srdivackyieee80211_addba_response(struct ieee80211_node *ni, 837198090Srdivacky struct ieee80211_tx_ampdu *tap, 838201360Srdivacky int status, int baparamset, int batimeout) 839201360Srdivacky{ 840201360Srdivacky int bufsiz; 841201360Srdivacky 842201360Srdivacky /* XXX locking */ 843201360Srdivacky addba_stop_timeout(tap); 844201360Srdivacky if (status == IEEE80211_STATUS_SUCCESS) { 845201360Srdivacky bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 846195098Sed /* XXX override our request? */ 847198090Srdivacky tap->txa_wnd = (bufsiz == 0) ? 848198090Srdivacky IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); 849198090Srdivacky tap->txa_flags |= IEEE80211_AGGR_RUNNING; 850201360Srdivacky } 851201360Srdivacky return 1; 852201360Srdivacky} 853201360Srdivacky 854201360Srdivacky/* 855201360Srdivacky * Default method for stopping A-MPDU tx aggregation. 856201360Srdivacky * Any timer is cleared and we drain any pending frames. 857201360Srdivacky */ 858195098Sedstatic void 859204642Srdivackyieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) 860204642Srdivacky{ 861204642Srdivacky /* XXX locking */ 862204642Srdivacky addba_stop_timeout(tap); 863204642Srdivacky if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { 864204642Srdivacky /* clear aggregation queue */ 865204642Srdivacky ieee80211_drain_ifq(&tap->txa_q); 866204642Srdivacky tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; 867204642Srdivacky } 868204642Srdivacky tap->txa_attempts = 0; 869204642Srdivacky} 870204642Srdivacky 871204642Srdivacky/* 872204642Srdivacky * Process a received action frame using the default aggregation 873204642Srdivacky * policy. We intercept ADDBA-related frames and use them to 874204642Srdivacky * update our aggregation state. All other frames are passed up 875204642Srdivacky * for processing by ieee80211_recv_action. 876204642Srdivacky */ 877204642Srdivackystatic void 878198090Srdivackyieee80211_aggr_recv_action(struct ieee80211_node *ni, 879198892Srdivacky const uint8_t *frm, const uint8_t *efrm) 880198090Srdivacky{ 881198892Srdivacky struct ieee80211com *ic = ni->ni_ic; 882198090Srdivacky const struct ieee80211_action *ia; 883198892Srdivacky struct ieee80211_rx_ampdu *rap; 884198090Srdivacky struct ieee80211_tx_ampdu *tap; 885198892Srdivacky uint8_t dialogtoken; 886198892Srdivacky uint16_t baparamset, batimeout, baseqctl, code; 887198090Srdivacky uint16_t args[4]; 888198090Srdivacky int tid, ac, bufsiz; 889194612Sed 890195098Sed ia = (const struct ieee80211_action *) frm; 891195098Sed switch (ia->ia_category) { 892195098Sed case IEEE80211_ACTION_CAT_BA: 893195340Sed switch (ia->ia_action) { 894204642Srdivacky case IEEE80211_ACTION_BA_ADDBA_REQUEST: 895201360Srdivacky dialogtoken = frm[2]; 896195340Sed baparamset = LE_READ_2(frm+3); 897195340Sed batimeout = LE_READ_2(frm+5); 898201360Srdivacky baseqctl = LE_READ_2(frm+7); 899201360Srdivacky 900195340Sed tid = MS(baparamset, IEEE80211_BAPS_TID); 901195340Sed bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 902201360Srdivacky 903201360Srdivacky IEEE80211_NOTE(ic, 904195340Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 905198090Srdivacky "recv ADDBA request: dialogtoken %u " 906195340Sed "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " 907201360Srdivacky "baseqctl %d", 908198090Srdivacky dialogtoken, baparamset, tid, bufsiz, 909198892Srdivacky batimeout, baseqctl); 910205218Srdivacky 911198090Srdivacky rap = &ni->ni_rx_ampdu[tid]; 912201360Srdivacky 913201360Srdivacky /* Send ADDBA response */ 914195340Sed args[0] = dialogtoken; 915201360Srdivacky if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) { 916195340Sed ampdu_rx_start(rap, bufsiz, 917195340Sed MS(baseqctl, IEEE80211_BASEQ_START)); 918195340Sed 919195340Sed args[1] = IEEE80211_STATUS_SUCCESS; 920195340Sed } else 921195340Sed args[1] = IEEE80211_STATUS_UNSPECIFIED; 922195340Sed /* XXX honor rap flags? */ 923195340Sed args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE 924195340Sed | SM(tid, IEEE80211_BAPS_TID) 925195340Sed | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) 926195340Sed ; 927195340Sed args[3] = 0; 928195340Sed ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 929195340Sed IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); 930195340Sed return; 931195340Sed 932195340Sed case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 933195340Sed dialogtoken = frm[2]; 934195340Sed code = LE_READ_2(frm+3); 935195340Sed baparamset = LE_READ_2(frm+5); 936195340Sed tid = MS(baparamset, IEEE80211_BAPS_TID); 937195340Sed bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 938195340Sed batimeout = LE_READ_2(frm+7); 939195340Sed 940195340Sed IEEE80211_NOTE(ic, 941195340Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 942195340Sed "recv ADDBA response: dialogtoken %u code %d " 943195340Sed "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", 944195340Sed dialogtoken, code, baparamset, tid, bufsiz, 945195340Sed batimeout); 946195340Sed 947195340Sed ac = TID_TO_WME_AC(tid); 948195340Sed tap = &ni->ni_tx_ampdu[ac]; 949195340Sed 950195340Sed ic->ic_addba_response(ni, tap, 951195340Sed code, baparamset, batimeout); 952195340Sed return; 953195340Sed 954195340Sed case IEEE80211_ACTION_BA_DELBA: 955195340Sed baparamset = LE_READ_2(frm+2); 956195340Sed code = LE_READ_2(frm+4); 957195340Sed 958195340Sed tid = MS(baparamset, IEEE80211_DELBAPS_TID); 959201360Srdivacky 960195340Sed IEEE80211_NOTE(ic, 961198090Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 962198892Srdivacky "recv DELBA: baparamset 0x%x (tid %d initiator %d) " 963195340Sed "code %d", baparamset, tid, 964195340Sed MS(baparamset, IEEE80211_DELBAPS_INIT), code); 965201360Srdivacky 966195340Sed if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { 967198090Srdivacky ac = TID_TO_WME_AC(tid); 968198892Srdivacky tap = &ni->ni_tx_ampdu[ac]; 969195340Sed ic->ic_addba_stop(ni, tap); 970195340Sed } else { 971201360Srdivacky rap = &ni->ni_rx_ampdu[tid]; 972195340Sed ampdu_rx_stop(rap); 973198090Srdivacky } 974198892Srdivacky return; 975195340Sed } 976201360Srdivacky break; 977195340Sed } 978198090Srdivacky return ieee80211_recv_action(ni, frm, efrm); 979198892Srdivacky} 980195340Sed 981195340Sed/* 982201360Srdivacky * Process a received 802.11n action frame. 983195340Sed * Aggregation-related frames are assumed to be handled 984198090Srdivacky * already; we handle any other frames we can, otherwise 985198892Srdivacky * complain about being unsupported (with debugging). 986195340Sed */ 987201360Srdivackyvoid 988195340Sedieee80211_recv_action(struct ieee80211_node *ni, 989198090Srdivacky const uint8_t *frm, const uint8_t *efrm) 990198892Srdivacky{ 991195340Sed struct ieee80211com *ic = ni->ni_ic; 992195340Sed const struct ieee80211_action *ia; 993201360Srdivacky int chw; 994195340Sed 995198090Srdivacky ia = (const struct ieee80211_action *) frm; 996198892Srdivacky switch (ia->ia_category) { 997195340Sed case IEEE80211_ACTION_CAT_BA: 998201360Srdivacky IEEE80211_NOTE(ic, 999195340Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1000198090Srdivacky "%s: BA action %d not implemented", __func__, 1001198892Srdivacky ia->ia_action); 1002195340Sed ic->ic_stats.is_rx_mgtdiscard++; 1003195340Sed break; 1004201360Srdivacky case IEEE80211_ACTION_CAT_HT: 1005195340Sed switch (ia->ia_action) { 1006198090Srdivacky case IEEE80211_ACTION_HT_TXCHWIDTH: 1007198892Srdivacky chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20; 1008195340Sed if (chw != ni->ni_chw) { 1009201360Srdivacky ni->ni_chw = chw; 1010195340Sed ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; 1011198090Srdivacky } 1012198892Srdivacky IEEE80211_NOTE(ic, 1013195340Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1014195340Sed "%s: HT txchwidth. width %d (%s)", 1015195340Sed __func__, chw, 1016204792Srdivacky ni->ni_flags & IEEE80211_NODE_CHWUPDATE ? 1017204792Srdivacky "new" : "no change"); 1018204792Srdivacky break; 1019204792Srdivacky default: 1020204792Srdivacky IEEE80211_NOTE(ic, 1021204792Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1022204792Srdivacky "%s: HT action %d not implemented", __func__, 1023204792Srdivacky ia->ia_action); 1024204792Srdivacky ic->ic_stats.is_rx_mgtdiscard++; 1025204792Srdivacky break; 1026204792Srdivacky } 1027204792Srdivacky break; 1028204792Srdivacky default: 1029204792Srdivacky IEEE80211_NOTE(ic, 1030204792Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1031204792Srdivacky "%s: category %d not implemented", __func__, 1032204792Srdivacky ia->ia_category); 1033204792Srdivacky ic->ic_stats.is_rx_mgtdiscard++; 1034204792Srdivacky break; 1035204792Srdivacky } 1036204792Srdivacky} 1037204792Srdivacky 1038195340Sed/* 1039204642Srdivacky * Transmit processing. 1040204642Srdivacky */ 1041204642Srdivacky 1042195340Sed/* 1043195340Sed * Request A-MPDU tx aggregation. Setup local state and 1044198090Srdivacky * issue an ADDBA request. BA use will only happen after 1045201360Srdivacky * the other end replies with ADDBA response. 1046198090Srdivacky */ 1047198892Srdivackyint 1048195340Sedieee80211_ampdu_request(struct ieee80211_node *ni, 1049195340Sed struct ieee80211_tx_ampdu *tap) 1050201360Srdivacky{ 1051195340Sed struct ieee80211com *ic = ni->ni_ic; 1052198090Srdivacky uint16_t args[4]; 1053198892Srdivacky int tid, dialogtoken; 1054195340Sed static int tokens = 0; /* XXX */ 1055195340Sed 1056195340Sed /* XXX locking */ 1057201360Srdivacky if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { 1058195340Sed /* do deferred setup of state */ 1059198090Srdivacky /* XXX tap->txa_q */ 1060198892Srdivacky callout_init(&tap->txa_timer, CALLOUT_MPSAFE); 1061195340Sed tap->txa_flags |= IEEE80211_AGGR_SETUP; 1062199989Srdivacky } 1063195340Sed if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES && 1064201360Srdivacky (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) { 1065195340Sed /* 1066198090Srdivacky * Don't retry too often; IEEE80211_AGGR_MINRETRY 1067198892Srdivacky * defines the minimum interval we'll retry after 1068195340Sed * IEEE80211_AGGR_MAXTRIES failed attempts to 1069195340Sed * negotiate use. 1070195340Sed */ 1071201360Srdivacky return 0; 1072195340Sed } 1073198090Srdivacky dialogtoken = (tokens+1) % 63; /* XXX */ 1074198892Srdivacky 1075195340Sed tid = WME_AC_TO_TID(tap->txa_ac); 1076195340Sed args[0] = dialogtoken; 1077195340Sed args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE 1078201360Srdivacky | SM(tid, IEEE80211_BAPS_TID) 1079195340Sed | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) 1080198090Srdivacky ; 1081198892Srdivacky args[2] = 0; /* batimeout */ 1082195340Sed args[3] = SM(0, IEEE80211_BASEQ_START) 1083195340Sed | SM(0, IEEE80211_BASEQ_FRAG) 1084195340Sed ; 1085201360Srdivacky /* NB: do first so there's no race against reply */ 1086195340Sed if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { 1087198090Srdivacky /* unable to setup state, don't make request */ 1088198892Srdivacky return 0; 1089195340Sed } 1090195340Sed tokens = dialogtoken; /* allocate token */ 1091195340Sed return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, 1092204792Srdivacky IEEE80211_ACTION_BA_ADDBA_REQUEST, args); 1093204792Srdivacky} 1094204792Srdivacky 1095204792Srdivacky/* 1096204792Srdivacky * Transmit a BAR frame to the specified node. The 1097204792Srdivacky * BAR contents are drawn from the supplied aggregation 1098204792Srdivacky * state associated with the node. 1099204792Srdivacky */ 1100204792Srdivackyint 1101204792Srdivackyieee80211_send_bar(struct ieee80211_node *ni, 1102204792Srdivacky const struct ieee80211_tx_ampdu *tap) 1103204792Srdivacky{ 1104204792Srdivacky#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 1105204792Srdivacky#define ADDSHORT(frm, v) do { \ 1106204792Srdivacky frm[0] = (v) & 0xff; \ 1107195340Sed frm[1] = (v) >> 8; \ 1108204792Srdivacky frm += 2; \ 1109204792Srdivacky} while (0) 1110204792Srdivacky struct ieee80211com *ic = ni->ni_ic; 1111204792Srdivacky struct ifnet *ifp = ic->ic_ifp; 1112205218Srdivacky struct ieee80211_frame_min *wh; 1113205218Srdivacky struct mbuf *m; 1114195340Sed uint8_t *frm; 1115205218Srdivacky uint16_t barctl, barseqctl; 1116205218Srdivacky int tid, ret; 1117205218Srdivacky 1118205218Srdivacky ieee80211_ref_node(ni); 1119205218Srdivacky 1120205218Srdivacky m = ieee80211_getmgtframe(&frm, 1121205218Srdivacky ic->ic_headroom + sizeof(struct ieee80211_frame_min), 1122205218Srdivacky sizeof(struct ieee80211_ba_request) 1123205218Srdivacky ); 1124205218Srdivacky if (m == NULL) 1125205218Srdivacky senderr(ENOMEM, is_tx_nobuf); 1126205218Srdivacky 1127205218Srdivacky wh = mtod(m, struct ieee80211_frame_min *); 1128205218Srdivacky wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | 1129205218Srdivacky IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; 1130205218Srdivacky wh->i_fc[1] = 0; 1131204792Srdivacky IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 1132204792Srdivacky IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 1133205218Srdivacky 1134205218Srdivacky tid = WME_AC_TO_TID(tap->txa_ac); 1135205218Srdivacky barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 1136204792Srdivacky IEEE80211_BAPS_POLICY_IMMEDIATE : 1137204792Srdivacky IEEE80211_BAPS_POLICY_DELAYED) 1138205218Srdivacky | SM(tid, IEEE80211_BAPS_TID) 1139205218Srdivacky | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ) 1140204792Srdivacky ; 1141204792Srdivacky barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START) 1142204792Srdivacky | SM(0, IEEE80211_BASEQ_FRAG) 1143204792Srdivacky ; 1144204792Srdivacky ADDSHORT(frm, barctl); 1145204792Srdivacky ADDSHORT(frm, barseqctl); 1146204792Srdivacky m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1147204792Srdivacky 1148204792Srdivacky IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ 1149205218Srdivacky 1150205218Srdivacky IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 1151204792Srdivacky "[%s] send bar frame (tid %u start %u) on channel %u\n", 1152204792Srdivacky ether_sprintf(ni->ni_macaddr), tid, tap->txa_start, 1153204792Srdivacky ieee80211_chan2ieee(ic, ic->ic_curchan)); 1154204792Srdivacky 1155204792Srdivacky m->m_pkthdr.rcvif = (void *)ni; 1156204792Srdivacky IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ 1157204792Srdivacky (*ifp->if_start)(ifp); 1158204792Srdivacky 1159204792Srdivacky return 0; 1160204792Srdivackybad: 1161205218Srdivacky ieee80211_free_node(ni); 1162205218Srdivacky return ret; 1163204792Srdivacky#undef ADDSHORT 1164204792Srdivacky#undef senderr 1165204792Srdivacky} 1166204792Srdivacky 1167204792Srdivacky/* 1168204792Srdivacky * Send an action management frame. The arguments are stuff 1169204792Srdivacky * into a frame without inspection; the caller is assumed to 1170204792Srdivacky * prepare them carefully (e.g. based on the aggregation state). 1171204792Srdivacky */ 1172204792Srdivackyint 1173204792Srdivackyieee80211_send_action(struct ieee80211_node *ni, 1174204792Srdivacky int category, int action, uint16_t args[4]) 1175204792Srdivacky{ 1176204792Srdivacky#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 1177204792Srdivacky#define ADDSHORT(frm, v) do { \ 1178204792Srdivacky frm[0] = (v) & 0xff; \ 1179204792Srdivacky frm[1] = (v) >> 8; \ 1180204792Srdivacky frm += 2; \ 1181204792Srdivacky} while (0) 1182204792Srdivacky struct ieee80211com *ic = ni->ni_ic; 1183204792Srdivacky struct mbuf *m; 1184204792Srdivacky uint8_t *frm; 1185204792Srdivacky uint16_t baparamset; 1186204792Srdivacky int ret; 1187204792Srdivacky 1188204792Srdivacky KASSERT(ni != NULL, ("null node")); 1189204792Srdivacky 1190204792Srdivacky /* 1191204792Srdivacky * Hold a reference on the node so it doesn't go away until after 1192204792Srdivacky * the xmit is complete all the way in the driver. On error we 1193204792Srdivacky * will remove our reference. 1194204792Srdivacky */ 1195204792Srdivacky IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 1196204792Srdivacky "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1197204792Srdivacky __func__, __LINE__, 1198204792Srdivacky ni, ether_sprintf(ni->ni_macaddr), 1199204792Srdivacky ieee80211_node_refcnt(ni)+1); 1200204792Srdivacky ieee80211_ref_node(ni); 1201204792Srdivacky 1202204792Srdivacky m = ieee80211_getmgtframe(&frm, 1203195098Sed ic->ic_headroom + sizeof(struct ieee80211_frame), 1204195340Sed sizeof(uint16_t) /* action+category */ 1205195340Sed /* XXX may action payload */ 1206195340Sed + sizeof(struct ieee80211_action_ba_addbaresponse) 1207205218Srdivacky ); 1208205218Srdivacky if (m == NULL) 1209205218Srdivacky senderr(ENOMEM, is_tx_nobuf); 1210205218Srdivacky 1211201360Srdivacky *frm++ = category; 1212201360Srdivacky *frm++ = action; 1213201360Srdivacky switch (category) { 1214201360Srdivacky case IEEE80211_ACTION_CAT_BA: 1215205218Srdivacky switch (action) { 1216201360Srdivacky case IEEE80211_ACTION_BA_ADDBA_REQUEST: 1217201360Srdivacky IEEE80211_NOTE(ic, 1218195340Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1219205218Srdivacky "send ADDBA request: tid %d, baparamset 0x%x", 1220205218Srdivacky args[0], args[1]); 1221205407Srdivacky 1222205218Srdivacky *frm++ = args[0]; /* dialog token */ 1223201360Srdivacky ADDSHORT(frm, args[1]); /* baparamset */ 1224201360Srdivacky ADDSHORT(frm, args[2]); /* batimeout */ 1225201360Srdivacky ADDSHORT(frm, args[3]); /* baseqctl */ 1226201360Srdivacky break; 1227205218Srdivacky case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 1228205218Srdivacky IEEE80211_NOTE(ic, 1229205218Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1230205218Srdivacky "send ADDBA response: dialogtoken %d status %d " 1231205218Srdivacky "baparamset 0x%x (tid %d) batimeout %d", 1232205218Srdivacky args[0], args[1], args[2], 1233205218Srdivacky MS(args[2], IEEE80211_BAPS_TID), args[3]); 1234205218Srdivacky 1235205218Srdivacky *frm++ = args[0]; /* dialog token */ 1236205218Srdivacky ADDSHORT(frm, args[1]); /* statuscode */ 1237205218Srdivacky ADDSHORT(frm, args[2]); /* baparamset */ 1238205218Srdivacky ADDSHORT(frm, args[3]); /* batimeout */ 1239205218Srdivacky break; 1240205218Srdivacky case IEEE80211_ACTION_BA_DELBA: 1241201360Srdivacky /* XXX */ 1242201360Srdivacky baparamset = SM(args[0], IEEE80211_DELBAPS_TID) 1243195340Sed | SM(args[1], IEEE80211_DELBAPS_INIT) 1244205218Srdivacky ; 1245205218Srdivacky ADDSHORT(frm, baparamset); 1246205218Srdivacky ADDSHORT(frm, args[2]); /* reason code */ 1247205407Srdivacky 1248205218Srdivacky IEEE80211_NOTE(ic, 1249205218Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1250205218Srdivacky "send DELBA action: tid %d, initiator %d reason %d", 1251205218Srdivacky args[0], args[1], args[2]); 1252205218Srdivacky break; 1253205218Srdivacky default: 1254205218Srdivacky goto badaction; 1255205218Srdivacky } 1256205218Srdivacky break; 1257205218Srdivacky case IEEE80211_ACTION_CAT_HT: 1258195340Sed switch (action) { 1259194612Sed case IEEE80211_ACTION_HT_TXCHWIDTH: 1260194612Sed IEEE80211_NOTE(ic, 1261194612Sed IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, 1262194754Sed ni, "send HT txchwidth: width %d", 1263198090Srdivacky IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 1264201360Srdivacky ); 1265201360Srdivacky *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 1266201360Srdivacky IEEE80211_A_HT_TXCHWIDTH_2040 : 1267201360Srdivacky IEEE80211_A_HT_TXCHWIDTH_20; 1268201360Srdivacky break; 1269201360Srdivacky default: 1270201360Srdivacky goto badaction; 1271201360Srdivacky } 1272201360Srdivacky break; 1273194612Sed default: 1274198090Srdivacky badaction: 1275198090Srdivacky IEEE80211_NOTE(ic, 1276198090Srdivacky IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, 1277198892Srdivacky "%s: unsupported category %d action %d", __func__, 1278201360Srdivacky category, action); 1279201360Srdivacky senderr(EINVAL, is_tx_unknownmgt); 1280201360Srdivacky /* NOTREACHED */ 1281201360Srdivacky } 1282201360Srdivacky m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1283201360Srdivacky 1284201360Srdivacky ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION); 1285201360Srdivacky if (ret != 0) 1286195098Sed goto bad; 1287195098Sed return 0; 1288198090Srdivackybad: 1289198892Srdivacky ieee80211_free_node(ni); 1290201360Srdivacky return ret; 1291201360Srdivacky#undef ADDSHORT 1292201360Srdivacky#undef senderr 1293201360Srdivacky} 1294201360Srdivacky 1295201360Srdivacky/* 1296201360Srdivacky * Construct the MCS bit mask for inclusion 1297194612Sed * in an HT information element. 1298194754Sed */ 1299198090Srdivackystatic void 1300198892Srdivackyieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1301198090Srdivacky{ 1302201360Srdivacky int i; 1303201360Srdivacky 1304201360Srdivacky for (i = 0; i < rs->rs_nrates; i++) { 1305201360Srdivacky int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1306201360Srdivacky if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ 1307201360Srdivacky /* NB: this assumes a particular implementation */ 1308201360Srdivacky setbit(frm, r); 1309194754Sed } 1310198396Srdivacky } 1311198396Srdivacky} 1312194612Sed 1313195340Sed/* 1314195340Sed * Add body of an HTCAP information element. 1315195340Sed */ 1316195340Sedstatic uint8_t * 1317195340Sedieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) 1318201360Srdivacky{ 1319201360Srdivacky#define ADDSHORT(frm, v) do { \ 1320201360Srdivacky frm[0] = (v) & 0xff; \ 1321201360Srdivacky frm[1] = (v) >> 8; \ 1322204642Srdivacky frm += 2; \ 1323195340Sed} while (0) 1324201360Srdivacky struct ieee80211com *ic = ni->ni_ic; 1325195340Sed uint16_t caps; 1326201360Srdivacky 1327195340Sed /* HT capabilities */ 1328204642Srdivacky caps = ic->ic_htcaps & 0xffff; 1329195340Sed /* override 20/40 use based on channel and config */ 1330204642Srdivacky if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && 1331195340Sed (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) 1332195340Sed caps |= IEEE80211_HTCAP_CHWIDTH40; 1333195340Sed else 1334195340Sed caps &= ~IEEE80211_HTCAP_CHWIDTH40; 1335201360Srdivacky /* adjust short GI based on channel and config */ 1336201360Srdivacky if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) 1337201360Srdivacky caps &= ~IEEE80211_HTCAP_SHORTGI20; 1338201360Srdivacky if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || 1339204792Srdivacky (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) 1340201360Srdivacky caps &= ~IEEE80211_HTCAP_SHORTGI40; 1341195340Sed ADDSHORT(frm, caps); 1342195340Sed 1343195340Sed /* HT parameters */ 1344195340Sed switch (ic->ic_ampdu_rxmax / 1024) { 1345195340Sed case 8: *frm = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; 1346195340Sed case 16: *frm = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; 1347201360Srdivacky case 32: *frm = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; 1348199989Srdivacky default: *frm = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; 1349201360Srdivacky } 1350199989Srdivacky *frm |= SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY); 1351204642Srdivacky frm++; 1352195340Sed 1353195340Sed /* pre-zero remainder of ie */ 1354195340Sed memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - 1355194612Sed __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); 1356194612Sed 1357194612Sed /* supported MCS set */ 1358201360Srdivacky ieee80211_set_htrates(frm, &ni->ni_htrates); 1359201360Srdivacky 1360201360Srdivacky frm += sizeof(struct ieee80211_ie_htcap) - 1361201360Srdivacky __offsetof(struct ieee80211_ie_htcap, hc_mcsset); 1362194612Sed return frm; 1363194754Sed#undef ADDSHORT 1364201360Srdivacky} 1365201360Srdivacky 1366201360Srdivacky/* 1367201360Srdivacky * Add 802.11n HT capabilities information element 1368194612Sed */ 1369201360Srdivackyuint8_t * 1370204642Srdivackyieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) 1371201360Srdivacky{ 1372204642Srdivacky frm[0] = IEEE80211_ELEMID_HTCAP; 1373204642Srdivacky frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; 1374204642Srdivacky return ieee80211_add_htcap_body(frm + 2, ni); 1375204642Srdivacky} 1376204642Srdivacky 1377194612Sed/* 1378198090Srdivacky * Add Broadcom OUI wrapped standard HTCAP ie; this is 1379201360Srdivacky * used for compatibility w/ pre-draft implementations. 1380201360Srdivacky */ 1381201360Srdivackyuint8_t * 1382201360Srdivackyieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) 1383194612Sed{ 1384194754Sed frm[0] = IEEE80211_ELEMID_VENDOR; 1385198090Srdivacky frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; 1386198090Srdivacky frm[2] = (BCM_OUI >> 0) & 0xff; 1387198090Srdivacky frm[3] = (BCM_OUI >> 8) & 0xff; 1388195098Sed frm[4] = (BCM_OUI >> 16) & 0xff; 1389195098Sed frm[5] = BCM_OUI_HTCAP; 1390195098Sed return ieee80211_add_htcap_body(frm + 6, ni); 1391195098Sed} 1392194612Sed 1393204642Srdivacky/* 1394194754Sed * Construct the MCS bit mask of basic rates 1395204642Srdivacky * for inclusion in an HT information element. 1396204642Srdivacky */ 1397204642Srdivackystatic void 1398204642Srdivackyieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1399204642Srdivacky{ 1400204642Srdivacky int i; 1401204642Srdivacky 1402204642Srdivacky for (i = 0; i < rs->rs_nrates; i++) { 1403204642Srdivacky int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1404204642Srdivacky if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && 1405204642Srdivacky r < IEEE80211_HTRATE_MAXSIZE) { 1406204642Srdivacky /* NB: this assumes a particular implementation */ 1407204642Srdivacky setbit(frm, r); 1408204642Srdivacky } 1409204642Srdivacky } 1410204642Srdivacky} 1411204642Srdivacky 1412204642Srdivacky/* 1413204642Srdivacky * Update the HTINFO ie for a beacon frame. 1414204642Srdivacky */ 1415204642Srdivackyvoid 1416204642Srdivackyieee80211_ht_update_beacon(struct ieee80211com *ic, 1417204642Srdivacky struct ieee80211_beacon_offsets *bo) 1418204642Srdivacky{ 1419204642Srdivacky#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) 1420204642Srdivacky struct ieee80211_ie_htinfo *ht = 1421204642Srdivacky (struct ieee80211_ie_htinfo *) bo->bo_htinfo; 1422204642Srdivacky 1423204642Srdivacky /* XXX only update on channel change */ 1424204642Srdivacky ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan); 1425204642Srdivacky ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; 1426204642Srdivacky if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) 1427204642Srdivacky ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 1428204642Srdivacky else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) 1429204642Srdivacky ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; 1430204642Srdivacky else 1431204642Srdivacky ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; 1432204642Srdivacky if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) 1433204642Srdivacky ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; 1434204642Srdivacky 1435204642Srdivacky /* protection mode */ 1436204642Srdivacky ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; 1437204642Srdivacky 1438204642Srdivacky /* XXX propagate to vendor ie's */ 1439204642Srdivacky#undef PROTMODE 1440204642Srdivacky} 1441204642Srdivacky 1442204642Srdivacky/* 1443204642Srdivacky * Add body of an HTINFO information element. 1444204642Srdivacky */ 1445204642Srdivackystatic uint8_t * 1446204642Srdivackyieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) 1447204642Srdivacky{ 1448204642Srdivacky struct ieee80211com *ic = ni->ni_ic; 1449204642Srdivacky 1450204642Srdivacky /* pre-zero remainder of ie */ 1451204642Srdivacky memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); 1452204642Srdivacky 1453204642Srdivacky /* primary/control channel center */ 1454204642Srdivacky *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); 1455204642Srdivacky 1456204642Srdivacky frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; 1457204642Srdivacky if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) 1458204642Srdivacky frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 1459204642Srdivacky else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) 1460204642Srdivacky frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; 1461204642Srdivacky else 1462204642Srdivacky frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; 1463204642Srdivacky if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) 1464204642Srdivacky frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; 1465204642Srdivacky 1466204642Srdivacky frm[1] = ic->ic_curhtprotmode; 1467204642Srdivacky 1468204642Srdivacky frm += 5; 1469204642Srdivacky 1470204642Srdivacky /* basic MCS set */ 1471204642Srdivacky ieee80211_set_basic_htrates(frm, &ni->ni_htrates); 1472204642Srdivacky frm += sizeof(struct ieee80211_ie_htinfo) - 1473204642Srdivacky __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); 1474204642Srdivacky return frm; 1475204642Srdivacky} 1476204642Srdivacky 1477204642Srdivacky/* 1478204642Srdivacky * Add 802.11n HT information information element. 1479204642Srdivacky */ 1480204642Srdivackyuint8_t * 1481204642Srdivackyieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) 1482204642Srdivacky{ 1483204642Srdivacky frm[0] = IEEE80211_ELEMID_HTINFO; 1484204642Srdivacky frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; 1485204642Srdivacky return ieee80211_add_htinfo_body(frm + 2, ni); 1486204642Srdivacky} 1487204642Srdivacky 1488204642Srdivacky/* 1489204642Srdivacky * Add Broadcom OUI wrapped standard HTINFO ie; this is 1490204642Srdivacky * used for compatibility w/ pre-draft implementations. 1491204642Srdivacky */ 1492204642Srdivackyuint8_t * 1493204642Srdivackyieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) 1494204642Srdivacky{ 1495204642Srdivacky frm[0] = IEEE80211_ELEMID_VENDOR; 1496204642Srdivacky frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; 1497204642Srdivacky frm[2] = (BCM_OUI >> 0) & 0xff; 1498204642Srdivacky frm[3] = (BCM_OUI >> 8) & 0xff; 1499204642Srdivacky frm[4] = (BCM_OUI >> 16) & 0xff; 1500204642Srdivacky frm[5] = BCM_OUI_HTINFO; 1501204642Srdivacky return ieee80211_add_htinfo_body(frm + 6, ni); 1502204642Srdivacky} 1503204642Srdivacky