1*10266SQuaker.Fang@Sun.COM /* 2*10266SQuaker.Fang@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3*10266SQuaker.Fang@Sun.COM * Use is subject to license terms. 4*10266SQuaker.Fang@Sun.COM */ 5*10266SQuaker.Fang@Sun.COM 6*10266SQuaker.Fang@Sun.COM /* 7*10266SQuaker.Fang@Sun.COM * Copyright (c) 2007 Sam Leffler, Errno Consulting 8*10266SQuaker.Fang@Sun.COM * All rights reserved. 9*10266SQuaker.Fang@Sun.COM * 10*10266SQuaker.Fang@Sun.COM * Redistribution and use in source and binary forms, with or without 11*10266SQuaker.Fang@Sun.COM * modification, are permitted provided that the following conditions 12*10266SQuaker.Fang@Sun.COM * are met: 13*10266SQuaker.Fang@Sun.COM * 1. Redistributions of source code must retain the above copyright 14*10266SQuaker.Fang@Sun.COM * notice, this list of conditions and the following disclaimer. 15*10266SQuaker.Fang@Sun.COM * 2. Redistributions in binary form must reproduce the above copyright 16*10266SQuaker.Fang@Sun.COM * notice, this list of conditions and the following disclaimer in the 17*10266SQuaker.Fang@Sun.COM * documentation and/or other materials provided with the distribution. 18*10266SQuaker.Fang@Sun.COM * 19*10266SQuaker.Fang@Sun.COM * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20*10266SQuaker.Fang@Sun.COM * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21*10266SQuaker.Fang@Sun.COM * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22*10266SQuaker.Fang@Sun.COM * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23*10266SQuaker.Fang@Sun.COM * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24*10266SQuaker.Fang@Sun.COM * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25*10266SQuaker.Fang@Sun.COM * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26*10266SQuaker.Fang@Sun.COM * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27*10266SQuaker.Fang@Sun.COM * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28*10266SQuaker.Fang@Sun.COM * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29*10266SQuaker.Fang@Sun.COM */ 30*10266SQuaker.Fang@Sun.COM 31*10266SQuaker.Fang@Sun.COM /* 32*10266SQuaker.Fang@Sun.COM * IEEE 802.11n protocol support. 33*10266SQuaker.Fang@Sun.COM */ 34*10266SQuaker.Fang@Sun.COM #include <sys/mac_provider.h> 35*10266SQuaker.Fang@Sun.COM #include <sys/strsun.h> 36*10266SQuaker.Fang@Sun.COM #include <sys/byteorder.h> 37*10266SQuaker.Fang@Sun.COM 38*10266SQuaker.Fang@Sun.COM #include "net80211_impl.h" 39*10266SQuaker.Fang@Sun.COM 40*10266SQuaker.Fang@Sun.COM /* define here, used throughout file */ 41*10266SQuaker.Fang@Sun.COM #define MS(_v, _f) (((_v) & _f) >> _f##_S) 42*10266SQuaker.Fang@Sun.COM #define SM(_v, _f) (((_v) << _f##_S) & _f) 43*10266SQuaker.Fang@Sun.COM 44*10266SQuaker.Fang@Sun.COM /* need max array size */ 45*10266SQuaker.Fang@Sun.COM /* NB: these are for HT20 w/ long GI */ 46*10266SQuaker.Fang@Sun.COM const int ieee80211_htrates[16] = { 47*10266SQuaker.Fang@Sun.COM 13, /* IFM_IEEE80211_MCS0 */ 48*10266SQuaker.Fang@Sun.COM 26, /* IFM_IEEE80211_MCS1 */ 49*10266SQuaker.Fang@Sun.COM 39, /* IFM_IEEE80211_MCS2 */ 50*10266SQuaker.Fang@Sun.COM 52, /* IFM_IEEE80211_MCS3 */ 51*10266SQuaker.Fang@Sun.COM 78, /* IFM_IEEE80211_MCS4 */ 52*10266SQuaker.Fang@Sun.COM 104, /* IFM_IEEE80211_MCS5 */ 53*10266SQuaker.Fang@Sun.COM 117, /* IFM_IEEE80211_MCS6 */ 54*10266SQuaker.Fang@Sun.COM 130, /* IFM_IEEE80211_MCS7 */ 55*10266SQuaker.Fang@Sun.COM 26, /* IFM_IEEE80211_MCS8 */ 56*10266SQuaker.Fang@Sun.COM 52, /* IFM_IEEE80211_MCS9 */ 57*10266SQuaker.Fang@Sun.COM 78, /* IFM_IEEE80211_MCS10 */ 58*10266SQuaker.Fang@Sun.COM 104, /* IFM_IEEE80211_MCS11 */ 59*10266SQuaker.Fang@Sun.COM 156, /* IFM_IEEE80211_MCS12 */ 60*10266SQuaker.Fang@Sun.COM 208, /* IFM_IEEE80211_MCS13 */ 61*10266SQuaker.Fang@Sun.COM 234, /* IFM_IEEE80211_MCS14 */ 62*10266SQuaker.Fang@Sun.COM 260, /* IFM_IEEE80211_MCS15 */ 63*10266SQuaker.Fang@Sun.COM }; 64*10266SQuaker.Fang@Sun.COM 65*10266SQuaker.Fang@Sun.COM struct ieee80211_htrateset ieee80211_rateset_11n = 66*10266SQuaker.Fang@Sun.COM { 16, { 67*10266SQuaker.Fang@Sun.COM /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */ 68*10266SQuaker.Fang@Sun.COM 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69*10266SQuaker.Fang@Sun.COM /* 39 52 78 104 117, 130 */ 70*10266SQuaker.Fang@Sun.COM 10, 11, 12, 13, 14, 15 } 71*10266SQuaker.Fang@Sun.COM }; 72*10266SQuaker.Fang@Sun.COM 73*10266SQuaker.Fang@Sun.COM #define IEEE80211_AMPDU_AGE 74*10266SQuaker.Fang@Sun.COM 75*10266SQuaker.Fang@Sun.COM #define IEEE80211_AGGR_TIMEOUT 250 /* msecs */ 76*10266SQuaker.Fang@Sun.COM #define IEEE80211_AGGR_MINRETRY (10 * hz) /* ticks */ 77*10266SQuaker.Fang@Sun.COM #define IEEE80211_AGGR_MAXTRIES 3 78*10266SQuaker.Fang@Sun.COM 79*10266SQuaker.Fang@Sun.COM /* 80*10266SQuaker.Fang@Sun.COM * Receive processing. 81*10266SQuaker.Fang@Sun.COM */ 82*10266SQuaker.Fang@Sun.COM 83*10266SQuaker.Fang@Sun.COM /* 84*10266SQuaker.Fang@Sun.COM * Decap the encapsulated A-MSDU frames and dispatch all but 85*10266SQuaker.Fang@Sun.COM * the last for delivery. The last frame is returned for 86*10266SQuaker.Fang@Sun.COM * delivery via the normal path. 87*10266SQuaker.Fang@Sun.COM */ 88*10266SQuaker.Fang@Sun.COM #define FF_LLC_SIZE \ 89*10266SQuaker.Fang@Sun.COM (sizeof (struct ether_header) + sizeof (struct ieee80211_llc)) 90*10266SQuaker.Fang@Sun.COM mblk_t * 91*10266SQuaker.Fang@Sun.COM ieee80211_decap_amsdu(struct ieee80211_node *in, mblk_t *mp) 92*10266SQuaker.Fang@Sun.COM { 93*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 94*10266SQuaker.Fang@Sun.COM struct ether_header *eh; 95*10266SQuaker.Fang@Sun.COM struct ieee80211_frame *wh; 96*10266SQuaker.Fang@Sun.COM int framelen, hdrspace; 97*10266SQuaker.Fang@Sun.COM mblk_t *m0; 98*10266SQuaker.Fang@Sun.COM 99*10266SQuaker.Fang@Sun.COM /* all msdu has same ieee80211_frame header */ 100*10266SQuaker.Fang@Sun.COM wh = (struct ieee80211_frame *)mp->b_rptr; 101*10266SQuaker.Fang@Sun.COM hdrspace = ieee80211_hdrspace(ic, wh); 102*10266SQuaker.Fang@Sun.COM mp->b_rptr += hdrspace; /* A-MSDU subframe follows */ 103*10266SQuaker.Fang@Sun.COM 104*10266SQuaker.Fang@Sun.COM for (;;) { 105*10266SQuaker.Fang@Sun.COM /* 106*10266SQuaker.Fang@Sun.COM * The frame has an 802.3 header followed by an 802.2 107*10266SQuaker.Fang@Sun.COM * LLC header. The encapsulated frame length is in the 108*10266SQuaker.Fang@Sun.COM * first header type field; 109*10266SQuaker.Fang@Sun.COM */ 110*10266SQuaker.Fang@Sun.COM if (MBLKL(mp) < FF_LLC_SIZE) { 111*10266SQuaker.Fang@Sun.COM ieee80211_err("too short, decap failed\n"); 112*10266SQuaker.Fang@Sun.COM goto out; 113*10266SQuaker.Fang@Sun.COM } 114*10266SQuaker.Fang@Sun.COM /* 115*10266SQuaker.Fang@Sun.COM * Decap frames, encapsulate to 802.11 frame then deliver. 116*10266SQuaker.Fang@Sun.COM * 802.3 header is first (struct ether_header) 117*10266SQuaker.Fang@Sun.COM * 802.2 header follows (struct ieee80211_llc) 118*10266SQuaker.Fang@Sun.COM * data, msdu = llc + data 119*10266SQuaker.Fang@Sun.COM */ 120*10266SQuaker.Fang@Sun.COM eh = (struct ether_header *)mp->b_rptr; 121*10266SQuaker.Fang@Sun.COM /* 802.2 header follows */ 122*10266SQuaker.Fang@Sun.COM framelen = ntohs(eh->ether_type); /* llc + data */ 123*10266SQuaker.Fang@Sun.COM m0 = allocb(hdrspace + framelen, BPRI_MED); 124*10266SQuaker.Fang@Sun.COM if (m0 == NULL) { 125*10266SQuaker.Fang@Sun.COM ieee80211_err("decap_msdu(): can't alloc mblk\n"); 126*10266SQuaker.Fang@Sun.COM goto out; 127*10266SQuaker.Fang@Sun.COM } 128*10266SQuaker.Fang@Sun.COM (void) memcpy(m0->b_wptr, (uint8_t *)wh, hdrspace); 129*10266SQuaker.Fang@Sun.COM m0->b_wptr += hdrspace; 130*10266SQuaker.Fang@Sun.COM (void) memcpy(m0->b_wptr, 131*10266SQuaker.Fang@Sun.COM mp->b_rptr + sizeof (struct ether_header), framelen); 132*10266SQuaker.Fang@Sun.COM m0->b_wptr += framelen; 133*10266SQuaker.Fang@Sun.COM 134*10266SQuaker.Fang@Sun.COM ic->ic_stats.is_rx_frags++; 135*10266SQuaker.Fang@Sun.COM ic->ic_stats.is_rx_bytes += MBLKL(m0); 136*10266SQuaker.Fang@Sun.COM IEEE80211_UNLOCK(ic); 137*10266SQuaker.Fang@Sun.COM mac_rx(ic->ic_mach, NULL, m0); /* deliver to mac */ 138*10266SQuaker.Fang@Sun.COM IEEE80211_LOCK(ic); 139*10266SQuaker.Fang@Sun.COM 140*10266SQuaker.Fang@Sun.COM framelen += sizeof (struct ether_header); 141*10266SQuaker.Fang@Sun.COM if (MBLKL(mp) == framelen) /* last, no padding */ 142*10266SQuaker.Fang@Sun.COM goto out; 143*10266SQuaker.Fang@Sun.COM /* 144*10266SQuaker.Fang@Sun.COM * Remove frame contents; each intermediate frame 145*10266SQuaker.Fang@Sun.COM * is required to be aligned to a 4-byte boundary. 146*10266SQuaker.Fang@Sun.COM */ 147*10266SQuaker.Fang@Sun.COM mp->b_rptr += roundup(framelen, 4); /* padding */ 148*10266SQuaker.Fang@Sun.COM } 149*10266SQuaker.Fang@Sun.COM 150*10266SQuaker.Fang@Sun.COM out: 151*10266SQuaker.Fang@Sun.COM freemsg(mp); 152*10266SQuaker.Fang@Sun.COM return (NULL); /* none delivered by caller */ 153*10266SQuaker.Fang@Sun.COM } 154*10266SQuaker.Fang@Sun.COM #undef FF_LLC_SIZE 155*10266SQuaker.Fang@Sun.COM 156*10266SQuaker.Fang@Sun.COM /* 157*10266SQuaker.Fang@Sun.COM * Start A-MPDU rx/re-order processing for the specified TID. 158*10266SQuaker.Fang@Sun.COM */ 159*10266SQuaker.Fang@Sun.COM static void 160*10266SQuaker.Fang@Sun.COM ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) 161*10266SQuaker.Fang@Sun.COM { 162*10266SQuaker.Fang@Sun.COM (void) memset(rap, 0, sizeof (*rap)); 163*10266SQuaker.Fang@Sun.COM rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX 164*10266SQuaker.Fang@Sun.COM : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX); 165*10266SQuaker.Fang@Sun.COM rap->rxa_start = (uint16_t)start; 166*10266SQuaker.Fang@Sun.COM rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; 167*10266SQuaker.Fang@Sun.COM } 168*10266SQuaker.Fang@Sun.COM 169*10266SQuaker.Fang@Sun.COM /* 170*10266SQuaker.Fang@Sun.COM * Purge all frames in the A-MPDU re-order queue. 171*10266SQuaker.Fang@Sun.COM */ 172*10266SQuaker.Fang@Sun.COM static void 173*10266SQuaker.Fang@Sun.COM ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) 174*10266SQuaker.Fang@Sun.COM { 175*10266SQuaker.Fang@Sun.COM mblk_t *m; 176*10266SQuaker.Fang@Sun.COM int i; 177*10266SQuaker.Fang@Sun.COM 178*10266SQuaker.Fang@Sun.COM for (i = 0; i < rap->rxa_wnd; i++) { 179*10266SQuaker.Fang@Sun.COM m = rap->rxa_m[i]; 180*10266SQuaker.Fang@Sun.COM if (m != NULL) { 181*10266SQuaker.Fang@Sun.COM rap->rxa_m[i] = NULL; 182*10266SQuaker.Fang@Sun.COM rap->rxa_qbytes -= MBLKL(m); 183*10266SQuaker.Fang@Sun.COM freemsg(m); 184*10266SQuaker.Fang@Sun.COM if (--rap->rxa_qframes == 0) 185*10266SQuaker.Fang@Sun.COM break; 186*10266SQuaker.Fang@Sun.COM } 187*10266SQuaker.Fang@Sun.COM } 188*10266SQuaker.Fang@Sun.COM ASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0); 189*10266SQuaker.Fang@Sun.COM } 190*10266SQuaker.Fang@Sun.COM 191*10266SQuaker.Fang@Sun.COM /* 192*10266SQuaker.Fang@Sun.COM * Stop A-MPDU rx processing for the specified TID. 193*10266SQuaker.Fang@Sun.COM */ 194*10266SQuaker.Fang@Sun.COM static void 195*10266SQuaker.Fang@Sun.COM ampdu_rx_stop(struct ieee80211_rx_ampdu *rap) 196*10266SQuaker.Fang@Sun.COM { 197*10266SQuaker.Fang@Sun.COM rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND; 198*10266SQuaker.Fang@Sun.COM ampdu_rx_purge(rap); 199*10266SQuaker.Fang@Sun.COM } 200*10266SQuaker.Fang@Sun.COM 201*10266SQuaker.Fang@Sun.COM /* 202*10266SQuaker.Fang@Sun.COM * Dispatch a frame from the A-MPDU reorder queue. The 203*10266SQuaker.Fang@Sun.COM * frame is fed back into ieee80211_input marked with an 204*10266SQuaker.Fang@Sun.COM * M_AMPDU flag so it doesn't come back to us (it also 205*10266SQuaker.Fang@Sun.COM * permits ieee80211_input to optimize re-processing). 206*10266SQuaker.Fang@Sun.COM */ 207*10266SQuaker.Fang@Sun.COM static void 208*10266SQuaker.Fang@Sun.COM ampdu_dispatch(struct ieee80211_node *in, mblk_t *m) 209*10266SQuaker.Fang@Sun.COM { 210*10266SQuaker.Fang@Sun.COM m->b_flag |= M_AMPDU; /* bypass normal processing */ 211*10266SQuaker.Fang@Sun.COM /* NB: rssi and rstamp are ignored w/ M_AMPDU set */ 212*10266SQuaker.Fang@Sun.COM (void) ieee80211_input(in->in_ic, m, in, 0, 0); 213*10266SQuaker.Fang@Sun.COM } 214*10266SQuaker.Fang@Sun.COM 215*10266SQuaker.Fang@Sun.COM /* 216*10266SQuaker.Fang@Sun.COM * Dispatch as many frames as possible from the re-order queue. 217*10266SQuaker.Fang@Sun.COM * Frames will always be "at the front"; we process all frames 218*10266SQuaker.Fang@Sun.COM * up to the first empty slot in the window. On completion we 219*10266SQuaker.Fang@Sun.COM * cleanup state if there are still pending frames in the current 220*10266SQuaker.Fang@Sun.COM * BA window. We assume the frame at slot 0 is already handled 221*10266SQuaker.Fang@Sun.COM * by the caller; we always start at slot 1. 222*10266SQuaker.Fang@Sun.COM */ 223*10266SQuaker.Fang@Sun.COM static void 224*10266SQuaker.Fang@Sun.COM ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *in) 225*10266SQuaker.Fang@Sun.COM { 226*10266SQuaker.Fang@Sun.COM mblk_t *m; 227*10266SQuaker.Fang@Sun.COM int i; 228*10266SQuaker.Fang@Sun.COM 229*10266SQuaker.Fang@Sun.COM /* flush run of frames */ 230*10266SQuaker.Fang@Sun.COM for (i = 1; i < rap->rxa_wnd; i++) { 231*10266SQuaker.Fang@Sun.COM m = rap->rxa_m[i]; 232*10266SQuaker.Fang@Sun.COM if (m == NULL) 233*10266SQuaker.Fang@Sun.COM break; 234*10266SQuaker.Fang@Sun.COM rap->rxa_m[i] = NULL; 235*10266SQuaker.Fang@Sun.COM rap->rxa_qbytes -= MBLKL(m); 236*10266SQuaker.Fang@Sun.COM rap->rxa_qframes--; 237*10266SQuaker.Fang@Sun.COM 238*10266SQuaker.Fang@Sun.COM ampdu_dispatch(in, m); 239*10266SQuaker.Fang@Sun.COM } 240*10266SQuaker.Fang@Sun.COM /* 241*10266SQuaker.Fang@Sun.COM * If frames remain, copy the mbuf pointers down so 242*10266SQuaker.Fang@Sun.COM * they correspond to the offsets in the new window. 243*10266SQuaker.Fang@Sun.COM */ 244*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes != 0) { 245*10266SQuaker.Fang@Sun.COM int n = rap->rxa_qframes, j; 246*10266SQuaker.Fang@Sun.COM for (j = i+1; j < rap->rxa_wnd; j++) { 247*10266SQuaker.Fang@Sun.COM if (rap->rxa_m[j] != NULL) { 248*10266SQuaker.Fang@Sun.COM rap->rxa_m[j-i] = rap->rxa_m[j]; 249*10266SQuaker.Fang@Sun.COM rap->rxa_m[j] = NULL; 250*10266SQuaker.Fang@Sun.COM if (--n == 0) 251*10266SQuaker.Fang@Sun.COM break; 252*10266SQuaker.Fang@Sun.COM } 253*10266SQuaker.Fang@Sun.COM } 254*10266SQuaker.Fang@Sun.COM ASSERT(n == 0); 255*10266SQuaker.Fang@Sun.COM } 256*10266SQuaker.Fang@Sun.COM /* 257*10266SQuaker.Fang@Sun.COM * Adjust the start of the BA window to 258*10266SQuaker.Fang@Sun.COM * reflect the frames just dispatched. 259*10266SQuaker.Fang@Sun.COM */ 260*10266SQuaker.Fang@Sun.COM rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); 261*10266SQuaker.Fang@Sun.COM } 262*10266SQuaker.Fang@Sun.COM 263*10266SQuaker.Fang@Sun.COM #ifdef IEEE80211_AMPDU_AGE 264*10266SQuaker.Fang@Sun.COM /* 265*10266SQuaker.Fang@Sun.COM * Dispatch all frames in the A-MPDU re-order queue. 266*10266SQuaker.Fang@Sun.COM */ 267*10266SQuaker.Fang@Sun.COM static void 268*10266SQuaker.Fang@Sun.COM ampdu_rx_flush(struct ieee80211_node *in, struct ieee80211_rx_ampdu *rap) 269*10266SQuaker.Fang@Sun.COM { 270*10266SQuaker.Fang@Sun.COM mblk_t *m; 271*10266SQuaker.Fang@Sun.COM int i; 272*10266SQuaker.Fang@Sun.COM 273*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 274*10266SQuaker.Fang@Sun.COM "ampdu_rx_flush(%d)\n", 275*10266SQuaker.Fang@Sun.COM rap->rxa_wnd); 276*10266SQuaker.Fang@Sun.COM 277*10266SQuaker.Fang@Sun.COM for (i = 0; i < rap->rxa_wnd; i++) { 278*10266SQuaker.Fang@Sun.COM m = rap->rxa_m[i]; 279*10266SQuaker.Fang@Sun.COM if (m == NULL) 280*10266SQuaker.Fang@Sun.COM continue; 281*10266SQuaker.Fang@Sun.COM rap->rxa_m[i] = NULL; 282*10266SQuaker.Fang@Sun.COM rap->rxa_qbytes -= MBLKL(m); 283*10266SQuaker.Fang@Sun.COM rap->rxa_qframes--; 284*10266SQuaker.Fang@Sun.COM 285*10266SQuaker.Fang@Sun.COM ampdu_dispatch(in, m); 286*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes == 0) 287*10266SQuaker.Fang@Sun.COM break; 288*10266SQuaker.Fang@Sun.COM } 289*10266SQuaker.Fang@Sun.COM } 290*10266SQuaker.Fang@Sun.COM #endif /* IEEE80211_AMPDU_AGE */ 291*10266SQuaker.Fang@Sun.COM 292*10266SQuaker.Fang@Sun.COM /* 293*10266SQuaker.Fang@Sun.COM * Dispatch all frames in the A-MPDU re-order queue 294*10266SQuaker.Fang@Sun.COM * preceding the specified sequence number. This logic 295*10266SQuaker.Fang@Sun.COM * handles window moves due to a received MSDU or BAR. 296*10266SQuaker.Fang@Sun.COM */ 297*10266SQuaker.Fang@Sun.COM static void 298*10266SQuaker.Fang@Sun.COM ampdu_rx_flush_upto(struct ieee80211_node *in, 299*10266SQuaker.Fang@Sun.COM struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) 300*10266SQuaker.Fang@Sun.COM { 301*10266SQuaker.Fang@Sun.COM mblk_t *m; 302*10266SQuaker.Fang@Sun.COM ieee80211_seq seqno; 303*10266SQuaker.Fang@Sun.COM int i; 304*10266SQuaker.Fang@Sun.COM 305*10266SQuaker.Fang@Sun.COM /* 306*10266SQuaker.Fang@Sun.COM * Flush any complete MSDU's with a sequence number lower 307*10266SQuaker.Fang@Sun.COM * than winstart. Gaps may exist. Note that we may actually 308*10266SQuaker.Fang@Sun.COM * dispatch frames past winstart if a run continues; this is 309*10266SQuaker.Fang@Sun.COM * an optimization that avoids having to do a separate pass 310*10266SQuaker.Fang@Sun.COM * to dispatch frames after moving the BA window start. 311*10266SQuaker.Fang@Sun.COM */ 312*10266SQuaker.Fang@Sun.COM seqno = rap->rxa_start; 313*10266SQuaker.Fang@Sun.COM for (i = 0; i < rap->rxa_wnd; i++) { 314*10266SQuaker.Fang@Sun.COM m = rap->rxa_m[i]; 315*10266SQuaker.Fang@Sun.COM if (m != NULL) { 316*10266SQuaker.Fang@Sun.COM rap->rxa_m[i] = NULL; 317*10266SQuaker.Fang@Sun.COM rap->rxa_qbytes -= MBLKL(m); 318*10266SQuaker.Fang@Sun.COM rap->rxa_qframes--; 319*10266SQuaker.Fang@Sun.COM 320*10266SQuaker.Fang@Sun.COM ampdu_dispatch(in, m); 321*10266SQuaker.Fang@Sun.COM } else { 322*10266SQuaker.Fang@Sun.COM if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) 323*10266SQuaker.Fang@Sun.COM break; 324*10266SQuaker.Fang@Sun.COM } 325*10266SQuaker.Fang@Sun.COM seqno = IEEE80211_SEQ_INC(seqno); 326*10266SQuaker.Fang@Sun.COM } 327*10266SQuaker.Fang@Sun.COM /* 328*10266SQuaker.Fang@Sun.COM * If frames remain, copy the mbuf pointers down so 329*10266SQuaker.Fang@Sun.COM * they correspond to the offsets in the new window. 330*10266SQuaker.Fang@Sun.COM */ 331*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes != 0) { 332*10266SQuaker.Fang@Sun.COM int n = rap->rxa_qframes, j; 333*10266SQuaker.Fang@Sun.COM for (j = i+1; j < rap->rxa_wnd; j++) { 334*10266SQuaker.Fang@Sun.COM if (rap->rxa_m[j] != NULL) { 335*10266SQuaker.Fang@Sun.COM rap->rxa_m[j-i] = rap->rxa_m[j]; 336*10266SQuaker.Fang@Sun.COM rap->rxa_m[j] = NULL; 337*10266SQuaker.Fang@Sun.COM if (--n == 0) 338*10266SQuaker.Fang@Sun.COM break; 339*10266SQuaker.Fang@Sun.COM } 340*10266SQuaker.Fang@Sun.COM } 341*10266SQuaker.Fang@Sun.COM if (n != 0) { 342*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 343*10266SQuaker.Fang@Sun.COM "ampdu_rx_flush_upto(): " 344*10266SQuaker.Fang@Sun.COM "lost %d frames, qframes %d off %d " 345*10266SQuaker.Fang@Sun.COM "BA win <%d:%d> winstart %d\n", 346*10266SQuaker.Fang@Sun.COM n, rap->rxa_qframes, i, rap->rxa_start, 347*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 348*10266SQuaker.Fang@Sun.COM winstart); 349*10266SQuaker.Fang@Sun.COM } 350*10266SQuaker.Fang@Sun.COM } 351*10266SQuaker.Fang@Sun.COM /* 352*10266SQuaker.Fang@Sun.COM * Move the start of the BA window; we use the 353*10266SQuaker.Fang@Sun.COM * sequence number of the last MSDU that was 354*10266SQuaker.Fang@Sun.COM * passed up the stack+1 or winstart if stopped on 355*10266SQuaker.Fang@Sun.COM * a gap in the reorder buffer. 356*10266SQuaker.Fang@Sun.COM */ 357*10266SQuaker.Fang@Sun.COM rap->rxa_start = seqno; 358*10266SQuaker.Fang@Sun.COM } 359*10266SQuaker.Fang@Sun.COM 360*10266SQuaker.Fang@Sun.COM /* 361*10266SQuaker.Fang@Sun.COM * Process a received QoS data frame for an HT station. Handle 362*10266SQuaker.Fang@Sun.COM * A-MPDU reordering: if this frame is received out of order 363*10266SQuaker.Fang@Sun.COM * and falls within the BA window hold onto it. Otherwise if 364*10266SQuaker.Fang@Sun.COM * this frame completes a run, flush any pending frames. We 365*10266SQuaker.Fang@Sun.COM * return 1 if the frame is consumed. A 0 is returned if 366*10266SQuaker.Fang@Sun.COM * the frame should be processed normally by the caller. 367*10266SQuaker.Fang@Sun.COM */ 368*10266SQuaker.Fang@Sun.COM int 369*10266SQuaker.Fang@Sun.COM ieee80211_ampdu_reorder(struct ieee80211_node *in, mblk_t *m) 370*10266SQuaker.Fang@Sun.COM { 371*10266SQuaker.Fang@Sun.COM #define IEEE80211_FC0_QOSDATA \ 372*10266SQuaker.Fang@Sun.COM (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS | \ 373*10266SQuaker.Fang@Sun.COM IEEE80211_FC0_VERSION_0) 374*10266SQuaker.Fang@Sun.COM 375*10266SQuaker.Fang@Sun.COM #define PROCESS 0 /* caller should process frame */ 376*10266SQuaker.Fang@Sun.COM #define CONSUMED 1 /* frame consumed, caller does nothing */ 377*10266SQuaker.Fang@Sun.COM 378*10266SQuaker.Fang@Sun.COM struct ieee80211_qosframe *wh; 379*10266SQuaker.Fang@Sun.COM struct ieee80211_rx_ampdu *rap; 380*10266SQuaker.Fang@Sun.COM ieee80211_seq rxseq; 381*10266SQuaker.Fang@Sun.COM uint8_t tid; 382*10266SQuaker.Fang@Sun.COM int off; 383*10266SQuaker.Fang@Sun.COM 384*10266SQuaker.Fang@Sun.COM ASSERT(in->in_flags & IEEE80211_NODE_HT); 385*10266SQuaker.Fang@Sun.COM 386*10266SQuaker.Fang@Sun.COM /* NB: m_len known to be sufficient */ 387*10266SQuaker.Fang@Sun.COM wh = (struct ieee80211_qosframe *)m->b_rptr; 388*10266SQuaker.Fang@Sun.COM ASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA); 389*10266SQuaker.Fang@Sun.COM 390*10266SQuaker.Fang@Sun.COM if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) 391*10266SQuaker.Fang@Sun.COM tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0]; 392*10266SQuaker.Fang@Sun.COM else 393*10266SQuaker.Fang@Sun.COM tid = wh->i_qos[0]; 394*10266SQuaker.Fang@Sun.COM tid &= IEEE80211_QOS_TID; 395*10266SQuaker.Fang@Sun.COM rap = &in->in_rx_ampdu[tid]; 396*10266SQuaker.Fang@Sun.COM if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 397*10266SQuaker.Fang@Sun.COM /* 398*10266SQuaker.Fang@Sun.COM * No ADDBA request yet, don't touch. 399*10266SQuaker.Fang@Sun.COM */ 400*10266SQuaker.Fang@Sun.COM return (PROCESS); 401*10266SQuaker.Fang@Sun.COM } 402*10266SQuaker.Fang@Sun.COM rxseq = LE_16(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 403*10266SQuaker.Fang@Sun.COM rap->rxa_nframes++; 404*10266SQuaker.Fang@Sun.COM again: 405*10266SQuaker.Fang@Sun.COM if (rxseq == rap->rxa_start) { 406*10266SQuaker.Fang@Sun.COM /* 407*10266SQuaker.Fang@Sun.COM * First frame in window. 408*10266SQuaker.Fang@Sun.COM */ 409*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes != 0) { 410*10266SQuaker.Fang@Sun.COM /* 411*10266SQuaker.Fang@Sun.COM * Dispatch as many packets as we can. 412*10266SQuaker.Fang@Sun.COM */ 413*10266SQuaker.Fang@Sun.COM ASSERT(rap->rxa_m[0] == NULL); /* [0] is m */ 414*10266SQuaker.Fang@Sun.COM ampdu_dispatch(in, m); 415*10266SQuaker.Fang@Sun.COM ampdu_rx_dispatch(rap, in); 416*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 417*10266SQuaker.Fang@Sun.COM "ieee80211_ampdu_reorder(%u), CONSUMED ...\n", 418*10266SQuaker.Fang@Sun.COM rap->rxa_qframes); 419*10266SQuaker.Fang@Sun.COM return (CONSUMED); 420*10266SQuaker.Fang@Sun.COM } else { 421*10266SQuaker.Fang@Sun.COM /* 422*10266SQuaker.Fang@Sun.COM * In order; advance window and notify 423*10266SQuaker.Fang@Sun.COM * caller to dispatch directly. 424*10266SQuaker.Fang@Sun.COM */ 425*10266SQuaker.Fang@Sun.COM rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 426*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 427*10266SQuaker.Fang@Sun.COM "ieee80211_ampdu_reorder(%u), PROCESS ...\n", 428*10266SQuaker.Fang@Sun.COM rap->rxa_start); 429*10266SQuaker.Fang@Sun.COM return (PROCESS); 430*10266SQuaker.Fang@Sun.COM } 431*10266SQuaker.Fang@Sun.COM } 432*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 433*10266SQuaker.Fang@Sun.COM "ieee80211_ampdu_reorder(%u, %u), out of order ...\n", 434*10266SQuaker.Fang@Sun.COM rxseq, rap->rxa_start); 435*10266SQuaker.Fang@Sun.COM /* 436*10266SQuaker.Fang@Sun.COM * Frame is out of order; store if in the BA window. 437*10266SQuaker.Fang@Sun.COM */ 438*10266SQuaker.Fang@Sun.COM /* calculate offset in BA window */ 439*10266SQuaker.Fang@Sun.COM off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 440*10266SQuaker.Fang@Sun.COM if (off < rap->rxa_wnd) { 441*10266SQuaker.Fang@Sun.COM #ifdef IEEE80211_AMPDU_AGE 442*10266SQuaker.Fang@Sun.COM /* 443*10266SQuaker.Fang@Sun.COM * Common case (hopefully): in the BA window. 444*10266SQuaker.Fang@Sun.COM * Sec 9.10.7.6 a) (D2.04 p.118 line 47) 445*10266SQuaker.Fang@Sun.COM * -- 446*10266SQuaker.Fang@Sun.COM * Check for frames sitting too long in the reorder queue. 447*10266SQuaker.Fang@Sun.COM * This should only ever happen if frames are not delivered 448*10266SQuaker.Fang@Sun.COM * without the sender otherwise notifying us (e.g. with a 449*10266SQuaker.Fang@Sun.COM * BAR to move the window). Typically this happens because 450*10266SQuaker.Fang@Sun.COM * of vendor bugs that cause the sequence number to jump. 451*10266SQuaker.Fang@Sun.COM * When this happens we get a gap in the reorder queue that 452*10266SQuaker.Fang@Sun.COM * leaves frame sitting on the queue until they get pushed 453*10266SQuaker.Fang@Sun.COM * out due to window moves. When the vendor does not send 454*10266SQuaker.Fang@Sun.COM * BAR this move only happens due to explicit packet sends 455*10266SQuaker.Fang@Sun.COM * 456*10266SQuaker.Fang@Sun.COM * NB: we only track the time of the oldest frame in the 457*10266SQuaker.Fang@Sun.COM * reorder q; this means that if we flush we might push 458*10266SQuaker.Fang@Sun.COM * frames that still "new"; if this happens then subsequent 459*10266SQuaker.Fang@Sun.COM * frames will result in BA window moves which cost something 460*10266SQuaker.Fang@Sun.COM * but is still better than a big throughput dip. 461*10266SQuaker.Fang@Sun.COM */ 462*10266SQuaker.Fang@Sun.COM clock_t ticks; 463*10266SQuaker.Fang@Sun.COM 464*10266SQuaker.Fang@Sun.COM ticks = ddi_get_lbolt(); 465*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes != 0) { 466*10266SQuaker.Fang@Sun.COM /* honor batimeout? */ 467*10266SQuaker.Fang@Sun.COM if (ticks - rap->rxa_age > drv_usectohz(500*1000)) { 468*10266SQuaker.Fang@Sun.COM /* 469*10266SQuaker.Fang@Sun.COM * Too long since we received the first 470*10266SQuaker.Fang@Sun.COM * frame; flush the reorder buffer. 471*10266SQuaker.Fang@Sun.COM */ 472*10266SQuaker.Fang@Sun.COM if (rap->rxa_qframes != 0) { 473*10266SQuaker.Fang@Sun.COM ampdu_rx_flush(in, rap); 474*10266SQuaker.Fang@Sun.COM } 475*10266SQuaker.Fang@Sun.COM rap->rxa_start = IEEE80211_SEQ_INC(rxseq); 476*10266SQuaker.Fang@Sun.COM return (PROCESS); 477*10266SQuaker.Fang@Sun.COM } 478*10266SQuaker.Fang@Sun.COM } else { 479*10266SQuaker.Fang@Sun.COM /* 480*10266SQuaker.Fang@Sun.COM * First frame, start aging timer. 481*10266SQuaker.Fang@Sun.COM */ 482*10266SQuaker.Fang@Sun.COM rap->rxa_age = ticks; 483*10266SQuaker.Fang@Sun.COM } 484*10266SQuaker.Fang@Sun.COM #endif /* IEEE80211_AMPDU_AGE */ 485*10266SQuaker.Fang@Sun.COM /* save packet */ 486*10266SQuaker.Fang@Sun.COM if (rap->rxa_m[off] == NULL) { 487*10266SQuaker.Fang@Sun.COM rap->rxa_m[off] = m; 488*10266SQuaker.Fang@Sun.COM rap->rxa_qframes++; 489*10266SQuaker.Fang@Sun.COM rap->rxa_qbytes += MBLKL(m); 490*10266SQuaker.Fang@Sun.COM } else { 491*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT, 492*10266SQuaker.Fang@Sun.COM "a-mpdu duplicate " 493*10266SQuaker.Fang@Sun.COM "seqno %u tid %u BA win <%u:%u>\n", 494*10266SQuaker.Fang@Sun.COM rxseq, tid, rap->rxa_start, 495*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, 496*10266SQuaker.Fang@Sun.COM rap->rxa_wnd - 1)); 497*10266SQuaker.Fang@Sun.COM freemsg(m); 498*10266SQuaker.Fang@Sun.COM } 499*10266SQuaker.Fang@Sun.COM return (CONSUMED); 500*10266SQuaker.Fang@Sun.COM } 501*10266SQuaker.Fang@Sun.COM if (off < IEEE80211_SEQ_BA_RANGE) { 502*10266SQuaker.Fang@Sun.COM /* 503*10266SQuaker.Fang@Sun.COM * Outside the BA window, but within range; 504*10266SQuaker.Fang@Sun.COM * flush the reorder q and move the window. 505*10266SQuaker.Fang@Sun.COM * Sec 9.10.7.6 b) (D2.04 p.118 line 60) 506*10266SQuaker.Fang@Sun.COM */ 507*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 508*10266SQuaker.Fang@Sun.COM "move BA win <%u:%u> (%u frames) rxseq %u tid %u\n", 509*10266SQuaker.Fang@Sun.COM rap->rxa_start, 510*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd - 1), 511*10266SQuaker.Fang@Sun.COM rap->rxa_qframes, rxseq, tid); 512*10266SQuaker.Fang@Sun.COM 513*10266SQuaker.Fang@Sun.COM /* 514*10266SQuaker.Fang@Sun.COM * The spec says to flush frames up to but not including: 515*10266SQuaker.Fang@Sun.COM * WinStart_B = rxseq - rap->rxa_wnd + 1 516*10266SQuaker.Fang@Sun.COM * Then insert the frame or notify the caller to process 517*10266SQuaker.Fang@Sun.COM * it immediately. We can safely do this by just starting 518*10266SQuaker.Fang@Sun.COM * over again because we know the frame will now be within 519*10266SQuaker.Fang@Sun.COM * the BA window. 520*10266SQuaker.Fang@Sun.COM */ 521*10266SQuaker.Fang@Sun.COM /* NB: rxa_wnd known to be >0 */ 522*10266SQuaker.Fang@Sun.COM ampdu_rx_flush_upto(in, rap, 523*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); 524*10266SQuaker.Fang@Sun.COM goto again; 525*10266SQuaker.Fang@Sun.COM } else { 526*10266SQuaker.Fang@Sun.COM /* 527*10266SQuaker.Fang@Sun.COM * Outside the BA window and out of range; toss. 528*10266SQuaker.Fang@Sun.COM * Sec 9.10.7.6 c) (D2.04 p.119 line 16) 529*10266SQuaker.Fang@Sun.COM */ 530*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, "MSDU" 531*10266SQuaker.Fang@Sun.COM "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n", 532*10266SQuaker.Fang@Sun.COM rap->rxa_start, 533*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 534*10266SQuaker.Fang@Sun.COM rap->rxa_qframes, rxseq, tid, 535*10266SQuaker.Fang@Sun.COM wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); 536*10266SQuaker.Fang@Sun.COM freemsg(m); 537*10266SQuaker.Fang@Sun.COM return (CONSUMED); 538*10266SQuaker.Fang@Sun.COM } 539*10266SQuaker.Fang@Sun.COM 540*10266SQuaker.Fang@Sun.COM #undef CONSUMED 541*10266SQuaker.Fang@Sun.COM #undef PROCESS 542*10266SQuaker.Fang@Sun.COM #undef IEEE80211_FC0_QOSDATA 543*10266SQuaker.Fang@Sun.COM } 544*10266SQuaker.Fang@Sun.COM 545*10266SQuaker.Fang@Sun.COM /* 546*10266SQuaker.Fang@Sun.COM * Process a BAR ctl frame. Dispatch all frames up to 547*10266SQuaker.Fang@Sun.COM * the sequence number of the frame. If this frame is 548*10266SQuaker.Fang@Sun.COM * out of range it's discarded. 549*10266SQuaker.Fang@Sun.COM */ 550*10266SQuaker.Fang@Sun.COM void 551*10266SQuaker.Fang@Sun.COM ieee80211_recv_bar(struct ieee80211_node *in, mblk_t *m0) 552*10266SQuaker.Fang@Sun.COM { 553*10266SQuaker.Fang@Sun.COM struct ieee80211_frame_bar *wh; 554*10266SQuaker.Fang@Sun.COM struct ieee80211_rx_ampdu *rap; 555*10266SQuaker.Fang@Sun.COM ieee80211_seq rxseq; 556*10266SQuaker.Fang@Sun.COM int tid, off; 557*10266SQuaker.Fang@Sun.COM 558*10266SQuaker.Fang@Sun.COM wh = (struct ieee80211_frame_bar *)m0->b_rptr; 559*10266SQuaker.Fang@Sun.COM /* check basic BAR */ 560*10266SQuaker.Fang@Sun.COM tid = MS(LE_16(wh->i_ctl), IEEE80211_BAR_TID); 561*10266SQuaker.Fang@Sun.COM rap = &in->in_rx_ampdu[tid]; 562*10266SQuaker.Fang@Sun.COM if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 563*10266SQuaker.Fang@Sun.COM /* 564*10266SQuaker.Fang@Sun.COM * No ADDBA request yet, don't touch. 565*10266SQuaker.Fang@Sun.COM */ 566*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT, 567*10266SQuaker.Fang@Sun.COM "BAR no BA stream, tid %u\n", tid); 568*10266SQuaker.Fang@Sun.COM return; 569*10266SQuaker.Fang@Sun.COM } 570*10266SQuaker.Fang@Sun.COM rxseq = LE_16(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 571*10266SQuaker.Fang@Sun.COM if (rxseq == rap->rxa_start) 572*10266SQuaker.Fang@Sun.COM return; 573*10266SQuaker.Fang@Sun.COM /* calculate offset in BA window */ 574*10266SQuaker.Fang@Sun.COM off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); 575*10266SQuaker.Fang@Sun.COM if (off < IEEE80211_SEQ_BA_RANGE) { 576*10266SQuaker.Fang@Sun.COM /* 577*10266SQuaker.Fang@Sun.COM * Flush the reorder q up to rxseq and move the window. 578*10266SQuaker.Fang@Sun.COM * Sec 9.10.7.6 a) (D2.04 p.119 line 22) 579*10266SQuaker.Fang@Sun.COM */ 580*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 581*10266SQuaker.Fang@Sun.COM "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u\n", 582*10266SQuaker.Fang@Sun.COM rap->rxa_start, 583*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 584*10266SQuaker.Fang@Sun.COM rap->rxa_qframes, rxseq, tid); 585*10266SQuaker.Fang@Sun.COM 586*10266SQuaker.Fang@Sun.COM ampdu_rx_flush_upto(in, rap, rxseq); 587*10266SQuaker.Fang@Sun.COM if (off >= rap->rxa_wnd) { 588*10266SQuaker.Fang@Sun.COM /* 589*10266SQuaker.Fang@Sun.COM * BAR specifies a window start to the right of BA 590*10266SQuaker.Fang@Sun.COM * window; we must move it explicitly since 591*10266SQuaker.Fang@Sun.COM * ampdu_rx_flush_upto will not. 592*10266SQuaker.Fang@Sun.COM */ 593*10266SQuaker.Fang@Sun.COM rap->rxa_start = rxseq; 594*10266SQuaker.Fang@Sun.COM } 595*10266SQuaker.Fang@Sun.COM } else { 596*10266SQuaker.Fang@Sun.COM /* 597*10266SQuaker.Fang@Sun.COM * Out of range; toss. 598*10266SQuaker.Fang@Sun.COM * Sec 9.10.7.6 b) (D2.04 p.119 line 41) 599*10266SQuaker.Fang@Sun.COM */ 600*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, "BAR " 601*10266SQuaker.Fang@Sun.COM "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n", 602*10266SQuaker.Fang@Sun.COM rap->rxa_start, 603*10266SQuaker.Fang@Sun.COM IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), 604*10266SQuaker.Fang@Sun.COM rap->rxa_qframes, rxseq, tid, 605*10266SQuaker.Fang@Sun.COM wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); 606*10266SQuaker.Fang@Sun.COM } 607*10266SQuaker.Fang@Sun.COM } 608*10266SQuaker.Fang@Sun.COM 609*10266SQuaker.Fang@Sun.COM /* 610*10266SQuaker.Fang@Sun.COM * Setup HT-specific state in a node. Called only 611*10266SQuaker.Fang@Sun.COM * when HT use is negotiated so we don't do extra 612*10266SQuaker.Fang@Sun.COM * work for temporary and/or legacy sta's. 613*10266SQuaker.Fang@Sun.COM */ 614*10266SQuaker.Fang@Sun.COM void 615*10266SQuaker.Fang@Sun.COM ieee80211_ht_node_init(struct ieee80211_node *in, const uint8_t *htcap) 616*10266SQuaker.Fang@Sun.COM { 617*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap; 618*10266SQuaker.Fang@Sun.COM int ac; 619*10266SQuaker.Fang@Sun.COM 620*10266SQuaker.Fang@Sun.COM if (in->in_flags & IEEE80211_NODE_HT) { 621*10266SQuaker.Fang@Sun.COM /* 622*10266SQuaker.Fang@Sun.COM * Clean AMPDU state on re-associate. This handles the case 623*10266SQuaker.Fang@Sun.COM * where a station leaves w/o notifying us and then returns 624*10266SQuaker.Fang@Sun.COM * before node is reaped for inactivity. 625*10266SQuaker.Fang@Sun.COM */ 626*10266SQuaker.Fang@Sun.COM ieee80211_ht_node_cleanup(in); 627*10266SQuaker.Fang@Sun.COM } 628*10266SQuaker.Fang@Sun.COM ieee80211_parse_htcap(in, htcap); 629*10266SQuaker.Fang@Sun.COM for (ac = 0; ac < WME_NUM_AC; ac++) { 630*10266SQuaker.Fang@Sun.COM tap = &in->in_tx_ampdu[ac]; 631*10266SQuaker.Fang@Sun.COM tap->txa_ac = (uint8_t)ac; 632*10266SQuaker.Fang@Sun.COM /* NB: further initialization deferred */ 633*10266SQuaker.Fang@Sun.COM } 634*10266SQuaker.Fang@Sun.COM in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; 635*10266SQuaker.Fang@Sun.COM } 636*10266SQuaker.Fang@Sun.COM 637*10266SQuaker.Fang@Sun.COM /* 638*10266SQuaker.Fang@Sun.COM * Cleanup HT-specific state in a node. Called only 639*10266SQuaker.Fang@Sun.COM * when HT use has been marked. 640*10266SQuaker.Fang@Sun.COM */ 641*10266SQuaker.Fang@Sun.COM void 642*10266SQuaker.Fang@Sun.COM ieee80211_ht_node_cleanup(struct ieee80211_node *in) 643*10266SQuaker.Fang@Sun.COM { 644*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 645*10266SQuaker.Fang@Sun.COM int i; 646*10266SQuaker.Fang@Sun.COM 647*10266SQuaker.Fang@Sun.COM ASSERT(in->in_flags & IEEE80211_NODE_HT); 648*10266SQuaker.Fang@Sun.COM 649*10266SQuaker.Fang@Sun.COM /* optimize this */ 650*10266SQuaker.Fang@Sun.COM for (i = 0; i < WME_NUM_AC; i++) { 651*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[i]; 652*10266SQuaker.Fang@Sun.COM if (tap->txa_flags & IEEE80211_AGGR_SETUP) { 653*10266SQuaker.Fang@Sun.COM /* 654*10266SQuaker.Fang@Sun.COM * Stop BA stream if setup so driver has a chance 655*10266SQuaker.Fang@Sun.COM * to reclaim any resources it might have allocated. 656*10266SQuaker.Fang@Sun.COM */ 657*10266SQuaker.Fang@Sun.COM ic->ic_addba_stop(in, &in->in_tx_ampdu[i]); 658*10266SQuaker.Fang@Sun.COM /* IEEE80211_TAPQ_DESTROY(tap); */ 659*10266SQuaker.Fang@Sun.COM /* NB: clearing NAK means we may re-send ADDBA */ 660*10266SQuaker.Fang@Sun.COM tap->txa_flags &= 661*10266SQuaker.Fang@Sun.COM ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); 662*10266SQuaker.Fang@Sun.COM } 663*10266SQuaker.Fang@Sun.COM } 664*10266SQuaker.Fang@Sun.COM for (i = 0; i < WME_NUM_TID; i++) 665*10266SQuaker.Fang@Sun.COM ampdu_rx_stop(&in->in_rx_ampdu[i]); 666*10266SQuaker.Fang@Sun.COM 667*10266SQuaker.Fang@Sun.COM in->in_htcap = 0; 668*10266SQuaker.Fang@Sun.COM in->in_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT | 669*10266SQuaker.Fang@Sun.COM IEEE80211_NODE_AMPDU); 670*10266SQuaker.Fang@Sun.COM } 671*10266SQuaker.Fang@Sun.COM 672*10266SQuaker.Fang@Sun.COM static struct ieee80211_channel * 673*10266SQuaker.Fang@Sun.COM findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) 674*10266SQuaker.Fang@Sun.COM { 675*10266SQuaker.Fang@Sun.COM return ieee80211_find_channel(ic, c->ich_freq, 676*10266SQuaker.Fang@Sun.COM (c->ich_flags &~ IEEE80211_CHAN_HT) | htflags); 677*10266SQuaker.Fang@Sun.COM } 678*10266SQuaker.Fang@Sun.COM 679*10266SQuaker.Fang@Sun.COM /* 680*10266SQuaker.Fang@Sun.COM * Adjust a channel to be HT/non-HT according to the vap's configuration. 681*10266SQuaker.Fang@Sun.COM */ 682*10266SQuaker.Fang@Sun.COM struct ieee80211_channel * 683*10266SQuaker.Fang@Sun.COM ieee80211_ht_adjust_channel(struct ieee80211com *ic, 684*10266SQuaker.Fang@Sun.COM struct ieee80211_channel *chan, int flags) 685*10266SQuaker.Fang@Sun.COM { 686*10266SQuaker.Fang@Sun.COM struct ieee80211_channel *c; 687*10266SQuaker.Fang@Sun.COM 688*10266SQuaker.Fang@Sun.COM if (flags & IEEE80211_FEXT_HT) { 689*10266SQuaker.Fang@Sun.COM /* promote to HT if possible */ 690*10266SQuaker.Fang@Sun.COM if (flags & IEEE80211_FEXT_USEHT40) { 691*10266SQuaker.Fang@Sun.COM if (!IEEE80211_IS_CHAN_HT40(chan)) { 692*10266SQuaker.Fang@Sun.COM /* NB: arbitrarily pick ht40+ over ht40- */ 693*10266SQuaker.Fang@Sun.COM c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); 694*10266SQuaker.Fang@Sun.COM if (c == NULL) 695*10266SQuaker.Fang@Sun.COM c = findhtchan(ic, chan, 696*10266SQuaker.Fang@Sun.COM IEEE80211_CHAN_HT40D); 697*10266SQuaker.Fang@Sun.COM if (c == NULL) 698*10266SQuaker.Fang@Sun.COM c = findhtchan(ic, chan, 699*10266SQuaker.Fang@Sun.COM IEEE80211_CHAN_HT20); 700*10266SQuaker.Fang@Sun.COM if (c != NULL) 701*10266SQuaker.Fang@Sun.COM chan = c; 702*10266SQuaker.Fang@Sun.COM } 703*10266SQuaker.Fang@Sun.COM } else if (!IEEE80211_IS_CHAN_HT20(chan)) { 704*10266SQuaker.Fang@Sun.COM c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); 705*10266SQuaker.Fang@Sun.COM if (c != NULL) 706*10266SQuaker.Fang@Sun.COM chan = c; 707*10266SQuaker.Fang@Sun.COM } 708*10266SQuaker.Fang@Sun.COM } else if (IEEE80211_IS_CHAN_HT(chan)) { 709*10266SQuaker.Fang@Sun.COM /* demote to legacy, HT use is disabled */ 710*10266SQuaker.Fang@Sun.COM c = ieee80211_find_channel(ic, chan->ich_freq, 711*10266SQuaker.Fang@Sun.COM chan->ich_flags &~ IEEE80211_CHAN_HT); 712*10266SQuaker.Fang@Sun.COM if (c != NULL) 713*10266SQuaker.Fang@Sun.COM chan = c; 714*10266SQuaker.Fang@Sun.COM } 715*10266SQuaker.Fang@Sun.COM return (chan); 716*10266SQuaker.Fang@Sun.COM } 717*10266SQuaker.Fang@Sun.COM 718*10266SQuaker.Fang@Sun.COM /* 719*10266SQuaker.Fang@Sun.COM * Setup HT-specific state for a legacy WDS peer. 720*10266SQuaker.Fang@Sun.COM */ 721*10266SQuaker.Fang@Sun.COM void 722*10266SQuaker.Fang@Sun.COM ieee80211_ht_wds_init(struct ieee80211_node *in) 723*10266SQuaker.Fang@Sun.COM { 724*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 725*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap; 726*10266SQuaker.Fang@Sun.COM int ac; 727*10266SQuaker.Fang@Sun.COM 728*10266SQuaker.Fang@Sun.COM ASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT); 729*10266SQuaker.Fang@Sun.COM 730*10266SQuaker.Fang@Sun.COM /* check scan cache in case peer has an ap and we have info */ 731*10266SQuaker.Fang@Sun.COM /* 732*10266SQuaker.Fang@Sun.COM * If setup with a legacy channel; locate an HT channel. 733*10266SQuaker.Fang@Sun.COM * Otherwise if the inherited channel (from a companion 734*10266SQuaker.Fang@Sun.COM * AP) is suitable use it so we use the same location 735*10266SQuaker.Fang@Sun.COM * for the extension channel). 736*10266SQuaker.Fang@Sun.COM */ 737*10266SQuaker.Fang@Sun.COM in->in_chan = ieee80211_ht_adjust_channel(ic, in->in_chan, 738*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext); 739*10266SQuaker.Fang@Sun.COM 740*10266SQuaker.Fang@Sun.COM in->in_htcap = 0; 741*10266SQuaker.Fang@Sun.COM if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) 742*10266SQuaker.Fang@Sun.COM in->in_htcap |= IEEE80211_HTCAP_SHORTGI20; 743*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40(in->in_chan)) { 744*10266SQuaker.Fang@Sun.COM in->in_htcap |= IEEE80211_HTCAP_CHWIDTH40; 745*10266SQuaker.Fang@Sun.COM in->in_chw = 40; 746*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40U(in->in_chan)) 747*10266SQuaker.Fang@Sun.COM in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; 748*10266SQuaker.Fang@Sun.COM else if (IEEE80211_IS_CHAN_HT40D(in->in_chan)) 749*10266SQuaker.Fang@Sun.COM in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; 750*10266SQuaker.Fang@Sun.COM if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) 751*10266SQuaker.Fang@Sun.COM in->in_htcap |= IEEE80211_HTCAP_SHORTGI40; 752*10266SQuaker.Fang@Sun.COM } else { 753*10266SQuaker.Fang@Sun.COM in->in_chw = 20; 754*10266SQuaker.Fang@Sun.COM in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; 755*10266SQuaker.Fang@Sun.COM } 756*10266SQuaker.Fang@Sun.COM in->in_htctlchan = ieee80211_chan2ieee(ic, in->in_chan); 757*10266SQuaker.Fang@Sun.COM 758*10266SQuaker.Fang@Sun.COM in->in_htopmode = 0; /* need protection state */ 759*10266SQuaker.Fang@Sun.COM in->in_htstbc = 0; /* need info */ 760*10266SQuaker.Fang@Sun.COM 761*10266SQuaker.Fang@Sun.COM for (ac = 0; ac < WME_NUM_AC; ac++) { 762*10266SQuaker.Fang@Sun.COM tap = &in->in_tx_ampdu[ac]; 763*10266SQuaker.Fang@Sun.COM tap->txa_ac = (uint8_t)ac; 764*10266SQuaker.Fang@Sun.COM } 765*10266SQuaker.Fang@Sun.COM /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */ 766*10266SQuaker.Fang@Sun.COM in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; 767*10266SQuaker.Fang@Sun.COM } 768*10266SQuaker.Fang@Sun.COM 769*10266SQuaker.Fang@Sun.COM /* 770*10266SQuaker.Fang@Sun.COM * Notify hostap vaps of a change in the HTINFO ie. 771*10266SQuaker.Fang@Sun.COM */ 772*10266SQuaker.Fang@Sun.COM static void 773*10266SQuaker.Fang@Sun.COM htinfo_notify(struct ieee80211com *ic) 774*10266SQuaker.Fang@Sun.COM { 775*10266SQuaker.Fang@Sun.COM if (ic->ic_opmode != IEEE80211_M_HOSTAP) 776*10266SQuaker.Fang@Sun.COM return; 777*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT, 778*10266SQuaker.Fang@Sun.COM "HT bss occupancy change: %d sta, %d ht, " 779*10266SQuaker.Fang@Sun.COM "%d ht40%s, HT protmode now 0x%x\n", 780*10266SQuaker.Fang@Sun.COM ic->ic_sta_assoc, 781*10266SQuaker.Fang@Sun.COM ic->ic_ht_sta_assoc, 782*10266SQuaker.Fang@Sun.COM ic->ic_ht40_sta_assoc, 783*10266SQuaker.Fang@Sun.COM (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ? 784*10266SQuaker.Fang@Sun.COM ", non-HT sta present" : "", 785*10266SQuaker.Fang@Sun.COM ic->ic_curhtprotmode); 786*10266SQuaker.Fang@Sun.COM } 787*10266SQuaker.Fang@Sun.COM 788*10266SQuaker.Fang@Sun.COM /* 789*10266SQuaker.Fang@Sun.COM * Calculate HT protection mode from current 790*10266SQuaker.Fang@Sun.COM * state and handle updates. 791*10266SQuaker.Fang@Sun.COM */ 792*10266SQuaker.Fang@Sun.COM static void 793*10266SQuaker.Fang@Sun.COM htinfo_update(struct ieee80211com *ic) 794*10266SQuaker.Fang@Sun.COM { 795*10266SQuaker.Fang@Sun.COM uint8_t protmode; 796*10266SQuaker.Fang@Sun.COM 797*10266SQuaker.Fang@Sun.COM if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) { 798*10266SQuaker.Fang@Sun.COM protmode = IEEE80211_HTINFO_OPMODE_PROTOPT 799*10266SQuaker.Fang@Sun.COM | IEEE80211_HTINFO_NONHT_PRESENT; 800*10266SQuaker.Fang@Sun.COM } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { 801*10266SQuaker.Fang@Sun.COM protmode = IEEE80211_HTINFO_OPMODE_MIXED 802*10266SQuaker.Fang@Sun.COM | IEEE80211_HTINFO_NONHT_PRESENT; 803*10266SQuaker.Fang@Sun.COM } else if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan) && 804*10266SQuaker.Fang@Sun.COM ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { 805*10266SQuaker.Fang@Sun.COM protmode = IEEE80211_HTINFO_OPMODE_HT20PR; 806*10266SQuaker.Fang@Sun.COM } else { 807*10266SQuaker.Fang@Sun.COM protmode = IEEE80211_HTINFO_OPMODE_PURE; 808*10266SQuaker.Fang@Sun.COM } 809*10266SQuaker.Fang@Sun.COM if (protmode != ic->ic_curhtprotmode) { 810*10266SQuaker.Fang@Sun.COM ic->ic_curhtprotmode = protmode; 811*10266SQuaker.Fang@Sun.COM htinfo_notify(ic); 812*10266SQuaker.Fang@Sun.COM } 813*10266SQuaker.Fang@Sun.COM } 814*10266SQuaker.Fang@Sun.COM 815*10266SQuaker.Fang@Sun.COM /* 816*10266SQuaker.Fang@Sun.COM * Handle an HT station joining a BSS. 817*10266SQuaker.Fang@Sun.COM */ 818*10266SQuaker.Fang@Sun.COM void 819*10266SQuaker.Fang@Sun.COM ieee80211_ht_node_join(struct ieee80211_node *in) 820*10266SQuaker.Fang@Sun.COM { 821*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 822*10266SQuaker.Fang@Sun.COM 823*10266SQuaker.Fang@Sun.COM IEEE80211_LOCK_ASSERT(ic); 824*10266SQuaker.Fang@Sun.COM 825*10266SQuaker.Fang@Sun.COM if (in->in_flags & IEEE80211_NODE_HT) { 826*10266SQuaker.Fang@Sun.COM ic->ic_ht_sta_assoc++; 827*10266SQuaker.Fang@Sun.COM if (in->in_chw == 40) 828*10266SQuaker.Fang@Sun.COM ic->ic_ht40_sta_assoc++; 829*10266SQuaker.Fang@Sun.COM } 830*10266SQuaker.Fang@Sun.COM htinfo_update(ic); 831*10266SQuaker.Fang@Sun.COM } 832*10266SQuaker.Fang@Sun.COM 833*10266SQuaker.Fang@Sun.COM /* 834*10266SQuaker.Fang@Sun.COM * Handle an HT station leaving a BSS. 835*10266SQuaker.Fang@Sun.COM */ 836*10266SQuaker.Fang@Sun.COM void 837*10266SQuaker.Fang@Sun.COM ieee80211_ht_node_leave(struct ieee80211_node *in) 838*10266SQuaker.Fang@Sun.COM { 839*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 840*10266SQuaker.Fang@Sun.COM 841*10266SQuaker.Fang@Sun.COM IEEE80211_LOCK_ASSERT(ic); 842*10266SQuaker.Fang@Sun.COM 843*10266SQuaker.Fang@Sun.COM if (in->in_flags & IEEE80211_NODE_HT) { 844*10266SQuaker.Fang@Sun.COM ic->ic_ht_sta_assoc--; 845*10266SQuaker.Fang@Sun.COM if (in->in_chw == 40) 846*10266SQuaker.Fang@Sun.COM ic->ic_ht40_sta_assoc--; 847*10266SQuaker.Fang@Sun.COM } 848*10266SQuaker.Fang@Sun.COM htinfo_update(ic); 849*10266SQuaker.Fang@Sun.COM } 850*10266SQuaker.Fang@Sun.COM 851*10266SQuaker.Fang@Sun.COM /* 852*10266SQuaker.Fang@Sun.COM * Public version of htinfo_update; used for processing 853*10266SQuaker.Fang@Sun.COM * beacon frames from overlapping bss in hostap_recv_mgmt. 854*10266SQuaker.Fang@Sun.COM */ 855*10266SQuaker.Fang@Sun.COM void 856*10266SQuaker.Fang@Sun.COM ieee80211_htinfo_update(struct ieee80211com *ic, int protmode) 857*10266SQuaker.Fang@Sun.COM { 858*10266SQuaker.Fang@Sun.COM if (protmode != ic->ic_curhtprotmode) { 859*10266SQuaker.Fang@Sun.COM ic->ic_curhtprotmode = (uint8_t)protmode; 860*10266SQuaker.Fang@Sun.COM htinfo_notify(ic); 861*10266SQuaker.Fang@Sun.COM } 862*10266SQuaker.Fang@Sun.COM } 863*10266SQuaker.Fang@Sun.COM 864*10266SQuaker.Fang@Sun.COM /* unalligned little endian access */ 865*10266SQuaker.Fang@Sun.COM #define LE_READ_2(p) \ 866*10266SQuaker.Fang@Sun.COM ((uint16_t) \ 867*10266SQuaker.Fang@Sun.COM ((((const uint8_t *)(p))[0]) | \ 868*10266SQuaker.Fang@Sun.COM (((const uint8_t *)(p))[1] << 8))) 869*10266SQuaker.Fang@Sun.COM 870*10266SQuaker.Fang@Sun.COM /* 871*10266SQuaker.Fang@Sun.COM * Process an 802.11n HT capabilities ie. 872*10266SQuaker.Fang@Sun.COM */ 873*10266SQuaker.Fang@Sun.COM void 874*10266SQuaker.Fang@Sun.COM ieee80211_parse_htcap(struct ieee80211_node *in, const uint8_t *ie) 875*10266SQuaker.Fang@Sun.COM { 876*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 877*10266SQuaker.Fang@Sun.COM 878*10266SQuaker.Fang@Sun.COM if (ie[0] == IEEE80211_ELEMID_VENDOR) { 879*10266SQuaker.Fang@Sun.COM /* 880*10266SQuaker.Fang@Sun.COM * Station used Vendor OUI ie to associate; 881*10266SQuaker.Fang@Sun.COM * mark the node so when we respond we'll use 882*10266SQuaker.Fang@Sun.COM * the Vendor OUI's and not the standard ie's. 883*10266SQuaker.Fang@Sun.COM */ 884*10266SQuaker.Fang@Sun.COM in->in_flags |= IEEE80211_NODE_HTCOMPAT; 885*10266SQuaker.Fang@Sun.COM ie += 4; 886*10266SQuaker.Fang@Sun.COM } else 887*10266SQuaker.Fang@Sun.COM in->in_flags &= ~IEEE80211_NODE_HTCOMPAT; 888*10266SQuaker.Fang@Sun.COM 889*10266SQuaker.Fang@Sun.COM in->in_htcap = *(uint16_t *)(ie + 890*10266SQuaker.Fang@Sun.COM offsetof(struct ieee80211_ie_htcap, hc_cap)); 891*10266SQuaker.Fang@Sun.COM in->in_htparam = ie[offsetof(struct ieee80211_ie_htcap, hc_param)]; 892*10266SQuaker.Fang@Sun.COM /* needed or will ieee80211_parse_htinfo always be called? */ 893*10266SQuaker.Fang@Sun.COM in->in_chw = (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40) && 894*10266SQuaker.Fang@Sun.COM (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20; 895*10266SQuaker.Fang@Sun.COM } 896*10266SQuaker.Fang@Sun.COM 897*10266SQuaker.Fang@Sun.COM /* 898*10266SQuaker.Fang@Sun.COM * Process an 802.11n HT info ie and update the node state. 899*10266SQuaker.Fang@Sun.COM * Note that we handle use this information to identify the 900*10266SQuaker.Fang@Sun.COM * correct channel (HT20, HT40+, HT40-, legacy). The caller 901*10266SQuaker.Fang@Sun.COM * is responsible for insuring any required channel change is 902*10266SQuaker.Fang@Sun.COM * done (e.g. in sta mode when parsing the contents of a 903*10266SQuaker.Fang@Sun.COM * beacon frame). 904*10266SQuaker.Fang@Sun.COM */ 905*10266SQuaker.Fang@Sun.COM void 906*10266SQuaker.Fang@Sun.COM ieee80211_parse_htinfo(struct ieee80211_node *in, const uint8_t *ie) 907*10266SQuaker.Fang@Sun.COM { 908*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 909*10266SQuaker.Fang@Sun.COM const struct ieee80211_ie_htinfo *htinfo; 910*10266SQuaker.Fang@Sun.COM struct ieee80211_channel *c; 911*10266SQuaker.Fang@Sun.COM uint16_t w; 912*10266SQuaker.Fang@Sun.COM int htflags, chanflags; 913*10266SQuaker.Fang@Sun.COM 914*10266SQuaker.Fang@Sun.COM if (ie[0] == IEEE80211_ELEMID_VENDOR) 915*10266SQuaker.Fang@Sun.COM ie += 4; 916*10266SQuaker.Fang@Sun.COM htinfo = (const struct ieee80211_ie_htinfo *)ie; 917*10266SQuaker.Fang@Sun.COM in->in_htctlchan = htinfo->hi_ctrlchannel; 918*10266SQuaker.Fang@Sun.COM in->in_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); 919*10266SQuaker.Fang@Sun.COM w = *(uint16_t *)(&htinfo->hi_byte2); 920*10266SQuaker.Fang@Sun.COM in->in_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); 921*10266SQuaker.Fang@Sun.COM w = *(uint16_t *)(&htinfo->hi_byte45); 922*10266SQuaker.Fang@Sun.COM in->in_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); 923*10266SQuaker.Fang@Sun.COM /* 924*10266SQuaker.Fang@Sun.COM * Handle 11n channel switch. Use the received HT ie's to 925*10266SQuaker.Fang@Sun.COM * identify the right channel to use. If we cannot locate it 926*10266SQuaker.Fang@Sun.COM * in the channel table then fallback to legacy operation. 927*10266SQuaker.Fang@Sun.COM */ 928*10266SQuaker.Fang@Sun.COM htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ? 929*10266SQuaker.Fang@Sun.COM IEEE80211_CHAN_HT20 : 0; 930*10266SQuaker.Fang@Sun.COM /* NB: honor operating mode constraint */ 931*10266SQuaker.Fang@Sun.COM if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && 932*10266SQuaker.Fang@Sun.COM (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) { 933*10266SQuaker.Fang@Sun.COM if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) 934*10266SQuaker.Fang@Sun.COM htflags = IEEE80211_CHAN_HT40U; 935*10266SQuaker.Fang@Sun.COM else if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) 936*10266SQuaker.Fang@Sun.COM htflags = IEEE80211_CHAN_HT40D; 937*10266SQuaker.Fang@Sun.COM } 938*10266SQuaker.Fang@Sun.COM chanflags = (in->in_chan->ich_flags &~ IEEE80211_CHAN_HT) | htflags; 939*10266SQuaker.Fang@Sun.COM if (chanflags != in->in_chan->ich_flags) { 940*10266SQuaker.Fang@Sun.COM c = ieee80211_find_channel(ic, 941*10266SQuaker.Fang@Sun.COM in->in_chan->ich_freq, chanflags); 942*10266SQuaker.Fang@Sun.COM if (c == NULL && htflags != IEEE80211_CHAN_HT20) { 943*10266SQuaker.Fang@Sun.COM /* 944*10266SQuaker.Fang@Sun.COM * No HT40 channel entry in our table; fall back 945*10266SQuaker.Fang@Sun.COM * to HT20 operation. This should not happen. 946*10266SQuaker.Fang@Sun.COM */ 947*10266SQuaker.Fang@Sun.COM c = findhtchan(ic, in->in_chan, IEEE80211_CHAN_HT20); 948*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT, 949*10266SQuaker.Fang@Sun.COM "no HT40 channel (freq %u), falling back to HT20\n", 950*10266SQuaker.Fang@Sun.COM in->in_chan->ich_freq); 951*10266SQuaker.Fang@Sun.COM /* stat */ 952*10266SQuaker.Fang@Sun.COM } 953*10266SQuaker.Fang@Sun.COM if (c != NULL && c != in->in_chan) { 954*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT, 955*10266SQuaker.Fang@Sun.COM "switch station to HT%d channel %u/0x%x\n", 956*10266SQuaker.Fang@Sun.COM IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, 957*10266SQuaker.Fang@Sun.COM c->ich_freq, c->ich_flags); 958*10266SQuaker.Fang@Sun.COM in->in_chan = c; 959*10266SQuaker.Fang@Sun.COM } 960*10266SQuaker.Fang@Sun.COM /* NB: caller responsible for forcing any channel change */ 961*10266SQuaker.Fang@Sun.COM } 962*10266SQuaker.Fang@Sun.COM /* update node's tx channel width */ 963*10266SQuaker.Fang@Sun.COM in->in_chw = IEEE80211_IS_CHAN_HT40(in->in_chan)? 40 : 20; 964*10266SQuaker.Fang@Sun.COM } 965*10266SQuaker.Fang@Sun.COM 966*10266SQuaker.Fang@Sun.COM /* 967*10266SQuaker.Fang@Sun.COM * Install received HT rate set by parsing the HT cap ie. 968*10266SQuaker.Fang@Sun.COM */ 969*10266SQuaker.Fang@Sun.COM int 970*10266SQuaker.Fang@Sun.COM ieee80211_setup_htrates(struct ieee80211_node *in, const uint8_t *ie, int flags) 971*10266SQuaker.Fang@Sun.COM { 972*10266SQuaker.Fang@Sun.COM const struct ieee80211_ie_htcap *htcap; 973*10266SQuaker.Fang@Sun.COM struct ieee80211_htrateset *rs; 974*10266SQuaker.Fang@Sun.COM int i; 975*10266SQuaker.Fang@Sun.COM 976*10266SQuaker.Fang@Sun.COM rs = &in->in_htrates; 977*10266SQuaker.Fang@Sun.COM (void) memset(rs, 0, sizeof (*rs)); 978*10266SQuaker.Fang@Sun.COM if (ie != NULL) { 979*10266SQuaker.Fang@Sun.COM if (ie[0] == IEEE80211_ELEMID_VENDOR) 980*10266SQuaker.Fang@Sun.COM ie += 4; 981*10266SQuaker.Fang@Sun.COM htcap = (const struct ieee80211_ie_htcap *) ie; 982*10266SQuaker.Fang@Sun.COM for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 983*10266SQuaker.Fang@Sun.COM if (ieee80211_isclr(htcap->hc_mcsset, i)) 984*10266SQuaker.Fang@Sun.COM continue; 985*10266SQuaker.Fang@Sun.COM if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { 986*10266SQuaker.Fang@Sun.COM ieee80211_dbg( 987*10266SQuaker.Fang@Sun.COM IEEE80211_MSG_XRATE | IEEE80211_MSG_HT, 988*10266SQuaker.Fang@Sun.COM "WARNING, HT rate set too large; only " 989*10266SQuaker.Fang@Sun.COM "using %u rates\n", 990*10266SQuaker.Fang@Sun.COM IEEE80211_HTRATE_MAXSIZE); 991*10266SQuaker.Fang@Sun.COM break; 992*10266SQuaker.Fang@Sun.COM } 993*10266SQuaker.Fang@Sun.COM rs->rs_rates[rs->rs_nrates++] = (uint8_t)i; 994*10266SQuaker.Fang@Sun.COM } 995*10266SQuaker.Fang@Sun.COM } 996*10266SQuaker.Fang@Sun.COM return (ieee80211_fix_rate(in, (struct ieee80211_rateset *)rs, flags)); 997*10266SQuaker.Fang@Sun.COM } 998*10266SQuaker.Fang@Sun.COM 999*10266SQuaker.Fang@Sun.COM /* 1000*10266SQuaker.Fang@Sun.COM * Mark rates in a node's HT rate set as basic according 1001*10266SQuaker.Fang@Sun.COM * to the information in the supplied HT info ie. 1002*10266SQuaker.Fang@Sun.COM */ 1003*10266SQuaker.Fang@Sun.COM void 1004*10266SQuaker.Fang@Sun.COM ieee80211_setup_basic_htrates(struct ieee80211_node *in, const uint8_t *ie) 1005*10266SQuaker.Fang@Sun.COM { 1006*10266SQuaker.Fang@Sun.COM const struct ieee80211_ie_htinfo *htinfo; 1007*10266SQuaker.Fang@Sun.COM struct ieee80211_htrateset *rs; 1008*10266SQuaker.Fang@Sun.COM int i, j; 1009*10266SQuaker.Fang@Sun.COM 1010*10266SQuaker.Fang@Sun.COM if (ie[0] == IEEE80211_ELEMID_VENDOR) 1011*10266SQuaker.Fang@Sun.COM ie += 4; 1012*10266SQuaker.Fang@Sun.COM htinfo = (const struct ieee80211_ie_htinfo *) ie; 1013*10266SQuaker.Fang@Sun.COM rs = &in->in_htrates; 1014*10266SQuaker.Fang@Sun.COM if (rs->rs_nrates == 0) { 1015*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_XRATE | IEEE80211_MSG_HT, 1016*10266SQuaker.Fang@Sun.COM "WARNING, empty HT rate set\n"); 1017*10266SQuaker.Fang@Sun.COM return; 1018*10266SQuaker.Fang@Sun.COM } 1019*10266SQuaker.Fang@Sun.COM for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { 1020*10266SQuaker.Fang@Sun.COM if (ieee80211_isclr(htinfo->hi_basicmcsset, i)) 1021*10266SQuaker.Fang@Sun.COM continue; 1022*10266SQuaker.Fang@Sun.COM for (j = 0; j < rs->rs_nrates; j++) 1023*10266SQuaker.Fang@Sun.COM if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) 1024*10266SQuaker.Fang@Sun.COM rs->rs_rates[j] |= IEEE80211_RATE_BASIC; 1025*10266SQuaker.Fang@Sun.COM } 1026*10266SQuaker.Fang@Sun.COM } 1027*10266SQuaker.Fang@Sun.COM 1028*10266SQuaker.Fang@Sun.COM static void 1029*10266SQuaker.Fang@Sun.COM addba_timeout(void *arg) 1030*10266SQuaker.Fang@Sun.COM { 1031*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap = arg; 1032*10266SQuaker.Fang@Sun.COM 1033*10266SQuaker.Fang@Sun.COM tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 1034*10266SQuaker.Fang@Sun.COM tap->txa_attempts++; 1035*10266SQuaker.Fang@Sun.COM } 1036*10266SQuaker.Fang@Sun.COM 1037*10266SQuaker.Fang@Sun.COM static void 1038*10266SQuaker.Fang@Sun.COM addba_start_timeout(struct ieee80211_tx_ampdu *tap) 1039*10266SQuaker.Fang@Sun.COM { 1040*10266SQuaker.Fang@Sun.COM tap->txa_timer = timeout(addba_timeout, (void *)tap, 1041*10266SQuaker.Fang@Sun.COM drv_usectohz(IEEE80211_AGGR_TIMEOUT * 1000)); 1042*10266SQuaker.Fang@Sun.COM tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; 1043*10266SQuaker.Fang@Sun.COM tap->txa_lastrequest = ddi_get_lbolt(); 1044*10266SQuaker.Fang@Sun.COM } 1045*10266SQuaker.Fang@Sun.COM 1046*10266SQuaker.Fang@Sun.COM static void 1047*10266SQuaker.Fang@Sun.COM addba_stop_timeout(struct ieee80211_tx_ampdu *tap) 1048*10266SQuaker.Fang@Sun.COM { 1049*10266SQuaker.Fang@Sun.COM if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { 1050*10266SQuaker.Fang@Sun.COM if (tap->txa_timer != NULL) { 1051*10266SQuaker.Fang@Sun.COM (void) untimeout(tap->txa_timer); 1052*10266SQuaker.Fang@Sun.COM tap->txa_timer = NULL; 1053*10266SQuaker.Fang@Sun.COM } 1054*10266SQuaker.Fang@Sun.COM tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; 1055*10266SQuaker.Fang@Sun.COM } 1056*10266SQuaker.Fang@Sun.COM } 1057*10266SQuaker.Fang@Sun.COM 1058*10266SQuaker.Fang@Sun.COM /* 1059*10266SQuaker.Fang@Sun.COM * Default method for requesting A-MPDU tx aggregation. 1060*10266SQuaker.Fang@Sun.COM * We setup the specified state block and start a timer 1061*10266SQuaker.Fang@Sun.COM * to wait for an ADDBA response frame. 1062*10266SQuaker.Fang@Sun.COM */ 1063*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1064*10266SQuaker.Fang@Sun.COM static int 1065*10266SQuaker.Fang@Sun.COM ieee80211_addba_request(struct ieee80211_node *in, 1066*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap, 1067*10266SQuaker.Fang@Sun.COM int dialogtoken, int baparamset, int batimeout) 1068*10266SQuaker.Fang@Sun.COM { 1069*10266SQuaker.Fang@Sun.COM int bufsiz; 1070*10266SQuaker.Fang@Sun.COM 1071*10266SQuaker.Fang@Sun.COM tap->txa_token = (uint8_t)dialogtoken; 1072*10266SQuaker.Fang@Sun.COM tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; 1073*10266SQuaker.Fang@Sun.COM tap->txa_start = tap->txa_seqstart = 0; 1074*10266SQuaker.Fang@Sun.COM bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1075*10266SQuaker.Fang@Sun.COM tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX 1076*10266SQuaker.Fang@Sun.COM : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX); 1077*10266SQuaker.Fang@Sun.COM addba_start_timeout(tap); 1078*10266SQuaker.Fang@Sun.COM return (1); 1079*10266SQuaker.Fang@Sun.COM } 1080*10266SQuaker.Fang@Sun.COM 1081*10266SQuaker.Fang@Sun.COM /* 1082*10266SQuaker.Fang@Sun.COM * Default method for processing an A-MPDU tx aggregation 1083*10266SQuaker.Fang@Sun.COM * response. We shutdown any pending timer and update the 1084*10266SQuaker.Fang@Sun.COM * state block according to the reply. 1085*10266SQuaker.Fang@Sun.COM */ 1086*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1087*10266SQuaker.Fang@Sun.COM static int 1088*10266SQuaker.Fang@Sun.COM ieee80211_addba_response(struct ieee80211_node *in, 1089*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap, 1090*10266SQuaker.Fang@Sun.COM int status, int baparamset, int batimeout) 1091*10266SQuaker.Fang@Sun.COM { 1092*10266SQuaker.Fang@Sun.COM int bufsiz; 1093*10266SQuaker.Fang@Sun.COM 1094*10266SQuaker.Fang@Sun.COM addba_stop_timeout(tap); 1095*10266SQuaker.Fang@Sun.COM if (status == IEEE80211_STATUS_SUCCESS) { 1096*10266SQuaker.Fang@Sun.COM bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1097*10266SQuaker.Fang@Sun.COM /* override our request? */ 1098*10266SQuaker.Fang@Sun.COM tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX 1099*10266SQuaker.Fang@Sun.COM : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX); 1100*10266SQuaker.Fang@Sun.COM tap->txa_flags |= IEEE80211_AGGR_RUNNING; 1101*10266SQuaker.Fang@Sun.COM } else { 1102*10266SQuaker.Fang@Sun.COM /* mark tid so we don't try again */ 1103*10266SQuaker.Fang@Sun.COM tap->txa_flags |= IEEE80211_AGGR_NAK; 1104*10266SQuaker.Fang@Sun.COM } 1105*10266SQuaker.Fang@Sun.COM return (1); 1106*10266SQuaker.Fang@Sun.COM } 1107*10266SQuaker.Fang@Sun.COM 1108*10266SQuaker.Fang@Sun.COM /* 1109*10266SQuaker.Fang@Sun.COM * Default method for stopping A-MPDU tx aggregation. 1110*10266SQuaker.Fang@Sun.COM * Any timer is cleared and we drain any pending frames. 1111*10266SQuaker.Fang@Sun.COM */ 1112*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1113*10266SQuaker.Fang@Sun.COM static void 1114*10266SQuaker.Fang@Sun.COM ieee80211_addba_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap) 1115*10266SQuaker.Fang@Sun.COM { 1116*10266SQuaker.Fang@Sun.COM addba_stop_timeout(tap); 1117*10266SQuaker.Fang@Sun.COM if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { 1118*10266SQuaker.Fang@Sun.COM /* clear aggregation queue */ 1119*10266SQuaker.Fang@Sun.COM tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; 1120*10266SQuaker.Fang@Sun.COM } 1121*10266SQuaker.Fang@Sun.COM tap->txa_attempts = 0; 1122*10266SQuaker.Fang@Sun.COM } 1123*10266SQuaker.Fang@Sun.COM 1124*10266SQuaker.Fang@Sun.COM /* 1125*10266SQuaker.Fang@Sun.COM * Process a received action frame using the default aggregation 1126*10266SQuaker.Fang@Sun.COM * policy. We intercept ADDBA-related frames and use them to 1127*10266SQuaker.Fang@Sun.COM * update our aggregation state. All other frames are passed up 1128*10266SQuaker.Fang@Sun.COM * for processing by ieee80211_recv_action. 1129*10266SQuaker.Fang@Sun.COM */ 1130*10266SQuaker.Fang@Sun.COM static void 1131*10266SQuaker.Fang@Sun.COM ieee80211_aggr_recv_action(struct ieee80211_node *in, 1132*10266SQuaker.Fang@Sun.COM const uint8_t *frm, const uint8_t *efrm) 1133*10266SQuaker.Fang@Sun.COM { 1134*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1135*10266SQuaker.Fang@Sun.COM const struct ieee80211_action *ia; 1136*10266SQuaker.Fang@Sun.COM struct ieee80211_rx_ampdu *rap; 1137*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap; 1138*10266SQuaker.Fang@Sun.COM uint8_t dialogtoken; 1139*10266SQuaker.Fang@Sun.COM uint16_t baparamset, batimeout, baseqctl, code; 1140*10266SQuaker.Fang@Sun.COM uint16_t args[4]; 1141*10266SQuaker.Fang@Sun.COM int tid, ac, bufsiz; 1142*10266SQuaker.Fang@Sun.COM 1143*10266SQuaker.Fang@Sun.COM ia = (const struct ieee80211_action *) frm; 1144*10266SQuaker.Fang@Sun.COM switch (ia->ia_category) { 1145*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_CAT_BA: 1146*10266SQuaker.Fang@Sun.COM switch (ia->ia_action) { 1147*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_ADDBA_REQUEST: 1148*10266SQuaker.Fang@Sun.COM dialogtoken = frm[2]; 1149*10266SQuaker.Fang@Sun.COM baparamset = *(uint16_t *)(frm+3); 1150*10266SQuaker.Fang@Sun.COM batimeout = *(uint16_t *)(frm+5); 1151*10266SQuaker.Fang@Sun.COM baseqctl = *(uint16_t *)(frm+7); 1152*10266SQuaker.Fang@Sun.COM 1153*10266SQuaker.Fang@Sun.COM tid = MS(baparamset, IEEE80211_BAPS_TID); 1154*10266SQuaker.Fang@Sun.COM bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1155*10266SQuaker.Fang@Sun.COM 1156*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1157*10266SQuaker.Fang@Sun.COM "recv ADDBA request: dialogtoken %u " 1158*10266SQuaker.Fang@Sun.COM "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " 1159*10266SQuaker.Fang@Sun.COM "baseqctl %d:%d\n", 1160*10266SQuaker.Fang@Sun.COM dialogtoken, baparamset, tid, bufsiz, batimeout, 1161*10266SQuaker.Fang@Sun.COM MS(baseqctl, IEEE80211_BASEQ_START), 1162*10266SQuaker.Fang@Sun.COM MS(baseqctl, IEEE80211_BASEQ_FRAG)); 1163*10266SQuaker.Fang@Sun.COM 1164*10266SQuaker.Fang@Sun.COM rap = &in->in_rx_ampdu[tid]; 1165*10266SQuaker.Fang@Sun.COM 1166*10266SQuaker.Fang@Sun.COM /* Send ADDBA response */ 1167*10266SQuaker.Fang@Sun.COM args[0] = dialogtoken; 1168*10266SQuaker.Fang@Sun.COM /* 1169*10266SQuaker.Fang@Sun.COM * NB: We ack only if the sta associated with HT and 1170*10266SQuaker.Fang@Sun.COM * the ap is configured to do AMPDU rx (the latter 1171*10266SQuaker.Fang@Sun.COM * violates the 11n spec and is mostly for testing). 1172*10266SQuaker.Fang@Sun.COM */ 1173*10266SQuaker.Fang@Sun.COM if ((in->in_flags & IEEE80211_NODE_AMPDU_RX) && 1174*10266SQuaker.Fang@Sun.COM (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) { 1175*10266SQuaker.Fang@Sun.COM ampdu_rx_start(rap, bufsiz, 1176*10266SQuaker.Fang@Sun.COM MS(baseqctl, IEEE80211_BASEQ_START)); 1177*10266SQuaker.Fang@Sun.COM 1178*10266SQuaker.Fang@Sun.COM args[1] = IEEE80211_STATUS_SUCCESS; 1179*10266SQuaker.Fang@Sun.COM } else { 1180*10266SQuaker.Fang@Sun.COM ieee80211_dbg( 1181*10266SQuaker.Fang@Sun.COM IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1182*10266SQuaker.Fang@Sun.COM "reject ADDBA request: %s\n", 1183*10266SQuaker.Fang@Sun.COM in->in_flags & IEEE80211_NODE_AMPDU_RX ? 1184*10266SQuaker.Fang@Sun.COM "administratively disabled" : 1185*10266SQuaker.Fang@Sun.COM "not negotiated for station"); 1186*10266SQuaker.Fang@Sun.COM args[1] = IEEE80211_STATUS_UNSPECIFIED; 1187*10266SQuaker.Fang@Sun.COM } 1188*10266SQuaker.Fang@Sun.COM /* honor rap flags? */ 1189*10266SQuaker.Fang@Sun.COM args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE 1190*10266SQuaker.Fang@Sun.COM | SM(tid, IEEE80211_BAPS_TID) 1191*10266SQuaker.Fang@Sun.COM | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ); 1192*10266SQuaker.Fang@Sun.COM args[3] = 0; 1193*10266SQuaker.Fang@Sun.COM ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA, 1194*10266SQuaker.Fang@Sun.COM IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); 1195*10266SQuaker.Fang@Sun.COM return; 1196*10266SQuaker.Fang@Sun.COM 1197*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 1198*10266SQuaker.Fang@Sun.COM dialogtoken = frm[2]; 1199*10266SQuaker.Fang@Sun.COM code = *(uint16_t *)(frm+3); 1200*10266SQuaker.Fang@Sun.COM baparamset = *(uint16_t *)(frm+5); 1201*10266SQuaker.Fang@Sun.COM tid = MS(baparamset, IEEE80211_BAPS_TID); 1202*10266SQuaker.Fang@Sun.COM bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); 1203*10266SQuaker.Fang@Sun.COM batimeout = *(uint16_t *)(frm+7); 1204*10266SQuaker.Fang@Sun.COM 1205*10266SQuaker.Fang@Sun.COM ac = TID_TO_WME_AC(tid); 1206*10266SQuaker.Fang@Sun.COM tap = &in->in_tx_ampdu[ac]; 1207*10266SQuaker.Fang@Sun.COM if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { 1208*10266SQuaker.Fang@Sun.COM ieee80211_err("ADDBA response" 1209*10266SQuaker.Fang@Sun.COM "no pending ADDBA, tid %d dialogtoken %u " 1210*10266SQuaker.Fang@Sun.COM "code %d\n", tid, dialogtoken, code); 1211*10266SQuaker.Fang@Sun.COM return; 1212*10266SQuaker.Fang@Sun.COM } 1213*10266SQuaker.Fang@Sun.COM if (dialogtoken != tap->txa_token) { 1214*10266SQuaker.Fang@Sun.COM ieee80211_err("ADDBA response" 1215*10266SQuaker.Fang@Sun.COM "dialogtoken mismatch: waiting for %d, " 1216*10266SQuaker.Fang@Sun.COM "received %d, tid %d code %d\n", 1217*10266SQuaker.Fang@Sun.COM tap->txa_token, dialogtoken, tid, code); 1218*10266SQuaker.Fang@Sun.COM return; 1219*10266SQuaker.Fang@Sun.COM } 1220*10266SQuaker.Fang@Sun.COM 1221*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1222*10266SQuaker.Fang@Sun.COM "recv ADDBA response: dialogtoken %u code %d " 1223*10266SQuaker.Fang@Sun.COM "baparamset 0x%x (tid %d bufsiz %d) batimeout %d\n", 1224*10266SQuaker.Fang@Sun.COM dialogtoken, code, baparamset, tid, bufsiz, 1225*10266SQuaker.Fang@Sun.COM batimeout); 1226*10266SQuaker.Fang@Sun.COM ic->ic_addba_response(in, tap, 1227*10266SQuaker.Fang@Sun.COM code, baparamset, batimeout); 1228*10266SQuaker.Fang@Sun.COM return; 1229*10266SQuaker.Fang@Sun.COM 1230*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_DELBA: 1231*10266SQuaker.Fang@Sun.COM baparamset = *(uint16_t *)(frm+2); 1232*10266SQuaker.Fang@Sun.COM code = *(uint16_t *)(frm+4); 1233*10266SQuaker.Fang@Sun.COM 1234*10266SQuaker.Fang@Sun.COM tid = MS(baparamset, IEEE80211_DELBAPS_TID); 1235*10266SQuaker.Fang@Sun.COM 1236*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1237*10266SQuaker.Fang@Sun.COM "recv DELBA: baparamset 0x%x (tid %d initiator %d) " 1238*10266SQuaker.Fang@Sun.COM "code %d\n", baparamset, tid, 1239*10266SQuaker.Fang@Sun.COM MS(baparamset, IEEE80211_DELBAPS_INIT), code); 1240*10266SQuaker.Fang@Sun.COM 1241*10266SQuaker.Fang@Sun.COM if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { 1242*10266SQuaker.Fang@Sun.COM ac = TID_TO_WME_AC(tid); 1243*10266SQuaker.Fang@Sun.COM tap = &in->in_tx_ampdu[ac]; 1244*10266SQuaker.Fang@Sun.COM ic->ic_addba_stop(in, tap); 1245*10266SQuaker.Fang@Sun.COM } else { 1246*10266SQuaker.Fang@Sun.COM rap = &in->in_rx_ampdu[tid]; 1247*10266SQuaker.Fang@Sun.COM ampdu_rx_stop(rap); 1248*10266SQuaker.Fang@Sun.COM } 1249*10266SQuaker.Fang@Sun.COM return; 1250*10266SQuaker.Fang@Sun.COM } 1251*10266SQuaker.Fang@Sun.COM break; 1252*10266SQuaker.Fang@Sun.COM } 1253*10266SQuaker.Fang@Sun.COM ieee80211_recv_action(in, frm, efrm); 1254*10266SQuaker.Fang@Sun.COM } 1255*10266SQuaker.Fang@Sun.COM 1256*10266SQuaker.Fang@Sun.COM /* 1257*10266SQuaker.Fang@Sun.COM * Process a received 802.11n action frame. 1258*10266SQuaker.Fang@Sun.COM * Aggregation-related frames are assumed to be handled 1259*10266SQuaker.Fang@Sun.COM * already; we handle any other frames we can, otherwise 1260*10266SQuaker.Fang@Sun.COM * complain about being unsupported (with debugging). 1261*10266SQuaker.Fang@Sun.COM */ 1262*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1263*10266SQuaker.Fang@Sun.COM void 1264*10266SQuaker.Fang@Sun.COM ieee80211_recv_action(struct ieee80211_node *in, 1265*10266SQuaker.Fang@Sun.COM const uint8_t *frm, const uint8_t *efrm) 1266*10266SQuaker.Fang@Sun.COM { 1267*10266SQuaker.Fang@Sun.COM const struct ieee80211_action *ia; 1268*10266SQuaker.Fang@Sun.COM int chw; 1269*10266SQuaker.Fang@Sun.COM 1270*10266SQuaker.Fang@Sun.COM ia = (const struct ieee80211_action *) frm; 1271*10266SQuaker.Fang@Sun.COM switch (ia->ia_category) { 1272*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_CAT_BA: 1273*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1274*10266SQuaker.Fang@Sun.COM "BA action %d not implemented\n", 1275*10266SQuaker.Fang@Sun.COM ia->ia_action); 1276*10266SQuaker.Fang@Sun.COM break; 1277*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_CAT_HT: 1278*10266SQuaker.Fang@Sun.COM switch (ia->ia_action) { 1279*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_HT_TXCHWIDTH: 1280*10266SQuaker.Fang@Sun.COM chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20; 1281*10266SQuaker.Fang@Sun.COM if (chw != in->in_chw) { 1282*10266SQuaker.Fang@Sun.COM in->in_chw = (uint8_t)chw; 1283*10266SQuaker.Fang@Sun.COM in->in_flags |= IEEE80211_NODE_CHWUPDATE; 1284*10266SQuaker.Fang@Sun.COM } 1285*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1286*10266SQuaker.Fang@Sun.COM "HT txchwidth, width %d (%s)\n", 1287*10266SQuaker.Fang@Sun.COM chw, 1288*10266SQuaker.Fang@Sun.COM in->in_flags & IEEE80211_NODE_CHWUPDATE ? 1289*10266SQuaker.Fang@Sun.COM "new" : "no change"); 1290*10266SQuaker.Fang@Sun.COM break; 1291*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_HT_MIMOPWRSAVE: 1292*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1293*10266SQuaker.Fang@Sun.COM "HT MIMO PS\n"); 1294*10266SQuaker.Fang@Sun.COM break; 1295*10266SQuaker.Fang@Sun.COM default: 1296*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1297*10266SQuaker.Fang@Sun.COM "HT action %d not implemented\n", 1298*10266SQuaker.Fang@Sun.COM ia->ia_action); 1299*10266SQuaker.Fang@Sun.COM break; 1300*10266SQuaker.Fang@Sun.COM } 1301*10266SQuaker.Fang@Sun.COM break; 1302*10266SQuaker.Fang@Sun.COM default: 1303*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1304*10266SQuaker.Fang@Sun.COM "category %d not implemented\n", 1305*10266SQuaker.Fang@Sun.COM ia->ia_category); 1306*10266SQuaker.Fang@Sun.COM break; 1307*10266SQuaker.Fang@Sun.COM } 1308*10266SQuaker.Fang@Sun.COM } 1309*10266SQuaker.Fang@Sun.COM 1310*10266SQuaker.Fang@Sun.COM /* 1311*10266SQuaker.Fang@Sun.COM * Transmit processing. 1312*10266SQuaker.Fang@Sun.COM */ 1313*10266SQuaker.Fang@Sun.COM 1314*10266SQuaker.Fang@Sun.COM /* 1315*10266SQuaker.Fang@Sun.COM * Request A-MPDU tx aggregation. Setup local state and 1316*10266SQuaker.Fang@Sun.COM * issue an ADDBA request. BA use will only happen after 1317*10266SQuaker.Fang@Sun.COM * the other end replies with ADDBA response. 1318*10266SQuaker.Fang@Sun.COM */ 1319*10266SQuaker.Fang@Sun.COM int 1320*10266SQuaker.Fang@Sun.COM ieee80211_ampdu_request(struct ieee80211_node *in, 1321*10266SQuaker.Fang@Sun.COM struct ieee80211_tx_ampdu *tap) 1322*10266SQuaker.Fang@Sun.COM { 1323*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1324*10266SQuaker.Fang@Sun.COM uint16_t args[4]; 1325*10266SQuaker.Fang@Sun.COM int tid, dialogtoken; 1326*10266SQuaker.Fang@Sun.COM static int tokens = 0; /* tokens */ 1327*10266SQuaker.Fang@Sun.COM clock_t ticks; 1328*10266SQuaker.Fang@Sun.COM 1329*10266SQuaker.Fang@Sun.COM ticks = ddi_get_lbolt(); 1330*10266SQuaker.Fang@Sun.COM if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { 1331*10266SQuaker.Fang@Sun.COM /* do deferred setup of state */ 1332*10266SQuaker.Fang@Sun.COM tap->txa_flags |= IEEE80211_AGGR_SETUP; 1333*10266SQuaker.Fang@Sun.COM } 1334*10266SQuaker.Fang@Sun.COM if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES && 1335*10266SQuaker.Fang@Sun.COM (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) { 1336*10266SQuaker.Fang@Sun.COM /* 1337*10266SQuaker.Fang@Sun.COM * Don't retry too often; IEEE80211_AGGR_MINRETRY 1338*10266SQuaker.Fang@Sun.COM * defines the minimum interval we'll retry after 1339*10266SQuaker.Fang@Sun.COM * IEEE80211_AGGR_MAXTRIES failed attempts to 1340*10266SQuaker.Fang@Sun.COM * negotiate use. 1341*10266SQuaker.Fang@Sun.COM */ 1342*10266SQuaker.Fang@Sun.COM return (0); 1343*10266SQuaker.Fang@Sun.COM } 1344*10266SQuaker.Fang@Sun.COM /* hack for not doing proper locking */ 1345*10266SQuaker.Fang@Sun.COM tap->txa_flags &= ~IEEE80211_AGGR_NAK; 1346*10266SQuaker.Fang@Sun.COM 1347*10266SQuaker.Fang@Sun.COM dialogtoken = (tokens+1) % 63; /* algorithm */ 1348*10266SQuaker.Fang@Sun.COM 1349*10266SQuaker.Fang@Sun.COM tid = WME_AC_TO_TID(tap->txa_ac); 1350*10266SQuaker.Fang@Sun.COM args[0] = (uint16_t)dialogtoken; 1351*10266SQuaker.Fang@Sun.COM args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE 1352*10266SQuaker.Fang@Sun.COM | SM(tid, IEEE80211_BAPS_TID) 1353*10266SQuaker.Fang@Sun.COM | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ); 1354*10266SQuaker.Fang@Sun.COM args[2] = 0; /* batimeout */ 1355*10266SQuaker.Fang@Sun.COM args[3] = SM(0, IEEE80211_BASEQ_START) 1356*10266SQuaker.Fang@Sun.COM | SM(0, IEEE80211_BASEQ_FRAG); 1357*10266SQuaker.Fang@Sun.COM /* NB: do first so there's no race against reply */ 1358*10266SQuaker.Fang@Sun.COM if (!ic->ic_addba_request(in, tap, dialogtoken, args[1], args[2])) { 1359*10266SQuaker.Fang@Sun.COM /* unable to setup state, don't make request */ 1360*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, 1361*10266SQuaker.Fang@Sun.COM "could not setup BA stream for AC %d\n", 1362*10266SQuaker.Fang@Sun.COM tap->txa_ac); 1363*10266SQuaker.Fang@Sun.COM /* defer next try so we don't slam the driver with requests */ 1364*10266SQuaker.Fang@Sun.COM tap->txa_attempts = IEEE80211_AGGR_MAXTRIES; 1365*10266SQuaker.Fang@Sun.COM tap->txa_lastrequest = ticks; 1366*10266SQuaker.Fang@Sun.COM return (0); 1367*10266SQuaker.Fang@Sun.COM } 1368*10266SQuaker.Fang@Sun.COM tokens = dialogtoken; /* allocate token */ 1369*10266SQuaker.Fang@Sun.COM return (ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA, 1370*10266SQuaker.Fang@Sun.COM IEEE80211_ACTION_BA_ADDBA_REQUEST, args)); 1371*10266SQuaker.Fang@Sun.COM } 1372*10266SQuaker.Fang@Sun.COM 1373*10266SQuaker.Fang@Sun.COM /* 1374*10266SQuaker.Fang@Sun.COM * Terminate an AMPDU tx stream. State is reclaimed 1375*10266SQuaker.Fang@Sun.COM * and the peer notified with a DelBA Action frame. 1376*10266SQuaker.Fang@Sun.COM */ 1377*10266SQuaker.Fang@Sun.COM void 1378*10266SQuaker.Fang@Sun.COM ieee80211_ampdu_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap) 1379*10266SQuaker.Fang@Sun.COM { 1380*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1381*10266SQuaker.Fang@Sun.COM uint16_t args[4]; 1382*10266SQuaker.Fang@Sun.COM 1383*10266SQuaker.Fang@Sun.COM if (IEEE80211_AMPDU_RUNNING(tap)) { 1384*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1385*10266SQuaker.Fang@Sun.COM "stop BA stream for AC %d\n", tap->txa_ac); 1386*10266SQuaker.Fang@Sun.COM 1387*10266SQuaker.Fang@Sun.COM ic->ic_addba_stop(in, tap); 1388*10266SQuaker.Fang@Sun.COM args[0] = WME_AC_TO_TID(tap->txa_ac); 1389*10266SQuaker.Fang@Sun.COM args[1] = IEEE80211_DELBAPS_INIT; 1390*10266SQuaker.Fang@Sun.COM args[2] = 1; /* reason code */ 1391*10266SQuaker.Fang@Sun.COM (void) ieee80211_send_action(in, IEEE80211_ACTION_CAT_BA, 1392*10266SQuaker.Fang@Sun.COM IEEE80211_ACTION_BA_DELBA, args); 1393*10266SQuaker.Fang@Sun.COM } else { 1394*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1395*10266SQuaker.Fang@Sun.COM "BA stream for AC %d not running\n", 1396*10266SQuaker.Fang@Sun.COM tap->txa_ac); 1397*10266SQuaker.Fang@Sun.COM } 1398*10266SQuaker.Fang@Sun.COM } 1399*10266SQuaker.Fang@Sun.COM 1400*10266SQuaker.Fang@Sun.COM /* 1401*10266SQuaker.Fang@Sun.COM * Transmit a BAR frame to the specified node. The 1402*10266SQuaker.Fang@Sun.COM * BAR contents are drawn from the supplied aggregation 1403*10266SQuaker.Fang@Sun.COM * state associated with the node. 1404*10266SQuaker.Fang@Sun.COM */ 1405*10266SQuaker.Fang@Sun.COM int 1406*10266SQuaker.Fang@Sun.COM ieee80211_send_bar(struct ieee80211_node *in, 1407*10266SQuaker.Fang@Sun.COM const struct ieee80211_tx_ampdu *tap) 1408*10266SQuaker.Fang@Sun.COM { 1409*10266SQuaker.Fang@Sun.COM #define ADDSHORT(frm, v) do { \ 1410*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1411*10266SQuaker.Fang@Sun.COM frm[0] = (v) & 0xff; \ 1412*10266SQuaker.Fang@Sun.COM frm[1] = (v) >> 8; \ 1413*10266SQuaker.Fang@Sun.COM frm += 2; \ 1414*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1415*10266SQuaker.Fang@Sun.COM } while (0) 1416*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1417*10266SQuaker.Fang@Sun.COM struct ieee80211_frame_min *wh; 1418*10266SQuaker.Fang@Sun.COM mblk_t *m; 1419*10266SQuaker.Fang@Sun.COM uint8_t *frm; 1420*10266SQuaker.Fang@Sun.COM uint16_t barctl, barseqctl; 1421*10266SQuaker.Fang@Sun.COM int tid; 1422*10266SQuaker.Fang@Sun.COM 1423*10266SQuaker.Fang@Sun.COM 1424*10266SQuaker.Fang@Sun.COM m = ieee80211_getmgtframe(&frm, sizeof (struct ieee80211_ba_request)); 1425*10266SQuaker.Fang@Sun.COM if (m == NULL) 1426*10266SQuaker.Fang@Sun.COM return (ENOMEM); 1427*10266SQuaker.Fang@Sun.COM 1428*10266SQuaker.Fang@Sun.COM wh = (struct ieee80211_frame_min *)m->b_rptr; 1429*10266SQuaker.Fang@Sun.COM wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | 1430*10266SQuaker.Fang@Sun.COM IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; 1431*10266SQuaker.Fang@Sun.COM wh->i_fc[1] = 0; 1432*10266SQuaker.Fang@Sun.COM IEEE80211_ADDR_COPY(wh->i_addr1, in->in_macaddr); 1433*10266SQuaker.Fang@Sun.COM IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr); 1434*10266SQuaker.Fang@Sun.COM 1435*10266SQuaker.Fang@Sun.COM tid = WME_AC_TO_TID(tap->txa_ac); 1436*10266SQuaker.Fang@Sun.COM barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 1437*10266SQuaker.Fang@Sun.COM IEEE80211_BAPS_POLICY_IMMEDIATE : 1438*10266SQuaker.Fang@Sun.COM IEEE80211_BAPS_POLICY_DELAYED) 1439*10266SQuaker.Fang@Sun.COM | SM(tid, IEEE80211_BAPS_TID) 1440*10266SQuaker.Fang@Sun.COM | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ); 1441*10266SQuaker.Fang@Sun.COM barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START) 1442*10266SQuaker.Fang@Sun.COM | SM(0, IEEE80211_BASEQ_FRAG); 1443*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, barctl); 1444*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, barseqctl); 1445*10266SQuaker.Fang@Sun.COM m->b_wptr = frm; 1446*10266SQuaker.Fang@Sun.COM 1447*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_DEBUG, 1448*10266SQuaker.Fang@Sun.COM "send bar frame (tid %u start %u) on channel %u\n", 1449*10266SQuaker.Fang@Sun.COM tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan)); 1450*10266SQuaker.Fang@Sun.COM 1451*10266SQuaker.Fang@Sun.COM (void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_CTL); /* MGT? */ 1452*10266SQuaker.Fang@Sun.COM 1453*10266SQuaker.Fang@Sun.COM return (0); 1454*10266SQuaker.Fang@Sun.COM #undef ADDSHORT 1455*10266SQuaker.Fang@Sun.COM } 1456*10266SQuaker.Fang@Sun.COM 1457*10266SQuaker.Fang@Sun.COM /* 1458*10266SQuaker.Fang@Sun.COM * Send an action management frame. The arguments are stuff 1459*10266SQuaker.Fang@Sun.COM * into a frame without inspection; the caller is assumed to 1460*10266SQuaker.Fang@Sun.COM * prepare them carefully (e.g. based on the aggregation state). 1461*10266SQuaker.Fang@Sun.COM */ 1462*10266SQuaker.Fang@Sun.COM int 1463*10266SQuaker.Fang@Sun.COM ieee80211_send_action(struct ieee80211_node *in, 1464*10266SQuaker.Fang@Sun.COM int category, int action, uint16_t args[4]) 1465*10266SQuaker.Fang@Sun.COM { 1466*10266SQuaker.Fang@Sun.COM #define ADDSHORT(frm, v) do { \ 1467*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1468*10266SQuaker.Fang@Sun.COM frm[0] = (v) & 0xff; \ 1469*10266SQuaker.Fang@Sun.COM frm[1] = (v) >> 8; \ 1470*10266SQuaker.Fang@Sun.COM frm += 2; \ 1471*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1472*10266SQuaker.Fang@Sun.COM } while (0) 1473*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1474*10266SQuaker.Fang@Sun.COM mblk_t *m; 1475*10266SQuaker.Fang@Sun.COM uint8_t *frm; 1476*10266SQuaker.Fang@Sun.COM uint16_t baparamset; 1477*10266SQuaker.Fang@Sun.COM int ret; 1478*10266SQuaker.Fang@Sun.COM 1479*10266SQuaker.Fang@Sun.COM ASSERT(in != NULL); 1480*10266SQuaker.Fang@Sun.COM 1481*10266SQuaker.Fang@Sun.COM m = ieee80211_getmgtframe(&frm, 1482*10266SQuaker.Fang@Sun.COM sizeof (uint16_t) /* action+category */ 1483*10266SQuaker.Fang@Sun.COM /* may action payload */ 1484*10266SQuaker.Fang@Sun.COM + sizeof (struct ieee80211_action_ba_addbaresponse)); 1485*10266SQuaker.Fang@Sun.COM if (m == NULL) 1486*10266SQuaker.Fang@Sun.COM return (ENOMEM); 1487*10266SQuaker.Fang@Sun.COM 1488*10266SQuaker.Fang@Sun.COM *frm++ = (uint8_t)category; 1489*10266SQuaker.Fang@Sun.COM *frm++ = (uint8_t)action; 1490*10266SQuaker.Fang@Sun.COM switch (category) { 1491*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_CAT_BA: 1492*10266SQuaker.Fang@Sun.COM switch (action) { 1493*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_ADDBA_REQUEST: 1494*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1495*10266SQuaker.Fang@Sun.COM "send ADDBA request: dialogtoken %d " 1496*10266SQuaker.Fang@Sun.COM "baparamset 0x%x (tid %d) " 1497*10266SQuaker.Fang@Sun.COM "batimeout 0x%x baseqctl 0x%x\n", 1498*10266SQuaker.Fang@Sun.COM args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), 1499*10266SQuaker.Fang@Sun.COM args[2], args[3]); 1500*10266SQuaker.Fang@Sun.COM 1501*10266SQuaker.Fang@Sun.COM *frm++ = args[0]; /* dialog token */ 1502*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[1]); /* baparamset */ 1503*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[2]); /* batimeout */ 1504*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[3]); /* baseqctl */ 1505*10266SQuaker.Fang@Sun.COM break; 1506*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 1507*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1508*10266SQuaker.Fang@Sun.COM "send ADDBA response: dialogtoken %d status %d " 1509*10266SQuaker.Fang@Sun.COM "baparamset 0x%x (tid %d) batimeout %d\n", 1510*10266SQuaker.Fang@Sun.COM args[0], args[1], args[2], 1511*10266SQuaker.Fang@Sun.COM MS(args[2], IEEE80211_BAPS_TID), args[3]); 1512*10266SQuaker.Fang@Sun.COM 1513*10266SQuaker.Fang@Sun.COM *frm++ = args[0]; /* dialog token */ 1514*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[1]); /* statuscode */ 1515*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[2]); /* baparamset */ 1516*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[3]); /* batimeout */ 1517*10266SQuaker.Fang@Sun.COM break; 1518*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_BA_DELBA: 1519*10266SQuaker.Fang@Sun.COM baparamset = SM(args[0], IEEE80211_DELBAPS_TID) 1520*10266SQuaker.Fang@Sun.COM | SM(args[1], IEEE80211_DELBAPS_INIT); 1521*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, baparamset); 1522*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, args[2]); /* reason code */ 1523*10266SQuaker.Fang@Sun.COM 1524*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1525*10266SQuaker.Fang@Sun.COM "send DELBA action: tid %d, initiator %d " 1526*10266SQuaker.Fang@Sun.COM "reason %d\n", 1527*10266SQuaker.Fang@Sun.COM args[0], args[1], args[2]); 1528*10266SQuaker.Fang@Sun.COM break; 1529*10266SQuaker.Fang@Sun.COM default: 1530*10266SQuaker.Fang@Sun.COM goto badaction; 1531*10266SQuaker.Fang@Sun.COM } 1532*10266SQuaker.Fang@Sun.COM break; 1533*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_CAT_HT: 1534*10266SQuaker.Fang@Sun.COM switch (action) { 1535*10266SQuaker.Fang@Sun.COM case IEEE80211_ACTION_HT_TXCHWIDTH: 1536*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1537*10266SQuaker.Fang@Sun.COM "send HT txchwidth: width %d\n", 1538*10266SQuaker.Fang@Sun.COM IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ? 40 : 20); 1539*10266SQuaker.Fang@Sun.COM *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ? 1540*10266SQuaker.Fang@Sun.COM IEEE80211_A_HT_TXCHWIDTH_2040 : 1541*10266SQuaker.Fang@Sun.COM IEEE80211_A_HT_TXCHWIDTH_20; 1542*10266SQuaker.Fang@Sun.COM break; 1543*10266SQuaker.Fang@Sun.COM default: 1544*10266SQuaker.Fang@Sun.COM goto badaction; 1545*10266SQuaker.Fang@Sun.COM } 1546*10266SQuaker.Fang@Sun.COM break; 1547*10266SQuaker.Fang@Sun.COM default: 1548*10266SQuaker.Fang@Sun.COM badaction: 1549*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT, 1550*10266SQuaker.Fang@Sun.COM "unsupported category %d action %d\n", 1551*10266SQuaker.Fang@Sun.COM category, action); 1552*10266SQuaker.Fang@Sun.COM return (EINVAL); 1553*10266SQuaker.Fang@Sun.COM /* NOTREACHED */ 1554*10266SQuaker.Fang@Sun.COM } 1555*10266SQuaker.Fang@Sun.COM m->b_wptr = frm; 1556*10266SQuaker.Fang@Sun.COM 1557*10266SQuaker.Fang@Sun.COM ret = ieee80211_mgmt_output(ic, in, m, IEEE80211_FC0_SUBTYPE_ACTION, 0); 1558*10266SQuaker.Fang@Sun.COM 1559*10266SQuaker.Fang@Sun.COM return (ret); 1560*10266SQuaker.Fang@Sun.COM #undef ADDSHORT 1561*10266SQuaker.Fang@Sun.COM } 1562*10266SQuaker.Fang@Sun.COM 1563*10266SQuaker.Fang@Sun.COM /* 1564*10266SQuaker.Fang@Sun.COM * Construct the MCS bit mask for inclusion 1565*10266SQuaker.Fang@Sun.COM * in an HT information element. 1566*10266SQuaker.Fang@Sun.COM */ 1567*10266SQuaker.Fang@Sun.COM static void 1568*10266SQuaker.Fang@Sun.COM ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1569*10266SQuaker.Fang@Sun.COM { 1570*10266SQuaker.Fang@Sun.COM int i; 1571*10266SQuaker.Fang@Sun.COM 1572*10266SQuaker.Fang@Sun.COM for (i = 0; i < rs->rs_nrates; i++) { 1573*10266SQuaker.Fang@Sun.COM int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1574*10266SQuaker.Fang@Sun.COM if (r < IEEE80211_HTRATE_MAXSIZE) { 1575*10266SQuaker.Fang@Sun.COM /* NB: this assumes a particular implementation */ 1576*10266SQuaker.Fang@Sun.COM ieee80211_setbit(frm, r); 1577*10266SQuaker.Fang@Sun.COM } 1578*10266SQuaker.Fang@Sun.COM } 1579*10266SQuaker.Fang@Sun.COM } 1580*10266SQuaker.Fang@Sun.COM 1581*10266SQuaker.Fang@Sun.COM /* 1582*10266SQuaker.Fang@Sun.COM * Add body of an HTCAP information element. 1583*10266SQuaker.Fang@Sun.COM */ 1584*10266SQuaker.Fang@Sun.COM static uint8_t * 1585*10266SQuaker.Fang@Sun.COM ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *in) 1586*10266SQuaker.Fang@Sun.COM { 1587*10266SQuaker.Fang@Sun.COM #define ADDSHORT(frm, v) do { \ 1588*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1589*10266SQuaker.Fang@Sun.COM frm[0] = (v) & 0xff; \ 1590*10266SQuaker.Fang@Sun.COM frm[1] = (v) >> 8; \ 1591*10266SQuaker.Fang@Sun.COM frm += 2; \ 1592*10266SQuaker.Fang@Sun.COM _NOTE(CONSTCOND) \ 1593*10266SQuaker.Fang@Sun.COM } while (0) 1594*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1595*10266SQuaker.Fang@Sun.COM uint16_t caps; 1596*10266SQuaker.Fang@Sun.COM int rxmax, density; 1597*10266SQuaker.Fang@Sun.COM 1598*10266SQuaker.Fang@Sun.COM /* HT capabilities */ 1599*10266SQuaker.Fang@Sun.COM caps = ic->ic_htcaps & 0xffff; 1600*10266SQuaker.Fang@Sun.COM /* 1601*10266SQuaker.Fang@Sun.COM * Note channel width depends on whether we are operating as 1602*10266SQuaker.Fang@Sun.COM * a sta or not. When operating as a sta we are generating 1603*10266SQuaker.Fang@Sun.COM * a request based on our desired configuration. Otherwise 1604*10266SQuaker.Fang@Sun.COM * we are operational and the channel attributes identify 1605*10266SQuaker.Fang@Sun.COM * how we've been setup (which might be different if a fixed 1606*10266SQuaker.Fang@Sun.COM * channel is specified). 1607*10266SQuaker.Fang@Sun.COM */ 1608*10266SQuaker.Fang@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) { 1609*10266SQuaker.Fang@Sun.COM /* override 20/40 use based on config */ 1610*10266SQuaker.Fang@Sun.COM if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) 1611*10266SQuaker.Fang@Sun.COM caps |= IEEE80211_HTCAP_CHWIDTH40; 1612*10266SQuaker.Fang@Sun.COM else 1613*10266SQuaker.Fang@Sun.COM caps &= ~IEEE80211_HTCAP_CHWIDTH40; 1614*10266SQuaker.Fang@Sun.COM /* use advertised setting (locally constraint) */ 1615*10266SQuaker.Fang@Sun.COM rxmax = MS(in->in_htparam, IEEE80211_HTCAP_MAXRXAMPDU); 1616*10266SQuaker.Fang@Sun.COM density = MS(in->in_htparam, IEEE80211_HTCAP_MPDUDENSITY); 1617*10266SQuaker.Fang@Sun.COM } else { 1618*10266SQuaker.Fang@Sun.COM /* override 20/40 use based on current channel */ 1619*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) 1620*10266SQuaker.Fang@Sun.COM caps |= IEEE80211_HTCAP_CHWIDTH40; 1621*10266SQuaker.Fang@Sun.COM else 1622*10266SQuaker.Fang@Sun.COM caps &= ~IEEE80211_HTCAP_CHWIDTH40; 1623*10266SQuaker.Fang@Sun.COM rxmax = ic->ic_ampdu_rxmax; 1624*10266SQuaker.Fang@Sun.COM density = ic->ic_ampdu_density; 1625*10266SQuaker.Fang@Sun.COM } 1626*10266SQuaker.Fang@Sun.COM /* adjust short GI based on channel and config */ 1627*10266SQuaker.Fang@Sun.COM if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) 1628*10266SQuaker.Fang@Sun.COM caps &= ~IEEE80211_HTCAP_SHORTGI20; 1629*10266SQuaker.Fang@Sun.COM if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || 1630*10266SQuaker.Fang@Sun.COM (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) 1631*10266SQuaker.Fang@Sun.COM caps &= ~IEEE80211_HTCAP_SHORTGI40; 1632*10266SQuaker.Fang@Sun.COM ADDSHORT(frm, caps); 1633*10266SQuaker.Fang@Sun.COM 1634*10266SQuaker.Fang@Sun.COM /* HT parameters */ 1635*10266SQuaker.Fang@Sun.COM *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) 1636*10266SQuaker.Fang@Sun.COM | SM(density, IEEE80211_HTCAP_MPDUDENSITY); 1637*10266SQuaker.Fang@Sun.COM frm++; 1638*10266SQuaker.Fang@Sun.COM 1639*10266SQuaker.Fang@Sun.COM /* pre-zero remainder of ie */ 1640*10266SQuaker.Fang@Sun.COM (void) memset(frm, 0, sizeof (struct ieee80211_ie_htcap) - 1641*10266SQuaker.Fang@Sun.COM offsetof(struct ieee80211_ie_htcap, hc_mcsset)); 1642*10266SQuaker.Fang@Sun.COM 1643*10266SQuaker.Fang@Sun.COM /* supported MCS set */ 1644*10266SQuaker.Fang@Sun.COM /* 1645*10266SQuaker.Fang@Sun.COM * it would better to get the rate set from in_htrates 1646*10266SQuaker.Fang@Sun.COM * so we can restrict it but for sta mode in_htrates isn't 1647*10266SQuaker.Fang@Sun.COM * setup when we're called to form an AssocReq frame so for 1648*10266SQuaker.Fang@Sun.COM * now we're restricted to the default HT rate set. 1649*10266SQuaker.Fang@Sun.COM */ 1650*10266SQuaker.Fang@Sun.COM ieee80211_set_htrates(frm, &ieee80211_rateset_11n); 1651*10266SQuaker.Fang@Sun.COM 1652*10266SQuaker.Fang@Sun.COM frm += sizeof (struct ieee80211_ie_htcap) - 1653*10266SQuaker.Fang@Sun.COM offsetof(struct ieee80211_ie_htcap, hc_mcsset); 1654*10266SQuaker.Fang@Sun.COM 1655*10266SQuaker.Fang@Sun.COM return (frm); 1656*10266SQuaker.Fang@Sun.COM #undef ADDSHORT 1657*10266SQuaker.Fang@Sun.COM } 1658*10266SQuaker.Fang@Sun.COM 1659*10266SQuaker.Fang@Sun.COM /* 1660*10266SQuaker.Fang@Sun.COM * Add 802.11n HT capabilities information element 1661*10266SQuaker.Fang@Sun.COM */ 1662*10266SQuaker.Fang@Sun.COM uint8_t * 1663*10266SQuaker.Fang@Sun.COM ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *in) 1664*10266SQuaker.Fang@Sun.COM { 1665*10266SQuaker.Fang@Sun.COM frm[0] = IEEE80211_ELEMID_HTCAP; 1666*10266SQuaker.Fang@Sun.COM frm[1] = sizeof (struct ieee80211_ie_htcap) - 2; 1667*10266SQuaker.Fang@Sun.COM return (ieee80211_add_htcap_body(frm + 2, in)); 1668*10266SQuaker.Fang@Sun.COM } 1669*10266SQuaker.Fang@Sun.COM 1670*10266SQuaker.Fang@Sun.COM /* 1671*10266SQuaker.Fang@Sun.COM * Add Broadcom OUI wrapped standard HTCAP ie; this is 1672*10266SQuaker.Fang@Sun.COM * used for compatibility w/ pre-draft implementations. 1673*10266SQuaker.Fang@Sun.COM */ 1674*10266SQuaker.Fang@Sun.COM uint8_t * 1675*10266SQuaker.Fang@Sun.COM ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *in) 1676*10266SQuaker.Fang@Sun.COM { 1677*10266SQuaker.Fang@Sun.COM frm[0] = IEEE80211_ELEMID_VENDOR; 1678*10266SQuaker.Fang@Sun.COM frm[1] = 4 + sizeof (struct ieee80211_ie_htcap) - 2; 1679*10266SQuaker.Fang@Sun.COM frm[2] = (BCM_OUI >> 0) & 0xff; 1680*10266SQuaker.Fang@Sun.COM frm[3] = (BCM_OUI >> 8) & 0xff; 1681*10266SQuaker.Fang@Sun.COM frm[4] = (BCM_OUI >> 16) & 0xff; 1682*10266SQuaker.Fang@Sun.COM frm[5] = BCM_OUI_HTCAP; 1683*10266SQuaker.Fang@Sun.COM return (ieee80211_add_htcap_body(frm + 6, in)); 1684*10266SQuaker.Fang@Sun.COM } 1685*10266SQuaker.Fang@Sun.COM 1686*10266SQuaker.Fang@Sun.COM /* 1687*10266SQuaker.Fang@Sun.COM * Construct the MCS bit mask of basic rates 1688*10266SQuaker.Fang@Sun.COM * for inclusion in an HT information element. 1689*10266SQuaker.Fang@Sun.COM */ 1690*10266SQuaker.Fang@Sun.COM static void 1691*10266SQuaker.Fang@Sun.COM ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) 1692*10266SQuaker.Fang@Sun.COM { 1693*10266SQuaker.Fang@Sun.COM int i; 1694*10266SQuaker.Fang@Sun.COM 1695*10266SQuaker.Fang@Sun.COM for (i = 0; i < rs->rs_nrates; i++) { 1696*10266SQuaker.Fang@Sun.COM int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; 1697*10266SQuaker.Fang@Sun.COM if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && 1698*10266SQuaker.Fang@Sun.COM r < IEEE80211_HTRATE_MAXSIZE) { 1699*10266SQuaker.Fang@Sun.COM /* NB: this assumes a particular implementation */ 1700*10266SQuaker.Fang@Sun.COM ieee80211_setbit(frm, r); 1701*10266SQuaker.Fang@Sun.COM } 1702*10266SQuaker.Fang@Sun.COM } 1703*10266SQuaker.Fang@Sun.COM } 1704*10266SQuaker.Fang@Sun.COM 1705*10266SQuaker.Fang@Sun.COM /* 1706*10266SQuaker.Fang@Sun.COM * Update the HTINFO ie for a beacon frame. 1707*10266SQuaker.Fang@Sun.COM */ 1708*10266SQuaker.Fang@Sun.COM void 1709*10266SQuaker.Fang@Sun.COM ieee80211_ht_update_beacon(struct ieee80211com *ic, 1710*10266SQuaker.Fang@Sun.COM struct ieee80211_beacon_offsets *bo) 1711*10266SQuaker.Fang@Sun.COM { 1712*10266SQuaker.Fang@Sun.COM #define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) 1713*10266SQuaker.Fang@Sun.COM struct ieee80211_ie_htinfo *ht = 1714*10266SQuaker.Fang@Sun.COM (struct ieee80211_ie_htinfo *)bo->bo_htinfo; 1715*10266SQuaker.Fang@Sun.COM 1716*10266SQuaker.Fang@Sun.COM /* only update on channel change */ 1717*10266SQuaker.Fang@Sun.COM ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_curchan); 1718*10266SQuaker.Fang@Sun.COM ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; 1719*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) 1720*10266SQuaker.Fang@Sun.COM ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 1721*10266SQuaker.Fang@Sun.COM else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan)) 1722*10266SQuaker.Fang@Sun.COM ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; 1723*10266SQuaker.Fang@Sun.COM else /* LINTED */ 1724*10266SQuaker.Fang@Sun.COM ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; 1725*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) 1726*10266SQuaker.Fang@Sun.COM ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; 1727*10266SQuaker.Fang@Sun.COM 1728*10266SQuaker.Fang@Sun.COM /* protection mode */ 1729*10266SQuaker.Fang@Sun.COM ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; 1730*10266SQuaker.Fang@Sun.COM 1731*10266SQuaker.Fang@Sun.COM /* propagate to vendor ie's */ 1732*10266SQuaker.Fang@Sun.COM #undef PROTMODE 1733*10266SQuaker.Fang@Sun.COM } 1734*10266SQuaker.Fang@Sun.COM 1735*10266SQuaker.Fang@Sun.COM /* 1736*10266SQuaker.Fang@Sun.COM * Add body of an HTINFO information element. 1737*10266SQuaker.Fang@Sun.COM * 1738*10266SQuaker.Fang@Sun.COM * NB: We don't use struct ieee80211_ie_htinfo because we can 1739*10266SQuaker.Fang@Sun.COM * be called to fillin both a standard ie and a compat ie that 1740*10266SQuaker.Fang@Sun.COM * has a vendor OUI at the front. 1741*10266SQuaker.Fang@Sun.COM */ 1742*10266SQuaker.Fang@Sun.COM static uint8_t * 1743*10266SQuaker.Fang@Sun.COM ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *in) 1744*10266SQuaker.Fang@Sun.COM { 1745*10266SQuaker.Fang@Sun.COM struct ieee80211com *ic = in->in_ic; 1746*10266SQuaker.Fang@Sun.COM 1747*10266SQuaker.Fang@Sun.COM /* pre-zero remainder of ie */ 1748*10266SQuaker.Fang@Sun.COM (void) memset(frm, 0, sizeof (struct ieee80211_ie_htinfo) - 2); 1749*10266SQuaker.Fang@Sun.COM 1750*10266SQuaker.Fang@Sun.COM /* primary/control channel center */ 1751*10266SQuaker.Fang@Sun.COM *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); 1752*10266SQuaker.Fang@Sun.COM 1753*10266SQuaker.Fang@Sun.COM frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; 1754*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) 1755*10266SQuaker.Fang@Sun.COM frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; 1756*10266SQuaker.Fang@Sun.COM else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan)) 1757*10266SQuaker.Fang@Sun.COM frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; 1758*10266SQuaker.Fang@Sun.COM else /* LINTED */ 1759*10266SQuaker.Fang@Sun.COM frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; 1760*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) 1761*10266SQuaker.Fang@Sun.COM frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; 1762*10266SQuaker.Fang@Sun.COM 1763*10266SQuaker.Fang@Sun.COM frm[1] = ic->ic_curhtprotmode; 1764*10266SQuaker.Fang@Sun.COM 1765*10266SQuaker.Fang@Sun.COM frm += 5; 1766*10266SQuaker.Fang@Sun.COM 1767*10266SQuaker.Fang@Sun.COM /* basic MCS set */ 1768*10266SQuaker.Fang@Sun.COM ieee80211_set_basic_htrates(frm, &in->in_htrates); 1769*10266SQuaker.Fang@Sun.COM frm += sizeof (struct ieee80211_ie_htinfo) - 1770*10266SQuaker.Fang@Sun.COM offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); 1771*10266SQuaker.Fang@Sun.COM return (frm); 1772*10266SQuaker.Fang@Sun.COM } 1773*10266SQuaker.Fang@Sun.COM 1774*10266SQuaker.Fang@Sun.COM /* 1775*10266SQuaker.Fang@Sun.COM * Add 802.11n HT information information element. 1776*10266SQuaker.Fang@Sun.COM */ 1777*10266SQuaker.Fang@Sun.COM uint8_t * 1778*10266SQuaker.Fang@Sun.COM ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *in) 1779*10266SQuaker.Fang@Sun.COM { 1780*10266SQuaker.Fang@Sun.COM frm[0] = IEEE80211_ELEMID_HTINFO; 1781*10266SQuaker.Fang@Sun.COM frm[1] = sizeof (struct ieee80211_ie_htinfo) - 2; 1782*10266SQuaker.Fang@Sun.COM 1783*10266SQuaker.Fang@Sun.COM return (ieee80211_add_htinfo_body(frm + 2, in)); 1784*10266SQuaker.Fang@Sun.COM } 1785*10266SQuaker.Fang@Sun.COM 1786*10266SQuaker.Fang@Sun.COM /* 1787*10266SQuaker.Fang@Sun.COM * Add Broadcom OUI wrapped standard HTINFO ie; this is 1788*10266SQuaker.Fang@Sun.COM * used for compatibility w/ pre-draft implementations. 1789*10266SQuaker.Fang@Sun.COM */ 1790*10266SQuaker.Fang@Sun.COM uint8_t * 1791*10266SQuaker.Fang@Sun.COM ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *in) 1792*10266SQuaker.Fang@Sun.COM { 1793*10266SQuaker.Fang@Sun.COM frm[0] = IEEE80211_ELEMID_VENDOR; 1794*10266SQuaker.Fang@Sun.COM frm[1] = 4 + sizeof (struct ieee80211_ie_htinfo) - 2; 1795*10266SQuaker.Fang@Sun.COM frm[2] = (BCM_OUI >> 0) & 0xff; 1796*10266SQuaker.Fang@Sun.COM frm[3] = (BCM_OUI >> 8) & 0xff; 1797*10266SQuaker.Fang@Sun.COM frm[4] = (BCM_OUI >> 16) & 0xff; 1798*10266SQuaker.Fang@Sun.COM frm[5] = BCM_OUI_HTINFO; 1799*10266SQuaker.Fang@Sun.COM 1800*10266SQuaker.Fang@Sun.COM return (ieee80211_add_htinfo_body(frm + 6, in)); 1801*10266SQuaker.Fang@Sun.COM } 1802*10266SQuaker.Fang@Sun.COM 1803*10266SQuaker.Fang@Sun.COM void 1804*10266SQuaker.Fang@Sun.COM ieee80211_ht_attach(struct ieee80211com *ic) 1805*10266SQuaker.Fang@Sun.COM { 1806*10266SQuaker.Fang@Sun.COM /* setup default aggregation policy */ 1807*10266SQuaker.Fang@Sun.COM ic->ic_recv_action = ieee80211_aggr_recv_action; 1808*10266SQuaker.Fang@Sun.COM ic->ic_send_action = ieee80211_send_action; 1809*10266SQuaker.Fang@Sun.COM ic->ic_addba_request = ieee80211_addba_request; 1810*10266SQuaker.Fang@Sun.COM ic->ic_addba_response = ieee80211_addba_response; 1811*10266SQuaker.Fang@Sun.COM ic->ic_addba_stop = ieee80211_addba_stop; 1812*10266SQuaker.Fang@Sun.COM 1813*10266SQuaker.Fang@Sun.COM ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; 1814*10266SQuaker.Fang@Sun.COM ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; 1815*10266SQuaker.Fang@Sun.COM 1816*10266SQuaker.Fang@Sun.COM /* get from driver */ 1817*10266SQuaker.Fang@Sun.COM ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; 1818*10266SQuaker.Fang@Sun.COM ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; 1819*10266SQuaker.Fang@Sun.COM ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; 1820*10266SQuaker.Fang@Sun.COM ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; 1821*10266SQuaker.Fang@Sun.COM 1822*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTC_HT) { 1823*10266SQuaker.Fang@Sun.COM /* 1824*10266SQuaker.Fang@Sun.COM * Device is HT capable; enable all HT-related 1825*10266SQuaker.Fang@Sun.COM * facilities by default. 1826*10266SQuaker.Fang@Sun.COM * these choices may be too aggressive. 1827*10266SQuaker.Fang@Sun.COM */ 1828*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_HT | IEEE80211_FEXT_HTCOMPAT; 1829*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) 1830*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; 1831*10266SQuaker.Fang@Sun.COM /* infer from channel list? */ 1832*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { 1833*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; 1834*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) 1835*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; 1836*10266SQuaker.Fang@Sun.COM } 1837*10266SQuaker.Fang@Sun.COM /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ 1838*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; 1839*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTC_AMPDU) 1840*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; 1841*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; 1842*10266SQuaker.Fang@Sun.COM if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) 1843*10266SQuaker.Fang@Sun.COM ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; 1844*10266SQuaker.Fang@Sun.COM } 1845*10266SQuaker.Fang@Sun.COM 1846*10266SQuaker.Fang@Sun.COM #define ieee80211_isset16(a, i) ((a) & (1 << (i))) 1847*10266SQuaker.Fang@Sun.COM /* fill default rate sets for 11NA/11NG if driver has no specified */ 1848*10266SQuaker.Fang@Sun.COM if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NA) && 1849*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11NA].ir_nrates == 0) { 1850*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11NA] = 1851*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11A]; 1852*10266SQuaker.Fang@Sun.COM } 1853*10266SQuaker.Fang@Sun.COM 1854*10266SQuaker.Fang@Sun.COM if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NG) && 1855*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11NG].ir_nrates == 0) { 1856*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11NG] = 1857*10266SQuaker.Fang@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G]; 1858*10266SQuaker.Fang@Sun.COM } 1859*10266SQuaker.Fang@Sun.COM #undef ieee80211_isset16 1860*10266SQuaker.Fang@Sun.COM } 1861*10266SQuaker.Fang@Sun.COM 1862*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1863*10266SQuaker.Fang@Sun.COM void 1864*10266SQuaker.Fang@Sun.COM ieee80211_ht_detach(struct ieee80211com *ic) 1865*10266SQuaker.Fang@Sun.COM { 1866*10266SQuaker.Fang@Sun.COM } 1867*10266SQuaker.Fang@Sun.COM 1868*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1869*10266SQuaker.Fang@Sun.COM static void 1870*10266SQuaker.Fang@Sun.COM ht_announce(struct ieee80211com *ic, int mode, 1871*10266SQuaker.Fang@Sun.COM const struct ieee80211_htrateset *rs) 1872*10266SQuaker.Fang@Sun.COM { 1873*10266SQuaker.Fang@Sun.COM int i, rate; 1874*10266SQuaker.Fang@Sun.COM 1875*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, "%s MCS: \n", 1876*10266SQuaker.Fang@Sun.COM ieee80211_phymode_name[mode]); 1877*10266SQuaker.Fang@Sun.COM for (i = 0; i < rs->rs_nrates; i++) { 1878*10266SQuaker.Fang@Sun.COM rate = ieee80211_htrates[rs->rs_rates[i]]; 1879*10266SQuaker.Fang@Sun.COM ieee80211_dbg(IEEE80211_MSG_HT, "%s%d%sMbps\n", 1880*10266SQuaker.Fang@Sun.COM (i != 0 ? " " : ""), 1881*10266SQuaker.Fang@Sun.COM rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); 1882*10266SQuaker.Fang@Sun.COM } 1883*10266SQuaker.Fang@Sun.COM } 1884*10266SQuaker.Fang@Sun.COM 1885*10266SQuaker.Fang@Sun.COM void 1886*10266SQuaker.Fang@Sun.COM ieee80211_ht_announce(struct ieee80211com *ic) 1887*10266SQuaker.Fang@Sun.COM { 1888*10266SQuaker.Fang@Sun.COM if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NA)) 1889*10266SQuaker.Fang@Sun.COM ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); 1890*10266SQuaker.Fang@Sun.COM if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NG)) 1891*10266SQuaker.Fang@Sun.COM ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); 1892*10266SQuaker.Fang@Sun.COM } 1893*10266SQuaker.Fang@Sun.COM 1894*10266SQuaker.Fang@Sun.COM /* ARGSUSED */ 1895*10266SQuaker.Fang@Sun.COM const struct ieee80211_htrateset * 1896*10266SQuaker.Fang@Sun.COM ieee80211_get_suphtrates(struct ieee80211com *ic, 1897*10266SQuaker.Fang@Sun.COM const struct ieee80211_channel *c) 1898*10266SQuaker.Fang@Sun.COM { 1899*10266SQuaker.Fang@Sun.COM return (&ieee80211_rateset_11n); 1900*10266SQuaker.Fang@Sun.COM } 1901