xref: /freebsd-src/sys/net80211/ieee80211_ht.c (revision 2c8b0d6205f6f98855773e3a82640b50abb2f2f6)
168e8e04eSSam Leffler /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
4b032f27cSSam Leffler  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
568e8e04eSSam Leffler  * All rights reserved.
668e8e04eSSam Leffler  *
768e8e04eSSam Leffler  * Redistribution and use in source and binary forms, with or without
868e8e04eSSam Leffler  * modification, are permitted provided that the following conditions
968e8e04eSSam Leffler  * are met:
1068e8e04eSSam Leffler  * 1. Redistributions of source code must retain the above copyright
1168e8e04eSSam Leffler  *    notice, this list of conditions and the following disclaimer.
1268e8e04eSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
1368e8e04eSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
1468e8e04eSSam Leffler  *    documentation and/or other materials provided with the distribution.
1568e8e04eSSam Leffler  *
1668e8e04eSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1768e8e04eSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1868e8e04eSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1968e8e04eSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2068e8e04eSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2168e8e04eSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2268e8e04eSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2368e8e04eSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2468e8e04eSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2568e8e04eSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2668e8e04eSSam Leffler  */
2768e8e04eSSam Leffler 
2868e8e04eSSam Leffler /*
2968e8e04eSSam Leffler  * IEEE 802.11n protocol support.
3068e8e04eSSam Leffler  */
3168e8e04eSSam Leffler 
3268e8e04eSSam Leffler #include "opt_inet.h"
33b032f27cSSam Leffler #include "opt_wlan.h"
3468e8e04eSSam Leffler 
3568e8e04eSSam Leffler #include <sys/param.h>
3668e8e04eSSam Leffler #include <sys/kernel.h>
378ec07310SGleb Smirnoff #include <sys/malloc.h>
3868e8e04eSSam Leffler #include <sys/systm.h>
3968e8e04eSSam Leffler #include <sys/endian.h>
4068e8e04eSSam Leffler 
4168e8e04eSSam Leffler #include <sys/socket.h>
4268e8e04eSSam Leffler 
4368e8e04eSSam Leffler #include <net/if.h>
4476039bc8SGleb Smirnoff #include <net/if_var.h>
4568e8e04eSSam Leffler #include <net/if_media.h>
4668e8e04eSSam Leffler #include <net/ethernet.h>
4768e8e04eSSam Leffler 
4868e8e04eSSam Leffler #include <net80211/ieee80211_var.h>
4976340123SSam Leffler #include <net80211/ieee80211_action.h>
50b032f27cSSam Leffler #include <net80211/ieee80211_input.h>
5168e8e04eSSam Leffler 
52f136f45fSBernhard Schmidt const struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = {
53b032f27cSSam Leffler 	{  13,  14,   27,   30 },	/* MCS 0 */
54b032f27cSSam Leffler 	{  26,  29,   54,   60 },	/* MCS 1 */
55b032f27cSSam Leffler 	{  39,  43,   81,   90 },	/* MCS 2 */
56b032f27cSSam Leffler 	{  52,  58,  108,  120 },	/* MCS 3 */
57b032f27cSSam Leffler 	{  78,  87,  162,  180 },	/* MCS 4 */
58b032f27cSSam Leffler 	{ 104, 116,  216,  240 },	/* MCS 5 */
59b032f27cSSam Leffler 	{ 117, 130,  243,  270 },	/* MCS 6 */
60b032f27cSSam Leffler 	{ 130, 144,  270,  300 },	/* MCS 7 */
61b032f27cSSam Leffler 	{  26,  29,   54,   60 },	/* MCS 8 */
62b032f27cSSam Leffler 	{  52,  58,  108,  120 },	/* MCS 9 */
63b032f27cSSam Leffler 	{  78,  87,  162,  180 },	/* MCS 10 */
64b032f27cSSam Leffler 	{ 104, 116,  216,  240 },	/* MCS 11 */
65b032f27cSSam Leffler 	{ 156, 173,  324,  360 },	/* MCS 12 */
66b032f27cSSam Leffler 	{ 208, 231,  432,  480 },	/* MCS 13 */
67b032f27cSSam Leffler 	{ 234, 260,  486,  540 },	/* MCS 14 */
68f136f45fSBernhard Schmidt 	{ 260, 289,  540,  600 },	/* MCS 15 */
69f136f45fSBernhard Schmidt 	{  39,  43,   81,   90 },	/* MCS 16 */
70f136f45fSBernhard Schmidt 	{  78,  87,  162,  180 },	/* MCS 17 */
71f136f45fSBernhard Schmidt 	{ 117, 130,  243,  270 },	/* MCS 18 */
72f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 19 */
73f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 20 */
74f136f45fSBernhard Schmidt 	{ 312, 347,  648,  720 },	/* MCS 21 */
75f136f45fSBernhard Schmidt 	{ 351, 390,  729,  810 },	/* MCS 22 */
76f136f45fSBernhard Schmidt 	{ 390, 433,  810,  900 },	/* MCS 23 */
77f136f45fSBernhard Schmidt 	{  52,  58,  108,  120 },	/* MCS 24 */
78f136f45fSBernhard Schmidt 	{ 104, 116,  216,  240 },	/* MCS 25 */
79f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 26 */
80f136f45fSBernhard Schmidt 	{ 208, 231,  432,  480 },	/* MCS 27 */
81f136f45fSBernhard Schmidt 	{ 312, 347,  648,  720 },	/* MCS 28 */
82f136f45fSBernhard Schmidt 	{ 416, 462,  864,  960 },	/* MCS 29 */
83f136f45fSBernhard Schmidt 	{ 468, 520,  972, 1080 },	/* MCS 30 */
84f136f45fSBernhard Schmidt 	{ 520, 578, 1080, 1200 },	/* MCS 31 */
85f136f45fSBernhard Schmidt 	{   0,   0,   12,   13 },	/* MCS 32 */
86f136f45fSBernhard Schmidt 	{  78,  87,  162,  180 },	/* MCS 33 */
87f136f45fSBernhard Schmidt 	{ 104, 116,  216,  240 },	/* MCS 34 */
88f136f45fSBernhard Schmidt 	{ 130, 144,  270,  300 },	/* MCS 35 */
89f136f45fSBernhard Schmidt 	{ 117, 130,  243,  270 },	/* MCS 36 */
90f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 37 */
91f136f45fSBernhard Schmidt 	{ 195, 217,  405,  450 },	/* MCS 38 */
92f136f45fSBernhard Schmidt 	{ 104, 116,  216,  240 },	/* MCS 39 */
93f136f45fSBernhard Schmidt 	{ 130, 144,  270,  300 },	/* MCS 40 */
94f136f45fSBernhard Schmidt 	{ 130, 144,  270,  300 },	/* MCS 41 */
95f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 42 */
96f136f45fSBernhard Schmidt 	{ 182, 202,  378,  420 },	/* MCS 43 */
97f136f45fSBernhard Schmidt 	{ 182, 202,  378,  420 },	/* MCS 44 */
98f136f45fSBernhard Schmidt 	{ 208, 231,  432,  480 },	/* MCS 45 */
99f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 46 */
100f136f45fSBernhard Schmidt 	{ 195, 217,  405,  450 },	/* MCS 47 */
101f136f45fSBernhard Schmidt 	{ 195, 217,  405,  450 },	/* MCS 48 */
102f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 49 */
103f136f45fSBernhard Schmidt 	{ 273, 303,  567,  630 },	/* MCS 50 */
104f136f45fSBernhard Schmidt 	{ 273, 303,  567,  630 },	/* MCS 51 */
105f136f45fSBernhard Schmidt 	{ 312, 347,  648,  720 },	/* MCS 52 */
106f136f45fSBernhard Schmidt 	{ 130, 144,  270,  300 },	/* MCS 53 */
107f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 54 */
108f136f45fSBernhard Schmidt 	{ 182, 202,  378,  420 },	/* MCS 55 */
109f136f45fSBernhard Schmidt 	{ 156, 173,  324,  360 },	/* MCS 56 */
110f136f45fSBernhard Schmidt 	{ 182, 202,  378,  420 },	/* MCS 57 */
111f136f45fSBernhard Schmidt 	{ 208, 231,  432,  480 },	/* MCS 58 */
112f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 59 */
113f136f45fSBernhard Schmidt 	{ 208, 231,  432,  480 },	/* MCS 60 */
114f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 61 */
115f136f45fSBernhard Schmidt 	{ 260, 289,  540,  600 },	/* MCS 62 */
116f136f45fSBernhard Schmidt 	{ 260, 289,  540,  600 },	/* MCS 63 */
117f136f45fSBernhard Schmidt 	{ 286, 318,  594,  660 },	/* MCS 64 */
118f136f45fSBernhard Schmidt 	{ 195, 217,  405,  450 },	/* MCS 65 */
119f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 66 */
120f136f45fSBernhard Schmidt 	{ 273, 303,  567,  630 },	/* MCS 67 */
121f136f45fSBernhard Schmidt 	{ 234, 260,  486,  540 },	/* MCS 68 */
122f136f45fSBernhard Schmidt 	{ 273, 303,  567,  630 },	/* MCS 69 */
123f136f45fSBernhard Schmidt 	{ 312, 347,  648,  720 },	/* MCS 70 */
124f136f45fSBernhard Schmidt 	{ 351, 390,  729,  810 },	/* MCS 71 */
125f136f45fSBernhard Schmidt 	{ 312, 347,  648,  720 },	/* MCS 72 */
126f136f45fSBernhard Schmidt 	{ 351, 390,  729,  810 },	/* MCS 73 */
127f136f45fSBernhard Schmidt 	{ 390, 433,  810,  900 },	/* MCS 74 */
128f136f45fSBernhard Schmidt 	{ 390, 433,  810,  900 },	/* MCS 75 */
129f136f45fSBernhard Schmidt 	{ 429, 477,  891,  990 },	/* MCS 76 */
13068e8e04eSSam Leffler };
13168e8e04eSSam Leffler 
132a77b10b3SSam Leffler static	int ieee80211_ampdu_age = -1;	/* threshold for ampdu reorder q (ms) */
13308f5e6bbSPawel Biernacki SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age,
13408f5e6bbSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
135a77b10b3SSam Leffler     &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
136a77b10b3SSam Leffler     "AMPDU max reorder age (ms)");
137a77b10b3SSam Leffler 
138a77b10b3SSam Leffler static	int ieee80211_recv_bar_ena = 1;
139a77b10b3SSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
140a77b10b3SSam Leffler 	    0, "BAR frame processing (ena/dis)");
141a77b10b3SSam Leffler 
142a77b10b3SSam Leffler static	int ieee80211_addba_timeout = -1;/* timeout for ADDBA response */
14308f5e6bbSPawel Biernacki SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout,
14408f5e6bbSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
145a77b10b3SSam Leffler     &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
146a77b10b3SSam Leffler     "ADDBA request timeout (ms)");
147a77b10b3SSam Leffler static	int ieee80211_addba_backoff = -1;/* backoff after max ADDBA requests */
14808f5e6bbSPawel Biernacki SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff,
14908f5e6bbSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
150a77b10b3SSam Leffler     &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
151a77b10b3SSam Leffler     "ADDBA request backoff (ms)");
152a77b10b3SSam Leffler static	int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */
153f0188618SHans Petter Selasky SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
154a77b10b3SSam Leffler 	&ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
155a77b10b3SSam Leffler 
156a77b10b3SSam Leffler static	int ieee80211_bar_timeout = -1;	/* timeout waiting for BAR response */
157a77b10b3SSam Leffler static	int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */
1581b6167d2SSam Leffler 
15976340123SSam Leffler static	ieee80211_recv_action_func ht_recv_action_ba_addba_request;
16076340123SSam Leffler static	ieee80211_recv_action_func ht_recv_action_ba_addba_response;
16176340123SSam Leffler static	ieee80211_recv_action_func ht_recv_action_ba_delba;
16276340123SSam Leffler static	ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave;
16376340123SSam Leffler static	ieee80211_recv_action_func ht_recv_action_ht_txchwidth;
16476340123SSam Leffler 
16576340123SSam Leffler static	ieee80211_send_action_func ht_send_action_ba_addba;
16676340123SSam Leffler static	ieee80211_send_action_func ht_send_action_ba_delba;
16776340123SSam Leffler static	ieee80211_send_action_func ht_send_action_ht_txchwidth;
16876340123SSam Leffler 
16976340123SSam Leffler static void
17076340123SSam Leffler ieee80211_ht_init(void)
17176340123SSam Leffler {
172b032f27cSSam Leffler 	/*
173b032f27cSSam Leffler 	 * Setup HT parameters that depends on the clock frequency.
174b032f27cSSam Leffler 	 */
175b032f27cSSam Leffler 	ieee80211_ampdu_age = msecs_to_ticks(500);
176b032f27cSSam Leffler 	ieee80211_addba_timeout = msecs_to_ticks(250);
177b032f27cSSam Leffler 	ieee80211_addba_backoff = msecs_to_ticks(10*1000);
178cc71a422SSam Leffler 	ieee80211_bar_timeout = msecs_to_ticks(250);
17976340123SSam Leffler 	/*
18076340123SSam Leffler 	 * Register action frame handlers.
18176340123SSam Leffler 	 */
18276340123SSam Leffler 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
18376340123SSam Leffler 	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request);
18476340123SSam Leffler 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
18576340123SSam Leffler 	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response);
18676340123SSam Leffler 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA,
18776340123SSam Leffler 	    IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba);
18876340123SSam Leffler 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT,
18976340123SSam Leffler 	    IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave);
19076340123SSam Leffler 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT,
19176340123SSam Leffler 	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth);
19276340123SSam Leffler 
19376340123SSam Leffler 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
19476340123SSam Leffler 	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba);
19576340123SSam Leffler 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
19676340123SSam Leffler 	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba);
19776340123SSam Leffler 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA,
19876340123SSam Leffler 	    IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba);
19976340123SSam Leffler 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT,
20076340123SSam Leffler 	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth);
201b032f27cSSam Leffler }
20276340123SSam Leffler SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL);
20368e8e04eSSam Leffler 
204b032f27cSSam Leffler static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
205b032f27cSSam Leffler 	struct ieee80211_tx_ampdu *tap);
20668e8e04eSSam Leffler static int ieee80211_addba_request(struct ieee80211_node *ni,
20768e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap,
20868e8e04eSSam Leffler 	int dialogtoken, int baparamset, int batimeout);
20968e8e04eSSam Leffler static int ieee80211_addba_response(struct ieee80211_node *ni,
21068e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap,
21168e8e04eSSam Leffler 	int code, int baparamset, int batimeout);
21268e8e04eSSam Leffler static void ieee80211_addba_stop(struct ieee80211_node *ni,
21368e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap);
214a834836dSAdrian Chadd static void null_addba_response_timeout(struct ieee80211_node *ni,
215a834836dSAdrian Chadd 	struct ieee80211_tx_ampdu *tap);
216a834836dSAdrian Chadd 
217cc71a422SSam Leffler static void ieee80211_bar_response(struct ieee80211_node *ni,
218cc71a422SSam Leffler 	struct ieee80211_tx_ampdu *tap, int status);
219cc71a422SSam Leffler static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
220cc71a422SSam Leffler static void bar_stop_timer(struct ieee80211_tx_ampdu *tap);
22151b5aba2SSam Leffler static int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *,
22251b5aba2SSam Leffler 	int baparamset, int batimeout, int baseqctl);
22351b5aba2SSam Leffler static void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *);
22468e8e04eSSam Leffler 
22568e8e04eSSam Leffler void
22668e8e04eSSam Leffler ieee80211_ht_attach(struct ieee80211com *ic)
22768e8e04eSSam Leffler {
22868e8e04eSSam Leffler 	/* setup default aggregation policy */
22976340123SSam Leffler 	ic->ic_recv_action = ieee80211_recv_action;
23068e8e04eSSam Leffler 	ic->ic_send_action = ieee80211_send_action;
231b032f27cSSam Leffler 	ic->ic_ampdu_enable = ieee80211_ampdu_enable;
23268e8e04eSSam Leffler 	ic->ic_addba_request = ieee80211_addba_request;
23368e8e04eSSam Leffler 	ic->ic_addba_response = ieee80211_addba_response;
234a834836dSAdrian Chadd 	ic->ic_addba_response_timeout = null_addba_response_timeout;
23568e8e04eSSam Leffler 	ic->ic_addba_stop = ieee80211_addba_stop;
236cc71a422SSam Leffler 	ic->ic_bar_response = ieee80211_bar_response;
23751b5aba2SSam Leffler 	ic->ic_ampdu_rx_start = ampdu_rx_start;
23851b5aba2SSam Leffler 	ic->ic_ampdu_rx_stop = ampdu_rx_stop;
23968e8e04eSSam Leffler 
2401b6167d2SSam Leffler 	ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
2411b6167d2SSam Leffler 	ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
242b032f27cSSam Leffler }
2431b6167d2SSam Leffler 
244b032f27cSSam Leffler void
245b032f27cSSam Leffler ieee80211_ht_detach(struct ieee80211com *ic)
246b032f27cSSam Leffler {
247b032f27cSSam Leffler }
2481b6167d2SSam Leffler 
249b032f27cSSam Leffler void
250b032f27cSSam Leffler ieee80211_ht_vattach(struct ieee80211vap *vap)
251b032f27cSSam Leffler {
252b032f27cSSam Leffler 
253b032f27cSSam Leffler 	/* driver can override defaults */
254b032f27cSSam Leffler 	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
255b032f27cSSam Leffler 	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
256b032f27cSSam Leffler 	vap->iv_ampdu_limit = vap->iv_ampdu_rxmax;
257b032f27cSSam Leffler 	vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU;
258b032f27cSSam Leffler 	/* tx aggregation traffic thresholds */
259b032f27cSSam Leffler 	vap->iv_ampdu_mintraffic[WME_AC_BK] = 128;
260b032f27cSSam Leffler 	vap->iv_ampdu_mintraffic[WME_AC_BE] = 64;
261b032f27cSSam Leffler 	vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
262b032f27cSSam Leffler 	vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
263b032f27cSSam Leffler 
264f1481c8dSAdrian Chadd 	vap->iv_htprotmode = IEEE80211_PROT_RTSCTS;
265f1481c8dSAdrian Chadd 	vap->iv_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
266f1481c8dSAdrian Chadd 
267b032f27cSSam Leffler 	if (vap->iv_htcaps & IEEE80211_HTC_HT) {
26868e8e04eSSam Leffler 		/*
2691b6167d2SSam Leffler 		 * Device is HT capable; enable all HT-related
2701b6167d2SSam Leffler 		 * facilities by default.
27168e8e04eSSam Leffler 		 * XXX these choices may be too aggressive.
27268e8e04eSSam Leffler 		 */
2732bfc8a91SSam Leffler 		vap->iv_flags_ht |= IEEE80211_FHT_HT
2742bfc8a91SSam Leffler 				 |  IEEE80211_FHT_HTCOMPAT
27568e8e04eSSam Leffler 				 ;
276b032f27cSSam Leffler 		if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20)
2772bfc8a91SSam Leffler 			vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20;
2781b6167d2SSam Leffler 		/* XXX infer from channel list? */
279b032f27cSSam Leffler 		if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2802bfc8a91SSam Leffler 			vap->iv_flags_ht |= IEEE80211_FHT_USEHT40;
281b032f27cSSam Leffler 			if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40)
2822bfc8a91SSam Leffler 				vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40;
28368e8e04eSSam Leffler 		}
28444f7a6edSSam Leffler 		/* enable RIFS if capable */
28544f7a6edSSam Leffler 		if (vap->iv_htcaps & IEEE80211_HTC_RIFS)
2862bfc8a91SSam Leffler 			vap->iv_flags_ht |= IEEE80211_FHT_RIFS;
28744f7a6edSSam Leffler 
28868e8e04eSSam Leffler 		/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
2892bfc8a91SSam Leffler 		vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX;
290b032f27cSSam Leffler 		if (vap->iv_htcaps & IEEE80211_HTC_AMPDU)
2912bfc8a91SSam Leffler 			vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX;
2922bfc8a91SSam Leffler 		vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX;
293b032f27cSSam Leffler 		if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
2942bfc8a91SSam Leffler 			vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX;
2955706199dSAdrian Chadd 
2965706199dSAdrian Chadd 		if (vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC)
2975706199dSAdrian Chadd 			vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX;
2985706199dSAdrian Chadd 		if (vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC)
2995706199dSAdrian Chadd 			vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX;
300c5bba9daSAndriy Voskoboinyk 
301c5bba9daSAndriy Voskoboinyk 		if (vap->iv_htcaps & IEEE80211_HTCAP_LDPC)
302c5bba9daSAndriy Voskoboinyk 			vap->iv_flags_ht |= IEEE80211_FHT_LDPC_RX;
303c5bba9daSAndriy Voskoboinyk 		if (vap->iv_htcaps & IEEE80211_HTC_TXLDPC)
304c5bba9daSAndriy Voskoboinyk 			vap->iv_flags_ht |= IEEE80211_FHT_LDPC_TX;
30568e8e04eSSam Leffler 	}
306b032f27cSSam Leffler 	/* NB: disable default legacy WDS, too many issues right now */
307b032f27cSSam Leffler 	if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
3082bfc8a91SSam Leffler 		vap->iv_flags_ht &= ~IEEE80211_FHT_HT;
30968e8e04eSSam Leffler }
31068e8e04eSSam Leffler 
31168e8e04eSSam Leffler void
312b032f27cSSam Leffler ieee80211_ht_vdetach(struct ieee80211vap *vap)
31368e8e04eSSam Leffler {
31468e8e04eSSam Leffler }
31568e8e04eSSam Leffler 
316bffb67e3SBernhard Schmidt static int
317fcd9500fSBernhard Schmidt ht_getrate(struct ieee80211com *ic, int index, enum ieee80211_phymode mode,
318fcd9500fSBernhard Schmidt     int ratetype)
31968e8e04eSSam Leffler {
320bffb67e3SBernhard Schmidt 	int mword, rate;
32168e8e04eSSam Leffler 
322bffb67e3SBernhard Schmidt 	mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode);
32368e8e04eSSam Leffler 	if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
324bffb67e3SBernhard Schmidt 		return (0);
3250917631fSRui Paulo 	switch (ratetype) {
3260917631fSRui Paulo 	case 0:
327bffb67e3SBernhard Schmidt 		rate = ieee80211_htrates[index].ht20_rate_800ns;
3280917631fSRui Paulo 		break;
3290917631fSRui Paulo 	case 1:
330bffb67e3SBernhard Schmidt 		rate = ieee80211_htrates[index].ht20_rate_400ns;
3310917631fSRui Paulo 		break;
3320917631fSRui Paulo 	case 2:
333bffb67e3SBernhard Schmidt 		rate = ieee80211_htrates[index].ht40_rate_800ns;
3340917631fSRui Paulo 		break;
3350917631fSRui Paulo 	default:
336bffb67e3SBernhard Schmidt 		rate = ieee80211_htrates[index].ht40_rate_400ns;
3370917631fSRui Paulo 		break;
3380917631fSRui Paulo 	}
339bffb67e3SBernhard Schmidt 	return (rate);
34068e8e04eSSam Leffler }
341bffb67e3SBernhard Schmidt 
342bffb67e3SBernhard Schmidt static struct printranges {
343bffb67e3SBernhard Schmidt 	int	minmcs;
344bffb67e3SBernhard Schmidt 	int	maxmcs;
345bffb67e3SBernhard Schmidt 	int	txstream;
346bffb67e3SBernhard Schmidt 	int	ratetype;
347bffb67e3SBernhard Schmidt 	int	htcapflags;
348bffb67e3SBernhard Schmidt } ranges[] = {
349bffb67e3SBernhard Schmidt 	{  0,  7, 1, 0, 0 },
350bffb67e3SBernhard Schmidt 	{  8, 15, 2, 0, 0 },
351bffb67e3SBernhard Schmidt 	{ 16, 23, 3, 0, 0 },
352bffb67e3SBernhard Schmidt 	{ 24, 31, 4, 0, 0 },
353bffb67e3SBernhard Schmidt 	{ 32,  0, 1, 2, IEEE80211_HTC_TXMCS32 },
354bffb67e3SBernhard Schmidt 	{ 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL },
355bffb67e3SBernhard Schmidt 	{ 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL },
356bffb67e3SBernhard Schmidt 	{ 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL },
357bffb67e3SBernhard Schmidt 	{  0,  0, 0, 0, 0 },
358bffb67e3SBernhard Schmidt };
359bffb67e3SBernhard Schmidt 
360bffb67e3SBernhard Schmidt static void
361fcd9500fSBernhard Schmidt ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype)
362bffb67e3SBernhard Schmidt {
363bffb67e3SBernhard Schmidt 	int minrate, maxrate;
364bffb67e3SBernhard Schmidt 	struct printranges *range;
365bffb67e3SBernhard Schmidt 
366bffb67e3SBernhard Schmidt 	for (range = ranges; range->txstream != 0; range++) {
367bffb67e3SBernhard Schmidt 		if (ic->ic_txstream < range->txstream)
368bffb67e3SBernhard Schmidt 			continue;
369bffb67e3SBernhard Schmidt 		if (range->htcapflags &&
370bffb67e3SBernhard Schmidt 		    (ic->ic_htcaps & range->htcapflags) == 0)
371bffb67e3SBernhard Schmidt 			continue;
372bffb67e3SBernhard Schmidt 		if (ratetype < range->ratetype)
373bffb67e3SBernhard Schmidt 			continue;
374bffb67e3SBernhard Schmidt 		minrate = ht_getrate(ic, range->minmcs, mode, ratetype);
375bffb67e3SBernhard Schmidt 		maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype);
376bffb67e3SBernhard Schmidt 		if (range->maxmcs) {
377c8f5794eSGleb Smirnoff 			ic_printf(ic, "MCS %d-%d: %d%sMbps - %d%sMbps\n",
378bffb67e3SBernhard Schmidt 			    range->minmcs, range->maxmcs,
379bffb67e3SBernhard Schmidt 			    minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""),
380bffb67e3SBernhard Schmidt 			    maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : ""));
381bffb67e3SBernhard Schmidt 		} else {
382c8f5794eSGleb Smirnoff 			ic_printf(ic, "MCS %d: %d%sMbps\n", range->minmcs,
383bffb67e3SBernhard Schmidt 			    minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""));
384bffb67e3SBernhard Schmidt 		}
385bffb67e3SBernhard Schmidt 	}
38668e8e04eSSam Leffler }
38768e8e04eSSam Leffler 
3880917631fSRui Paulo static void
389fcd9500fSBernhard Schmidt ht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
3900917631fSRui Paulo {
3910917631fSRui Paulo 	const char *modestr = ieee80211_phymode_name[mode];
3920917631fSRui Paulo 
393c8f5794eSGleb Smirnoff 	ic_printf(ic, "%s MCS 20MHz\n", modestr);
394bffb67e3SBernhard Schmidt 	ht_rateprint(ic, mode, 0);
395bffb67e3SBernhard Schmidt 	if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) {
396c8f5794eSGleb Smirnoff 		ic_printf(ic, "%s MCS 20MHz SGI\n", modestr);
397bffb67e3SBernhard Schmidt 		ht_rateprint(ic, mode, 1);
398bffb67e3SBernhard Schmidt 	}
399bffb67e3SBernhard Schmidt 	if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
400c8f5794eSGleb Smirnoff 		ic_printf(ic, "%s MCS 40MHz:\n", modestr);
401bffb67e3SBernhard Schmidt 		ht_rateprint(ic, mode, 2);
402bffb67e3SBernhard Schmidt 	}
403bffb67e3SBernhard Schmidt 	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
404bffb67e3SBernhard Schmidt 	    (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) {
405c8f5794eSGleb Smirnoff 		ic_printf(ic, "%s MCS 40MHz SGI:\n", modestr);
406bffb67e3SBernhard Schmidt 		ht_rateprint(ic, mode, 3);
407bffb67e3SBernhard Schmidt 	}
4080917631fSRui Paulo }
4090917631fSRui Paulo 
41068e8e04eSSam Leffler void
41168e8e04eSSam Leffler ieee80211_ht_announce(struct ieee80211com *ic)
41268e8e04eSSam Leffler {
413bffb67e3SBernhard Schmidt 
414bffb67e3SBernhard Schmidt 	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
415bffb67e3SBernhard Schmidt 	    isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
416c8f5794eSGleb Smirnoff 		ic_printf(ic, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream);
41768e8e04eSSam Leffler 	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
418bffb67e3SBernhard Schmidt 		ht_announce(ic, IEEE80211_MODE_11NA);
41968e8e04eSSam Leffler 	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
420bffb67e3SBernhard Schmidt 		ht_announce(ic, IEEE80211_MODE_11NG);
42168e8e04eSSam Leffler }
42268e8e04eSSam Leffler 
423dfabbaa0SAndriy Voskoboinyk void
424dfabbaa0SAndriy Voskoboinyk ieee80211_init_suphtrates(struct ieee80211com *ic)
42568e8e04eSSam Leffler {
426597029bfSBernhard Schmidt #define	ADDRATE(x)	do {						\
427dfabbaa0SAndriy Voskoboinyk 	htrateset->rs_rates[htrateset->rs_nrates] = x;			\
428dfabbaa0SAndriy Voskoboinyk 	htrateset->rs_nrates++;						\
429597029bfSBernhard Schmidt } while (0)
430dfabbaa0SAndriy Voskoboinyk 	struct ieee80211_htrateset *htrateset = &ic->ic_sup_htrates;
431597029bfSBernhard Schmidt 	int i;
432597029bfSBernhard Schmidt 
433dfabbaa0SAndriy Voskoboinyk 	memset(htrateset, 0, sizeof(struct ieee80211_htrateset));
434597029bfSBernhard Schmidt 	for (i = 0; i < ic->ic_txstream * 8; i++)
435597029bfSBernhard Schmidt 		ADDRATE(i);
436597029bfSBernhard Schmidt 	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
437597029bfSBernhard Schmidt 	    (ic->ic_htcaps & IEEE80211_HTC_TXMCS32))
43865d22fe9SBernhard Schmidt 		ADDRATE(32);
439597029bfSBernhard Schmidt 	if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
440597029bfSBernhard Schmidt 		if (ic->ic_txstream >= 2) {
441597029bfSBernhard Schmidt 			 for (i = 33; i <= 38; i++)
442597029bfSBernhard Schmidt 				ADDRATE(i);
443597029bfSBernhard Schmidt 		}
444597029bfSBernhard Schmidt 		if (ic->ic_txstream >= 3) {
445597029bfSBernhard Schmidt 			for (i = 39; i <= 52; i++)
446597029bfSBernhard Schmidt 				ADDRATE(i);
447597029bfSBernhard Schmidt 		}
448597029bfSBernhard Schmidt 		if (ic->ic_txstream == 4) {
449597029bfSBernhard Schmidt 			for (i = 53; i <= 76; i++)
450597029bfSBernhard Schmidt 				ADDRATE(i);
451597029bfSBernhard Schmidt 		}
452597029bfSBernhard Schmidt 	}
453597029bfSBernhard Schmidt #undef	ADDRATE
45468e8e04eSSam Leffler }
45568e8e04eSSam Leffler 
45668e8e04eSSam Leffler /*
45768e8e04eSSam Leffler  * Receive processing.
45868e8e04eSSam Leffler  */
45968e8e04eSSam Leffler 
46068e8e04eSSam Leffler /*
46168e8e04eSSam Leffler  * Decap the encapsulated A-MSDU frames and dispatch all but
46268e8e04eSSam Leffler  * the last for delivery.  The last frame is returned for
46368e8e04eSSam Leffler  * delivery via the normal path.
46468e8e04eSSam Leffler  */
46568e8e04eSSam Leffler struct mbuf *
46668e8e04eSSam Leffler ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
46768e8e04eSSam Leffler {
468b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
4690c99c16bSSam Leffler 	int framelen;
47068e8e04eSSam Leffler 	struct mbuf *n;
47168e8e04eSSam Leffler 
47268e8e04eSSam Leffler 	/* discard 802.3 header inserted by ieee80211_decap */
47368e8e04eSSam Leffler 	m_adj(m, sizeof(struct ether_header));
47468e8e04eSSam Leffler 
475b032f27cSSam Leffler 	vap->iv_stats.is_amsdu_decap++;
47668e8e04eSSam Leffler 
47768e8e04eSSam Leffler 	for (;;) {
47868e8e04eSSam Leffler 		/*
47968e8e04eSSam Leffler 		 * Decap the first frame, bust it apart from the
48068e8e04eSSam Leffler 		 * remainder and deliver.  We leave the last frame
48168e8e04eSSam Leffler 		 * delivery to the caller (for consistency with other
48268e8e04eSSam Leffler 		 * code paths, could also do it here).
48368e8e04eSSam Leffler 		 */
48468e8e04eSSam Leffler 		m = ieee80211_decap1(m, &framelen);
48568e8e04eSSam Leffler 		if (m == NULL) {
486b032f27cSSam Leffler 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
4870c99c16bSSam Leffler 			    ni->ni_macaddr, "a-msdu", "%s", "decap failed");
488b032f27cSSam Leffler 			vap->iv_stats.is_amsdu_tooshort++;
48968e8e04eSSam Leffler 			return NULL;
49068e8e04eSSam Leffler 		}
4910c99c16bSSam Leffler 		if (m->m_pkthdr.len == framelen)
49268e8e04eSSam Leffler 			break;
493bd29f817SBjoern A. Zeeb 		n = m_split(m, framelen, IEEE80211_M_NOWAIT);
49468e8e04eSSam Leffler 		if (n == NULL) {
495b032f27cSSam Leffler 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
49668e8e04eSSam Leffler 			    ni->ni_macaddr, "a-msdu",
49768e8e04eSSam Leffler 			    "%s", "unable to split encapsulated frames");
498b032f27cSSam Leffler 			vap->iv_stats.is_amsdu_split++;
49968e8e04eSSam Leffler 			m_freem(m);			/* NB: must reclaim */
50068e8e04eSSam Leffler 			return NULL;
50168e8e04eSSam Leffler 		}
502b032f27cSSam Leffler 		vap->iv_deliver_data(vap, ni, m);
50368e8e04eSSam Leffler 
50468e8e04eSSam Leffler 		/*
50568e8e04eSSam Leffler 		 * Remove frame contents; each intermediate frame
50668e8e04eSSam Leffler 		 * is required to be aligned to a 4-byte boundary.
50768e8e04eSSam Leffler 		 */
50868e8e04eSSam Leffler 		m = n;
50968e8e04eSSam Leffler 		m_adj(m, roundup2(framelen, 4) - framelen);	/* padding */
51068e8e04eSSam Leffler 	}
51168e8e04eSSam Leffler 	return m;				/* last delivered by caller */
51268e8e04eSSam Leffler }
51368e8e04eSSam Leffler 
514e81d9092SAdrian Chadd static void
515e81d9092SAdrian Chadd ampdu_rx_purge_slot(struct ieee80211_rx_ampdu *rap, int i)
516e81d9092SAdrian Chadd {
517e81d9092SAdrian Chadd 	struct mbuf *m;
518e81d9092SAdrian Chadd 
519e81d9092SAdrian Chadd 	/* Walk the queue, removing frames as appropriate */
52069773116SJohn Baldwin 	for (;;) {
521e81d9092SAdrian Chadd 		m = mbufq_dequeue(&rap->rxa_mq[i]);
522e81d9092SAdrian Chadd 		if (m == NULL)
523e81d9092SAdrian Chadd 			break;
524e81d9092SAdrian Chadd 		rap->rxa_qbytes -= m->m_pkthdr.len;
525e81d9092SAdrian Chadd 		rap->rxa_qframes--;
526e81d9092SAdrian Chadd 		m_freem(m);
527e81d9092SAdrian Chadd 	}
528e81d9092SAdrian Chadd }
529e81d9092SAdrian Chadd 
53068e8e04eSSam Leffler /*
53182bd08eeSAdrian Chadd  * Add the given frame to the current RX reorder slot.
53282bd08eeSAdrian Chadd  *
53382bd08eeSAdrian Chadd  * For future offloaded A-MSDU handling where multiple frames with
53482bd08eeSAdrian Chadd  * the same sequence number show up here, this routine will append
53582bd08eeSAdrian Chadd  * those frames as long as they're appropriately tagged.
53682bd08eeSAdrian Chadd  */
53782bd08eeSAdrian Chadd static int
53882bd08eeSAdrian Chadd ampdu_rx_add_slot(struct ieee80211_rx_ampdu *rap, int off, int tid,
53982bd08eeSAdrian Chadd     ieee80211_seq rxseq,
54082bd08eeSAdrian Chadd     struct ieee80211_node *ni,
541e81d9092SAdrian Chadd     struct mbuf *m,
542e81d9092SAdrian Chadd     const struct ieee80211_rx_stats *rxs)
54382bd08eeSAdrian Chadd {
544e81d9092SAdrian Chadd 	const struct ieee80211_rx_stats *rxs_final = NULL;
54582bd08eeSAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
546e81d9092SAdrian Chadd 	int toss_dup;
547e81d9092SAdrian Chadd #define	PROCESS		0	/* caller should process frame */
548e81d9092SAdrian Chadd #define	CONSUMED	1	/* frame consumed, caller does nothing */
54982bd08eeSAdrian Chadd 
550e81d9092SAdrian Chadd 	/*
551e81d9092SAdrian Chadd 	 * Figure out if this is a duplicate frame for the given slot.
552e81d9092SAdrian Chadd 	 *
553e81d9092SAdrian Chadd 	 * We're assuming that the driver will hand us all the frames
554e81d9092SAdrian Chadd 	 * for a given AMSDU decap pass and if we get /a/ frame
555e81d9092SAdrian Chadd 	 * for an AMSDU decap then we'll get all of them.
556e81d9092SAdrian Chadd 	 *
557e81d9092SAdrian Chadd 	 * The tricksy bit is that we don't know when the /end/ of
558e81d9092SAdrian Chadd 	 * the decap pass is, because we aren't tracking state here
559e81d9092SAdrian Chadd 	 * per-slot to know that we've finished receiving the frame list.
560e81d9092SAdrian Chadd 	 *
561e81d9092SAdrian Chadd 	 * The driver sets RX_F_AMSDU and RX_F_AMSDU_MORE to tell us
562e81d9092SAdrian Chadd 	 * what's going on; so ideally we'd just check the frame at the
563e81d9092SAdrian Chadd 	 * end of the reassembly slot to see if its F_AMSDU w/ no F_AMSDU_MORE -
564e81d9092SAdrian Chadd 	 * that means we've received the whole AMSDU decap pass.
565e81d9092SAdrian Chadd 	 */
566e81d9092SAdrian Chadd 
567e81d9092SAdrian Chadd 	/*
568e81d9092SAdrian Chadd 	 * Get the rxs of the final mbuf in the slot, if one exists.
569e81d9092SAdrian Chadd 	 */
5708cb9b68fSJohn Baldwin 	if (!mbufq_empty(&rap->rxa_mq[off])) {
571e81d9092SAdrian Chadd 		rxs_final = ieee80211_get_rx_params_ptr(mbufq_last(&rap->rxa_mq[off]));
572e81d9092SAdrian Chadd 	}
573e81d9092SAdrian Chadd 
574e81d9092SAdrian Chadd 	/* Default to tossing the duplicate frame */
575e81d9092SAdrian Chadd 	toss_dup = 1;
576e81d9092SAdrian Chadd 
577e81d9092SAdrian Chadd 	/*
578e81d9092SAdrian Chadd 	 * Check to see if the final frame has F_AMSDU and F_AMSDU set, AND
579e81d9092SAdrian Chadd 	 * this frame has F_AMSDU set (MORE or otherwise.)  That's a sign
580e81d9092SAdrian Chadd 	 * that more can come.
581e81d9092SAdrian Chadd 	 */
582e81d9092SAdrian Chadd 
583e81d9092SAdrian Chadd 	if ((rxs != NULL) && (rxs_final != NULL) &&
584e81d9092SAdrian Chadd 	    ieee80211_check_rxseq_amsdu(rxs) &&
585e81d9092SAdrian Chadd 	    ieee80211_check_rxseq_amsdu(rxs_final)) {
586e81d9092SAdrian Chadd 		if (! ieee80211_check_rxseq_amsdu_more(rxs_final)) {
587e81d9092SAdrian Chadd 			/*
588e81d9092SAdrian Chadd 			 * amsdu_more() returning 0 means "it's not the
589e81d9092SAdrian Chadd 			 * final frame" so we can append more
590e81d9092SAdrian Chadd 			 * frames here.
591e81d9092SAdrian Chadd 			 */
592e81d9092SAdrian Chadd 			toss_dup = 0;
593e81d9092SAdrian Chadd 		}
594e81d9092SAdrian Chadd 	}
595e81d9092SAdrian Chadd 
596e81d9092SAdrian Chadd 	/*
597e81d9092SAdrian Chadd 	 * If the list is empty OR we have determined we can put more
598e81d9092SAdrian Chadd 	 * driver decap'ed AMSDU frames in here, then insert.
599e81d9092SAdrian Chadd 	 */
6008cb9b68fSJohn Baldwin 	if (mbufq_empty(&rap->rxa_mq[off]) || (toss_dup == 0)) {
601e81d9092SAdrian Chadd 		if (mbufq_enqueue(&rap->rxa_mq[off], m) != 0) {
602e81d9092SAdrian Chadd 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
603e81d9092SAdrian Chadd 			    ni->ni_macaddr,
604e81d9092SAdrian Chadd 			    "a-mpdu queue fail",
605e81d9092SAdrian Chadd 			    "seqno %u tid %u BA win <%u:%u> off=%d, qlen=%d, maxqlen=%d",
606e81d9092SAdrian Chadd 			    rxseq, tid, rap->rxa_start,
607e81d9092SAdrian Chadd 			    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
608e81d9092SAdrian Chadd 			    off,
609e81d9092SAdrian Chadd 			    mbufq_len(&rap->rxa_mq[off]),
610e81d9092SAdrian Chadd 			    rap->rxa_mq[off].mq_maxlen);
611e81d9092SAdrian Chadd 			/* XXX error count */
612e81d9092SAdrian Chadd 			m_freem(m);
613e81d9092SAdrian Chadd 			return CONSUMED;
614e81d9092SAdrian Chadd 		}
61582bd08eeSAdrian Chadd 		rap->rxa_qframes++;
61682bd08eeSAdrian Chadd 		rap->rxa_qbytes += m->m_pkthdr.len;
61782bd08eeSAdrian Chadd 		vap->iv_stats.is_ampdu_rx_reorder++;
618e81d9092SAdrian Chadd 		/*
619e81d9092SAdrian Chadd 		 * Statistics for AMSDU decap.
620e81d9092SAdrian Chadd 		 */
621e81d9092SAdrian Chadd 		if (rxs != NULL && ieee80211_check_rxseq_amsdu(rxs)) {
622e81d9092SAdrian Chadd 			if (ieee80211_check_rxseq_amsdu_more(rxs)) {
623e81d9092SAdrian Chadd 				/* more=1, AMSDU, end of batch */
624e81d9092SAdrian Chadd 				IEEE80211_NODE_STAT(ni, rx_amsdu_more_end);
625e81d9092SAdrian Chadd 			} else {
626e81d9092SAdrian Chadd 				IEEE80211_NODE_STAT(ni, rx_amsdu_more);
627e81d9092SAdrian Chadd 			}
628e81d9092SAdrian Chadd 		}
62982bd08eeSAdrian Chadd 	} else {
63082bd08eeSAdrian Chadd 		IEEE80211_DISCARD_MAC(vap,
63182bd08eeSAdrian Chadd 		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
63282bd08eeSAdrian Chadd 		    ni->ni_macaddr, "a-mpdu duplicate",
63382bd08eeSAdrian Chadd 		    "seqno %u tid %u BA win <%u:%u>",
63482bd08eeSAdrian Chadd 		    rxseq, tid, rap->rxa_start,
63582bd08eeSAdrian Chadd 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
636e81d9092SAdrian Chadd 		if (rxs != NULL) {
637e81d9092SAdrian Chadd 			IEEE80211_DISCARD_MAC(vap,
638e81d9092SAdrian Chadd 			    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
639e81d9092SAdrian Chadd 			    ni->ni_macaddr, "a-mpdu duplicate",
640e81d9092SAdrian Chadd 			    "seqno %d tid %u pktflags 0x%08x\n",
641e81d9092SAdrian Chadd 			    rxseq, tid, rxs->c_pktflags);
642e81d9092SAdrian Chadd 		}
643e81d9092SAdrian Chadd 		if (rxs_final != NULL) {
644e81d9092SAdrian Chadd 			IEEE80211_DISCARD_MAC(vap,
645e81d9092SAdrian Chadd 			    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
646e81d9092SAdrian Chadd 			    ni->ni_macaddr, "a-mpdu duplicate",
647e81d9092SAdrian Chadd 			    "final: pktflags 0x%08x\n",
648e81d9092SAdrian Chadd 			    rxs_final->c_pktflags);
649e81d9092SAdrian Chadd 		}
65082bd08eeSAdrian Chadd 		vap->iv_stats.is_rx_dup++;
65182bd08eeSAdrian Chadd 		IEEE80211_NODE_STAT(ni, rx_dup);
65282bd08eeSAdrian Chadd 		m_freem(m);
65382bd08eeSAdrian Chadd 	}
654e81d9092SAdrian Chadd 	return CONSUMED;
655e81d9092SAdrian Chadd #undef	CONSUMED
656e81d9092SAdrian Chadd #undef	PROCESS
65782bd08eeSAdrian Chadd }
65882bd08eeSAdrian Chadd 
65982bd08eeSAdrian Chadd /*
66068e8e04eSSam Leffler  * Purge all frames in the A-MPDU re-order queue.
66168e8e04eSSam Leffler  */
66268e8e04eSSam Leffler static void
66368e8e04eSSam Leffler ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
66468e8e04eSSam Leffler {
66568e8e04eSSam Leffler 	int i;
66668e8e04eSSam Leffler 
66768e8e04eSSam Leffler 	for (i = 0; i < rap->rxa_wnd; i++) {
66882bd08eeSAdrian Chadd 		ampdu_rx_purge_slot(rap, i);
66982bd08eeSAdrian Chadd 		if (rap->rxa_qframes == 0)
67068e8e04eSSam Leffler 			break;
67168e8e04eSSam Leffler 	}
67268e8e04eSSam Leffler 	KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
67368e8e04eSSam Leffler 	    ("lost %u data, %u frames on ampdu rx q",
67468e8e04eSSam Leffler 	    rap->rxa_qbytes, rap->rxa_qframes));
67568e8e04eSSam Leffler }
67668e8e04eSSam Leffler 
677e81d9092SAdrian Chadd static void
678e81d9092SAdrian Chadd ieee80211_ampdu_rx_init_rap(struct ieee80211_node *ni,
679e81d9092SAdrian Chadd     struct ieee80211_rx_ampdu *rap)
680e81d9092SAdrian Chadd {
681e81d9092SAdrian Chadd 	int i;
682e81d9092SAdrian Chadd 
683e81d9092SAdrian Chadd 	/* XXX TODO: ensure the queues are empty */
684e81d9092SAdrian Chadd 	memset(rap, 0, sizeof(*rap));
685e81d9092SAdrian Chadd 	for (i = 0; i < IEEE80211_AGGR_BAWMAX; i++)
686e81d9092SAdrian Chadd 		mbufq_init(&rap->rxa_mq[i], 256);
687e81d9092SAdrian Chadd }
688e81d9092SAdrian Chadd 
68968e8e04eSSam Leffler /*
690b032f27cSSam Leffler  * Start A-MPDU rx/re-order processing for the specified TID.
691b032f27cSSam Leffler  */
69251b5aba2SSam Leffler static int
69351b5aba2SSam Leffler ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
69451b5aba2SSam Leffler 	int baparamset, int batimeout, int baseqctl)
695b032f27cSSam Leffler {
696ebb9b256SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
697fe5ebb23SBjoern A. Zeeb 	int bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ);
69851b5aba2SSam Leffler 
699b032f27cSSam Leffler 	if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
700b032f27cSSam Leffler 		/*
701b032f27cSSam Leffler 		 * AMPDU previously setup and not terminated with a DELBA,
702b032f27cSSam Leffler 		 * flush the reorder q's in case anything remains.
703b032f27cSSam Leffler 		 */
704b032f27cSSam Leffler 		ampdu_rx_purge(rap);
705b032f27cSSam Leffler 	}
706e81d9092SAdrian Chadd 	ieee80211_ampdu_rx_init_rap(ni, rap);
707b032f27cSSam Leffler 	rap->rxa_wnd = (bufsiz == 0) ?
708b032f27cSSam Leffler 	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
709fe5ebb23SBjoern A. Zeeb 	rap->rxa_start = _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_START);
710b032f27cSSam Leffler 	rap->rxa_flags |=  IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
71151b5aba2SSam Leffler 
712ebb9b256SAdrian Chadd 	/* XXX this should be a configuration flag */
713ebb9b256SAdrian Chadd 	if ((vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU) &&
714fe5ebb23SBjoern A. Zeeb 	    (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU)))
715ebb9b256SAdrian Chadd 		rap->rxa_flags |= IEEE80211_AGGR_AMSDU;
716ebb9b256SAdrian Chadd 	else
717ebb9b256SAdrian Chadd 		rap->rxa_flags &= ~IEEE80211_AGGR_AMSDU;
718ebb9b256SAdrian Chadd 
71951b5aba2SSam Leffler 	return 0;
720b032f27cSSam Leffler }
721b032f27cSSam Leffler 
722b032f27cSSam Leffler /*
7231f3a8d11SAdrian Chadd  * Public function; manually setup the RX ampdu state.
7241f3a8d11SAdrian Chadd  */
7251f3a8d11SAdrian Chadd int
7261f3a8d11SAdrian Chadd ieee80211_ampdu_rx_start_ext(struct ieee80211_node *ni, int tid, int seq, int baw)
7271f3a8d11SAdrian Chadd {
7281f3a8d11SAdrian Chadd 	struct ieee80211_rx_ampdu *rap;
7291f3a8d11SAdrian Chadd 
7301f3a8d11SAdrian Chadd 	/* XXX TODO: sanity check tid, seq, baw */
7311f3a8d11SAdrian Chadd 
7321f3a8d11SAdrian Chadd 	rap = &ni->ni_rx_ampdu[tid];
7331f3a8d11SAdrian Chadd 
7341f3a8d11SAdrian Chadd 	if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
7351f3a8d11SAdrian Chadd 		/*
7361f3a8d11SAdrian Chadd 		 * AMPDU previously setup and not terminated with a DELBA,
7371f3a8d11SAdrian Chadd 		 * flush the reorder q's in case anything remains.
7381f3a8d11SAdrian Chadd 		 */
7391f3a8d11SAdrian Chadd 		ampdu_rx_purge(rap);
7401f3a8d11SAdrian Chadd 	}
7411f3a8d11SAdrian Chadd 
742e81d9092SAdrian Chadd 	ieee80211_ampdu_rx_init_rap(ni, rap);
743e81d9092SAdrian Chadd 
7441f3a8d11SAdrian Chadd 	rap->rxa_wnd = (baw== 0) ?
7451f3a8d11SAdrian Chadd 	    IEEE80211_AGGR_BAWMAX : min(baw, IEEE80211_AGGR_BAWMAX);
746339be86fSAdrian Chadd 	if (seq == -1) {
747339be86fSAdrian Chadd 		/* Wait for the first RX frame, use that as BAW */
748339be86fSAdrian Chadd 		rap->rxa_start = 0;
749339be86fSAdrian Chadd 		rap->rxa_flags |= IEEE80211_AGGR_WAITRX;
750339be86fSAdrian Chadd 	} else {
7511f3a8d11SAdrian Chadd 		rap->rxa_start = seq;
752339be86fSAdrian Chadd 	}
7531f3a8d11SAdrian Chadd 	rap->rxa_flags |=  IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
7541f3a8d11SAdrian Chadd 
755ebb9b256SAdrian Chadd 	/* XXX TODO: no amsdu flag */
756ebb9b256SAdrian Chadd 
7571f3a8d11SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
7583d12d1f1SAdrian Chadd 	    "%s: tid=%d, start=%d, wnd=%d, flags=0x%08x",
7591f3a8d11SAdrian Chadd 	    __func__,
7601f3a8d11SAdrian Chadd 	    tid,
7611f3a8d11SAdrian Chadd 	    seq,
7621f3a8d11SAdrian Chadd 	    rap->rxa_wnd,
7631f3a8d11SAdrian Chadd 	    rap->rxa_flags);
7641f3a8d11SAdrian Chadd 
7651f3a8d11SAdrian Chadd 	return 0;
7661f3a8d11SAdrian Chadd }
7671f3a8d11SAdrian Chadd 
7681f3a8d11SAdrian Chadd /*
7690cc02885SAdrian Chadd  * Public function; manually stop the RX AMPDU state.
7700cc02885SAdrian Chadd  */
7710cc02885SAdrian Chadd void
7720cc02885SAdrian Chadd ieee80211_ampdu_rx_stop_ext(struct ieee80211_node *ni, int tid)
7730cc02885SAdrian Chadd {
7740cc02885SAdrian Chadd 	struct ieee80211_rx_ampdu *rap;
7750cc02885SAdrian Chadd 
7760cc02885SAdrian Chadd 	/* XXX TODO: sanity check tid, seq, baw */
7770cc02885SAdrian Chadd 	rap = &ni->ni_rx_ampdu[tid];
7780cc02885SAdrian Chadd 	ampdu_rx_stop(ni, rap);
7790cc02885SAdrian Chadd }
7800cc02885SAdrian Chadd 
7810cc02885SAdrian Chadd /*
78268e8e04eSSam Leffler  * Stop A-MPDU rx processing for the specified TID.
78368e8e04eSSam Leffler  */
78468e8e04eSSam Leffler static void
78551b5aba2SSam Leffler ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
78668e8e04eSSam Leffler {
7870917631fSRui Paulo 
78868e8e04eSSam Leffler 	ampdu_rx_purge(rap);
789339be86fSAdrian Chadd 	rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING
790339be86fSAdrian Chadd 	    | IEEE80211_AGGR_XCHGPEND
791339be86fSAdrian Chadd 	    | IEEE80211_AGGR_WAITRX);
79268e8e04eSSam Leffler }
79368e8e04eSSam Leffler 
79468e8e04eSSam Leffler /*
79568e8e04eSSam Leffler  * Dispatch a frame from the A-MPDU reorder queue.  The
79668e8e04eSSam Leffler  * frame is fed back into ieee80211_input marked with an
79745f856e3SSam Leffler  * M_AMPDU_MPDU flag so it doesn't come back to us (it also
79868e8e04eSSam Leffler  * permits ieee80211_input to optimize re-processing).
79968e8e04eSSam Leffler  */
80068e8e04eSSam Leffler static __inline void
80168e8e04eSSam Leffler ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
80268e8e04eSSam Leffler {
80345f856e3SSam Leffler 	m->m_flags |= M_AMPDU_MPDU;	/* bypass normal processing */
8045463c4a4SSam Leffler 	/* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */
8055463c4a4SSam Leffler 	(void) ieee80211_input(ni, m, 0, 0);
80668e8e04eSSam Leffler }
80768e8e04eSSam Leffler 
8082d6ab41dSAdrian Chadd static int
8092d6ab41dSAdrian Chadd ampdu_dispatch_slot(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni,
8102d6ab41dSAdrian Chadd     int i)
8112d6ab41dSAdrian Chadd {
8122d6ab41dSAdrian Chadd 	struct mbuf *m;
813e81d9092SAdrian Chadd 	int n = 0;
8142d6ab41dSAdrian Chadd 
81569773116SJohn Baldwin 	for (;;) {
816e81d9092SAdrian Chadd 		m = mbufq_dequeue(&rap->rxa_mq[i]);
817e81d9092SAdrian Chadd 		if (m == NULL)
818e81d9092SAdrian Chadd 			break;
819e81d9092SAdrian Chadd 		n++;
8202d6ab41dSAdrian Chadd 
8212d6ab41dSAdrian Chadd 		rap->rxa_qbytes -= m->m_pkthdr.len;
8222d6ab41dSAdrian Chadd 		rap->rxa_qframes--;
8232d6ab41dSAdrian Chadd 
8242d6ab41dSAdrian Chadd 		ampdu_dispatch(ni, m);
825e81d9092SAdrian Chadd 	}
826e81d9092SAdrian Chadd 	return (n);
8272d6ab41dSAdrian Chadd }
8282d6ab41dSAdrian Chadd 
829b06dfd58SAdrian Chadd static void
830b06dfd58SAdrian Chadd ampdu_rx_moveup(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni,
831b06dfd58SAdrian Chadd     int i, int winstart)
832b06dfd58SAdrian Chadd {
833b06dfd58SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
834b06dfd58SAdrian Chadd 
835e81d9092SAdrian Chadd 	/*
836e81d9092SAdrian Chadd 	 * If frames remain, copy the mbuf pointers down so
837e81d9092SAdrian Chadd 	 * they correspond to the offsets in the new window.
838e81d9092SAdrian Chadd 	 */
839b06dfd58SAdrian Chadd 	if (rap->rxa_qframes != 0) {
840b06dfd58SAdrian Chadd 		int n = rap->rxa_qframes, j;
841b06dfd58SAdrian Chadd 		for (j = i+1; j < rap->rxa_wnd; j++) {
842e81d9092SAdrian Chadd 			/*
843e81d9092SAdrian Chadd 			 * Concat the list contents over, which will
844e81d9092SAdrian Chadd 			 * blank the source list for us.
845e81d9092SAdrian Chadd 			 */
846e81d9092SAdrian Chadd 			if (mbufq_len(&rap->rxa_mq[j]) != 0) {
847e81d9092SAdrian Chadd 				n = n - mbufq_len(&rap->rxa_mq[j]);
848e81d9092SAdrian Chadd 				mbufq_concat(&rap->rxa_mq[j-i], &rap->rxa_mq[j]);
849e81d9092SAdrian Chadd 				KASSERT(n >= 0, ("%s: n < 0 (%d)", __func__, n));
850e81d9092SAdrian Chadd 				if (n == 0)
851b06dfd58SAdrian Chadd 					break;
852b06dfd58SAdrian Chadd 			}
853b06dfd58SAdrian Chadd 		}
854b06dfd58SAdrian Chadd 		KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
855b06dfd58SAdrian Chadd 		    "BA win <%d:%d> winstart %d",
856b06dfd58SAdrian Chadd 		    __func__, n, rap->rxa_qframes, i, rap->rxa_start,
857b06dfd58SAdrian Chadd 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
858b06dfd58SAdrian Chadd 		    winstart));
859b06dfd58SAdrian Chadd 		vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
860b06dfd58SAdrian Chadd 	}
861b06dfd58SAdrian Chadd }
862b06dfd58SAdrian Chadd 
86368e8e04eSSam Leffler /*
86468e8e04eSSam Leffler  * Dispatch as many frames as possible from the re-order queue.
86568e8e04eSSam Leffler  * Frames will always be "at the front"; we process all frames
86668e8e04eSSam Leffler  * up to the first empty slot in the window.  On completion we
86768e8e04eSSam Leffler  * cleanup state if there are still pending frames in the current
86868e8e04eSSam Leffler  * BA window.  We assume the frame at slot 0 is already handled
86968e8e04eSSam Leffler  * by the caller; we always start at slot 1.
87068e8e04eSSam Leffler  */
87168e8e04eSSam Leffler static void
87268e8e04eSSam Leffler ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
87368e8e04eSSam Leffler {
874b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
875e81d9092SAdrian Chadd 	int i, r, r2;
87668e8e04eSSam Leffler 
87768e8e04eSSam Leffler 	/* flush run of frames */
878e81d9092SAdrian Chadd 	r2 = 0;
87968e8e04eSSam Leffler 	for (i = 1; i < rap->rxa_wnd; i++) {
880e81d9092SAdrian Chadd 		r = ampdu_dispatch_slot(rap, ni, i);
881e81d9092SAdrian Chadd 		if (r == 0)
88268e8e04eSSam Leffler 			break;
883e81d9092SAdrian Chadd 		r2 += r;
88468e8e04eSSam Leffler 	}
8852d6ab41dSAdrian Chadd 
886e81d9092SAdrian Chadd 	/* move up frames */
887b06dfd58SAdrian Chadd 	ampdu_rx_moveup(rap, ni, i, -1);
888b06dfd58SAdrian Chadd 
8891b6167d2SSam Leffler 	/*
8901b6167d2SSam Leffler 	 * Adjust the start of the BA window to
8911b6167d2SSam Leffler 	 * reflect the frames just dispatched.
8921b6167d2SSam Leffler 	 */
8931b6167d2SSam Leffler 	rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
894e81d9092SAdrian Chadd 	vap->iv_stats.is_ampdu_rx_oor += r2;
895e81d9092SAdrian Chadd 
896e81d9092SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
897e81d9092SAdrian Chadd 	    "%s: moved slot up %d slots to start at %d (%d frames)",
898e81d9092SAdrian Chadd 	    __func__,
899e81d9092SAdrian Chadd 	    i,
900e81d9092SAdrian Chadd 	    rap->rxa_start,
901e81d9092SAdrian Chadd 	    r2);
90268e8e04eSSam Leffler }
90368e8e04eSSam Leffler 
90468e8e04eSSam Leffler /*
9051b6167d2SSam Leffler  * Dispatch all frames in the A-MPDU re-order queue.
90668e8e04eSSam Leffler  */
90768e8e04eSSam Leffler static void
9081b6167d2SSam Leffler ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
90968e8e04eSSam Leffler {
9102d6ab41dSAdrian Chadd 	int i, r;
91168e8e04eSSam Leffler 
9121b6167d2SSam Leffler 	for (i = 0; i < rap->rxa_wnd; i++) {
9132d6ab41dSAdrian Chadd 		r = ampdu_dispatch_slot(rap, ni, i);
9142d6ab41dSAdrian Chadd 		if (r == 0)
91568e8e04eSSam Leffler 			continue;
916e81d9092SAdrian Chadd 		ni->ni_vap->iv_stats.is_ampdu_rx_oor += r;
917e81d9092SAdrian Chadd 
918e81d9092SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
919e81d9092SAdrian Chadd 		    "%s: moved slot up %d slots to start at %d (%d frames)",
920e81d9092SAdrian Chadd 		    __func__,
921e81d9092SAdrian Chadd 		    1,
922e81d9092SAdrian Chadd 		    rap->rxa_start,
923e81d9092SAdrian Chadd 		    r);
9241b6167d2SSam Leffler 
9251b6167d2SSam Leffler 		if (rap->rxa_qframes == 0)
92668e8e04eSSam Leffler 			break;
92768e8e04eSSam Leffler 	}
92868e8e04eSSam Leffler }
9291b6167d2SSam Leffler 
9301b6167d2SSam Leffler /*
9311b6167d2SSam Leffler  * Dispatch all frames in the A-MPDU re-order queue
9321b6167d2SSam Leffler  * preceding the specified sequence number.  This logic
9331b6167d2SSam Leffler  * handles window moves due to a received MSDU or BAR.
9341b6167d2SSam Leffler  */
9351b6167d2SSam Leffler static void
9361b6167d2SSam Leffler ampdu_rx_flush_upto(struct ieee80211_node *ni,
9371b6167d2SSam Leffler 	struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
9381b6167d2SSam Leffler {
939b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
9401b6167d2SSam Leffler 	ieee80211_seq seqno;
9412d6ab41dSAdrian Chadd 	int i, r;
9421b6167d2SSam Leffler 
9431b6167d2SSam Leffler 	/*
9441b6167d2SSam Leffler 	 * Flush any complete MSDU's with a sequence number lower
9451b6167d2SSam Leffler 	 * than winstart.  Gaps may exist.  Note that we may actually
9461b6167d2SSam Leffler 	 * dispatch frames past winstart if a run continues; this is
9471b6167d2SSam Leffler 	 * an optimization that avoids having to do a separate pass
9481b6167d2SSam Leffler 	 * to dispatch frames after moving the BA window start.
9491b6167d2SSam Leffler 	 */
9501b6167d2SSam Leffler 	seqno = rap->rxa_start;
9511b6167d2SSam Leffler 	for (i = 0; i < rap->rxa_wnd; i++) {
952e81d9092SAdrian Chadd 		if ((r = mbufq_len(&rap->rxa_mq[i])) != 0) {
953e81d9092SAdrian Chadd 			(void) ampdu_dispatch_slot(rap, ni, i);
954e81d9092SAdrian Chadd 		} else {
9551b6167d2SSam Leffler 			if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
9561b6167d2SSam Leffler 				break;
9571b6167d2SSam Leffler 		}
9582d6ab41dSAdrian Chadd 		vap->iv_stats.is_ampdu_rx_oor += r;
9591b6167d2SSam Leffler 		seqno = IEEE80211_SEQ_INC(seqno);
960e81d9092SAdrian Chadd 
961e81d9092SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
962e81d9092SAdrian Chadd 		    "%s: moved slot up %d slots to start at %d (%d frames)",
963e81d9092SAdrian Chadd 		    __func__,
964e81d9092SAdrian Chadd 		    1,
965e81d9092SAdrian Chadd 		    seqno,
966e81d9092SAdrian Chadd 		    r);
9671b6167d2SSam Leffler 	}
968e81d9092SAdrian Chadd 
9691b6167d2SSam Leffler 	/*
9701b6167d2SSam Leffler 	 * If frames remain, copy the mbuf pointers down so
9711b6167d2SSam Leffler 	 * they correspond to the offsets in the new window.
9721b6167d2SSam Leffler 	 */
973b06dfd58SAdrian Chadd 	ampdu_rx_moveup(rap, ni, i, winstart);
974b032f27cSSam Leffler 
9751b6167d2SSam Leffler 	/*
9761b6167d2SSam Leffler 	 * Move the start of the BA window; we use the
9771b6167d2SSam Leffler 	 * sequence number of the last MSDU that was
9781b6167d2SSam Leffler 	 * passed up the stack+1 or winstart if stopped on
9791b6167d2SSam Leffler 	 * a gap in the reorder buffer.
9801b6167d2SSam Leffler 	 */
9811b6167d2SSam Leffler 	rap->rxa_start = seqno;
9821b6167d2SSam Leffler }
98368e8e04eSSam Leffler 
98468e8e04eSSam Leffler /*
98568e8e04eSSam Leffler  * Process a received QoS data frame for an HT station.  Handle
98668e8e04eSSam Leffler  * A-MPDU reordering: if this frame is received out of order
98768e8e04eSSam Leffler  * and falls within the BA window hold onto it.  Otherwise if
9881b6167d2SSam Leffler  * this frame completes a run, flush any pending frames.  We
98968e8e04eSSam Leffler  * return 1 if the frame is consumed.  A 0 is returned if
99068e8e04eSSam Leffler  * the frame should be processed normally by the caller.
991e81d9092SAdrian Chadd  *
992e81d9092SAdrian Chadd  * A-MSDU: handle hardware decap'ed A-MSDU frames that are
993e81d9092SAdrian Chadd  * pretending to be MPDU's.  They're dispatched directly if
994e81d9092SAdrian Chadd  * able; or attempted to put into the receive reordering slot.
99568e8e04eSSam Leffler  */
99668e8e04eSSam Leffler int
99785c4e670SAdrian Chadd ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m,
99885c4e670SAdrian Chadd     const struct ieee80211_rx_stats *rxs)
99968e8e04eSSam Leffler {
10001b6167d2SSam Leffler #define	PROCESS		0	/* caller should process frame */
10011b6167d2SSam Leffler #define	CONSUMED	1	/* frame consumed, caller does nothing */
1002b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
100368e8e04eSSam Leffler 	struct ieee80211_qosframe *wh;
100468e8e04eSSam Leffler 	struct ieee80211_rx_ampdu *rap;
100568e8e04eSSam Leffler 	ieee80211_seq rxseq;
100668e8e04eSSam Leffler 	uint8_t tid;
100768e8e04eSSam Leffler 	int off;
1008e81d9092SAdrian Chadd 	int amsdu = ieee80211_check_rxseq_amsdu(rxs);
1009e81d9092SAdrian Chadd 	int amsdu_end = ieee80211_check_rxseq_amsdu_more(rxs);
101068e8e04eSSam Leffler 
101145f856e3SSam Leffler 	KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU,
101245f856e3SSam Leffler 	    ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags));
101368e8e04eSSam Leffler 	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
101468e8e04eSSam Leffler 
101568e8e04eSSam Leffler 	/* NB: m_len known to be sufficient */
101668e8e04eSSam Leffler 	wh = mtod(m, struct ieee80211_qosframe *);
101737e54466SAdrian Chadd 	if (!IEEE80211_IS_QOSDATA(wh)) {
101845f856e3SSam Leffler 		/*
101945f856e3SSam Leffler 		 * Not QoS data, shouldn't get here but just
102045f856e3SSam Leffler 		 * return it to the caller for processing.
102145f856e3SSam Leffler 		 */
102245f856e3SSam Leffler 		return PROCESS;
102345f856e3SSam Leffler 	}
10249764ef21SAdrian Chadd 
10259764ef21SAdrian Chadd 	/*
10269764ef21SAdrian Chadd 	 * 802.11-2012 9.3.2.10 - Duplicate detection and recovery.
10279764ef21SAdrian Chadd 	 *
10289764ef21SAdrian Chadd 	 * Multicast QoS data frames are checked against a different
10299764ef21SAdrian Chadd 	 * counter, not the per-TID counter.
10309764ef21SAdrian Chadd 	 */
10319764ef21SAdrian Chadd 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
10329764ef21SAdrian Chadd 		return PROCESS;
10339764ef21SAdrian Chadd 
1034f3f08e16SAndriy Voskoboinyk 	tid = ieee80211_getqos(wh)[0];
10351b6167d2SSam Leffler 	tid &= IEEE80211_QOS_TID;
103668e8e04eSSam Leffler 	rap = &ni->ni_rx_ampdu[tid];
103768e8e04eSSam Leffler 	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
103868e8e04eSSam Leffler 		/*
103968e8e04eSSam Leffler 		 * No ADDBA request yet, don't touch.
104068e8e04eSSam Leffler 		 */
10411b6167d2SSam Leffler 		return PROCESS;
104268e8e04eSSam Leffler 	}
10431582f256SSam Leffler 	rxseq = le16toh(*(uint16_t *)wh->i_seq);
10441582f256SSam Leffler 	if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) {
10451582f256SSam Leffler 		/*
10461582f256SSam Leffler 		 * Fragments are not allowed; toss.
10471582f256SSam Leffler 		 */
10481582f256SSam Leffler 		IEEE80211_DISCARD_MAC(vap,
10491582f256SSam Leffler 		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
10501582f256SSam Leffler 		    "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid,
10511582f256SSam Leffler 		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
10521582f256SSam Leffler 		vap->iv_stats.is_ampdu_rx_drop++;
10531582f256SSam Leffler 		IEEE80211_NODE_STAT(ni, rx_drop);
10541582f256SSam Leffler 		m_freem(m);
10551582f256SSam Leffler 		return CONSUMED;
10561582f256SSam Leffler 	}
10571582f256SSam Leffler 	rxseq >>= IEEE80211_SEQ_SEQ_SHIFT;
10581b6167d2SSam Leffler 	rap->rxa_nframes++;
1059339be86fSAdrian Chadd 
1060339be86fSAdrian Chadd 	/*
1061339be86fSAdrian Chadd 	 * Handle waiting for the first frame to define the BAW.
1062339be86fSAdrian Chadd 	 * Some firmware doesn't provide the RX of the starting point
1063339be86fSAdrian Chadd 	 * of the BAW and we have to cope.
1064339be86fSAdrian Chadd 	 */
1065339be86fSAdrian Chadd 	if (rap->rxa_flags & IEEE80211_AGGR_WAITRX) {
1066339be86fSAdrian Chadd 		rap->rxa_flags &= ~IEEE80211_AGGR_WAITRX;
1067339be86fSAdrian Chadd 		rap->rxa_start = rxseq;
1068339be86fSAdrian Chadd 	}
10691b6167d2SSam Leffler again:
107068e8e04eSSam Leffler 	if (rxseq == rap->rxa_start) {
107168e8e04eSSam Leffler 		/*
107268e8e04eSSam Leffler 		 * First frame in window.
107368e8e04eSSam Leffler 		 */
107468e8e04eSSam Leffler 		if (rap->rxa_qframes != 0) {
107568e8e04eSSam Leffler 			/*
107668e8e04eSSam Leffler 			 * Dispatch as many packets as we can.
107768e8e04eSSam Leffler 			 */
10788cb9b68fSJohn Baldwin 			KASSERT(mbufq_empty(&rap->rxa_mq[0]), ("unexpected dup"));
107968e8e04eSSam Leffler 			ampdu_dispatch(ni, m);
108068e8e04eSSam Leffler 			ampdu_rx_dispatch(rap, ni);
10811b6167d2SSam Leffler 			return CONSUMED;
108268e8e04eSSam Leffler 		} else {
108368e8e04eSSam Leffler 			/*
1084a67acf11SAdrian Chadd 			 * In order; advance window if needed and notify
108568e8e04eSSam Leffler 			 * caller to dispatch directly.
108668e8e04eSSam Leffler 			 */
1087e81d9092SAdrian Chadd 			if (amsdu) {
1088e81d9092SAdrian Chadd 				if (amsdu_end) {
108968e8e04eSSam Leffler 					rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
1090e81d9092SAdrian Chadd 					IEEE80211_NODE_STAT(ni, rx_amsdu_more_end);
1091e81d9092SAdrian Chadd 				} else {
1092e81d9092SAdrian Chadd 					IEEE80211_NODE_STAT(ni, rx_amsdu_more);
1093e81d9092SAdrian Chadd 				}
1094e81d9092SAdrian Chadd 			} else {
1095e81d9092SAdrian Chadd 				rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
1096e81d9092SAdrian Chadd 			}
10971b6167d2SSam Leffler 			return PROCESS;
109868e8e04eSSam Leffler 		}
109968e8e04eSSam Leffler 	}
110068e8e04eSSam Leffler 	/*
11011b6167d2SSam Leffler 	 * Frame is out of order; store if in the BA window.
110268e8e04eSSam Leffler 	 */
110368e8e04eSSam Leffler 	/* calculate offset in BA window */
110468e8e04eSSam Leffler 	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
11051b6167d2SSam Leffler 	if (off < rap->rxa_wnd) {
110668e8e04eSSam Leffler 		/*
11071b6167d2SSam Leffler 		 * Common case (hopefully): in the BA window.
11080917631fSRui Paulo 		 * Sec 9.10.7.6.2 a) (p.137)
11091b6167d2SSam Leffler 		 */
11106548c62cSAdrian Chadd 
11111b6167d2SSam Leffler 		/*
11121b6167d2SSam Leffler 		 * Check for frames sitting too long in the reorder queue.
11131b6167d2SSam Leffler 		 * This should only ever happen if frames are not delivered
11141b6167d2SSam Leffler 		 * without the sender otherwise notifying us (e.g. with a
11151b6167d2SSam Leffler 		 * BAR to move the window).  Typically this happens because
11161b6167d2SSam Leffler 		 * of vendor bugs that cause the sequence number to jump.
11171b6167d2SSam Leffler 		 * When this happens we get a gap in the reorder queue that
11181b6167d2SSam Leffler 		 * leaves frame sitting on the queue until they get pushed
11191b6167d2SSam Leffler 		 * out due to window moves.  When the vendor does not send
11201b6167d2SSam Leffler 		 * BAR this move only happens due to explicit packet sends
112168e8e04eSSam Leffler 		 *
11221b6167d2SSam Leffler 		 * NB: we only track the time of the oldest frame in the
11231b6167d2SSam Leffler 		 * reorder q; this means that if we flush we might push
11241b6167d2SSam Leffler 		 * frames that still "new"; if this happens then subsequent
11251b6167d2SSam Leffler 		 * frames will result in BA window moves which cost something
11261b6167d2SSam Leffler 		 * but is still better than a big throughput dip.
112768e8e04eSSam Leffler 		 */
112868e8e04eSSam Leffler 		if (rap->rxa_qframes != 0) {
112968e8e04eSSam Leffler 			/* XXX honor batimeout? */
11301b6167d2SSam Leffler 			if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
113168e8e04eSSam Leffler 				/*
11321b6167d2SSam Leffler 				 * Too long since we received the first
11331b6167d2SSam Leffler 				 * frame; flush the reorder buffer.
113468e8e04eSSam Leffler 				 */
113568e8e04eSSam Leffler 				if (rap->rxa_qframes != 0) {
1136b032f27cSSam Leffler 					vap->iv_stats.is_ampdu_rx_age +=
113768e8e04eSSam Leffler 					    rap->rxa_qframes;
11381b6167d2SSam Leffler 					ampdu_rx_flush(ni, rap);
113968e8e04eSSam Leffler 				}
1140e81d9092SAdrian Chadd 				/*
1141e81d9092SAdrian Chadd 				 * Advance the window if needed and notify
1142e81d9092SAdrian Chadd 				 * the caller to dispatch directly.
1143e81d9092SAdrian Chadd 				 */
1144e81d9092SAdrian Chadd 				if (amsdu) {
1145e81d9092SAdrian Chadd 					if (amsdu_end) {
1146e81d9092SAdrian Chadd 						rap->rxa_start =
1147e81d9092SAdrian Chadd 						    IEEE80211_SEQ_INC(rxseq);
1148e81d9092SAdrian Chadd 						IEEE80211_NODE_STAT(ni,
1149e81d9092SAdrian Chadd 						    rx_amsdu_more_end);
1150e81d9092SAdrian Chadd 					} else {
1151e81d9092SAdrian Chadd 						IEEE80211_NODE_STAT(ni,
1152e81d9092SAdrian Chadd 						    rx_amsdu_more);
1153e81d9092SAdrian Chadd 					}
1154e81d9092SAdrian Chadd 				} else {
1155e81d9092SAdrian Chadd 					rap->rxa_start =
1156e81d9092SAdrian Chadd 					    IEEE80211_SEQ_INC(rxseq);
1157e81d9092SAdrian Chadd 				}
11581b6167d2SSam Leffler 				return PROCESS;
115968e8e04eSSam Leffler 			}
116068e8e04eSSam Leffler 		} else {
116168e8e04eSSam Leffler 			/*
116268e8e04eSSam Leffler 			 * First frame, start aging timer.
116368e8e04eSSam Leffler 			 */
11641b6167d2SSam Leffler 			rap->rxa_age = ticks;
116568e8e04eSSam Leffler 		}
11666548c62cSAdrian Chadd 
116782bd08eeSAdrian Chadd 		/* save packet - this consumes, no matter what */
1168e81d9092SAdrian Chadd 		ampdu_rx_add_slot(rap, off, tid, rxseq, ni, m, rxs);
11691b6167d2SSam Leffler 		return CONSUMED;
11701b6167d2SSam Leffler 	}
11711b6167d2SSam Leffler 	if (off < IEEE80211_SEQ_BA_RANGE) {
11721b6167d2SSam Leffler 		/*
11731b6167d2SSam Leffler 		 * Outside the BA window, but within range;
11741b6167d2SSam Leffler 		 * flush the reorder q and move the window.
11750917631fSRui Paulo 		 * Sec 9.10.7.6.2 b) (p.138)
11761b6167d2SSam Leffler 		 */
1177b032f27cSSam Leffler 		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
11781b6167d2SSam Leffler 		    "move BA win <%u:%u> (%u frames) rxseq %u tid %u",
11791b6167d2SSam Leffler 		    rap->rxa_start,
11801b6167d2SSam Leffler 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
11811b6167d2SSam Leffler 		    rap->rxa_qframes, rxseq, tid);
1182b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_rx_move++;
11831b6167d2SSam Leffler 
11841b6167d2SSam Leffler 		/*
11851b6167d2SSam Leffler 		 * The spec says to flush frames up to but not including:
11861b6167d2SSam Leffler 		 * 	WinStart_B = rxseq - rap->rxa_wnd + 1
11871b6167d2SSam Leffler 		 * Then insert the frame or notify the caller to process
11881b6167d2SSam Leffler 		 * it immediately.  We can safely do this by just starting
11891b6167d2SSam Leffler 		 * over again because we know the frame will now be within
11901b6167d2SSam Leffler 		 * the BA window.
11911b6167d2SSam Leffler 		 */
11921b6167d2SSam Leffler 		/* NB: rxa_wnd known to be >0 */
11931b6167d2SSam Leffler 		ampdu_rx_flush_upto(ni, rap,
11941b6167d2SSam Leffler 		    IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
11951b6167d2SSam Leffler 		goto again;
11961b6167d2SSam Leffler 	} else {
11971b6167d2SSam Leffler 		/*
11981b6167d2SSam Leffler 		 * Outside the BA window and out of range; toss.
11990917631fSRui Paulo 		 * Sec 9.10.7.6.2 c) (p.138)
12001b6167d2SSam Leffler 		 */
1201b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap,
12021b6167d2SSam Leffler 		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
1203b032f27cSSam Leffler 		    "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
12041b6167d2SSam Leffler 		    rap->rxa_start,
12051b6167d2SSam Leffler 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
12061b6167d2SSam Leffler 		    rap->rxa_qframes, rxseq, tid,
12071b6167d2SSam Leffler 		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
1208b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_rx_drop++;
12091b6167d2SSam Leffler 		IEEE80211_NODE_STAT(ni, rx_drop);
12101b6167d2SSam Leffler 		m_freem(m);
12111b6167d2SSam Leffler 		return CONSUMED;
12121b6167d2SSam Leffler 	}
12131b6167d2SSam Leffler #undef CONSUMED
12141b6167d2SSam Leffler #undef PROCESS
121568e8e04eSSam Leffler }
121668e8e04eSSam Leffler 
121768e8e04eSSam Leffler /*
121868e8e04eSSam Leffler  * Process a BAR ctl frame.  Dispatch all frames up to
121968e8e04eSSam Leffler  * the sequence number of the frame.  If this frame is
12201b6167d2SSam Leffler  * out of range it's discarded.
122168e8e04eSSam Leffler  */
122268e8e04eSSam Leffler void
122368e8e04eSSam Leffler ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
122468e8e04eSSam Leffler {
1225b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
122668e8e04eSSam Leffler 	struct ieee80211_frame_bar *wh;
122768e8e04eSSam Leffler 	struct ieee80211_rx_ampdu *rap;
122868e8e04eSSam Leffler 	ieee80211_seq rxseq;
122968e8e04eSSam Leffler 	int tid, off;
123068e8e04eSSam Leffler 
12311b6167d2SSam Leffler 	if (!ieee80211_recv_bar_ena) {
12321b6167d2SSam Leffler #if 0
1233b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N,
12341b6167d2SSam Leffler 		    ni->ni_macaddr, "BAR", "%s", "processing disabled");
12351b6167d2SSam Leffler #endif
1236b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_bar_bad++;
12371b6167d2SSam Leffler 		return;
12381b6167d2SSam Leffler 	}
123968e8e04eSSam Leffler 	wh = mtod(m0, struct ieee80211_frame_bar *);
124068e8e04eSSam Leffler 	/* XXX check basic BAR */
1241fe5ebb23SBjoern A. Zeeb 	tid = _IEEE80211_MASKSHIFT(le16toh(wh->i_ctl), IEEE80211_BAR_TID);
124268e8e04eSSam Leffler 	rap = &ni->ni_rx_ampdu[tid];
124368e8e04eSSam Leffler 	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
124468e8e04eSSam Leffler 		/*
124568e8e04eSSam Leffler 		 * No ADDBA request yet, don't touch.
124668e8e04eSSam Leffler 		 */
1247b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap,
124868e8e04eSSam Leffler 		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
124968e8e04eSSam Leffler 		    ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
1250b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_bar_bad++;
125168e8e04eSSam Leffler 		return;
125268e8e04eSSam Leffler 	}
1253b032f27cSSam Leffler 	vap->iv_stats.is_ampdu_bar_rx++;
125468e8e04eSSam Leffler 	rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
12551b6167d2SSam Leffler 	if (rxseq == rap->rxa_start)
12561b6167d2SSam Leffler 		return;
125768e8e04eSSam Leffler 	/* calculate offset in BA window */
125868e8e04eSSam Leffler 	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
12591b6167d2SSam Leffler 	if (off < IEEE80211_SEQ_BA_RANGE) {
12601b6167d2SSam Leffler 		/*
12611b6167d2SSam Leffler 		 * Flush the reorder q up to rxseq and move the window.
12620917631fSRui Paulo 		 * Sec 9.10.7.6.3 a) (p.138)
12631b6167d2SSam Leffler 		 */
1264b032f27cSSam Leffler 		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
12651b6167d2SSam Leffler 		    "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
12661b6167d2SSam Leffler 		    rap->rxa_start,
12671b6167d2SSam Leffler 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
12681b6167d2SSam Leffler 		    rap->rxa_qframes, rxseq, tid);
1269b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_bar_move++;
12701b6167d2SSam Leffler 
12711b6167d2SSam Leffler 		ampdu_rx_flush_upto(ni, rap, rxseq);
127268e8e04eSSam Leffler 		if (off >= rap->rxa_wnd) {
127368e8e04eSSam Leffler 			/*
12741b6167d2SSam Leffler 			 * BAR specifies a window start to the right of BA
12751b6167d2SSam Leffler 			 * window; we must move it explicitly since
12761b6167d2SSam Leffler 			 * ampdu_rx_flush_upto will not.
127768e8e04eSSam Leffler 			 */
127868e8e04eSSam Leffler 			rap->rxa_start = rxseq;
12791b6167d2SSam Leffler 		}
12801b6167d2SSam Leffler 	} else {
12811b6167d2SSam Leffler 		/*
12821b6167d2SSam Leffler 		 * Out of range; toss.
12830917631fSRui Paulo 		 * Sec 9.10.7.6.3 b) (p.138)
12841b6167d2SSam Leffler 		 */
1285b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap,
12861b6167d2SSam Leffler 		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
12871b6167d2SSam Leffler 		    "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
12881b6167d2SSam Leffler 		    rap->rxa_start,
12891b6167d2SSam Leffler 		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
12901b6167d2SSam Leffler 		    rap->rxa_qframes, rxseq, tid,
12911b6167d2SSam Leffler 		    wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
1292b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_bar_oow++;
12931b6167d2SSam Leffler 		IEEE80211_NODE_STAT(ni, rx_drop);
12941b6167d2SSam Leffler 	}
129568e8e04eSSam Leffler }
129668e8e04eSSam Leffler 
129768e8e04eSSam Leffler /*
129868e8e04eSSam Leffler  * Setup HT-specific state in a node.  Called only
129968e8e04eSSam Leffler  * when HT use is negotiated so we don't do extra
130068e8e04eSSam Leffler  * work for temporary and/or legacy sta's.
130168e8e04eSSam Leffler  */
130268e8e04eSSam Leffler void
1303fdabd982SSam Leffler ieee80211_ht_node_init(struct ieee80211_node *ni)
130468e8e04eSSam Leffler {
130568e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap;
13062aa563dfSAdrian Chadd 	int tid;
130768e8e04eSSam Leffler 
13080ef1bc21SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
13090ef1bc21SAdrian Chadd 	    ni,
13105cf581e2SAdrian Chadd 	    "%s: called (%p)",
13115cf581e2SAdrian Chadd 	    __func__,
13125cf581e2SAdrian Chadd 	    ni);
13130ef1bc21SAdrian Chadd 
13141b6167d2SSam Leffler 	if (ni->ni_flags & IEEE80211_NODE_HT) {
13151b6167d2SSam Leffler 		/*
13161b6167d2SSam Leffler 		 * Clean AMPDU state on re-associate.  This handles the case
13171b6167d2SSam Leffler 		 * where a station leaves w/o notifying us and then returns
13181b6167d2SSam Leffler 		 * before node is reaped for inactivity.
13191b6167d2SSam Leffler 		 */
13200ef1bc21SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
13210ef1bc21SAdrian Chadd 		    ni,
13225cf581e2SAdrian Chadd 		    "%s: calling cleanup (%p)",
13235cf581e2SAdrian Chadd 		    __func__, ni);
13241b6167d2SSam Leffler 		ieee80211_ht_node_cleanup(ni);
13251b6167d2SSam Leffler 	}
13262aa563dfSAdrian Chadd 	for (tid = 0; tid < WME_NUM_TID; tid++) {
13272aa563dfSAdrian Chadd 		tap = &ni->ni_tx_ampdu[tid];
13282aa563dfSAdrian Chadd 		tap->txa_tid = tid;
1329cc71a422SSam Leffler 		tap->txa_ni = ni;
1330d7621b8cSAdrian Chadd 		ieee80211_txampdu_init_pps(tap);
13311b6167d2SSam Leffler 		/* NB: further initialization deferred */
1332e81d9092SAdrian Chadd 		ieee80211_ampdu_rx_init_rap(ni, &ni->ni_rx_ampdu[tid]);
133368e8e04eSSam Leffler 	}
1334ebb9b256SAdrian Chadd 	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU |
1335ebb9b256SAdrian Chadd 	    IEEE80211_NODE_AMSDU;
133668e8e04eSSam Leffler }
133768e8e04eSSam Leffler 
133868e8e04eSSam Leffler /*
133968e8e04eSSam Leffler  * Cleanup HT-specific state in a node.  Called only
134068e8e04eSSam Leffler  * when HT use has been marked.
134168e8e04eSSam Leffler  */
134268e8e04eSSam Leffler void
134368e8e04eSSam Leffler ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
134468e8e04eSSam Leffler {
134551b5aba2SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
134668e8e04eSSam Leffler 	int i;
134768e8e04eSSam Leffler 
13480ef1bc21SAdrian Chadd 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
13490ef1bc21SAdrian Chadd 	    ni,
13505cf581e2SAdrian Chadd 	    "%s: called (%p)",
13515cf581e2SAdrian Chadd 	    __func__, ni);
13520ef1bc21SAdrian Chadd 
135368e8e04eSSam Leffler 	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
135468e8e04eSSam Leffler 
135568e8e04eSSam Leffler 	/* XXX optimize this */
13562aa563dfSAdrian Chadd 	for (i = 0; i < WME_NUM_TID; i++) {
135768e8e04eSSam Leffler 		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
1358cc71a422SSam Leffler 		if (tap->txa_flags & IEEE80211_AGGR_SETUP)
1359cc71a422SSam Leffler 			ampdu_tx_stop(tap);
136068e8e04eSSam Leffler 	}
136168e8e04eSSam Leffler 	for (i = 0; i < WME_NUM_TID; i++)
136251b5aba2SSam Leffler 		ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]);
136368e8e04eSSam Leffler 
136468e8e04eSSam Leffler 	ni->ni_htcap = 0;
13659c62b7d4SSam Leffler 	ni->ni_flags &= ~IEEE80211_NODE_HT_ALL;
13661b6167d2SSam Leffler }
13671b6167d2SSam Leffler 
1368b032f27cSSam Leffler /*
1369b032f27cSSam Leffler  * Age out HT resources for a station.
1370b032f27cSSam Leffler  */
1371b032f27cSSam Leffler void
1372b032f27cSSam Leffler ieee80211_ht_node_age(struct ieee80211_node *ni)
1373b032f27cSSam Leffler {
1374b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
1375b032f27cSSam Leffler 	uint8_t tid;
1376b032f27cSSam Leffler 
1377b032f27cSSam Leffler 	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
1378b032f27cSSam Leffler 
1379b032f27cSSam Leffler 	for (tid = 0; tid < WME_NUM_TID; tid++) {
1380b032f27cSSam Leffler 		struct ieee80211_rx_ampdu *rap;
1381b032f27cSSam Leffler 
1382b032f27cSSam Leffler 		rap = &ni->ni_rx_ampdu[tid];
1383b032f27cSSam Leffler 		if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
1384b032f27cSSam Leffler 			continue;
1385b032f27cSSam Leffler 		if (rap->rxa_qframes == 0)
1386b032f27cSSam Leffler 			continue;
1387b032f27cSSam Leffler 		/*
1388b032f27cSSam Leffler 		 * Check for frames sitting too long in the reorder queue.
1389b032f27cSSam Leffler 		 * See above for more details on what's happening here.
1390b032f27cSSam Leffler 		 */
1391b032f27cSSam Leffler 		/* XXX honor batimeout? */
1392b032f27cSSam Leffler 		if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
1393b032f27cSSam Leffler 			/*
1394b032f27cSSam Leffler 			 * Too long since we received the first
1395b032f27cSSam Leffler 			 * frame; flush the reorder buffer.
1396b032f27cSSam Leffler 			 */
1397b032f27cSSam Leffler 			vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
1398b032f27cSSam Leffler 			ampdu_rx_flush(ni, rap);
1399b032f27cSSam Leffler 		}
1400b032f27cSSam Leffler 	}
1401b032f27cSSam Leffler }
1402b032f27cSSam Leffler 
14031b6167d2SSam Leffler static struct ieee80211_channel *
14041b6167d2SSam Leffler findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
14051b6167d2SSam Leffler {
14061b6167d2SSam Leffler 	return ieee80211_find_channel(ic, c->ic_freq,
14071b6167d2SSam Leffler 	    (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags);
14081b6167d2SSam Leffler }
14091b6167d2SSam Leffler 
14101b6167d2SSam Leffler /*
14111b6167d2SSam Leffler  * Adjust a channel to be HT/non-HT according to the vap's configuration.
14121b6167d2SSam Leffler  */
14131b6167d2SSam Leffler struct ieee80211_channel *
14141b6167d2SSam Leffler ieee80211_ht_adjust_channel(struct ieee80211com *ic,
14151b6167d2SSam Leffler 	struct ieee80211_channel *chan, int flags)
14161b6167d2SSam Leffler {
14171b6167d2SSam Leffler 	struct ieee80211_channel *c;
14181b6167d2SSam Leffler 
14192bfc8a91SSam Leffler 	if (flags & IEEE80211_FHT_HT) {
14201b6167d2SSam Leffler 		/* promote to HT if possible */
14212bfc8a91SSam Leffler 		if (flags & IEEE80211_FHT_USEHT40) {
14221b6167d2SSam Leffler 			if (!IEEE80211_IS_CHAN_HT40(chan)) {
14231b6167d2SSam Leffler 				/* NB: arbitrarily pick ht40+ over ht40- */
14241b6167d2SSam Leffler 				c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
14251b6167d2SSam Leffler 				if (c == NULL)
14261b6167d2SSam Leffler 					c = findhtchan(ic, chan,
14271b6167d2SSam Leffler 						IEEE80211_CHAN_HT40D);
14281b6167d2SSam Leffler 				if (c == NULL)
14291b6167d2SSam Leffler 					c = findhtchan(ic, chan,
14301b6167d2SSam Leffler 						IEEE80211_CHAN_HT20);
14311b6167d2SSam Leffler 				if (c != NULL)
14321b6167d2SSam Leffler 					chan = c;
14331b6167d2SSam Leffler 			}
14341b6167d2SSam Leffler 		} else if (!IEEE80211_IS_CHAN_HT20(chan)) {
14351b6167d2SSam Leffler 			c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
14361b6167d2SSam Leffler 			if (c != NULL)
14371b6167d2SSam Leffler 				chan = c;
14381b6167d2SSam Leffler 		}
14391b6167d2SSam Leffler 	} else if (IEEE80211_IS_CHAN_HT(chan)) {
14401b6167d2SSam Leffler 		/* demote to legacy, HT use is disabled */
14411b6167d2SSam Leffler 		c = ieee80211_find_channel(ic, chan->ic_freq,
14421b6167d2SSam Leffler 		    chan->ic_flags &~ IEEE80211_CHAN_HT);
14431b6167d2SSam Leffler 		if (c != NULL)
14441b6167d2SSam Leffler 			chan = c;
14451b6167d2SSam Leffler 	}
14461b6167d2SSam Leffler 	return chan;
14471b6167d2SSam Leffler }
14481b6167d2SSam Leffler 
14491b6167d2SSam Leffler /*
14501b6167d2SSam Leffler  * Setup HT-specific state for a legacy WDS peer.
14511b6167d2SSam Leffler  */
14521b6167d2SSam Leffler void
14531b6167d2SSam Leffler ieee80211_ht_wds_init(struct ieee80211_node *ni)
14541b6167d2SSam Leffler {
1455b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
14561b6167d2SSam Leffler 	struct ieee80211_tx_ampdu *tap;
14572aa563dfSAdrian Chadd 	int tid;
14581b6167d2SSam Leffler 
14592bfc8a91SSam Leffler 	KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested"));
14601b6167d2SSam Leffler 
14611b6167d2SSam Leffler 	/* XXX check scan cache in case peer has an ap and we have info */
14621b6167d2SSam Leffler 	/*
14631b6167d2SSam Leffler 	 * If setup with a legacy channel; locate an HT channel.
14641b6167d2SSam Leffler 	 * Otherwise if the inherited channel (from a companion
14651b6167d2SSam Leffler 	 * AP) is suitable use it so we use the same location
14661b6167d2SSam Leffler 	 * for the extension channel).
14671b6167d2SSam Leffler 	 */
1468b032f27cSSam Leffler 	ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic,
1469b032f27cSSam Leffler 	    ni->ni_chan, ieee80211_htchanflags(ni->ni_chan));
14701b6167d2SSam Leffler 
14711b6167d2SSam Leffler 	ni->ni_htcap = 0;
14722bfc8a91SSam Leffler 	if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)
14731b6167d2SSam Leffler 		ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
14741b6167d2SSam Leffler 	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
14751b6167d2SSam Leffler 		ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
1476ca389486SBjoern A. Zeeb 		ni->ni_chw = IEEE80211_STA_RX_BW_40;
14771b6167d2SSam Leffler 		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
14781b6167d2SSam Leffler 			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
14791b6167d2SSam Leffler 		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
14801b6167d2SSam Leffler 			ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
14812bfc8a91SSam Leffler 		if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)
14821b6167d2SSam Leffler 			ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
14831b6167d2SSam Leffler 	} else {
1484ca389486SBjoern A. Zeeb 		ni->ni_chw = IEEE80211_STA_RX_BW_20;
14851b6167d2SSam Leffler 		ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
14861b6167d2SSam Leffler 	}
14871b6167d2SSam Leffler 	ni->ni_htctlchan = ni->ni_chan->ic_ieee;
14882bfc8a91SSam Leffler 	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
148944f7a6edSSam Leffler 		ni->ni_flags |= IEEE80211_NODE_RIFS;
149044f7a6edSSam Leffler 	/* XXX does it make sense to enable SMPS? */
14911b6167d2SSam Leffler 
14921b6167d2SSam Leffler 	ni->ni_htopmode = 0;		/* XXX need protection state */
14931b6167d2SSam Leffler 	ni->ni_htstbc = 0;		/* XXX need info */
14941b6167d2SSam Leffler 
14952aa563dfSAdrian Chadd 	for (tid = 0; tid < WME_NUM_TID; tid++) {
14962aa563dfSAdrian Chadd 		tap = &ni->ni_tx_ampdu[tid];
14972aa563dfSAdrian Chadd 		tap->txa_tid = tid;
1498d7621b8cSAdrian Chadd 		ieee80211_txampdu_init_pps(tap);
14991b6167d2SSam Leffler 	}
15002bfc8a91SSam Leffler 	/* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */
1501ebb9b256SAdrian Chadd 	ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU |
1502ebb9b256SAdrian Chadd 	    IEEE80211_NODE_AMSDU;
15031b6167d2SSam Leffler }
15041b6167d2SSam Leffler 
15051b6167d2SSam Leffler /*
1506f1481c8dSAdrian Chadd  * Notify a VAP of a change in the HTINFO ie if it's a hostap VAP.
1507f1481c8dSAdrian Chadd  *
1508f1481c8dSAdrian Chadd  * This is to be called from the deferred HT protection update
1509f1481c8dSAdrian Chadd  * task once the flags are updated.
15101b6167d2SSam Leffler  */
1511f1481c8dSAdrian Chadd void
1512f1481c8dSAdrian Chadd ieee80211_htinfo_notify(struct ieee80211vap *vap)
15131b6167d2SSam Leffler {
1514b032f27cSSam Leffler 
1515f1481c8dSAdrian Chadd 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
1516b032f27cSSam Leffler 
1517b032f27cSSam Leffler 	if (vap->iv_opmode != IEEE80211_M_HOSTAP)
1518f1481c8dSAdrian Chadd 		return;
1519bd985970SSam Leffler 	if (vap->iv_state != IEEE80211_S_RUN ||
1520bd985970SSam Leffler 	    !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
1521f1481c8dSAdrian Chadd 		return;
1522f1481c8dSAdrian Chadd 
1523b032f27cSSam Leffler 	IEEE80211_NOTE(vap,
15241b6167d2SSam Leffler 	    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
1525b032f27cSSam Leffler 	    vap->iv_bss,
15261b6167d2SSam Leffler 	    "HT bss occupancy change: %d sta, %d ht, "
15271b6167d2SSam Leffler 	    "%d ht40%s, HT protmode now 0x%x"
1528f1481c8dSAdrian Chadd 	    , vap->iv_sta_assoc
1529f1481c8dSAdrian Chadd 	    , vap->iv_ht_sta_assoc
1530f1481c8dSAdrian Chadd 	    , vap->iv_ht40_sta_assoc
1531f1481c8dSAdrian Chadd 	    , (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) ?
15321b6167d2SSam Leffler 		 ", non-HT sta present" : ""
1533f1481c8dSAdrian Chadd 	    , vap->iv_curhtprotmode);
1534f1481c8dSAdrian Chadd 
1535b032f27cSSam Leffler 	ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
1536b032f27cSSam Leffler }
15371b6167d2SSam Leffler 
15381b6167d2SSam Leffler /*
15391b6167d2SSam Leffler  * Calculate HT protection mode from current
15401b6167d2SSam Leffler  * state and handle updates.
15411b6167d2SSam Leffler  */
15421b6167d2SSam Leffler static void
1543f1481c8dSAdrian Chadd htinfo_update(struct ieee80211vap *vap)
15441b6167d2SSam Leffler {
1545f1481c8dSAdrian Chadd 	struct ieee80211com *ic = vap->iv_ic;
15461b6167d2SSam Leffler 	uint8_t protmode;
15471b6167d2SSam Leffler 
1548f1481c8dSAdrian Chadd 	if (vap->iv_sta_assoc != vap->iv_ht_sta_assoc) {
15491b6167d2SSam Leffler 		protmode = IEEE80211_HTINFO_OPMODE_MIXED
15501b6167d2SSam Leffler 			 | IEEE80211_HTINFO_NONHT_PRESENT;
1551f1481c8dSAdrian Chadd 	} else if (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) {
1552b032f27cSSam Leffler 		protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
1553b032f27cSSam Leffler 			 | IEEE80211_HTINFO_NONHT_PRESENT;
1554b032f27cSSam Leffler 	} else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
1555b032f27cSSam Leffler 	    IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
1556f1481c8dSAdrian Chadd 	    vap->iv_sta_assoc != vap->iv_ht40_sta_assoc) {
15571b6167d2SSam Leffler 		protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
15581b6167d2SSam Leffler 	} else {
15591b6167d2SSam Leffler 		protmode = IEEE80211_HTINFO_OPMODE_PURE;
15601b6167d2SSam Leffler 	}
1561f1481c8dSAdrian Chadd 	if (protmode != vap->iv_curhtprotmode) {
1562f1481c8dSAdrian Chadd 		vap->iv_curhtprotmode = protmode;
1563f1481c8dSAdrian Chadd 		/* Update VAP with new protection mode */
1564f1481c8dSAdrian Chadd 		ieee80211_vap_update_ht_protmode(vap);
15651b6167d2SSam Leffler 	}
15661b6167d2SSam Leffler }
15671b6167d2SSam Leffler 
15681b6167d2SSam Leffler /*
15691b6167d2SSam Leffler  * Handle an HT station joining a BSS.
15701b6167d2SSam Leffler  */
15711b6167d2SSam Leffler void
15721b6167d2SSam Leffler ieee80211_ht_node_join(struct ieee80211_node *ni)
15731b6167d2SSam Leffler {
1574f1481c8dSAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
15751b6167d2SSam Leffler 
1576f1481c8dSAdrian Chadd 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
15771b6167d2SSam Leffler 
15781b6167d2SSam Leffler 	if (ni->ni_flags & IEEE80211_NODE_HT) {
1579f1481c8dSAdrian Chadd 		vap->iv_ht_sta_assoc++;
1580ca389486SBjoern A. Zeeb 		if (ni->ni_chw == IEEE80211_STA_RX_BW_40)
1581f1481c8dSAdrian Chadd 			vap->iv_ht40_sta_assoc++;
15821b6167d2SSam Leffler 	}
1583f1481c8dSAdrian Chadd 	htinfo_update(vap);
15841b6167d2SSam Leffler }
15851b6167d2SSam Leffler 
15861b6167d2SSam Leffler /*
15871b6167d2SSam Leffler  * Handle an HT station leaving a BSS.
15881b6167d2SSam Leffler  */
15891b6167d2SSam Leffler void
15901b6167d2SSam Leffler ieee80211_ht_node_leave(struct ieee80211_node *ni)
15911b6167d2SSam Leffler {
1592f1481c8dSAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
15931b6167d2SSam Leffler 
1594f1481c8dSAdrian Chadd 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
15951b6167d2SSam Leffler 
15961b6167d2SSam Leffler 	if (ni->ni_flags & IEEE80211_NODE_HT) {
1597f1481c8dSAdrian Chadd 		vap->iv_ht_sta_assoc--;
1598ca389486SBjoern A. Zeeb 		if (ni->ni_chw == IEEE80211_STA_RX_BW_40)
1599f1481c8dSAdrian Chadd 			vap->iv_ht40_sta_assoc--;
16001b6167d2SSam Leffler 	}
1601f1481c8dSAdrian Chadd 	htinfo_update(vap);
16021b6167d2SSam Leffler }
16031b6167d2SSam Leffler 
16041b6167d2SSam Leffler /*
16051b6167d2SSam Leffler  * Public version of htinfo_update; used for processing
1606b032f27cSSam Leffler  * beacon frames from overlapping bss.
1607b032f27cSSam Leffler  *
1608b032f27cSSam Leffler  * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
1609b032f27cSSam Leffler  * (on receipt of a beacon that advertises MIXED) or
1610b032f27cSSam Leffler  * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
1611b032f27cSSam Leffler  * from an overlapping legacy bss).  We treat MIXED with
1612b032f27cSSam Leffler  * a higher precedence than PROTOPT (i.e. we will not change
1613b032f27cSSam Leffler  * change PROTOPT -> MIXED; only MIXED -> PROTOPT).  This
1614b032f27cSSam Leffler  * corresponds to how we handle things in htinfo_update.
1615f1481c8dSAdrian Chadd  *
16161b6167d2SSam Leffler  */
16171b6167d2SSam Leffler void
1618f1481c8dSAdrian Chadd ieee80211_htprot_update(struct ieee80211vap *vap, int protmode)
16191b6167d2SSam Leffler {
1620f1481c8dSAdrian Chadd 	struct ieee80211com *ic = vap->iv_ic;
1621fe5ebb23SBjoern A. Zeeb #define	OPMODE(x)	_IEEE80211_SHIFTMASK(x, IEEE80211_HTINFO_OPMODE)
1622d3d6c952SSam Leffler 	IEEE80211_LOCK(ic);
1623b69b7fe7SSam Leffler 
1624b032f27cSSam Leffler 	/* track non-HT station presence */
1625b032f27cSSam Leffler 	KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
1626b69b7fe7SSam Leffler 	    ("protmode 0x%x", protmode));
1627f1481c8dSAdrian Chadd 	vap->iv_flags_ht |= IEEE80211_FHT_NONHT_PR;
1628f1481c8dSAdrian Chadd 	vap->iv_lastnonht = ticks;
1629b032f27cSSam Leffler 
1630f1481c8dSAdrian Chadd 	if (protmode != vap->iv_curhtprotmode &&
1631f1481c8dSAdrian Chadd 	    (OPMODE(vap->iv_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
1632b69b7fe7SSam Leffler 	     OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) {
1633f1481c8dSAdrian Chadd 		vap->iv_curhtprotmode = protmode;
1634f1481c8dSAdrian Chadd 		/* Update VAP with new protection mode */
1635f1481c8dSAdrian Chadd 		ieee80211_vap_update_ht_protmode(vap);
1636b69b7fe7SSam Leffler 	}
1637d3d6c952SSam Leffler 	IEEE80211_UNLOCK(ic);
1638b032f27cSSam Leffler #undef OPMODE
16391b6167d2SSam Leffler }
16401b6167d2SSam Leffler 
16411b6167d2SSam Leffler /*
16421b6167d2SSam Leffler  * Time out presence of an overlapping bss with non-HT
16431b6167d2SSam Leffler  * stations.  When operating in hostap mode we listen for
16441b6167d2SSam Leffler  * beacons from other stations and if we identify a non-HT
16451b6167d2SSam Leffler  * station is present we update the opmode field of the
16461b6167d2SSam Leffler  * HTINFO ie.  To identify when all non-HT stations are
16471b6167d2SSam Leffler  * gone we time out this condition.
16481b6167d2SSam Leffler  */
16491b6167d2SSam Leffler void
1650f1481c8dSAdrian Chadd ieee80211_ht_timeout(struct ieee80211vap *vap)
16511b6167d2SSam Leffler {
16521b6167d2SSam Leffler 
1653f1481c8dSAdrian Chadd 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
1654f1481c8dSAdrian Chadd 
1655f1481c8dSAdrian Chadd 	if ((vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) &&
1656f1481c8dSAdrian Chadd 	    ieee80211_time_after(ticks, vap->iv_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
1657f1481c8dSAdrian Chadd 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
16581b6167d2SSam Leffler 		    "%s", "time out non-HT STA present on channel");
1659f1481c8dSAdrian Chadd 		vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
1660f1481c8dSAdrian Chadd 		htinfo_update(vap);
16611b6167d2SSam Leffler 	}
166268e8e04eSSam Leffler }
166368e8e04eSSam Leffler 
166468e8e04eSSam Leffler /*
166568e8e04eSSam Leffler  * Process an 802.11n HT capabilities ie.
166668e8e04eSSam Leffler  */
166768e8e04eSSam Leffler void
166868e8e04eSSam Leffler ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
166968e8e04eSSam Leffler {
167068e8e04eSSam Leffler 	if (ie[0] == IEEE80211_ELEMID_VENDOR) {
167168e8e04eSSam Leffler 		/*
167268e8e04eSSam Leffler 		 * Station used Vendor OUI ie to associate;
167368e8e04eSSam Leffler 		 * mark the node so when we respond we'll use
167468e8e04eSSam Leffler 		 * the Vendor OUI's and not the standard ie's.
167568e8e04eSSam Leffler 		 */
167668e8e04eSSam Leffler 		ni->ni_flags |= IEEE80211_NODE_HTCOMPAT;
167768e8e04eSSam Leffler 		ie += 4;
167868e8e04eSSam Leffler 	} else
167968e8e04eSSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
168068e8e04eSSam Leffler 
168131021a2bSAndriy Voskoboinyk 	ni->ni_htcap = le16dec(ie +
168268e8e04eSSam Leffler 		__offsetof(struct ieee80211_ie_htcap, hc_cap));
168368e8e04eSSam Leffler 	ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
168468e8e04eSSam Leffler }
168568e8e04eSSam Leffler 
1686fdabd982SSam Leffler static void
1687fdabd982SSam Leffler htinfo_parse(struct ieee80211_node *ni,
1688fdabd982SSam Leffler 	const struct ieee80211_ie_htinfo *htinfo)
168968e8e04eSSam Leffler {
169068e8e04eSSam Leffler 	uint16_t w;
169168e8e04eSSam Leffler 
169268e8e04eSSam Leffler 	ni->ni_htctlchan = htinfo->hi_ctrlchannel;
1693fe5ebb23SBjoern A. Zeeb 	ni->ni_ht2ndchan = _IEEE80211_SHIFTMASK(htinfo->hi_byte1,
1694fe5ebb23SBjoern A. Zeeb 	    IEEE80211_HTINFO_2NDCHAN);
169531021a2bSAndriy Voskoboinyk 	w = le16dec(&htinfo->hi_byte2);
1696fe5ebb23SBjoern A. Zeeb 	ni->ni_htopmode = _IEEE80211_SHIFTMASK(w, IEEE80211_HTINFO_OPMODE);
169731021a2bSAndriy Voskoboinyk 	w = le16dec(&htinfo->hi_byte45);
1698fe5ebb23SBjoern A. Zeeb 	ni->ni_htstbc = _IEEE80211_SHIFTMASK(w, IEEE80211_HTINFO_BASIC_STBCMCS);
1699fdabd982SSam Leffler }
1700fdabd982SSam Leffler 
1701fdabd982SSam Leffler /*
1702fdabd982SSam Leffler  * Parse an 802.11n HT info ie and save useful information
1703fdabd982SSam Leffler  * to the node state.  Note this does not effect any state
1704fdabd982SSam Leffler  * changes such as for channel width change.
1705fdabd982SSam Leffler  */
1706fdabd982SSam Leffler void
1707fdabd982SSam Leffler ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
1708fdabd982SSam Leffler {
1709fdabd982SSam Leffler 	if (ie[0] == IEEE80211_ELEMID_VENDOR)
1710fdabd982SSam Leffler 		ie += 4;
1711fdabd982SSam Leffler 	htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie);
1712fdabd982SSam Leffler }
1713fdabd982SSam Leffler 
17141b6167d2SSam Leffler /*
171551172f62SAdrian Chadd  * Handle 11n/11ac channel switch.
171651172f62SAdrian Chadd  *
171751172f62SAdrian Chadd  * Use the received HT/VHT ie's to identify the right channel to use.
171851172f62SAdrian Chadd  * If we cannot locate it in the channel table then fallback to
171951172f62SAdrian Chadd  * legacy operation.
172051172f62SAdrian Chadd  *
1721fdabd982SSam Leffler  * Note that we use this information to identify the node's
1722fdabd982SSam Leffler  * channel only; the caller is responsible for insuring any
1723fdabd982SSam Leffler  * required channel change is done (e.g. in sta mode when
1724fdabd982SSam Leffler  * parsing the contents of a beacon frame).
17251b6167d2SSam Leffler  */
1726b94299c4SAdrian Chadd static int
172751172f62SAdrian Chadd htinfo_update_chw(struct ieee80211_node *ni, int htflags, int vhtflags)
1728fdabd982SSam Leffler {
1729fdabd982SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
1730fdabd982SSam Leffler 	struct ieee80211_channel *c;
1731fdabd982SSam Leffler 	int chanflags;
1732b94299c4SAdrian Chadd 	int ret = 0;
1733fdabd982SSam Leffler 
173451172f62SAdrian Chadd 	/*
173551172f62SAdrian Chadd 	 * First step - do HT/VHT only channel lookup based on operating mode
173651172f62SAdrian Chadd 	 * flags.  This involves masking out the VHT flags as well.
173751172f62SAdrian Chadd 	 * Otherwise we end up doing the full channel walk each time
173851172f62SAdrian Chadd 	 * we trigger this, which is expensive.
173951172f62SAdrian Chadd 	 */
174051172f62SAdrian Chadd 	chanflags = (ni->ni_chan->ic_flags &~
174151172f62SAdrian Chadd 	    (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags | vhtflags;
174251172f62SAdrian Chadd 
174351172f62SAdrian Chadd 	if (chanflags == ni->ni_chan->ic_flags)
174451172f62SAdrian Chadd 		goto done;
174551172f62SAdrian Chadd 
174651172f62SAdrian Chadd 	/*
174751172f62SAdrian Chadd 	 * If HT /or/ VHT flags have changed then check both.
174851172f62SAdrian Chadd 	 * We need to start by picking a HT channel anyway.
174951172f62SAdrian Chadd 	 */
175051172f62SAdrian Chadd 
175151172f62SAdrian Chadd 	c = NULL;
175251172f62SAdrian Chadd 	chanflags = (ni->ni_chan->ic_flags &~
175351172f62SAdrian Chadd 	    (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags;
1754fdabd982SSam Leffler 	/* XXX not right for ht40- */
17551b6167d2SSam Leffler 	c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
1756b032f27cSSam Leffler 	if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
17571b6167d2SSam Leffler 		/*
17581b6167d2SSam Leffler 		 * No HT40 channel entry in our table; fall back
17591b6167d2SSam Leffler 		 * to HT20 operation.  This should not happen.
17601b6167d2SSam Leffler 		 */
17611b6167d2SSam Leffler 		c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
1762fdabd982SSam Leffler #if 0
1763fdabd982SSam Leffler 		IEEE80211_NOTE(ni->ni_vap,
17641b6167d2SSam Leffler 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
17651b6167d2SSam Leffler 		    "no HT40 channel (freq %u), falling back to HT20",
17661b6167d2SSam Leffler 		    ni->ni_chan->ic_freq);
1767fdabd982SSam Leffler #endif
17681b6167d2SSam Leffler 		/* XXX stat */
17691b6167d2SSam Leffler 	}
177051172f62SAdrian Chadd 
177151172f62SAdrian Chadd 	/* Nothing found - leave it alone; move onto VHT */
177251172f62SAdrian Chadd 	if (c == NULL)
177351172f62SAdrian Chadd 		c = ni->ni_chan;
177451172f62SAdrian Chadd 
177551172f62SAdrian Chadd 	/*
177651172f62SAdrian Chadd 	 * If it's non-HT, then bail out now.
177751172f62SAdrian Chadd 	 */
177851172f62SAdrian Chadd 	if (! IEEE80211_IS_CHAN_HT(c)) {
177951172f62SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap,
178051172f62SAdrian Chadd 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
178151172f62SAdrian Chadd 		    "not HT; skipping VHT check (%u/0x%x)",
178251172f62SAdrian Chadd 		    c->ic_freq, c->ic_flags);
178351172f62SAdrian Chadd 		goto done;
178451172f62SAdrian Chadd 	}
178551172f62SAdrian Chadd 
178651172f62SAdrian Chadd 	/*
178751172f62SAdrian Chadd 	 * Next step - look at the current VHT flags and determine
178851172f62SAdrian Chadd 	 * if we need to upgrade.  Mask out the VHT and HT flags since
178951172f62SAdrian Chadd 	 * the vhtflags field will already have the correct HT
179051172f62SAdrian Chadd 	 * flags to use.
179151172f62SAdrian Chadd 	 */
179251172f62SAdrian Chadd 	if (IEEE80211_CONF_VHT(ic) && ni->ni_vhtcap != 0 && vhtflags != 0) {
179351172f62SAdrian Chadd 		chanflags = (c->ic_flags
179451172f62SAdrian Chadd 		    &~ (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT))
179551172f62SAdrian Chadd 		    | vhtflags;
179651172f62SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap,
179751172f62SAdrian Chadd 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
179851172f62SAdrian Chadd 		    ni,
179951172f62SAdrian Chadd 		    "%s: VHT; chanwidth=0x%02x; vhtflags=0x%08x",
180051172f62SAdrian Chadd 		    __func__, ni->ni_vht_chanwidth, vhtflags);
180151172f62SAdrian Chadd 
180251172f62SAdrian Chadd 		IEEE80211_NOTE(ni->ni_vap,
180351172f62SAdrian Chadd 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
180451172f62SAdrian Chadd 		    ni,
180551172f62SAdrian Chadd 		    "%s: VHT; trying lookup for %d/0x%08x",
180651172f62SAdrian Chadd 		    __func__, c->ic_freq, chanflags);
180751172f62SAdrian Chadd 		c = ieee80211_find_channel(ic, c->ic_freq, chanflags);
180851172f62SAdrian Chadd 	}
180951172f62SAdrian Chadd 
181051172f62SAdrian Chadd 	/* Finally, if it's changed */
18111b6167d2SSam Leffler 	if (c != NULL && c != ni->ni_chan) {
1812fdabd982SSam Leffler 		IEEE80211_NOTE(ni->ni_vap,
18131b6167d2SSam Leffler 		    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
181451172f62SAdrian Chadd 		    "switch station to %s%d channel %u/0x%x",
181551172f62SAdrian Chadd 		    IEEE80211_IS_CHAN_VHT(c) ? "VHT" : "HT",
181651172f62SAdrian Chadd 		    IEEE80211_IS_CHAN_VHT80(c) ? 80 :
181751172f62SAdrian Chadd 		      (IEEE80211_IS_CHAN_HT40(c) ? 40 : 20),
18181b6167d2SSam Leffler 		    c->ic_freq, c->ic_flags);
18191b6167d2SSam Leffler 		ni->ni_chan = c;
1820b94299c4SAdrian Chadd 		ret = 1;
18211b6167d2SSam Leffler 	}
18221b6167d2SSam Leffler 	/* NB: caller responsible for forcing any channel change */
182351172f62SAdrian Chadd 
182451172f62SAdrian Chadd done:
182551172f62SAdrian Chadd 	/* update node's (11n) tx channel width */
1826ca389486SBjoern A. Zeeb 	ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
1827ca389486SBjoern A. Zeeb 	    IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
1828b94299c4SAdrian Chadd 	return (ret);
182968e8e04eSSam Leffler }
183068e8e04eSSam Leffler 
183168e8e04eSSam Leffler /*
18328c070d69SSam Leffler  * Update 11n MIMO PS state according to received htcap.
18338c070d69SSam Leffler  */
18348c070d69SSam Leffler static __inline int
18358c070d69SSam Leffler htcap_update_mimo_ps(struct ieee80211_node *ni)
18368c070d69SSam Leffler {
18378c070d69SSam Leffler 	uint16_t oflags = ni->ni_flags;
18388c070d69SSam Leffler 
18398c070d69SSam Leffler 	switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
18408c070d69SSam Leffler 	case IEEE80211_HTCAP_SMPS_DYNAMIC:
18418c070d69SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
18428c070d69SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
18438c070d69SSam Leffler 		break;
18448c070d69SSam Leffler 	case IEEE80211_HTCAP_SMPS_ENA:
18458c070d69SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
18468c070d69SSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
18478c070d69SSam Leffler 		break;
18488c070d69SSam Leffler 	case IEEE80211_HTCAP_SMPS_OFF:
18498c070d69SSam Leffler 	default:		/* disable on rx of reserved value */
18508c070d69SSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
18518c070d69SSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
18528c070d69SSam Leffler 		break;
18538c070d69SSam Leffler 	}
18548c070d69SSam Leffler 	return (oflags ^ ni->ni_flags);
18558c070d69SSam Leffler }
18568c070d69SSam Leffler 
18578c070d69SSam Leffler /*
185849942a97SSam Leffler  * Update short GI state according to received htcap
185949942a97SSam Leffler  * and local settings.
186049942a97SSam Leffler  */
186149942a97SSam Leffler static __inline void
186249942a97SSam Leffler htcap_update_shortgi(struct ieee80211_node *ni)
186349942a97SSam Leffler {
186449942a97SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
186549942a97SSam Leffler 
186649942a97SSam Leffler 	ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40);
186749942a97SSam Leffler 	if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) &&
18682bfc8a91SSam Leffler 	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20))
186949942a97SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_SGI20;
187049942a97SSam Leffler 	if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) &&
18712bfc8a91SSam Leffler 	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40))
187249942a97SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_SGI40;
187349942a97SSam Leffler }
187449942a97SSam Leffler 
187549942a97SSam Leffler /*
1876c5bba9daSAndriy Voskoboinyk  * Update LDPC state according to received htcap
1877c5bba9daSAndriy Voskoboinyk  * and local settings.
1878c5bba9daSAndriy Voskoboinyk  */
1879c5bba9daSAndriy Voskoboinyk static __inline void
1880c5bba9daSAndriy Voskoboinyk htcap_update_ldpc(struct ieee80211_node *ni)
1881c5bba9daSAndriy Voskoboinyk {
1882c5bba9daSAndriy Voskoboinyk 	struct ieee80211vap *vap = ni->ni_vap;
1883c5bba9daSAndriy Voskoboinyk 
1884c5bba9daSAndriy Voskoboinyk 	if ((ni->ni_htcap & IEEE80211_HTCAP_LDPC) &&
1885c5bba9daSAndriy Voskoboinyk 	    (vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX))
1886c5bba9daSAndriy Voskoboinyk 		ni->ni_flags |= IEEE80211_NODE_LDPC;
1887c5bba9daSAndriy Voskoboinyk }
1888c5bba9daSAndriy Voskoboinyk 
1889c5bba9daSAndriy Voskoboinyk /*
1890fdabd982SSam Leffler  * Parse and update HT-related state extracted from
1891fdabd982SSam Leffler  * the HT cap and info ie's.
189251172f62SAdrian Chadd  *
189351172f62SAdrian Chadd  * This is called from the STA management path and
189451172f62SAdrian Chadd  * the ieee80211_node_join() path.  It will take into
189551172f62SAdrian Chadd  * account the IEs discovered during scanning and
189651172f62SAdrian Chadd  * adjust things accordingly.
1897fdabd982SSam Leffler  */
189851172f62SAdrian Chadd void
1899fdabd982SSam Leffler ieee80211_ht_updateparams(struct ieee80211_node *ni,
1900fdabd982SSam Leffler 	const uint8_t *htcapie, const uint8_t *htinfoie)
1901fdabd982SSam Leffler {
1902fdabd982SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
1903fdabd982SSam Leffler 	const struct ieee80211_ie_htinfo *htinfo;
1904fdabd982SSam Leffler 
1905fdabd982SSam Leffler 	ieee80211_parse_htcap(ni, htcapie);
1906f35b8451SAndriy Voskoboinyk 	if (vap->iv_htcaps & IEEE80211_HTC_SMPS)
19078c070d69SSam Leffler 		htcap_update_mimo_ps(ni);
190849942a97SSam Leffler 	htcap_update_shortgi(ni);
1909c5bba9daSAndriy Voskoboinyk 	htcap_update_ldpc(ni);
1910fdabd982SSam Leffler 
1911fdabd982SSam Leffler 	if (htinfoie[0] == IEEE80211_ELEMID_VENDOR)
1912fdabd982SSam Leffler 		htinfoie += 4;
1913fdabd982SSam Leffler 	htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
1914fdabd982SSam Leffler 	htinfo_parse(ni, htinfo);
1915fdabd982SSam Leffler 
191651172f62SAdrian Chadd 	/*
191751172f62SAdrian Chadd 	 * Defer the node channel change; we need to now
191851172f62SAdrian Chadd 	 * update VHT parameters before we do it.
191951172f62SAdrian Chadd 	 */
192051172f62SAdrian Chadd 
192151172f62SAdrian Chadd 	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
192251172f62SAdrian Chadd 	    (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
192351172f62SAdrian Chadd 		ni->ni_flags |= IEEE80211_NODE_RIFS;
192451172f62SAdrian Chadd 	else
192551172f62SAdrian Chadd 		ni->ni_flags &= ~IEEE80211_NODE_RIFS;
192651172f62SAdrian Chadd }
192751172f62SAdrian Chadd 
192851172f62SAdrian Chadd static uint32_t
192951172f62SAdrian Chadd ieee80211_vht_get_vhtflags(struct ieee80211_node *ni, uint32_t htflags)
193051172f62SAdrian Chadd {
193151172f62SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
193251172f62SAdrian Chadd 	uint32_t vhtflags = 0;
193351172f62SAdrian Chadd 
193451172f62SAdrian Chadd 	vhtflags = 0;
1935ef48d4faSBjoern A. Zeeb 	if (ni->ni_flags & IEEE80211_NODE_VHT && vap->iv_vht_flags & IEEE80211_FVHT_VHT) {
193651172f62SAdrian Chadd 		if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) &&
19375fdc4824SBjoern A. Zeeb 		    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(vap->iv_vht_cap.vht_cap_info) &&
1938ef48d4faSBjoern A. Zeeb 		    (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160)) {
193951172f62SAdrian Chadd 			vhtflags = IEEE80211_CHAN_VHT160;
194051172f62SAdrian Chadd 			/* Mirror the HT40 flags */
194151172f62SAdrian Chadd 			if (htflags == IEEE80211_CHAN_HT40U) {
194251172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40U;
194351172f62SAdrian Chadd 			} else if (htflags == IEEE80211_CHAN_HT40D) {
194451172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40D;
194551172f62SAdrian Chadd 			}
194651172f62SAdrian Chadd 		} else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80P80MHZ) &&
19475fdc4824SBjoern A. Zeeb 		    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(vap->iv_vht_cap.vht_cap_info) &&
1948ef48d4faSBjoern A. Zeeb 		    (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80)) {
1949fd6eb8feSBjoern A. Zeeb 			vhtflags = IEEE80211_CHAN_VHT80P80;
195051172f62SAdrian Chadd 			/* Mirror the HT40 flags */
195151172f62SAdrian Chadd 			if (htflags == IEEE80211_CHAN_HT40U) {
195251172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40U;
195351172f62SAdrian Chadd 			} else if (htflags == IEEE80211_CHAN_HT40D) {
195451172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40D;
195551172f62SAdrian Chadd 			}
195651172f62SAdrian Chadd 		} else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80MHZ) &&
1957ef48d4faSBjoern A. Zeeb 		    (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80)) {
195851172f62SAdrian Chadd 			vhtflags = IEEE80211_CHAN_VHT80;
195951172f62SAdrian Chadd 			/* Mirror the HT40 flags */
196051172f62SAdrian Chadd 			if (htflags == IEEE80211_CHAN_HT40U) {
196151172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40U;
196251172f62SAdrian Chadd 			} else if (htflags == IEEE80211_CHAN_HT40D) {
196351172f62SAdrian Chadd 				vhtflags |= IEEE80211_CHAN_HT40D;
196451172f62SAdrian Chadd 			}
196551172f62SAdrian Chadd 		} else if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT) {
196651172f62SAdrian Chadd 			/* Mirror the HT40 flags */
196751172f62SAdrian Chadd 			/*
196851172f62SAdrian Chadd 			 * XXX TODO: if ht40 is disabled, but vht40 isn't
196951172f62SAdrian Chadd 			 * disabled then this logic will get very, very sad.
197051172f62SAdrian Chadd 			 * It's quite possible the only sane thing to do is
197151172f62SAdrian Chadd 			 * to not have vht40 as an option, and just obey
197251172f62SAdrian Chadd 			 * 'ht40' as that flag.
197351172f62SAdrian Chadd 			 */
197451172f62SAdrian Chadd 			if ((htflags == IEEE80211_CHAN_HT40U) &&
1975ef48d4faSBjoern A. Zeeb 			    (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) {
197651172f62SAdrian Chadd 				vhtflags = IEEE80211_CHAN_VHT40U
197751172f62SAdrian Chadd 				    | IEEE80211_CHAN_HT40U;
197851172f62SAdrian Chadd 			} else if (htflags == IEEE80211_CHAN_HT40D &&
1979ef48d4faSBjoern A. Zeeb 			    (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) {
198051172f62SAdrian Chadd 				vhtflags = IEEE80211_CHAN_VHT40D
198151172f62SAdrian Chadd 				    | IEEE80211_CHAN_HT40D;
198251172f62SAdrian Chadd 			} else if (htflags == IEEE80211_CHAN_HT20) {
198351172f62SAdrian Chadd 				vhtflags = IEEE80211_CHAN_VHT20
198451172f62SAdrian Chadd 				    | IEEE80211_CHAN_HT20;
198551172f62SAdrian Chadd 			}
198651172f62SAdrian Chadd 		} else {
198751172f62SAdrian Chadd 			vhtflags = IEEE80211_CHAN_VHT20;
198851172f62SAdrian Chadd 		}
198951172f62SAdrian Chadd 	}
199051172f62SAdrian Chadd 	return (vhtflags);
199151172f62SAdrian Chadd }
199251172f62SAdrian Chadd 
199351172f62SAdrian Chadd /*
199451172f62SAdrian Chadd  * Final part of updating the HT parameters.
199551172f62SAdrian Chadd  *
199651172f62SAdrian Chadd  * This is called from the STA management path and
199751172f62SAdrian Chadd  * the ieee80211_node_join() path.  It will take into
199851172f62SAdrian Chadd  * account the IEs discovered during scanning and
199951172f62SAdrian Chadd  * adjust things accordingly.
200051172f62SAdrian Chadd  *
200151172f62SAdrian Chadd  * This is done after a call to ieee80211_ht_updateparams()
200251172f62SAdrian Chadd  * because it (and the upcoming VHT version of updateparams)
200351172f62SAdrian Chadd  * needs to ensure everything is parsed before htinfo_update_chw()
200451172f62SAdrian Chadd  * is called - which will change the channel config for the
200551172f62SAdrian Chadd  * node for us.
200651172f62SAdrian Chadd  */
200751172f62SAdrian Chadd int
200851172f62SAdrian Chadd ieee80211_ht_updateparams_final(struct ieee80211_node *ni,
200951172f62SAdrian Chadd 	const uint8_t *htcapie, const uint8_t *htinfoie)
201051172f62SAdrian Chadd {
201151172f62SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
201251172f62SAdrian Chadd 	const struct ieee80211_ie_htinfo *htinfo;
201351172f62SAdrian Chadd 	int htflags, vhtflags;
201451172f62SAdrian Chadd 	int ret = 0;
201551172f62SAdrian Chadd 
201651172f62SAdrian Chadd 	htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
201751172f62SAdrian Chadd 
20182bfc8a91SSam Leffler 	htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
2019fdabd982SSam Leffler 	    IEEE80211_CHAN_HT20 : 0;
202051172f62SAdrian Chadd 
2021fdabd982SSam Leffler 	/* NB: honor operating mode constraint */
2022fdabd982SSam Leffler 	if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
20232bfc8a91SSam Leffler 	    (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) {
2024fdabd982SSam Leffler 		if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
2025fdabd982SSam Leffler 			htflags = IEEE80211_CHAN_HT40U;
2026fdabd982SSam Leffler 		else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
2027fdabd982SSam Leffler 			htflags = IEEE80211_CHAN_HT40D;
2028fdabd982SSam Leffler 	}
202944f7a6edSSam Leffler 
203051172f62SAdrian Chadd 	/*
203151172f62SAdrian Chadd 	 * VHT flags - do much the same; check whether VHT is available
203251172f62SAdrian Chadd 	 * and if so, what our ideal channel use would be based on our
203351172f62SAdrian Chadd 	 * capabilities and the (pre-parsed) VHT info IE.
203451172f62SAdrian Chadd 	 */
203551172f62SAdrian Chadd 	vhtflags = ieee80211_vht_get_vhtflags(ni, htflags);
203651172f62SAdrian Chadd 
203751172f62SAdrian Chadd 	if (htinfo_update_chw(ni, htflags, vhtflags))
203851172f62SAdrian Chadd 		ret = 1;
2039b94299c4SAdrian Chadd 
2040b94299c4SAdrian Chadd 	return (ret);
2041fdabd982SSam Leffler }
2042fdabd982SSam Leffler 
2043fdabd982SSam Leffler /*
2044fdabd982SSam Leffler  * Parse and update HT-related state extracted from the HT cap ie
2045fdabd982SSam Leffler  * for a station joining an HT BSS.
204651172f62SAdrian Chadd  *
204751172f62SAdrian Chadd  * This is called from the hostap path for each station.
2048fdabd982SSam Leffler  */
2049fdabd982SSam Leffler void
2050fdabd982SSam Leffler ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
2051fdabd982SSam Leffler {
2052fdabd982SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
2053fdabd982SSam Leffler 
2054fdabd982SSam Leffler 	ieee80211_parse_htcap(ni, htcapie);
2055f35b8451SAndriy Voskoboinyk 	if (vap->iv_htcaps & IEEE80211_HTC_SMPS)
20568c070d69SSam Leffler 		htcap_update_mimo_ps(ni);
205749942a97SSam Leffler 	htcap_update_shortgi(ni);
2058c5bba9daSAndriy Voskoboinyk 	htcap_update_ldpc(ni);
205951172f62SAdrian Chadd }
206051172f62SAdrian Chadd 
206151172f62SAdrian Chadd /*
206251172f62SAdrian Chadd  * Called once HT and VHT capabilities are parsed in hostap mode -
206351172f62SAdrian Chadd  * this will adjust the channel configuration of the given node
206451172f62SAdrian Chadd  * based on the configuration and capabilities.
206551172f62SAdrian Chadd  */
206651172f62SAdrian Chadd void
206751172f62SAdrian Chadd ieee80211_ht_updatehtcap_final(struct ieee80211_node *ni)
206851172f62SAdrian Chadd {
206951172f62SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
207051172f62SAdrian Chadd 	int htflags;
207151172f62SAdrian Chadd 	int vhtflags;
2072fdabd982SSam Leffler 
2073fdabd982SSam Leffler 	/* NB: honor operating mode constraint */
20748dc8c1f7SBernhard Schmidt 	/* XXX 40 MHz intolerant */
20752bfc8a91SSam Leffler 	htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
2076fdabd982SSam Leffler 	    IEEE80211_CHAN_HT20 : 0;
2077fdabd982SSam Leffler 	if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
20782bfc8a91SSam Leffler 	    (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) {
2079fdabd982SSam Leffler 		if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan))
2080fdabd982SSam Leffler 			htflags = IEEE80211_CHAN_HT40U;
2081fdabd982SSam Leffler 		else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan))
2082fdabd982SSam Leffler 			htflags = IEEE80211_CHAN_HT40D;
2083fdabd982SSam Leffler 	}
208451172f62SAdrian Chadd 	/*
208551172f62SAdrian Chadd 	 * VHT flags - do much the same; check whether VHT is available
208651172f62SAdrian Chadd 	 * and if so, what our ideal channel use would be based on our
208751172f62SAdrian Chadd 	 * capabilities and the (pre-parsed) VHT info IE.
208851172f62SAdrian Chadd 	 */
208951172f62SAdrian Chadd 	vhtflags = ieee80211_vht_get_vhtflags(ni, htflags);
209051172f62SAdrian Chadd 
209151172f62SAdrian Chadd 	(void) htinfo_update_chw(ni, htflags, vhtflags);
2092fdabd982SSam Leffler }
2093fdabd982SSam Leffler 
2094fdabd982SSam Leffler /*
209568e8e04eSSam Leffler  * Install received HT rate set by parsing the HT cap ie.
209668e8e04eSSam Leffler  */
209768e8e04eSSam Leffler int
209868e8e04eSSam Leffler ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
209968e8e04eSSam Leffler {
2100597029bfSBernhard Schmidt 	struct ieee80211com *ic = ni->ni_ic;
2101b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
210268e8e04eSSam Leffler 	const struct ieee80211_ie_htcap *htcap;
210368e8e04eSSam Leffler 	struct ieee80211_htrateset *rs;
2104597029bfSBernhard Schmidt 	int i, maxequalmcs, maxunequalmcs;
2105597029bfSBernhard Schmidt 
2106597029bfSBernhard Schmidt 	maxequalmcs = ic->ic_txstream * 8 - 1;
21076dbbec93SAndriy Voskoboinyk 	maxunequalmcs = 0;
2108597029bfSBernhard Schmidt 	if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
2109597029bfSBernhard Schmidt 		if (ic->ic_txstream >= 2)
2110597029bfSBernhard Schmidt 			maxunequalmcs = 38;
2111597029bfSBernhard Schmidt 		if (ic->ic_txstream >= 3)
2112597029bfSBernhard Schmidt 			maxunequalmcs = 52;
2113597029bfSBernhard Schmidt 		if (ic->ic_txstream >= 4)
2114597029bfSBernhard Schmidt 			maxunequalmcs = 76;
21156dbbec93SAndriy Voskoboinyk 	}
211668e8e04eSSam Leffler 
211768e8e04eSSam Leffler 	rs = &ni->ni_htrates;
211868e8e04eSSam Leffler 	memset(rs, 0, sizeof(*rs));
211968e8e04eSSam Leffler 	if (ie != NULL) {
212068e8e04eSSam Leffler 		if (ie[0] == IEEE80211_ELEMID_VENDOR)
212168e8e04eSSam Leffler 			ie += 4;
212268e8e04eSSam Leffler 		htcap = (const struct ieee80211_ie_htcap *) ie;
212368e8e04eSSam Leffler 		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
212468e8e04eSSam Leffler 			if (isclr(htcap->hc_mcsset, i))
212568e8e04eSSam Leffler 				continue;
212668e8e04eSSam Leffler 			if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
2127b032f27cSSam Leffler 				IEEE80211_NOTE(vap,
212868e8e04eSSam Leffler 				    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
212968e8e04eSSam Leffler 				    "WARNING, HT rate set too large; only "
213068e8e04eSSam Leffler 				    "using %u rates", IEEE80211_HTRATE_MAXSIZE);
2131b032f27cSSam Leffler 				vap->iv_stats.is_rx_rstoobig++;
213268e8e04eSSam Leffler 				break;
213368e8e04eSSam Leffler 			}
2134597029bfSBernhard Schmidt 			if (i <= 31 && i > maxequalmcs)
2135597029bfSBernhard Schmidt 				continue;
2136597029bfSBernhard Schmidt 			if (i == 32 &&
2137597029bfSBernhard Schmidt 			    (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0)
2138597029bfSBernhard Schmidt 				continue;
2139597029bfSBernhard Schmidt 			if (i > 32 && i > maxunequalmcs)
2140597029bfSBernhard Schmidt 				continue;
214168e8e04eSSam Leffler 			rs->rs_rates[rs->rs_nrates++] = i;
214268e8e04eSSam Leffler 		}
214368e8e04eSSam Leffler 	}
214468e8e04eSSam Leffler 	return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags);
214568e8e04eSSam Leffler }
214668e8e04eSSam Leffler 
214768e8e04eSSam Leffler /*
214868e8e04eSSam Leffler  * Mark rates in a node's HT rate set as basic according
214968e8e04eSSam Leffler  * to the information in the supplied HT info ie.
215068e8e04eSSam Leffler  */
215168e8e04eSSam Leffler void
215268e8e04eSSam Leffler ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
215368e8e04eSSam Leffler {
215468e8e04eSSam Leffler 	const struct ieee80211_ie_htinfo *htinfo;
215568e8e04eSSam Leffler 	struct ieee80211_htrateset *rs;
215668e8e04eSSam Leffler 	int i, j;
215768e8e04eSSam Leffler 
215868e8e04eSSam Leffler 	if (ie[0] == IEEE80211_ELEMID_VENDOR)
215968e8e04eSSam Leffler 		ie += 4;
216068e8e04eSSam Leffler 	htinfo = (const struct ieee80211_ie_htinfo *) ie;
216168e8e04eSSam Leffler 	rs = &ni->ni_htrates;
216268e8e04eSSam Leffler 	if (rs->rs_nrates == 0) {
2163b032f27cSSam Leffler 		IEEE80211_NOTE(ni->ni_vap,
216468e8e04eSSam Leffler 		    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
216568e8e04eSSam Leffler 		    "%s", "WARNING, empty HT rate set");
216668e8e04eSSam Leffler 		return;
216768e8e04eSSam Leffler 	}
216868e8e04eSSam Leffler 	for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
216968e8e04eSSam Leffler 		if (isclr(htinfo->hi_basicmcsset, i))
217068e8e04eSSam Leffler 			continue;
217168e8e04eSSam Leffler 		for (j = 0; j < rs->rs_nrates; j++)
217268e8e04eSSam Leffler 			if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
217368e8e04eSSam Leffler 				rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
217468e8e04eSSam Leffler 	}
217568e8e04eSSam Leffler }
217668e8e04eSSam Leffler 
217768e8e04eSSam Leffler static void
2178cc71a422SSam Leffler ampdu_tx_setup(struct ieee80211_tx_ampdu *tap)
2179cc71a422SSam Leffler {
2180fd90e2edSJung-uk Kim 	callout_init(&tap->txa_timer, 1);
2181cc71a422SSam Leffler 	tap->txa_flags |= IEEE80211_AGGR_SETUP;
218283249ceaSAdrian Chadd 	tap->txa_lastsample = ticks;
2183cc71a422SSam Leffler }
2184cc71a422SSam Leffler 
2185cc71a422SSam Leffler static void
2186cc71a422SSam Leffler ampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
2187cc71a422SSam Leffler {
2188cc71a422SSam Leffler 	struct ieee80211_node *ni = tap->txa_ni;
2189cc71a422SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
2190cc71a422SSam Leffler 
21910ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
21920ef1bc21SAdrian Chadd 	    tap->txa_ni,
21930ef1bc21SAdrian Chadd 	    "%s: called",
21940ef1bc21SAdrian Chadd 	    __func__);
21950ef1bc21SAdrian Chadd 
2196cc71a422SSam Leffler 	KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP,
21972aa563dfSAdrian Chadd 	    ("txa_flags 0x%x tid %d ac %d", tap->txa_flags, tap->txa_tid,
21982aa563dfSAdrian Chadd 	    TID_TO_WME_AC(tap->txa_tid)));
2199cc71a422SSam Leffler 
2200cc71a422SSam Leffler 	/*
2201cc71a422SSam Leffler 	 * Stop BA stream if setup so driver has a chance
2202cc71a422SSam Leffler 	 * to reclaim any resources it might have allocated.
2203cc71a422SSam Leffler 	 */
2204cc71a422SSam Leffler 	ic->ic_addba_stop(ni, tap);
2205cc71a422SSam Leffler 	/*
2206cc71a422SSam Leffler 	 * Stop any pending BAR transmit.
2207cc71a422SSam Leffler 	 */
2208cc71a422SSam Leffler 	bar_stop_timer(tap);
2209cc71a422SSam Leffler 
221083249ceaSAdrian Chadd 	/*
221183249ceaSAdrian Chadd 	 * Reset packet estimate.
221283249ceaSAdrian Chadd 	 */
2213d7621b8cSAdrian Chadd 	ieee80211_txampdu_init_pps(tap);
221483249ceaSAdrian Chadd 
2215cc71a422SSam Leffler 	/* NB: clearing NAK means we may re-send ADDBA */
2216cc71a422SSam Leffler 	tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
2217cc71a422SSam Leffler }
2218cc71a422SSam Leffler 
2219a834836dSAdrian Chadd /*
2220a834836dSAdrian Chadd  * ADDBA response timeout.
2221a834836dSAdrian Chadd  *
2222a834836dSAdrian Chadd  * If software aggregation and per-TID queue management was done here,
2223a834836dSAdrian Chadd  * that queue would be unpaused after the ADDBA timeout occurs.
2224a834836dSAdrian Chadd  */
2225cc71a422SSam Leffler static void
222668e8e04eSSam Leffler addba_timeout(void *arg)
222768e8e04eSSam Leffler {
222868e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap = arg;
2229a834836dSAdrian Chadd 	struct ieee80211_node *ni = tap->txa_ni;
2230a834836dSAdrian Chadd 	struct ieee80211com *ic = ni->ni_ic;
223168e8e04eSSam Leffler 
223268e8e04eSSam Leffler 	/* XXX ? */
223368e8e04eSSam Leffler 	tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
223468e8e04eSSam Leffler 	tap->txa_attempts++;
2235a834836dSAdrian Chadd 	ic->ic_addba_response_timeout(ni, tap);
223668e8e04eSSam Leffler }
223768e8e04eSSam Leffler 
223868e8e04eSSam Leffler static void
223968e8e04eSSam Leffler addba_start_timeout(struct ieee80211_tx_ampdu *tap)
224068e8e04eSSam Leffler {
224168e8e04eSSam Leffler 	/* XXX use CALLOUT_PENDING instead? */
2242b032f27cSSam Leffler 	callout_reset(&tap->txa_timer, ieee80211_addba_timeout,
224368e8e04eSSam Leffler 	    addba_timeout, tap);
224468e8e04eSSam Leffler 	tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
2245b032f27cSSam Leffler 	tap->txa_nextrequest = ticks + ieee80211_addba_timeout;
224668e8e04eSSam Leffler }
224768e8e04eSSam Leffler 
224868e8e04eSSam Leffler static void
224968e8e04eSSam Leffler addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
225068e8e04eSSam Leffler {
225168e8e04eSSam Leffler 	/* XXX use CALLOUT_PENDING instead? */
225268e8e04eSSam Leffler 	if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
225368e8e04eSSam Leffler 		callout_stop(&tap->txa_timer);
225468e8e04eSSam Leffler 		tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
225568e8e04eSSam Leffler 	}
225668e8e04eSSam Leffler }
225768e8e04eSSam Leffler 
2258a834836dSAdrian Chadd static void
2259a834836dSAdrian Chadd null_addba_response_timeout(struct ieee80211_node *ni,
2260a834836dSAdrian Chadd     struct ieee80211_tx_ampdu *tap)
2261a834836dSAdrian Chadd {
2262a834836dSAdrian Chadd }
2263a834836dSAdrian Chadd 
226468e8e04eSSam Leffler /*
226568e8e04eSSam Leffler  * Default method for requesting A-MPDU tx aggregation.
226668e8e04eSSam Leffler  * We setup the specified state block and start a timer
226768e8e04eSSam Leffler  * to wait for an ADDBA response frame.
226868e8e04eSSam Leffler  */
226968e8e04eSSam Leffler static int
227068e8e04eSSam Leffler ieee80211_addba_request(struct ieee80211_node *ni,
227168e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap,
227268e8e04eSSam Leffler 	int dialogtoken, int baparamset, int batimeout)
227368e8e04eSSam Leffler {
227468e8e04eSSam Leffler 	int bufsiz;
227568e8e04eSSam Leffler 
227668e8e04eSSam Leffler 	/* XXX locking */
227768e8e04eSSam Leffler 	tap->txa_token = dialogtoken;
227868e8e04eSSam Leffler 	tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
2279fe5ebb23SBjoern A. Zeeb 	bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ);
228068e8e04eSSam Leffler 	tap->txa_wnd = (bufsiz == 0) ?
228168e8e04eSSam Leffler 	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
228268e8e04eSSam Leffler 	addba_start_timeout(tap);
228368e8e04eSSam Leffler 	return 1;
228468e8e04eSSam Leffler }
228568e8e04eSSam Leffler 
228668e8e04eSSam Leffler /*
2287163f8705SAdrian Chadd  * Called by drivers that wish to request an ADDBA session be
2288163f8705SAdrian Chadd  * setup.  This brings it up and starts the request timer.
2289163f8705SAdrian Chadd  */
2290163f8705SAdrian Chadd int
2291163f8705SAdrian Chadd ieee80211_ampdu_tx_request_ext(struct ieee80211_node *ni, int tid)
2292163f8705SAdrian Chadd {
2293163f8705SAdrian Chadd 	struct ieee80211_tx_ampdu *tap;
2294163f8705SAdrian Chadd 
2295163f8705SAdrian Chadd 	if (tid < 0 || tid > 15)
2296163f8705SAdrian Chadd 		return (0);
2297163f8705SAdrian Chadd 	tap = &ni->ni_tx_ampdu[tid];
2298163f8705SAdrian Chadd 
2299163f8705SAdrian Chadd 	/* XXX locking */
2300163f8705SAdrian Chadd 	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
2301163f8705SAdrian Chadd 		/* do deferred setup of state */
2302163f8705SAdrian Chadd 		ampdu_tx_setup(tap);
2303163f8705SAdrian Chadd 	}
2304163f8705SAdrian Chadd 	/* XXX hack for not doing proper locking */
2305163f8705SAdrian Chadd 	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
2306163f8705SAdrian Chadd 	addba_start_timeout(tap);
2307163f8705SAdrian Chadd 	return (1);
2308163f8705SAdrian Chadd }
2309163f8705SAdrian Chadd 
2310163f8705SAdrian Chadd /*
2311163f8705SAdrian Chadd  * Called by drivers that have marked a session as active.
2312163f8705SAdrian Chadd  */
2313163f8705SAdrian Chadd int
2314163f8705SAdrian Chadd ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni, int tid,
2315163f8705SAdrian Chadd     int status)
2316163f8705SAdrian Chadd {
2317163f8705SAdrian Chadd 	struct ieee80211_tx_ampdu *tap;
2318163f8705SAdrian Chadd 
2319163f8705SAdrian Chadd 	if (tid < 0 || tid > 15)
2320163f8705SAdrian Chadd 		return (0);
2321163f8705SAdrian Chadd 	tap = &ni->ni_tx_ampdu[tid];
2322163f8705SAdrian Chadd 
2323163f8705SAdrian Chadd 	/* XXX locking */
2324163f8705SAdrian Chadd 	addba_stop_timeout(tap);
2325163f8705SAdrian Chadd 	if (status == 1) {
2326163f8705SAdrian Chadd 		tap->txa_flags |= IEEE80211_AGGR_RUNNING;
2327163f8705SAdrian Chadd 		tap->txa_attempts = 0;
2328163f8705SAdrian Chadd 	} else {
2329163f8705SAdrian Chadd 		/* mark tid so we don't try again */
2330163f8705SAdrian Chadd 		tap->txa_flags |= IEEE80211_AGGR_NAK;
2331163f8705SAdrian Chadd 	}
2332163f8705SAdrian Chadd 	return (1);
2333163f8705SAdrian Chadd }
2334163f8705SAdrian Chadd 
2335163f8705SAdrian Chadd /*
233668e8e04eSSam Leffler  * Default method for processing an A-MPDU tx aggregation
233768e8e04eSSam Leffler  * response.  We shutdown any pending timer and update the
233868e8e04eSSam Leffler  * state block according to the reply.
233968e8e04eSSam Leffler  */
234068e8e04eSSam Leffler static int
234168e8e04eSSam Leffler ieee80211_addba_response(struct ieee80211_node *ni,
234268e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap,
234368e8e04eSSam Leffler 	int status, int baparamset, int batimeout)
234468e8e04eSSam Leffler {
2345ebb9b256SAdrian Chadd 	struct ieee80211vap *vap = ni->ni_vap;
234605ea7a3eSBjoern A. Zeeb 	int bufsiz;
234768e8e04eSSam Leffler 
234868e8e04eSSam Leffler 	/* XXX locking */
234968e8e04eSSam Leffler 	addba_stop_timeout(tap);
235068e8e04eSSam Leffler 	if (status == IEEE80211_STATUS_SUCCESS) {
2351fe5ebb23SBjoern A. Zeeb 		bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ);
235268e8e04eSSam Leffler 		/* XXX override our request? */
235368e8e04eSSam Leffler 		tap->txa_wnd = (bufsiz == 0) ?
235468e8e04eSSam Leffler 		    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
235505ea7a3eSBjoern A. Zeeb #ifdef __notyet__
2356fe5ebb23SBjoern A. Zeeb 		tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID);
235705ea7a3eSBjoern A. Zeeb #endif
235868e8e04eSSam Leffler 		tap->txa_flags |= IEEE80211_AGGR_RUNNING;
2359cc71a422SSam Leffler 		tap->txa_attempts = 0;
2360ebb9b256SAdrian Chadd 		/* TODO: this should be a vap flag */
2361ebb9b256SAdrian Chadd 		if ((vap->iv_htcaps & IEEE80211_HTC_TX_AMSDU_AMPDU) &&
2362ebb9b256SAdrian Chadd 		    (ni->ni_flags & IEEE80211_NODE_AMSDU_TX) &&
2363fe5ebb23SBjoern A. Zeeb 		    (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU)))
2364ebb9b256SAdrian Chadd 			tap->txa_flags |= IEEE80211_AGGR_AMSDU;
2365ebb9b256SAdrian Chadd 		else
2366ebb9b256SAdrian Chadd 			tap->txa_flags &= ~IEEE80211_AGGR_AMSDU;
23671b6167d2SSam Leffler 	} else {
23681b6167d2SSam Leffler 		/* mark tid so we don't try again */
23691b6167d2SSam Leffler 		tap->txa_flags |= IEEE80211_AGGR_NAK;
237068e8e04eSSam Leffler 	}
237168e8e04eSSam Leffler 	return 1;
237268e8e04eSSam Leffler }
237368e8e04eSSam Leffler 
237468e8e04eSSam Leffler /*
237568e8e04eSSam Leffler  * Default method for stopping A-MPDU tx aggregation.
237668e8e04eSSam Leffler  * Any timer is cleared and we drain any pending frames.
237768e8e04eSSam Leffler  */
237868e8e04eSSam Leffler static void
237968e8e04eSSam Leffler ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
238068e8e04eSSam Leffler {
238168e8e04eSSam Leffler 	/* XXX locking */
238268e8e04eSSam Leffler 	addba_stop_timeout(tap);
238368e8e04eSSam Leffler 	if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
2384c0756f5fSSam Leffler 		/* XXX clear aggregation queue */
2385ebb9b256SAdrian Chadd 		tap->txa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_AMSDU);
238668e8e04eSSam Leffler 	}
238768e8e04eSSam Leffler 	tap->txa_attempts = 0;
238868e8e04eSSam Leffler }
238968e8e04eSSam Leffler 
239068e8e04eSSam Leffler /*
239168e8e04eSSam Leffler  * Process a received action frame using the default aggregation
239268e8e04eSSam Leffler  * policy.  We intercept ADDBA-related frames and use them to
239368e8e04eSSam Leffler  * update our aggregation state.  All other frames are passed up
239468e8e04eSSam Leffler  * for processing by ieee80211_recv_action.
239568e8e04eSSam Leffler  */
239676340123SSam Leffler static int
239776340123SSam Leffler ht_recv_action_ba_addba_request(struct ieee80211_node *ni,
239876340123SSam Leffler 	const struct ieee80211_frame *wh,
239968e8e04eSSam Leffler 	const uint8_t *frm, const uint8_t *efrm)
240068e8e04eSSam Leffler {
240168e8e04eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
2402b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
240368e8e04eSSam Leffler 	struct ieee80211_rx_ampdu *rap;
240476340123SSam Leffler 	uint8_t dialogtoken;
240576340123SSam Leffler 	uint16_t baparamset, batimeout, baseqctl;
24060917631fSRui Paulo 	uint16_t args[5];
240776340123SSam Leffler 	int tid;
240868e8e04eSSam Leffler 
240968e8e04eSSam Leffler 	dialogtoken = frm[2];
241031021a2bSAndriy Voskoboinyk 	baparamset = le16dec(frm+3);
241131021a2bSAndriy Voskoboinyk 	batimeout = le16dec(frm+5);
241231021a2bSAndriy Voskoboinyk 	baseqctl = le16dec(frm+7);
241368e8e04eSSam Leffler 
2414fe5ebb23SBjoern A. Zeeb 	tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID);
241568e8e04eSSam Leffler 
241676340123SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
241776340123SSam Leffler 	    "recv ADDBA request: dialogtoken %u baparamset 0x%x "
24187280f374SAdrian Chadd 	    "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d amsdu %d",
241951b5aba2SSam Leffler 	    dialogtoken, baparamset,
2420fe5ebb23SBjoern A. Zeeb 	    tid, _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ),
242151b5aba2SSam Leffler 	    batimeout,
2422fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_START),
2423fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_FRAG),
2424fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU));
242568e8e04eSSam Leffler 
242668e8e04eSSam Leffler 	rap = &ni->ni_rx_ampdu[tid];
242768e8e04eSSam Leffler 
242868e8e04eSSam Leffler 	/* Send ADDBA response */
242968e8e04eSSam Leffler 	args[0] = dialogtoken;
24301b6167d2SSam Leffler 	/*
24311b6167d2SSam Leffler 	 * NB: We ack only if the sta associated with HT and
24321b6167d2SSam Leffler 	 * the ap is configured to do AMPDU rx (the latter
24331b6167d2SSam Leffler 	 * violates the 11n spec and is mostly for testing).
24341b6167d2SSam Leffler 	 */
24351b6167d2SSam Leffler 	if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
24362bfc8a91SSam Leffler 	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) {
2437ebb9b256SAdrian Chadd 		/* XXX TODO: handle ampdu_rx_start failure */
243851b5aba2SSam Leffler 		ic->ic_ampdu_rx_start(ni, rap,
243951b5aba2SSam Leffler 		    baparamset, batimeout, baseqctl);
244068e8e04eSSam Leffler 
244168e8e04eSSam Leffler 		args[1] = IEEE80211_STATUS_SUCCESS;
24421b6167d2SSam Leffler 	} else {
244376340123SSam Leffler 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
24441b6167d2SSam Leffler 		    ni, "reject ADDBA request: %s",
24451b6167d2SSam Leffler 		    ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
24461b6167d2SSam Leffler 		       "administratively disabled" :
24471b6167d2SSam Leffler 		       "not negotiated for station");
2448b032f27cSSam Leffler 		vap->iv_stats.is_addba_reject++;
244968e8e04eSSam Leffler 		args[1] = IEEE80211_STATUS_UNSPECIFIED;
24501b6167d2SSam Leffler 	}
245168e8e04eSSam Leffler 	/* XXX honor rap flags? */
245268e8e04eSSam Leffler 	args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
2453fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(tid, IEEE80211_BAPS_TID)
2454fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
245568e8e04eSSam Leffler 		;
2456ebb9b256SAdrian Chadd 
2457ebb9b256SAdrian Chadd 	/*
2458ebb9b256SAdrian Chadd 	 * TODO: we're out of iv_flags_ht fields; once
2459ebb9b256SAdrian Chadd 	 * this is extended we should make this configurable.
2460ebb9b256SAdrian Chadd 	 */
2461ebb9b256SAdrian Chadd 	if ((baparamset & IEEE80211_BAPS_AMSDU) &&
2462ebb9b256SAdrian Chadd 	    (ni->ni_flags & IEEE80211_NODE_AMSDU_RX) &&
2463ebb9b256SAdrian Chadd 	    (vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU))
2464ebb9b256SAdrian Chadd 		args[2] |= IEEE80211_BAPS_AMSDU;
2465ebb9b256SAdrian Chadd 
246668e8e04eSSam Leffler 	args[3] = 0;
24670917631fSRui Paulo 	args[4] = 0;
246868e8e04eSSam Leffler 	ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
246968e8e04eSSam Leffler 		IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
247076340123SSam Leffler 	return 0;
247176340123SSam Leffler }
247268e8e04eSSam Leffler 
247376340123SSam Leffler static int
247476340123SSam Leffler ht_recv_action_ba_addba_response(struct ieee80211_node *ni,
247576340123SSam Leffler 	const struct ieee80211_frame *wh,
247676340123SSam Leffler 	const uint8_t *frm, const uint8_t *efrm)
247776340123SSam Leffler {
247876340123SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
247976340123SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
248076340123SSam Leffler 	struct ieee80211_tx_ampdu *tap;
248176340123SSam Leffler 	uint8_t dialogtoken, policy;
248276340123SSam Leffler 	uint16_t baparamset, batimeout, code;
248305ea7a3eSBjoern A. Zeeb 	int tid;
248405ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG
248505ea7a3eSBjoern A. Zeeb 	int amsdu, bufsiz;
248605ea7a3eSBjoern A. Zeeb #endif
248776340123SSam Leffler 
248868e8e04eSSam Leffler 	dialogtoken = frm[2];
248931021a2bSAndriy Voskoboinyk 	code = le16dec(frm+3);
249031021a2bSAndriy Voskoboinyk 	baparamset = le16dec(frm+5);
2491fe5ebb23SBjoern A. Zeeb 	tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID);
249205ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG
2493fe5ebb23SBjoern A. Zeeb 	bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ);
2494fe5ebb23SBjoern A. Zeeb 	amsdu = !! _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU);
249505ea7a3eSBjoern A. Zeeb #endif
249605ea7a3eSBjoern A. Zeeb 	policy = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_POLICY);
249731021a2bSAndriy Voskoboinyk 	batimeout = le16dec(frm+7);
249868e8e04eSSam Leffler 
24992aa563dfSAdrian Chadd 	tap = &ni->ni_tx_ampdu[tid];
2500ddc53684SSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
2501b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap,
25021b6167d2SSam Leffler 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
25031b6167d2SSam Leffler 		    ni->ni_macaddr, "ADDBA response",
25041b6167d2SSam Leffler 		    "no pending ADDBA, tid %d dialogtoken %u "
25051b6167d2SSam Leffler 		    "code %d", tid, dialogtoken, code);
2506b032f27cSSam Leffler 		vap->iv_stats.is_addba_norequest++;
250776340123SSam Leffler 		return 0;
25081b6167d2SSam Leffler 	}
25091b6167d2SSam Leffler 	if (dialogtoken != tap->txa_token) {
2510b032f27cSSam Leffler 		IEEE80211_DISCARD_MAC(vap,
25111b6167d2SSam Leffler 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
25121b6167d2SSam Leffler 		    ni->ni_macaddr, "ADDBA response",
25131b6167d2SSam Leffler 		    "dialogtoken mismatch: waiting for %d, "
25141b6167d2SSam Leffler 		    "received %d, tid %d code %d",
25151b6167d2SSam Leffler 		    tap->txa_token, dialogtoken, tid, code);
2516b032f27cSSam Leffler 		vap->iv_stats.is_addba_badtoken++;
251776340123SSam Leffler 		return 0;
25181b6167d2SSam Leffler 	}
2519eaff8e26SSam Leffler 	/* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
2520eaff8e26SSam Leffler 	if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) {
2521eaff8e26SSam Leffler 		IEEE80211_DISCARD_MAC(vap,
2522eaff8e26SSam Leffler 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2523eaff8e26SSam Leffler 		    ni->ni_macaddr, "ADDBA response",
2524eaff8e26SSam Leffler 		    "policy mismatch: expecting %s, "
2525eaff8e26SSam Leffler 		    "received %s, tid %d code %d",
2526eaff8e26SSam Leffler 		    tap->txa_flags & IEEE80211_AGGR_IMMEDIATE,
2527eaff8e26SSam Leffler 		    policy, tid, code);
2528eaff8e26SSam Leffler 		vap->iv_stats.is_addba_badpolicy++;
252976340123SSam Leffler 		return 0;
2530eaff8e26SSam Leffler 	}
2531eaff8e26SSam Leffler #if 0
2532eaff8e26SSam Leffler 	/* XXX we take MIN in ieee80211_addba_response */
2533eaff8e26SSam Leffler 	if (bufsiz > IEEE80211_AGGR_BAWMAX) {
2534eaff8e26SSam Leffler 		IEEE80211_DISCARD_MAC(vap,
2535eaff8e26SSam Leffler 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2536eaff8e26SSam Leffler 		    ni->ni_macaddr, "ADDBA response",
2537eaff8e26SSam Leffler 		    "BA window too large: max %d, "
2538eaff8e26SSam Leffler 		    "received %d, tid %d code %d",
2539eaff8e26SSam Leffler 		    bufsiz, IEEE80211_AGGR_BAWMAX, tid, code);
2540eaff8e26SSam Leffler 		vap->iv_stats.is_addba_badbawinsize++;
254176340123SSam Leffler 		return 0;
2542eaff8e26SSam Leffler 	}
2543eaff8e26SSam Leffler #endif
25447280f374SAdrian Chadd 
254576340123SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
254668e8e04eSSam Leffler 	    "recv ADDBA response: dialogtoken %u code %d "
2547ebb9b256SAdrian Chadd 	    "baparamset 0x%x (tid %d bufsiz %d amsdu %d) batimeout %d",
2548ebb9b256SAdrian Chadd 	    dialogtoken, code, baparamset, tid,
2549ebb9b256SAdrian Chadd 	    bufsiz,
2550ebb9b256SAdrian Chadd 	    amsdu,
255168e8e04eSSam Leffler 	    batimeout);
255276340123SSam Leffler 	ic->ic_addba_response(ni, tap, code, baparamset, batimeout);
255376340123SSam Leffler 	return 0;
255476340123SSam Leffler }
255568e8e04eSSam Leffler 
255676340123SSam Leffler static int
255776340123SSam Leffler ht_recv_action_ba_delba(struct ieee80211_node *ni,
255876340123SSam Leffler 	const struct ieee80211_frame *wh,
255976340123SSam Leffler 	const uint8_t *frm, const uint8_t *efrm)
256076340123SSam Leffler {
256176340123SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
256276340123SSam Leffler 	struct ieee80211_rx_ampdu *rap;
256376340123SSam Leffler 	struct ieee80211_tx_ampdu *tap;
256405ea7a3eSBjoern A. Zeeb 	uint16_t baparamset;
256505ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG
256605ea7a3eSBjoern A. Zeeb 	uint16_t code;
256705ea7a3eSBjoern A. Zeeb #endif
25682aa563dfSAdrian Chadd 	int tid;
256976340123SSam Leffler 
257031021a2bSAndriy Voskoboinyk 	baparamset = le16dec(frm+2);
257105ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG
257231021a2bSAndriy Voskoboinyk 	code = le16dec(frm+4);
257305ea7a3eSBjoern A. Zeeb #endif
257468e8e04eSSam Leffler 
2575fe5ebb23SBjoern A. Zeeb 	tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_DELBAPS_TID);
257668e8e04eSSam Leffler 
257776340123SSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
257868e8e04eSSam Leffler 	    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
257968e8e04eSSam Leffler 	    "code %d", baparamset, tid,
2580fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(baparamset, IEEE80211_DELBAPS_INIT), code);
258168e8e04eSSam Leffler 
258268e8e04eSSam Leffler 	if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
25832aa563dfSAdrian Chadd 		tap = &ni->ni_tx_ampdu[tid];
258468e8e04eSSam Leffler 		ic->ic_addba_stop(ni, tap);
258568e8e04eSSam Leffler 	} else {
258668e8e04eSSam Leffler 		rap = &ni->ni_rx_ampdu[tid];
258751b5aba2SSam Leffler 		ic->ic_ampdu_rx_stop(ni, rap);
258868e8e04eSSam Leffler 	}
258976340123SSam Leffler 	return 0;
259068e8e04eSSam Leffler }
259168e8e04eSSam Leffler 
259276340123SSam Leffler static int
259376340123SSam Leffler ht_recv_action_ht_txchwidth(struct ieee80211_node *ni,
259430e82523SBjoern A. Zeeb 	const struct ieee80211_frame *wh __unused,
259530e82523SBjoern A. Zeeb 	const uint8_t *frm, const uint8_t *efrm __unused)
259668e8e04eSSam Leffler {
259768e8e04eSSam Leffler 	int chw;
259868e8e04eSSam Leffler 
259930e82523SBjoern A. Zeeb 	/* If 20/40 is not supported the chw cannot change. */
260030e82523SBjoern A. Zeeb 	if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) == 0)
260130e82523SBjoern A. Zeeb 		return (0);
260230e82523SBjoern A. Zeeb 
2603ca389486SBjoern A. Zeeb 	chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ?
2604ca389486SBjoern A. Zeeb 	    IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
260576340123SSam Leffler 
260676340123SSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2607*2c8b0d62SBjoern A. Zeeb 	    "%s: HT txchwidth, width %d%s (%s)", __func__,
2608*2c8b0d62SBjoern A. Zeeb 	    chw, ni->ni_chw != chw ? "*" : "", ieee80211_ni_chw_to_str(chw));
2609c54fbb33SSam Leffler 	if (chw != ni->ni_chw) {
261051172f62SAdrian Chadd 		/* XXX does this need to change the ht40 station count? */
2611c54fbb33SSam Leffler 		ni->ni_chw = chw;
2612c54fbb33SSam Leffler 		/* XXX notify on change */
2613c54fbb33SSam Leffler 	}
261476340123SSam Leffler 	return 0;
261576340123SSam Leffler }
261676340123SSam Leffler 
261776340123SSam Leffler static int
261876340123SSam Leffler ht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni,
261976340123SSam Leffler 	const struct ieee80211_frame *wh,
262076340123SSam Leffler 	const uint8_t *frm, const uint8_t *efrm)
262176340123SSam Leffler {
26228c070d69SSam Leffler 	const struct ieee80211_action_ht_mimopowersave *mps =
262376340123SSam Leffler 	    (const struct ieee80211_action_ht_mimopowersave *) frm;
262476340123SSam Leffler 
26258c070d69SSam Leffler 	/* XXX check iv_htcaps */
26268c070d69SSam Leffler 	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA)
26278c070d69SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
26288c070d69SSam Leffler 	else
26298c070d69SSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
26308c070d69SSam Leffler 	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE)
26318c070d69SSam Leffler 		ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
26328c070d69SSam Leffler 	else
26338c070d69SSam Leffler 		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
26348c070d69SSam Leffler 	/* XXX notify on change */
263576340123SSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
26368c070d69SSam Leffler 	    "%s: HT MIMO PS (%s%s)", __func__,
263776340123SSam Leffler 	    (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ?  "on" : "off",
263876340123SSam Leffler 	    (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ?  "+rts" : ""
26398c070d69SSam Leffler 	);
264076340123SSam Leffler 	return 0;
264168e8e04eSSam Leffler }
264268e8e04eSSam Leffler 
264368e8e04eSSam Leffler /*
264468e8e04eSSam Leffler  * Transmit processing.
264568e8e04eSSam Leffler  */
264668e8e04eSSam Leffler 
264768e8e04eSSam Leffler /*
2648b032f27cSSam Leffler  * Check if A-MPDU should be requested/enabled for a stream.
2649b032f27cSSam Leffler  * We require a traffic rate above a per-AC threshold and we
2650b032f27cSSam Leffler  * also handle backoff from previous failed attempts.
2651b032f27cSSam Leffler  *
2652b032f27cSSam Leffler  * Drivers may override this method to bring in information
2653b032f27cSSam Leffler  * such as link state conditions in making the decision.
2654b032f27cSSam Leffler  */
2655b032f27cSSam Leffler static int
2656b032f27cSSam Leffler ieee80211_ampdu_enable(struct ieee80211_node *ni,
2657b032f27cSSam Leffler 	struct ieee80211_tx_ampdu *tap)
2658b032f27cSSam Leffler {
2659b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
2660b032f27cSSam Leffler 
26612aa563dfSAdrian Chadd 	if (tap->txa_avgpps <
26622aa563dfSAdrian Chadd 	    vap->iv_ampdu_mintraffic[TID_TO_WME_AC(tap->txa_tid)])
2663b032f27cSSam Leffler 		return 0;
2664b032f27cSSam Leffler 	/* XXX check rssi? */
2665b032f27cSSam Leffler 	if (tap->txa_attempts >= ieee80211_addba_maxtries &&
2666b4fb3d6eSAdrian Chadd 	    ieee80211_time_after(ticks, tap->txa_nextrequest)) {
2667b032f27cSSam Leffler 		/*
2668b032f27cSSam Leffler 		 * Don't retry too often; txa_nextrequest is set
2669b032f27cSSam Leffler 		 * to the minimum interval we'll retry after
2670b032f27cSSam Leffler 		 * ieee80211_addba_maxtries failed attempts are made.
2671b032f27cSSam Leffler 		 */
2672b032f27cSSam Leffler 		return 0;
2673b032f27cSSam Leffler 	}
2674b032f27cSSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
267575426752SAdrian Chadd 	    "enable AMPDU on tid %d (%s), avgpps %d pkts %d attempt %d",
26762aa563dfSAdrian Chadd 	    tap->txa_tid, ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)],
267775426752SAdrian Chadd 	    tap->txa_avgpps, tap->txa_pkts, tap->txa_attempts);
2678b032f27cSSam Leffler 	return 1;
2679b032f27cSSam Leffler }
2680b032f27cSSam Leffler 
2681b032f27cSSam Leffler /*
268268e8e04eSSam Leffler  * Request A-MPDU tx aggregation.  Setup local state and
268368e8e04eSSam Leffler  * issue an ADDBA request.  BA use will only happen after
268468e8e04eSSam Leffler  * the other end replies with ADDBA response.
268568e8e04eSSam Leffler  */
268668e8e04eSSam Leffler int
268768e8e04eSSam Leffler ieee80211_ampdu_request(struct ieee80211_node *ni,
268868e8e04eSSam Leffler 	struct ieee80211_tx_ampdu *tap)
268968e8e04eSSam Leffler {
269068e8e04eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
26910917631fSRui Paulo 	uint16_t args[5];
269268e8e04eSSam Leffler 	int tid, dialogtoken;
269368e8e04eSSam Leffler 	static int tokens = 0;	/* XXX */
269468e8e04eSSam Leffler 
269568e8e04eSSam Leffler 	/* XXX locking */
269668e8e04eSSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
269768e8e04eSSam Leffler 		/* do deferred setup of state */
2698cc71a422SSam Leffler 		ampdu_tx_setup(tap);
269968e8e04eSSam Leffler 	}
27001b6167d2SSam Leffler 	/* XXX hack for not doing proper locking */
27011b6167d2SSam Leffler 	tap->txa_flags &= ~IEEE80211_AGGR_NAK;
27021b6167d2SSam Leffler 
270368e8e04eSSam Leffler 	dialogtoken = (tokens+1) % 63;		/* XXX */
27042aa563dfSAdrian Chadd 	tid = tap->txa_tid;
270551172f62SAdrian Chadd 
270651172f62SAdrian Chadd 	/*
270751172f62SAdrian Chadd 	 * XXX TODO: This is racy with any other parallel TX going on. :(
270851172f62SAdrian Chadd 	 */
2709c5f9511eSSam Leffler 	tap->txa_start = ni->ni_txseqs[tid];
271068e8e04eSSam Leffler 
271168e8e04eSSam Leffler 	args[0] = dialogtoken;
27120917631fSRui Paulo 	args[1] = 0;	/* NB: status code not used */
27130917631fSRui Paulo 	args[2]	= IEEE80211_BAPS_POLICY_IMMEDIATE
2714fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(tid, IEEE80211_BAPS_TID)
2715fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(IEEE80211_AGGR_BAWMAX,
2716fe5ebb23SBjoern A. Zeeb 		    IEEE80211_BAPS_BUFSIZ)
271768e8e04eSSam Leffler 		;
2718ebb9b256SAdrian Chadd 
2719ebb9b256SAdrian Chadd 	/* XXX TODO: this should be a flag, not iv_htcaps */
2720ebb9b256SAdrian Chadd 	if ((ni->ni_flags & IEEE80211_NODE_AMSDU_TX) &&
2721ebb9b256SAdrian Chadd 	    (ni->ni_vap->iv_htcaps & IEEE80211_HTC_TX_AMSDU_AMPDU))
2722ebb9b256SAdrian Chadd 		args[2] |= IEEE80211_BAPS_AMSDU;
27237280f374SAdrian Chadd 
27240917631fSRui Paulo 	args[3] = 0;	/* batimeout */
272568e8e04eSSam Leffler 	/* NB: do first so there's no race against reply */
27260917631fSRui Paulo 	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) {
272768e8e04eSSam Leffler 		/* unable to setup state, don't make request */
2728b032f27cSSam Leffler 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
27292aa563dfSAdrian Chadd 		    ni, "%s: could not setup BA stream for TID %d AC %d",
27302aa563dfSAdrian Chadd 		    __func__, tap->txa_tid, TID_TO_WME_AC(tap->txa_tid));
27311b6167d2SSam Leffler 		/* defer next try so we don't slam the driver with requests */
2732b032f27cSSam Leffler 		tap->txa_attempts = ieee80211_addba_maxtries;
2733b032f27cSSam Leffler 		/* NB: check in case driver wants to override */
2734b032f27cSSam Leffler 		if (tap->txa_nextrequest <= ticks)
2735b032f27cSSam Leffler 			tap->txa_nextrequest = ticks + ieee80211_addba_backoff;
273668e8e04eSSam Leffler 		return 0;
273768e8e04eSSam Leffler 	}
273868e8e04eSSam Leffler 	tokens = dialogtoken;			/* allocate token */
2739c5f9511eSSam Leffler 	/* NB: after calling ic_addba_request so driver can set txa_start */
2740fe5ebb23SBjoern A. Zeeb 	args[4] = _IEEE80211_SHIFTMASK(tap->txa_start, IEEE80211_BASEQ_START)
2741fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(0, IEEE80211_BASEQ_FRAG)
27427f514436SSam Leffler 		;
274368e8e04eSSam Leffler 	return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
274468e8e04eSSam Leffler 		IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
274568e8e04eSSam Leffler }
274668e8e04eSSam Leffler 
274768e8e04eSSam Leffler /*
27481b6167d2SSam Leffler  * Terminate an AMPDU tx stream.  State is reclaimed
27491b6167d2SSam Leffler  * and the peer notified with a DelBA Action frame.
27501b6167d2SSam Leffler  */
27511b6167d2SSam Leffler void
27523c1a492eSSam Leffler ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
27533c1a492eSSam Leffler 	int reason)
27541b6167d2SSam Leffler {
27551b6167d2SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
2756b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
27571b6167d2SSam Leffler 	uint16_t args[4];
27581b6167d2SSam Leffler 
27591b6167d2SSam Leffler 	/* XXX locking */
2760cc71a422SSam Leffler 	tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
27611b6167d2SSam Leffler 	if (IEEE80211_AMPDU_RUNNING(tap)) {
2762b032f27cSSam Leffler 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2763d72d72d3SAndriy Voskoboinyk 		    ni, "%s: stop BA stream for TID %d (reason: %d (%s))",
2764d72d72d3SAndriy Voskoboinyk 		    __func__, tap->txa_tid, reason,
2765d72d72d3SAndriy Voskoboinyk 		    ieee80211_reason_to_string(reason));
2766b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_stop++;
27671b6167d2SSam Leffler 
27681b6167d2SSam Leffler 		ic->ic_addba_stop(ni, tap);
27692aa563dfSAdrian Chadd 		args[0] = tap->txa_tid;
27701b6167d2SSam Leffler 		args[1] = IEEE80211_DELBAPS_INIT;
27713c1a492eSSam Leffler 		args[2] = reason;			/* XXX reason code */
277276340123SSam Leffler 		ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
27731b6167d2SSam Leffler 			IEEE80211_ACTION_BA_DELBA, args);
27741b6167d2SSam Leffler 	} else {
2775b032f27cSSam Leffler 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2776d72d72d3SAndriy Voskoboinyk 		    ni, "%s: BA stream for TID %d not running "
2777d72d72d3SAndriy Voskoboinyk 		    "(reason: %d (%s))", __func__, tap->txa_tid, reason,
2778d72d72d3SAndriy Voskoboinyk 		    ieee80211_reason_to_string(reason));
2779b032f27cSSam Leffler 		vap->iv_stats.is_ampdu_stop_failed++;
27801b6167d2SSam Leffler 	}
27811b6167d2SSam Leffler }
27821b6167d2SSam Leffler 
2783545c8862SAdrian Chadd /* XXX */
2784545c8862SAdrian Chadd static void bar_start_timer(struct ieee80211_tx_ampdu *tap);
2785545c8862SAdrian Chadd 
2786cc71a422SSam Leffler static void
2787cc71a422SSam Leffler bar_timeout(void *arg)
2788cc71a422SSam Leffler {
2789cc71a422SSam Leffler 	struct ieee80211_tx_ampdu *tap = arg;
2790cc71a422SSam Leffler 	struct ieee80211_node *ni = tap->txa_ni;
2791cc71a422SSam Leffler 
2792cc71a422SSam Leffler 	KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0,
2793cc71a422SSam Leffler 	    ("bar/addba collision, flags 0x%x", tap->txa_flags));
2794cc71a422SSam Leffler 
2795cc71a422SSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2796cc71a422SSam Leffler 	    ni, "%s: tid %u flags 0x%x attempts %d", __func__,
27972aa563dfSAdrian Chadd 	    tap->txa_tid, tap->txa_flags, tap->txa_attempts);
2798cc71a422SSam Leffler 
2799cc71a422SSam Leffler 	/* guard against race with bar_tx_complete */
2800cc71a422SSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
2801cc71a422SSam Leffler 		return;
2802cc71a422SSam Leffler 	/* XXX ? */
2803fcacf931SAdrian Chadd 	if (tap->txa_attempts >= ieee80211_bar_maxtries) {
2804545c8862SAdrian Chadd 		struct ieee80211com *ic = ni->ni_ic;
2805545c8862SAdrian Chadd 
2806fcacf931SAdrian Chadd 		ni->ni_vap->iv_stats.is_ampdu_bar_tx_fail++;
2807545c8862SAdrian Chadd 		/*
2808545c8862SAdrian Chadd 		 * If (at least) the last BAR TX timeout was due to
2809545c8862SAdrian Chadd 		 * an ieee80211_send_bar() failures, then we need
2810545c8862SAdrian Chadd 		 * to make sure we notify the driver that a BAR
2811545c8862SAdrian Chadd 		 * TX did occur and fail.  This gives the driver
2812545c8862SAdrian Chadd 		 * a chance to undo any queue pause that may
2813a4641f4eSPedro F. Giffuni 		 * have occurred.
2814545c8862SAdrian Chadd 		 */
2815545c8862SAdrian Chadd 		ic->ic_bar_response(ni, tap, 1);
2816cc71a422SSam Leffler 		ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT);
2817fcacf931SAdrian Chadd 	} else {
2818fcacf931SAdrian Chadd 		ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++;
2819545c8862SAdrian Chadd 		if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) {
28200ef1bc21SAdrian Chadd 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
28210ef1bc21SAdrian Chadd 			    ni, "%s: failed to TX, starting timer\n",
28220ef1bc21SAdrian Chadd 			    __func__);
2823545c8862SAdrian Chadd 			/*
2824545c8862SAdrian Chadd 			 * If ieee80211_send_bar() fails here, the
2825545c8862SAdrian Chadd 			 * timer may have stopped and/or the pending
2826545c8862SAdrian Chadd 			 * flag may be clear.  Because of this,
2827545c8862SAdrian Chadd 			 * fake the BARPEND and reset the timer.
2828545c8862SAdrian Chadd 			 * A retransmission attempt will then occur
2829545c8862SAdrian Chadd 			 * during the next timeout.
2830545c8862SAdrian Chadd 			 */
2831545c8862SAdrian Chadd 			/* XXX locking */
2832545c8862SAdrian Chadd 			tap->txa_flags |= IEEE80211_AGGR_BARPEND;
2833545c8862SAdrian Chadd 			bar_start_timer(tap);
2834545c8862SAdrian Chadd 		}
2835cc71a422SSam Leffler 	}
2836fcacf931SAdrian Chadd }
2837cc71a422SSam Leffler 
2838cc71a422SSam Leffler static void
2839cc71a422SSam Leffler bar_start_timer(struct ieee80211_tx_ampdu *tap)
2840cc71a422SSam Leffler {
28410ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
28420ef1bc21SAdrian Chadd 	    tap->txa_ni,
28430ef1bc21SAdrian Chadd 	    "%s: called",
28440ef1bc21SAdrian Chadd 	    __func__);
2845cc71a422SSam Leffler 	callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap);
2846cc71a422SSam Leffler }
2847cc71a422SSam Leffler 
2848cc71a422SSam Leffler static void
2849cc71a422SSam Leffler bar_stop_timer(struct ieee80211_tx_ampdu *tap)
2850cc71a422SSam Leffler {
28510ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
28520ef1bc21SAdrian Chadd 	    tap->txa_ni,
28530ef1bc21SAdrian Chadd 	    "%s: called",
28540ef1bc21SAdrian Chadd 	    __func__);
2855cc71a422SSam Leffler 	callout_stop(&tap->txa_timer);
2856cc71a422SSam Leffler }
2857cc71a422SSam Leffler 
2858cc71a422SSam Leffler static void
2859cc71a422SSam Leffler bar_tx_complete(struct ieee80211_node *ni, void *arg, int status)
2860cc71a422SSam Leffler {
2861cc71a422SSam Leffler 	struct ieee80211_tx_ampdu *tap = arg;
2862cc71a422SSam Leffler 
2863cc71a422SSam Leffler 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2864cc71a422SSam Leffler 	    ni, "%s: tid %u flags 0x%x pending %d status %d",
28652aa563dfSAdrian Chadd 	    __func__, tap->txa_tid, tap->txa_flags,
2866cc71a422SSam Leffler 	    callout_pending(&tap->txa_timer), status);
2867cc71a422SSam Leffler 
2868fcacf931SAdrian Chadd 	ni->ni_vap->iv_stats.is_ampdu_bar_tx++;
2869cc71a422SSam Leffler 	/* XXX locking */
2870cc71a422SSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) &&
2871cc71a422SSam Leffler 	    callout_pending(&tap->txa_timer)) {
2872cc71a422SSam Leffler 		struct ieee80211com *ic = ni->ni_ic;
2873cc71a422SSam Leffler 
28746291312cSAdrian Chadd 		if (status == 0)		/* ACK'd */
2875cc71a422SSam Leffler 			bar_stop_timer(tap);
2876cc71a422SSam Leffler 		ic->ic_bar_response(ni, tap, status);
2877cc71a422SSam Leffler 		/* NB: just let timer expire so we pace requests */
2878cc71a422SSam Leffler 	}
2879cc71a422SSam Leffler }
2880cc71a422SSam Leffler 
2881cc71a422SSam Leffler static void
2882cc71a422SSam Leffler ieee80211_bar_response(struct ieee80211_node *ni,
2883cc71a422SSam Leffler 	struct ieee80211_tx_ampdu *tap, int status)
2884cc71a422SSam Leffler {
2885cc71a422SSam Leffler 
28860ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
28870ef1bc21SAdrian Chadd 	    tap->txa_ni,
28880ef1bc21SAdrian Chadd 	    "%s: called",
28890ef1bc21SAdrian Chadd 	    __func__);
28906291312cSAdrian Chadd 	if (status == 0) {		/* got ACK */
2891cc71a422SSam Leffler 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
2892cc71a422SSam Leffler 		    ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
2893cc71a422SSam Leffler 		    tap->txa_start,
2894cc71a422SSam Leffler 		    IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1),
2895cc71a422SSam Leffler 		    tap->txa_qframes, tap->txa_seqpending,
28962aa563dfSAdrian Chadd 		    tap->txa_tid);
2897cc71a422SSam Leffler 
2898cc71a422SSam Leffler 		/* NB: timer already stopped in bar_tx_complete */
2899cc71a422SSam Leffler 		tap->txa_start = tap->txa_seqpending;
2900cc71a422SSam Leffler 		tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
2901cc71a422SSam Leffler 	}
2902cc71a422SSam Leffler }
2903cc71a422SSam Leffler 
29041b6167d2SSam Leffler /*
290568e8e04eSSam Leffler  * Transmit a BAR frame to the specified node.  The
290668e8e04eSSam Leffler  * BAR contents are drawn from the supplied aggregation
290768e8e04eSSam Leffler  * state associated with the node.
2908cc71a422SSam Leffler  *
2909cc71a422SSam Leffler  * NB: we only handle immediate ACK w/ compressed bitmap.
291068e8e04eSSam Leffler  */
291168e8e04eSSam Leffler int
291268e8e04eSSam Leffler ieee80211_send_bar(struct ieee80211_node *ni,
2913cc71a422SSam Leffler 	struct ieee80211_tx_ampdu *tap, ieee80211_seq seq)
291468e8e04eSSam Leffler {
2915b032f27cSSam Leffler #define	senderr(_x, _v)	do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
2916b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
291768e8e04eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
2918cc71a422SSam Leffler 	struct ieee80211_frame_bar *bar;
291968e8e04eSSam Leffler 	struct mbuf *m;
292068e8e04eSSam Leffler 	uint16_t barctl, barseqctl;
2921cc71a422SSam Leffler 	uint8_t *frm;
292268e8e04eSSam Leffler 	int tid, ret;
29230ef1bc21SAdrian Chadd 
29240ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
29250ef1bc21SAdrian Chadd 	    tap->txa_ni,
29260ef1bc21SAdrian Chadd 	    "%s: called",
29270ef1bc21SAdrian Chadd 	    __func__);
29280ef1bc21SAdrian Chadd 
2929cc71a422SSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) {
2930cc71a422SSam Leffler 		/* no ADDBA response, should not happen */
2931cc71a422SSam Leffler 		/* XXX stat+msg */
2932cc71a422SSam Leffler 		return EINVAL;
2933cc71a422SSam Leffler 	}
2934cc71a422SSam Leffler 	/* XXX locking */
2935cc71a422SSam Leffler 	bar_stop_timer(tap);
2936cc71a422SSam Leffler 
293768e8e04eSSam Leffler 	ieee80211_ref_node(ni);
293868e8e04eSSam Leffler 
2939cc71a422SSam Leffler 	m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar));
294068e8e04eSSam Leffler 	if (m == NULL)
294168e8e04eSSam Leffler 		senderr(ENOMEM, is_tx_nobuf);
294268e8e04eSSam Leffler 
2943cc71a422SSam Leffler 	if (!ieee80211_add_callback(m, bar_tx_complete, tap)) {
2944cc71a422SSam Leffler 		m_freem(m);
2945cc71a422SSam Leffler 		senderr(ENOMEM, is_tx_nobuf);	/* XXX */
2946cc71a422SSam Leffler 		/* NOTREACHED */
2947cc71a422SSam Leffler 	}
2948cc71a422SSam Leffler 
2949cc71a422SSam Leffler 	bar = mtod(m, struct ieee80211_frame_bar *);
2950cc71a422SSam Leffler 	bar->i_fc[0] = IEEE80211_FC0_VERSION_0 |
295168e8e04eSSam Leffler 		IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
2952cc71a422SSam Leffler 	bar->i_fc[1] = 0;
2953cc71a422SSam Leffler 	IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr);
2954cc71a422SSam Leffler 	IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr);
295568e8e04eSSam Leffler 
29562aa563dfSAdrian Chadd 	tid = tap->txa_tid;
295768e8e04eSSam Leffler 	barctl 	= (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
2958cc71a422SSam Leffler 			0 : IEEE80211_BAR_NOACK)
2959cc71a422SSam Leffler 		| IEEE80211_BAR_COMP
2960fe5ebb23SBjoern A. Zeeb 		| _IEEE80211_SHIFTMASK(tid, IEEE80211_BAR_TID)
296168e8e04eSSam Leffler 		;
2962fe5ebb23SBjoern A. Zeeb 	barseqctl = _IEEE80211_SHIFTMASK(seq, IEEE80211_BAR_SEQ_START);
2963cc71a422SSam Leffler 	/* NB: known to have proper alignment */
2964cc71a422SSam Leffler 	bar->i_ctl = htole16(barctl);
2965cc71a422SSam Leffler 	bar->i_seq = htole16(barseqctl);
2966cc71a422SSam Leffler 	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar);
296768e8e04eSSam Leffler 
2968b032f27cSSam Leffler 	M_WME_SETAC(m, WME_AC_VO);
2969b032f27cSSam Leffler 
297068e8e04eSSam Leffler 	IEEE80211_NODE_STAT(ni, tx_mgmt);	/* XXX tx_ctl? */
297168e8e04eSSam Leffler 
2972cc71a422SSam Leffler 	/* XXX locking */
2973cc71a422SSam Leffler 	/* init/bump attempts counter */
2974cc71a422SSam Leffler 	if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
2975cc71a422SSam Leffler 		tap->txa_attempts = 1;
2976cc71a422SSam Leffler 	else
2977cc71a422SSam Leffler 		tap->txa_attempts++;
2978cc71a422SSam Leffler 	tap->txa_seqpending = seq;
2979cc71a422SSam Leffler 	tap->txa_flags |= IEEE80211_AGGR_BARPEND;
298068e8e04eSSam Leffler 
2981cc71a422SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
2982cc71a422SSam Leffler 	    ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)",
2983cc71a422SSam Leffler 	    tid, barctl, seq, tap->txa_attempts);
2984cc71a422SSam Leffler 
2985198af96eSAdrian Chadd 	/*
2986198af96eSAdrian Chadd 	 * ic_raw_xmit will free the node reference
2987198af96eSAdrian Chadd 	 * regardless of queue/TX success or failure.
2988198af96eSAdrian Chadd 	 */
29895cda6006SAdrian Chadd 	IEEE80211_TX_LOCK(ic);
29905cda6006SAdrian Chadd 	ret = ieee80211_raw_output(vap, ni, m, NULL);
29915cda6006SAdrian Chadd 	IEEE80211_TX_UNLOCK(ic);
2992cc71a422SSam Leffler 	if (ret != 0) {
29930ef1bc21SAdrian Chadd 		IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
29940ef1bc21SAdrian Chadd 		    ni, "send BAR: failed: (ret = %d)\n",
29950ef1bc21SAdrian Chadd 		    ret);
2996cc71a422SSam Leffler 		/* xmit failed, clear state flag */
2997cc71a422SSam Leffler 		tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
2998fcacf931SAdrian Chadd 		vap->iv_stats.is_ampdu_bar_tx_fail++;
2999198af96eSAdrian Chadd 		return ret;
3000cc71a422SSam Leffler 	}
3001cc71a422SSam Leffler 	/* XXX hack against tx complete happening before timer is started */
3002cc71a422SSam Leffler 	if (tap->txa_flags & IEEE80211_AGGR_BARPEND)
3003cc71a422SSam Leffler 		bar_start_timer(tap);
3004cc71a422SSam Leffler 	return 0;
300568e8e04eSSam Leffler bad:
30060ef1bc21SAdrian Chadd 	IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
30070ef1bc21SAdrian Chadd 	    tap->txa_ni,
30080ef1bc21SAdrian Chadd 	    "%s: bad! ret=%d",
30090ef1bc21SAdrian Chadd 	    __func__, ret);
3010fcacf931SAdrian Chadd 	vap->iv_stats.is_ampdu_bar_tx_fail++;
301168e8e04eSSam Leffler 	ieee80211_free_node(ni);
301268e8e04eSSam Leffler 	return ret;
301368e8e04eSSam Leffler #undef senderr
301468e8e04eSSam Leffler }
301568e8e04eSSam Leffler 
301676340123SSam Leffler static int
301776340123SSam Leffler ht_action_output(struct ieee80211_node *ni, struct mbuf *m)
301868e8e04eSSam Leffler {
30198ac160cdSSam Leffler 	struct ieee80211_bpf_params params;
302068e8e04eSSam Leffler 
30218ac160cdSSam Leffler 	memset(&params, 0, sizeof(params));
30228ac160cdSSam Leffler 	params.ibp_pri = WME_AC_VO;
30238ac160cdSSam Leffler 	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
30248ac160cdSSam Leffler 	/* NB: we know all frames are unicast */
30258ac160cdSSam Leffler 	params.ibp_try0 = ni->ni_txparms->maxretry;
30268ac160cdSSam Leffler 	params.ibp_power = ni->ni_txpower;
30278ac160cdSSam Leffler 	return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
30288ac160cdSSam Leffler 	     &params);
302968e8e04eSSam Leffler }
303068e8e04eSSam Leffler 
303176340123SSam Leffler #define	ADDSHORT(frm, v) do {			\
303276340123SSam Leffler 	frm[0] = (v) & 0xff;			\
303376340123SSam Leffler 	frm[1] = (v) >> 8;			\
303476340123SSam Leffler 	frm += 2;				\
303576340123SSam Leffler } while (0)
303676340123SSam Leffler 
303776340123SSam Leffler /*
303876340123SSam Leffler  * Send an action management frame.  The arguments are stuff
303976340123SSam Leffler  * into a frame without inspection; the caller is assumed to
304076340123SSam Leffler  * prepare them carefully (e.g. based on the aggregation state).
304176340123SSam Leffler  */
304276340123SSam Leffler static int
304376340123SSam Leffler ht_send_action_ba_addba(struct ieee80211_node *ni,
304476340123SSam Leffler 	int category, int action, void *arg0)
304576340123SSam Leffler {
304676340123SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
304776340123SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
304876340123SSam Leffler 	uint16_t *args = arg0;
304976340123SSam Leffler 	struct mbuf *m;
305076340123SSam Leffler 	uint8_t *frm;
305176340123SSam Leffler 
305276340123SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
30530917631fSRui Paulo 	    "send ADDBA %s: dialogtoken %d status %d "
3054ebb9b256SAdrian Chadd 	    "baparamset 0x%x (tid %d amsdu %d) batimeout 0x%x baseqctl 0x%x",
305576340123SSam Leffler 	    (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ?
3056fe5ebb23SBjoern A. Zeeb 		"request" : "response", args[0], args[1], args[2],
3057fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(args[2], IEEE80211_BAPS_TID),
3058fe5ebb23SBjoern A. Zeeb 	    _IEEE80211_MASKSHIFT(args[2], IEEE80211_BAPS_AMSDU),
3059fe5ebb23SBjoern A. Zeeb 	    args[3], args[4]);
306076340123SSam Leffler 
306176340123SSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
306276340123SSam Leffler 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
306376340123SSam Leffler 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
306476340123SSam Leffler 	ieee80211_ref_node(ni);
306576340123SSam Leffler 
306676340123SSam Leffler 	m = ieee80211_getmgtframe(&frm,
306776340123SSam Leffler 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
306876340123SSam Leffler 	    sizeof(uint16_t)	/* action+category */
306976340123SSam Leffler 	    /* XXX may action payload */
307076340123SSam Leffler 	    + sizeof(struct ieee80211_action_ba_addbaresponse)
307176340123SSam Leffler 	);
307276340123SSam Leffler 	if (m != NULL) {
307376340123SSam Leffler 		*frm++ = category;
307476340123SSam Leffler 		*frm++ = action;
307576340123SSam Leffler 		*frm++ = args[0];		/* dialog token */
30760917631fSRui Paulo 		if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE)
30770917631fSRui Paulo 			ADDSHORT(frm, args[1]);	/* status code */
30780917631fSRui Paulo 		ADDSHORT(frm, args[2]);		/* baparamset */
30790917631fSRui Paulo 		ADDSHORT(frm, args[3]);		/* batimeout */
308076340123SSam Leffler 		if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST)
30810917631fSRui Paulo 			ADDSHORT(frm, args[4]);	/* baseqctl */
308276340123SSam Leffler 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
308376340123SSam Leffler 		return ht_action_output(ni, m);
308476340123SSam Leffler 	} else {
308576340123SSam Leffler 		vap->iv_stats.is_tx_nobuf++;
308676340123SSam Leffler 		ieee80211_free_node(ni);
308776340123SSam Leffler 		return ENOMEM;
308876340123SSam Leffler 	}
308976340123SSam Leffler }
309076340123SSam Leffler 
309176340123SSam Leffler static int
309276340123SSam Leffler ht_send_action_ba_delba(struct ieee80211_node *ni,
309376340123SSam Leffler 	int category, int action, void *arg0)
309476340123SSam Leffler {
309576340123SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
309676340123SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
309776340123SSam Leffler 	uint16_t *args = arg0;
309876340123SSam Leffler 	struct mbuf *m;
309976340123SSam Leffler 	uint16_t baparamset;
310076340123SSam Leffler 	uint8_t *frm;
310176340123SSam Leffler 
3102fe5ebb23SBjoern A. Zeeb 	baparamset = _IEEE80211_SHIFTMASK(args[0], IEEE80211_DELBAPS_TID)
310376340123SSam Leffler 		   | args[1]
310476340123SSam Leffler 		   ;
310576340123SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
3106d72d72d3SAndriy Voskoboinyk 	    "send DELBA action: tid %d, initiator %d reason %d (%s)",
3107d72d72d3SAndriy Voskoboinyk 	    args[0], args[1], args[2], ieee80211_reason_to_string(args[2]));
310876340123SSam Leffler 
310976340123SSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
311076340123SSam Leffler 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
311176340123SSam Leffler 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
311276340123SSam Leffler 	ieee80211_ref_node(ni);
311376340123SSam Leffler 
311476340123SSam Leffler 	m = ieee80211_getmgtframe(&frm,
311576340123SSam Leffler 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
311676340123SSam Leffler 	    sizeof(uint16_t)	/* action+category */
311776340123SSam Leffler 	    /* XXX may action payload */
311876340123SSam Leffler 	    + sizeof(struct ieee80211_action_ba_addbaresponse)
311976340123SSam Leffler 	);
312076340123SSam Leffler 	if (m != NULL) {
312176340123SSam Leffler 		*frm++ = category;
312276340123SSam Leffler 		*frm++ = action;
312376340123SSam Leffler 		ADDSHORT(frm, baparamset);
312476340123SSam Leffler 		ADDSHORT(frm, args[2]);		/* reason code */
312576340123SSam Leffler 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
312676340123SSam Leffler 		return ht_action_output(ni, m);
312776340123SSam Leffler 	} else {
312876340123SSam Leffler 		vap->iv_stats.is_tx_nobuf++;
312976340123SSam Leffler 		ieee80211_free_node(ni);
313076340123SSam Leffler 		return ENOMEM;
313176340123SSam Leffler 	}
313276340123SSam Leffler }
313376340123SSam Leffler 
313476340123SSam Leffler static int
313576340123SSam Leffler ht_send_action_ht_txchwidth(struct ieee80211_node *ni,
313676340123SSam Leffler 	int category, int action, void *arg0)
313776340123SSam Leffler {
313876340123SSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
313976340123SSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
314076340123SSam Leffler 	struct mbuf *m;
314176340123SSam Leffler 	uint8_t *frm;
314276340123SSam Leffler 
314376340123SSam Leffler 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
314476340123SSam Leffler 	    "send HT txchwidth: width %d",
314576340123SSam Leffler 	    IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20);
314676340123SSam Leffler 
314776340123SSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
314876340123SSam Leffler 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
314976340123SSam Leffler 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
315076340123SSam Leffler 	ieee80211_ref_node(ni);
315176340123SSam Leffler 
315276340123SSam Leffler 	m = ieee80211_getmgtframe(&frm,
315376340123SSam Leffler 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
315476340123SSam Leffler 	    sizeof(uint16_t)	/* action+category */
315576340123SSam Leffler 	    /* XXX may action payload */
315676340123SSam Leffler 	    + sizeof(struct ieee80211_action_ba_addbaresponse)
315776340123SSam Leffler 	);
315876340123SSam Leffler 	if (m != NULL) {
315976340123SSam Leffler 		*frm++ = category;
316076340123SSam Leffler 		*frm++ = action;
316176340123SSam Leffler 		*frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
316276340123SSam Leffler 			IEEE80211_A_HT_TXCHWIDTH_2040 :
316376340123SSam Leffler 			IEEE80211_A_HT_TXCHWIDTH_20;
316476340123SSam Leffler 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
316576340123SSam Leffler 		return ht_action_output(ni, m);
316676340123SSam Leffler 	} else {
316776340123SSam Leffler 		vap->iv_stats.is_tx_nobuf++;
316876340123SSam Leffler 		ieee80211_free_node(ni);
316976340123SSam Leffler 		return ENOMEM;
317076340123SSam Leffler 	}
317176340123SSam Leffler }
317276340123SSam Leffler #undef ADDSHORT
317376340123SSam Leffler 
317468e8e04eSSam Leffler /*
3175d8e14b5eSBernhard Schmidt  * Construct the MCS bit mask for inclusion in an HT capabilities
3176d8e14b5eSBernhard Schmidt  * information element.
317768e8e04eSSam Leffler  */
317868e8e04eSSam Leffler static void
3179d8e14b5eSBernhard Schmidt ieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm)
318068e8e04eSSam Leffler {
318168e8e04eSSam Leffler 	int i;
3182d8e14b5eSBernhard Schmidt 	uint8_t txparams;
318368e8e04eSSam Leffler 
3184d8e14b5eSBernhard Schmidt 	KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4),
3185d8e14b5eSBernhard Schmidt 	    ("ic_rxstream %d out of range", ic->ic_rxstream));
3186d8e14b5eSBernhard Schmidt 	KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4),
3187d8e14b5eSBernhard Schmidt 	    ("ic_txstream %d out of range", ic->ic_txstream));
3188d8e14b5eSBernhard Schmidt 
3189d8e14b5eSBernhard Schmidt 	for (i = 0; i < ic->ic_rxstream * 8; i++)
3190d8e14b5eSBernhard Schmidt 		setbit(frm, i);
3191d8e14b5eSBernhard Schmidt 	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
3192d8e14b5eSBernhard Schmidt 	    (ic->ic_htcaps & IEEE80211_HTC_RXMCS32))
3193d8e14b5eSBernhard Schmidt 		setbit(frm, 32);
3194d8e14b5eSBernhard Schmidt 	if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) {
3195d8e14b5eSBernhard Schmidt 		if (ic->ic_rxstream >= 2) {
3196d8e14b5eSBernhard Schmidt 			for (i = 33; i <= 38; i++)
3197d8e14b5eSBernhard Schmidt 				setbit(frm, i);
3198d8e14b5eSBernhard Schmidt 		}
3199d8e14b5eSBernhard Schmidt 		if (ic->ic_rxstream >= 3) {
3200d8e14b5eSBernhard Schmidt 			for (i = 39; i <= 52; i++)
3201d8e14b5eSBernhard Schmidt 				setbit(frm, i);
3202d8e14b5eSBernhard Schmidt 		}
32032f53b599SMikhail Pchelin 		if (ic->ic_rxstream >= 4) {
3204d8e14b5eSBernhard Schmidt 			for (i = 53; i <= 76; i++)
3205d8e14b5eSBernhard Schmidt 				setbit(frm, i);
320668e8e04eSSam Leffler 		}
320768e8e04eSSam Leffler 	}
3208d8e14b5eSBernhard Schmidt 
3209d8e14b5eSBernhard Schmidt 	txparams = 0x1;			/* TX MCS set defined */
3210ea26545cSMikhail Pchelin 	if (ic->ic_rxstream != ic->ic_txstream) {
3211d8e14b5eSBernhard Schmidt 		txparams |= 0x2;		/* TX RX MCS not equal */
3212d8e14b5eSBernhard Schmidt 		txparams |= (ic->ic_txstream - 1) << 2;	/* num TX streams */
3213d8e14b5eSBernhard Schmidt 		if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL)
3214d8e14b5eSBernhard Schmidt 			txparams |= 0x16;	/* TX unequal modulation sup */
3215ea26545cSMikhail Pchelin 	}
3216ea26545cSMikhail Pchelin 
3217d8e14b5eSBernhard Schmidt 	frm[12] = txparams;
321868e8e04eSSam Leffler }
321968e8e04eSSam Leffler 
322068e8e04eSSam Leffler /*
322168e8e04eSSam Leffler  * Add body of an HTCAP information element.
322268e8e04eSSam Leffler  */
322368e8e04eSSam Leffler static uint8_t *
322468e8e04eSSam Leffler ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
322568e8e04eSSam Leffler {
322668e8e04eSSam Leffler #define	ADDSHORT(frm, v) do {			\
322768e8e04eSSam Leffler 	frm[0] = (v) & 0xff;			\
322868e8e04eSSam Leffler 	frm[1] = (v) >> 8;			\
322968e8e04eSSam Leffler 	frm += 2;				\
323068e8e04eSSam Leffler } while (0)
3231bf1f9222SBernhard Schmidt 	struct ieee80211com *ic = ni->ni_ic;
3232b032f27cSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
3233e1d36f83SRui Paulo 	uint16_t caps, extcaps;
3234849b4d99SSam Leffler 	int rxmax, density;
323568e8e04eSSam Leffler 
323668e8e04eSSam Leffler 	/* HT capabilities */
3237b032f27cSSam Leffler 	caps = vap->iv_htcaps & 0xffff;
32381b6167d2SSam Leffler 	/*
32391b6167d2SSam Leffler 	 * Note channel width depends on whether we are operating as
32401b6167d2SSam Leffler 	 * a sta or not.  When operating as a sta we are generating
32411b6167d2SSam Leffler 	 * a request based on our desired configuration.  Otherwise
32421b6167d2SSam Leffler 	 * we are operational and the channel attributes identify
32431b6167d2SSam Leffler 	 * how we've been setup (which might be different if a fixed
32441b6167d2SSam Leffler 	 * channel is specified).
32451b6167d2SSam Leffler 	 */
3246b032f27cSSam Leffler 	if (vap->iv_opmode == IEEE80211_M_STA) {
32471b6167d2SSam Leffler 		/* override 20/40 use based on config */
32482bfc8a91SSam Leffler 		if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)
324968e8e04eSSam Leffler 			caps |= IEEE80211_HTCAP_CHWIDTH40;
325068e8e04eSSam Leffler 		else
325168e8e04eSSam Leffler 			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
32523265af35SAdrian Chadd 
32533265af35SAdrian Chadd 		/* Start by using the advertised settings */
3254fe5ebb23SBjoern A. Zeeb 		rxmax = _IEEE80211_MASKSHIFT(ni->ni_htparam,
3255fe5ebb23SBjoern A. Zeeb 		    IEEE80211_HTCAP_MAXRXAMPDU);
3256fe5ebb23SBjoern A. Zeeb 		density = _IEEE80211_MASKSHIFT(ni->ni_htparam,
3257fe5ebb23SBjoern A. Zeeb 		    IEEE80211_HTCAP_MPDUDENSITY);
3258bf1f9222SBernhard Schmidt 
3259ced0a895SAdrian Chadd 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
3260ced0a895SAdrian Chadd 		    "%s: advertised rxmax=%d, density=%d, vap rxmax=%d, density=%d\n",
3261ced0a895SAdrian Chadd 		    __func__,
3262ced0a895SAdrian Chadd 		    rxmax,
3263ced0a895SAdrian Chadd 		    density,
3264ced0a895SAdrian Chadd 		    vap->iv_ampdu_rxmax,
3265ced0a895SAdrian Chadd 		    vap->iv_ampdu_density);
3266ced0a895SAdrian Chadd 
32673265af35SAdrian Chadd 		/* Cap at VAP rxmax */
32683265af35SAdrian Chadd 		if (rxmax > vap->iv_ampdu_rxmax)
32693265af35SAdrian Chadd 			rxmax = vap->iv_ampdu_rxmax;
32703265af35SAdrian Chadd 
32713265af35SAdrian Chadd 		/*
32723265af35SAdrian Chadd 		 * If the VAP ampdu density value greater, use that.
32733265af35SAdrian Chadd 		 *
32743265af35SAdrian Chadd 		 * (Larger density value == larger minimum gap between A-MPDU
32753265af35SAdrian Chadd 		 * subframes.)
32763265af35SAdrian Chadd 		 */
32773265af35SAdrian Chadd 		if (vap->iv_ampdu_density > density)
32783265af35SAdrian Chadd 			density = vap->iv_ampdu_density;
32793265af35SAdrian Chadd 
3280bf1f9222SBernhard Schmidt 		/*
3281bf1f9222SBernhard Schmidt 		 * NB: Hardware might support HT40 on some but not all
3282bf1f9222SBernhard Schmidt 		 * channels. We can't determine this earlier because only
3283bf1f9222SBernhard Schmidt 		 * after association the channel is upgraded to HT based
3284bf1f9222SBernhard Schmidt 		 * on the negotiated capabilities.
3285bf1f9222SBernhard Schmidt 		 */
3286bf1f9222SBernhard Schmidt 		if (ni->ni_chan != IEEE80211_CHAN_ANYC &&
3287bf1f9222SBernhard Schmidt 		    findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40U) == NULL &&
3288bf1f9222SBernhard Schmidt 		    findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40D) == NULL)
3289bf1f9222SBernhard Schmidt 			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
32901b6167d2SSam Leffler 	} else {
32911b6167d2SSam Leffler 		/* override 20/40 use based on current channel */
3292b032f27cSSam Leffler 		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
32931b6167d2SSam Leffler 			caps |= IEEE80211_HTCAP_CHWIDTH40;
32941b6167d2SSam Leffler 		else
32951b6167d2SSam Leffler 			caps &= ~IEEE80211_HTCAP_CHWIDTH40;
32963265af35SAdrian Chadd 
32973265af35SAdrian Chadd 		/* XXX TODO should it start by using advertised settings? */
3298b032f27cSSam Leffler 		rxmax = vap->iv_ampdu_rxmax;
3299b032f27cSSam Leffler 		density = vap->iv_ampdu_density;
33001b6167d2SSam Leffler 	}
33013265af35SAdrian Chadd 
330268e8e04eSSam Leffler 	/* adjust short GI based on channel and config */
33032bfc8a91SSam Leffler 	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0)
330468e8e04eSSam Leffler 		caps &= ~IEEE80211_HTCAP_SHORTGI20;
33052bfc8a91SSam Leffler 	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 ||
330668e8e04eSSam Leffler 	    (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
330768e8e04eSSam Leffler 		caps &= ~IEEE80211_HTCAP_SHORTGI40;
33085706199dSAdrian Chadd 
33095706199dSAdrian Chadd 	/* adjust STBC based on receive capabilities */
33105706199dSAdrian Chadd 	if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
33115706199dSAdrian Chadd 		caps &= ~IEEE80211_HTCAP_RXSTBC;
33125706199dSAdrian Chadd 
3313c5bba9daSAndriy Voskoboinyk 	/* adjust LDPC based on receive capabilites */
3314c5bba9daSAndriy Voskoboinyk 	if ((vap->iv_flags_ht & IEEE80211_FHT_LDPC_RX) == 0)
3315c5bba9daSAndriy Voskoboinyk 		caps &= ~IEEE80211_HTCAP_LDPC;
33165706199dSAdrian Chadd 
331768e8e04eSSam Leffler 	ADDSHORT(frm, caps);
331868e8e04eSSam Leffler 
331968e8e04eSSam Leffler 	/* HT parameters */
3320fe5ebb23SBjoern A. Zeeb 	*frm = _IEEE80211_SHIFTMASK(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
3321fe5ebb23SBjoern A. Zeeb 	     | _IEEE80211_SHIFTMASK(density, IEEE80211_HTCAP_MPDUDENSITY)
33221b6167d2SSam Leffler 	     ;
332368e8e04eSSam Leffler 	frm++;
332468e8e04eSSam Leffler 
332568e8e04eSSam Leffler 	/* pre-zero remainder of ie */
332668e8e04eSSam Leffler 	memset(frm, 0, sizeof(struct ieee80211_ie_htcap) -
332768e8e04eSSam Leffler 		__offsetof(struct ieee80211_ie_htcap, hc_mcsset));
332868e8e04eSSam Leffler 
332968e8e04eSSam Leffler 	/* supported MCS set */
33301b6167d2SSam Leffler 	/*
3331d8e14b5eSBernhard Schmidt 	 * XXX: For sta mode the rate set should be restricted based
3332d8e14b5eSBernhard Schmidt 	 * on the AP's capabilities, but ni_htrates isn't setup when
3333d8e14b5eSBernhard Schmidt 	 * we're called to form an AssocReq frame so for now we're
3334d8e14b5eSBernhard Schmidt 	 * restricted to the device capabilities.
33351b6167d2SSam Leffler 	 */
3336d8e14b5eSBernhard Schmidt 	ieee80211_set_mcsset(ni->ni_ic, frm);
333768e8e04eSSam Leffler 
3338e1d36f83SRui Paulo 	frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) -
333968e8e04eSSam Leffler 		__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
3340e1d36f83SRui Paulo 
3341e1d36f83SRui Paulo 	/* HT extended capabilities */
3342e1d36f83SRui Paulo 	extcaps = vap->iv_htextcaps & 0xffff;
3343e1d36f83SRui Paulo 
3344e1d36f83SRui Paulo 	ADDSHORT(frm, extcaps);
3345e1d36f83SRui Paulo 
3346e1d36f83SRui Paulo 	frm += sizeof(struct ieee80211_ie_htcap) -
3347e1d36f83SRui Paulo 		__offsetof(struct ieee80211_ie_htcap, hc_txbf);
3348e1d36f83SRui Paulo 
334968e8e04eSSam Leffler 	return frm;
335068e8e04eSSam Leffler #undef ADDSHORT
335168e8e04eSSam Leffler }
335268e8e04eSSam Leffler 
335368e8e04eSSam Leffler /*
335468e8e04eSSam Leffler  * Add 802.11n HT capabilities information element
335568e8e04eSSam Leffler  */
335668e8e04eSSam Leffler uint8_t *
335768e8e04eSSam Leffler ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
335868e8e04eSSam Leffler {
335968e8e04eSSam Leffler 	frm[0] = IEEE80211_ELEMID_HTCAP;
336068e8e04eSSam Leffler 	frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
336168e8e04eSSam Leffler 	return ieee80211_add_htcap_body(frm + 2, ni);
336268e8e04eSSam Leffler }
336368e8e04eSSam Leffler 
336468e8e04eSSam Leffler /*
3365f383e58fSAdrian Chadd  * Non-associated probe request - add HT capabilities based on
3366f383e58fSAdrian Chadd  * the current channel configuration.
3367f383e58fSAdrian Chadd  */
3368f383e58fSAdrian Chadd static uint8_t *
3369f383e58fSAdrian Chadd ieee80211_add_htcap_body_ch(uint8_t *frm, struct ieee80211vap *vap,
3370f383e58fSAdrian Chadd     struct ieee80211_channel *c)
3371f383e58fSAdrian Chadd {
3372f383e58fSAdrian Chadd #define	ADDSHORT(frm, v) do {			\
3373f383e58fSAdrian Chadd 	frm[0] = (v) & 0xff;			\
3374f383e58fSAdrian Chadd 	frm[1] = (v) >> 8;			\
3375f383e58fSAdrian Chadd 	frm += 2;				\
3376f383e58fSAdrian Chadd } while (0)
3377f383e58fSAdrian Chadd 	struct ieee80211com *ic = vap->iv_ic;
3378f383e58fSAdrian Chadd 	uint16_t caps, extcaps;
3379f383e58fSAdrian Chadd 	int rxmax, density;
3380f383e58fSAdrian Chadd 
3381f383e58fSAdrian Chadd 	/* HT capabilities */
3382f383e58fSAdrian Chadd 	caps = vap->iv_htcaps & 0xffff;
3383f383e58fSAdrian Chadd 
3384f383e58fSAdrian Chadd 	/*
3385f383e58fSAdrian Chadd 	 * We don't use this in STA mode; only in IBSS mode.
3386f383e58fSAdrian Chadd 	 * So in IBSS mode we base our HTCAP flags on the
3387f383e58fSAdrian Chadd 	 * given channel.
3388f383e58fSAdrian Chadd 	 */
3389f383e58fSAdrian Chadd 
3390f383e58fSAdrian Chadd 	/* override 20/40 use based on current channel */
3391f383e58fSAdrian Chadd 	if (IEEE80211_IS_CHAN_HT40(c))
3392f383e58fSAdrian Chadd 		caps |= IEEE80211_HTCAP_CHWIDTH40;
3393f383e58fSAdrian Chadd 	else
3394f383e58fSAdrian Chadd 		caps &= ~IEEE80211_HTCAP_CHWIDTH40;
3395f383e58fSAdrian Chadd 
3396f383e58fSAdrian Chadd 	/* Use the currently configured values */
3397f383e58fSAdrian Chadd 	rxmax = vap->iv_ampdu_rxmax;
3398f383e58fSAdrian Chadd 	density = vap->iv_ampdu_density;
3399f383e58fSAdrian Chadd 
3400f383e58fSAdrian Chadd 	/* adjust short GI based on channel and config */
3401f383e58fSAdrian Chadd 	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0)
3402f383e58fSAdrian Chadd 		caps &= ~IEEE80211_HTCAP_SHORTGI20;
3403f383e58fSAdrian Chadd 	if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 ||
3404f383e58fSAdrian Chadd 	    (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
3405f383e58fSAdrian Chadd 		caps &= ~IEEE80211_HTCAP_SHORTGI40;
3406f383e58fSAdrian Chadd 	ADDSHORT(frm, caps);
3407f383e58fSAdrian Chadd 
3408f383e58fSAdrian Chadd 	/* HT parameters */
3409fe5ebb23SBjoern A. Zeeb 	*frm = _IEEE80211_SHIFTMASK(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
3410fe5ebb23SBjoern A. Zeeb 	     | _IEEE80211_SHIFTMASK(density, IEEE80211_HTCAP_MPDUDENSITY)
3411f383e58fSAdrian Chadd 	     ;
3412f383e58fSAdrian Chadd 	frm++;
3413f383e58fSAdrian Chadd 
3414f383e58fSAdrian Chadd 	/* pre-zero remainder of ie */
3415f383e58fSAdrian Chadd 	memset(frm, 0, sizeof(struct ieee80211_ie_htcap) -
3416f383e58fSAdrian Chadd 		__offsetof(struct ieee80211_ie_htcap, hc_mcsset));
3417f383e58fSAdrian Chadd 
3418f383e58fSAdrian Chadd 	/* supported MCS set */
3419f383e58fSAdrian Chadd 	/*
3420f383e58fSAdrian Chadd 	 * XXX: For sta mode the rate set should be restricted based
3421f383e58fSAdrian Chadd 	 * on the AP's capabilities, but ni_htrates isn't setup when
3422f383e58fSAdrian Chadd 	 * we're called to form an AssocReq frame so for now we're
3423f383e58fSAdrian Chadd 	 * restricted to the device capabilities.
3424f383e58fSAdrian Chadd 	 */
3425f383e58fSAdrian Chadd 	ieee80211_set_mcsset(ic, frm);
3426f383e58fSAdrian Chadd 
3427f383e58fSAdrian Chadd 	frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) -
3428f383e58fSAdrian Chadd 		__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
3429f383e58fSAdrian Chadd 
3430f383e58fSAdrian Chadd 	/* HT extended capabilities */
3431f383e58fSAdrian Chadd 	extcaps = vap->iv_htextcaps & 0xffff;
3432f383e58fSAdrian Chadd 
3433f383e58fSAdrian Chadd 	ADDSHORT(frm, extcaps);
3434f383e58fSAdrian Chadd 
3435f383e58fSAdrian Chadd 	frm += sizeof(struct ieee80211_ie_htcap) -
3436f383e58fSAdrian Chadd 		__offsetof(struct ieee80211_ie_htcap, hc_txbf);
3437f383e58fSAdrian Chadd 
3438f383e58fSAdrian Chadd 	return frm;
3439f383e58fSAdrian Chadd #undef ADDSHORT
3440f383e58fSAdrian Chadd }
3441f383e58fSAdrian Chadd 
3442f383e58fSAdrian Chadd /*
3443f383e58fSAdrian Chadd  * Add 802.11n HT capabilities information element
3444f383e58fSAdrian Chadd  */
3445f383e58fSAdrian Chadd uint8_t *
3446f383e58fSAdrian Chadd ieee80211_add_htcap_ch(uint8_t *frm, struct ieee80211vap *vap,
3447f383e58fSAdrian Chadd     struct ieee80211_channel *c)
3448f383e58fSAdrian Chadd {
3449f383e58fSAdrian Chadd 	frm[0] = IEEE80211_ELEMID_HTCAP;
3450f383e58fSAdrian Chadd 	frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
3451f383e58fSAdrian Chadd 	return ieee80211_add_htcap_body_ch(frm + 2, vap, c);
3452f383e58fSAdrian Chadd }
3453f383e58fSAdrian Chadd 
3454f383e58fSAdrian Chadd /*
345568e8e04eSSam Leffler  * Add Broadcom OUI wrapped standard HTCAP ie; this is
345668e8e04eSSam Leffler  * used for compatibility w/ pre-draft implementations.
345768e8e04eSSam Leffler  */
345868e8e04eSSam Leffler uint8_t *
345968e8e04eSSam Leffler ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni)
346068e8e04eSSam Leffler {
346168e8e04eSSam Leffler 	frm[0] = IEEE80211_ELEMID_VENDOR;
346268e8e04eSSam Leffler 	frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2;
346368e8e04eSSam Leffler 	frm[2] = (BCM_OUI >> 0) & 0xff;
346468e8e04eSSam Leffler 	frm[3] = (BCM_OUI >> 8) & 0xff;
346568e8e04eSSam Leffler 	frm[4] = (BCM_OUI >> 16) & 0xff;
346668e8e04eSSam Leffler 	frm[5] = BCM_OUI_HTCAP;
346768e8e04eSSam Leffler 	return ieee80211_add_htcap_body(frm + 6, ni);
346868e8e04eSSam Leffler }
346968e8e04eSSam Leffler 
347068e8e04eSSam Leffler /*
347168e8e04eSSam Leffler  * Construct the MCS bit mask of basic rates
347268e8e04eSSam Leffler  * for inclusion in an HT information element.
347368e8e04eSSam Leffler  */
347468e8e04eSSam Leffler static void
347568e8e04eSSam Leffler ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
347668e8e04eSSam Leffler {
347768e8e04eSSam Leffler 	int i;
347868e8e04eSSam Leffler 
347968e8e04eSSam Leffler 	for (i = 0; i < rs->rs_nrates; i++) {
348068e8e04eSSam Leffler 		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
348168e8e04eSSam Leffler 		if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
348268e8e04eSSam Leffler 		    r < IEEE80211_HTRATE_MAXSIZE) {
348368e8e04eSSam Leffler 			/* NB: this assumes a particular implementation */
348468e8e04eSSam Leffler 			setbit(frm, r);
348568e8e04eSSam Leffler 		}
348668e8e04eSSam Leffler 	}
348768e8e04eSSam Leffler }
348868e8e04eSSam Leffler 
348968e8e04eSSam Leffler /*
3490b105a069SSam Leffler  * Update the HTINFO ie for a beacon frame.
3491b105a069SSam Leffler  */
3492b105a069SSam Leffler void
3493b032f27cSSam Leffler ieee80211_ht_update_beacon(struct ieee80211vap *vap,
3494b105a069SSam Leffler 	struct ieee80211_beacon_offsets *bo)
3495b105a069SSam Leffler {
3496b105a069SSam Leffler #define	PROTMODE	(IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
34975b58efc6SAdrian Chadd 	struct ieee80211_node *ni;
34985b58efc6SAdrian Chadd 	const struct ieee80211_channel *bsschan;
3499b032f27cSSam Leffler 	struct ieee80211com *ic = vap->iv_ic;
3500b105a069SSam Leffler 	struct ieee80211_ie_htinfo *ht =
3501b105a069SSam Leffler 	   (struct ieee80211_ie_htinfo *) bo->bo_htinfo;
3502b105a069SSam Leffler 
35035b58efc6SAdrian Chadd 	ni = ieee80211_ref_node(vap->iv_bss);
35045b58efc6SAdrian Chadd 	bsschan = ni->ni_chan;
35055b58efc6SAdrian Chadd 
3506b105a069SSam Leffler 	/* XXX only update on channel change */
3507b032f27cSSam Leffler 	ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
35082bfc8a91SSam Leffler 	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
350944f7a6edSSam Leffler 		ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM;
351044f7a6edSSam Leffler 	else
3511b105a069SSam Leffler 		ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
3512b032f27cSSam Leffler 	if (IEEE80211_IS_CHAN_HT40U(bsschan))
3513b105a069SSam Leffler 		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
3514b032f27cSSam Leffler 	else if (IEEE80211_IS_CHAN_HT40D(bsschan))
3515b105a069SSam Leffler 		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
3516b105a069SSam Leffler 	else
3517b105a069SSam Leffler 		ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
3518b032f27cSSam Leffler 	if (IEEE80211_IS_CHAN_HT40(bsschan))
3519b105a069SSam Leffler 		ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
3520b105a069SSam Leffler 
3521b105a069SSam Leffler 	/* protection mode */
3522f1481c8dSAdrian Chadd 	/*
3523f1481c8dSAdrian Chadd 	 * XXX TODO: this uses the global flag, not the per-VAP flag.
3524f1481c8dSAdrian Chadd 	 * Eventually (once the protection modes are done per-channel
3525f1481c8dSAdrian Chadd 	 * rather than per-VAP) we can flip this over to be per-VAP but
3526f1481c8dSAdrian Chadd 	 * using the channel protection mode.
3527f1481c8dSAdrian Chadd 	 */
3528b105a069SSam Leffler 	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
3529b105a069SSam Leffler 
35305b58efc6SAdrian Chadd 	ieee80211_free_node(ni);
35315b58efc6SAdrian Chadd 
3532b105a069SSam Leffler 	/* XXX propagate to vendor ie's */
3533b105a069SSam Leffler #undef PROTMODE
3534b105a069SSam Leffler }
3535b105a069SSam Leffler 
3536b105a069SSam Leffler /*
353768e8e04eSSam Leffler  * Add body of an HTINFO information element.
35381b6167d2SSam Leffler  *
35391b6167d2SSam Leffler  * NB: We don't use struct ieee80211_ie_htinfo because we can
35401b6167d2SSam Leffler  * be called to fillin both a standard ie and a compat ie that
35411b6167d2SSam Leffler  * has a vendor OUI at the front.
354268e8e04eSSam Leffler  */
354368e8e04eSSam Leffler static uint8_t *
354468e8e04eSSam Leffler ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
354568e8e04eSSam Leffler {
354644f7a6edSSam Leffler 	struct ieee80211vap *vap = ni->ni_vap;
354768e8e04eSSam Leffler 	struct ieee80211com *ic = ni->ni_ic;
354868e8e04eSSam Leffler 
354968e8e04eSSam Leffler 	/* pre-zero remainder of ie */
355068e8e04eSSam Leffler 	memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
355168e8e04eSSam Leffler 
355268e8e04eSSam Leffler 	/* primary/control channel center */
3553b032f27cSSam Leffler 	*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
355468e8e04eSSam Leffler 
35552bfc8a91SSam Leffler 	if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
355644f7a6edSSam Leffler 		frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM;
355744f7a6edSSam Leffler 	else
355868e8e04eSSam Leffler 		frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
3559b032f27cSSam Leffler 	if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
356068e8e04eSSam Leffler 		frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
3561b032f27cSSam Leffler 	else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
356268e8e04eSSam Leffler 		frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
356368e8e04eSSam Leffler 	else
356468e8e04eSSam Leffler 		frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
3565b032f27cSSam Leffler 	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
356668e8e04eSSam Leffler 		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
356768e8e04eSSam Leffler 
3568f1481c8dSAdrian Chadd 	/*
3569f1481c8dSAdrian Chadd 	 * Add current protection mode.  Unlike for beacons,
3570f1481c8dSAdrian Chadd 	 * this will respect the per-VAP flags.
3571f1481c8dSAdrian Chadd 	 */
3572f1481c8dSAdrian Chadd 	frm[1] = vap->iv_curhtprotmode;
357368e8e04eSSam Leffler 
357468e8e04eSSam Leffler 	frm += 5;
357568e8e04eSSam Leffler 
357668e8e04eSSam Leffler 	/* basic MCS set */
357768e8e04eSSam Leffler 	ieee80211_set_basic_htrates(frm, &ni->ni_htrates);
357868e8e04eSSam Leffler 	frm += sizeof(struct ieee80211_ie_htinfo) -
357968e8e04eSSam Leffler 		__offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
358068e8e04eSSam Leffler 	return frm;
358168e8e04eSSam Leffler }
358268e8e04eSSam Leffler 
358368e8e04eSSam Leffler /*
3584caa7e52fSEitan Adler  * Add 802.11n HT information element.
358568e8e04eSSam Leffler  */
358668e8e04eSSam Leffler uint8_t *
358768e8e04eSSam Leffler ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni)
358868e8e04eSSam Leffler {
358968e8e04eSSam Leffler 	frm[0] = IEEE80211_ELEMID_HTINFO;
359068e8e04eSSam Leffler 	frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2;
359168e8e04eSSam Leffler 	return ieee80211_add_htinfo_body(frm + 2, ni);
359268e8e04eSSam Leffler }
359368e8e04eSSam Leffler 
359468e8e04eSSam Leffler /*
359568e8e04eSSam Leffler  * Add Broadcom OUI wrapped standard HTINFO ie; this is
359668e8e04eSSam Leffler  * used for compatibility w/ pre-draft implementations.
359768e8e04eSSam Leffler  */
359868e8e04eSSam Leffler uint8_t *
359968e8e04eSSam Leffler ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
360068e8e04eSSam Leffler {
360168e8e04eSSam Leffler 	frm[0] = IEEE80211_ELEMID_VENDOR;
360268e8e04eSSam Leffler 	frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2;
360368e8e04eSSam Leffler 	frm[2] = (BCM_OUI >> 0) & 0xff;
360468e8e04eSSam Leffler 	frm[3] = (BCM_OUI >> 8) & 0xff;
360568e8e04eSSam Leffler 	frm[4] = (BCM_OUI >> 16) & 0xff;
360668e8e04eSSam Leffler 	frm[5] = BCM_OUI_HTINFO;
360768e8e04eSSam Leffler 	return ieee80211_add_htinfo_body(frm + 6, ni);
360868e8e04eSSam Leffler }
3609c6b44f64SAdrian Chadd 
3610c6b44f64SAdrian Chadd /*
3611c6b44f64SAdrian Chadd  * Get the HT density for the given 802.11n node.
3612c6b44f64SAdrian Chadd  *
3613c6b44f64SAdrian Chadd  * Take into account the density advertised from the peer.
3614c6b44f64SAdrian Chadd  * Larger values are longer A-MPDU density spacing values, and
3615c6b44f64SAdrian Chadd  * we want to obey them per station if we get them.
3616c6b44f64SAdrian Chadd  */
3617c6b44f64SAdrian Chadd int
3618c6b44f64SAdrian Chadd ieee80211_ht_get_node_ampdu_density(const struct ieee80211_node *ni)
3619c6b44f64SAdrian Chadd {
3620c6b44f64SAdrian Chadd 	struct ieee80211vap *vap;
3621c6b44f64SAdrian Chadd 	int peer_mpdudensity;
3622c6b44f64SAdrian Chadd 
3623c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3624c6b44f64SAdrian Chadd 	peer_mpdudensity =
3625c6b44f64SAdrian Chadd 	    _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
3626c6b44f64SAdrian Chadd 	if (vap->iv_ampdu_density > peer_mpdudensity)
3627c6b44f64SAdrian Chadd 		peer_mpdudensity = vap->iv_ampdu_density;
3628c6b44f64SAdrian Chadd 	return (peer_mpdudensity);
3629c6b44f64SAdrian Chadd }
3630c6b44f64SAdrian Chadd 
3631c6b44f64SAdrian Chadd /*
3632c6b44f64SAdrian Chadd  * Get the transmit A-MPDU limit for the given 802.11n node.
3633c6b44f64SAdrian Chadd  *
3634c6b44f64SAdrian Chadd  * Take into account the limit advertised from the peer.
3635c6b44f64SAdrian Chadd  * Smaller values indicate smaller maximum A-MPDU sizes, and
3636c6b44f64SAdrian Chadd  * should be used when forming an A-MPDU to the given peer.
3637c6b44f64SAdrian Chadd  */
3638c6b44f64SAdrian Chadd int
3639c6b44f64SAdrian Chadd ieee80211_ht_get_node_ampdu_limit(const struct ieee80211_node *ni)
3640c6b44f64SAdrian Chadd {
3641c6b44f64SAdrian Chadd 	struct ieee80211vap *vap;
3642c6b44f64SAdrian Chadd 	int peer_mpdulimit;
3643c6b44f64SAdrian Chadd 
3644c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3645c6b44f64SAdrian Chadd 	peer_mpdulimit =
3646c6b44f64SAdrian Chadd 	    _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
3647c6b44f64SAdrian Chadd 
3648c6b44f64SAdrian Chadd 	return (MIN(vap->iv_ampdu_limit, peer_mpdulimit));
3649c6b44f64SAdrian Chadd }
3650c6b44f64SAdrian Chadd 
3651c6b44f64SAdrian Chadd /*
3652c6b44f64SAdrian Chadd  * Return true if short-GI is available when transmitting to
3653c6b44f64SAdrian Chadd  * the given node at 20MHz.
3654c6b44f64SAdrian Chadd  *
3655c6b44f64SAdrian Chadd  * Ensure it's configured and available in the VAP / driver as
3656c6b44f64SAdrian Chadd  * well as the node.
3657c6b44f64SAdrian Chadd  */
3658c6b44f64SAdrian Chadd bool
3659c6b44f64SAdrian Chadd ieee80211_ht_check_tx_shortgi_20(const struct ieee80211_node *ni)
3660c6b44f64SAdrian Chadd {
3661c6b44f64SAdrian Chadd 	const struct ieee80211vap *vap;
3662c6b44f64SAdrian Chadd 	const struct ieee80211com *ic;
3663c6b44f64SAdrian Chadd 
3664c6b44f64SAdrian Chadd 	if (! ieee80211_ht_check_tx_ht(ni))
3665c6b44f64SAdrian Chadd 		return (false);
3666c6b44f64SAdrian Chadd 
3667c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3668c6b44f64SAdrian Chadd 	ic = ni->ni_ic;
3669c6b44f64SAdrian Chadd 
3670c6b44f64SAdrian Chadd 	return ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) &&
3671c6b44f64SAdrian Chadd 	    (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) &&
3672c6b44f64SAdrian Chadd 	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20));
3673c6b44f64SAdrian Chadd }
3674c6b44f64SAdrian Chadd 
3675c6b44f64SAdrian Chadd /*
3676c6b44f64SAdrian Chadd  * Return true if short-GI is available when transmitting to
3677c6b44f64SAdrian Chadd  * the given node at 40MHz.
3678c6b44f64SAdrian Chadd  *
3679c6b44f64SAdrian Chadd  * Ensure it's configured and available in the VAP / driver as
3680c6b44f64SAdrian Chadd  * well as the node and BSS.
3681c6b44f64SAdrian Chadd  */
3682c6b44f64SAdrian Chadd bool
3683c6b44f64SAdrian Chadd ieee80211_ht_check_tx_shortgi_40(const struct ieee80211_node *ni)
3684c6b44f64SAdrian Chadd {
3685c6b44f64SAdrian Chadd 	const struct ieee80211vap *vap;
3686c6b44f64SAdrian Chadd 	const struct ieee80211com *ic;
3687c6b44f64SAdrian Chadd 
3688c6b44f64SAdrian Chadd 	if (! ieee80211_ht_check_tx_ht40(ni))
3689c6b44f64SAdrian Chadd 		return (false);
3690c6b44f64SAdrian Chadd 
3691c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3692c6b44f64SAdrian Chadd 	ic = ni->ni_ic;
3693c6b44f64SAdrian Chadd 
3694c6b44f64SAdrian Chadd 	return ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) &&
3695c6b44f64SAdrian Chadd 	    (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) &&
3696c6b44f64SAdrian Chadd 	    (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40));
3697c6b44f64SAdrian Chadd }
3698c6b44f64SAdrian Chadd 
3699c6b44f64SAdrian Chadd /*
3700c6b44f64SAdrian Chadd  * Return true if HT rates can be used for the given node.
3701c6b44f64SAdrian Chadd  *
3702c6b44f64SAdrian Chadd  * There are some situations seen in the wild, wild past where
3703c6b44f64SAdrian Chadd  * HT APs would announce HT but no HT rates.
3704c6b44f64SAdrian Chadd  */
3705c6b44f64SAdrian Chadd bool
3706c6b44f64SAdrian Chadd ieee80211_ht_check_tx_ht(const struct ieee80211_node *ni)
3707c6b44f64SAdrian Chadd {
3708c6b44f64SAdrian Chadd 	const struct ieee80211vap *vap;
3709c6b44f64SAdrian Chadd 	const struct ieee80211_channel *bss_chan;
3710c6b44f64SAdrian Chadd 
3711c6b44f64SAdrian Chadd 	if (ni == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC ||
3712c6b44f64SAdrian Chadd 	    ni->ni_vap == NULL || ni->ni_vap->iv_bss == NULL)
3713c6b44f64SAdrian Chadd 		return (false);
3714c6b44f64SAdrian Chadd 
3715c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3716c6b44f64SAdrian Chadd 	bss_chan = vap->iv_bss->ni_chan;
3717c6b44f64SAdrian Chadd 
3718c6b44f64SAdrian Chadd 	if (bss_chan == IEEE80211_CHAN_ANYC)
3719c6b44f64SAdrian Chadd 		return (false);
3720c6b44f64SAdrian Chadd 
3721c6b44f64SAdrian Chadd 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
3722c6b44f64SAdrian Chadd 	    ni->ni_htrates.rs_nrates == 0)
3723c6b44f64SAdrian Chadd 		return (false);
3724c6b44f64SAdrian Chadd 	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
3725c6b44f64SAdrian Chadd }
3726c6b44f64SAdrian Chadd 
3727c6b44f64SAdrian Chadd /*
3728c6b44f64SAdrian Chadd  * Return true if HT40 rates can be transmitted to the given node.
3729c6b44f64SAdrian Chadd  *
3730c6b44f64SAdrian Chadd  * This verifies that the BSS is HT40 capable and the current
3731c6b44f64SAdrian Chadd  * node channel width is 40MHz.
3732c6b44f64SAdrian Chadd  */
3733c6b44f64SAdrian Chadd bool
3734c6b44f64SAdrian Chadd ieee80211_ht_check_tx_ht40(const struct ieee80211_node *ni)
3735c6b44f64SAdrian Chadd {
3736c6b44f64SAdrian Chadd 	struct ieee80211vap *vap;
3737c6b44f64SAdrian Chadd 	struct ieee80211_channel *bss_chan;
3738c6b44f64SAdrian Chadd 
3739c6b44f64SAdrian Chadd 	if (! ieee80211_ht_check_tx_ht(ni))
3740c6b44f64SAdrian Chadd 		return (false);
3741c6b44f64SAdrian Chadd 
3742c6b44f64SAdrian Chadd 	vap = ni->ni_vap;
3743c6b44f64SAdrian Chadd 	bss_chan = vap->iv_bss->ni_chan;
3744c6b44f64SAdrian Chadd 
3745c6b44f64SAdrian Chadd 	return (IEEE80211_IS_CHAN_HT40(bss_chan) &&
3746c6b44f64SAdrian Chadd 	    IEEE80211_IS_CHAN_HT40(ni->ni_chan) &&
3747ca389486SBjoern A. Zeeb 	    (ni->ni_chw == IEEE80211_STA_RX_BW_40));
3748c6b44f64SAdrian Chadd }
3749