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