2 * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 * $FreeBSD: head/sys/net80211/ieee80211_ht.c 195377 2009-07-05 17:59:19Z sam $
29 * IEEE 802.11n protocol support.
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/systm.h>
38 #include <sys/endian.h>
40 #include <sys/socket.h>
43 #include <net/if_media.h>
44 #include <net/ethernet.h>
45 #include <net/route.h>
47 #include <netproto/802_11/ieee80211_var.h>
48 #include <netproto/802_11/ieee80211_action.h>
49 #include <netproto/802_11/ieee80211_input.h>
51 /* define here, used throughout file */
52 #define MS(_v, _f) (((_v) & _f) >> _f##_S)
53 #define SM(_v, _f) (((_v) << _f##_S) & _f)
55 const struct ieee80211_mcs_rates ieee80211_htrates
[16] = {
56 { 13, 14, 27, 30 }, /* MCS 0 */
57 { 26, 29, 54, 60 }, /* MCS 1 */
58 { 39, 43, 81, 90 }, /* MCS 2 */
59 { 52, 58, 108, 120 }, /* MCS 3 */
60 { 78, 87, 162, 180 }, /* MCS 4 */
61 { 104, 116, 216, 240 }, /* MCS 5 */
62 { 117, 130, 243, 270 }, /* MCS 6 */
63 { 130, 144, 270, 300 }, /* MCS 7 */
64 { 26, 29, 54, 60 }, /* MCS 8 */
65 { 52, 58, 108, 120 }, /* MCS 9 */
66 { 78, 87, 162, 180 }, /* MCS 10 */
67 { 104, 116, 216, 240 }, /* MCS 11 */
68 { 156, 173, 324, 360 }, /* MCS 12 */
69 { 208, 231, 432, 480 }, /* MCS 13 */
70 { 234, 260, 486, 540 }, /* MCS 14 */
71 { 260, 289, 540, 600 } /* MCS 15 */
74 static const struct ieee80211_htrateset ieee80211_rateset_11n
=
76 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
77 10, 11, 12, 13, 14, 15 }
80 #ifdef IEEE80211_AMPDU_AGE
81 static int ieee80211_ampdu_age
= -1; /* threshold for ampdu reorder q (ms) */
82 SYSCTL_PROC(_net_wlan
, OID_AUTO
, ampdu_age
, CTLTYPE_INT
| CTLFLAG_RW
,
83 &ieee80211_ampdu_age
, 0, ieee80211_sysctl_msecs_ticks
, "I",
84 "AMPDU max reorder age (ms)");
87 static int ieee80211_recv_bar_ena
= 1;
88 SYSCTL_INT(_net_wlan
, OID_AUTO
, recv_bar
, CTLFLAG_RW
, &ieee80211_recv_bar_ena
,
89 0, "BAR frame processing (ena/dis)");
91 static int ieee80211_addba_timeout
= -1;/* timeout for ADDBA response */
92 SYSCTL_PROC(_net_wlan
, OID_AUTO
, addba_timeout
, CTLTYPE_INT
| CTLFLAG_RW
,
93 &ieee80211_addba_timeout
, 0, ieee80211_sysctl_msecs_ticks
, "I",
94 "ADDBA request timeout (ms)");
95 static int ieee80211_addba_backoff
= -1;/* backoff after max ADDBA requests */
96 SYSCTL_PROC(_net_wlan
, OID_AUTO
, addba_backoff
, CTLTYPE_INT
| CTLFLAG_RW
,
97 &ieee80211_addba_backoff
, 0, ieee80211_sysctl_msecs_ticks
, "I",
98 "ADDBA request backoff (ms)");
99 static int ieee80211_addba_maxtries
= 3;/* max ADDBA requests before backoff */
100 SYSCTL_INT(_net_wlan
, OID_AUTO
, addba_maxtries
, CTLTYPE_INT
| CTLFLAG_RW
,
101 &ieee80211_addba_maxtries
, 0, "max ADDBA requests sent before backoff");
103 static int ieee80211_bar_timeout
= -1; /* timeout waiting for BAR response */
104 static int ieee80211_bar_maxtries
= 50;/* max BAR requests before DELBA */
106 static ieee80211_recv_action_func ht_recv_action_ba_addba_request
;
107 static ieee80211_recv_action_func ht_recv_action_ba_addba_response
;
108 static ieee80211_recv_action_func ht_recv_action_ba_delba
;
109 static ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave
;
110 static ieee80211_recv_action_func ht_recv_action_ht_txchwidth
;
112 static ieee80211_send_action_func ht_send_action_ba_addba
;
113 static ieee80211_send_action_func ht_send_action_ba_delba
;
114 static ieee80211_send_action_func ht_send_action_ht_txchwidth
;
117 ieee80211_ht_init(void)
120 * Setup HT parameters that depends on the clock frequency.
122 #ifdef IEEE80211_AMPDU_AGE
123 ieee80211_ampdu_age
= msecs_to_ticks(500);
125 ieee80211_addba_timeout
= msecs_to_ticks(250);
126 ieee80211_addba_backoff
= msecs_to_ticks(10*1000);
127 ieee80211_bar_timeout
= msecs_to_ticks(250);
129 * Register action frame handlers.
131 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA
,
132 IEEE80211_ACTION_BA_ADDBA_REQUEST
, ht_recv_action_ba_addba_request
);
133 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA
,
134 IEEE80211_ACTION_BA_ADDBA_RESPONSE
, ht_recv_action_ba_addba_response
);
135 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA
,
136 IEEE80211_ACTION_BA_DELBA
, ht_recv_action_ba_delba
);
137 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT
,
138 IEEE80211_ACTION_HT_MIMOPWRSAVE
, ht_recv_action_ht_mimopwrsave
);
139 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT
,
140 IEEE80211_ACTION_HT_TXCHWIDTH
, ht_recv_action_ht_txchwidth
);
142 ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA
,
143 IEEE80211_ACTION_BA_ADDBA_REQUEST
, ht_send_action_ba_addba
);
144 ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA
,
145 IEEE80211_ACTION_BA_ADDBA_RESPONSE
, ht_send_action_ba_addba
);
146 ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA
,
147 IEEE80211_ACTION_BA_DELBA
, ht_send_action_ba_delba
);
148 ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT
,
149 IEEE80211_ACTION_HT_TXCHWIDTH
, ht_send_action_ht_txchwidth
);
151 SYSINIT(wlan_ht
, SI_SUB_DRIVERS
, SI_ORDER_FIRST
, ieee80211_ht_init
, NULL
);
153 static int ieee80211_ampdu_enable(struct ieee80211_node
*ni
,
154 struct ieee80211_tx_ampdu
*tap
);
155 static int ieee80211_addba_request(struct ieee80211_node
*ni
,
156 struct ieee80211_tx_ampdu
*tap
,
157 int dialogtoken
, int baparamset
, int batimeout
);
158 static int ieee80211_addba_response(struct ieee80211_node
*ni
,
159 struct ieee80211_tx_ampdu
*tap
,
160 int code
, int baparamset
, int batimeout
);
161 static void ieee80211_addba_stop(struct ieee80211_node
*ni
,
162 struct ieee80211_tx_ampdu
*tap
);
163 static void ieee80211_bar_response(struct ieee80211_node
*ni
,
164 struct ieee80211_tx_ampdu
*tap
, int status
);
165 static void ampdu_tx_stop(struct ieee80211_tx_ampdu
*tap
);
166 static void bar_stop_timer(struct ieee80211_tx_ampdu
*tap
);
167 static int ampdu_rx_start(struct ieee80211_node
*, struct ieee80211_rx_ampdu
*,
168 int baparamset
, int batimeout
, int baseqctl
);
169 static void ampdu_rx_stop(struct ieee80211_node
*, struct ieee80211_rx_ampdu
*);
172 ieee80211_ht_attach(struct ieee80211com
*ic
)
174 /* setup default aggregation policy */
175 ic
->ic_recv_action
= ieee80211_recv_action
;
176 ic
->ic_send_action
= ieee80211_send_action
;
177 ic
->ic_ampdu_enable
= ieee80211_ampdu_enable
;
178 ic
->ic_addba_request
= ieee80211_addba_request
;
179 ic
->ic_addba_response
= ieee80211_addba_response
;
180 ic
->ic_addba_stop
= ieee80211_addba_stop
;
181 ic
->ic_bar_response
= ieee80211_bar_response
;
182 ic
->ic_ampdu_rx_start
= ampdu_rx_start
;
183 ic
->ic_ampdu_rx_stop
= ampdu_rx_stop
;
185 ic
->ic_htprotmode
= IEEE80211_PROT_RTSCTS
;
186 ic
->ic_curhtprotmode
= IEEE80211_HTINFO_OPMODE_PURE
;
190 ieee80211_ht_detach(struct ieee80211com
*ic
)
195 ieee80211_ht_vattach(struct ieee80211vap
*vap
)
198 /* driver can override defaults */
199 vap
->iv_ampdu_rxmax
= IEEE80211_HTCAP_MAXRXAMPDU_8K
;
200 vap
->iv_ampdu_density
= IEEE80211_HTCAP_MPDUDENSITY_NA
;
201 vap
->iv_ampdu_limit
= vap
->iv_ampdu_rxmax
;
202 vap
->iv_amsdu_limit
= vap
->iv_htcaps
& IEEE80211_HTCAP_MAXAMSDU
;
203 /* tx aggregation traffic thresholds */
204 vap
->iv_ampdu_mintraffic
[WME_AC_BK
] = 128;
205 vap
->iv_ampdu_mintraffic
[WME_AC_BE
] = 64;
206 vap
->iv_ampdu_mintraffic
[WME_AC_VO
] = 32;
207 vap
->iv_ampdu_mintraffic
[WME_AC_VI
] = 32;
209 if (vap
->iv_htcaps
& IEEE80211_HTC_HT
) {
211 * Device is HT capable; enable all HT-related
212 * facilities by default.
213 * XXX these choices may be too aggressive.
215 vap
->iv_flags_ht
|= IEEE80211_FHT_HT
216 | IEEE80211_FHT_HTCOMPAT
218 if (vap
->iv_htcaps
& IEEE80211_HTCAP_SHORTGI20
)
219 vap
->iv_flags_ht
|= IEEE80211_FHT_SHORTGI20
;
220 /* XXX infer from channel list? */
221 if (vap
->iv_htcaps
& IEEE80211_HTCAP_CHWIDTH40
) {
222 vap
->iv_flags_ht
|= IEEE80211_FHT_USEHT40
;
223 if (vap
->iv_htcaps
& IEEE80211_HTCAP_SHORTGI40
)
224 vap
->iv_flags_ht
|= IEEE80211_FHT_SHORTGI40
;
226 /* enable RIFS if capable */
227 if (vap
->iv_htcaps
& IEEE80211_HTC_RIFS
)
228 vap
->iv_flags_ht
|= IEEE80211_FHT_RIFS
;
230 /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
231 vap
->iv_flags_ht
|= IEEE80211_FHT_AMPDU_RX
;
232 if (vap
->iv_htcaps
& IEEE80211_HTC_AMPDU
)
233 vap
->iv_flags_ht
|= IEEE80211_FHT_AMPDU_TX
;
234 vap
->iv_flags_ht
|= IEEE80211_FHT_AMSDU_RX
;
235 if (vap
->iv_htcaps
& IEEE80211_HTC_AMSDU
)
236 vap
->iv_flags_ht
|= IEEE80211_FHT_AMSDU_TX
;
238 /* NB: disable default legacy WDS, too many issues right now */
239 if (vap
->iv_flags_ext
& IEEE80211_FEXT_WDSLEGACY
)
240 vap
->iv_flags_ht
&= ~IEEE80211_FHT_HT
;
244 ieee80211_ht_vdetach(struct ieee80211vap
*vap
)
249 ht_announce(struct ieee80211com
*ic
, int mode
,
250 const struct ieee80211_htrateset
*rs
)
252 struct ifnet
*ifp
= ic
->ic_ifp
;
255 if_printf(ifp
, "%s MCS: ", ieee80211_phymode_name
[mode
]);
256 for (i
= 0; i
< rs
->rs_nrates
; i
++) {
257 mword
= ieee80211_rate2media(ic
,
258 rs
->rs_rates
[i
] | IEEE80211_RATE_MCS
, mode
);
259 if (IFM_SUBTYPE(mword
) != IFM_IEEE80211_MCS
)
261 rate
= ieee80211_htrates
[rs
->rs_rates
[i
]].ht40_rate_400ns
;
262 kprintf("%s%d%sMbps", (i
!= 0 ? " " : ""),
263 rate
/ 2, ((rate
& 0x1) != 0 ? ".5" : ""));
269 ieee80211_ht_announce(struct ieee80211com
*ic
)
271 if (isset(ic
->ic_modecaps
, IEEE80211_MODE_11NA
))
272 ht_announce(ic
, IEEE80211_MODE_11NA
, &ieee80211_rateset_11n
);
273 if (isset(ic
->ic_modecaps
, IEEE80211_MODE_11NG
))
274 ht_announce(ic
, IEEE80211_MODE_11NG
, &ieee80211_rateset_11n
);
277 const struct ieee80211_htrateset
*
278 ieee80211_get_suphtrates(struct ieee80211com
*ic
,
279 const struct ieee80211_channel
*c
)
281 return &ieee80211_rateset_11n
;
285 * Receive processing.
289 * Decap the encapsulated A-MSDU frames and dispatch all but
290 * the last for delivery. The last frame is returned for
291 * delivery via the normal path.
294 ieee80211_decap_amsdu(struct ieee80211_node
*ni
, struct mbuf
*m
)
296 struct ieee80211vap
*vap
= ni
->ni_vap
;
300 /* discard 802.3 header inserted by ieee80211_decap */
301 m_adj(m
, sizeof(struct ether_header
));
303 vap
->iv_stats
.is_amsdu_decap
++;
307 * Decap the first frame, bust it apart from the
308 * remainder and deliver. We leave the last frame
309 * delivery to the caller (for consistency with other
310 * code paths, could also do it here).
312 m
= ieee80211_decap1(m
, &framelen
);
314 IEEE80211_DISCARD_MAC(vap
, IEEE80211_MSG_ANY
,
315 ni
->ni_macaddr
, "a-msdu", "%s", "decap failed");
316 vap
->iv_stats
.is_amsdu_tooshort
++;
319 if (m
->m_pkthdr
.len
== framelen
)
321 n
= m_split(m
, framelen
, MB_DONTWAIT
);
323 IEEE80211_DISCARD_MAC(vap
, IEEE80211_MSG_ANY
,
324 ni
->ni_macaddr
, "a-msdu",
325 "%s", "unable to split encapsulated frames");
326 vap
->iv_stats
.is_amsdu_split
++;
327 m_freem(m
); /* NB: must reclaim */
330 vap
->iv_deliver_data(vap
, ni
, m
);
333 * Remove frame contents; each intermediate frame
334 * is required to be aligned to a 4-byte boundary.
337 m_adj(m
, roundup2(framelen
, 4) - framelen
); /* padding */
339 return m
; /* last delivered by caller */
343 * Purge all frames in the A-MPDU re-order queue.
346 ampdu_rx_purge(struct ieee80211_rx_ampdu
*rap
)
351 for (i
= 0; i
< rap
->rxa_wnd
; i
++) {
354 rap
->rxa_m
[i
] = NULL
;
355 rap
->rxa_qbytes
-= m
->m_pkthdr
.len
;
357 if (--rap
->rxa_qframes
== 0)
361 KASSERT(rap
->rxa_qbytes
== 0 && rap
->rxa_qframes
== 0,
362 ("lost %u data, %u frames on ampdu rx q",
363 rap
->rxa_qbytes
, rap
->rxa_qframes
));
367 * Start A-MPDU rx/re-order processing for the specified TID.
370 ampdu_rx_start(struct ieee80211_node
*ni
, struct ieee80211_rx_ampdu
*rap
,
371 int baparamset
, int batimeout
, int baseqctl
)
373 int bufsiz
= MS(baparamset
, IEEE80211_BAPS_BUFSIZ
);
375 if (rap
->rxa_flags
& IEEE80211_AGGR_RUNNING
) {
377 * AMPDU previously setup and not terminated with a DELBA,
378 * flush the reorder q's in case anything remains.
382 memset(rap
, 0, sizeof(*rap
));
383 rap
->rxa_wnd
= (bufsiz
== 0) ?
384 IEEE80211_AGGR_BAWMAX
: min(bufsiz
, IEEE80211_AGGR_BAWMAX
);
385 rap
->rxa_start
= MS(baseqctl
, IEEE80211_BASEQ_START
);
386 rap
->rxa_flags
|= IEEE80211_AGGR_RUNNING
| IEEE80211_AGGR_XCHGPEND
;
392 * Stop A-MPDU rx processing for the specified TID.
395 ampdu_rx_stop(struct ieee80211_node
*ni
, struct ieee80211_rx_ampdu
*rap
)
398 rap
->rxa_flags
&= ~(IEEE80211_AGGR_RUNNING
| IEEE80211_AGGR_XCHGPEND
);
402 * Dispatch a frame from the A-MPDU reorder queue. The
403 * frame is fed back into ieee80211_input marked with an
404 * M_AMPDU_MPDU flag so it doesn't come back to us (it also
405 * permits ieee80211_input to optimize re-processing).
408 ampdu_dispatch(struct ieee80211_node
*ni
, struct mbuf
*m
)
410 m
->m_flags
|= M_AMPDU_MPDU
; /* bypass normal processing */
411 /* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */
412 (void) ieee80211_input(ni
, m
, 0, 0);
416 * Dispatch as many frames as possible from the re-order queue.
417 * Frames will always be "at the front"; we process all frames
418 * up to the first empty slot in the window. On completion we
419 * cleanup state if there are still pending frames in the current
420 * BA window. We assume the frame at slot 0 is already handled
421 * by the caller; we always start at slot 1.
424 ampdu_rx_dispatch(struct ieee80211_rx_ampdu
*rap
, struct ieee80211_node
*ni
)
426 struct ieee80211vap
*vap
= ni
->ni_vap
;
430 /* flush run of frames */
431 for (i
= 1; i
< rap
->rxa_wnd
; i
++) {
435 rap
->rxa_m
[i
] = NULL
;
436 rap
->rxa_qbytes
-= m
->m_pkthdr
.len
;
439 ampdu_dispatch(ni
, m
);
442 * If frames remain, copy the mbuf pointers down so
443 * they correspond to the offsets in the new window.
445 if (rap
->rxa_qframes
!= 0) {
446 int n
= rap
->rxa_qframes
, j
;
447 for (j
= i
+1; j
< rap
->rxa_wnd
; j
++) {
448 if (rap
->rxa_m
[j
] != NULL
) {
449 rap
->rxa_m
[j
-i
] = rap
->rxa_m
[j
];
450 rap
->rxa_m
[j
] = NULL
;
455 KASSERT(n
== 0, ("lost %d frames", n
));
456 vap
->iv_stats
.is_ampdu_rx_copy
+= rap
->rxa_qframes
;
459 * Adjust the start of the BA window to
460 * reflect the frames just dispatched.
462 rap
->rxa_start
= IEEE80211_SEQ_ADD(rap
->rxa_start
, i
);
463 vap
->iv_stats
.is_ampdu_rx_oor
+= i
;
466 #ifdef IEEE80211_AMPDU_AGE
468 * Dispatch all frames in the A-MPDU re-order queue.
471 ampdu_rx_flush(struct ieee80211_node
*ni
, struct ieee80211_rx_ampdu
*rap
)
473 struct ieee80211vap
*vap
= ni
->ni_vap
;
477 for (i
= 0; i
< rap
->rxa_wnd
; i
++) {
481 rap
->rxa_m
[i
] = NULL
;
482 rap
->rxa_qbytes
-= m
->m_pkthdr
.len
;
484 vap
->iv_stats
.is_ampdu_rx_oor
++;
486 ampdu_dispatch(ni
, m
);
487 if (rap
->rxa_qframes
== 0)
491 #endif /* IEEE80211_AMPDU_AGE */
494 * Dispatch all frames in the A-MPDU re-order queue
495 * preceding the specified sequence number. This logic
496 * handles window moves due to a received MSDU or BAR.
499 ampdu_rx_flush_upto(struct ieee80211_node
*ni
,
500 struct ieee80211_rx_ampdu
*rap
, ieee80211_seq winstart
)
502 struct ieee80211vap
*vap
= ni
->ni_vap
;
508 * Flush any complete MSDU's with a sequence number lower
509 * than winstart. Gaps may exist. Note that we may actually
510 * dispatch frames past winstart if a run continues; this is
511 * an optimization that avoids having to do a separate pass
512 * to dispatch frames after moving the BA window start.
514 seqno
= rap
->rxa_start
;
515 for (i
= 0; i
< rap
->rxa_wnd
; i
++) {
518 rap
->rxa_m
[i
] = NULL
;
519 rap
->rxa_qbytes
-= m
->m_pkthdr
.len
;
521 vap
->iv_stats
.is_ampdu_rx_oor
++;
523 ampdu_dispatch(ni
, m
);
525 if (!IEEE80211_SEQ_BA_BEFORE(seqno
, winstart
))
528 seqno
= IEEE80211_SEQ_INC(seqno
);
531 * If frames remain, copy the mbuf pointers down so
532 * they correspond to the offsets in the new window.
534 if (rap
->rxa_qframes
!= 0) {
535 int n
= rap
->rxa_qframes
, j
;
537 /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
538 KASSERT(rap
->rxa_m
[0] == NULL
,
539 ("%s: BA window slot 0 occupied", __func__
));
540 for (j
= i
+1; j
< rap
->rxa_wnd
; j
++) {
541 if (rap
->rxa_m
[j
] != NULL
) {
542 rap
->rxa_m
[j
-i
] = rap
->rxa_m
[j
];
543 rap
->rxa_m
[j
] = NULL
;
548 KASSERT(n
== 0, ("%s: lost %d frames, qframes %d off %d "
549 "BA win <%d:%d> winstart %d",
550 __func__
, n
, rap
->rxa_qframes
, i
, rap
->rxa_start
,
551 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1),
553 vap
->iv_stats
.is_ampdu_rx_copy
+= rap
->rxa_qframes
;
556 * Move the start of the BA window; we use the
557 * sequence number of the last MSDU that was
558 * passed up the stack+1 or winstart if stopped on
559 * a gap in the reorder buffer.
561 rap
->rxa_start
= seqno
;
565 * Process a received QoS data frame for an HT station. Handle
566 * A-MPDU reordering: if this frame is received out of order
567 * and falls within the BA window hold onto it. Otherwise if
568 * this frame completes a run, flush any pending frames. We
569 * return 1 if the frame is consumed. A 0 is returned if
570 * the frame should be processed normally by the caller.
573 ieee80211_ampdu_reorder(struct ieee80211_node
*ni
, struct mbuf
*m
)
575 #define IEEE80211_FC0_QOSDATA \
576 (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
577 #define PROCESS 0 /* caller should process frame */
578 #define CONSUMED 1 /* frame consumed, caller does nothing */
579 struct ieee80211vap
*vap
= ni
->ni_vap
;
580 struct ieee80211_qosframe
*wh
;
581 struct ieee80211_rx_ampdu
*rap
;
586 KASSERT((m
->m_flags
& (M_AMPDU
| M_AMPDU_MPDU
)) == M_AMPDU
,
587 ("!a-mpdu or already re-ordered, flags 0x%x", m
->m_flags
));
588 KASSERT(ni
->ni_flags
& IEEE80211_NODE_HT
, ("not an HT sta"));
590 /* NB: m_len known to be sufficient */
591 wh
= mtod(m
, struct ieee80211_qosframe
*);
592 if (wh
->i_fc
[0] != IEEE80211_FC0_QOSDATA
) {
594 * Not QoS data, shouldn't get here but just
595 * return it to the caller for processing.
599 if (IEEE80211_IS_DSTODS(wh
))
600 tid
= ((struct ieee80211_qosframe_addr4
*)wh
)->i_qos
[0];
603 tid
&= IEEE80211_QOS_TID
;
604 rap
= &ni
->ni_rx_ampdu
[tid
];
605 if ((rap
->rxa_flags
& IEEE80211_AGGR_XCHGPEND
) == 0) {
607 * No ADDBA request yet, don't touch.
611 rxseq
= le16toh(*(uint16_t *)wh
->i_seq
);
612 if ((rxseq
& IEEE80211_SEQ_FRAG_MASK
) != 0) {
614 * Fragments are not allowed; toss.
616 IEEE80211_DISCARD_MAC(vap
,
617 IEEE80211_MSG_INPUT
| IEEE80211_MSG_11N
, ni
->ni_macaddr
,
618 "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq
, tid
,
619 wh
->i_fc
[1] & IEEE80211_FC1_RETRY
? " (retransmit)" : "");
620 vap
->iv_stats
.is_ampdu_rx_drop
++;
621 IEEE80211_NODE_STAT(ni
, rx_drop
);
625 rxseq
>>= IEEE80211_SEQ_SEQ_SHIFT
;
628 if (rxseq
== rap
->rxa_start
) {
630 * First frame in window.
632 if (rap
->rxa_qframes
!= 0) {
634 * Dispatch as many packets as we can.
636 KASSERT(rap
->rxa_m
[0] == NULL
, ("unexpected dup"));
637 ampdu_dispatch(ni
, m
);
638 ampdu_rx_dispatch(rap
, ni
);
642 * In order; advance window and notify
643 * caller to dispatch directly.
645 rap
->rxa_start
= IEEE80211_SEQ_INC(rxseq
);
650 * Frame is out of order; store if in the BA window.
652 /* calculate offset in BA window */
653 off
= IEEE80211_SEQ_SUB(rxseq
, rap
->rxa_start
);
654 if (off
< rap
->rxa_wnd
) {
656 * Common case (hopefully): in the BA window.
657 * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
659 #ifdef IEEE80211_AMPDU_AGE
661 * Check for frames sitting too long in the reorder queue.
662 * This should only ever happen if frames are not delivered
663 * without the sender otherwise notifying us (e.g. with a
664 * BAR to move the window). Typically this happens because
665 * of vendor bugs that cause the sequence number to jump.
666 * When this happens we get a gap in the reorder queue that
667 * leaves frame sitting on the queue until they get pushed
668 * out due to window moves. When the vendor does not send
669 * BAR this move only happens due to explicit packet sends
671 * NB: we only track the time of the oldest frame in the
672 * reorder q; this means that if we flush we might push
673 * frames that still "new"; if this happens then subsequent
674 * frames will result in BA window moves which cost something
675 * but is still better than a big throughput dip.
677 if (rap
->rxa_qframes
!= 0) {
678 /* XXX honor batimeout? */
679 if (ticks
- rap
->rxa_age
> ieee80211_ampdu_age
) {
681 * Too long since we received the first
682 * frame; flush the reorder buffer.
684 if (rap
->rxa_qframes
!= 0) {
685 vap
->iv_stats
.is_ampdu_rx_age
+=
687 ampdu_rx_flush(ni
, rap
);
689 rap
->rxa_start
= IEEE80211_SEQ_INC(rxseq
);
694 * First frame, start aging timer.
696 rap
->rxa_age
= ticks
;
698 #endif /* IEEE80211_AMPDU_AGE */
700 if (rap
->rxa_m
[off
] == NULL
) {
703 rap
->rxa_qbytes
+= m
->m_pkthdr
.len
;
704 vap
->iv_stats
.is_ampdu_rx_reorder
++;
706 IEEE80211_DISCARD_MAC(vap
,
707 IEEE80211_MSG_INPUT
| IEEE80211_MSG_11N
,
708 ni
->ni_macaddr
, "a-mpdu duplicate",
709 "seqno %u tid %u BA win <%u:%u>",
710 rxseq
, tid
, rap
->rxa_start
,
711 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1));
712 vap
->iv_stats
.is_rx_dup
++;
713 IEEE80211_NODE_STAT(ni
, rx_dup
);
718 if (off
< IEEE80211_SEQ_BA_RANGE
) {
720 * Outside the BA window, but within range;
721 * flush the reorder q and move the window.
722 * Sec 9.10.7.6 b) (D2.04 p.118 line 60)
724 IEEE80211_NOTE(vap
, IEEE80211_MSG_11N
, ni
,
725 "move BA win <%u:%u> (%u frames) rxseq %u tid %u",
727 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1),
728 rap
->rxa_qframes
, rxseq
, tid
);
729 vap
->iv_stats
.is_ampdu_rx_move
++;
732 * The spec says to flush frames up to but not including:
733 * WinStart_B = rxseq - rap->rxa_wnd + 1
734 * Then insert the frame or notify the caller to process
735 * it immediately. We can safely do this by just starting
736 * over again because we know the frame will now be within
739 /* NB: rxa_wnd known to be >0 */
740 ampdu_rx_flush_upto(ni
, rap
,
741 IEEE80211_SEQ_SUB(rxseq
, rap
->rxa_wnd
-1));
745 * Outside the BA window and out of range; toss.
746 * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
748 IEEE80211_DISCARD_MAC(vap
,
749 IEEE80211_MSG_INPUT
| IEEE80211_MSG_11N
, ni
->ni_macaddr
,
750 "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
752 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1),
753 rap
->rxa_qframes
, rxseq
, tid
,
754 wh
->i_fc
[1] & IEEE80211_FC1_RETRY
? " (retransmit)" : "");
755 vap
->iv_stats
.is_ampdu_rx_drop
++;
756 IEEE80211_NODE_STAT(ni
, rx_drop
);
762 #undef IEEE80211_FC0_QOSDATA
766 * Process a BAR ctl frame. Dispatch all frames up to
767 * the sequence number of the frame. If this frame is
768 * out of range it's discarded.
771 ieee80211_recv_bar(struct ieee80211_node
*ni
, struct mbuf
*m0
)
773 struct ieee80211vap
*vap
= ni
->ni_vap
;
774 struct ieee80211_frame_bar
*wh
;
775 struct ieee80211_rx_ampdu
*rap
;
779 if (!ieee80211_recv_bar_ena
) {
781 IEEE80211_DISCARD_MAC(vap
, IEEE80211_MSG_11N
,
782 ni
->ni_macaddr
, "BAR", "%s", "processing disabled");
784 vap
->iv_stats
.is_ampdu_bar_bad
++;
787 wh
= mtod(m0
, struct ieee80211_frame_bar
*);
788 /* XXX check basic BAR */
789 tid
= MS(le16toh(wh
->i_ctl
), IEEE80211_BAR_TID
);
790 rap
= &ni
->ni_rx_ampdu
[tid
];
791 if ((rap
->rxa_flags
& IEEE80211_AGGR_XCHGPEND
) == 0) {
793 * No ADDBA request yet, don't touch.
795 IEEE80211_DISCARD_MAC(vap
,
796 IEEE80211_MSG_INPUT
| IEEE80211_MSG_11N
,
797 ni
->ni_macaddr
, "BAR", "no BA stream, tid %u", tid
);
798 vap
->iv_stats
.is_ampdu_bar_bad
++;
801 vap
->iv_stats
.is_ampdu_bar_rx
++;
802 rxseq
= le16toh(wh
->i_seq
) >> IEEE80211_SEQ_SEQ_SHIFT
;
803 if (rxseq
== rap
->rxa_start
)
805 /* calculate offset in BA window */
806 off
= IEEE80211_SEQ_SUB(rxseq
, rap
->rxa_start
);
807 if (off
< IEEE80211_SEQ_BA_RANGE
) {
809 * Flush the reorder q up to rxseq and move the window.
810 * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
812 IEEE80211_NOTE(vap
, IEEE80211_MSG_11N
, ni
,
813 "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
815 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1),
816 rap
->rxa_qframes
, rxseq
, tid
);
817 vap
->iv_stats
.is_ampdu_bar_move
++;
819 ampdu_rx_flush_upto(ni
, rap
, rxseq
);
820 if (off
>= rap
->rxa_wnd
) {
822 * BAR specifies a window start to the right of BA
823 * window; we must move it explicitly since
824 * ampdu_rx_flush_upto will not.
826 rap
->rxa_start
= rxseq
;
830 * Out of range; toss.
831 * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
833 IEEE80211_DISCARD_MAC(vap
,
834 IEEE80211_MSG_INPUT
| IEEE80211_MSG_11N
, ni
->ni_macaddr
,
835 "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
837 IEEE80211_SEQ_ADD(rap
->rxa_start
, rap
->rxa_wnd
-1),
838 rap
->rxa_qframes
, rxseq
, tid
,
839 wh
->i_fc
[1] & IEEE80211_FC1_RETRY
? " (retransmit)" : "");
840 vap
->iv_stats
.is_ampdu_bar_oow
++;
841 IEEE80211_NODE_STAT(ni
, rx_drop
);
846 * Setup HT-specific state in a node. Called only
847 * when HT use is negotiated so we don't do extra
848 * work for temporary and/or legacy sta's.
851 ieee80211_ht_node_init(struct ieee80211_node
*ni
)
853 struct ieee80211_tx_ampdu
*tap
;
856 if (ni
->ni_flags
& IEEE80211_NODE_HT
) {
858 * Clean AMPDU state on re-associate. This handles the case
859 * where a station leaves w/o notifying us and then returns
860 * before node is reaped for inactivity.
862 ieee80211_ht_node_cleanup(ni
);
864 for (ac
= 0; ac
< WME_NUM_AC
; ac
++) {
865 tap
= &ni
->ni_tx_ampdu
[ac
];
868 /* NB: further initialization deferred */
870 ni
->ni_flags
|= IEEE80211_NODE_HT
| IEEE80211_NODE_AMPDU
;
874 * Cleanup HT-specific state in a node. Called only
875 * when HT use has been marked.
878 ieee80211_ht_node_cleanup(struct ieee80211_node
*ni
)
880 struct ieee80211com
*ic
= ni
->ni_ic
;
883 KASSERT(ni
->ni_flags
& IEEE80211_NODE_HT
, ("not an HT node"));
885 /* XXX optimize this */
886 for (i
= 0; i
< WME_NUM_AC
; i
++) {
887 struct ieee80211_tx_ampdu
*tap
= &ni
->ni_tx_ampdu
[i
];
888 if (tap
->txa_flags
& IEEE80211_AGGR_SETUP
)
891 for (i
= 0; i
< WME_NUM_TID
; i
++)
892 ic
->ic_ampdu_rx_stop(ni
, &ni
->ni_rx_ampdu
[i
]);
895 ni
->ni_flags
&= ~IEEE80211_NODE_HT_ALL
;
899 * Age out HT resources for a station.
902 ieee80211_ht_node_age(struct ieee80211_node
*ni
)
904 #ifdef IEEE80211_AMPDU_AGE
905 struct ieee80211vap
*vap
= ni
->ni_vap
;
909 KASSERT(ni
->ni_flags
& IEEE80211_NODE_HT
, ("not an HT sta"));
911 #ifdef IEEE80211_AMPDU_AGE
912 for (tid
= 0; tid
< WME_NUM_TID
; tid
++) {
913 struct ieee80211_rx_ampdu
*rap
;
915 rap
= &ni
->ni_rx_ampdu
[tid
];
916 if ((rap
->rxa_flags
& IEEE80211_AGGR_XCHGPEND
) == 0)
918 if (rap
->rxa_qframes
== 0)
921 * Check for frames sitting too long in the reorder queue.
922 * See above for more details on what's happening here.
924 /* XXX honor batimeout? */
925 if (ticks
- rap
->rxa_age
> ieee80211_ampdu_age
) {
927 * Too long since we received the first
928 * frame; flush the reorder buffer.
930 vap
->iv_stats
.is_ampdu_rx_age
+= rap
->rxa_qframes
;
931 ampdu_rx_flush(ni
, rap
);
934 #endif /* IEEE80211_AMPDU_AGE */
937 static struct ieee80211_channel
*
938 findhtchan(struct ieee80211com
*ic
, struct ieee80211_channel
*c
, int htflags
)
940 return ieee80211_find_channel(ic
, c
->ic_freq
,
941 (c
->ic_flags
&~ IEEE80211_CHAN_HT
) | htflags
);
945 * Adjust a channel to be HT/non-HT according to the vap's configuration.
947 struct ieee80211_channel
*
948 ieee80211_ht_adjust_channel(struct ieee80211com
*ic
,
949 struct ieee80211_channel
*chan
, int flags
)
951 struct ieee80211_channel
*c
;
953 if (flags
& IEEE80211_FHT_HT
) {
954 /* promote to HT if possible */
955 if (flags
& IEEE80211_FHT_USEHT40
) {
956 if (!IEEE80211_IS_CHAN_HT40(chan
)) {
957 /* NB: arbitrarily pick ht40+ over ht40- */
958 c
= findhtchan(ic
, chan
, IEEE80211_CHAN_HT40U
);
960 c
= findhtchan(ic
, chan
,
961 IEEE80211_CHAN_HT40D
);
963 c
= findhtchan(ic
, chan
,
964 IEEE80211_CHAN_HT20
);
968 } else if (!IEEE80211_IS_CHAN_HT20(chan
)) {
969 c
= findhtchan(ic
, chan
, IEEE80211_CHAN_HT20
);
973 } else if (IEEE80211_IS_CHAN_HT(chan
)) {
974 /* demote to legacy, HT use is disabled */
975 c
= ieee80211_find_channel(ic
, chan
->ic_freq
,
976 chan
->ic_flags
&~ IEEE80211_CHAN_HT
);
984 * Setup HT-specific state for a legacy WDS peer.
987 ieee80211_ht_wds_init(struct ieee80211_node
*ni
)
989 struct ieee80211vap
*vap
= ni
->ni_vap
;
990 struct ieee80211_tx_ampdu
*tap
;
993 KASSERT(vap
->iv_flags_ht
& IEEE80211_FHT_HT
, ("no HT requested"));
995 /* XXX check scan cache in case peer has an ap and we have info */
997 * If setup with a legacy channel; locate an HT channel.
998 * Otherwise if the inherited channel (from a companion
999 * AP) is suitable use it so we use the same location
1000 * for the extension channel).
1002 ni
->ni_chan
= ieee80211_ht_adjust_channel(ni
->ni_ic
,
1003 ni
->ni_chan
, ieee80211_htchanflags(ni
->ni_chan
));
1006 if (vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI20
)
1007 ni
->ni_htcap
|= IEEE80211_HTCAP_SHORTGI20
;
1008 if (IEEE80211_IS_CHAN_HT40(ni
->ni_chan
)) {
1009 ni
->ni_htcap
|= IEEE80211_HTCAP_CHWIDTH40
;
1011 if (IEEE80211_IS_CHAN_HT40U(ni
->ni_chan
))
1012 ni
->ni_ht2ndchan
= IEEE80211_HTINFO_2NDCHAN_ABOVE
;
1013 else if (IEEE80211_IS_CHAN_HT40D(ni
->ni_chan
))
1014 ni
->ni_ht2ndchan
= IEEE80211_HTINFO_2NDCHAN_BELOW
;
1015 if (vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI40
)
1016 ni
->ni_htcap
|= IEEE80211_HTCAP_SHORTGI40
;
1019 ni
->ni_ht2ndchan
= IEEE80211_HTINFO_2NDCHAN_NONE
;
1021 ni
->ni_htctlchan
= ni
->ni_chan
->ic_ieee
;
1022 if (vap
->iv_flags_ht
& IEEE80211_FHT_RIFS
)
1023 ni
->ni_flags
|= IEEE80211_NODE_RIFS
;
1024 /* XXX does it make sense to enable SMPS? */
1026 ni
->ni_htopmode
= 0; /* XXX need protection state */
1027 ni
->ni_htstbc
= 0; /* XXX need info */
1029 for (ac
= 0; ac
< WME_NUM_AC
; ac
++) {
1030 tap
= &ni
->ni_tx_ampdu
[ac
];
1033 /* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */
1034 ni
->ni_flags
|= IEEE80211_NODE_HT
| IEEE80211_NODE_AMPDU
;
1038 * Notify hostap vaps of a change in the HTINFO ie.
1041 htinfo_notify(struct ieee80211com
*ic
)
1043 struct ieee80211vap
*vap
;
1046 TAILQ_FOREACH(vap
, &ic
->ic_vaps
, iv_next
) {
1047 if (vap
->iv_opmode
!= IEEE80211_M_HOSTAP
)
1049 if (vap
->iv_state
!= IEEE80211_S_RUN
||
1050 !IEEE80211_IS_CHAN_HT(vap
->iv_bss
->ni_chan
))
1054 IEEE80211_MSG_ASSOC
| IEEE80211_MSG_11N
,
1056 "HT bss occupancy change: %d sta, %d ht, "
1057 "%d ht40%s, HT protmode now 0x%x"
1059 , ic
->ic_ht_sta_assoc
1060 , ic
->ic_ht40_sta_assoc
1061 , (ic
->ic_flags_ht
& IEEE80211_FHT_NONHT_PR
) ?
1062 ", non-HT sta present" : ""
1063 , ic
->ic_curhtprotmode
);
1066 ieee80211_beacon_notify(vap
, IEEE80211_BEACON_HTINFO
);
1071 * Calculate HT protection mode from current
1072 * state and handle updates.
1075 htinfo_update(struct ieee80211com
*ic
)
1079 if (ic
->ic_sta_assoc
!= ic
->ic_ht_sta_assoc
) {
1080 protmode
= IEEE80211_HTINFO_OPMODE_MIXED
1081 | IEEE80211_HTINFO_NONHT_PRESENT
;
1082 } else if (ic
->ic_flags_ht
& IEEE80211_FHT_NONHT_PR
) {
1083 protmode
= IEEE80211_HTINFO_OPMODE_PROTOPT
1084 | IEEE80211_HTINFO_NONHT_PRESENT
;
1085 } else if (ic
->ic_bsschan
!= IEEE80211_CHAN_ANYC
&&
1086 IEEE80211_IS_CHAN_HT40(ic
->ic_bsschan
) &&
1087 ic
->ic_sta_assoc
!= ic
->ic_ht40_sta_assoc
) {
1088 protmode
= IEEE80211_HTINFO_OPMODE_HT20PR
;
1090 protmode
= IEEE80211_HTINFO_OPMODE_PURE
;
1092 if (protmode
!= ic
->ic_curhtprotmode
) {
1093 ic
->ic_curhtprotmode
= protmode
;
1099 * Handle an HT station joining a BSS.
1102 ieee80211_ht_node_join(struct ieee80211_node
*ni
)
1104 struct ieee80211com
*ic
= ni
->ni_ic
;
1106 if (ni
->ni_flags
& IEEE80211_NODE_HT
) {
1107 ic
->ic_ht_sta_assoc
++;
1108 if (ni
->ni_chw
== 40)
1109 ic
->ic_ht40_sta_assoc
++;
1115 * Handle an HT station leaving a BSS.
1118 ieee80211_ht_node_leave(struct ieee80211_node
*ni
)
1120 struct ieee80211com
*ic
= ni
->ni_ic
;
1122 if (ni
->ni_flags
& IEEE80211_NODE_HT
) {
1123 ic
->ic_ht_sta_assoc
--;
1124 if (ni
->ni_chw
== 40)
1125 ic
->ic_ht40_sta_assoc
--;
1131 * Public version of htinfo_update; used for processing
1132 * beacon frames from overlapping bss.
1134 * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
1135 * (on receipt of a beacon that advertises MIXED) or
1136 * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
1137 * from an overlapping legacy bss). We treat MIXED with
1138 * a higher precedence than PROTOPT (i.e. we will not change
1139 * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This
1140 * corresponds to how we handle things in htinfo_update.
1143 ieee80211_htprot_update(struct ieee80211com
*ic
, int protmode
)
1145 #define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE)
1146 /* track non-HT station presence */
1147 KASSERT(protmode
& IEEE80211_HTINFO_NONHT_PRESENT
,
1148 ("protmode 0x%x", protmode
));
1149 ic
->ic_flags_ht
|= IEEE80211_FHT_NONHT_PR
;
1150 ic
->ic_lastnonht
= ticks
;
1152 if (protmode
!= ic
->ic_curhtprotmode
&&
1153 (OPMODE(ic
->ic_curhtprotmode
) != IEEE80211_HTINFO_OPMODE_MIXED
||
1154 OPMODE(protmode
) == IEEE80211_HTINFO_OPMODE_PROTOPT
)) {
1155 /* push beacon update */
1156 ic
->ic_curhtprotmode
= protmode
;
1163 * Time out presence of an overlapping bss with non-HT
1164 * stations. When operating in hostap mode we listen for
1165 * beacons from other stations and if we identify a non-HT
1166 * station is present we update the opmode field of the
1167 * HTINFO ie. To identify when all non-HT stations are
1168 * gone we time out this condition.
1171 ieee80211_ht_timeout(struct ieee80211com
*ic
)
1173 if ((ic
->ic_flags_ht
& IEEE80211_FHT_NONHT_PR
) &&
1174 time_after(ticks
, ic
->ic_lastnonht
+ IEEE80211_NONHT_PRESENT_AGE
)) {
1176 IEEE80211_NOTE(vap
, IEEE80211_MSG_11N
, ni
,
1177 "%s", "time out non-HT STA present on channel");
1179 ic
->ic_flags_ht
&= ~IEEE80211_FHT_NONHT_PR
;
1184 /* unalligned little endian access */
1185 #define LE_READ_2(p) \
1187 ((((const uint8_t *)(p))[0] ) | \
1188 (((const uint8_t *)(p))[1] << 8)))
1191 * Process an 802.11n HT capabilities ie.
1194 ieee80211_parse_htcap(struct ieee80211_node
*ni
, const uint8_t *ie
)
1196 if (ie
[0] == IEEE80211_ELEMID_VENDOR
) {
1198 * Station used Vendor OUI ie to associate;
1199 * mark the node so when we respond we'll use
1200 * the Vendor OUI's and not the standard ie's.
1202 ni
->ni_flags
|= IEEE80211_NODE_HTCOMPAT
;
1205 ni
->ni_flags
&= ~IEEE80211_NODE_HTCOMPAT
;
1207 ni
->ni_htcap
= LE_READ_2(ie
+
1208 __offsetof(struct ieee80211_ie_htcap
, hc_cap
));
1209 ni
->ni_htparam
= ie
[__offsetof(struct ieee80211_ie_htcap
, hc_param
)];
1213 htinfo_parse(struct ieee80211_node
*ni
,
1214 const struct ieee80211_ie_htinfo
*htinfo
)
1218 ni
->ni_htctlchan
= htinfo
->hi_ctrlchannel
;
1219 ni
->ni_ht2ndchan
= SM(htinfo
->hi_byte1
, IEEE80211_HTINFO_2NDCHAN
);
1220 w
= LE_READ_2(&htinfo
->hi_byte2
);
1221 ni
->ni_htopmode
= SM(w
, IEEE80211_HTINFO_OPMODE
);
1222 w
= LE_READ_2(&htinfo
->hi_byte45
);
1223 ni
->ni_htstbc
= SM(w
, IEEE80211_HTINFO_BASIC_STBCMCS
);
1227 * Parse an 802.11n HT info ie and save useful information
1228 * to the node state. Note this does not effect any state
1229 * changes such as for channel width change.
1232 ieee80211_parse_htinfo(struct ieee80211_node
*ni
, const uint8_t *ie
)
1234 if (ie
[0] == IEEE80211_ELEMID_VENDOR
)
1236 htinfo_parse(ni
, (const struct ieee80211_ie_htinfo
*) ie
);
1240 * Handle 11n channel switch. Use the received HT ie's to
1241 * identify the right channel to use. If we cannot locate it
1242 * in the channel table then fallback to legacy operation.
1243 * Note that we use this information to identify the node's
1244 * channel only; the caller is responsible for insuring any
1245 * required channel change is done (e.g. in sta mode when
1246 * parsing the contents of a beacon frame).
1249 htinfo_update_chw(struct ieee80211_node
*ni
, int htflags
)
1251 struct ieee80211com
*ic
= ni
->ni_ic
;
1252 struct ieee80211_channel
*c
;
1255 chanflags
= (ni
->ni_chan
->ic_flags
&~ IEEE80211_CHAN_HT
) | htflags
;
1256 if (chanflags
!= ni
->ni_chan
->ic_flags
) {
1257 /* XXX not right for ht40- */
1258 c
= ieee80211_find_channel(ic
, ni
->ni_chan
->ic_freq
, chanflags
);
1259 if (c
== NULL
&& (htflags
& IEEE80211_CHAN_HT40
)) {
1261 * No HT40 channel entry in our table; fall back
1262 * to HT20 operation. This should not happen.
1264 c
= findhtchan(ic
, ni
->ni_chan
, IEEE80211_CHAN_HT20
);
1266 IEEE80211_NOTE(ni
->ni_vap
,
1267 IEEE80211_MSG_ASSOC
| IEEE80211_MSG_11N
, ni
,
1268 "no HT40 channel (freq %u), falling back to HT20",
1269 ni
->ni_chan
->ic_freq
);
1273 if (c
!= NULL
&& c
!= ni
->ni_chan
) {
1274 IEEE80211_NOTE(ni
->ni_vap
,
1275 IEEE80211_MSG_ASSOC
| IEEE80211_MSG_11N
, ni
,
1276 "switch station to HT%d channel %u/0x%x",
1277 IEEE80211_IS_CHAN_HT40(c
) ? 40 : 20,
1278 c
->ic_freq
, c
->ic_flags
);
1281 /* NB: caller responsible for forcing any channel change */
1283 /* update node's tx channel width */
1284 ni
->ni_chw
= IEEE80211_IS_CHAN_HT40(ni
->ni_chan
)? 40 : 20;
1288 * Update 11n MIMO PS state according to received htcap.
1291 htcap_update_mimo_ps(struct ieee80211_node
*ni
)
1293 uint16_t oflags
= ni
->ni_flags
;
1295 switch (ni
->ni_htcap
& IEEE80211_HTCAP_SMPS
) {
1296 case IEEE80211_HTCAP_SMPS_DYNAMIC
:
1297 ni
->ni_flags
|= IEEE80211_NODE_MIMO_PS
;
1298 ni
->ni_flags
|= IEEE80211_NODE_MIMO_RTS
;
1300 case IEEE80211_HTCAP_SMPS_ENA
:
1301 ni
->ni_flags
|= IEEE80211_NODE_MIMO_PS
;
1302 ni
->ni_flags
&= ~IEEE80211_NODE_MIMO_RTS
;
1304 case IEEE80211_HTCAP_SMPS_OFF
:
1305 default: /* disable on rx of reserved value */
1306 ni
->ni_flags
&= ~IEEE80211_NODE_MIMO_PS
;
1307 ni
->ni_flags
&= ~IEEE80211_NODE_MIMO_RTS
;
1310 return (oflags
^ ni
->ni_flags
);
1314 * Update short GI state according to received htcap
1315 * and local settings.
1317 static __inline
void
1318 htcap_update_shortgi(struct ieee80211_node
*ni
)
1320 struct ieee80211vap
*vap
= ni
->ni_vap
;
1322 ni
->ni_flags
&= ~(IEEE80211_NODE_SGI20
|IEEE80211_NODE_SGI40
);
1323 if ((ni
->ni_htcap
& IEEE80211_HTCAP_SHORTGI20
) &&
1324 (vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI20
))
1325 ni
->ni_flags
|= IEEE80211_NODE_SGI20
;
1326 if ((ni
->ni_htcap
& IEEE80211_HTCAP_SHORTGI40
) &&
1327 (vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI40
))
1328 ni
->ni_flags
|= IEEE80211_NODE_SGI40
;
1332 * Parse and update HT-related state extracted from
1333 * the HT cap and info ie's.
1336 ieee80211_ht_updateparams(struct ieee80211_node
*ni
,
1337 const uint8_t *htcapie
, const uint8_t *htinfoie
)
1339 struct ieee80211vap
*vap
= ni
->ni_vap
;
1340 const struct ieee80211_ie_htinfo
*htinfo
;
1343 ieee80211_parse_htcap(ni
, htcapie
);
1344 if (vap
->iv_htcaps
& IEEE80211_HTCAP_SMPS
)
1345 htcap_update_mimo_ps(ni
);
1346 htcap_update_shortgi(ni
);
1348 if (htinfoie
[0] == IEEE80211_ELEMID_VENDOR
)
1350 htinfo
= (const struct ieee80211_ie_htinfo
*) htinfoie
;
1351 htinfo_parse(ni
, htinfo
);
1353 htflags
= (vap
->iv_flags_ht
& IEEE80211_FHT_HT
) ?
1354 IEEE80211_CHAN_HT20
: 0;
1355 /* NB: honor operating mode constraint */
1356 if ((htinfo
->hi_byte1
& IEEE80211_HTINFO_TXWIDTH_2040
) &&
1357 (vap
->iv_flags_ht
& IEEE80211_FHT_USEHT40
)) {
1358 if (ni
->ni_ht2ndchan
== IEEE80211_HTINFO_2NDCHAN_ABOVE
)
1359 htflags
= IEEE80211_CHAN_HT40U
;
1360 else if (ni
->ni_ht2ndchan
== IEEE80211_HTINFO_2NDCHAN_BELOW
)
1361 htflags
= IEEE80211_CHAN_HT40D
;
1363 htinfo_update_chw(ni
, htflags
);
1365 if ((htinfo
->hi_byte1
& IEEE80211_HTINFO_RIFSMODE_PERM
) &&
1366 (vap
->iv_flags_ht
& IEEE80211_FHT_RIFS
))
1367 ni
->ni_flags
|= IEEE80211_NODE_RIFS
;
1369 ni
->ni_flags
&= ~IEEE80211_NODE_RIFS
;
1373 * Parse and update HT-related state extracted from the HT cap ie
1374 * for a station joining an HT BSS.
1377 ieee80211_ht_updatehtcap(struct ieee80211_node
*ni
, const uint8_t *htcapie
)
1379 struct ieee80211vap
*vap
= ni
->ni_vap
;
1382 ieee80211_parse_htcap(ni
, htcapie
);
1383 if (vap
->iv_htcaps
& IEEE80211_HTCAP_SMPS
)
1384 htcap_update_mimo_ps(ni
);
1385 htcap_update_shortgi(ni
);
1387 /* NB: honor operating mode constraint */
1388 /* XXX 40 MHZ intolerant */
1389 htflags
= (vap
->iv_flags_ht
& IEEE80211_FHT_HT
) ?
1390 IEEE80211_CHAN_HT20
: 0;
1391 if ((ni
->ni_htcap
& IEEE80211_HTCAP_CHWIDTH40
) &&
1392 (vap
->iv_flags_ht
& IEEE80211_FHT_USEHT40
)) {
1393 if (IEEE80211_IS_CHAN_HT40U(vap
->iv_bss
->ni_chan
))
1394 htflags
= IEEE80211_CHAN_HT40U
;
1395 else if (IEEE80211_IS_CHAN_HT40D(vap
->iv_bss
->ni_chan
))
1396 htflags
= IEEE80211_CHAN_HT40D
;
1398 htinfo_update_chw(ni
, htflags
);
1402 * Install received HT rate set by parsing the HT cap ie.
1405 ieee80211_setup_htrates(struct ieee80211_node
*ni
, const uint8_t *ie
, int flags
)
1407 struct ieee80211vap
*vap
= ni
->ni_vap
;
1408 const struct ieee80211_ie_htcap
*htcap
;
1409 struct ieee80211_htrateset
*rs
;
1412 rs
= &ni
->ni_htrates
;
1413 memset(rs
, 0, sizeof(*rs
));
1415 if (ie
[0] == IEEE80211_ELEMID_VENDOR
)
1417 htcap
= (const struct ieee80211_ie_htcap
*) ie
;
1418 for (i
= 0; i
< IEEE80211_HTRATE_MAXSIZE
; i
++) {
1419 if (isclr(htcap
->hc_mcsset
, i
))
1421 if (rs
->rs_nrates
== IEEE80211_HTRATE_MAXSIZE
) {
1423 IEEE80211_MSG_XRATE
| IEEE80211_MSG_11N
, ni
,
1424 "WARNING, HT rate set too large; only "
1425 "using %u rates", IEEE80211_HTRATE_MAXSIZE
);
1426 vap
->iv_stats
.is_rx_rstoobig
++;
1429 rs
->rs_rates
[rs
->rs_nrates
++] = i
;
1432 return ieee80211_fix_rate(ni
, (struct ieee80211_rateset
*) rs
, flags
);
1436 * Mark rates in a node's HT rate set as basic according
1437 * to the information in the supplied HT info ie.
1440 ieee80211_setup_basic_htrates(struct ieee80211_node
*ni
, const uint8_t *ie
)
1442 const struct ieee80211_ie_htinfo
*htinfo
;
1443 struct ieee80211_htrateset
*rs
;
1446 if (ie
[0] == IEEE80211_ELEMID_VENDOR
)
1448 htinfo
= (const struct ieee80211_ie_htinfo
*) ie
;
1449 rs
= &ni
->ni_htrates
;
1450 if (rs
->rs_nrates
== 0) {
1451 IEEE80211_NOTE(ni
->ni_vap
,
1452 IEEE80211_MSG_XRATE
| IEEE80211_MSG_11N
, ni
,
1453 "%s", "WARNING, empty HT rate set");
1456 for (i
= 0; i
< IEEE80211_HTRATE_MAXSIZE
; i
++) {
1457 if (isclr(htinfo
->hi_basicmcsset
, i
))
1459 for (j
= 0; j
< rs
->rs_nrates
; j
++)
1460 if ((rs
->rs_rates
[j
] & IEEE80211_RATE_VAL
) == i
)
1461 rs
->rs_rates
[j
] |= IEEE80211_RATE_BASIC
;
1466 ampdu_tx_setup(struct ieee80211_tx_ampdu
*tap
)
1468 callout_init_mp(&tap
->txa_timer
);
1469 tap
->txa_flags
|= IEEE80211_AGGR_SETUP
;
1473 ampdu_tx_stop(struct ieee80211_tx_ampdu
*tap
)
1475 struct ieee80211_node
*ni
= tap
->txa_ni
;
1476 struct ieee80211com
*ic
= ni
->ni_ic
;
1478 KASSERT(tap
->txa_flags
& IEEE80211_AGGR_SETUP
,
1479 ("txa_flags 0x%x ac %d", tap
->txa_flags
, tap
->txa_ac
));
1482 * Stop BA stream if setup so driver has a chance
1483 * to reclaim any resources it might have allocated.
1485 ic
->ic_addba_stop(ni
, tap
);
1487 * Stop any pending BAR transmit.
1489 bar_stop_timer(tap
);
1491 tap
->txa_lastsample
= 0;
1492 tap
->txa_avgpps
= 0;
1493 /* NB: clearing NAK means we may re-send ADDBA */
1494 tap
->txa_flags
&= ~(IEEE80211_AGGR_SETUP
| IEEE80211_AGGR_NAK
);
1498 addba_timeout_callout(void *arg
)
1500 struct ieee80211_tx_ampdu
*tap
= arg
;
1502 wlan_serialize_enter();
1504 tap
->txa_flags
&= ~IEEE80211_AGGR_XCHGPEND
;
1505 tap
->txa_attempts
++;
1506 wlan_serialize_exit();
1510 addba_start_timeout(struct ieee80211_tx_ampdu
*tap
)
1512 /* XXX use CALLOUT_PENDING instead? */
1513 callout_reset(&tap
->txa_timer
, ieee80211_addba_timeout
,
1514 addba_timeout_callout
, tap
);
1515 tap
->txa_flags
|= IEEE80211_AGGR_XCHGPEND
;
1516 tap
->txa_nextrequest
= ticks
+ ieee80211_addba_timeout
;
1520 addba_stop_timeout(struct ieee80211_tx_ampdu
*tap
)
1522 /* XXX use CALLOUT_PENDING instead? */
1523 if (tap
->txa_flags
& IEEE80211_AGGR_XCHGPEND
) {
1524 callout_stop(&tap
->txa_timer
);
1525 tap
->txa_flags
&= ~IEEE80211_AGGR_XCHGPEND
;
1530 * Default method for requesting A-MPDU tx aggregation.
1531 * We setup the specified state block and start a timer
1532 * to wait for an ADDBA response frame.
1535 ieee80211_addba_request(struct ieee80211_node
*ni
,
1536 struct ieee80211_tx_ampdu
*tap
,
1537 int dialogtoken
, int baparamset
, int batimeout
)
1542 tap
->txa_token
= dialogtoken
;
1543 tap
->txa_flags
|= IEEE80211_AGGR_IMMEDIATE
;
1544 bufsiz
= MS(baparamset
, IEEE80211_BAPS_BUFSIZ
);
1545 tap
->txa_wnd
= (bufsiz
== 0) ?
1546 IEEE80211_AGGR_BAWMAX
: min(bufsiz
, IEEE80211_AGGR_BAWMAX
);
1547 addba_start_timeout(tap
);
1552 * Default method for processing an A-MPDU tx aggregation
1553 * response. We shutdown any pending timer and update the
1554 * state block according to the reply.
1557 ieee80211_addba_response(struct ieee80211_node
*ni
,
1558 struct ieee80211_tx_ampdu
*tap
,
1559 int status
, int baparamset
, int batimeout
)
1564 addba_stop_timeout(tap
);
1565 if (status
== IEEE80211_STATUS_SUCCESS
) {
1566 bufsiz
= MS(baparamset
, IEEE80211_BAPS_BUFSIZ
);
1567 /* XXX override our request? */
1568 tap
->txa_wnd
= (bufsiz
== 0) ?
1569 IEEE80211_AGGR_BAWMAX
: min(bufsiz
, IEEE80211_AGGR_BAWMAX
);
1571 tid
= MS(baparamset
, IEEE80211_BAPS_TID
);
1572 tap
->txa_flags
|= IEEE80211_AGGR_RUNNING
;
1573 tap
->txa_attempts
= 0;
1575 /* mark tid so we don't try again */
1576 tap
->txa_flags
|= IEEE80211_AGGR_NAK
;
1582 * Default method for stopping A-MPDU tx aggregation.
1583 * Any timer is cleared and we drain any pending frames.
1586 ieee80211_addba_stop(struct ieee80211_node
*ni
, struct ieee80211_tx_ampdu
*tap
)
1589 addba_stop_timeout(tap
);
1590 if (tap
->txa_flags
& IEEE80211_AGGR_RUNNING
) {
1591 /* XXX clear aggregation queue */
1592 tap
->txa_flags
&= ~IEEE80211_AGGR_RUNNING
;
1594 tap
->txa_attempts
= 0;
1598 * Process a received action frame using the default aggregation
1599 * policy. We intercept ADDBA-related frames and use them to
1600 * update our aggregation state. All other frames are passed up
1601 * for processing by ieee80211_recv_action.
1604 ht_recv_action_ba_addba_request(struct ieee80211_node
*ni
,
1605 const struct ieee80211_frame
*wh
,
1606 const uint8_t *frm
, const uint8_t *efrm
)
1608 struct ieee80211com
*ic
= ni
->ni_ic
;
1609 struct ieee80211vap
*vap
= ni
->ni_vap
;
1610 struct ieee80211_rx_ampdu
*rap
;
1611 uint8_t dialogtoken
;
1612 uint16_t baparamset
, batimeout
, baseqctl
;
1616 dialogtoken
= frm
[2];
1617 baparamset
= LE_READ_2(frm
+3);
1618 batimeout
= LE_READ_2(frm
+5);
1619 baseqctl
= LE_READ_2(frm
+7);
1621 tid
= MS(baparamset
, IEEE80211_BAPS_TID
);
1623 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
1624 "recv ADDBA request: dialogtoken %u baparamset 0x%x "
1625 "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d",
1626 dialogtoken
, baparamset
,
1627 tid
, MS(baparamset
, IEEE80211_BAPS_BUFSIZ
),
1629 MS(baseqctl
, IEEE80211_BASEQ_START
),
1630 MS(baseqctl
, IEEE80211_BASEQ_FRAG
));
1632 rap
= &ni
->ni_rx_ampdu
[tid
];
1634 /* Send ADDBA response */
1635 args
[0] = dialogtoken
;
1637 * NB: We ack only if the sta associated with HT and
1638 * the ap is configured to do AMPDU rx (the latter
1639 * violates the 11n spec and is mostly for testing).
1641 if ((ni
->ni_flags
& IEEE80211_NODE_AMPDU_RX
) &&
1642 (vap
->iv_flags_ht
& IEEE80211_FHT_AMPDU_RX
)) {
1643 /* XXX handle ampdu_rx_start failure */
1644 ic
->ic_ampdu_rx_start(ni
, rap
,
1645 baparamset
, batimeout
, baseqctl
);
1647 args
[1] = IEEE80211_STATUS_SUCCESS
;
1649 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1650 ni
, "reject ADDBA request: %s",
1651 ni
->ni_flags
& IEEE80211_NODE_AMPDU_RX
?
1652 "administratively disabled" :
1653 "not negotiated for station");
1654 vap
->iv_stats
.is_addba_reject
++;
1655 args
[1] = IEEE80211_STATUS_UNSPECIFIED
;
1657 /* XXX honor rap flags? */
1658 args
[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
1659 | SM(tid
, IEEE80211_BAPS_TID
)
1660 | SM(rap
->rxa_wnd
, IEEE80211_BAPS_BUFSIZ
)
1663 ic
->ic_send_action(ni
, IEEE80211_ACTION_CAT_BA
,
1664 IEEE80211_ACTION_BA_ADDBA_RESPONSE
, args
);
1669 ht_recv_action_ba_addba_response(struct ieee80211_node
*ni
,
1670 const struct ieee80211_frame
*wh
,
1671 const uint8_t *frm
, const uint8_t *efrm
)
1673 struct ieee80211com
*ic
= ni
->ni_ic
;
1674 struct ieee80211vap
*vap
= ni
->ni_vap
;
1675 struct ieee80211_tx_ampdu
*tap
;
1676 uint8_t dialogtoken
, policy
;
1677 uint16_t baparamset
, batimeout
, code
;
1678 int tid
, ac
, bufsiz
;
1680 dialogtoken
= frm
[2];
1681 code
= LE_READ_2(frm
+3);
1682 baparamset
= LE_READ_2(frm
+5);
1683 tid
= MS(baparamset
, IEEE80211_BAPS_TID
);
1684 bufsiz
= MS(baparamset
, IEEE80211_BAPS_BUFSIZ
);
1685 policy
= MS(baparamset
, IEEE80211_BAPS_POLICY
);
1686 batimeout
= LE_READ_2(frm
+7);
1688 ac
= TID_TO_WME_AC(tid
);
1689 tap
= &ni
->ni_tx_ampdu
[ac
];
1690 if ((tap
->txa_flags
& IEEE80211_AGGR_XCHGPEND
) == 0) {
1691 IEEE80211_DISCARD_MAC(vap
,
1692 IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1693 ni
->ni_macaddr
, "ADDBA response",
1694 "no pending ADDBA, tid %d dialogtoken %u "
1695 "code %d", tid
, dialogtoken
, code
);
1696 vap
->iv_stats
.is_addba_norequest
++;
1699 if (dialogtoken
!= tap
->txa_token
) {
1700 IEEE80211_DISCARD_MAC(vap
,
1701 IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1702 ni
->ni_macaddr
, "ADDBA response",
1703 "dialogtoken mismatch: waiting for %d, "
1704 "received %d, tid %d code %d",
1705 tap
->txa_token
, dialogtoken
, tid
, code
);
1706 vap
->iv_stats
.is_addba_badtoken
++;
1709 /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
1710 if (policy
!= (tap
->txa_flags
& IEEE80211_AGGR_IMMEDIATE
)) {
1711 IEEE80211_DISCARD_MAC(vap
,
1712 IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1713 ni
->ni_macaddr
, "ADDBA response",
1714 "policy mismatch: expecting %d, "
1715 "received %d, tid %d code %d",
1716 tap
->txa_flags
& IEEE80211_AGGR_IMMEDIATE
,
1718 vap
->iv_stats
.is_addba_badpolicy
++;
1722 /* XXX we take MIN in ieee80211_addba_response */
1723 if (bufsiz
> IEEE80211_AGGR_BAWMAX
) {
1724 IEEE80211_DISCARD_MAC(vap
,
1725 IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1726 ni
->ni_macaddr
, "ADDBA response",
1727 "BA window too large: max %d, "
1728 "received %d, tid %d code %d",
1729 bufsiz
, IEEE80211_AGGR_BAWMAX
, tid
, code
);
1730 vap
->iv_stats
.is_addba_badbawinsize
++;
1734 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
1735 "recv ADDBA response: dialogtoken %u code %d "
1736 "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
1737 dialogtoken
, code
, baparamset
, tid
, bufsiz
,
1739 ic
->ic_addba_response(ni
, tap
, code
, baparamset
, batimeout
);
1744 ht_recv_action_ba_delba(struct ieee80211_node
*ni
,
1745 const struct ieee80211_frame
*wh
,
1746 const uint8_t *frm
, const uint8_t *efrm
)
1748 struct ieee80211com
*ic
= ni
->ni_ic
;
1749 struct ieee80211_rx_ampdu
*rap
;
1750 struct ieee80211_tx_ampdu
*tap
;
1751 uint16_t baparamset
, code
;
1754 baparamset
= LE_READ_2(frm
+2);
1755 code
= LE_READ_2(frm
+4);
1757 tid
= MS(baparamset
, IEEE80211_DELBAPS_TID
);
1759 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
1760 "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
1761 "code %d", baparamset
, tid
,
1762 MS(baparamset
, IEEE80211_DELBAPS_INIT
), code
);
1764 if ((baparamset
& IEEE80211_DELBAPS_INIT
) == 0) {
1765 ac
= TID_TO_WME_AC(tid
);
1766 tap
= &ni
->ni_tx_ampdu
[ac
];
1767 ic
->ic_addba_stop(ni
, tap
);
1769 rap
= &ni
->ni_rx_ampdu
[tid
];
1770 ic
->ic_ampdu_rx_stop(ni
, rap
);
1776 ht_recv_action_ht_txchwidth(struct ieee80211_node
*ni
,
1777 const struct ieee80211_frame
*wh
,
1778 const uint8_t *frm
, const uint8_t *efrm
)
1782 chw
= (frm
[2] == IEEE80211_A_HT_TXCHWIDTH_2040
) ? 40 : 20;
1784 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
1785 "%s: HT txchwidth, width %d%s",
1786 __func__
, chw
, ni
->ni_chw
!= chw
? "*" : "");
1787 if (chw
!= ni
->ni_chw
) {
1789 /* XXX notify on change */
1795 ht_recv_action_ht_mimopwrsave(struct ieee80211_node
*ni
,
1796 const struct ieee80211_frame
*wh
,
1797 const uint8_t *frm
, const uint8_t *efrm
)
1799 const struct ieee80211_action_ht_mimopowersave
*mps
=
1800 (const struct ieee80211_action_ht_mimopowersave
*) frm
;
1802 /* XXX check iv_htcaps */
1803 if (mps
->am_control
& IEEE80211_A_HT_MIMOPWRSAVE_ENA
)
1804 ni
->ni_flags
|= IEEE80211_NODE_MIMO_PS
;
1806 ni
->ni_flags
&= ~IEEE80211_NODE_MIMO_PS
;
1807 if (mps
->am_control
& IEEE80211_A_HT_MIMOPWRSAVE_MODE
)
1808 ni
->ni_flags
|= IEEE80211_NODE_MIMO_RTS
;
1810 ni
->ni_flags
&= ~IEEE80211_NODE_MIMO_RTS
;
1811 /* XXX notify on change */
1812 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
1813 "%s: HT MIMO PS (%s%s)", __func__
,
1814 (ni
->ni_flags
& IEEE80211_NODE_MIMO_PS
) ? "on" : "off",
1815 (ni
->ni_flags
& IEEE80211_NODE_MIMO_RTS
) ? "+rts" : ""
1821 * Transmit processing.
1825 * Check if A-MPDU should be requested/enabled for a stream.
1826 * We require a traffic rate above a per-AC threshold and we
1827 * also handle backoff from previous failed attempts.
1829 * Drivers may override this method to bring in information
1830 * such as link state conditions in making the decision.
1833 ieee80211_ampdu_enable(struct ieee80211_node
*ni
,
1834 struct ieee80211_tx_ampdu
*tap
)
1836 struct ieee80211vap
*vap
= ni
->ni_vap
;
1838 if (tap
->txa_avgpps
< vap
->iv_ampdu_mintraffic
[tap
->txa_ac
])
1840 /* XXX check rssi? */
1841 if (tap
->txa_attempts
>= ieee80211_addba_maxtries
&&
1842 ticks
< tap
->txa_nextrequest
) {
1844 * Don't retry too often; txa_nextrequest is set
1845 * to the minimum interval we'll retry after
1846 * ieee80211_addba_maxtries failed attempts are made.
1850 IEEE80211_NOTE(vap
, IEEE80211_MSG_11N
, ni
,
1851 "enable AMPDU on %s, avgpps %d pkts %d",
1852 ieee80211_wme_acnames
[tap
->txa_ac
], tap
->txa_avgpps
, tap
->txa_pkts
);
1857 * Request A-MPDU tx aggregation. Setup local state and
1858 * issue an ADDBA request. BA use will only happen after
1859 * the other end replies with ADDBA response.
1862 ieee80211_ampdu_request(struct ieee80211_node
*ni
,
1863 struct ieee80211_tx_ampdu
*tap
)
1865 struct ieee80211com
*ic
= ni
->ni_ic
;
1867 int tid
, dialogtoken
;
1868 static int tokens
= 0; /* XXX */
1871 if ((tap
->txa_flags
& IEEE80211_AGGR_SETUP
) == 0) {
1872 /* do deferred setup of state */
1873 ampdu_tx_setup(tap
);
1875 /* XXX hack for not doing proper locking */
1876 tap
->txa_flags
&= ~IEEE80211_AGGR_NAK
;
1878 dialogtoken
= (tokens
+1) % 63; /* XXX */
1879 tid
= WME_AC_TO_TID(tap
->txa_ac
);
1880 tap
->txa_start
= ni
->ni_txseqs
[tid
];
1882 args
[0] = dialogtoken
;
1883 args
[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
1884 | SM(tid
, IEEE80211_BAPS_TID
)
1885 | SM(IEEE80211_AGGR_BAWMAX
, IEEE80211_BAPS_BUFSIZ
)
1887 args
[2] = 0; /* batimeout */
1888 /* NB: do first so there's no race against reply */
1889 if (!ic
->ic_addba_request(ni
, tap
, dialogtoken
, args
[1], args
[2])) {
1890 /* unable to setup state, don't make request */
1891 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_11N
,
1892 ni
, "%s: could not setup BA stream for AC %d",
1893 __func__
, tap
->txa_ac
);
1894 /* defer next try so we don't slam the driver with requests */
1895 tap
->txa_attempts
= ieee80211_addba_maxtries
;
1896 /* NB: check in case driver wants to override */
1897 if (tap
->txa_nextrequest
<= ticks
)
1898 tap
->txa_nextrequest
= ticks
+ ieee80211_addba_backoff
;
1901 tokens
= dialogtoken
; /* allocate token */
1902 /* NB: after calling ic_addba_request so driver can set txa_start */
1903 args
[3] = SM(tap
->txa_start
, IEEE80211_BASEQ_START
)
1904 | SM(0, IEEE80211_BASEQ_FRAG
)
1906 return ic
->ic_send_action(ni
, IEEE80211_ACTION_CAT_BA
,
1907 IEEE80211_ACTION_BA_ADDBA_REQUEST
, args
);
1911 * Terminate an AMPDU tx stream. State is reclaimed
1912 * and the peer notified with a DelBA Action frame.
1915 ieee80211_ampdu_stop(struct ieee80211_node
*ni
, struct ieee80211_tx_ampdu
*tap
,
1918 struct ieee80211com
*ic
= ni
->ni_ic
;
1919 struct ieee80211vap
*vap
= ni
->ni_vap
;
1923 tap
->txa_flags
&= ~IEEE80211_AGGR_BARPEND
;
1924 if (IEEE80211_AMPDU_RUNNING(tap
)) {
1925 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1926 ni
, "%s: stop BA stream for AC %d (reason %d)",
1927 __func__
, tap
->txa_ac
, reason
);
1928 vap
->iv_stats
.is_ampdu_stop
++;
1930 ic
->ic_addba_stop(ni
, tap
);
1931 args
[0] = WME_AC_TO_TID(tap
->txa_ac
);
1932 args
[1] = IEEE80211_DELBAPS_INIT
;
1933 args
[2] = reason
; /* XXX reason code */
1934 ic
->ic_send_action(ni
, IEEE80211_ACTION_CAT_BA
,
1935 IEEE80211_ACTION_BA_DELBA
, args
);
1937 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
,
1938 ni
, "%s: BA stream for AC %d not running (reason %d)",
1939 __func__
, tap
->txa_ac
, reason
);
1940 vap
->iv_stats
.is_ampdu_stop_failed
++;
1945 bar_timeout_callout(void *arg
)
1947 struct ieee80211_tx_ampdu
*tap
= arg
;
1948 struct ieee80211_node
*ni
;
1950 wlan_serialize_enter();
1952 KASSERT((tap
->txa_flags
& IEEE80211_AGGR_XCHGPEND
) == 0,
1953 ("bar/addba collision, flags 0x%x", tap
->txa_flags
));
1955 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_11N
,
1956 ni
, "%s: tid %u flags 0x%x attempts %d", __func__
,
1957 tap
->txa_ac
, tap
->txa_flags
, tap
->txa_attempts
);
1959 /* guard against race with bar_tx_complete */
1960 if (tap
->txa_flags
& IEEE80211_AGGR_BARPEND
) {
1962 if (tap
->txa_attempts
>= ieee80211_bar_maxtries
)
1963 ieee80211_ampdu_stop(ni
, tap
, IEEE80211_REASON_TIMEOUT
);
1965 ieee80211_send_bar(ni
, tap
, tap
->txa_seqpending
);
1967 wlan_serialize_exit();
1971 bar_start_timer(struct ieee80211_tx_ampdu
*tap
)
1973 callout_reset(&tap
->txa_timer
, ieee80211_bar_timeout
,
1974 bar_timeout_callout
, tap
);
1978 bar_stop_timer(struct ieee80211_tx_ampdu
*tap
)
1980 callout_stop(&tap
->txa_timer
);
1984 bar_tx_complete(struct ieee80211_node
*ni
, void *arg
, int status
)
1986 struct ieee80211_tx_ampdu
*tap
= arg
;
1988 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_11N
,
1989 ni
, "%s: tid %u flags 0x%x pending %d status %d",
1990 __func__
, tap
->txa_ac
, tap
->txa_flags
,
1991 callout_pending(&tap
->txa_timer
), status
);
1994 if ((tap
->txa_flags
& IEEE80211_AGGR_BARPEND
) &&
1995 callout_pending(&tap
->txa_timer
)) {
1996 struct ieee80211com
*ic
= ni
->ni_ic
;
1998 if (status
) /* ACK'd */
1999 bar_stop_timer(tap
);
2000 ic
->ic_bar_response(ni
, tap
, status
);
2001 /* NB: just let timer expire so we pace requests */
2006 ieee80211_bar_response(struct ieee80211_node
*ni
,
2007 struct ieee80211_tx_ampdu
*tap
, int status
)
2010 if (status
!= 0) { /* got ACK */
2011 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_11N
,
2012 ni
, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
2014 IEEE80211_SEQ_ADD(tap
->txa_start
, tap
->txa_wnd
-1),
2015 tap
->txa_qframes
, tap
->txa_seqpending
,
2016 WME_AC_TO_TID(tap
->txa_ac
));
2018 /* NB: timer already stopped in bar_tx_complete */
2019 tap
->txa_start
= tap
->txa_seqpending
;
2020 tap
->txa_flags
&= ~IEEE80211_AGGR_BARPEND
;
2025 * Transmit a BAR frame to the specified node. The
2026 * BAR contents are drawn from the supplied aggregation
2027 * state associated with the node.
2029 * NB: we only handle immediate ACK w/ compressed bitmap.
2032 ieee80211_send_bar(struct ieee80211_node
*ni
,
2033 struct ieee80211_tx_ampdu
*tap
, ieee80211_seq seq
)
2035 #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
2036 struct ieee80211vap
*vap
= ni
->ni_vap
;
2037 struct ieee80211com
*ic
= ni
->ni_ic
;
2038 struct ieee80211_frame_bar
*bar
;
2040 uint16_t barctl
, barseqctl
;
2044 if ((tap
->txa_flags
& IEEE80211_AGGR_RUNNING
) == 0) {
2045 /* no ADDBA response, should not happen */
2050 bar_stop_timer(tap
);
2052 ieee80211_ref_node(ni
);
2054 m
= ieee80211_getmgtframe(&frm
, ic
->ic_headroom
, sizeof(*bar
));
2056 senderr(ENOMEM
, is_tx_nobuf
);
2058 if (!ieee80211_add_callback(m
, bar_tx_complete
, tap
)) {
2060 senderr(ENOMEM
, is_tx_nobuf
); /* XXX */
2064 bar
= mtod(m
, struct ieee80211_frame_bar
*);
2065 bar
->i_fc
[0] = IEEE80211_FC0_VERSION_0
|
2066 IEEE80211_FC0_TYPE_CTL
| IEEE80211_FC0_SUBTYPE_BAR
;
2068 IEEE80211_ADDR_COPY(bar
->i_ra
, ni
->ni_macaddr
);
2069 IEEE80211_ADDR_COPY(bar
->i_ta
, vap
->iv_myaddr
);
2071 tid
= WME_AC_TO_TID(tap
->txa_ac
);
2072 barctl
= (tap
->txa_flags
& IEEE80211_AGGR_IMMEDIATE
?
2073 0 : IEEE80211_BAR_NOACK
)
2074 | IEEE80211_BAR_COMP
2075 | SM(tid
, IEEE80211_BAR_TID
)
2077 barseqctl
= SM(seq
, IEEE80211_BAR_SEQ_START
);
2078 /* NB: known to have proper alignment */
2079 bar
->i_ctl
= htole16(barctl
);
2080 bar
->i_seq
= htole16(barseqctl
);
2081 m
->m_pkthdr
.len
= m
->m_len
= sizeof(struct ieee80211_frame_bar
);
2083 M_WME_SETAC(m
, WME_AC_VO
);
2085 IEEE80211_NODE_STAT(ni
, tx_mgmt
); /* XXX tx_ctl? */
2088 /* init/bump attempts counter */
2089 if ((tap
->txa_flags
& IEEE80211_AGGR_BARPEND
) == 0)
2090 tap
->txa_attempts
= 1;
2092 tap
->txa_attempts
++;
2093 tap
->txa_seqpending
= seq
;
2094 tap
->txa_flags
|= IEEE80211_AGGR_BARPEND
;
2096 IEEE80211_NOTE(vap
, IEEE80211_MSG_DEBUG
| IEEE80211_MSG_11N
,
2097 ni
, "send BAR: tid %u ctl 0x%x start %u (attempt %d)",
2098 tid
, barctl
, seq
, tap
->txa_attempts
);
2100 ret
= ic
->ic_raw_xmit(ni
, m
, NULL
);
2102 /* xmit failed, clear state flag */
2103 tap
->txa_flags
&= ~IEEE80211_AGGR_BARPEND
;
2106 /* XXX hack against tx complete happening before timer is started */
2107 if (tap
->txa_flags
& IEEE80211_AGGR_BARPEND
)
2108 bar_start_timer(tap
);
2111 ieee80211_free_node(ni
);
2117 ht_action_output(struct ieee80211_node
*ni
, struct mbuf
*m
)
2119 struct ieee80211_bpf_params params
;
2121 memset(¶ms
, 0, sizeof(params
));
2122 params
.ibp_pri
= WME_AC_VO
;
2123 params
.ibp_rate0
= ni
->ni_txparms
->mgmtrate
;
2124 /* NB: we know all frames are unicast */
2125 params
.ibp_try0
= ni
->ni_txparms
->maxretry
;
2126 params
.ibp_power
= ni
->ni_txpower
;
2127 return ieee80211_mgmt_output(ni
, m
, IEEE80211_FC0_SUBTYPE_ACTION
,
2131 #define ADDSHORT(frm, v) do { \
2132 frm[0] = (v) & 0xff; \
2133 frm[1] = (v) >> 8; \
2138 * Send an action management frame. The arguments are stuff
2139 * into a frame without inspection; the caller is assumed to
2140 * prepare them carefully (e.g. based on the aggregation state).
2143 ht_send_action_ba_addba(struct ieee80211_node
*ni
,
2144 int category
, int action
, void *arg0
)
2146 struct ieee80211vap
*vap
= ni
->ni_vap
;
2147 struct ieee80211com
*ic
= ni
->ni_ic
;
2148 uint16_t *args
= arg0
;
2152 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
2153 "send ADDBA %s: dialogtoken %d "
2154 "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
2155 (action
== IEEE80211_ACTION_BA_ADDBA_REQUEST
) ?
2156 "request" : "response",
2157 args
[0], args
[1], MS(args
[1], IEEE80211_BAPS_TID
),
2160 IEEE80211_DPRINTF(vap
, IEEE80211_MSG_NODE
,
2161 "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", __func__
, __LINE__
,
2162 ni
, ni
->ni_macaddr
, ":", ieee80211_node_refcnt(ni
)+1);
2163 ieee80211_ref_node(ni
);
2165 m
= ieee80211_getmgtframe(&frm
,
2166 ic
->ic_headroom
+ sizeof(struct ieee80211_frame
),
2167 sizeof(uint16_t) /* action+category */
2168 /* XXX may action payload */
2169 + sizeof(struct ieee80211_action_ba_addbaresponse
)
2174 *frm
++ = args
[0]; /* dialog token */
2175 ADDSHORT(frm
, args
[1]); /* baparamset */
2176 ADDSHORT(frm
, args
[2]); /* batimeout */
2177 if (action
== IEEE80211_ACTION_BA_ADDBA_REQUEST
)
2178 ADDSHORT(frm
, args
[3]); /* baseqctl */
2179 m
->m_pkthdr
.len
= m
->m_len
= frm
- mtod(m
, uint8_t *);
2180 return ht_action_output(ni
, m
);
2182 vap
->iv_stats
.is_tx_nobuf
++;
2183 ieee80211_free_node(ni
);
2189 ht_send_action_ba_delba(struct ieee80211_node
*ni
,
2190 int category
, int action
, void *arg0
)
2192 struct ieee80211vap
*vap
= ni
->ni_vap
;
2193 struct ieee80211com
*ic
= ni
->ni_ic
;
2194 uint16_t *args
= arg0
;
2196 uint16_t baparamset
;
2199 baparamset
= SM(args
[0], IEEE80211_DELBAPS_TID
)
2202 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
2203 "send DELBA action: tid %d, initiator %d reason %d",
2204 args
[0], args
[1], args
[2]);
2206 IEEE80211_DPRINTF(vap
, IEEE80211_MSG_NODE
,
2207 "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", __func__
, __LINE__
,
2208 ni
, ni
->ni_macaddr
, ":", ieee80211_node_refcnt(ni
)+1);
2209 ieee80211_ref_node(ni
);
2211 m
= ieee80211_getmgtframe(&frm
,
2212 ic
->ic_headroom
+ sizeof(struct ieee80211_frame
),
2213 sizeof(uint16_t) /* action+category */
2214 /* XXX may action payload */
2215 + sizeof(struct ieee80211_action_ba_addbaresponse
)
2220 ADDSHORT(frm
, baparamset
);
2221 ADDSHORT(frm
, args
[2]); /* reason code */
2222 m
->m_pkthdr
.len
= m
->m_len
= frm
- mtod(m
, uint8_t *);
2223 return ht_action_output(ni
, m
);
2225 vap
->iv_stats
.is_tx_nobuf
++;
2226 ieee80211_free_node(ni
);
2232 ht_send_action_ht_txchwidth(struct ieee80211_node
*ni
,
2233 int category
, int action
, void *arg0
)
2235 struct ieee80211vap
*vap
= ni
->ni_vap
;
2236 struct ieee80211com
*ic
= ni
->ni_ic
;
2240 IEEE80211_NOTE(vap
, IEEE80211_MSG_ACTION
| IEEE80211_MSG_11N
, ni
,
2241 "send HT txchwidth: width %d",
2242 IEEE80211_IS_CHAN_HT40(ni
->ni_chan
) ? 40 : 20);
2244 IEEE80211_DPRINTF(vap
, IEEE80211_MSG_NODE
,
2245 "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", __func__
, __LINE__
,
2246 ni
, ni
->ni_macaddr
, ":", ieee80211_node_refcnt(ni
)+1);
2247 ieee80211_ref_node(ni
);
2249 m
= ieee80211_getmgtframe(&frm
,
2250 ic
->ic_headroom
+ sizeof(struct ieee80211_frame
),
2251 sizeof(uint16_t) /* action+category */
2252 /* XXX may action payload */
2253 + sizeof(struct ieee80211_action_ba_addbaresponse
)
2258 *frm
++ = IEEE80211_IS_CHAN_HT40(ni
->ni_chan
) ?
2259 IEEE80211_A_HT_TXCHWIDTH_2040
:
2260 IEEE80211_A_HT_TXCHWIDTH_20
;
2261 m
->m_pkthdr
.len
= m
->m_len
= frm
- mtod(m
, uint8_t *);
2262 return ht_action_output(ni
, m
);
2264 vap
->iv_stats
.is_tx_nobuf
++;
2265 ieee80211_free_node(ni
);
2272 * Construct the MCS bit mask for inclusion
2273 * in an HT information element.
2276 ieee80211_set_htrates(uint8_t *frm
, const struct ieee80211_htrateset
*rs
)
2280 for (i
= 0; i
< rs
->rs_nrates
; i
++) {
2281 int r
= rs
->rs_rates
[i
] & IEEE80211_RATE_VAL
;
2282 if (r
< IEEE80211_HTRATE_MAXSIZE
) { /* XXX? */
2283 /* NB: this assumes a particular implementation */
2290 * Add body of an HTCAP information element.
2293 ieee80211_add_htcap_body(uint8_t *frm
, struct ieee80211_node
*ni
)
2295 #define ADDSHORT(frm, v) do { \
2296 frm[0] = (v) & 0xff; \
2297 frm[1] = (v) >> 8; \
2300 struct ieee80211vap
*vap
= ni
->ni_vap
;
2304 /* HT capabilities */
2305 caps
= vap
->iv_htcaps
& 0xffff;
2307 * Note channel width depends on whether we are operating as
2308 * a sta or not. When operating as a sta we are generating
2309 * a request based on our desired configuration. Otherwise
2310 * we are operational and the channel attributes identify
2311 * how we've been setup (which might be different if a fixed
2312 * channel is specified).
2314 if (vap
->iv_opmode
== IEEE80211_M_STA
) {
2315 /* override 20/40 use based on config */
2316 if (vap
->iv_flags_ht
& IEEE80211_FHT_USEHT40
)
2317 caps
|= IEEE80211_HTCAP_CHWIDTH40
;
2319 caps
&= ~IEEE80211_HTCAP_CHWIDTH40
;
2320 /* use advertised setting (XXX locally constraint) */
2321 rxmax
= MS(ni
->ni_htparam
, IEEE80211_HTCAP_MAXRXAMPDU
);
2322 density
= MS(ni
->ni_htparam
, IEEE80211_HTCAP_MPDUDENSITY
);
2324 /* override 20/40 use based on current channel */
2325 if (IEEE80211_IS_CHAN_HT40(ni
->ni_chan
))
2326 caps
|= IEEE80211_HTCAP_CHWIDTH40
;
2328 caps
&= ~IEEE80211_HTCAP_CHWIDTH40
;
2329 rxmax
= vap
->iv_ampdu_rxmax
;
2330 density
= vap
->iv_ampdu_density
;
2332 /* adjust short GI based on channel and config */
2333 if ((vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI20
) == 0)
2334 caps
&= ~IEEE80211_HTCAP_SHORTGI20
;
2335 if ((vap
->iv_flags_ht
& IEEE80211_FHT_SHORTGI40
) == 0 ||
2336 (caps
& IEEE80211_HTCAP_CHWIDTH40
) == 0)
2337 caps
&= ~IEEE80211_HTCAP_SHORTGI40
;
2338 ADDSHORT(frm
, caps
);
2341 *frm
= SM(rxmax
, IEEE80211_HTCAP_MAXRXAMPDU
)
2342 | SM(density
, IEEE80211_HTCAP_MPDUDENSITY
)
2346 /* pre-zero remainder of ie */
2347 memset(frm
, 0, sizeof(struct ieee80211_ie_htcap
) -
2348 __offsetof(struct ieee80211_ie_htcap
, hc_mcsset
));
2350 /* supported MCS set */
2352 * XXX it would better to get the rate set from ni_htrates
2353 * so we can restrict it but for sta mode ni_htrates isn't
2354 * setup when we're called to form an AssocReq frame so for
2355 * now we're restricted to the default HT rate set.
2357 ieee80211_set_htrates(frm
, &ieee80211_rateset_11n
);
2359 frm
+= sizeof(struct ieee80211_ie_htcap
) -
2360 __offsetof(struct ieee80211_ie_htcap
, hc_mcsset
);
2366 * Add 802.11n HT capabilities information element
2369 ieee80211_add_htcap(uint8_t *frm
, struct ieee80211_node
*ni
)
2371 frm
[0] = IEEE80211_ELEMID_HTCAP
;
2372 frm
[1] = sizeof(struct ieee80211_ie_htcap
) - 2;
2373 return ieee80211_add_htcap_body(frm
+ 2, ni
);
2377 * Add Broadcom OUI wrapped standard HTCAP ie; this is
2378 * used for compatibility w/ pre-draft implementations.
2381 ieee80211_add_htcap_vendor(uint8_t *frm
, struct ieee80211_node
*ni
)
2383 frm
[0] = IEEE80211_ELEMID_VENDOR
;
2384 frm
[1] = 4 + sizeof(struct ieee80211_ie_htcap
) - 2;
2385 frm
[2] = (BCM_OUI
>> 0) & 0xff;
2386 frm
[3] = (BCM_OUI
>> 8) & 0xff;
2387 frm
[4] = (BCM_OUI
>> 16) & 0xff;
2388 frm
[5] = BCM_OUI_HTCAP
;
2389 return ieee80211_add_htcap_body(frm
+ 6, ni
);
2393 * Construct the MCS bit mask of basic rates
2394 * for inclusion in an HT information element.
2397 ieee80211_set_basic_htrates(uint8_t *frm
, const struct ieee80211_htrateset
*rs
)
2401 for (i
= 0; i
< rs
->rs_nrates
; i
++) {
2402 int r
= rs
->rs_rates
[i
] & IEEE80211_RATE_VAL
;
2403 if ((rs
->rs_rates
[i
] & IEEE80211_RATE_BASIC
) &&
2404 r
< IEEE80211_HTRATE_MAXSIZE
) {
2405 /* NB: this assumes a particular implementation */
2412 * Update the HTINFO ie for a beacon frame.
2415 ieee80211_ht_update_beacon(struct ieee80211vap
*vap
,
2416 struct ieee80211_beacon_offsets
*bo
)
2418 #define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
2419 const struct ieee80211_channel
*bsschan
= vap
->iv_bss
->ni_chan
;
2420 struct ieee80211com
*ic
= vap
->iv_ic
;
2421 struct ieee80211_ie_htinfo
*ht
=
2422 (struct ieee80211_ie_htinfo
*) bo
->bo_htinfo
;
2424 /* XXX only update on channel change */
2425 ht
->hi_ctrlchannel
= ieee80211_chan2ieee(ic
, bsschan
);
2426 if (vap
->iv_flags_ht
& IEEE80211_FHT_RIFS
)
2427 ht
->hi_byte1
= IEEE80211_HTINFO_RIFSMODE_PERM
;
2429 ht
->hi_byte1
= IEEE80211_HTINFO_RIFSMODE_PROH
;
2430 if (IEEE80211_IS_CHAN_HT40U(bsschan
))
2431 ht
->hi_byte1
|= IEEE80211_HTINFO_2NDCHAN_ABOVE
;
2432 else if (IEEE80211_IS_CHAN_HT40D(bsschan
))
2433 ht
->hi_byte1
|= IEEE80211_HTINFO_2NDCHAN_BELOW
;
2435 ht
->hi_byte1
|= IEEE80211_HTINFO_2NDCHAN_NONE
;
2436 if (IEEE80211_IS_CHAN_HT40(bsschan
))
2437 ht
->hi_byte1
|= IEEE80211_HTINFO_TXWIDTH_2040
;
2439 /* protection mode */
2440 ht
->hi_byte2
= (ht
->hi_byte2
&~ PROTMODE
) | ic
->ic_curhtprotmode
;
2442 /* XXX propagate to vendor ie's */
2447 * Add body of an HTINFO information element.
2449 * NB: We don't use struct ieee80211_ie_htinfo because we can
2450 * be called to fillin both a standard ie and a compat ie that
2451 * has a vendor OUI at the front.
2454 ieee80211_add_htinfo_body(uint8_t *frm
, struct ieee80211_node
*ni
)
2456 struct ieee80211vap
*vap
= ni
->ni_vap
;
2457 struct ieee80211com
*ic
= ni
->ni_ic
;
2459 /* pre-zero remainder of ie */
2460 memset(frm
, 0, sizeof(struct ieee80211_ie_htinfo
) - 2);
2462 /* primary/control channel center */
2463 *frm
++ = ieee80211_chan2ieee(ic
, ni
->ni_chan
);
2465 if (vap
->iv_flags_ht
& IEEE80211_FHT_RIFS
)
2466 frm
[0] = IEEE80211_HTINFO_RIFSMODE_PERM
;
2468 frm
[0] = IEEE80211_HTINFO_RIFSMODE_PROH
;
2469 if (IEEE80211_IS_CHAN_HT40U(ni
->ni_chan
))
2470 frm
[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE
;
2471 else if (IEEE80211_IS_CHAN_HT40D(ni
->ni_chan
))
2472 frm
[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW
;
2474 frm
[0] |= IEEE80211_HTINFO_2NDCHAN_NONE
;
2475 if (IEEE80211_IS_CHAN_HT40(ni
->ni_chan
))
2476 frm
[0] |= IEEE80211_HTINFO_TXWIDTH_2040
;
2478 frm
[1] = ic
->ic_curhtprotmode
;
2483 ieee80211_set_basic_htrates(frm
, &ni
->ni_htrates
);
2484 frm
+= sizeof(struct ieee80211_ie_htinfo
) -
2485 __offsetof(struct ieee80211_ie_htinfo
, hi_basicmcsset
);
2490 * Add 802.11n HT information information element.
2493 ieee80211_add_htinfo(uint8_t *frm
, struct ieee80211_node
*ni
)
2495 frm
[0] = IEEE80211_ELEMID_HTINFO
;
2496 frm
[1] = sizeof(struct ieee80211_ie_htinfo
) - 2;
2497 return ieee80211_add_htinfo_body(frm
+ 2, ni
);
2501 * Add Broadcom OUI wrapped standard HTINFO ie; this is
2502 * used for compatibility w/ pre-draft implementations.
2505 ieee80211_add_htinfo_vendor(uint8_t *frm
, struct ieee80211_node
*ni
)
2507 frm
[0] = IEEE80211_ELEMID_VENDOR
;
2508 frm
[1] = 4 + sizeof(struct ieee80211_ie_htinfo
) - 2;
2509 frm
[2] = (BCM_OUI
>> 0) & 0xff;
2510 frm
[3] = (BCM_OUI
>> 8) & 0xff;
2511 frm
[4] = (BCM_OUI
>> 16) & 0xff;
2512 frm
[5] = BCM_OUI_HTINFO
;
2513 return ieee80211_add_htinfo_body(frm
+ 6, ni
);