ieee80211_ht.c revision 219606
1149871Sscottl/*-
2149871Sscottl * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3136849Sscottl * All rights reserved.
4136849Sscottl *
5136849Sscottl * Redistribution and use in source and binary forms, with or without
6136849Sscottl * modification, are permitted provided that the following conditions
7136849Sscottl * are met:
8136849Sscottl * 1. Redistributions of source code must retain the above copyright
9136849Sscottl *    notice, this list of conditions and the following disclaimer.
10136849Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11136849Sscottl *    notice, this list of conditions and the following disclaimer in the
12136849Sscottl *    documentation and/or other materials provided with the distribution.
13136849Sscottl *
14136849Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15136849Sscottl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16136849Sscottl * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17136849Sscottl * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18136849Sscottl * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19136849Sscottl * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20136849Sscottl * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21136849Sscottl * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22136849Sscottl * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23136849Sscottl * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24136849Sscottl */
25136849Sscottl
26227912Smarius#include <sys/cdefs.h>
27227912Smarius#ifdef __FreeBSD__
28227912Smarius__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 219606 2011-03-13 13:05:50Z bschmidt $");
29149871Sscottl#endif
30136849Sscottl
31136849Sscottl/*
32136849Sscottl * IEEE 802.11n protocol support.
33136849Sscottl */
34136849Sscottl
35136849Sscottl#include "opt_inet.h"
36136849Sscottl#include "opt_wlan.h"
37136849Sscottl
38136849Sscottl#include <sys/param.h>
39136849Sscottl#include <sys/kernel.h>
40136849Sscottl#include <sys/systm.h>
41136849Sscottl#include <sys/endian.h>
42136849Sscottl
43149871Sscottl#include <sys/socket.h>
44149871Sscottl
45269617Sjhb#include <net/if.h>
46149871Sscottl#include <net/if_media.h>
47136849Sscottl#include <net/ethernet.h>
48136849Sscottl
49136849Sscottl#include <net80211/ieee80211_var.h>
50149871Sscottl#include <net80211/ieee80211_action.h>
51149871Sscottl#include <net80211/ieee80211_input.h>
52149871Sscottl
53149871Sscottl/* define here, used throughout file */
54136849Sscottl#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
55136849Sscottl#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
56136849Sscottl
57143039Sscottlconst struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = {
58136849Sscottl	{  13,  14,   27,   30 },	/* MCS 0 */
59149871Sscottl	{  26,  29,   54,   60 },	/* MCS 1 */
60136849Sscottl	{  39,  43,   81,   90 },	/* MCS 2 */
61136849Sscottl	{  52,  58,  108,  120 },	/* MCS 3 */
62136849Sscottl	{  78,  87,  162,  180 },	/* MCS 4 */
63149871Sscottl	{ 104, 116,  216,  240 },	/* MCS 5 */
64136849Sscottl	{ 117, 130,  243,  270 },	/* MCS 6 */
65136849Sscottl	{ 130, 144,  270,  300 },	/* MCS 7 */
66136849Sscottl	{  26,  29,   54,   60 },	/* MCS 8 */
67136849Sscottl	{  52,  58,  108,  120 },	/* MCS 9 */
68136849Sscottl	{  78,  87,  162,  180 },	/* MCS 10 */
69149871Sscottl	{ 104, 116,  216,  240 },	/* MCS 11 */
70136849Sscottl	{ 156, 173,  324,  360 },	/* MCS 12 */
71136849Sscottl	{ 208, 231,  432,  480 },	/* MCS 13 */
72136849Sscottl	{ 234, 260,  486,  540 },	/* MCS 14 */
73149871Sscottl	{ 260, 289,  540,  600 },	/* MCS 15 */
74149871Sscottl	{  39,  43,   81,   90 },	/* MCS 16 */
75149871Sscottl	{  78,  87,  162,  180 },	/* MCS 17 */
76149871Sscottl	{ 117, 130,  243,  270 },	/* MCS 18 */
77149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 19 */
78149871Sscottl	{ 234, 260,  486,  540 },	/* MCS 20 */
79149871Sscottl	{ 312, 347,  648,  720 },	/* MCS 21 */
80149871Sscottl	{ 351, 390,  729,  810 },	/* MCS 22 */
81149871Sscottl	{ 390, 433,  810,  900 },	/* MCS 23 */
82149871Sscottl	{  52,  58,  108,  120 },	/* MCS 24 */
83149871Sscottl	{ 104, 116,  216,  240 },	/* MCS 25 */
84149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 26 */
85149871Sscottl	{ 208, 231,  432,  480 },	/* MCS 27 */
86149871Sscottl	{ 312, 347,  648,  720 },	/* MCS 28 */
87149871Sscottl	{ 416, 462,  864,  960 },	/* MCS 29 */
88149871Sscottl	{ 468, 520,  972, 1080 },	/* MCS 30 */
89190863Sdelphij	{ 520, 578, 1080, 1200 },	/* MCS 31 */
90227912Smarius	{   0,   0,   12,   13 },	/* MCS 32 */
91149871Sscottl	{  78,  87,  162,  180 },	/* MCS 33 */
92149871Sscottl	{ 104, 116,  216,  240 },	/* MCS 34 */
93149871Sscottl	{ 130, 144,  270,  300 },	/* MCS 35 */
94149871Sscottl	{ 117, 130,  243,  270 },	/* MCS 36 */
95149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 37 */
96149871Sscottl	{ 195, 217,  405,  450 },	/* MCS 38 */
97149871Sscottl	{ 104, 116,  216,  240 },	/* MCS 39 */
98149871Sscottl	{ 130, 144,  270,  300 },	/* MCS 40 */
99149871Sscottl	{ 130, 144,  270,  300 },	/* MCS 41 */
100149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 42 */
101149871Sscottl	{ 182, 202,  378,  420 },	/* MCS 43 */
102149871Sscottl	{ 182, 202,  378,  420 },	/* MCS 44 */
103236379Seadler	{ 208, 231,  432,  480 },	/* MCS 45 */
104149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 46 */
105149871Sscottl	{ 195, 217,  405,  450 },	/* MCS 47 */
106149871Sscottl	{ 195, 217,  405,  450 },	/* MCS 48 */
107149871Sscottl	{ 234, 260,  486,  540 },	/* MCS 49 */
108149871Sscottl	{ 273, 303,  567,  630 },	/* MCS 50 */
109149871Sscottl	{ 273, 303,  567,  630 },	/* MCS 51 */
110149871Sscottl	{ 312, 347,  648,  720 },	/* MCS 52 */
111149871Sscottl	{ 130, 144,  270,  300 },	/* MCS 53 */
112149871Sscottl	{ 156, 173,  324,  360 },	/* MCS 54 */
113149871Sscottl	{ 182, 202,  378,  420 },	/* MCS 55 */
114269617Sjhb	{ 156, 173,  324,  360 },	/* MCS 56 */
115136849Sscottl	{ 182, 202,  378,  420 },	/* MCS 57 */
116136849Sscottl	{ 208, 231,  432,  480 },	/* MCS 58 */
117136849Sscottl	{ 234, 260,  486,  540 },	/* MCS 59 */
118136849Sscottl	{ 208, 231,  432,  480 },	/* MCS 60 */
119136849Sscottl	{ 234, 260,  486,  540 },	/* MCS 61 */
120136849Sscottl	{ 260, 289,  540,  600 },	/* MCS 62 */
121136849Sscottl	{ 260, 289,  540,  600 },	/* MCS 63 */
122136849Sscottl	{ 286, 318,  594,  660 },	/* MCS 64 */
123136849Sscottl	{ 195, 217,  405,  450 },	/* MCS 65 */
124136849Sscottl	{ 234, 260,  486,  540 },	/* MCS 66 */
125136849Sscottl	{ 273, 303,  567,  630 },	/* MCS 67 */
126136849Sscottl	{ 234, 260,  486,  540 },	/* MCS 68 */
127136849Sscottl	{ 273, 303,  567,  630 },	/* MCS 69 */
128136849Sscottl	{ 312, 347,  648,  720 },	/* MCS 70 */
129136849Sscottl	{ 351, 390,  729,  810 },	/* MCS 71 */
130136849Sscottl	{ 312, 347,  648,  720 },	/* MCS 72 */
131136849Sscottl	{ 351, 390,  729,  810 },	/* MCS 73 */
132136849Sscottl	{ 390, 433,  810,  900 },	/* MCS 74 */
133136849Sscottl	{ 390, 433,  810,  900 },	/* MCS 75 */
134136849Sscottl	{ 429, 477,  891,  990 },	/* MCS 76 */
135136849Sscottl};
136136849Sscottl
137136849Sscottl#ifdef IEEE80211_AMPDU_AGE
138136849Sscottlstatic	int ieee80211_ampdu_age = -1;	/* threshold for ampdu reorder q (ms) */
139269617SjhbSYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW,
140269617Sjhb	&ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
141136849Sscottl	"AMPDU max reorder age (ms)");
142136849Sscottl#endif
143149871Sscottl
144136849Sscottlstatic	int ieee80211_recv_bar_ena = 1;
145136849SscottlSYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
146136849Sscottl	    0, "BAR frame processing (ena/dis)");
147136849Sscottl
148136849Sscottlstatic	int ieee80211_addba_timeout = -1;/* timeout for ADDBA response */
149136849SscottlSYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLTYPE_INT | CTLFLAG_RW,
150136849Sscottl	&ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
151136849Sscottl	"ADDBA request timeout (ms)");
152136849Sscottlstatic	int ieee80211_addba_backoff = -1;/* backoff after max ADDBA requests */
153136849SscottlSYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLTYPE_INT | CTLFLAG_RW,
154136849Sscottl	&ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
155136849Sscottl	"ADDBA request backoff (ms)");
156136849Sscottlstatic	int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */
157269617SjhbSYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW,
158269617Sjhb	&ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
159136849Sscottl
160190809Sdelphijstatic	int ieee80211_bar_timeout = -1;	/* timeout waiting for BAR response */
161136849Sscottlstatic	int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */
162136849Sscottl
163136849Sscottlstatic	ieee80211_recv_action_func ht_recv_action_ba_addba_request;
164136849Sscottlstatic	ieee80211_recv_action_func ht_recv_action_ba_addba_response;
165136849Sscottlstatic	ieee80211_recv_action_func ht_recv_action_ba_delba;
166136849Sscottlstatic	ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave;
167298955Spfgstatic	ieee80211_recv_action_func ht_recv_action_ht_txchwidth;
168136849Sscottl
169136849Sscottlstatic	ieee80211_send_action_func ht_send_action_ba_addba;
170136849Sscottlstatic	ieee80211_send_action_func ht_send_action_ba_delba;
171136849Sscottlstatic	ieee80211_send_action_func ht_send_action_ht_txchwidth;
172136849Sscottl
173136849Sscottlstatic void
174136849Sscottlieee80211_ht_init(void)
175136849Sscottl{
176136849Sscottl	/*
177149871Sscottl	 * Setup HT parameters that depends on the clock frequency.
178136849Sscottl	 */
179149871Sscottl#ifdef IEEE80211_AMPDU_AGE
180149871Sscottl	ieee80211_ampdu_age = msecs_to_ticks(500);
181149871Sscottl#endif
182149871Sscottl	ieee80211_addba_timeout = msecs_to_ticks(250);
183149871Sscottl	ieee80211_addba_backoff = msecs_to_ticks(10*1000);
184149871Sscottl	ieee80211_bar_timeout = msecs_to_ticks(250);
185149871Sscottl	/*
186149871Sscottl	 * Register action frame handlers.
187149871Sscottl	 */
188149871Sscottl	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
189149871Sscottl	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request);
190149871Sscottl	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
191149871Sscottl	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response);
192149871Sscottl	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
193149871Sscottl	    IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba);
194149871Sscottl	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT,
195149871Sscottl	    IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave);
196136849Sscottl	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT,
197149871Sscottl	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth);
198136849Sscottl
199136849Sscottl	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
200136849Sscottl	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba);
201136849Sscottl	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
202136849Sscottl	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba);
203136849Sscottl	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
204136849Sscottl	    IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba);
205136849Sscottl	ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT,
206136849Sscottl	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth);
207136849Sscottl}
208136849SscottlSYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL);
209136849Sscottl
210136849Sscottlstatic int ieee80211_ampdu_enable(struct ieee80211_node *ni,
211136849Sscottl	struct ieee80211_tx_ampdu *tap);
212136849Sscottlstatic int ieee80211_addba_request(struct ieee80211_node *ni,
213136849Sscottl	struct ieee80211_tx_ampdu *tap,
214136849Sscottl	int dialogtoken, int baparamset, int batimeout);
215136849Sscottlstatic int ieee80211_addba_response(struct ieee80211_node *ni,
216149871Sscottl	struct ieee80211_tx_ampdu *tap,
217149871Sscottl	int code, int baparamset, int batimeout);
218149871Sscottlstatic void ieee80211_addba_stop(struct ieee80211_node *ni,
219136849Sscottl	struct ieee80211_tx_ampdu *tap);
220136849Sscottlstatic void ieee80211_bar_response(struct ieee80211_node *ni,
221136849Sscottl	struct ieee80211_tx_ampdu *tap, int status);
222136849Sscottlstatic void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
223149871Sscottlstatic void bar_stop_timer(struct ieee80211_tx_ampdu *tap);
224149871Sscottlstatic int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *,
225149871Sscottl	int baparamset, int batimeout, int baseqctl);
226149871Sscottlstatic void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *);
227149871Sscottl
228149871Sscottlvoid
229149871Sscottlieee80211_ht_attach(struct ieee80211com *ic)
230149871Sscottl{
231136849Sscottl	/* setup default aggregation policy */
232136849Sscottl	ic->ic_recv_action = ieee80211_recv_action;
233136849Sscottl	ic->ic_send_action = ieee80211_send_action;
234136849Sscottl	ic->ic_ampdu_enable = ieee80211_ampdu_enable;
235136849Sscottl	ic->ic_addba_request = ieee80211_addba_request;
236136849Sscottl	ic->ic_addba_response = ieee80211_addba_response;
237149871Sscottl	ic->ic_addba_stop = ieee80211_addba_stop;
238149871Sscottl	ic->ic_bar_response = ieee80211_bar_response;
239149871Sscottl	ic->ic_ampdu_rx_start = ampdu_rx_start;
240136849Sscottl	ic->ic_ampdu_rx_stop = ampdu_rx_stop;
241136849Sscottl
242136849Sscottl	ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
243136849Sscottl	ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
244136849Sscottl}
245136849Sscottl
246136849Sscottlvoid
247136849Sscottlieee80211_ht_detach(struct ieee80211com *ic)
248136849Sscottl{
249136849Sscottl}
250136849Sscottl
251136849Sscottlvoid
252136849Sscottlieee80211_ht_vattach(struct ieee80211vap *vap)
253136849Sscottl{
254190809Sdelphij
255136849Sscottl	/* driver can override defaults */
256136849Sscottl	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
257136849Sscottl	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
258136849Sscottl	vap->iv_ampdu_limit = vap->iv_ampdu_rxmax;
259149871Sscottl	vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU;
260136849Sscottl	/* tx aggregation traffic thresholds */
261136849Sscottl	vap->iv_ampdu_mintraffic[WME_AC_BK] = 128;
262136849Sscottl	vap->iv_ampdu_mintraffic[WME_AC_BE] = 64;
263136849Sscottl	vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
264136849Sscottl	vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
265149871Sscottl
266149871Sscottl	if (vap->iv_htcaps & IEEE80211_HTC_HT) {
267149871Sscottl		/*
268136849Sscottl		 * Device is HT capable; enable all HT-related
269136849Sscottl		 * facilities by default.
270149871Sscottl		 * XXX these choices may be too aggressive.
271136849Sscottl		 */
272136849Sscottl		vap->iv_flags_ht |= IEEE80211_FHT_HT
273136849Sscottl				 |  IEEE80211_FHT_HTCOMPAT
274136849Sscottl				 ;
275190809Sdelphij		if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20)
276149871Sscottl			vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20;
277149871Sscottl		/* XXX infer from channel list? */
278136849Sscottl		if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
279136849Sscottl			vap->iv_flags_ht |= IEEE80211_FHT_USEHT40;
280136849Sscottl			if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40)
281136849Sscottl				vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40;
282190809Sdelphij		}
283149871Sscottl		/* enable RIFS if capable */
284149871Sscottl		if (vap->iv_htcaps & IEEE80211_HTC_RIFS)
285149871Sscottl			vap->iv_flags_ht |= IEEE80211_FHT_RIFS;
286149871Sscottl
287149871Sscottl		/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
288136849Sscottl		vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX;
289136849Sscottl		if (vap->iv_htcaps & IEEE80211_HTC_AMPDU)
290136849Sscottl			vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX;
291136849Sscottl		vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX;
292190809Sdelphij		if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
293149871Sscottl			vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX;
294149871Sscottl	}
295136849Sscottl	/* NB: disable default legacy WDS, too many issues right now */
296136849Sscottl	if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
297136849Sscottl		vap->iv_flags_ht &= ~IEEE80211_FHT_HT;
298136849Sscottl}
299190809Sdelphij
300149871Sscottlvoid
301149871Sscottlieee80211_ht_vdetach(struct ieee80211vap *vap)
302136849Sscottl{
303136849Sscottl}
304136849Sscottl
305136849Sscottlstatic int
306136849Sscottlht_getrate(struct ieee80211com *ic, int index, int mode, int ratetype)
307136849Sscottl{
308136849Sscottl	int mword, rate;
309136849Sscottl
310149871Sscottl	mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode);
311190809Sdelphij	if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
312298955Spfg		return (0);
313149871Sscottl	switch (ratetype) {
314136849Sscottl	case 0:
315136849Sscottl		rate = ieee80211_htrates[index].ht20_rate_800ns;
316136849Sscottl		break;
317136849Sscottl	case 1:
318136849Sscottl		rate = ieee80211_htrates[index].ht20_rate_400ns;
319136849Sscottl		break;
320136849Sscottl	case 2:
321136849Sscottl		rate = ieee80211_htrates[index].ht40_rate_800ns;
322136849Sscottl		break;
323136849Sscottl	default:
324136849Sscottl		rate = ieee80211_htrates[index].ht40_rate_400ns;
325149871Sscottl		break;
326149871Sscottl	}
327136849Sscottl	return (rate);
328136849Sscottl}
329149871Sscottl
330149871Sscottlstatic struct printranges {
331149871Sscottl	int	minmcs;
332136849Sscottl	int	maxmcs;
333136849Sscottl	int	txstream;
334136849Sscottl	int	ratetype;
335149871Sscottl	int	htcapflags;
336149871Sscottl} ranges[] = {
337136849Sscottl	{  0,  7, 1, 0, 0 },
338136849Sscottl	{  8, 15, 2, 0, 0 },
339149871Sscottl	{ 16, 23, 3, 0, 0 },
340149871Sscottl	{ 24, 31, 4, 0, 0 },
341149871Sscottl	{ 32,  0, 1, 2, IEEE80211_HTC_TXMCS32 },
342136849Sscottl	{ 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL },
343136849Sscottl	{ 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL },
344136849Sscottl	{ 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL },
345149871Sscottl	{  0,  0, 0, 0, 0 },
346149871Sscottl};
347149871Sscottl
348136849Sscottlstatic void
349136849Sscottlht_rateprint(struct ieee80211com *ic, int mode, int ratetype)
350136849Sscottl{
351136849Sscottl	struct ifnet *ifp = ic->ic_ifp;
352136849Sscottl	int minrate, maxrate;
353136849Sscottl	struct printranges *range;
354149871Sscottl
355149871Sscottl	for (range = ranges; range->txstream != 0; range++) {
356149871Sscottl		if (ic->ic_txstream < range->txstream)
357136849Sscottl			continue;
358136849Sscottl		if (range->htcapflags &&
359136849Sscottl		    (ic->ic_htcaps & range->htcapflags) == 0)
360136849Sscottl			continue;
361149871Sscottl		if (ratetype < range->ratetype)
362149871Sscottl			continue;
363136849Sscottl		minrate = ht_getrate(ic, range->minmcs, mode, ratetype);
364136849Sscottl		maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype);
365149871Sscottl		if (range->maxmcs) {
366149871Sscottl			if_printf(ifp, "MCS %d-%d: %d%sMbps - %d%sMbps\n",
367149871Sscottl			    range->minmcs, range->maxmcs,
368136849Sscottl			    minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""),
369136849Sscottl			    maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : ""));
370136849Sscottl		} else {
371136849Sscottl			if_printf(ifp, "MCS %d: %d%sMbps\n", range->minmcs,
372136849Sscottl			    minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""));
373136849Sscottl		}
374136849Sscottl	}
375136849Sscottl}
376136849Sscottl
377149871Sscottlstatic void
378149871Sscottlht_announce(struct ieee80211com *ic, int mode)
379149871Sscottl{
380136849Sscottl	struct ifnet *ifp = ic->ic_ifp;
381136849Sscottl	const char *modestr = ieee80211_phymode_name[mode];
382136849Sscottl
383136849Sscottl	if_printf(ifp, "%s MCS 20MHz\n", modestr);
384136849Sscottl	ht_rateprint(ic, mode, 0);
385136849Sscottl	if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) {
386136849Sscottl		if_printf(ifp, "%s MCS 20MHz SGI\n", modestr);
387136849Sscottl		ht_rateprint(ic, mode, 1);
388136849Sscottl	}
389136849Sscottl	if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
390149871Sscottl		if_printf(ifp, "%s MCS 40MHz:\n", modestr);
391136849Sscottl		ht_rateprint(ic, mode, 2);
392136849Sscottl	}
393136849Sscottl	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
394149871Sscottl	    (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) {
395149871Sscottl		if_printf(ifp, "%s MCS 40MHz SGI:\n", modestr);
396149871Sscottl		ht_rateprint(ic, mode, 3);
397149871Sscottl	}
398136849Sscottl}
399136849Sscottl
400149871Sscottlvoid
401136849Sscottlieee80211_ht_announce(struct ieee80211com *ic)
402136849Sscottl{
403149871Sscottl	struct ifnet *ifp = ic->ic_ifp;
404136849Sscottl
405136849Sscottl	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
406136849Sscottl	    isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
407149871Sscottl		if_printf(ifp, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream);
408136849Sscottl	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
409136849Sscottl		ht_announce(ic, IEEE80211_MODE_11NA);
410136849Sscottl	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
411149871Sscottl		ht_announce(ic, IEEE80211_MODE_11NG);
412136849Sscottl}
413136849Sscottl
414149871Sscottlstatic struct ieee80211_htrateset htrateset;
415149871Sscottl
416149871Sscottlconst struct ieee80211_htrateset *
417149871Sscottlieee80211_get_suphtrates(struct ieee80211com *ic,
418136849Sscottl    const struct ieee80211_channel *c)
419149871Sscottl{
420149871Sscottl#define	ADDRATE(x)	do {						\
421136849Sscottl	htrateset.rs_rates[htrateset.rs_nrates] = x;			\
422136849Sscottl	htrateset.rs_nrates++;						\
423136849Sscottl} while (0)
424149871Sscottl	int i;
425149871Sscottl
426136849Sscottl	memset(&htrateset, 0, sizeof(struct ieee80211_htrateset));
427149871Sscottl	for (i = 0; i < ic->ic_txstream * 8; i++)
428136849Sscottl		ADDRATE(i);
429136849Sscottl	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
430136849Sscottl	    (ic->ic_htcaps & IEEE80211_HTC_TXMCS32))
431136849Sscottl		ADDRATE(i);
432136849Sscottl	if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
433149871Sscottl		if (ic->ic_txstream >= 2) {
434149871Sscottl			 for (i = 33; i <= 38; i++)
435136849Sscottl				ADDRATE(i);
436149871Sscottl		}
437149871Sscottl		if (ic->ic_txstream >= 3) {
438136849Sscottl			for (i = 39; i <= 52; i++)
439136849Sscottl				ADDRATE(i);
440136849Sscottl		}
441136849Sscottl		if (ic->ic_txstream == 4) {
442136849Sscottl			for (i = 53; i <= 76; i++)
443136849Sscottl				ADDRATE(i);
444136849Sscottl		}
445136849Sscottl	}
446136849Sscottl	return &htrateset;
447136849Sscottl#undef	ADDRATE
448136849Sscottl}
449136849Sscottl
450136849Sscottl/*
451136849Sscottl * Receive processing.
452136849Sscottl */
453149871Sscottl
454149871Sscottl/*
455149871Sscottl * Decap the encapsulated A-MSDU frames and dispatch all but
456136849Sscottl * the last for delivery.  The last frame is returned for
457149871Sscottl * delivery via the normal path.
458136849Sscottl */
459136849Sscottlstruct mbuf *
460136849Sscottlieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
461136849Sscottl{
462136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
463136849Sscottl	int framelen;
464149871Sscottl	struct mbuf *n;
465149871Sscottl
466149871Sscottl	/* discard 802.3 header inserted by ieee80211_decap */
467149871Sscottl	m_adj(m, sizeof(struct ether_header));
468136849Sscottl
469190809Sdelphij	vap->iv_stats.is_amsdu_decap++;
470149871Sscottl
471136849Sscottl	for (;;) {
472136849Sscottl		/*
473136849Sscottl		 * Decap the first frame, bust it apart from the
474149871Sscottl		 * remainder and deliver.  We leave the last frame
475149871Sscottl		 * delivery to the caller (for consistency with other
476190809Sdelphij		 * code paths, could also do it here).
477149871Sscottl		 */
478136849Sscottl		m = ieee80211_decap1(m, &framelen);
479136849Sscottl		if (m == NULL) {
480136849Sscottl			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
481136849Sscottl			    ni->ni_macaddr, "a-msdu", "%s", "decap failed");
482149871Sscottl			vap->iv_stats.is_amsdu_tooshort++;
483149871Sscottl			return NULL;
484149871Sscottl		}
485149871Sscottl		if (m->m_pkthdr.len == framelen)
486136849Sscottl			break;
487149871Sscottl		n = m_split(m, framelen, M_NOWAIT);
488149871Sscottl		if (n == NULL) {
489190809Sdelphij			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
490149871Sscottl			    ni->ni_macaddr, "a-msdu",
491136849Sscottl			    "%s", "unable to split encapsulated frames");
492136849Sscottl			vap->iv_stats.is_amsdu_split++;
493136849Sscottl			m_freem(m);			/* NB: must reclaim */
494136849Sscottl			return NULL;
495136849Sscottl		}
496149871Sscottl		vap->iv_deliver_data(vap, ni, m);
497149871Sscottl
498190809Sdelphij		/*
499149871Sscottl		 * Remove frame contents; each intermediate frame
500136849Sscottl		 * is required to be aligned to a 4-byte boundary.
501136849Sscottl		 */
502149871Sscottl		m = n;
503149871Sscottl		m_adj(m, roundup2(framelen, 4) - framelen);	/* padding */
504190809Sdelphij	}
505149871Sscottl	return m;				/* last delivered by caller */
506136849Sscottl}
507136849Sscottl
508136849Sscottl/*
509136849Sscottl * Purge all frames in the A-MPDU re-order queue.
510136849Sscottl */
511149871Sscottlstatic void
512149871Sscottlampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
513190809Sdelphij{
514149871Sscottl	struct mbuf *m;
515136849Sscottl	int i;
516149871Sscottl
517149871Sscottl	for (i = 0; i < rap->rxa_wnd; i++) {
518149871Sscottl		m = rap->rxa_m[i];
519190809Sdelphij		if (m != NULL) {
520149871Sscottl			rap->rxa_m[i] = NULL;
521136849Sscottl			rap->rxa_qbytes -= m->m_pkthdr.len;
522136849Sscottl			m_freem(m);
523136849Sscottl			if (--rap->rxa_qframes == 0)
524149871Sscottl				break;
525136849Sscottl		}
526149871Sscottl	}
527149871Sscottl	KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
528149871Sscottl	    ("lost %u data, %u frames on ampdu rx q",
529149871Sscottl	    rap->rxa_qbytes, rap->rxa_qframes));
530149871Sscottl}
531149871Sscottl
532149871Sscottl/*
533149871Sscottl * Start A-MPDU rx/re-order processing for the specified TID.
534190809Sdelphij */
535149871Sscottlstatic int
536136849Sscottlampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
537136849Sscottl	int baparamset, int batimeout, int baseqctl)
538136849Sscottl{
539190809Sdelphij	int bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
540149871Sscottl
541149871Sscottl	if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
542149871Sscottl		/*
543149871Sscottl		 * AMPDU previously setup and not terminated with a DELBA,
544190809Sdelphij		 * flush the reorder q's in case anything remains.
545149871Sscottl		 */
546136849Sscottl		ampdu_rx_purge(rap);
547149871Sscottl	}
548149871Sscottl	memset(rap, 0, sizeof(*rap));
549149871Sscottl	rap->rxa_wnd = (bufsiz == 0) ?
550149871Sscottl	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
551190809Sdelphij	rap->rxa_start = MS(baseqctl, IEEE80211_BASEQ_START);
552149871Sscottl	rap->rxa_flags |=  IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
553149871Sscottl
554149871Sscottl	return 0;
555149871Sscottl}
556149871Sscottl
557190809Sdelphij/*
558149871Sscottl * Stop A-MPDU rx processing for the specified TID.
559149871Sscottl */
560149871Sscottlstatic void
561136849Sscottlampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
562190809Sdelphij{
563149871Sscottl
564136849Sscottl	ampdu_rx_purge(rap);
565136849Sscottl	rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND);
566136849Sscottl}
567136849Sscottl
568190809Sdelphij/*
569149871Sscottl * Dispatch a frame from the A-MPDU reorder queue.  The
570136849Sscottl * frame is fed back into ieee80211_input marked with an
571149871Sscottl * M_AMPDU_MPDU flag so it doesn't come back to us (it also
572149871Sscottl * permits ieee80211_input to optimize re-processing).
573149871Sscottl */
574149871Sscottlstatic __inline void
575190809Sdelphijampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
576149871Sscottl{
577136849Sscottl	m->m_flags |= M_AMPDU_MPDU;	/* bypass normal processing */
578136849Sscottl	/* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */
579136849Sscottl	(void) ieee80211_input(ni, m, 0, 0);
580149871Sscottl}
581149871Sscottl
582136849Sscottl/*
583149871Sscottl * Dispatch as many frames as possible from the re-order queue.
584149871Sscottl * Frames will always be "at the front"; we process all frames
585149871Sscottl * up to the first empty slot in the window.  On completion we
586136849Sscottl * cleanup state if there are still pending frames in the current
587149871Sscottl * BA window.  We assume the frame at slot 0 is already handled
588149871Sscottl * by the caller; we always start at slot 1.
589149871Sscottl */
590149871Sscottlstatic void
591136849Sscottlampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
592136849Sscottl{
593136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
594190809Sdelphij	struct mbuf *m;
595149871Sscottl	int i;
596136849Sscottl
597136849Sscottl	/* flush run of frames */
598149871Sscottl	for (i = 1; i < rap->rxa_wnd; i++) {
599149871Sscottl		m = rap->rxa_m[i];
600149871Sscottl		if (m == NULL)
601190809Sdelphij			break;
602149871Sscottl		rap->rxa_m[i] = NULL;
603136849Sscottl		rap->rxa_qbytes -= m->m_pkthdr.len;
604136849Sscottl		rap->rxa_qframes--;
605136849Sscottl
606136849Sscottl		ampdu_dispatch(ni, m);
607149871Sscottl	}
608149871Sscottl	/*
609136849Sscottl	 * If frames remain, copy the mbuf pointers down so
610149871Sscottl	 * they correspond to the offsets in the new window.
611149871Sscottl	 */
612149871Sscottl	if (rap->rxa_qframes != 0) {
613136849Sscottl		int n = rap->rxa_qframes, j;
614149871Sscottl		for (j = i+1; j < rap->rxa_wnd; j++) {
615149871Sscottl			if (rap->rxa_m[j] != NULL) {
616149871Sscottl				rap->rxa_m[j-i] = rap->rxa_m[j];
617136849Sscottl				rap->rxa_m[j] = NULL;
618149871Sscottl				if (--n == 0)
619149871Sscottl					break;
620149871Sscottl			}
621136849Sscottl		}
622149871Sscottl		KASSERT(n == 0, ("lost %d frames", n));
623149871Sscottl		vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
624149871Sscottl	}
625136849Sscottl	/*
626136849Sscottl	 * Adjust the start of the BA window to
627136849Sscottl	 * reflect the frames just dispatched.
628190809Sdelphij	 */
629149871Sscottl	rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
630136849Sscottl	vap->iv_stats.is_ampdu_rx_oor += i;
631136849Sscottl}
632149871Sscottl
633149871Sscottl#ifdef IEEE80211_AMPDU_AGE
634149871Sscottl/*
635149871Sscottl * Dispatch all frames in the A-MPDU re-order queue.
636190809Sdelphij */
637149871Sscottlstatic void
638136849Sscottlampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
639149871Sscottl{
640136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
641136849Sscottl	struct mbuf *m;
642149871Sscottl	int i;
643149871Sscottl
644149871Sscottl	for (i = 0; i < rap->rxa_wnd; i++) {
645149871Sscottl		m = rap->rxa_m[i];
646149871Sscottl		if (m == NULL)
647149871Sscottl			continue;
648149871Sscottl		rap->rxa_m[i] = NULL;
649149871Sscottl		rap->rxa_qbytes -= m->m_pkthdr.len;
650149871Sscottl		rap->rxa_qframes--;
651149871Sscottl		vap->iv_stats.is_ampdu_rx_oor++;
652149871Sscottl
653149871Sscottl		ampdu_dispatch(ni, m);
654149871Sscottl		if (rap->rxa_qframes == 0)
655149871Sscottl			break;
656149871Sscottl	}
657149871Sscottl}
658149871Sscottl#endif /* IEEE80211_AMPDU_AGE */
659149871Sscottl
660149871Sscottl/*
661149871Sscottl * Dispatch all frames in the A-MPDU re-order queue
662149871Sscottl * preceding the specified sequence number.  This logic
663149871Sscottl * handles window moves due to a received MSDU or BAR.
664149871Sscottl */
665149871Sscottlstatic void
666149871Sscottlampdu_rx_flush_upto(struct ieee80211_node *ni,
667149871Sscottl	struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
668149871Sscottl{
669149871Sscottl	struct ieee80211vap *vap = ni->ni_vap;
670136849Sscottl	struct mbuf *m;
671149871Sscottl	ieee80211_seq seqno;
672136849Sscottl	int i;
673149871Sscottl
674136849Sscottl	/*
675149871Sscottl	 * Flush any complete MSDU's with a sequence number lower
676149871Sscottl	 * than winstart.  Gaps may exist.  Note that we may actually
677149871Sscottl	 * dispatch frames past winstart if a run continues; this is
678149871Sscottl	 * an optimization that avoids having to do a separate pass
679149871Sscottl	 * to dispatch frames after moving the BA window start.
680149871Sscottl	 */
681149871Sscottl	seqno = rap->rxa_start;
682149871Sscottl	for (i = 0; i < rap->rxa_wnd; i++) {
683190809Sdelphij		m = rap->rxa_m[i];
684149871Sscottl		if (m != NULL) {
685136849Sscottl			rap->rxa_m[i] = NULL;
686136849Sscottl			rap->rxa_qbytes -= m->m_pkthdr.len;
687136849Sscottl			rap->rxa_qframes--;
688190809Sdelphij			vap->iv_stats.is_ampdu_rx_oor++;
689149871Sscottl
690136849Sscottl			ampdu_dispatch(ni, m);
691149871Sscottl		} else {
692149871Sscottl			if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
693190809Sdelphij				break;
694149871Sscottl		}
695149871Sscottl		seqno = IEEE80211_SEQ_INC(seqno);
696149871Sscottl	}
697149871Sscottl	/*
698149871Sscottl	 * If frames remain, copy the mbuf pointers down so
699149871Sscottl	 * they correspond to the offsets in the new window.
700190809Sdelphij	 */
701149871Sscottl	if (rap->rxa_qframes != 0) {
702149871Sscottl		int n = rap->rxa_qframes, j;
703149871Sscottl
704149871Sscottl		/* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
705149871Sscottl		KASSERT(rap->rxa_m[0] == NULL,
706190809Sdelphij		    ("%s: BA window slot 0 occupied", __func__));
707149871Sscottl		for (j = i+1; j < rap->rxa_wnd; j++) {
708149871Sscottl			if (rap->rxa_m[j] != NULL) {
709149871Sscottl				rap->rxa_m[j-i] = rap->rxa_m[j];
710136849Sscottl				rap->rxa_m[j] = NULL;
711190809Sdelphij				if (--n == 0)
712149871Sscottl					break;
713149871Sscottl			}
714136849Sscottl		}
715136849Sscottl		KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
716136849Sscottl		    "BA win <%d:%d> winstart %d",
717149871Sscottl		    __func__, n, rap->rxa_qframes, i, rap->rxa_start,
718190809Sdelphij		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
719149871Sscottl		    winstart));
720149871Sscottl		vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
721149871Sscottl	}
722149871Sscottl	/*
723149871Sscottl	 * Move the start of the BA window; we use the
724190809Sdelphij	 * sequence number of the last MSDU that was
725149871Sscottl	 * passed up the stack+1 or winstart if stopped on
726136849Sscottl	 * a gap in the reorder buffer.
727136849Sscottl	 */
728136849Sscottl	rap->rxa_start = seqno;
729136849Sscottl}
730149871Sscottl
731149871Sscottl/*
732190809Sdelphij * Process a received QoS data frame for an HT station.  Handle
733149871Sscottl * A-MPDU reordering: if this frame is received out of order
734136849Sscottl * and falls within the BA window hold onto it.  Otherwise if
735136849Sscottl * this frame completes a run, flush any pending frames.  We
736190809Sdelphij * return 1 if the frame is consumed.  A 0 is returned if
737149871Sscottl * the frame should be processed normally by the caller.
738136849Sscottl */
739136849Sscottlint
740136849Sscottlieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
741136849Sscottl{
742136849Sscottl#define	IEEE80211_FC0_QOSDATA \
743136849Sscottl	(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
744136849Sscottl#define	PROCESS		0	/* caller should process frame */
745136849Sscottl#define	CONSUMED	1	/* frame consumed, caller does nothing */
746136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
747136849Sscottl	struct ieee80211_qosframe *wh;
748149871Sscottl	struct ieee80211_rx_ampdu *rap;
749149871Sscottl	ieee80211_seq rxseq;
750149871Sscottl	uint8_t tid;
751269617Sjhb	int off;
752269617Sjhb
753149871Sscottl	KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU,
754149871Sscottl	    ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags));
755149871Sscottl	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
756149871Sscottl
757149871Sscottl	/* NB: m_len known to be sufficient */
758149871Sscottl	wh = mtod(m, struct ieee80211_qosframe *);
759149871Sscottl	if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) {
760149871Sscottl		/*
761149871Sscottl		 * Not QoS data, shouldn't get here but just
762190809Sdelphij		 * return it to the caller for processing.
763149871Sscottl		 */
764149871Sscottl		return PROCESS;
765149871Sscottl	}
766149871Sscottl	if (IEEE80211_IS_DSTODS(wh))
767190809Sdelphij		tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
768149871Sscottl	else
769149871Sscottl		tid = wh->i_qos[0];
770149871Sscottl	tid &= IEEE80211_QOS_TID;
771149871Sscottl	rap = &ni->ni_rx_ampdu[tid];
772149871Sscottl	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
773149871Sscottl		/*
774149871Sscottl		 * No ADDBA request yet, don't touch.
775149871Sscottl		 */
776190809Sdelphij		return PROCESS;
777149871Sscottl	}
778149871Sscottl	rxseq = le16toh(*(uint16_t *)wh->i_seq);
779149871Sscottl	if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) {
780149871Sscottl		/*
781149871Sscottl		 * Fragments are not allowed; toss.
782149871Sscottl		 */
783149871Sscottl		IEEE80211_DISCARD_MAC(vap,
784149871Sscottl		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
785149871Sscottl		    "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid,
786149871Sscottl		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
787136849Sscottl		vap->iv_stats.is_ampdu_rx_drop++;
788149871Sscottl		IEEE80211_NODE_STAT(ni, rx_drop);
789149871Sscottl		m_freem(m);
790149871Sscottl		return CONSUMED;
791149871Sscottl	}
792190809Sdelphij	rxseq >>= IEEE80211_SEQ_SEQ_SHIFT;
793149871Sscottl	rap->rxa_nframes++;
794149871Sscottlagain:
795149871Sscottl	if (rxseq == rap->rxa_start) {
796149871Sscottl		/*
797149871Sscottl		 * First frame in window.
798149871Sscottl		 */
799149871Sscottl		if (rap->rxa_qframes != 0) {
800149871Sscottl			/*
801149871Sscottl			 * Dispatch as many packets as we can.
802149871Sscottl			 */
803149871Sscottl			KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup"));
804190809Sdelphij			ampdu_dispatch(ni, m);
805149871Sscottl			ampdu_rx_dispatch(rap, ni);
806149871Sscottl			return CONSUMED;
807149871Sscottl		} else {
808136849Sscottl			/*
809149871Sscottl			 * In order; advance window and notify
810149871Sscottl			 * caller to dispatch directly.
811190809Sdelphij			 */
812149871Sscottl			rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
813149871Sscottl			return PROCESS;
814149871Sscottl		}
815136849Sscottl	}
816136849Sscottl	/*
817149871Sscottl	 * Frame is out of order; store if in the BA window.
818149871Sscottl	 */
819136849Sscottl	/* calculate offset in BA window */
820149871Sscottl	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
821149871Sscottl	if (off < rap->rxa_wnd) {
822136849Sscottl		/*
823136849Sscottl		 * Common case (hopefully): in the BA window.
824149871Sscottl		 * Sec 9.10.7.6.2 a) (p.137)
825136849Sscottl		 */
826136849Sscottl#ifdef IEEE80211_AMPDU_AGE
827136849Sscottl		/*
828136849Sscottl		 * Check for frames sitting too long in the reorder queue.
829136849Sscottl		 * This should only ever happen if frames are not delivered
830136849Sscottl		 * without the sender otherwise notifying us (e.g. with a
831136849Sscottl		 * BAR to move the window).  Typically this happens because
832136849Sscottl		 * of vendor bugs that cause the sequence number to jump.
833149871Sscottl		 * When this happens we get a gap in the reorder queue that
834136849Sscottl		 * leaves frame sitting on the queue until they get pushed
835136849Sscottl		 * out due to window moves.  When the vendor does not send
836136849Sscottl		 * BAR this move only happens due to explicit packet sends
837136849Sscottl		 *
838136849Sscottl		 * NB: we only track the time of the oldest frame in the
839149871Sscottl		 * reorder q; this means that if we flush we might push
840136849Sscottl		 * frames that still "new"; if this happens then subsequent
841136849Sscottl		 * frames will result in BA window moves which cost something
842136849Sscottl		 * but is still better than a big throughput dip.
843136849Sscottl		 */
844149871Sscottl		if (rap->rxa_qframes != 0) {
845136849Sscottl			/* XXX honor batimeout? */
846149871Sscottl			if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
847136849Sscottl				/*
848149871Sscottl				 * Too long since we received the first
849136849Sscottl				 * frame; flush the reorder buffer.
850149871Sscottl				 */
851149871Sscottl				if (rap->rxa_qframes != 0) {
852149871Sscottl					vap->iv_stats.is_ampdu_rx_age +=
853136849Sscottl					    rap->rxa_qframes;
854149871Sscottl					ampdu_rx_flush(ni, rap);
855149871Sscottl				}
856149871Sscottl				rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
857190809Sdelphij				return PROCESS;
858149871Sscottl			}
859149871Sscottl		} else {
860269617Sjhb			/*
861149871Sscottl			 * First frame, start aging timer.
862149871Sscottl			 */
863149871Sscottl			rap->rxa_age = ticks;
864149871Sscottl		}
865190809Sdelphij#endif /* IEEE80211_AMPDU_AGE */
866149871Sscottl		/* save packet */
867149871Sscottl		if (rap->rxa_m[off] == NULL) {
868149871Sscottl			rap->rxa_m[off] = m;
869269617Sjhb			rap->rxa_qframes++;
870149871Sscottl			rap->rxa_qbytes += m->m_pkthdr.len;
871149871Sscottl			vap->iv_stats.is_ampdu_rx_reorder++;
872149871Sscottl		} else {
873149871Sscottl			IEEE80211_DISCARD_MAC(vap,
874149871Sscottl			    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
875136849Sscottl			    ni->ni_macaddr, "a-mpdu duplicate",
876250460Seadler			    "seqno %u tid %u BA win <%u:%u>",
877250460Seadler			    rxseq, tid, rap->rxa_start,
878149871Sscottl			    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
879149871Sscottl			vap->iv_stats.is_rx_dup++;
880149871Sscottl			IEEE80211_NODE_STAT(ni, rx_dup);
881149871Sscottl			m_freem(m);
882149871Sscottl		}
883149871Sscottl		return CONSUMED;
884190809Sdelphij	}
885149871Sscottl	if (off < IEEE80211_SEQ_BA_RANGE) {
886149871Sscottl		/*
887149871Sscottl		 * Outside the BA window, but within range;
888190809Sdelphij		 * flush the reorder q and move the window.
889149871Sscottl		 * Sec 9.10.7.6.2 b) (p.138)
890149871Sscottl		 */
891136849Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
892136849Sscottl		    "move BA win <%u:%u> (%u frames) rxseq %u tid %u",
893136849Sscottl		    rap->rxa_start,
894136849Sscottl		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
895149871Sscottl		    rap->rxa_qframes, rxseq, tid);
896136849Sscottl		vap->iv_stats.is_ampdu_rx_move++;
897136849Sscottl
898149871Sscottl		/*
899149871Sscottl		 * The spec says to flush frames up to but not including:
900149871Sscottl		 * 	WinStart_B = rxseq - rap->rxa_wnd + 1
901149871Sscottl		 * Then insert the frame or notify the caller to process
902190809Sdelphij		 * it immediately.  We can safely do this by just starting
903149871Sscottl		 * over again because we know the frame will now be within
904136849Sscottl		 * the BA window.
905136849Sscottl		 */
906149871Sscottl		/* NB: rxa_wnd known to be >0 */
907149871Sscottl		ampdu_rx_flush_upto(ni, rap,
908136849Sscottl		    IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
909149871Sscottl		goto again;
910149871Sscottl	} else {
911149871Sscottl		/*
912136849Sscottl		 * Outside the BA window and out of range; toss.
913149871Sscottl		 * Sec 9.10.7.6.2 c) (p.138)
914136849Sscottl		 */
915149871Sscottl		IEEE80211_DISCARD_MAC(vap,
916149871Sscottl		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
917149871Sscottl		    "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
918190809Sdelphij		    rap->rxa_start,
919149871Sscottl		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
920149871Sscottl		    rap->rxa_qframes, rxseq, tid,
921136849Sscottl		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
922136849Sscottl		vap->iv_stats.is_ampdu_rx_drop++;
923136849Sscottl		IEEE80211_NODE_STAT(ni, rx_drop);
924149871Sscottl		m_freem(m);
925149871Sscottl		return CONSUMED;
926149871Sscottl	}
927149871Sscottl#undef CONSUMED
928190809Sdelphij#undef PROCESS
929149871Sscottl#undef IEEE80211_FC0_QOSDATA
930149871Sscottl}
931136849Sscottl
932136849Sscottl/*
933149871Sscottl * Process a BAR ctl frame.  Dispatch all frames up to
934149871Sscottl * the sequence number of the frame.  If this frame is
935136849Sscottl * out of range it's discarded.
936149871Sscottl */
937149871Sscottlvoid
938149871Sscottlieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
939149871Sscottl{
940149871Sscottl	struct ieee80211vap *vap = ni->ni_vap;
941136849Sscottl	struct ieee80211_frame_bar *wh;
942149871Sscottl	struct ieee80211_rx_ampdu *rap;
943149871Sscottl	ieee80211_seq rxseq;
944149871Sscottl	int tid, off;
945250460Seadler
946149871Sscottl	if (!ieee80211_recv_bar_ena) {
947149871Sscottl#if 0
948149871Sscottl		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N,
949136849Sscottl		    ni->ni_macaddr, "BAR", "%s", "processing disabled");
950136849Sscottl#endif
951136849Sscottl		vap->iv_stats.is_ampdu_bar_bad++;
952136849Sscottl		return;
953136849Sscottl	}
954136849Sscottl	wh = mtod(m0, struct ieee80211_frame_bar *);
955136849Sscottl	/* XXX check basic BAR */
956136849Sscottl	tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID);
957149871Sscottl	rap = &ni->ni_rx_ampdu[tid];
958149871Sscottl	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
959136849Sscottl		/*
960136849Sscottl		 * No ADDBA request yet, don't touch.
961136849Sscottl		 */
962136849Sscottl		IEEE80211_DISCARD_MAC(vap,
963136849Sscottl		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
964136849Sscottl		    ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
965136849Sscottl		vap->iv_stats.is_ampdu_bar_bad++;
966149871Sscottl		return;
967136849Sscottl	}
968136849Sscottl	vap->iv_stats.is_ampdu_bar_rx++;
969136849Sscottl	rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
970136849Sscottl	if (rxseq == rap->rxa_start)
971136849Sscottl		return;
972136849Sscottl	/* calculate offset in BA window */
973136849Sscottl	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
974136849Sscottl	if (off < IEEE80211_SEQ_BA_RANGE) {
975136849Sscottl		/*
976136849Sscottl		 * Flush the reorder q up to rxseq and move the window.
977136849Sscottl		 * Sec 9.10.7.6.3 a) (p.138)
978136849Sscottl		 */
979136849Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
980136849Sscottl		    "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
981136849Sscottl		    rap->rxa_start,
982136849Sscottl		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
983136849Sscottl		    rap->rxa_qframes, rxseq, tid);
984136849Sscottl		vap->iv_stats.is_ampdu_bar_move++;
985136849Sscottl
986136849Sscottl		ampdu_rx_flush_upto(ni, rap, rxseq);
987136849Sscottl		if (off >= rap->rxa_wnd) {
988136849Sscottl			/*
989136849Sscottl			 * BAR specifies a window start to the right of BA
990149871Sscottl			 * window; we must move it explicitly since
991136849Sscottl			 * ampdu_rx_flush_upto will not.
992136849Sscottl			 */
993136849Sscottl			rap->rxa_start = rxseq;
994136849Sscottl		}
995136849Sscottl	} else {
996136849Sscottl		/*
997136849Sscottl		 * Out of range; toss.
998149871Sscottl		 * Sec 9.10.7.6.3 b) (p.138)
999136849Sscottl		 */
1000136849Sscottl		IEEE80211_DISCARD_MAC(vap,
1001136849Sscottl		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
1002149871Sscottl		    "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
1003149871Sscottl		    rap->rxa_start,
1004136849Sscottl		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
1005149871Sscottl		    rap->rxa_qframes, rxseq, tid,
1006149871Sscottl		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
1007136849Sscottl		vap->iv_stats.is_ampdu_bar_oow++;
1008136849Sscottl		IEEE80211_NODE_STAT(ni, rx_drop);
1009136849Sscottl	}
1010136849Sscottl}
1011136849Sscottl
1012136849Sscottl/*
1013149871Sscottl * Setup HT-specific state in a node.  Called only
1014149871Sscottl * when HT use is negotiated so we don't do extra
1015149871Sscottl * work for temporary and/or legacy sta's.
1016136849Sscottl */
1017136849Sscottlvoid
1018136849Sscottlieee80211_ht_node_init(struct ieee80211_node *ni)
1019136849Sscottl{
1020136849Sscottl	struct ieee80211_tx_ampdu *tap;
1021136849Sscottl	int ac;
1022136849Sscottl
1023136849Sscottl	if (ni->ni_flags & IEEE80211_NODE_HT) {
1024136849Sscottl		/*
1025136849Sscottl		 * Clean AMPDU state on re-associate.  This handles the case
1026136849Sscottl		 * where a station leaves w/o notifying us and then returns
1027149871Sscottl		 * before node is reaped for inactivity.
1028190809Sdelphij		 */
1029190809Sdelphij		ieee80211_ht_node_cleanup(ni);
1030136849Sscottl	}
1031136849Sscottl	for (ac = 0; ac < WME_NUM_AC; ac++) {
1032136849Sscottl		tap = &ni->ni_tx_ampdu[ac];
1033149871Sscottl		tap->txa_ac = ac;
1034149871Sscottl		tap->txa_ni = ni;
1035136849Sscottl		/* NB: further initialization deferred */
1036136849Sscottl	}
1037136849Sscottl	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
1038149871Sscottl}
1039136849Sscottl
1040136849Sscottl/*
1041149871Sscottl * Cleanup HT-specific state in a node.  Called only
1042149871Sscottl * when HT use has been marked.
1043149871Sscottl */
1044149871Sscottlvoid
1045149871Sscottlieee80211_ht_node_cleanup(struct ieee80211_node *ni)
1046149871Sscottl{
1047136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
1048136849Sscottl	int i;
1049136849Sscottl
1050136849Sscottl	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
1051136849Sscottl
1052136849Sscottl	/* XXX optimize this */
1053136849Sscottl	for (i = 0; i < WME_NUM_AC; i++) {
1054136849Sscottl		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
1055136849Sscottl		if (tap->txa_flags & IEEE80211_AGGR_SETUP)
1056136849Sscottl			ampdu_tx_stop(tap);
1057136849Sscottl	}
1058136849Sscottl	for (i = 0; i < WME_NUM_TID; i++)
1059136849Sscottl		ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]);
1060136849Sscottl
1061136849Sscottl	ni->ni_htcap = 0;
1062136849Sscottl	ni->ni_flags &= ~IEEE80211_NODE_HT_ALL;
1063149871Sscottl}
1064149871Sscottl
1065149871Sscottl/*
1066136849Sscottl * Age out HT resources for a station.
1067136849Sscottl */
1068136849Sscottlvoid
1069136849Sscottlieee80211_ht_node_age(struct ieee80211_node *ni)
1070136849Sscottl{
1071136849Sscottl#ifdef IEEE80211_AMPDU_AGE
1072136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1073136849Sscottl	uint8_t tid;
1074136849Sscottl#endif
1075136849Sscottl
1076136849Sscottl	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
1077136849Sscottl
1078136849Sscottl#ifdef IEEE80211_AMPDU_AGE
1079136849Sscottl	for (tid = 0; tid < WME_NUM_TID; tid++) {
1080136849Sscottl		struct ieee80211_rx_ampdu *rap;
1081136849Sscottl
1082136849Sscottl		rap = &ni->ni_rx_ampdu[tid];
1083136849Sscottl		if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
1084149871Sscottl			continue;
1085149871Sscottl		if (rap->rxa_qframes == 0)
1086149871Sscottl			continue;
1087136849Sscottl		/*
1088136849Sscottl		 * Check for frames sitting too long in the reorder queue.
1089136849Sscottl		 * See above for more details on what's happening here.
1090136849Sscottl		 */
1091136849Sscottl		/* XXX honor batimeout? */
1092136849Sscottl		if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
1093136849Sscottl			/*
1094190809Sdelphij			 * Too long since we received the first
1095190809Sdelphij			 * frame; flush the reorder buffer.
1096190809Sdelphij			 */
1097190809Sdelphij			vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
1098190809Sdelphij			ampdu_rx_flush(ni, rap);
1099190809Sdelphij		}
1100190809Sdelphij	}
1101190809Sdelphij#endif /* IEEE80211_AMPDU_AGE */
1102190809Sdelphij}
1103190809Sdelphij
1104190809Sdelphijstatic struct ieee80211_channel *
1105190809Sdelphijfindhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
1106190809Sdelphij{
1107190809Sdelphij	return ieee80211_find_channel(ic, c->ic_freq,
1108190809Sdelphij	    (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags);
1109190809Sdelphij}
1110190809Sdelphij
1111190809Sdelphij/*
1112190809Sdelphij * Adjust a channel to be HT/non-HT according to the vap's configuration.
1113190809Sdelphij */
1114190809Sdelphijstruct ieee80211_channel *
1115190809Sdelphijieee80211_ht_adjust_channel(struct ieee80211com *ic,
1116190809Sdelphij	struct ieee80211_channel *chan, int flags)
1117190809Sdelphij{
1118190809Sdelphij	struct ieee80211_channel *c;
1119190809Sdelphij
1120190809Sdelphij	if (flags & IEEE80211_FHT_HT) {
1121190809Sdelphij		/* promote to HT if possible */
1122190809Sdelphij		if (flags & IEEE80211_FHT_USEHT40) {
1123190809Sdelphij			if (!IEEE80211_IS_CHAN_HT40(chan)) {
1124190809Sdelphij				/* NB: arbitrarily pick ht40+ over ht40- */
1125190809Sdelphij				c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
1126190809Sdelphij				if (c == NULL)
1127190809Sdelphij					c = findhtchan(ic, chan,
1128190809Sdelphij						IEEE80211_CHAN_HT40D);
1129190809Sdelphij				if (c == NULL)
1130190809Sdelphij					c = findhtchan(ic, chan,
1131190809Sdelphij						IEEE80211_CHAN_HT20);
1132190809Sdelphij				if (c != NULL)
1133190809Sdelphij					chan = c;
1134190809Sdelphij			}
1135190809Sdelphij		} else if (!IEEE80211_IS_CHAN_HT20(chan)) {
1136190809Sdelphij			c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
1137190809Sdelphij			if (c != NULL)
1138190809Sdelphij				chan = c;
1139190809Sdelphij		}
1140190809Sdelphij	} else if (IEEE80211_IS_CHAN_HT(chan)) {
1141190809Sdelphij		/* demote to legacy, HT use is disabled */
1142190809Sdelphij		c = ieee80211_find_channel(ic, chan->ic_freq,
1143190809Sdelphij		    chan->ic_flags &~ IEEE80211_CHAN_HT);
1144190809Sdelphij		if (c != NULL)
1145190809Sdelphij			chan = c;
1146190809Sdelphij	}
1147190809Sdelphij	return chan;
1148190809Sdelphij}
1149190809Sdelphij
1150190809Sdelphij/*
1151190809Sdelphij * Setup HT-specific state for a legacy WDS peer.
1152190809Sdelphij */
1153190809Sdelphijvoid
1154190809Sdelphijieee80211_ht_wds_init(struct ieee80211_node *ni)
1155190809Sdelphij{
1156190809Sdelphij	struct ieee80211vap *vap = ni->ni_vap;
1157190809Sdelphij	struct ieee80211_tx_ampdu *tap;
1158190809Sdelphij	int ac;
1159190809Sdelphij
1160190809Sdelphij	KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested"));
1161190809Sdelphij
1162190809Sdelphij	/* XXX check scan cache in case peer has an ap and we have info */
1163190809Sdelphij	/*
1164190809Sdelphij	 * If setup with a legacy channel; locate an HT channel.
1165190809Sdelphij	 * Otherwise if the inherited channel (from a companion
1166190809Sdelphij	 * AP) is suitable use it so we use the same location
1167190809Sdelphij	 * for the extension channel).
1168190809Sdelphij	 */
1169190809Sdelphij	ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic,
1170190809Sdelphij	    ni->ni_chan, ieee80211_htchanflags(ni->ni_chan));
1171190809Sdelphij
1172190809Sdelphij	ni->ni_htcap = 0;
1173190809Sdelphij	if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)
1174190809Sdelphij		ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
1175190809Sdelphij	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
1176190809Sdelphij		ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
1177190809Sdelphij		ni->ni_chw = 40;
1178190809Sdelphij		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
1179190809Sdelphij			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
1180190809Sdelphij		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
1181190809Sdelphij			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
1182190809Sdelphij		if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)
1183190809Sdelphij			ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
1184190809Sdelphij	} else {
1185190809Sdelphij		ni->ni_chw = 20;
1186190809Sdelphij		ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
1187190809Sdelphij	}
1188190809Sdelphij	ni->ni_htctlchan = ni->ni_chan->ic_ieee;
1189190809Sdelphij	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
1190190809Sdelphij		ni->ni_flags |= IEEE80211_NODE_RIFS;
1191190809Sdelphij	/* XXX does it make sense to enable SMPS? */
1192190809Sdelphij
1193190809Sdelphij	ni->ni_htopmode = 0;		/* XXX need protection state */
1194190809Sdelphij	ni->ni_htstbc = 0;		/* XXX need info */
1195190809Sdelphij
1196190809Sdelphij	for (ac = 0; ac < WME_NUM_AC; ac++) {
1197190809Sdelphij		tap = &ni->ni_tx_ampdu[ac];
1198190809Sdelphij		tap->txa_ac = ac;
1199190809Sdelphij	}
1200190809Sdelphij	/* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */
1201190809Sdelphij	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
1202190809Sdelphij}
1203190809Sdelphij
1204190809Sdelphij/*
1205190809Sdelphij * Notify hostap vaps of a change in the HTINFO ie.
1206190809Sdelphij */
1207190809Sdelphijstatic void
1208190809Sdelphijhtinfo_notify(struct ieee80211com *ic)
1209190809Sdelphij{
1210190809Sdelphij	struct ieee80211vap *vap;
1211190809Sdelphij	int first = 1;
1212190809Sdelphij
1213190809Sdelphij	IEEE80211_LOCK_ASSERT(ic);
1214190809Sdelphij
1215136849Sscottl	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1216136849Sscottl		if (vap->iv_opmode != IEEE80211_M_HOSTAP)
1217149871Sscottl			continue;
1218136849Sscottl		if (vap->iv_state != IEEE80211_S_RUN ||
1219136849Sscottl		    !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
1220136849Sscottl			continue;
1221136849Sscottl		if (first) {
1222136849Sscottl			IEEE80211_NOTE(vap,
1223136849Sscottl			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
1224149871Sscottl			    vap->iv_bss,
1225136849Sscottl			    "HT bss occupancy change: %d sta, %d ht, "
1226136849Sscottl			    "%d ht40%s, HT protmode now 0x%x"
1227136849Sscottl			    , ic->ic_sta_assoc
1228136849Sscottl			    , ic->ic_ht_sta_assoc
1229136849Sscottl			    , ic->ic_ht40_sta_assoc
1230136849Sscottl			    , (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) ?
1231136849Sscottl				 ", non-HT sta present" : ""
1232149871Sscottl			    , ic->ic_curhtprotmode);
1233149871Sscottl			first = 0;
1234149871Sscottl		}
1235149871Sscottl		ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
1236149871Sscottl	}
1237149871Sscottl}
1238149871Sscottl
1239149871Sscottl/*
1240149871Sscottl * Calculate HT protection mode from current
1241149871Sscottl * state and handle updates.
1242149871Sscottl */
1243149871Sscottlstatic void
1244149871Sscottlhtinfo_update(struct ieee80211com *ic)
1245149871Sscottl{
1246149871Sscottl	uint8_t protmode;
1247149871Sscottl
1248136849Sscottl	if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
1249136849Sscottl		protmode = IEEE80211_HTINFO_OPMODE_MIXED
1250136849Sscottl			 | IEEE80211_HTINFO_NONHT_PRESENT;
1251136849Sscottl	} else if (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) {
1252136849Sscottl		protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
1253136849Sscottl			 | IEEE80211_HTINFO_NONHT_PRESENT;
1254149871Sscottl	} else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
1255136849Sscottl	    IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
1256136849Sscottl	    ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
1257136849Sscottl		protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
1258269617Sjhb	} else {
1259269617Sjhb		protmode = IEEE80211_HTINFO_OPMODE_PURE;
1260269617Sjhb	}
1261190809Sdelphij	if (protmode != ic->ic_curhtprotmode) {
1262269617Sjhb		ic->ic_curhtprotmode = protmode;
1263136849Sscottl		htinfo_notify(ic);
1264136849Sscottl	}
1265149871Sscottl}
1266136849Sscottl
1267136849Sscottl/*
1268149871Sscottl * Handle an HT station joining a BSS.
1269149871Sscottl */
1270136849Sscottlvoid
1271136849Sscottlieee80211_ht_node_join(struct ieee80211_node *ni)
1272136849Sscottl{
1273269617Sjhb	struct ieee80211com *ic = ni->ni_ic;
1274136849Sscottl
1275136849Sscottl	IEEE80211_LOCK_ASSERT(ic);
1276136849Sscottl
1277136849Sscottl	if (ni->ni_flags & IEEE80211_NODE_HT) {
1278136849Sscottl		ic->ic_ht_sta_assoc++;
1279136849Sscottl		if (ni->ni_chw == 40)
1280136849Sscottl			ic->ic_ht40_sta_assoc++;
1281232854Sscottl	}
1282149871Sscottl	htinfo_update(ic);
1283149871Sscottl}
1284149871Sscottl
1285149871Sscottl/*
1286149871Sscottl * Handle an HT station leaving a BSS.
1287149871Sscottl */
1288149871Sscottlvoid
1289149871Sscottlieee80211_ht_node_leave(struct ieee80211_node *ni)
1290149871Sscottl{
1291149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1292269617Sjhb
1293149871Sscottl	IEEE80211_LOCK_ASSERT(ic);
1294149871Sscottl
1295201758Smbr	if (ni->ni_flags & IEEE80211_NODE_HT) {
1296136849Sscottl		ic->ic_ht_sta_assoc--;
1297136849Sscottl		if (ni->ni_chw == 40)
1298136849Sscottl			ic->ic_ht40_sta_assoc--;
1299149871Sscottl	}
1300149871Sscottl	htinfo_update(ic);
1301190809Sdelphij}
1302149871Sscottl
1303136849Sscottl/*
1304136849Sscottl * Public version of htinfo_update; used for processing
1305136849Sscottl * beacon frames from overlapping bss.
1306136849Sscottl *
1307296135Sjhibbits * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
1308296135Sjhibbits * (on receipt of a beacon that advertises MIXED) or
1309149871Sscottl * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
1310149871Sscottl * from an overlapping legacy bss).  We treat MIXED with
1311149871Sscottl * a higher precedence than PROTOPT (i.e. we will not change
1312190809Sdelphij * change PROTOPT -> MIXED; only MIXED -> PROTOPT).  This
1313149871Sscottl * corresponds to how we handle things in htinfo_update.
1314149871Sscottl */
1315136849Sscottlvoid
1316149871Sscottlieee80211_htprot_update(struct ieee80211com *ic, int protmode)
1317149871Sscottl{
1318190809Sdelphij#define	OPMODE(x)	SM(x, IEEE80211_HTINFO_OPMODE)
1319149871Sscottl	IEEE80211_LOCK(ic);
1320136849Sscottl
1321136849Sscottl	/* track non-HT station presence */
1322136849Sscottl	KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
1323149871Sscottl	    ("protmode 0x%x", protmode));
1324136849Sscottl	ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR;
1325149871Sscottl	ic->ic_lastnonht = ticks;
1326190809Sdelphij
1327136849Sscottl	if (protmode != ic->ic_curhtprotmode &&
1328136849Sscottl	    (OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
1329136849Sscottl	     OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) {
1330136849Sscottl		/* push beacon update */
1331136849Sscottl		ic->ic_curhtprotmode = protmode;
1332136849Sscottl		htinfo_notify(ic);
1333136849Sscottl	}
1334136849Sscottl	IEEE80211_UNLOCK(ic);
1335136849Sscottl#undef OPMODE
1336149871Sscottl}
1337149871Sscottl
1338190809Sdelphij/*
1339149871Sscottl * Time out presence of an overlapping bss with non-HT
1340149871Sscottl * stations.  When operating in hostap mode we listen for
1341149871Sscottl * beacons from other stations and if we identify a non-HT
1342149871Sscottl * station is present we update the opmode field of the
1343149871Sscottl * HTINFO ie.  To identify when all non-HT stations are
1344136849Sscottl * gone we time out this condition.
1345136849Sscottl */
1346136849Sscottlvoid
1347136849Sscottlieee80211_ht_timeout(struct ieee80211com *ic)
1348136849Sscottl{
1349136849Sscottl	IEEE80211_LOCK_ASSERT(ic);
1350136849Sscottl
1351136849Sscottl	if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) &&
1352136849Sscottl	    time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
1353136849Sscottl#if 0
1354149871Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
1355149871Sscottl		    "%s", "time out non-HT STA present on channel");
1356149871Sscottl#endif
1357149871Sscottl		ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
1358149871Sscottl		htinfo_update(ic);
1359149871Sscottl	}
1360149871Sscottl}
1361136849Sscottl
1362149871Sscottl/* unalligned little endian access */
1363149871Sscottl#define LE_READ_2(p)					\
1364149871Sscottl	((uint16_t)					\
1365136849Sscottl	 ((((const uint8_t *)(p))[0]      ) |		\
1366149871Sscottl	  (((const uint8_t *)(p))[1] <<  8)))
1367149871Sscottl
1368149871Sscottl/*
1369149871Sscottl * Process an 802.11n HT capabilities ie.
1370149871Sscottl */
1371149871Sscottlvoid
1372136849Sscottlieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
1373136849Sscottl{
1374149871Sscottl	if (ie[0] == IEEE80211_ELEMID_VENDOR) {
1375149871Sscottl		/*
1376149871Sscottl		 * Station used Vendor OUI ie to associate;
1377149871Sscottl		 * mark the node so when we respond we'll use
1378149871Sscottl		 * the Vendor OUI's and not the standard ie's.
1379149871Sscottl		 */
1380149871Sscottl		ni->ni_flags |= IEEE80211_NODE_HTCOMPAT;
1381149871Sscottl		ie += 4;
1382149871Sscottl	} else
1383149871Sscottl		ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
1384149871Sscottl
1385149871Sscottl	ni->ni_htcap = LE_READ_2(ie +
1386149871Sscottl		__offsetof(struct ieee80211_ie_htcap, hc_cap));
1387269617Sjhb	ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
1388149871Sscottl}
1389136849Sscottl
1390136849Sscottlstatic void
1391136849Sscottlhtinfo_parse(struct ieee80211_node *ni,
1392149871Sscottl	const struct ieee80211_ie_htinfo *htinfo)
1393149871Sscottl{
1394149871Sscottl	uint16_t w;
1395149871Sscottl
1396136862Sscottl	ni->ni_htctlchan = htinfo->hi_ctrlchannel;
1397136849Sscottl	ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
1398136849Sscottl	w = LE_READ_2(&htinfo->hi_byte2);
1399136849Sscottl	ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
1400136849Sscottl	w = LE_READ_2(&htinfo->hi_byte45);
1401149871Sscottl	ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
1402149871Sscottl}
1403149871Sscottl
1404149871Sscottl/*
1405149871Sscottl * Parse an 802.11n HT info ie and save useful information
1406149871Sscottl * to the node state.  Note this does not effect any state
1407149871Sscottl * changes such as for channel width change.
1408149871Sscottl */
1409149871Sscottlvoid
1410136849Sscottlieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
1411136849Sscottl{
1412136849Sscottl	if (ie[0] == IEEE80211_ELEMID_VENDOR)
1413136849Sscottl		ie += 4;
1414136849Sscottl	htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie);
1415149871Sscottl}
1416149871Sscottl
1417136849Sscottl/*
1418136849Sscottl * Handle 11n channel switch.  Use the received HT ie's to
1419149871Sscottl * identify the right channel to use.  If we cannot locate it
1420149871Sscottl * in the channel table then fallback to legacy operation.
1421190809Sdelphij * Note that we use this information to identify the node's
1422149871Sscottl * channel only; the caller is responsible for insuring any
1423136849Sscottl * required channel change is done (e.g. in sta mode when
1424149871Sscottl * parsing the contents of a beacon frame).
1425149871Sscottl */
1426149871Sscottlstatic void
1427149871Sscottlhtinfo_update_chw(struct ieee80211_node *ni, int htflags)
1428190809Sdelphij{
1429149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1430149871Sscottl	struct ieee80211_channel *c;
1431149871Sscottl	int chanflags;
1432149871Sscottl
1433149871Sscottl	chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
1434149871Sscottl	if (chanflags != ni->ni_chan->ic_flags) {
1435149871Sscottl		/* XXX not right for ht40- */
1436190809Sdelphij		c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
1437149871Sscottl		if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
1438149871Sscottl			/*
1439149871Sscottl			 * No HT40 channel entry in our table; fall back
1440149871Sscottl			 * to HT20 operation.  This should not happen.
1441149871Sscottl			 */
1442149871Sscottl			c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
1443149871Sscottl#if 0
1444149871Sscottl			IEEE80211_NOTE(ni->ni_vap,
1445149871Sscottl			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
1446136849Sscottl			    "no HT40 channel (freq %u), falling back to HT20",
1447136849Sscottl			    ni->ni_chan->ic_freq);
1448136849Sscottl#endif
1449136849Sscottl			/* XXX stat */
1450136849Sscottl		}
1451136849Sscottl		if (c != NULL && c != ni->ni_chan) {
1452136849Sscottl			IEEE80211_NOTE(ni->ni_vap,
1453136849Sscottl			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
1454136849Sscottl			    "switch station to HT%d channel %u/0x%x",
1455136849Sscottl			    IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
1456136849Sscottl			    c->ic_freq, c->ic_flags);
1457136849Sscottl			ni->ni_chan = c;
1458136849Sscottl		}
1459136849Sscottl		/* NB: caller responsible for forcing any channel change */
1460136849Sscottl	}
1461149871Sscottl	/* update node's tx channel width */
1462136849Sscottl	ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20;
1463136849Sscottl}
1464136849Sscottl
1465136849Sscottl/*
1466136849Sscottl * Update 11n MIMO PS state according to received htcap.
1467136849Sscottl */
1468136849Sscottlstatic __inline int
1469136849Sscottlhtcap_update_mimo_ps(struct ieee80211_node *ni)
1470136849Sscottl{
1471136849Sscottl	uint16_t oflags = ni->ni_flags;
1472136849Sscottl
1473136849Sscottl	switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
1474136849Sscottl	case IEEE80211_HTCAP_SMPS_DYNAMIC:
1475136849Sscottl		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
1476136849Sscottl		ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
1477136849Sscottl		break;
1478136849Sscottl	case IEEE80211_HTCAP_SMPS_ENA:
1479136849Sscottl		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
1480136849Sscottl		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
1481136849Sscottl		break;
1482190809Sdelphij	case IEEE80211_HTCAP_SMPS_OFF:
1483136849Sscottl	default:		/* disable on rx of reserved value */
1484136849Sscottl		ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
1485136849Sscottl		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
1486136849Sscottl		break;
1487136849Sscottl	}
1488136849Sscottl	return (oflags ^ ni->ni_flags);
1489136849Sscottl}
1490136849Sscottl
1491136849Sscottl/*
1492149871Sscottl * Update short GI state according to received htcap
1493136849Sscottl * and local settings.
1494136849Sscottl */
1495136849Sscottlstatic __inline void
1496136849Sscottlhtcap_update_shortgi(struct ieee80211_node *ni)
1497136849Sscottl{
1498136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1499149871Sscottl
1500149871Sscottl	ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40);
1501190809Sdelphij	if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) &&
1502149871Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20))
1503149871Sscottl		ni->ni_flags |= IEEE80211_NODE_SGI20;
1504136849Sscottl	if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) &&
1505136849Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40))
1506136849Sscottl		ni->ni_flags |= IEEE80211_NODE_SGI40;
1507136849Sscottl}
1508149871Sscottl
1509149871Sscottl/*
1510190809Sdelphij * Parse and update HT-related state extracted from
1511149871Sscottl * the HT cap and info ie's.
1512136849Sscottl */
1513136849Sscottlvoid
1514136849Sscottlieee80211_ht_updateparams(struct ieee80211_node *ni,
1515136849Sscottl	const uint8_t *htcapie, const uint8_t *htinfoie)
1516149871Sscottl{
1517149871Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1518190809Sdelphij	const struct ieee80211_ie_htinfo *htinfo;
1519149871Sscottl	int htflags;
1520136849Sscottl
1521136849Sscottl	ieee80211_parse_htcap(ni, htcapie);
1522149871Sscottl	if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
1523149871Sscottl		htcap_update_mimo_ps(ni);
1524149871Sscottl	htcap_update_shortgi(ni);
1525149871Sscottl
1526149871Sscottl	if (htinfoie[0] == IEEE80211_ELEMID_VENDOR)
1527149871Sscottl		htinfoie += 4;
1528149871Sscottl	htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
1529149871Sscottl	htinfo_parse(ni, htinfo);
1530149871Sscottl
1531149871Sscottl	htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
1532149871Sscottl	    IEEE80211_CHAN_HT20 : 0;
1533149871Sscottl	/* NB: honor operating mode constraint */
1534149871Sscottl	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
1535149871Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) {
1536149871Sscottl		if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
1537149871Sscottl			htflags = IEEE80211_CHAN_HT40U;
1538149871Sscottl		else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
1539149871Sscottl			htflags = IEEE80211_CHAN_HT40D;
1540149871Sscottl	}
1541149871Sscottl	htinfo_update_chw(ni, htflags);
1542136849Sscottl
1543136849Sscottl	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
1544149871Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
1545149871Sscottl		ni->ni_flags |= IEEE80211_NODE_RIFS;
1546149871Sscottl	else
1547149871Sscottl		ni->ni_flags &= ~IEEE80211_NODE_RIFS;
1548149871Sscottl}
1549149871Sscottl
1550149871Sscottl/*
1551149871Sscottl * Parse and update HT-related state extracted from the HT cap ie
1552149871Sscottl * for a station joining an HT BSS.
1553136849Sscottl */
1554136849Sscottlvoid
1555136849Sscottlieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
1556136849Sscottl{
1557136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1558149871Sscottl	int htflags;
1559149871Sscottl
1560136849Sscottl	ieee80211_parse_htcap(ni, htcapie);
1561136849Sscottl	if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
1562136849Sscottl		htcap_update_mimo_ps(ni);
1563136849Sscottl	htcap_update_shortgi(ni);
1564136849Sscottl
1565136849Sscottl	/* NB: honor operating mode constraint */
1566136849Sscottl	/* XXX 40 MHz intolerant */
1567136849Sscottl	htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
1568136849Sscottl	    IEEE80211_CHAN_HT20 : 0;
1569136849Sscottl	if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
1570136849Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) {
1571149871Sscottl		if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan))
1572136849Sscottl			htflags = IEEE80211_CHAN_HT40U;
1573136849Sscottl		else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan))
1574149871Sscottl			htflags = IEEE80211_CHAN_HT40D;
1575149871Sscottl	}
1576136849Sscottl	htinfo_update_chw(ni, htflags);
1577136849Sscottl}
1578136849Sscottl
1579136849Sscottl/*
1580149871Sscottl * Install received HT rate set by parsing the HT cap ie.
1581136849Sscottl */
1582136849Sscottlint
1583136849Sscottlieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
1584136849Sscottl{
1585149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1586136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1587136849Sscottl	const struct ieee80211_ie_htcap *htcap;
1588136849Sscottl	struct ieee80211_htrateset *rs;
1589136849Sscottl	int i, maxequalmcs, maxunequalmcs;
1590136849Sscottl
1591136849Sscottl	maxequalmcs = ic->ic_txstream * 8 - 1;
1592136849Sscottl	if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
1593136849Sscottl		if (ic->ic_txstream >= 2)
1594136849Sscottl			maxunequalmcs = 38;
1595136849Sscottl		if (ic->ic_txstream >= 3)
1596149871Sscottl			maxunequalmcs = 52;
1597136849Sscottl		if (ic->ic_txstream >= 4)
1598149871Sscottl			maxunequalmcs = 76;
1599149871Sscottl	} else
1600136849Sscottl		maxunequalmcs = 0;
1601149871Sscottl
1602136849Sscottl	rs = &ni->ni_htrates;
1603136849Sscottl	memset(rs, 0, sizeof(*rs));
1604149871Sscottl	if (ie != NULL) {
1605136849Sscottl		if (ie[0] == IEEE80211_ELEMID_VENDOR)
1606136849Sscottl			ie += 4;
1607149871Sscottl		htcap = (const struct ieee80211_ie_htcap *) ie;
1608149871Sscottl		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
1609136849Sscottl			if (isclr(htcap->hc_mcsset, i))
1610136849Sscottl				continue;
1611136849Sscottl			if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
1612136849Sscottl				IEEE80211_NOTE(vap,
1613136849Sscottl				    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
1614136849Sscottl				    "WARNING, HT rate set too large; only "
1615136849Sscottl				    "using %u rates", IEEE80211_HTRATE_MAXSIZE);
1616136849Sscottl				vap->iv_stats.is_rx_rstoobig++;
1617136849Sscottl				break;
1618136849Sscottl			}
1619136849Sscottl			if (i <= 31 && i > maxequalmcs)
1620136862Sscottl				continue;
1621136849Sscottl			if (i == 32 &&
1622136849Sscottl			    (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0)
1623136849Sscottl				continue;
1624136849Sscottl			if (i > 32 && i > maxunequalmcs)
1625136849Sscottl				continue;
1626136849Sscottl			rs->rs_rates[rs->rs_nrates++] = i;
1627136849Sscottl		}
1628136849Sscottl	}
1629136849Sscottl	return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags);
1630136849Sscottl}
1631136849Sscottl
1632136849Sscottl/*
1633136849Sscottl * Mark rates in a node's HT rate set as basic according
1634136849Sscottl * to the information in the supplied HT info ie.
1635136849Sscottl */
1636136849Sscottlvoid
1637149871Sscottlieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
1638136849Sscottl{
1639136849Sscottl	const struct ieee80211_ie_htinfo *htinfo;
1640136849Sscottl	struct ieee80211_htrateset *rs;
1641136849Sscottl	int i, j;
1642136849Sscottl
1643136849Sscottl	if (ie[0] == IEEE80211_ELEMID_VENDOR)
1644149871Sscottl		ie += 4;
1645149871Sscottl	htinfo = (const struct ieee80211_ie_htinfo *) ie;
1646149871Sscottl	rs = &ni->ni_htrates;
1647149871Sscottl	if (rs->rs_nrates == 0) {
1648149871Sscottl		IEEE80211_NOTE(ni->ni_vap,
1649149871Sscottl		    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
1650149871Sscottl		    "%s", "WARNING, empty HT rate set");
1651136849Sscottl		return;
1652136849Sscottl	}
1653136849Sscottl	for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
1654136849Sscottl		if (isclr(htinfo->hi_basicmcsset, i))
1655136849Sscottl			continue;
1656149871Sscottl		for (j = 0; j < rs->rs_nrates; j++)
1657136849Sscottl			if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
1658149871Sscottl				rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
1659149871Sscottl	}
1660136849Sscottl}
1661136849Sscottl
1662136849Sscottlstatic void
1663136849Sscottlampdu_tx_setup(struct ieee80211_tx_ampdu *tap)
1664136849Sscottl{
1665136849Sscottl	callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
1666136849Sscottl	tap->txa_flags |= IEEE80211_AGGR_SETUP;
1667149871Sscottl}
1668149871Sscottl
1669136849Sscottlstatic void
1670136849Sscottlampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
1671149871Sscottl{
1672149871Sscottl	struct ieee80211_node *ni = tap->txa_ni;
1673149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1674149871Sscottl
1675149871Sscottl	KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP,
1676149871Sscottl	    ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac));
1677149871Sscottl
1678149871Sscottl	/*
1679149871Sscottl	 * Stop BA stream if setup so driver has a chance
1680136849Sscottl	 * to reclaim any resources it might have allocated.
1681149871Sscottl	 */
1682149871Sscottl	ic->ic_addba_stop(ni, tap);
1683136849Sscottl	/*
1684149871Sscottl	 * Stop any pending BAR transmit.
1685149871Sscottl	 */
1686136849Sscottl	bar_stop_timer(tap);
1687136849Sscottl
1688136849Sscottl	tap->txa_lastsample = 0;
1689136849Sscottl	tap->txa_avgpps = 0;
1690136849Sscottl	/* NB: clearing NAK means we may re-send ADDBA */
1691136849Sscottl	tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
1692136849Sscottl}
1693136849Sscottl
1694136849Sscottlstatic void
1695149871Sscottladdba_timeout(void *arg)
1696136849Sscottl{
1697136849Sscottl	struct ieee80211_tx_ampdu *tap = arg;
1698136849Sscottl
1699136849Sscottl	/* XXX ? */
1700136849Sscottl	tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
1701136849Sscottl	tap->txa_attempts++;
1702136849Sscottl}
1703136849Sscottl
1704136849Sscottlstatic void
1705136849Sscottladdba_start_timeout(struct ieee80211_tx_ampdu *tap)
1706136849Sscottl{
1707149871Sscottl	/* XXX use CALLOUT_PENDING instead? */
1708136849Sscottl	callout_reset(&tap->txa_timer, ieee80211_addba_timeout,
1709136849Sscottl	    addba_timeout, tap);
1710149871Sscottl	tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
1711149871Sscottl	tap->txa_nextrequest = ticks + ieee80211_addba_timeout;
1712190809Sdelphij}
1713149871Sscottl
1714149871Sscottlstatic void
1715136849Sscottladdba_stop_timeout(struct ieee80211_tx_ampdu *tap)
1716136849Sscottl{
1717149871Sscottl	/* XXX use CALLOUT_PENDING instead? */
1718149871Sscottl	if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
1719149871Sscottl		callout_stop(&tap->txa_timer);
1720190809Sdelphij		tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
1721149871Sscottl	}
1722149871Sscottl}
1723149871Sscottl
1724136849Sscottl/*
1725136849Sscottl * Default method for requesting A-MPDU tx aggregation.
1726136849Sscottl * We setup the specified state block and start a timer
1727136849Sscottl * to wait for an ADDBA response frame.
1728136849Sscottl */
1729136849Sscottlstatic int
1730136849Sscottlieee80211_addba_request(struct ieee80211_node *ni,
1731136849Sscottl	struct ieee80211_tx_ampdu *tap,
1732136849Sscottl	int dialogtoken, int baparamset, int batimeout)
1733136849Sscottl{
1734136849Sscottl	int bufsiz;
1735136849Sscottl
1736136849Sscottl	/* XXX locking */
1737136849Sscottl	tap->txa_token = dialogtoken;
1738149871Sscottl	tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
1739136849Sscottl	bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
1740136849Sscottl	tap->txa_wnd = (bufsiz == 0) ?
1741136849Sscottl	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
1742136849Sscottl	addba_start_timeout(tap);
1743136849Sscottl	return 1;
1744136849Sscottl}
1745136849Sscottl
1746136849Sscottl/*
1747190809Sdelphij * Default method for processing an A-MPDU tx aggregation
1748190809Sdelphij * response.  We shutdown any pending timer and update the
1749190809Sdelphij * state block according to the reply.
1750190809Sdelphij */
1751190809Sdelphijstatic int
1752190809Sdelphijieee80211_addba_response(struct ieee80211_node *ni,
1753136849Sscottl	struct ieee80211_tx_ampdu *tap,
1754149871Sscottl	int status, int baparamset, int batimeout)
1755149871Sscottl{
1756136849Sscottl	int bufsiz, tid;
1757136849Sscottl
1758136849Sscottl	/* XXX locking */
1759136849Sscottl	addba_stop_timeout(tap);
1760136849Sscottl	if (status == IEEE80211_STATUS_SUCCESS) {
1761136849Sscottl		bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
1762136849Sscottl		/* XXX override our request? */
1763136849Sscottl		tap->txa_wnd = (bufsiz == 0) ?
1764136849Sscottl		    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
1765136849Sscottl		/* XXX AC/TID */
1766136849Sscottl		tid = MS(baparamset, IEEE80211_BAPS_TID);
1767136849Sscottl		tap->txa_flags |= IEEE80211_AGGR_RUNNING;
1768136849Sscottl		tap->txa_attempts = 0;
1769136849Sscottl	} else {
1770136849Sscottl		/* mark tid so we don't try again */
1771136849Sscottl		tap->txa_flags |= IEEE80211_AGGR_NAK;
1772136849Sscottl	}
1773136849Sscottl	return 1;
1774136849Sscottl}
1775149871Sscottl
1776149871Sscottl/*
1777149871Sscottl * Default method for stopping A-MPDU tx aggregation.
1778149871Sscottl * Any timer is cleared and we drain any pending frames.
1779136849Sscottl */
1780149871Sscottlstatic void
1781149871Sscottlieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
1782149871Sscottl{
1783149871Sscottl	/* XXX locking */
1784149871Sscottl	addba_stop_timeout(tap);
1785149871Sscottl	if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
1786149871Sscottl		/* XXX clear aggregation queue */
1787149871Sscottl		tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
1788149871Sscottl	}
1789149871Sscottl	tap->txa_attempts = 0;
1790149871Sscottl}
1791149871Sscottl
1792149871Sscottl/*
1793149871Sscottl * Process a received action frame using the default aggregation
1794136849Sscottl * policy.  We intercept ADDBA-related frames and use them to
1795149871Sscottl * update our aggregation state.  All other frames are passed up
1796149871Sscottl * for processing by ieee80211_recv_action.
1797149871Sscottl */
1798149871Sscottlstatic int
1799149871Sscottlht_recv_action_ba_addba_request(struct ieee80211_node *ni,
1800149871Sscottl	const struct ieee80211_frame *wh,
1801149871Sscottl	const uint8_t *frm, const uint8_t *efrm)
1802149871Sscottl{
1803149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1804149871Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1805149871Sscottl	struct ieee80211_rx_ampdu *rap;
1806149871Sscottl	uint8_t dialogtoken;
1807149871Sscottl	uint16_t baparamset, batimeout, baseqctl;
1808149871Sscottl	uint16_t args[5];
1809149871Sscottl	int tid;
1810149871Sscottl
1811149871Sscottl	dialogtoken = frm[2];
1812149871Sscottl	baparamset = LE_READ_2(frm+3);
1813149871Sscottl	batimeout = LE_READ_2(frm+5);
1814149871Sscottl	baseqctl = LE_READ_2(frm+7);
1815149871Sscottl
1816149871Sscottl	tid = MS(baparamset, IEEE80211_BAPS_TID);
1817149871Sscottl
1818149871Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1819149871Sscottl	    "recv ADDBA request: dialogtoken %u baparamset 0x%x "
1820136849Sscottl	    "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d",
1821149871Sscottl	    dialogtoken, baparamset,
1822149871Sscottl	    tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ),
1823149871Sscottl	    batimeout,
1824149871Sscottl	    MS(baseqctl, IEEE80211_BASEQ_START),
1825149871Sscottl	    MS(baseqctl, IEEE80211_BASEQ_FRAG));
1826149871Sscottl
1827136849Sscottl	rap = &ni->ni_rx_ampdu[tid];
1828136849Sscottl
1829136849Sscottl	/* Send ADDBA response */
1830149871Sscottl	args[0] = dialogtoken;
1831136862Sscottl	/*
1832136849Sscottl	 * NB: We ack only if the sta associated with HT and
1833136849Sscottl	 * the ap is configured to do AMPDU rx (the latter
1834149871Sscottl	 * violates the 11n spec and is mostly for testing).
1835136849Sscottl	 */
1836136849Sscottl	if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
1837136849Sscottl	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) {
1838136849Sscottl		/* XXX handle ampdu_rx_start failure */
1839149871Sscottl		ic->ic_ampdu_rx_start(ni, rap,
1840149871Sscottl		    baparamset, batimeout, baseqctl);
1841136849Sscottl
1842136849Sscottl		args[1] = IEEE80211_STATUS_SUCCESS;
1843136849Sscottl	} else {
1844136849Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1845136849Sscottl		    ni, "reject ADDBA request: %s",
1846149871Sscottl		    ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
1847136849Sscottl		       "administratively disabled" :
1848149871Sscottl		       "not negotiated for station");
1849149871Sscottl		vap->iv_stats.is_addba_reject++;
1850136849Sscottl		args[1] = IEEE80211_STATUS_UNSPECIFIED;
1851136849Sscottl	}
1852136849Sscottl	/* XXX honor rap flags? */
1853136849Sscottl	args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
1854136849Sscottl		| SM(tid, IEEE80211_BAPS_TID)
1855149871Sscottl		| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
1856136849Sscottl		;
1857149871Sscottl	args[3] = 0;
1858149871Sscottl	args[4] = 0;
1859136849Sscottl	ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
1860149871Sscottl		IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
1861149871Sscottl	return 0;
1862136849Sscottl}
1863149871Sscottl
1864149871Sscottlstatic int
1865136849Sscottlht_recv_action_ba_addba_response(struct ieee80211_node *ni,
1866136849Sscottl	const struct ieee80211_frame *wh,
1867136849Sscottl	const uint8_t *frm, const uint8_t *efrm)
1868149871Sscottl{
1869149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
1870149871Sscottl	struct ieee80211vap *vap = ni->ni_vap;
1871136849Sscottl	struct ieee80211_tx_ampdu *tap;
1872136849Sscottl	uint8_t dialogtoken, policy;
1873136849Sscottl	uint16_t baparamset, batimeout, code;
1874136849Sscottl	int tid, ac, bufsiz;
1875149871Sscottl
1876149871Sscottl	dialogtoken = frm[2];
1877149871Sscottl	code = LE_READ_2(frm+3);
1878136849Sscottl	baparamset = LE_READ_2(frm+5);
1879136849Sscottl	tid = MS(baparamset, IEEE80211_BAPS_TID);
1880136849Sscottl	bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
1881149871Sscottl	policy = MS(baparamset, IEEE80211_BAPS_POLICY);
1882149871Sscottl	batimeout = LE_READ_2(frm+7);
1883149871Sscottl
1884136849Sscottl	ac = TID_TO_WME_AC(tid);
1885136849Sscottl	tap = &ni->ni_tx_ampdu[ac];
1886136849Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
1887149871Sscottl		IEEE80211_DISCARD_MAC(vap,
1888149871Sscottl		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1889136849Sscottl		    ni->ni_macaddr, "ADDBA response",
1890136849Sscottl		    "no pending ADDBA, tid %d dialogtoken %u "
1891136849Sscottl		    "code %d", tid, dialogtoken, code);
1892149871Sscottl		vap->iv_stats.is_addba_norequest++;
1893149871Sscottl		return 0;
1894136849Sscottl	}
1895136849Sscottl	if (dialogtoken != tap->txa_token) {
1896136849Sscottl		IEEE80211_DISCARD_MAC(vap,
1897136849Sscottl		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1898136849Sscottl		    ni->ni_macaddr, "ADDBA response",
1899136849Sscottl		    "dialogtoken mismatch: waiting for %d, "
1900136849Sscottl		    "received %d, tid %d code %d",
1901136849Sscottl		    tap->txa_token, dialogtoken, tid, code);
1902136849Sscottl		vap->iv_stats.is_addba_badtoken++;
1903136849Sscottl		return 0;
1904136849Sscottl	}
1905136849Sscottl	/* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
1906136849Sscottl	if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) {
1907136849Sscottl		IEEE80211_DISCARD_MAC(vap,
1908136849Sscottl		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1909149871Sscottl		    ni->ni_macaddr, "ADDBA response",
1910136849Sscottl		    "policy mismatch: expecting %s, "
1911149871Sscottl		    "received %s, tid %d code %d",
1912136849Sscottl		    tap->txa_flags & IEEE80211_AGGR_IMMEDIATE,
1913136849Sscottl		    policy, tid, code);
1914149871Sscottl		vap->iv_stats.is_addba_badpolicy++;
1915136849Sscottl		return 0;
1916136849Sscottl	}
1917149871Sscottl#if 0
1918149871Sscottl	/* XXX we take MIN in ieee80211_addba_response */
1919136849Sscottl	if (bufsiz > IEEE80211_AGGR_BAWMAX) {
1920136849Sscottl		IEEE80211_DISCARD_MAC(vap,
1921136849Sscottl		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1922136849Sscottl		    ni->ni_macaddr, "ADDBA response",
1923149871Sscottl		    "BA window too large: max %d, "
1924149871Sscottl		    "received %d, tid %d code %d",
1925149871Sscottl		    bufsiz, IEEE80211_AGGR_BAWMAX, tid, code);
1926149871Sscottl		vap->iv_stats.is_addba_badbawinsize++;
1927149871Sscottl		return 0;
1928149871Sscottl	}
1929149871Sscottl#endif
1930149871Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1931149871Sscottl	    "recv ADDBA response: dialogtoken %u code %d "
1932149871Sscottl	    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
1933149871Sscottl	    dialogtoken, code, baparamset, tid, bufsiz,
1934149871Sscottl	    batimeout);
1935149871Sscottl	ic->ic_addba_response(ni, tap, code, baparamset, batimeout);
1936149871Sscottl	return 0;
1937149871Sscottl}
1938136849Sscottl
1939136849Sscottlstatic int
1940136849Sscottlht_recv_action_ba_delba(struct ieee80211_node *ni,
1941136849Sscottl	const struct ieee80211_frame *wh,
1942136849Sscottl	const uint8_t *frm, const uint8_t *efrm)
1943136849Sscottl{
1944136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
1945136849Sscottl	struct ieee80211_rx_ampdu *rap;
1946136849Sscottl	struct ieee80211_tx_ampdu *tap;
1947136849Sscottl	uint16_t baparamset, code;
1948136849Sscottl	int tid, ac;
1949136849Sscottl
1950136849Sscottl	baparamset = LE_READ_2(frm+2);
1951136849Sscottl	code = LE_READ_2(frm+4);
1952136849Sscottl
1953136849Sscottl	tid = MS(baparamset, IEEE80211_DELBAPS_TID);
1954136849Sscottl
1955136849Sscottl	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1956136849Sscottl	    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
1957136849Sscottl	    "code %d", baparamset, tid,
1958136849Sscottl	    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
1959136849Sscottl
1960149871Sscottl	if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
1961149871Sscottl		ac = TID_TO_WME_AC(tid);
1962136849Sscottl		tap = &ni->ni_tx_ampdu[ac];
1963136849Sscottl		ic->ic_addba_stop(ni, tap);
1964267368Sdelphij	} else {
1965136849Sscottl		rap = &ni->ni_rx_ampdu[tid];
1966136849Sscottl		ic->ic_ampdu_rx_stop(ni, rap);
1967136849Sscottl	}
1968136849Sscottl	return 0;
1969136849Sscottl}
1970136849Sscottl
1971136849Sscottlstatic int
1972136849Sscottlht_recv_action_ht_txchwidth(struct ieee80211_node *ni,
1973136849Sscottl	const struct ieee80211_frame *wh,
1974136849Sscottl	const uint8_t *frm, const uint8_t *efrm)
1975136849Sscottl{
1976136849Sscottl	int chw;
1977136849Sscottl
1978149871Sscottl	chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ? 40 : 20;
1979136849Sscottl
1980136849Sscottl	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1981136849Sscottl	    "%s: HT txchwidth, width %d%s",
1982136849Sscottl	    __func__, chw, ni->ni_chw != chw ? "*" : "");
1983136849Sscottl	if (chw != ni->ni_chw) {
1984269617Sjhb		ni->ni_chw = chw;
1985136849Sscottl		/* XXX notify on change */
1986136849Sscottl	}
1987136849Sscottl	return 0;
1988136849Sscottl}
1989136849Sscottl
1990136849Sscottlstatic int
1991136849Sscottlht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni,
1992136849Sscottl	const struct ieee80211_frame *wh,
1993295790Sjhibbits	const uint8_t *frm, const uint8_t *efrm)
1994149871Sscottl{
1995136849Sscottl	const struct ieee80211_action_ht_mimopowersave *mps =
1996136849Sscottl	    (const struct ieee80211_action_ht_mimopowersave *) frm;
1997136849Sscottl
1998136849Sscottl	/* XXX check iv_htcaps */
1999269617Sjhb	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA)
2000269617Sjhb		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
2001190809Sdelphij	else
2002149871Sscottl		ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
2003136849Sscottl	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE)
2004136849Sscottl		ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
2005136849Sscottl	else
2006136849Sscottl		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
2007136849Sscottl	/* XXX notify on change */
2008149871Sscottl	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2009149871Sscottl	    "%s: HT MIMO PS (%s%s)", __func__,
2010149871Sscottl	    (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ?  "on" : "off",
2011149871Sscottl	    (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ?  "+rts" : ""
2012136849Sscottl	);
2013136849Sscottl	return 0;
2014149871Sscottl}
2015149871Sscottl
2016149871Sscottl/*
2017136849Sscottl * Transmit processing.
2018136849Sscottl */
2019136849Sscottl
2020136849Sscottl/*
2021136849Sscottl * Check if A-MPDU should be requested/enabled for a stream.
2022149871Sscottl * We require a traffic rate above a per-AC threshold and we
2023149871Sscottl * also handle backoff from previous failed attempts.
2024136849Sscottl *
2025136849Sscottl * Drivers may override this method to bring in information
2026136849Sscottl * such as link state conditions in making the decision.
2027136849Sscottl */
2028136849Sscottlstatic int
2029136849Sscottlieee80211_ampdu_enable(struct ieee80211_node *ni,
2030136849Sscottl	struct ieee80211_tx_ampdu *tap)
2031190809Sdelphij{
2032269617Sjhb	struct ieee80211vap *vap = ni->ni_vap;
2033269617Sjhb
2034190809Sdelphij	if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
2035136849Sscottl		return 0;
2036136849Sscottl	/* XXX check rssi? */
2037136849Sscottl	if (tap->txa_attempts >= ieee80211_addba_maxtries &&
2038136849Sscottl	    ticks < tap->txa_nextrequest) {
2039269617Sjhb		/*
2040190809Sdelphij		 * Don't retry too often; txa_nextrequest is set
2041149871Sscottl		 * to the minimum interval we'll retry after
2042136849Sscottl		 * ieee80211_addba_maxtries failed attempts are made.
2043269617Sjhb		 */
2044136849Sscottl		return 0;
2045136849Sscottl	}
2046136849Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
2047136849Sscottl	    "enable AMPDU on %s, avgpps %d pkts %d",
2048136849Sscottl	    ieee80211_wme_acnames[tap->txa_ac], tap->txa_avgpps, tap->txa_pkts);
2049149871Sscottl	return 1;
2050149871Sscottl}
2051149871Sscottl
2052136849Sscottl/*
2053136849Sscottl * Request A-MPDU tx aggregation.  Setup local state and
2054269617Sjhb * issue an ADDBA request.  BA use will only happen after
2055136849Sscottl * the other end replies with ADDBA response.
2056136849Sscottl */
2057136849Sscottlint
2058269617Sjhbieee80211_ampdu_request(struct ieee80211_node *ni,
2059136849Sscottl	struct ieee80211_tx_ampdu *tap)
2060136849Sscottl{
2061136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
2062136849Sscottl	uint16_t args[5];
2063136849Sscottl	int tid, dialogtoken;
2064136849Sscottl	static int tokens = 0;	/* XXX */
2065136849Sscottl
2066136849Sscottl	/* XXX locking */
2067136849Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
2068190863Sdelphij		/* do deferred setup of state */
2069190810Sdelphij		ampdu_tx_setup(tap);
2070190810Sdelphij	}
2071139044Snjl	/* XXX hack for not doing proper locking */
2072136849Sscottl	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
2073136849Sscottl
2074136849Sscottl	dialogtoken = (tokens+1) % 63;		/* XXX */
2075136849Sscottl	tid = WME_AC_TO_TID(tap->txa_ac);
2076136849Sscottl	tap->txa_start = ni->ni_txseqs[tid];
2077136849Sscottl
2078149871Sscottl	args[0] = dialogtoken;
2079136849Sscottl	args[1] = 0;	/* NB: status code not used */
2080136849Sscottl	args[2]	= IEEE80211_BAPS_POLICY_IMMEDIATE
2081136849Sscottl		| SM(tid, IEEE80211_BAPS_TID)
2082149871Sscottl		| SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
2083136849Sscottl		;
2084136849Sscottl	args[3] = 0;	/* batimeout */
2085136849Sscottl	/* NB: do first so there's no race against reply */
2086136849Sscottl	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) {
2087136849Sscottl		/* unable to setup state, don't make request */
2088136849Sscottl		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2089136849Sscottl		    ni, "%s: could not setup BA stream for AC %d",
2090136849Sscottl		    __func__, tap->txa_ac);
2091269617Sjhb		/* defer next try so we don't slam the driver with requests */
2092269617Sjhb		tap->txa_attempts = ieee80211_addba_maxtries;
2093269617Sjhb		/* NB: check in case driver wants to override */
2094269617Sjhb		if (tap->txa_nextrequest <= ticks)
2095269617Sjhb			tap->txa_nextrequest = ticks + ieee80211_addba_backoff;
2096136849Sscottl		return 0;
2097136849Sscottl	}
2098136849Sscottl	tokens = dialogtoken;			/* allocate token */
2099136849Sscottl	/* NB: after calling ic_addba_request so driver can set txa_start */
2100136849Sscottl	args[4] = SM(tap->txa_start, IEEE80211_BASEQ_START)
2101136849Sscottl		| SM(0, IEEE80211_BASEQ_FRAG)
2102136849Sscottl		;
2103136849Sscottl	return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
2104136849Sscottl		IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
2105269617Sjhb}
2106269617Sjhb
2107269617Sjhb/*
2108269617Sjhb * Terminate an AMPDU tx stream.  State is reclaimed
2109269617Sjhb * and the peer notified with a DelBA Action frame.
2110269617Sjhb */
2111269617Sjhbvoid
2112269617Sjhbieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
2113269617Sjhb	int reason)
2114269617Sjhb{
2115269617Sjhb	struct ieee80211com *ic = ni->ni_ic;
2116269617Sjhb	struct ieee80211vap *vap = ni->ni_vap;
2117269617Sjhb	uint16_t args[4];
2118136849Sscottl
2119149871Sscottl	/* XXX locking */
2120149871Sscottl	tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
2121136849Sscottl	if (IEEE80211_AMPDU_RUNNING(tap)) {
2122136849Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2123136849Sscottl		    ni, "%s: stop BA stream for AC %d (reason %d)",
2124136849Sscottl		    __func__, tap->txa_ac, reason);
2125136849Sscottl		vap->iv_stats.is_ampdu_stop++;
2126136849Sscottl
2127136849Sscottl		ic->ic_addba_stop(ni, tap);
2128136849Sscottl		args[0] = WME_AC_TO_TID(tap->txa_ac);
2129136849Sscottl		args[1] = IEEE80211_DELBAPS_INIT;
2130136849Sscottl		args[2] = reason;			/* XXX reason code */
2131136849Sscottl		ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
2132136849Sscottl			IEEE80211_ACTION_BA_DELBA, args);
2133136849Sscottl	} else {
2134136849Sscottl		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2135136849Sscottl		    ni, "%s: BA stream for AC %d not running (reason %d)",
2136136849Sscottl		    __func__, tap->txa_ac, reason);
2137136849Sscottl		vap->iv_stats.is_ampdu_stop_failed++;
2138136849Sscottl	}
2139136849Sscottl}
2140136849Sscottl
2141136849Sscottlstatic void
2142136849Sscottlbar_timeout(void *arg)
2143136849Sscottl{
2144136849Sscottl	struct ieee80211_tx_ampdu *tap = arg;
2145136849Sscottl	struct ieee80211_node *ni = tap->txa_ni;
2146136849Sscottl
2147136849Sscottl	KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0,
2148136849Sscottl	    ("bar/addba collision, flags 0x%x", tap->txa_flags));
2149136849Sscottl
2150136849Sscottl	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2151136849Sscottl	    ni, "%s: tid %u flags 0x%x attempts %d", __func__,
2152136849Sscottl	    tap->txa_ac, tap->txa_flags, tap->txa_attempts);
2153136849Sscottl
2154136849Sscottl	/* guard against race with bar_tx_complete */
2155136849Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
2156136849Sscottl		return;
2157136849Sscottl	/* XXX ? */
2158149871Sscottl	if (tap->txa_attempts >= ieee80211_bar_maxtries)
2159136849Sscottl		ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT);
2160136849Sscottl	else
2161136849Sscottl		ieee80211_send_bar(ni, tap, tap->txa_seqpending);
2162136849Sscottl}
2163136849Sscottl
2164136849Sscottlstatic void
2165149871Sscottlbar_start_timer(struct ieee80211_tx_ampdu *tap)
2166149871Sscottl{
2167149871Sscottl	callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap);
2168136849Sscottl}
2169149871Sscottl
2170269617Sjhbstatic void
2171149871Sscottlbar_stop_timer(struct ieee80211_tx_ampdu *tap)
2172269617Sjhb{
2173149871Sscottl	callout_stop(&tap->txa_timer);
2174149871Sscottl}
2175149871Sscottl
2176149871Sscottlstatic void
2177136849Sscottlbar_tx_complete(struct ieee80211_node *ni, void *arg, int status)
2178136849Sscottl{
2179136849Sscottl	struct ieee80211_tx_ampdu *tap = arg;
2180136849Sscottl
2181136849Sscottl	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2182136849Sscottl	    ni, "%s: tid %u flags 0x%x pending %d status %d",
2183136849Sscottl	    __func__, tap->txa_ac, tap->txa_flags,
2184136849Sscottl	    callout_pending(&tap->txa_timer), status);
2185136849Sscottl
2186136849Sscottl	/* XXX locking */
2187149871Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) &&
2188149871Sscottl	    callout_pending(&tap->txa_timer)) {
2189136849Sscottl		struct ieee80211com *ic = ni->ni_ic;
2190149871Sscottl
2191149871Sscottl		if (status)		/* ACK'd */
2192149871Sscottl			bar_stop_timer(tap);
2193149871Sscottl		ic->ic_bar_response(ni, tap, status);
2194149871Sscottl		/* NB: just let timer expire so we pace requests */
2195149871Sscottl	}
2196149871Sscottl}
2197149871Sscottl
2198136849Sscottlstatic void
2199136849Sscottlieee80211_bar_response(struct ieee80211_node *ni,
2200136849Sscottl	struct ieee80211_tx_ampdu *tap, int status)
2201136849Sscottl{
2202136849Sscottl
2203149871Sscottl	if (status != 0) {		/* got ACK */
2204149871Sscottl		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2205136849Sscottl		    ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
2206149871Sscottl		    tap->txa_start,
2207136849Sscottl		    IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1),
2208136849Sscottl		    tap->txa_qframes, tap->txa_seqpending,
2209136849Sscottl		    WME_AC_TO_TID(tap->txa_ac));
2210136849Sscottl
2211136849Sscottl		/* NB: timer already stopped in bar_tx_complete */
2212136849Sscottl		tap->txa_start = tap->txa_seqpending;
2213136849Sscottl		tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
2214136849Sscottl	}
2215149871Sscottl}
2216149871Sscottl
2217149871Sscottl/*
2218136849Sscottl * Transmit a BAR frame to the specified node.  The
2219149871Sscottl * BAR contents are drawn from the supplied aggregation
2220136849Sscottl * state associated with the node.
2221136849Sscottl *
2222136849Sscottl * NB: we only handle immediate ACK w/ compressed bitmap.
2223136849Sscottl */
2224149871Sscottlint
2225149871Sscottlieee80211_send_bar(struct ieee80211_node *ni,
2226136849Sscottl	struct ieee80211_tx_ampdu *tap, ieee80211_seq seq)
2227136849Sscottl{
2228269617Sjhb#define	senderr(_x, _v)	do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
2229136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
2230136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
2231136849Sscottl	struct ieee80211_frame_bar *bar;
2232136849Sscottl	struct mbuf *m;
2233136849Sscottl	uint16_t barctl, barseqctl;
2234136849Sscottl	uint8_t *frm;
2235136849Sscottl	int tid, ret;
2236136849Sscottl
2237136849Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) {
2238136849Sscottl		/* no ADDBA response, should not happen */
2239136849Sscottl		/* XXX stat+msg */
2240136849Sscottl		return EINVAL;
2241136849Sscottl	}
2242136849Sscottl	/* XXX locking */
2243149871Sscottl	bar_stop_timer(tap);
2244136849Sscottl
2245136849Sscottl	ieee80211_ref_node(ni);
2246269617Sjhb
2247149878Sscottl	m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar));
2248149871Sscottl	if (m == NULL)
2249136849Sscottl		senderr(ENOMEM, is_tx_nobuf);
2250149871Sscottl
2251149871Sscottl	if (!ieee80211_add_callback(m, bar_tx_complete, tap)) {
2252149871Sscottl		m_freem(m);
2253149871Sscottl		senderr(ENOMEM, is_tx_nobuf);	/* XXX */
2254149871Sscottl		/* NOTREACHED */
2255136849Sscottl	}
2256149871Sscottl
2257149871Sscottl	bar = mtod(m, struct ieee80211_frame_bar *);
2258149871Sscottl	bar->i_fc[0] = IEEE80211_FC0_VERSION_0 |
2259149871Sscottl		IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
2260149871Sscottl	bar->i_fc[1] = 0;
2261149871Sscottl	IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr);
2262149871Sscottl	IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr);
2263149871Sscottl
2264149871Sscottl	tid = WME_AC_TO_TID(tap->txa_ac);
2265149871Sscottl	barctl 	= (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
2266149871Sscottl			0 : IEEE80211_BAR_NOACK)
2267136849Sscottl		| IEEE80211_BAR_COMP
2268149871Sscottl		| SM(tid, IEEE80211_BAR_TID)
2269149871Sscottl		;
2270136849Sscottl	barseqctl = SM(seq, IEEE80211_BAR_SEQ_START);
2271149871Sscottl	/* NB: known to have proper alignment */
2272149871Sscottl	bar->i_ctl = htole16(barctl);
2273149871Sscottl	bar->i_seq = htole16(barseqctl);
2274149871Sscottl	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar);
2275136849Sscottl
2276149871Sscottl	M_WME_SETAC(m, WME_AC_VO);
2277149871Sscottl
2278149871Sscottl	IEEE80211_NODE_STAT(ni, tx_mgmt);	/* XXX tx_ctl? */
2279149871Sscottl
2280136849Sscottl	/* XXX locking */
2281149871Sscottl	/* init/bump attempts counter */
2282149871Sscottl	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
2283149871Sscottl		tap->txa_attempts = 1;
2284136849Sscottl	else
2285149871Sscottl		tap->txa_attempts++;
2286149871Sscottl	tap->txa_seqpending = seq;
2287149871Sscottl	tap->txa_flags |= IEEE80211_AGGR_BARPEND;
2288149871Sscottl
2289149871Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
2290136849Sscottl	    ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)",
2291149871Sscottl	    tid, barctl, seq, tap->txa_attempts);
2292149871Sscottl
2293149871Sscottl	ret = ic->ic_raw_xmit(ni, m, NULL);
2294149871Sscottl	if (ret != 0) {
2295149871Sscottl		/* xmit failed, clear state flag */
2296149871Sscottl		tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
2297149871Sscottl		goto bad;
2298149871Sscottl	}
2299149871Sscottl	/* XXX hack against tx complete happening before timer is started */
2300149871Sscottl	if (tap->txa_flags & IEEE80211_AGGR_BARPEND)
2301149871Sscottl		bar_start_timer(tap);
2302136849Sscottl	return 0;
2303149871Sscottlbad:
2304149871Sscottl	ieee80211_free_node(ni);
2305149871Sscottl	return ret;
2306149871Sscottl#undef senderr
2307149871Sscottl}
2308149871Sscottl
2309136849Sscottlstatic int
2310149871Sscottlht_action_output(struct ieee80211_node *ni, struct mbuf *m)
2311227912Smarius{
2312149871Sscottl	struct ieee80211_bpf_params params;
2313149871Sscottl
2314136849Sscottl	memset(&params, 0, sizeof(params));
2315149871Sscottl	params.ibp_pri = WME_AC_VO;
2316149871Sscottl	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
2317149871Sscottl	/* NB: we know all frames are unicast */
2318136849Sscottl	params.ibp_try0 = ni->ni_txparms->maxretry;
2319149871Sscottl	params.ibp_power = ni->ni_txpower;
2320149871Sscottl	return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
2321149871Sscottl	     &params);
2322149871Sscottl}
2323149871Sscottl
2324149871Sscottl#define	ADDSHORT(frm, v) do {			\
2325136849Sscottl	frm[0] = (v) & 0xff;			\
2326149871Sscottl	frm[1] = (v) >> 8;			\
2327149871Sscottl	frm += 2;				\
2328149871Sscottl} while (0)
2329136849Sscottl
2330149871Sscottl/*
2331149871Sscottl * Send an action management frame.  The arguments are stuff
2332149871Sscottl * into a frame without inspection; the caller is assumed to
2333149871Sscottl * prepare them carefully (e.g. based on the aggregation state).
2334149871Sscottl */
2335149871Sscottlstatic int
2336209341Smavht_send_action_ba_addba(struct ieee80211_node *ni,
2337209341Smav	int category, int action, void *arg0)
2338209341Smav{
2339209341Smav	struct ieee80211vap *vap = ni->ni_vap;
2340149871Sscottl	struct ieee80211com *ic = ni->ni_ic;
2341149871Sscottl	uint16_t *args = arg0;
2342149871Sscottl	struct mbuf *m;
2343149871Sscottl	uint8_t *frm;
2344136849Sscottl
2345149871Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2346149871Sscottl	    "send ADDBA %s: dialogtoken %d status %d "
2347149871Sscottl	    "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
2348149871Sscottl	    (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ?
2349149871Sscottl		"request" : "response",
2350136849Sscottl	    args[0], args[1], args[2], MS(args[2], IEEE80211_BAPS_TID),
2351136849Sscottl	    args[3], args[4]);
2352136849Sscottl
2353136849Sscottl	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2354136849Sscottl	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2355136849Sscottl	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2356136849Sscottl	ieee80211_ref_node(ni);
2357136849Sscottl
2358136849Sscottl	m = ieee80211_getmgtframe(&frm,
2359136849Sscottl	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2360136849Sscottl	    sizeof(uint16_t)	/* action+category */
2361136849Sscottl	    /* XXX may action payload */
2362136849Sscottl	    + sizeof(struct ieee80211_action_ba_addbaresponse)
2363136849Sscottl	);
2364136849Sscottl	if (m != NULL) {
2365136849Sscottl		*frm++ = category;
2366136849Sscottl		*frm++ = action;
2367136849Sscottl		*frm++ = args[0];		/* dialog token */
2368136849Sscottl		if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE)
2369136849Sscottl			ADDSHORT(frm, args[1]);	/* status code */
2370136849Sscottl		ADDSHORT(frm, args[2]);		/* baparamset */
2371136849Sscottl		ADDSHORT(frm, args[3]);		/* batimeout */
2372136849Sscottl		if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST)
2373136849Sscottl			ADDSHORT(frm, args[4]);	/* baseqctl */
2374136849Sscottl		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2375136849Sscottl		return ht_action_output(ni, m);
2376149871Sscottl	} else {
2377136849Sscottl		vap->iv_stats.is_tx_nobuf++;
2378136849Sscottl		ieee80211_free_node(ni);
2379136849Sscottl		return ENOMEM;
2380136849Sscottl	}
2381136849Sscottl}
2382136849Sscottl
2383136849Sscottlstatic int
2384136849Sscottlht_send_action_ba_delba(struct ieee80211_node *ni,
2385136849Sscottl	int category, int action, void *arg0)
2386136849Sscottl{
2387136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
2388136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
2389136849Sscottl	uint16_t *args = arg0;
2390136849Sscottl	struct mbuf *m;
2391136849Sscottl	uint16_t baparamset;
2392136849Sscottl	uint8_t *frm;
2393136849Sscottl
2394136849Sscottl	baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
2395136849Sscottl		   | args[1]
2396136849Sscottl		   ;
2397136849Sscottl	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2398136849Sscottl	    "send DELBA action: tid %d, initiator %d reason %d",
2399269617Sjhb	    args[0], args[1], args[2]);
2400136849Sscottl
2401136849Sscottl	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2402136849Sscottl	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2403136849Sscottl	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2404136849Sscottl	ieee80211_ref_node(ni);
2405136849Sscottl
2406269617Sjhb	m = ieee80211_getmgtframe(&frm,
2407136849Sscottl	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2408136849Sscottl	    sizeof(uint16_t)	/* action+category */
2409269617Sjhb	    /* XXX may action payload */
2410269617Sjhb	    + sizeof(struct ieee80211_action_ba_addbaresponse)
2411136849Sscottl	);
2412149871Sscottl	if (m != NULL) {
2413136849Sscottl		*frm++ = category;
2414136849Sscottl		*frm++ = action;
2415136849Sscottl		ADDSHORT(frm, baparamset);
2416136849Sscottl		ADDSHORT(frm, args[2]);		/* reason code */
2417136849Sscottl		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2418136849Sscottl		return ht_action_output(ni, m);
2419136849Sscottl	} else {
2420269617Sjhb		vap->iv_stats.is_tx_nobuf++;
2421269617Sjhb		ieee80211_free_node(ni);
2422136849Sscottl		return ENOMEM;
2423149871Sscottl	}
2424149871Sscottl}
2425149871Sscottl
2426149871Sscottlstatic int
2427149871Sscottlht_send_action_ht_txchwidth(struct ieee80211_node *ni,
2428149871Sscottl	int category, int action, void *arg0)
2429149871Sscottl{
2430269617Sjhb	struct ieee80211vap *vap = ni->ni_vap;
2431269617Sjhb	struct ieee80211com *ic = ni->ni_ic;
2432149871Sscottl	struct mbuf *m;
2433149871Sscottl	uint8_t *frm;
2434149871Sscottl
2435269617Sjhb	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2436149871Sscottl	    "send HT txchwidth: width %d",
2437149871Sscottl	    IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20);
2438149871Sscottl
2439149871Sscottl	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2440149871Sscottl	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2441149871Sscottl	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2442149871Sscottl	ieee80211_ref_node(ni);
2443149871Sscottl
2444149871Sscottl	m = ieee80211_getmgtframe(&frm,
2445149871Sscottl	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2446149871Sscottl	    sizeof(uint16_t)	/* action+category */
2447149871Sscottl	    /* XXX may action payload */
2448136849Sscottl	    + sizeof(struct ieee80211_action_ba_addbaresponse)
2449269617Sjhb	);
2450149871Sscottl	if (m != NULL) {
2451136849Sscottl		*frm++ = category;
2452269617Sjhb		*frm++ = action;
2453136849Sscottl		*frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
2454269617Sjhb			IEEE80211_A_HT_TXCHWIDTH_2040 :
2455136849Sscottl			IEEE80211_A_HT_TXCHWIDTH_20;
2456269617Sjhb		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2457136849Sscottl		return ht_action_output(ni, m);
2458149871Sscottl	} else {
2459149871Sscottl		vap->iv_stats.is_tx_nobuf++;
2460136849Sscottl		ieee80211_free_node(ni);
2461136849Sscottl		return ENOMEM;
2462167086Sjhb	}
2463136849Sscottl}
2464149871Sscottl#undef ADDSHORT
2465172836Sjulian
2466269617Sjhb/*
2467136849Sscottl * Construct the MCS bit mask for inclusion in an HT capabilities
2468136849Sscottl * information element.
2469136849Sscottl */
2470136849Sscottlstatic void
2471136849Sscottlieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm)
2472136849Sscottl{
2473136849Sscottl	int i;
2474136849Sscottl	uint8_t txparams;
2475136849Sscottl
2476136849Sscottl	KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4),
2477149871Sscottl	    ("ic_rxstream %d out of range", ic->ic_rxstream));
2478136849Sscottl	KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4),
2479136849Sscottl	    ("ic_txstream %d out of range", ic->ic_txstream));
2480136849Sscottl
2481136849Sscottl	for (i = 0; i < ic->ic_rxstream * 8; i++)
2482136849Sscottl		setbit(frm, i);
2483136849Sscottl	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
2484136849Sscottl	    (ic->ic_htcaps & IEEE80211_HTC_RXMCS32))
2485269617Sjhb		setbit(frm, 32);
2486136849Sscottl	if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) {
2487136849Sscottl		if (ic->ic_rxstream >= 2) {
2488136849Sscottl			for (i = 33; i <= 38; i++)
2489136849Sscottl				setbit(frm, i);
2490136849Sscottl		}
2491136849Sscottl		if (ic->ic_rxstream >= 3) {
2492136849Sscottl			for (i = 39; i <= 52; i++)
2493136849Sscottl				setbit(frm, i);
2494136849Sscottl		}
2495149871Sscottl		if (ic->ic_txstream >= 4) {
2496149871Sscottl			for (i = 53; i <= 76; i++)
2497149871Sscottl				setbit(frm, i);
2498149871Sscottl		}
2499149871Sscottl	}
2500136849Sscottl
2501269617Sjhb	if (ic->ic_rxstream != ic->ic_txstream) {
2502136849Sscottl		txparams = 0x1;			/* TX MCS set defined */
2503136849Sscottl		txparams |= 0x2;		/* TX RX MCS not equal */
2504149871Sscottl		txparams |= (ic->ic_txstream - 1) << 2;	/* num TX streams */
2505136849Sscottl		if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL)
2506249849Smav			txparams |= 0x16;	/* TX unequal modulation sup */
2507249849Smav	} else
2508136849Sscottl		txparams = 0;
2509149871Sscottl	frm[12] = txparams;
2510149871Sscottl}
2511149871Sscottl
2512136849Sscottl/*
2513149871Sscottl * Add body of an HTCAP information element.
2514136849Sscottl */
2515136849Sscottlstatic uint8_t *
2516136849Sscottlieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
2517149871Sscottl{
2518136849Sscottl#define	ADDSHORT(frm, v) do {			\
2519267368Sdelphij	frm[0] = (v) & 0xff;			\
2520267368Sdelphij	frm[1] = (v) >> 8;			\
2521267368Sdelphij	frm += 2;				\
2522267368Sdelphij} while (0)
2523267368Sdelphij	struct ieee80211vap *vap = ni->ni_vap;
2524267368Sdelphij	uint16_t caps, extcaps;
2525267368Sdelphij	int rxmax, density;
2526267368Sdelphij
2527149871Sscottl	/* HT capabilities */
2528149871Sscottl	caps = vap->iv_htcaps & 0xffff;
2529149871Sscottl	/*
2530136849Sscottl	 * Note channel width depends on whether we are operating as
2531136849Sscottl	 * a sta or not.  When operating as a sta we are generating
2532136849Sscottl	 * a request based on our desired configuration.  Otherwise
2533136849Sscottl	 * we are operational and the channel attributes identify
2534136849Sscottl	 * how we've been setup (which might be different if a fixed
2535136849Sscottl	 * channel is specified).
2536136849Sscottl	 */
2537136849Sscottl	if (vap->iv_opmode == IEEE80211_M_STA) {
2538136849Sscottl		/* override 20/40 use based on config */
2539136849Sscottl		if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)
2540149871Sscottl			caps |= IEEE80211_HTCAP_CHWIDTH40;
2541149871Sscottl		else
2542136849Sscottl			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
2543136849Sscottl		/* use advertised setting (XXX locally constraint) */
2544136849Sscottl		rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
2545136849Sscottl		density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
2546136849Sscottl	} else {
2547136849Sscottl		/* override 20/40 use based on current channel */
2548136849Sscottl		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
2549136849Sscottl			caps |= IEEE80211_HTCAP_CHWIDTH40;
2550149871Sscottl		else
2551149871Sscottl			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
2552136849Sscottl		rxmax = vap->iv_ampdu_rxmax;
2553136849Sscottl		density = vap->iv_ampdu_density;
2554136849Sscottl	}
2555136849Sscottl	/* adjust short GI based on channel and config */
2556136849Sscottl	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0)
2557136849Sscottl		caps &= ~IEEE80211_HTCAP_SHORTGI20;
2558136849Sscottl	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 ||
2559136849Sscottl	    (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
2560136849Sscottl		caps &= ~IEEE80211_HTCAP_SHORTGI40;
2561136849Sscottl	ADDSHORT(frm, caps);
2562136849Sscottl
2563149871Sscottl	/* HT parameters */
2564136849Sscottl	*frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
2565136849Sscottl	     | SM(density, IEEE80211_HTCAP_MPDUDENSITY)
2566136849Sscottl	     ;
2567149871Sscottl	frm++;
2568149871Sscottl
2569149871Sscottl	/* pre-zero remainder of ie */
2570136849Sscottl	memset(frm, 0, sizeof(struct ieee80211_ie_htcap) -
2571136849Sscottl		__offsetof(struct ieee80211_ie_htcap, hc_mcsset));
2572136849Sscottl
2573136849Sscottl	/* supported MCS set */
2574136849Sscottl	/*
2575136849Sscottl	 * XXX: For sta mode the rate set should be restricted based
2576136849Sscottl	 * on the AP's capabilities, but ni_htrates isn't setup when
2577149871Sscottl	 * we're called to form an AssocReq frame so for now we're
2578149871Sscottl	 * restricted to the device capabilities.
2579149871Sscottl	 */
2580149871Sscottl	ieee80211_set_mcsset(ni->ni_ic, frm);
2581136849Sscottl
2582136849Sscottl	frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) -
2583136849Sscottl		__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
2584190809Sdelphij
2585136849Sscottl	/* HT extended capabilities */
2586149871Sscottl	extcaps = vap->iv_htextcaps & 0xffff;
2587136849Sscottl
2588149871Sscottl	ADDSHORT(frm, extcaps);
2589149871Sscottl
2590149871Sscottl	frm += sizeof(struct ieee80211_ie_htcap) -
2591136849Sscottl		__offsetof(struct ieee80211_ie_htcap, hc_txbf);
2592149871Sscottl
2593136849Sscottl	return frm;
2594136849Sscottl#undef ADDSHORT
2595149871Sscottl}
2596149871Sscottl
2597149871Sscottl/*
2598136849Sscottl * Add 802.11n HT capabilities information element
2599149871Sscottl */
2600136849Sscottluint8_t *
2601136849Sscottlieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
2602136849Sscottl{
2603136849Sscottl	frm[0] = IEEE80211_ELEMID_HTCAP;
2604136849Sscottl	frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
2605136849Sscottl	return ieee80211_add_htcap_body(frm + 2, ni);
2606136849Sscottl}
2607136849Sscottl
2608136849Sscottl/*
2609136849Sscottl * Add Broadcom OUI wrapped standard HTCAP ie; this is
2610136849Sscottl * used for compatibility w/ pre-draft implementations.
2611136849Sscottl */
2612136849Sscottluint8_t *
2613136849Sscottlieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni)
2614136849Sscottl{
2615136849Sscottl	frm[0] = IEEE80211_ELEMID_VENDOR;
2616136849Sscottl	frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2;
2617269617Sjhb	frm[2] = (BCM_OUI >> 0) & 0xff;
2618269617Sjhb	frm[3] = (BCM_OUI >> 8) & 0xff;
2619269617Sjhb	frm[4] = (BCM_OUI >> 16) & 0xff;
2620269617Sjhb	frm[5] = BCM_OUI_HTCAP;
2621269617Sjhb	return ieee80211_add_htcap_body(frm + 6, ni);
2622136849Sscottl}
2623136849Sscottl
2624136849Sscottl/*
2625149871Sscottl * Construct the MCS bit mask of basic rates
2626149871Sscottl * for inclusion in an HT information element.
2627149871Sscottl */
2628149871Sscottlstatic void
2629149871Sscottlieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
2630149871Sscottl{
2631149871Sscottl	int i;
2632149871Sscottl
2633149871Sscottl	for (i = 0; i < rs->rs_nrates; i++) {
2634149871Sscottl		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
2635149871Sscottl		if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
2636149871Sscottl		    r < IEEE80211_HTRATE_MAXSIZE) {
2637149871Sscottl			/* NB: this assumes a particular implementation */
2638149871Sscottl			setbit(frm, r);
2639149871Sscottl		}
2640246713Skib	}
2641149871Sscottl}
2642149871Sscottl
2643149871Sscottl/*
2644149871Sscottl * Update the HTINFO ie for a beacon frame.
2645246713Skib */
2646246713Skibvoid
2647246713Skibieee80211_ht_update_beacon(struct ieee80211vap *vap,
2648246713Skib	struct ieee80211_beacon_offsets *bo)
2649246713Skib{
2650246713Skib#define	PROTMODE	(IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
2651246713Skib	const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan;
2652246713Skib	struct ieee80211com *ic = vap->iv_ic;
2653246713Skib	struct ieee80211_ie_htinfo *ht =
2654246713Skib	   (struct ieee80211_ie_htinfo *) bo->bo_htinfo;
2655246713Skib
2656246713Skib	/* XXX only update on channel change */
2657246713Skib	ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
2658246713Skib	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
2659246713Skib		ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM;
2660246713Skib	else
2661246713Skib		ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
2662149871Sscottl	if (IEEE80211_IS_CHAN_HT40U(bsschan))
2663149871Sscottl		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
2664269617Sjhb	else if (IEEE80211_IS_CHAN_HT40D(bsschan))
2665149871Sscottl		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
2666149871Sscottl	else
2667149871Sscottl		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
2668149871Sscottl	if (IEEE80211_IS_CHAN_HT40(bsschan))
2669149871Sscottl		ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
2670149871Sscottl
2671136849Sscottl	/* protection mode */
2672136849Sscottl	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
2673136849Sscottl
2674149871Sscottl	/* XXX propagate to vendor ie's */
2675149871Sscottl#undef PROTMODE
2676149871Sscottl}
2677149871Sscottl
2678149871Sscottl/*
2679136849Sscottl * Add body of an HTINFO information element.
2680149871Sscottl *
2681136849Sscottl * NB: We don't use struct ieee80211_ie_htinfo because we can
2682136849Sscottl * be called to fillin both a standard ie and a compat ie that
2683136849Sscottl * has a vendor OUI at the front.
2684136849Sscottl */
2685136849Sscottlstatic uint8_t *
2686136849Sscottlieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
2687136849Sscottl{
2688136849Sscottl	struct ieee80211vap *vap = ni->ni_vap;
2689136849Sscottl	struct ieee80211com *ic = ni->ni_ic;
2690136849Sscottl
2691136849Sscottl	/* pre-zero remainder of ie */
2692136849Sscottl	memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
2693136849Sscottl
2694136849Sscottl	/* primary/control channel center */
2695136849Sscottl	*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
2696136849Sscottl
2697149871Sscottl	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
2698149871Sscottl		frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM;
2699149871Sscottl	else
2700149871Sscottl		frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
2701149871Sscottl	if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
2702149871Sscottl		frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
2703136849Sscottl	else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
2704149871Sscottl		frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
2705149871Sscottl	else
2706149871Sscottl		frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
2707149871Sscottl	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
2708149871Sscottl		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
2709136849Sscottl
2710149871Sscottl	frm[1] = ic->ic_curhtprotmode;
2711149871Sscottl
2712190809Sdelphij	frm += 5;
2713190809Sdelphij
2714190809Sdelphij	/* basic MCS set */
2715190809Sdelphij	ieee80211_set_basic_htrates(frm, &ni->ni_htrates);
2716190809Sdelphij	frm += sizeof(struct ieee80211_ie_htinfo) -
2717190809Sdelphij		__offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
2718190809Sdelphij	return frm;
2719190809Sdelphij}
2720190809Sdelphij
2721190809Sdelphij/*
2722190809Sdelphij * Add 802.11n HT information information element.
2723190809Sdelphij */
2724190809Sdelphijuint8_t *
2725149871Sscottlieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni)
2726190809Sdelphij{
2727190809Sdelphij	frm[0] = IEEE80211_ELEMID_HTINFO;
2728190809Sdelphij	frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2;
2729190809Sdelphij	return ieee80211_add_htinfo_body(frm + 2, ni);
2730190809Sdelphij}
2731149871Sscottl
2732149871Sscottl/*
2733136849Sscottl * Add Broadcom OUI wrapped standard HTINFO ie; this is
2734136849Sscottl * used for compatibility w/ pre-draft implementations.
2735190809Sdelphij */
2736190809Sdelphijuint8_t *
2737190809Sdelphijieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
2738190809Sdelphij{
2739190809Sdelphij	frm[0] = IEEE80211_ELEMID_VENDOR;
2740190809Sdelphij	frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2;
2741190809Sdelphij	frm[2] = (BCM_OUI >> 0) & 0xff;
2742190809Sdelphij	frm[3] = (BCM_OUI >> 8) & 0xff;
2743190809Sdelphij	frm[4] = (BCM_OUI >> 16) & 0xff;
2744190809Sdelphij	frm[5] = BCM_OUI_HTINFO;
2745190809Sdelphij	return ieee80211_add_htinfo_body(frm + 6, ni);
2746190809Sdelphij}
2747190809Sdelphij