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 * 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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 * 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 * 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 * 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 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 1864 ieee80211_ht_detach(struct ieee80211com *ic) 1865 { 1866 } 1867 1868 /* ARGSUSED */ 1869 static void 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 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 * 1896 ieee80211_get_suphtrates(struct ieee80211com *ic, 1897 const struct ieee80211_channel *c) 1898 { 1899 return (&ieee80211_rateset_11n); 1900 } 1901