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