xref: /titanic_44/usr/src/uts/common/io/net80211/net80211_ht.c (revision db8b037b5616a366b7dfdc01ef9552f02f9adfdd)
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