ieee80211_ht.c revision 172055
1114050Sobrien/*-
2114050Sobrien * Copyright (c) 2007 Sam Leffler, Errno Consulting
3131832Sobrien * All rights reserved.
4131832Sobrien *
5131832Sobrien * Redistribution and use in source and binary forms, with or without
6114050Sobrien * modification, are permitted provided that the following conditions
7131832Sobrien * are met:
8131832Sobrien * 1. Redistributions of source code must retain the above copyright
9219811Smarcel *    notice, this list of conditions and the following disclaimer.
10130575Sobrien * 2. Redistributions in binary form must reproduce the above copyright
11114050Sobrien *    notice, this list of conditions and the following disclaimer in the
12160578Sobrien *    documentation and/or other materials provided with the distribution.
13117130Sobrien *
14117130Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15215439Stijl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16117130Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17131832Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18131832Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19131832Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20131832Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21117130Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22117130Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23131832Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24131832Sobrien */
25117130Sobrien
26130575Sobrien#include <sys/cdefs.h>
27117130Sobrien#ifdef __FreeBSD__
28160578Sobrien__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 172055 2007-09-05 20:29:51Z sam $");
29#endif
30
31/*
32 * IEEE 802.11n protocol support.
33 */
34
35#include "opt_inet.h"
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/systm.h>
40#include <sys/endian.h>
41
42#include <sys/socket.h>
43
44#include <net/if.h>
45#include <net/if_media.h>
46#include <net/ethernet.h>
47
48#include <net80211/ieee80211_var.h>
49
50/* define here, used throughout file */
51#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
52#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
53
54/* XXX need max array size */
55const int ieee80211_htrates[16] = {
56	13,		/* IFM_IEEE80211_MCS0 */
57	26,		/* IFM_IEEE80211_MCS1 */
58	39,		/* IFM_IEEE80211_MCS2 */
59	52,		/* IFM_IEEE80211_MCS3 */
60	78,		/* IFM_IEEE80211_MCS4 */
61	104,		/* IFM_IEEE80211_MCS5 */
62	117,		/* IFM_IEEE80211_MCS6 */
63	130,		/* IFM_IEEE80211_MCS7 */
64	26,		/* IFM_IEEE80211_MCS8 */
65	52,		/* IFM_IEEE80211_MCS9 */
66	78,		/* IFM_IEEE80211_MCS10 */
67	104,		/* IFM_IEEE80211_MCS11 */
68	156,		/* IFM_IEEE80211_MCS12 */
69	208,		/* IFM_IEEE80211_MCS13 */
70	234,		/* IFM_IEEE80211_MCS14 */
71	260,		/* IFM_IEEE80211_MCS15 */
72};
73
74static const struct ieee80211_htrateset ieee80211_rateset_11n =
75	{ 16, {
76	/* MCS: 6.5   13 19.5   26   39  52 58.5  65  13  26 */
77	          0,   1,   2,   3,   4,  5,   6,  7,  8,  9,
78	/*       39   52   78  104  117, 130 */
79		 10,  11,  12,  13,  14,  15 }
80	};
81
82#define	IEEE80211_AGGR_TIMEOUT	msecs_to_ticks(250)
83#define	IEEE80211_AGGR_MINRETRY	msecs_to_ticks(10*1000)
84#define	IEEE80211_AGGR_MAXTRIES	3
85
86static int ieee80211_addba_request(struct ieee80211_node *ni,
87	struct ieee80211_tx_ampdu *tap,
88	int dialogtoken, int baparamset, int batimeout);
89static int ieee80211_addba_response(struct ieee80211_node *ni,
90	struct ieee80211_tx_ampdu *tap,
91	int code, int baparamset, int batimeout);
92static void ieee80211_addba_stop(struct ieee80211_node *ni,
93	struct ieee80211_tx_ampdu *tap);
94static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
95	const uint8_t *frm, const uint8_t *efrm);
96
97void
98ieee80211_ht_attach(struct ieee80211com *ic)
99{
100
101	ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
102	ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
103	ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
104
105	ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
106
107	/* setup default aggregation policy */
108	ic->ic_recv_action = ieee80211_aggr_recv_action;
109	ic->ic_send_action = ieee80211_send_action;
110	ic->ic_addba_request = ieee80211_addba_request;
111	ic->ic_addba_response = ieee80211_addba_response;
112	ic->ic_addba_stop = ieee80211_addba_stop;
113
114	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
115	    isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
116		/*
117		 * There are HT channels in the channel list; enable
118		 * all HT-related facilities by default.
119		 * XXX these choices may be too aggressive.
120		 */
121		ic->ic_flags_ext |= IEEE80211_FEXT_HT
122				 |  IEEE80211_FEXT_HTCOMPAT
123				 ;
124		if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
125			ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
126		/* XXX infer from channel list */
127		if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
128			ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
129			if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
130				ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
131		}
132		/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
133		ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
134		if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
135			ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
136		ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
137		if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
138			ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
139	}
140}
141
142void
143ieee80211_ht_detach(struct ieee80211com *ic)
144{
145}
146
147static void
148ht_announce(struct ieee80211com *ic, int mode,
149	const struct ieee80211_htrateset *rs)
150{
151	struct ifnet *ifp = ic->ic_ifp;
152	int i, rate, mword;
153
154	if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]);
155	for (i = 0; i < rs->rs_nrates; i++) {
156		mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode);
157		if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
158			continue;
159		rate = ieee80211_htrates[rs->rs_rates[i]];
160		printf("%s%d%sMbps", (i != 0 ? " " : ""),
161		    rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
162	}
163	printf("\n");
164}
165
166void
167ieee80211_ht_announce(struct ieee80211com *ic)
168{
169	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
170		ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
171	if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
172		ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
173}
174
175const struct ieee80211_htrateset *
176ieee80211_get_suphtrates(struct ieee80211com *ic,
177	const struct ieee80211_channel *c)
178{
179	if (IEEE80211_IS_CHAN_HT(c))
180		return &ieee80211_rateset_11n;
181	/* XXX what's the right thing to do here? */
182	return (const struct ieee80211_htrateset *)
183		ieee80211_get_suprates(ic, c);
184}
185
186/*
187 * Receive processing.
188 */
189
190/*
191 * Decap the encapsulated A-MSDU frames and dispatch all but
192 * the last for delivery.  The last frame is returned for
193 * delivery via the normal path.
194 */
195struct mbuf *
196ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
197{
198	struct ieee80211com *ic = ni->ni_ic;
199	int totallen, framelen;
200	struct mbuf *n;
201
202	/* discard 802.3 header inserted by ieee80211_decap */
203	m_adj(m, sizeof(struct ether_header));
204
205	ic->ic_stats.is_amsdu_decap++;
206
207	totallen = m->m_pkthdr.len;
208	for (;;) {
209		/*
210		 * Decap the first frame, bust it apart from the
211		 * remainder and deliver.  We leave the last frame
212		 * delivery to the caller (for consistency with other
213		 * code paths, could also do it here).
214		 */
215		m = ieee80211_decap1(m, &framelen);
216		if (m == NULL) {
217			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
218			    ni->ni_macaddr, "a-msdu", "%s", "first decap failed");
219			ic->ic_stats.is_amsdu_tooshort++;
220			return NULL;
221		}
222		if (framelen == totallen)
223			break;
224		n = m_split(m, framelen, M_NOWAIT);
225		if (n == NULL) {
226			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
227			    ni->ni_macaddr, "a-msdu",
228			    "%s", "unable to split encapsulated frames");
229			ic->ic_stats.is_amsdu_split++;
230			m_freem(m);			/* NB: must reclaim */
231			return NULL;
232		}
233		ieee80211_deliver_data(ic, ni, m);
234
235		/*
236		 * Remove frame contents; each intermediate frame
237		 * is required to be aligned to a 4-byte boundary.
238		 */
239		m = n;
240		m_adj(m, roundup2(framelen, 4) - framelen);	/* padding */
241	}
242	return m;				/* last delivered by caller */
243}
244
245/*
246 * Start A-MPDU rx/re-order processing for the specified TID.
247 */
248static void
249ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
250{
251	memset(rap, 0, sizeof(*rap));
252	rap->rxa_wnd = (bufsiz == 0) ?
253	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
254	rap->rxa_start = start;
255	rap->rxa_nxt = rap->rxa_start;
256	rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
257}
258
259/*
260 * Purge all frames in the A-MPDU re-order queue.
261 */
262static void
263ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
264{
265	struct mbuf *m;
266	int i;
267
268	for (i = 0; i < rap->rxa_wnd; i++) {
269		m = rap->rxa_m[i];
270		if (m != NULL) {
271			rap->rxa_m[i] = NULL;
272			rap->rxa_qbytes -= m->m_pkthdr.len;
273			m_freem(m);
274			if (--rap->rxa_qframes == 0)
275				break;
276		}
277	}
278	KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
279	    ("lost %u data, %u frames on ampdu rx q",
280	    rap->rxa_qbytes, rap->rxa_qframes));
281}
282
283/*
284 * Stop A-MPDU rx processing for the specified TID.
285 */
286static void
287ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
288{
289	rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
290	ampdu_rx_purge(rap);
291}
292
293/*
294 * Dispatch a frame from the A-MPDU reorder queue.  The
295 * frame is fed back into ieee80211_input marked with an
296 * M_AMPDU flag so it doesn't come back to us (it also
297 * permits ieee80211_input to optimize re-processing).
298 */
299static __inline void
300ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
301{
302	m->m_flags |= M_AMPDU;	/* bypass normal processing */
303	/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
304	(void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
305}
306
307/*
308 * Dispatch as many frames as possible from the re-order queue.
309 * Frames will always be "at the front"; we process all frames
310 * up to the first empty slot in the window.  On completion we
311 * cleanup state if there are still pending frames in the current
312 * BA window.  We assume the frame at slot 0 is already handled
313 * by the caller; we always start at slot 1.
314 */
315static void
316ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
317{
318	struct ieee80211com *ic = ni->ni_ic;
319	struct mbuf *m;
320	int i;
321
322	/* flush run of frames */
323	for (i = 1; i < rap->rxa_wnd; i++) {
324		m = rap->rxa_m[i];
325		if (m == NULL)
326			break;
327		rap->rxa_m[i] = NULL;
328		rap->rxa_qbytes -= m->m_pkthdr.len;
329		rap->rxa_qframes--;
330
331		ampdu_dispatch(ni, m);
332	}
333	/*
334	 * Adjust the start of the BA window to
335	 * reflect the frames just dispatched.
336	 */
337	rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
338	rap->rxa_nxt = rap->rxa_start;
339	ic->ic_stats.is_ampdu_rx_oor += i;
340	/*
341	 * If frames remain, copy the mbuf pointers down so
342	 * they correspond to the offsets in the new window.
343	 */
344	if (rap->rxa_qframes != 0) {
345		int n = rap->rxa_qframes, j;
346		for (j = i+1; j < rap->rxa_wnd; j++) {
347			if (rap->rxa_m[j] != NULL) {
348				rap->rxa_m[j-i] = rap->rxa_m[j];
349				rap->rxa_m[j] = NULL;
350				if (--n == 0)
351					break;
352			}
353		}
354		KASSERT(n == 0, ("lost %d frames", n));
355		ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
356	}
357}
358
359/*
360 * Dispatch all frames in the A-MPDU
361 * re-order queue up to the specified slot.
362 */
363static void
364ampdu_rx_flush(struct ieee80211_node *ni,
365	struct ieee80211_rx_ampdu *rap, int limit)
366{
367	struct mbuf *m;
368	int i;
369
370	for (i = 0; i < limit; i++) {
371		m = rap->rxa_m[i];
372		if (m == NULL)
373			continue;
374		rap->rxa_m[i] = NULL;
375		rap->rxa_qbytes -= m->m_pkthdr.len;
376		ampdu_dispatch(ni, m);
377		if (--rap->rxa_qframes == 0)
378			break;
379	}
380}
381
382/*
383 * Process a received QoS data frame for an HT station.  Handle
384 * A-MPDU reordering: if this frame is received out of order
385 * and falls within the BA window hold onto it.  Otherwise if
386 * this frame completes a run flush any pending frames.  We
387 * return 1 if the frame is consumed.  A 0 is returned if
388 * the frame should be processed normally by the caller.
389 */
390int
391ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
392{
393#define	IEEE80211_FC0_QOSDATA \
394	(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
395	struct ieee80211com *ic = ni->ni_ic;
396	struct ieee80211_qosframe *wh;
397	struct ieee80211_rx_ampdu *rap;
398	ieee80211_seq rxseq;
399	uint8_t tid;
400	int off;
401
402	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
403
404	/* NB: m_len known to be sufficient */
405	wh = mtod(m, struct ieee80211_qosframe *);
406	KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data"));
407
408	/* XXX 4-address frame */
409	tid = wh->i_qos[0] & IEEE80211_QOS_TID;
410	rap = &ni->ni_rx_ampdu[tid];
411	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
412		/*
413		 * No ADDBA request yet, don't touch.
414		 */
415		return 0;
416	}
417	rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
418	if (rxseq == rap->rxa_start) {
419		/*
420		 * First frame in window.
421		 */
422		if (rap->rxa_qframes != 0) {
423			/*
424			 * Dispatch as many packets as we can.
425			 */
426			KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup"));
427			ampdu_dispatch(ni, m);
428			ampdu_rx_dispatch(rap, ni);
429			return 1;		/* NB: consumed */
430		} else {
431			/*
432			 * In order; advance window and notify
433			 * caller to dispatch directly.
434			 */
435			rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
436			rap->rxa_nxt = rap->rxa_start;
437			return 0;		/* NB: process packet */
438		}
439	}
440	/*
441	 * This packet is out of order; store it
442	 * if it's in the BA window.
443	 */
444	/* calculate offset in BA window */
445	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
446	if (off >= rap->rxa_wnd) {
447		/*
448		 * Outside the window, clear the q and start over.
449		 *
450		 * NB: this handles the case where rxseq is before
451		 *     rxa_start because our max BA window is 64
452		 *     and the sequence number range is 4096.
453		 */
454		IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
455		    "flush BA win <%u:%u> (%u frames) rxseq %u tid %u",
456		    rap->rxa_start,
457		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd),
458		    rap->rxa_qframes, rxseq, tid);
459
460		if (rap->rxa_qframes != 0) {
461			ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes;
462			ampdu_rx_flush(ni, rap, rap->rxa_wnd);
463			KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
464			    ("lost %u data, %u frames on ampdu rx q",
465			    rap->rxa_qbytes, rap->rxa_qframes));
466		}
467		rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
468		rap->rxa_nxt = rap->rxa_start;
469		return 0;	/* NB: process packet */
470	}
471	if (rap->rxa_qframes != 0) {
472#if 0
473		/* XXX honor batimeout? */
474		if (ticks - mn->mn_age[tid] > 50) {
475			/*
476			 * Too long since we received the first frame; flush.
477			 */
478			if (rap->rxa_qframes != 0) {
479				ic->ic_stats.is_ampdu_rx_oor +=
480				    rap->rxa_qframes;
481				ampdu_rx_flush(ni, rap, rap->rxa_wnd);
482			}
483			rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
484			rap->rxa_nxt = rap->rxa_start;
485			return 0;		/* NB: process packet */
486		}
487#endif
488		rap->rxa_nxt = rxseq;
489	} else {
490		/*
491		 * First frame, start aging timer.
492		 */
493#if 0
494		mn->mn_age[tid] = ticks;
495#endif
496	}
497	/* save packet */
498	if (rap->rxa_m[off] == NULL) {
499		rap->rxa_m[off] = m;
500		rap->rxa_qframes++;
501		rap->rxa_qbytes += m->m_pkthdr.len;
502	} else {
503		IEEE80211_DISCARD_MAC(ic,
504		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
505		    ni->ni_macaddr, "a-mpdu duplicate",
506		    "seqno %u tid %u BA win <%u:%u>",
507		    rxseq, tid, rap->rxa_start, rap->rxa_wnd);
508		ic->ic_stats.is_rx_dup++;
509		IEEE80211_NODE_STAT(ni, rx_dup);
510		m_freem(m);
511	}
512	return 1;		/* NB: consumed */
513#undef IEEE80211_FC0_QOSDATA
514}
515
516/*
517 * Process a BAR ctl frame.  Dispatch all frames up to
518 * the sequence number of the frame.  If this frame is
519 * out of the window it's discarded.
520 */
521void
522ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
523{
524	struct ieee80211com *ic = ni->ni_ic;
525	struct ieee80211_frame_bar *wh;
526	struct ieee80211_rx_ampdu *rap;
527	ieee80211_seq rxseq;
528	int tid, off;
529
530	wh = mtod(m0, struct ieee80211_frame_bar *);
531	/* XXX check basic BAR */
532	tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID);
533	rap = &ni->ni_rx_ampdu[tid];
534	if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
535		/*
536		 * No ADDBA request yet, don't touch.
537		 */
538		IEEE80211_DISCARD_MAC(ic,
539		    IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
540		    ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
541		ic->ic_stats.is_ampdu_bar_bad++;
542		return;
543	}
544	ic->ic_stats.is_ampdu_bar_rx++;
545	rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
546	/* calculate offset in BA window */
547	off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
548	if (off >= rap->rxa_wnd) {
549		/*
550		 * Outside the window, flush the reorder q if
551		 * not pulling the sequence # backward.  The
552		 * latter is typically caused by a dropped BA.
553		 */
554		IEEE80211_NOTE(ic, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni,
555		    "recv BAR outside BA win <%u:%u> rxseq %u tid %u",
556		    rap->rxa_start,
557		    IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd),
558		    rxseq, tid);
559		ic->ic_stats.is_ampdu_bar_oow++;
560		if (rxseq < rap->rxa_start) {
561			/* XXX stat? */
562			return;
563		}
564		if (rap->rxa_qframes != 0) {
565			ic->ic_stats.is_ampdu_rx_oor += rap->rxa_qframes;
566			ampdu_rx_flush(ni, rap, rap->rxa_wnd);
567			KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
568			    ("lost %u data, %u frames on ampdu rx q",
569			    rap->rxa_qbytes, rap->rxa_qframes));
570		}
571	} else if (rap->rxa_qframes != 0) {
572		/*
573		 * Dispatch packets up to rxseq.
574		 */
575		ampdu_rx_flush(ni, rap, off);
576		ic->ic_stats.is_ampdu_rx_oor += off;
577
578		/*
579		 * If frames remain, copy the mbuf pointers down so
580		 * they correspond to the offsets in the new window.
581		 */
582		if (rap->rxa_qframes != 0) {
583			int n = rap->rxa_qframes, j;
584			for (j = off+1; j < rap->rxa_wnd; j++) {
585				if (rap->rxa_m[j] != NULL) {
586					rap->rxa_m[j-off] = rap->rxa_m[j];
587					rap->rxa_m[j] = NULL;
588					if (--n == 0)
589						break;
590				}
591			}
592			KASSERT(n == 0, ("lost %d frames", n));
593			ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
594		}
595	}
596	rap->rxa_start = rxseq;
597	rap->rxa_nxt = rap->rxa_start;
598}
599
600/*
601 * Setup HT-specific state in a node.  Called only
602 * when HT use is negotiated so we don't do extra
603 * work for temporary and/or legacy sta's.
604 */
605void
606ieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap)
607{
608	struct ieee80211_tx_ampdu *tap;
609	int ac;
610
611	ieee80211_parse_htcap(ni, htcap);
612	for (ac = 0; ac < WME_NUM_AC; ac++) {
613		tap = &ni->ni_tx_ampdu[ac];
614		tap->txa_ac = ac;
615	}
616	ni->ni_flags |= IEEE80211_NODE_HT;
617}
618
619/*
620 * Cleanup HT-specific state in a node.  Called only
621 * when HT use has been marked.
622 */
623void
624ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
625{
626	struct ieee80211com *ic = ni->ni_ic;
627	int i;
628
629	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
630
631	/* XXX optimize this */
632	for (i = 0; i < WME_NUM_AC; i++) {
633		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
634		if (IEEE80211_AMPDU_REQUESTED(tap))
635			ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
636	}
637	for (i = 0; i < WME_NUM_TID; i++)
638		ampdu_rx_stop(&ni->ni_rx_ampdu[i]);
639
640	ni->ni_htcap = 0;
641	ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT);
642}
643
644/* unalligned little endian access */
645#define LE_READ_2(p)					\
646	((uint16_t)					\
647	 ((((const uint8_t *)(p))[0]      ) |		\
648	  (((const uint8_t *)(p))[1] <<  8)))
649
650/*
651 * Process an 802.11n HT capabilities ie.
652 */
653void
654ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
655{
656	struct ieee80211com *ic = ni->ni_ic;
657
658	if (ie[0] == IEEE80211_ELEMID_VENDOR) {
659		/*
660		 * Station used Vendor OUI ie to associate;
661		 * mark the node so when we respond we'll use
662		 * the Vendor OUI's and not the standard ie's.
663		 */
664		ni->ni_flags |= IEEE80211_NODE_HTCOMPAT;
665		ie += 4;
666	} else
667		ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
668
669	ni->ni_htcap = LE_READ_2(ie +
670		__offsetof(struct ieee80211_ie_htcap, hc_cap));
671	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0)
672		ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI40;
673	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
674		ni->ni_htcap &= ~IEEE80211_HTCAP_SHORTGI20;
675	ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) ? 40 : 20;
676	ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
677#if 0
678	ni->ni_maxampdu =
679	    (8*1024) << MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
680	ni->ni_mpdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
681#endif
682}
683
684/*
685 * Process an 802.11n HT info ie.
686 */
687void
688ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
689{
690 	const struct ieee80211_ie_htinfo *htinfo;
691	uint16_t w;
692	int chw;
693
694	if (ie[0] == IEEE80211_ELEMID_VENDOR)
695		ie += 4;
696 	htinfo = (const struct ieee80211_ie_htinfo *) ie;
697	ni->ni_htctlchan = htinfo->hi_ctrlchannel;
698	ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
699	w = LE_READ_2(&htinfo->hi_byte2);
700	ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
701	w = LE_READ_2(&htinfo->hi_byte45);
702	ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
703	/* update node's recommended tx channel width */
704	chw = (htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) ? 40 : 20;
705	if (chw != ni->ni_chw) {
706		ni->ni_chw = chw;
707		ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
708	}
709}
710
711/*
712 * Install received HT rate set by parsing the HT cap ie.
713 */
714int
715ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
716{
717	struct ieee80211com *ic = ni->ni_ic;
718	const struct ieee80211_ie_htcap *htcap;
719	struct ieee80211_htrateset *rs;
720	int i;
721
722	rs = &ni->ni_htrates;
723	memset(rs, 0, sizeof(*rs));
724	if (ie != NULL) {
725		if (ie[0] == IEEE80211_ELEMID_VENDOR)
726			ie += 4;
727		htcap = (const struct ieee80211_ie_htcap *) ie;
728		for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
729			if (isclr(htcap->hc_mcsset, i))
730				continue;
731			if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
732				IEEE80211_NOTE(ic,
733				    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
734				    "WARNING, HT rate set too large; only "
735				    "using %u rates", IEEE80211_HTRATE_MAXSIZE);
736				ic->ic_stats.is_rx_rstoobig++;
737				break;
738			}
739			rs->rs_rates[rs->rs_nrates++] = i;
740		}
741	}
742	return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags);
743}
744
745/*
746 * Mark rates in a node's HT rate set as basic according
747 * to the information in the supplied HT info ie.
748 */
749void
750ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
751{
752	const struct ieee80211_ie_htinfo *htinfo;
753	struct ieee80211_htrateset *rs;
754	int i, j;
755
756	if (ie[0] == IEEE80211_ELEMID_VENDOR)
757		ie += 4;
758	htinfo = (const struct ieee80211_ie_htinfo *) ie;
759	rs = &ni->ni_htrates;
760	if (rs->rs_nrates == 0) {
761		IEEE80211_NOTE(ni->ni_ic,
762		    IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
763		    "%s", "WARNING, empty HT rate set");
764		return;
765	}
766	for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
767		if (isclr(htinfo->hi_basicmcsset, i))
768			continue;
769		for (j = 0; j < rs->rs_nrates; j++)
770			if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
771				rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
772	}
773}
774
775static void
776addba_timeout(void *arg)
777{
778	struct ieee80211_tx_ampdu *tap = arg;
779
780	/* XXX ? */
781	tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
782	tap->txa_attempts++;
783}
784
785static void
786addba_start_timeout(struct ieee80211_tx_ampdu *tap)
787{
788	/* XXX use CALLOUT_PENDING instead? */
789	callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
790	    addba_timeout, tap);
791	tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
792	tap->txa_lastrequest = ticks;
793}
794
795static void
796addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
797{
798	/* XXX use CALLOUT_PENDING instead? */
799	if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
800		callout_stop(&tap->txa_timer);
801		tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
802	}
803}
804
805/*
806 * Default method for requesting A-MPDU tx aggregation.
807 * We setup the specified state block and start a timer
808 * to wait for an ADDBA response frame.
809 */
810static int
811ieee80211_addba_request(struct ieee80211_node *ni,
812	struct ieee80211_tx_ampdu *tap,
813	int dialogtoken, int baparamset, int batimeout)
814{
815	int bufsiz;
816
817	/* XXX locking */
818	tap->txa_token = dialogtoken;
819	tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
820	tap->txa_start = tap->txa_seqstart = 0;
821	bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
822	tap->txa_wnd = (bufsiz == 0) ?
823	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
824	addba_start_timeout(tap);
825	return 1;
826}
827
828/*
829 * Default method for processing an A-MPDU tx aggregation
830 * response.  We shutdown any pending timer and update the
831 * state block according to the reply.
832 */
833static int
834ieee80211_addba_response(struct ieee80211_node *ni,
835	struct ieee80211_tx_ampdu *tap,
836	int status, int baparamset, int batimeout)
837{
838	int bufsiz;
839
840	/* XXX locking */
841	addba_stop_timeout(tap);
842	if (status == IEEE80211_STATUS_SUCCESS) {
843		bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
844		/* XXX override our request? */
845		tap->txa_wnd = (bufsiz == 0) ?
846		    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
847		tap->txa_flags |= IEEE80211_AGGR_RUNNING;
848	}
849	return 1;
850}
851
852/*
853 * Default method for stopping A-MPDU tx aggregation.
854 * Any timer is cleared and we drain any pending frames.
855 */
856static void
857ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
858{
859	/* XXX locking */
860	addba_stop_timeout(tap);
861	if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
862		/* clear aggregation queue */
863		ieee80211_drain_ifq(&tap->txa_q);
864		tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
865	}
866	tap->txa_attempts = 0;
867}
868
869/*
870 * Process a received action frame using the default aggregation
871 * policy.  We intercept ADDBA-related frames and use them to
872 * update our aggregation state.  All other frames are passed up
873 * for processing by ieee80211_recv_action.
874 */
875static void
876ieee80211_aggr_recv_action(struct ieee80211_node *ni,
877	const uint8_t *frm, const uint8_t *efrm)
878{
879	struct ieee80211com *ic = ni->ni_ic;
880	const struct ieee80211_action *ia;
881	struct ieee80211_rx_ampdu *rap;
882	struct ieee80211_tx_ampdu *tap;
883	uint8_t dialogtoken;
884	uint16_t baparamset, batimeout, baseqctl, code;
885	uint16_t args[4];
886	int tid, ac, bufsiz;
887
888	ia = (const struct ieee80211_action *) frm;
889	switch (ia->ia_category) {
890	case IEEE80211_ACTION_CAT_BA:
891		switch (ia->ia_action) {
892		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
893			dialogtoken = frm[2];
894			baparamset = LE_READ_2(frm+3);
895			batimeout = LE_READ_2(frm+5);
896			baseqctl = LE_READ_2(frm+7);
897
898			tid = MS(baparamset, IEEE80211_BAPS_TID);
899			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
900
901			IEEE80211_NOTE(ic,
902			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
903			    "recv ADDBA request: dialogtoken %u "
904			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
905			    "baseqctl %d",
906			    dialogtoken, baparamset, tid, bufsiz,
907			    batimeout, baseqctl);
908
909			rap = &ni->ni_rx_ampdu[tid];
910
911			/* Send ADDBA response */
912			args[0] = dialogtoken;
913			if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) {
914				ampdu_rx_start(rap, bufsiz,
915				    MS(baseqctl, IEEE80211_BASEQ_START));
916
917				args[1] = IEEE80211_STATUS_SUCCESS;
918			} else
919				args[1] = IEEE80211_STATUS_UNSPECIFIED;
920			/* XXX honor rap flags? */
921			args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
922				| SM(tid, IEEE80211_BAPS_TID)
923				| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
924				;
925			args[3] = 0;
926			ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
927				IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
928			return;
929
930		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
931			dialogtoken = frm[2];
932			code = LE_READ_2(frm+3);
933			baparamset = LE_READ_2(frm+5);
934			tid = MS(baparamset, IEEE80211_BAPS_TID);
935			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
936			batimeout = LE_READ_2(frm+7);
937
938			IEEE80211_NOTE(ic,
939			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
940			    "recv ADDBA response: dialogtoken %u code %d "
941			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
942			    dialogtoken, code, baparamset, tid, bufsiz,
943			    batimeout);
944
945			ac = TID_TO_WME_AC(tid);
946			tap = &ni->ni_tx_ampdu[ac];
947
948			ic->ic_addba_response(ni, tap,
949				code, baparamset, batimeout);
950			return;
951
952		case IEEE80211_ACTION_BA_DELBA:
953			baparamset = LE_READ_2(frm+2);
954			code = LE_READ_2(frm+4);
955
956			tid = MS(baparamset, IEEE80211_DELBAPS_TID);
957
958			IEEE80211_NOTE(ic,
959			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
960			    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
961			    "code %d", baparamset, tid,
962			    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
963
964			if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
965				ac = TID_TO_WME_AC(tid);
966				tap = &ni->ni_tx_ampdu[ac];
967				ic->ic_addba_stop(ni, tap);
968			} else {
969				rap = &ni->ni_rx_ampdu[tid];
970				ampdu_rx_stop(rap);
971			}
972			return;
973		}
974		break;
975	}
976	return ieee80211_recv_action(ni, frm, efrm);
977}
978
979/*
980 * Process a received 802.11n action frame.
981 * Aggregation-related frames are assumed to be handled
982 * already; we handle any other frames we can, otherwise
983 * complain about being unsupported (with debugging).
984 */
985void
986ieee80211_recv_action(struct ieee80211_node *ni,
987	const uint8_t *frm, const uint8_t *efrm)
988{
989	struct ieee80211com *ic = ni->ni_ic;
990	const struct ieee80211_action *ia;
991	int chw;
992
993	ia = (const struct ieee80211_action *) frm;
994	switch (ia->ia_category) {
995	case IEEE80211_ACTION_CAT_BA:
996		IEEE80211_NOTE(ic,
997		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
998		    "%s: BA action %d not implemented", __func__,
999		    ia->ia_action);
1000		ic->ic_stats.is_rx_mgtdiscard++;
1001		break;
1002	case IEEE80211_ACTION_CAT_HT:
1003		switch (ia->ia_action) {
1004		case IEEE80211_ACTION_HT_TXCHWIDTH:
1005			chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
1006			if (chw != ni->ni_chw) {
1007				ni->ni_chw = chw;
1008				ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
1009			}
1010			IEEE80211_NOTE(ic,
1011			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1012		            "%s: HT txchwidth. width %d (%s)",
1013			    __func__, chw,
1014			    ni->ni_flags & IEEE80211_NODE_CHWUPDATE ?
1015				"new" : "no change");
1016			break;
1017		default:
1018			IEEE80211_NOTE(ic,
1019			   IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1020		           "%s: HT action %d not implemented", __func__,
1021			   ia->ia_action);
1022			ic->ic_stats.is_rx_mgtdiscard++;
1023			break;
1024		}
1025		break;
1026	default:
1027		IEEE80211_NOTE(ic,
1028		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1029		    "%s: category %d not implemented", __func__,
1030		    ia->ia_category);
1031		ic->ic_stats.is_rx_mgtdiscard++;
1032		break;
1033	}
1034}
1035
1036/*
1037 * Transmit processing.
1038 */
1039
1040/*
1041 * Request A-MPDU tx aggregation.  Setup local state and
1042 * issue an ADDBA request.  BA use will only happen after
1043 * the other end replies with ADDBA response.
1044 */
1045int
1046ieee80211_ampdu_request(struct ieee80211_node *ni,
1047	struct ieee80211_tx_ampdu *tap)
1048{
1049	struct ieee80211com *ic = ni->ni_ic;
1050	uint16_t args[4];
1051	int tid, dialogtoken;
1052	static int tokens = 0;	/* XXX */
1053
1054	/* XXX locking */
1055	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
1056		/* do deferred setup of state */
1057		/* XXX tap->txa_q */
1058		callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
1059		tap->txa_flags |= IEEE80211_AGGR_SETUP;
1060	}
1061	if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
1062	    (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
1063		/*
1064		 * Don't retry too often; IEEE80211_AGGR_MINRETRY
1065		 * defines the minimum interval we'll retry after
1066		 * IEEE80211_AGGR_MAXTRIES failed attempts to
1067		 * negotiate use.
1068		 */
1069		return 0;
1070	}
1071	dialogtoken = (tokens+1) % 63;		/* XXX */
1072
1073	tid = WME_AC_TO_TID(tap->txa_ac);
1074	args[0] = dialogtoken;
1075	args[1]	= IEEE80211_BAPS_POLICY_IMMEDIATE
1076		| SM(tid, IEEE80211_BAPS_TID)
1077		| SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
1078		;
1079	args[2] = 0;	/* batimeout */
1080	args[3] = SM(0, IEEE80211_BASEQ_START)
1081		| SM(0, IEEE80211_BASEQ_FRAG)
1082		;
1083	/* NB: do first so there's no race against reply */
1084	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
1085		/* unable to setup state, don't make request */
1086		return 0;
1087	}
1088	tokens = dialogtoken;			/* allocate token */
1089	return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
1090		IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
1091}
1092
1093/*
1094 * Transmit a BAR frame to the specified node.  The
1095 * BAR contents are drawn from the supplied aggregation
1096 * state associated with the node.
1097 */
1098int
1099ieee80211_send_bar(struct ieee80211_node *ni,
1100	const struct ieee80211_tx_ampdu *tap)
1101{
1102#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
1103#define	ADDSHORT(frm, v) do {			\
1104	frm[0] = (v) & 0xff;			\
1105	frm[1] = (v) >> 8;			\
1106	frm += 2;				\
1107} while (0)
1108	struct ieee80211com *ic = ni->ni_ic;
1109	struct ifnet *ifp = ic->ic_ifp;
1110	struct ieee80211_frame_min *wh;
1111	struct mbuf *m;
1112	uint8_t *frm;
1113	uint16_t barctl, barseqctl;
1114	int tid, ret;
1115
1116	ieee80211_ref_node(ni);
1117
1118	m = ieee80211_getmgtframe(&frm,
1119		ic->ic_headroom + sizeof(struct ieee80211_frame_min),
1120		sizeof(struct ieee80211_ba_request)
1121	);
1122	if (m == NULL)
1123		senderr(ENOMEM, is_tx_nobuf);
1124
1125	wh = mtod(m, struct ieee80211_frame_min *);
1126	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
1127		IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
1128	wh->i_fc[1] = 0;
1129	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
1130	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
1131
1132	tid = WME_AC_TO_TID(tap->txa_ac);
1133	barctl 	= (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
1134			IEEE80211_BAPS_POLICY_IMMEDIATE :
1135			IEEE80211_BAPS_POLICY_DELAYED)
1136		| SM(tid, IEEE80211_BAPS_TID)
1137		| SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ)
1138		;
1139	barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
1140		| SM(0, IEEE80211_BASEQ_FRAG)
1141		;
1142	ADDSHORT(frm, barctl);
1143	ADDSHORT(frm, barseqctl);
1144	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
1145
1146	IEEE80211_NODE_STAT(ni, tx_mgmt);	/* XXX tx_ctl? */
1147
1148	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
1149	    "[%s] send bar frame (tid %u start %u) on channel %u\n",
1150	    ether_sprintf(ni->ni_macaddr), tid, tap->txa_start,
1151	    ieee80211_chan2ieee(ic, ic->ic_curchan));
1152
1153	m->m_pkthdr.rcvif = (void *)ni;
1154	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
1155	(*ifp->if_start)(ifp);
1156
1157	return 0;
1158bad:
1159	ieee80211_free_node(ni);
1160	return ret;
1161#undef ADDSHORT
1162#undef senderr
1163}
1164
1165/*
1166 * Send an action management frame.  The arguments are stuff
1167 * into a frame without inspection; the caller is assumed to
1168 * prepare them carefully (e.g. based on the aggregation state).
1169 */
1170int
1171ieee80211_send_action(struct ieee80211_node *ni,
1172	int category, int action, uint16_t args[4])
1173{
1174#define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
1175#define	ADDSHORT(frm, v) do {			\
1176	frm[0] = (v) & 0xff;			\
1177	frm[1] = (v) >> 8;			\
1178	frm += 2;				\
1179} while (0)
1180	struct ieee80211com *ic = ni->ni_ic;
1181	struct mbuf *m;
1182	uint8_t *frm;
1183	uint16_t baparamset;
1184	int ret;
1185
1186	KASSERT(ni != NULL, ("null node"));
1187
1188	/*
1189	 * Hold a reference on the node so it doesn't go away until after
1190	 * the xmit is complete all the way in the driver.  On error we
1191	 * will remove our reference.
1192	 */
1193	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
1194		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
1195		__func__, __LINE__,
1196		ni, ether_sprintf(ni->ni_macaddr),
1197		ieee80211_node_refcnt(ni)+1);
1198	ieee80211_ref_node(ni);
1199
1200	m = ieee80211_getmgtframe(&frm,
1201		ic->ic_headroom + sizeof(struct ieee80211_frame),
1202		  sizeof(uint16_t)	/* action+category */
1203		/* XXX may action payload */
1204		+ sizeof(struct ieee80211_action_ba_addbaresponse)
1205	);
1206	if (m == NULL)
1207		senderr(ENOMEM, is_tx_nobuf);
1208
1209	*frm++ = category;
1210	*frm++ = action;
1211	switch (category) {
1212	case IEEE80211_ACTION_CAT_BA:
1213		switch (action) {
1214		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
1215			IEEE80211_NOTE(ic,
1216			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1217			    "send ADDBA request: tid %d, baparamset 0x%x",
1218			    args[0], args[1]);
1219
1220			*frm++ = args[0];	/* dialog token */
1221			ADDSHORT(frm, args[1]);	/* baparamset */
1222			ADDSHORT(frm, args[2]);	/* batimeout */
1223			ADDSHORT(frm, args[3]);	/* baseqctl */
1224			break;
1225		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
1226			IEEE80211_NOTE(ic,
1227			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1228			    "send ADDBA response: dialogtoken %d status %d "
1229			    "baparamset 0x%x (tid %d) batimeout %d",
1230			    args[0], args[1], args[2],
1231			    MS(args[2], IEEE80211_BAPS_TID), args[3]);
1232
1233			*frm++ = args[0];	/* dialog token */
1234			ADDSHORT(frm, args[1]);	/* statuscode */
1235			ADDSHORT(frm, args[2]);	/* baparamset */
1236			ADDSHORT(frm, args[3]);	/* batimeout */
1237			break;
1238		case IEEE80211_ACTION_BA_DELBA:
1239			/* XXX */
1240			baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
1241				   | SM(args[1], IEEE80211_DELBAPS_INIT)
1242				   ;
1243			ADDSHORT(frm, baparamset);
1244			ADDSHORT(frm, args[2]);	/* reason code */
1245
1246			IEEE80211_NOTE(ic,
1247			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1248			    "send DELBA action: tid %d, initiator %d reason %d",
1249			    args[0], args[1], args[2]);
1250			break;
1251		default:
1252			goto badaction;
1253		}
1254		break;
1255	case IEEE80211_ACTION_CAT_HT:
1256		switch (action) {
1257		case IEEE80211_ACTION_HT_TXCHWIDTH:
1258			IEEE80211_NOTE(ic,
1259			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1260			    ni, "send HT txchwidth: width %d",
1261			   IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?  40 : 20
1262			);
1263			*frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?
1264				IEEE80211_A_HT_TXCHWIDTH_2040 :
1265				IEEE80211_A_HT_TXCHWIDTH_20;
1266			break;
1267		default:
1268			goto badaction;
1269		}
1270		break;
1271	default:
1272	badaction:
1273		IEEE80211_NOTE(ic,
1274		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1275		    "%s: unsupported category %d action %d", __func__,
1276		    category, action);
1277		senderr(EINVAL, is_tx_unknownmgt);
1278		/* NOTREACHED */
1279	}
1280	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
1281
1282	ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
1283	if (ret != 0)
1284		goto bad;
1285	return 0;
1286bad:
1287	ieee80211_free_node(ni);
1288	return ret;
1289#undef ADDSHORT
1290#undef senderr
1291}
1292
1293/*
1294 * Construct the MCS bit mask for inclusion
1295 * in an HT information element.
1296 */
1297static void
1298ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
1299{
1300	int i;
1301
1302	for (i = 0; i < rs->rs_nrates; i++) {
1303		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
1304		if (r < IEEE80211_HTRATE_MAXSIZE) {	/* XXX? */
1305			/* NB: this assumes a particular implementation */
1306			setbit(frm, r);
1307		}
1308	}
1309}
1310
1311/*
1312 * Add body of an HTCAP information element.
1313 */
1314static uint8_t *
1315ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
1316{
1317#define	ADDSHORT(frm, v) do {			\
1318	frm[0] = (v) & 0xff;			\
1319	frm[1] = (v) >> 8;			\
1320	frm += 2;				\
1321} while (0)
1322	struct ieee80211com *ic = ni->ni_ic;
1323	uint16_t caps;
1324
1325	/* HT capabilities */
1326	caps = ic->ic_htcaps & 0xffff;
1327	/* override 20/40 use based on channel and config */
1328	if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
1329	    (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40))
1330		caps |= IEEE80211_HTCAP_CHWIDTH40;
1331	else
1332		caps &= ~IEEE80211_HTCAP_CHWIDTH40;
1333	/* adjust short GI based on channel and config */
1334	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
1335		caps &= ~IEEE80211_HTCAP_SHORTGI20;
1336	if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
1337	    (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
1338		caps &= ~IEEE80211_HTCAP_SHORTGI40;
1339	ADDSHORT(frm, caps);
1340
1341	/* HT parameters */
1342	switch (ic->ic_ampdu_rxmax / 1024) {
1343	case 8:	 *frm = IEEE80211_HTCAP_MAXRXAMPDU_8K; break;
1344	case 16: *frm = IEEE80211_HTCAP_MAXRXAMPDU_16K; break;
1345	case 32: *frm = IEEE80211_HTCAP_MAXRXAMPDU_32K; break;
1346	default: *frm = IEEE80211_HTCAP_MAXRXAMPDU_64K; break;
1347	}
1348	*frm |= SM(ic->ic_ampdu_density, IEEE80211_HTCAP_MPDUDENSITY);
1349	frm++;
1350
1351	/* pre-zero remainder of ie */
1352	memset(frm, 0, sizeof(struct ieee80211_ie_htcap) -
1353		__offsetof(struct ieee80211_ie_htcap, hc_mcsset));
1354
1355	/* supported MCS set */
1356	ieee80211_set_htrates(frm, &ni->ni_htrates);
1357
1358	frm += sizeof(struct ieee80211_ie_htcap) -
1359		__offsetof(struct ieee80211_ie_htcap, hc_mcsset);
1360	return frm;
1361#undef ADDSHORT
1362}
1363
1364/*
1365 * Add 802.11n HT capabilities information element
1366 */
1367uint8_t *
1368ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
1369{
1370	frm[0] = IEEE80211_ELEMID_HTCAP;
1371	frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
1372	return ieee80211_add_htcap_body(frm + 2, ni);
1373}
1374
1375/*
1376 * Add Broadcom OUI wrapped standard HTCAP ie; this is
1377 * used for compatibility w/ pre-draft implementations.
1378 */
1379uint8_t *
1380ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni)
1381{
1382	frm[0] = IEEE80211_ELEMID_VENDOR;
1383	frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2;
1384	frm[2] = (BCM_OUI >> 0) & 0xff;
1385	frm[3] = (BCM_OUI >> 8) & 0xff;
1386	frm[4] = (BCM_OUI >> 16) & 0xff;
1387	frm[5] = BCM_OUI_HTCAP;
1388	return ieee80211_add_htcap_body(frm + 6, ni);
1389}
1390
1391/*
1392 * Construct the MCS bit mask of basic rates
1393 * for inclusion in an HT information element.
1394 */
1395static void
1396ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
1397{
1398	int i;
1399
1400	for (i = 0; i < rs->rs_nrates; i++) {
1401		int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
1402		if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
1403		    r < IEEE80211_HTRATE_MAXSIZE) {
1404			/* NB: this assumes a particular implementation */
1405			setbit(frm, r);
1406		}
1407	}
1408}
1409
1410/*
1411 * Add body of an HTINFO information element.
1412 */
1413static uint8_t *
1414ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
1415{
1416	struct ieee80211com *ic = ni->ni_ic;
1417
1418	/* pre-zero remainder of ie */
1419	memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
1420
1421	/* primary/control channel center */
1422	*frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
1423
1424	frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
1425	if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
1426		frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
1427	else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
1428		frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
1429	else
1430		frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
1431	if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
1432		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
1433
1434	frm[1] = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) ?
1435		IEEE80211_HTINFO_OPMODE_PURE : IEEE80211_HTINFO_OPMODE_MIXED;
1436	/* XXX IEEE80211_HTINFO_NONHT_PRESENT */
1437
1438	frm += 5;
1439
1440	/* basic MCS set */
1441	ieee80211_set_basic_htrates(frm, &ni->ni_htrates);
1442	frm += sizeof(struct ieee80211_ie_htinfo) -
1443		__offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
1444	return frm;
1445}
1446
1447/*
1448 * Add 802.11n HT information information element.
1449 */
1450uint8_t *
1451ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni)
1452{
1453	frm[0] = IEEE80211_ELEMID_HTINFO;
1454	frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2;
1455	return ieee80211_add_htinfo_body(frm + 2, ni);
1456}
1457
1458/*
1459 * Add Broadcom OUI wrapped standard HTINFO ie; this is
1460 * used for compatibility w/ pre-draft implementations.
1461 */
1462uint8_t *
1463ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
1464{
1465	frm[0] = IEEE80211_ELEMID_VENDOR;
1466	frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2;
1467	frm[2] = (BCM_OUI >> 0) & 0xff;
1468	frm[3] = (BCM_OUI >> 8) & 0xff;
1469	frm[4] = (BCM_OUI >> 16) & 0xff;
1470	frm[5] = BCM_OUI_HTINFO;
1471	return ieee80211_add_htinfo_body(frm + 6, ni);
1472}
1473