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