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