e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Use is subject to license terms.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Copyright (c) 2007 Sam Leffler, Errno Consulting
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * All rights reserved.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Redistribution and use in source and binary forms, with or without
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * modification, are permitted provided that the following conditions
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * are met:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * 1. Redistributions of source code must retain the above copyright
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * notice, this list of conditions and the following disclaimer.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * 2. Redistributions in binary form must reproduce the above copyright
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * notice, this list of conditions and the following disclaimer in the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * documentation and/or other materials provided with the distribution.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * IEEE 802.11n protocol support.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#include <sys/mac_provider.h>
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#include <sys/strsun.h>
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#include <sys/byteorder.h>
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#include "net80211_impl.h"
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* define here, used throughout file */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define MS(_v, _f) (((_v) & _f) >> _f##_S)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define SM(_v, _f) (((_v) << _f##_S) & _f)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* need max array size */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* NB: these are for HT20 w/ long GI */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangconst int ieee80211_htrates[16] = {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 13, /* IFM_IEEE80211_MCS0 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 26, /* IFM_IEEE80211_MCS1 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 39, /* IFM_IEEE80211_MCS2 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 52, /* IFM_IEEE80211_MCS3 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 78, /* IFM_IEEE80211_MCS4 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 104, /* IFM_IEEE80211_MCS5 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 117, /* IFM_IEEE80211_MCS6 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 130, /* IFM_IEEE80211_MCS7 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 26, /* IFM_IEEE80211_MCS8 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 52, /* IFM_IEEE80211_MCS9 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 78, /* IFM_IEEE80211_MCS10 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 104, /* IFM_IEEE80211_MCS11 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 156, /* IFM_IEEE80211_MCS12 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 208, /* IFM_IEEE80211_MCS13 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 234, /* IFM_IEEE80211_MCS14 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 260, /* IFM_IEEE80211_MCS15 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang};
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstruct ieee80211_htrateset ieee80211_rateset_11n =
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang { 16, {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* 39 52 78 104 117, 130 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang 10, 11, 12, 13, 14, 15 }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang };
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define IEEE80211_AMPDU_AGE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define IEEE80211_AGGR_TIMEOUT 250 /* msecs */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define IEEE80211_AGGR_MINRETRY (10 * hz) /* ticks */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define IEEE80211_AGGR_MAXTRIES 3
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Receive processing.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Decap the encapsulated A-MSDU frames and dispatch all but
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the last for delivery. The last frame is returned for
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * delivery via the normal path.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define FF_LLC_SIZE \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (sizeof (struct ether_header) + sizeof (struct ieee80211_llc))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangmblk_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_decap_amsdu(struct ieee80211_node *in, mblk_t *mp)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ether_header *eh;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_frame *wh;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int framelen, hdrspace;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* all msdu has same ieee80211_frame header */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh = (struct ieee80211_frame *)mp->b_rptr;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang hdrspace = ieee80211_hdrspace(ic, wh);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mp->b_rptr += hdrspace; /* A-MSDU subframe follows */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (;;) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * The frame has an 802.3 header followed by an 802.2
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * LLC header. The encapsulated frame length is in the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * first header type field;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (MBLKL(mp) < FF_LLC_SIZE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_err("too short, decap failed\n");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto out;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Decap frames, encapsulate to 802.11 frame then deliver.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * 802.3 header is first (struct ether_header)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * 802.2 header follows (struct ieee80211_llc)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * data, msdu = llc + data
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang eh = (struct ether_header *)mp->b_rptr;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* 802.2 header follows */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang framelen = ntohs(eh->ether_type); /* llc + data */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m0 = allocb(hdrspace + framelen, BPRI_MED);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m0 == NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_err("decap_msdu(): can't alloc mblk\n");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto out;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memcpy(m0->b_wptr, (uint8_t *)wh, hdrspace);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m0->b_wptr += hdrspace;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memcpy(m0->b_wptr,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mp->b_rptr + sizeof (struct ether_header), framelen);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m0->b_wptr += framelen;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_stats.is_rx_frags++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_stats.is_rx_bytes += MBLKL(m0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_UNLOCK(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mac_rx(ic->ic_mach, NULL, m0); /* deliver to mac */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_LOCK(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang framelen += sizeof (struct ether_header);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (MBLKL(mp) == framelen) /* last, no padding */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto out;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Remove frame contents; each intermediate frame
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * is required to be aligned to a 4-byte boundary.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mp->b_rptr += roundup(framelen, 4); /* padding */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangout:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang freemsg(mp);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (NULL); /* none delivered by caller */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef FF_LLC_SIZE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Start A-MPDU rx/re-order processing for the specified TID.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memset(rap, 0, sizeof (*rap));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = (uint16_t)start;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Purge all frames in the A-MPDU re-order queue.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rap->rxa_wnd; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = rap->rxa_m[i];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[i] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qbytes -= MBLKL(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang freemsg(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (--rap->rxa_qframes == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Stop A-MPDU rx processing for the specified TID.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_purge(rap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Dispatch a frame from the A-MPDU reorder queue. The
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * frame is fed back into ieee80211_input marked with an
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * M_AMPDU flag so it doesn't come back to us (it also
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * permits ieee80211_input to optimize re-processing).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_dispatch(struct ieee80211_node *in, mblk_t *m)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m->b_flag |= M_AMPDU; /* bypass normal processing */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: rssi and rstamp are ignored w/ M_AMPDU set */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) ieee80211_input(in->in_ic, m, in, 0, 0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Dispatch as many frames as possible from the re-order queue.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Frames will always be "at the front"; we process all frames
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * up to the first empty slot in the window. On completion we
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * cleanup state if there are still pending frames in the current
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * BA window. We assume the frame at slot 0 is already handled
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * by the caller; we always start at slot 1.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* flush run of frames */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 1; i < rap->rxa_wnd; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = rap->rxa_m[i];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[i] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qbytes -= MBLKL(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes--;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_dispatch(in, m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * If frames remain, copy the mbuf pointers down so
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * they correspond to the offsets in the new window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int n = rap->rxa_qframes, j;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (j = i+1; j < rap->rxa_wnd; j++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_m[j] != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[j-i] = rap->rxa_m[j];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[j] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (--n == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(n == 0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Adjust the start of the BA window to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * reflect the frames just dispatched.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#ifdef IEEE80211_AMPDU_AGE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Dispatch all frames in the A-MPDU re-order queue.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_flush(struct ieee80211_node *in, struct ieee80211_rx_ampdu *rap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "ampdu_rx_flush(%d)\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_wnd);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rap->rxa_wnd; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = rap->rxa_m[i];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang continue;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[i] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qbytes -= MBLKL(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes--;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_dispatch(in, m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#endif /* IEEE80211_AMPDU_AGE */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Dispatch all frames in the A-MPDU re-order queue
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * preceding the specified sequence number. This logic
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * handles window moves due to a received MSDU or BAR.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangampdu_rx_flush_upto(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_seq seqno;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Flush any complete MSDU's with a sequence number lower
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * than winstart. Gaps may exist. Note that we may actually
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * dispatch frames past winstart if a run continues; this is
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * an optimization that avoids having to do a separate pass
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * to dispatch frames after moving the BA window start.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang seqno = rap->rxa_start;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rap->rxa_wnd; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = rap->rxa_m[i];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[i] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qbytes -= MBLKL(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes--;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_dispatch(in, m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang seqno = IEEE80211_SEQ_INC(seqno);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * If frames remain, copy the mbuf pointers down so
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * they correspond to the offsets in the new window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int n = rap->rxa_qframes, j;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (j = i+1; j < rap->rxa_wnd; j++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_m[j] != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[j-i] = rap->rxa_m[j];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[j] = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (--n == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (n != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "ampdu_rx_flush_upto(): "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "lost %d frames, qframes %d off %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BA win <%d:%d> winstart %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang n, rap->rxa_qframes, i, rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang winstart);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Move the start of the BA window; we use the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * sequence number of the last MSDU that was
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * passed up the stack+1 or winstart if stopped on
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * a gap in the reorder buffer.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = seqno;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process a received QoS data frame for an HT station. Handle
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * A-MPDU reordering: if this frame is received out of order
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * and falls within the BA window hold onto it. Otherwise if
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * this frame completes a run, flush any pending frames. We
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * return 1 if the frame is consumed. A 0 is returned if
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the frame should be processed normally by the caller.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangint
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ampdu_reorder(struct ieee80211_node *in, mblk_t *m)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define IEEE80211_FC0_QOSDATA \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS | \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_FC0_VERSION_0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define PROCESS 0 /* caller should process frame */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define CONSUMED 1 /* frame consumed, caller does nothing */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_qosframe *wh;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_rx_ampdu *rap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_seq rxseq;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint8_t tid;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int off;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(in->in_flags & IEEE80211_NODE_HT);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: m_len known to be sufficient */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh = (struct ieee80211_qosframe *)m->b_rptr;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = wh->i_qos[0];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid &= IEEE80211_QOS_TID;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap = &in->in_rx_ampdu[tid];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * No ADDBA request yet, don't touch.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (PROCESS);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxseq = LE_16(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_nframes++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangagain:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rxseq == rap->rxa_start) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * First frame in window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Dispatch as many packets as we can.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(rap->rxa_m[0] == NULL); /* [0] is m */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_dispatch(in, m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_dispatch(rap, in);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "ieee80211_ampdu_reorder(%u), CONSUMED ...\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (CONSUMED);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * In order; advance window and notify
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * caller to dispatch directly.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "ieee80211_ampdu_reorder(%u), PROCESS ...\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (PROCESS);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "ieee80211_ampdu_reorder(%u, %u), out of order ...\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxseq, rap->rxa_start);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Frame is out of order; store if in the BA window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* calculate offset in BA window */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (off < rap->rxa_wnd) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#ifdef IEEE80211_AMPDU_AGE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Common case (hopefully): in the BA window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * --
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Check for frames sitting too long in the reorder queue.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * This should only ever happen if frames are not delivered
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * without the sender otherwise notifying us (e.g. with a
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * BAR to move the window). Typically this happens because
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * of vendor bugs that cause the sequence number to jump.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * When this happens we get a gap in the reorder queue that
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * leaves frame sitting on the queue until they get pushed
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * out due to window moves. When the vendor does not send
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * BAR this move only happens due to explicit packet sends
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * NB: we only track the time of the oldest frame in the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * reorder q; this means that if we flush we might push
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * frames that still "new"; if this happens then subsequent
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * frames will result in BA window moves which cost something
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * but is still better than a big throughput dip.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang clock_t ticks;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ticks = ddi_get_lbolt();
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* honor batimeout? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ticks - rap->rxa_age > drv_usectohz(500*1000)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Too long since we received the first
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * frame; flush the reorder buffer.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_qframes != 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_flush(in, rap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (PROCESS);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * First frame, start aging timer.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_age = ticks;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#endif /* IEEE80211_AMPDU_AGE */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* save packet */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rap->rxa_m[off] == NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_m[off] = m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qbytes += MBLKL(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "a-mpdu duplicate "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "seqno %u tid %u BA win <%u:%u>\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxseq, tid, rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_wnd - 1));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang freemsg(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (CONSUMED);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (off < IEEE80211_SEQ_BA_RANGE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Outside the BA window, but within range;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * flush the reorder q and move the window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Sec 9.10.7.6 b) (D2.04 p.118 line 60)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "move BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd - 1),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes, rxseq, tid);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * The spec says to flush frames up to but not including:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * WinStart_B = rxseq - rap->rxa_wnd + 1
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Then insert the frame or notify the caller to process
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * it immediately. We can safely do this by just starting
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * over again because we know the frame will now be within
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the BA window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: rxa_wnd known to be >0 */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_flush_upto(in, rap,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto again;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Outside the BA window and out of range; toss.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT, "MSDU"
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes, rxseq, tid,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang freemsg(m);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (CONSUMED);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef CONSUMED
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef PROCESS
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef IEEE80211_FC0_QOSDATA
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process a BAR ctl frame. Dispatch all frames up to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the sequence number of the frame. If this frame is
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * out of range it's discarded.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_recv_bar(struct ieee80211_node *in, mblk_t *m0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_frame_bar *wh;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_rx_ampdu *rap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_seq rxseq;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int tid, off;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh = (struct ieee80211_frame_bar *)m0->b_rptr;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* check basic BAR */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = MS(LE_16(wh->i_ctl), IEEE80211_BAR_TID);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap = &in->in_rx_ampdu[tid];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * No ADDBA request yet, don't touch.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BAR no BA stream, tid %u\n", tid);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxseq = LE_16(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rxseq == rap->rxa_start)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* calculate offset in BA window */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (off < IEEE80211_SEQ_BA_RANGE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Flush the reorder q up to rxseq and move the window.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes, rxseq, tid);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_flush_upto(in, rap, rxseq);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (off >= rap->rxa_wnd) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * BAR specifies a window start to the right of BA
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * window; we must move it explicitly since
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * ampdu_rx_flush_upto will not.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start = rxseq;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Out of range; toss.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT, "BAR "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_start,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap->rxa_qframes, rxseq, tid,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Setup HT-specific state in a node. Called only
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * when HT use is negotiated so we don't do extra
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * work for temporary and/or legacy sta's.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_node_init(struct ieee80211_node *in, const uint8_t *htcap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int ac;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_flags & IEEE80211_NODE_HT) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Clean AMPDU state on re-associate. This handles the case
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * where a station leaves w/o notifying us and then returns
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * before node is reaped for inactivity.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_ht_node_cleanup(in);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_parse_htcap(in, htcap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (ac = 0; ac < WME_NUM_AC; ac++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap = &in->in_tx_ampdu[ac];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_ac = (uint8_t)ac;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: further initialization deferred */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Cleanup HT-specific state in a node. Called only
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * when HT use has been marked.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_node_cleanup(struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(in->in_flags & IEEE80211_NODE_HT);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* optimize this */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < WME_NUM_AC; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[i];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (tap->txa_flags & IEEE80211_AGGR_SETUP) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Stop BA stream if setup so driver has a chance
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * to reclaim any resources it might have allocated.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_stop(in, &in->in_tx_ampdu[i]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* IEEE80211_TAPQ_DESTROY(tap); */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: clearing NAK means we may re-send ADDBA */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags &=
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < WME_NUM_TID; i++)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_stop(&in->in_rx_ampdu[i]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT |
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_NODE_AMPDU);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic struct ieee80211_channel *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangfindhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return ieee80211_find_channel(ic, c->ich_freq,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (c->ich_flags &~ IEEE80211_CHAN_HT) | htflags);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Adjust a channel to be HT/non-HT according to the vap's configuration.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstruct ieee80211_channel *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_adjust_channel(struct ieee80211com *ic,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_channel *chan, int flags)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_channel *c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (flags & IEEE80211_FEXT_HT) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* promote to HT if possible */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (flags & IEEE80211_FEXT_USEHT40) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (!IEEE80211_IS_CHAN_HT40(chan)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: arbitrarily pick ht40+ over ht40- */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = findhtchan(ic, chan,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_CHAN_HT40D);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = findhtchan(ic, chan,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_CHAN_HT20);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c != NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chan = c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else if (!IEEE80211_IS_CHAN_HT20(chan)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c != NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chan = c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else if (IEEE80211_IS_CHAN_HT(chan)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* demote to legacy, HT use is disabled */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = ieee80211_find_channel(ic, chan->ich_freq,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chan->ich_flags &~ IEEE80211_CHAN_HT);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c != NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chan = c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (chan);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Setup HT-specific state for a legacy WDS peer.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_wds_init(struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int ac;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* check scan cache in case peer has an ap and we have info */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * If setup with a legacy channel; locate an HT channel.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Otherwise if the inherited channel (from a companion
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * AP) is suitable use it so we use the same location
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * for the extension channel).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chan = ieee80211_ht_adjust_channel(ic, in->in_chan,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap |= IEEE80211_HTCAP_SHORTGI20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40(in->in_chan)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap |= IEEE80211_HTCAP_CHWIDTH40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chw = 40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40U(in->in_chan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else if (IEEE80211_IS_CHAN_HT40D(in->in_chan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap |= IEEE80211_HTCAP_SHORTGI40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chw = 20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htctlchan = ieee80211_chan2ieee(ic, in->in_chan);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htopmode = 0; /* need protection state */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htstbc = 0; /* need info */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (ac = 0; ac < WME_NUM_AC; ac++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap = &in->in_tx_ampdu[ac];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_ac = (uint8_t)ac;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Notify hostap vaps of a change in the HTINFO ie.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanghtinfo_notify(struct ieee80211com *ic)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_opmode != IEEE80211_M_HOSTAP)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "HT bss occupancy change: %d sta, %d ht, "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "%d ht40%s, HT protmode now 0x%x\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sta_assoc,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht_sta_assoc,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht40_sta_assoc,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ", non-HT sta present" : "",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_curhtprotmode);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Calculate HT protection mode from current
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * state and handle updates.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanghtinfo_update(struct ieee80211com *ic)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint8_t protmode;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | IEEE80211_HTINFO_NONHT_PRESENT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang protmode = IEEE80211_HTINFO_OPMODE_MIXED
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | IEEE80211_HTINFO_NONHT_PRESENT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang protmode = IEEE80211_HTINFO_OPMODE_PURE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (protmode != ic->ic_curhtprotmode) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_curhtprotmode = protmode;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo_notify(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Handle an HT station joining a BSS.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_node_join(struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_LOCK_ASSERT(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_flags & IEEE80211_NODE_HT) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht_sta_assoc++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_chw == 40)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht40_sta_assoc++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo_update(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Handle an HT station leaving a BSS.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_node_leave(struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_LOCK_ASSERT(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_flags & IEEE80211_NODE_HT) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht_sta_assoc--;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_chw == 40)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ht40_sta_assoc--;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo_update(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Public version of htinfo_update; used for processing
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * beacon frames from overlapping bss in hostap_recv_mgmt.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (protmode != ic->ic_curhtprotmode) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_curhtprotmode = (uint8_t)protmode;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo_notify(ic);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* unalligned little endian access */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define LE_READ_2(p) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ((uint16_t) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ((((const uint8_t *)(p))[0]) | \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (((const uint8_t *)(p))[1] << 8)))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process an 802.11n HT capabilities ie.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_parse_htcap(struct ieee80211_node *in, const uint8_t *ie)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ie[0] == IEEE80211_ELEMID_VENDOR) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Station used Vendor OUI ie to associate;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * mark the node so when we respond we'll use
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the Vendor OUI's and not the standard ie's.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags |= IEEE80211_NODE_HTCOMPAT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ie += 4;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags &= ~IEEE80211_NODE_HTCOMPAT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htcap = *(uint16_t *)(ie +
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang offsetof(struct ieee80211_ie_htcap, hc_cap));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htparam = ie[offsetof(struct ieee80211_ie_htcap, hc_param)];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* needed or will ieee80211_parse_htinfo always be called? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chw = (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process an 802.11n HT info ie and update the node state.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Note that we handle use this information to identify the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * correct channel (HT20, HT40+, HT40-, legacy). The caller
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * is responsible for insuring any required channel change is
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * done (e.g. in sta mode when parsing the contents of a
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * beacon frame).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_parse_htinfo(struct ieee80211_node *in, const uint8_t *ie)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_ie_htinfo *htinfo;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_channel *c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t w;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int htflags, chanflags;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ie[0] == IEEE80211_ELEMID_VENDOR)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ie += 4;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo = (const struct ieee80211_ie_htinfo *)ie;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htctlchan = htinfo->hi_ctrlchannel;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang w = *(uint16_t *)(&htinfo->hi_byte2);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang w = *(uint16_t *)(&htinfo->hi_byte45);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Handle 11n channel switch. Use the received HT ie's to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * identify the right channel to use. If we cannot locate it
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * in the channel table then fallback to legacy operation.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_CHAN_HT20 : 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: honor operating mode constraint */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htflags = IEEE80211_CHAN_HT40U;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htflags = IEEE80211_CHAN_HT40D;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chanflags = (in->in_chan->ich_flags &~ IEEE80211_CHAN_HT) | htflags;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (chanflags != in->in_chan->ich_flags) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = ieee80211_find_channel(ic,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chan->ich_freq, chanflags);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * No HT40 channel entry in our table; fall back
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * to HT20 operation. This should not happen.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c = findhtchan(ic, in->in_chan, IEEE80211_CHAN_HT20);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "no HT40 channel (freq %u), falling back to HT20\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chan->ich_freq);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* stat */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (c != NULL && c != in->in_chan) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "switch station to HT%d channel %u/0x%x\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang c->ich_freq, c->ich_flags);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chan = c;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: caller responsible for forcing any channel change */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* update node's tx channel width */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chw = IEEE80211_IS_CHAN_HT40(in->in_chan)? 40 : 20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Install received HT rate set by parsing the HT cap ie.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangint
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_setup_htrates(struct ieee80211_node *in, const uint8_t *ie, int flags)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_ie_htcap *htcap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_htrateset *rs;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rs = &in->in_htrates;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memset(rs, 0, sizeof (*rs));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ie != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ie[0] == IEEE80211_ELEMID_VENDOR)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ie += 4;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htcap = (const struct ieee80211_ie_htcap *) ie;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ieee80211_isclr(htcap->hc_mcsset, i))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang continue;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "WARNING, HT rate set too large; only "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "using %u rates\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_HTRATE_MAXSIZE);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rs->rs_rates[rs->rs_nrates++] = (uint8_t)i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ieee80211_fix_rate(in, (struct ieee80211_rateset *)rs, flags));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Mark rates in a node's HT rate set as basic according
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * to the information in the supplied HT info ie.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_setup_basic_htrates(struct ieee80211_node *in, const uint8_t *ie)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_ie_htinfo *htinfo;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_htrateset *rs;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i, j;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ie[0] == IEEE80211_ELEMID_VENDOR)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ie += 4;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang htinfo = (const struct ieee80211_ie_htinfo *) ie;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rs = &in->in_htrates;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (rs->rs_nrates == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "WARNING, empty HT rate set\n");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ieee80211_isclr(htinfo->hi_basicmcsset, i))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang continue;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (j = 0; j < rs->rs_nrates; j++)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangaddba_timeout(void *arg)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap = arg;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_attempts++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangaddba_start_timeout(struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_timer = timeout(addba_timeout, (void *)tap,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang drv_usectohz(IEEE80211_AGGR_TIMEOUT * 1000));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_lastrequest = ddi_get_lbolt();
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangaddba_stop_timeout(struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (tap->txa_timer != NULL) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) untimeout(tap->txa_timer);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_timer = NULL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Default method for requesting A-MPDU tx aggregation.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * We setup the specified state block and start a timer
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * to wait for an ADDBA response frame.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic int
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_addba_request(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int dialogtoken, int baparamset, int batimeout)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int bufsiz;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_token = (uint8_t)dialogtoken;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_start = tap->txa_seqstart = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang addba_start_timeout(tap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (1);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Default method for processing an A-MPDU tx aggregation
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * response. We shutdown any pending timer and update the
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * state block according to the reply.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic int
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_addba_response(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int status, int baparamset, int batimeout)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int bufsiz;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang addba_stop_timeout(tap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (status == IEEE80211_STATUS_SUCCESS) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* override our request? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags |= IEEE80211_AGGR_RUNNING;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* mark tid so we don't try again */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags |= IEEE80211_AGGR_NAK;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (1);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Default method for stopping A-MPDU tx aggregation.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Any timer is cleared and we drain any pending frames.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_addba_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang addba_stop_timeout(tap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* clear aggregation queue */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_attempts = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process a received action frame using the default aggregation
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * policy. We intercept ADDBA-related frames and use them to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * update our aggregation state. All other frames are passed up
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * for processing by ieee80211_recv_action.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_aggr_recv_action(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const uint8_t *frm, const uint8_t *efrm)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_action *ia;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_rx_ampdu *rap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint8_t dialogtoken;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t baparamset, batimeout, baseqctl, code;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t args[4];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int tid, ac, bufsiz;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ia = (const struct ieee80211_action *) frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (ia->ia_category) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_CAT_BA:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (ia->ia_action) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_ADDBA_REQUEST:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang dialogtoken = frm[2];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang baparamset = *(uint16_t *)(frm+3);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang batimeout = *(uint16_t *)(frm+5);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang baseqctl = *(uint16_t *)(frm+7);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = MS(baparamset, IEEE80211_BAPS_TID);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "recv ADDBA request: dialogtoken %u "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "baseqctl %d:%d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang dialogtoken, baparamset, tid, bufsiz, batimeout,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang MS(baseqctl, IEEE80211_BASEQ_START),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang MS(baseqctl, IEEE80211_BASEQ_FRAG));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap = &in->in_rx_ampdu[tid];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* Send ADDBA response */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0] = dialogtoken;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * NB: We ack only if the sta associated with HT and
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the ap is configured to do AMPDU rx (the latter
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * violates the 11n spec and is mostly for testing).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((in->in_flags & IEEE80211_NODE_AMPDU_RX) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_start(rap, bufsiz,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang MS(baseqctl, IEEE80211_BASEQ_START));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[1] = IEEE80211_STATUS_SUCCESS;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "reject ADDBA request: %s\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags & IEEE80211_NODE_AMPDU_RX ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "administratively disabled" :
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "not negotiated for station");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[1] = IEEE80211_STATUS_UNSPECIFIED;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* honor rap flags? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(tid, IEEE80211_BAPS_TID)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[3] = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang dialogtoken = frm[2];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang code = *(uint16_t *)(frm+3);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang baparamset = *(uint16_t *)(frm+5);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = MS(baparamset, IEEE80211_BAPS_TID);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang batimeout = *(uint16_t *)(frm+7);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ac = TID_TO_WME_AC(tid);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap = &in->in_tx_ampdu[ac];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_err("ADDBA response"
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "no pending ADDBA, tid %d dialogtoken %u "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "code %d\n", tid, dialogtoken, code);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (dialogtoken != tap->txa_token) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_err("ADDBA response"
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "dialogtoken mismatch: waiting for %d, "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "received %d, tid %d code %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_token, dialogtoken, tid, code);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "recv ADDBA response: dialogtoken %u code %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "baparamset 0x%x (tid %d bufsiz %d) batimeout %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang dialogtoken, code, baparamset, tid, bufsiz,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang batimeout);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_response(in, tap,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang code, baparamset, batimeout);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_DELBA:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang baparamset = *(uint16_t *)(frm+2);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang code = *(uint16_t *)(frm+4);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = MS(baparamset, IEEE80211_DELBAPS_TID);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "code %d\n", baparamset, tid,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang MS(baparamset, IEEE80211_DELBAPS_INIT), code);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ac = TID_TO_WME_AC(tid);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap = &in->in_tx_ampdu[ac];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_stop(in, tap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rap = &in->in_rx_ampdu[tid];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ampdu_rx_stop(rap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_recv_action(in, frm, efrm);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Process a received 802.11n action frame.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Aggregation-related frames are assumed to be handled
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * already; we handle any other frames we can, otherwise
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * complain about being unsupported (with debugging).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_recv_action(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const uint8_t *frm, const uint8_t *efrm)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_action *ia;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int chw;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ia = (const struct ieee80211_action *) frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (ia->ia_category) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_CAT_BA:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BA action %d not implemented\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ia->ia_action);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_CAT_HT:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (ia->ia_action) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_HT_TXCHWIDTH:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (chw != in->in_chw) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_chw = (uint8_t)chw;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags |= IEEE80211_NODE_CHWUPDATE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "HT txchwidth, width %d (%s)\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang chw,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang in->in_flags & IEEE80211_NODE_CHWUPDATE ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "new" : "no change");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_HT_MIMOPWRSAVE:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "HT MIMO PS\n");
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang default:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "HT action %d not implemented\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ia->ia_action);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang default:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "category %d not implemented\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ia->ia_category);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Transmit processing.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Request A-MPDU tx aggregation. Setup local state and
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * issue an ADDBA request. BA use will only happen after
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * the other end replies with ADDBA response.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangint
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ampdu_request(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t args[4];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int tid, dialogtoken;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang static int tokens = 0; /* tokens */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang clock_t ticks;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ticks = ddi_get_lbolt();
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* do deferred setup of state */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags |= IEEE80211_AGGR_SETUP;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Don't retry too often; IEEE80211_AGGR_MINRETRY
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * defines the minimum interval we'll retry after
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * IEEE80211_AGGR_MAXTRIES failed attempts to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * negotiate use.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* hack for not doing proper locking */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_flags &= ~IEEE80211_AGGR_NAK;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang dialogtoken = (tokens+1) % 63; /* algorithm */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = WME_AC_TO_TID(tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0] = (uint16_t)dialogtoken;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(tid, IEEE80211_BAPS_TID)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[2] = 0; /* batimeout */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[3] = SM(0, IEEE80211_BASEQ_START)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(0, IEEE80211_BASEQ_FRAG);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: do first so there's no race against reply */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (!ic->ic_addba_request(in, tap, dialogtoken, args[1], args[2])) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* unable to setup state, don't make request */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "could not setup BA stream for AC %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* defer next try so we don't slam the driver with requests */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_lastrequest = ticks;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tokens = dialogtoken; /* allocate token */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_ACTION_BA_ADDBA_REQUEST, args));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Terminate an AMPDU tx stream. State is reclaimed
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * and the peer notified with a DelBA Action frame.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ampdu_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t args[4];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_AMPDU_RUNNING(tap)) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "stop BA stream for AC %d\n", tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_stop(in, tap);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0] = WME_AC_TO_TID(tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[1] = IEEE80211_DELBAPS_INIT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[2] = 1; /* reason code */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) ieee80211_send_action(in, IEEE80211_ACTION_CAT_BA,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_ACTION_BA_DELBA, args);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "BA stream for AC %d not running\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Transmit a BAR frame to the specified node. The
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * BAR contents are drawn from the supplied aggregation
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * state associated with the node.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangint
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_send_bar(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_tx_ampdu *tap)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define ADDSHORT(frm, v) do { \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = (v) & 0xff; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = (v) >> 8; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += 2; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang} while (0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_frame_min *wh;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint8_t *frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t barctl, barseqctl;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int tid;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = ieee80211_getmgtframe(&frm, sizeof (struct ieee80211_ba_request));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ENOMEM);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh = (struct ieee80211_frame_min *)m->b_rptr;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang wh->i_fc[1] = 0;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_ADDR_COPY(wh->i_addr1, in->in_macaddr);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid = WME_AC_TO_TID(tap->txa_ac);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_BAPS_POLICY_IMMEDIATE :
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_BAPS_POLICY_DELAYED)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(tid, IEEE80211_BAPS_TID)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(0, IEEE80211_BASEQ_FRAG);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, barctl);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, barseqctl);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m->b_wptr = frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_DEBUG,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "send bar frame (tid %u start %u) on channel %u\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_CTL); /* MGT? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef ADDSHORT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Send an action management frame. The arguments are stuff
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * into a frame without inspection; the caller is assumed to
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * prepare them carefully (e.g. based on the aggregation state).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangint
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_send_action(struct ieee80211_node *in,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int category, int action, uint16_t args[4])
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define ADDSHORT(frm, v) do { \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = (v) & 0xff; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = (v) >> 8; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += 2; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang} while (0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang mblk_t *m;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint8_t *frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t baparamset;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int ret;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ASSERT(in != NULL);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m = ieee80211_getmgtframe(&frm,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang sizeof (uint16_t) /* action+category */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* may action payload */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang + sizeof (struct ieee80211_action_ba_addbaresponse));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (m == NULL)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ENOMEM);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = (uint8_t)category;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = (uint8_t)action;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (category) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_CAT_BA:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (action) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_ADDBA_REQUEST:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "send ADDBA request: dialogtoken %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "baparamset 0x%x (tid %d) "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "batimeout 0x%x baseqctl 0x%x\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[2], args[3]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = args[0]; /* dialog token */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[1]); /* baparamset */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[2]); /* batimeout */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[3]); /* baseqctl */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "send ADDBA response: dialogtoken %d status %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "baparamset 0x%x (tid %d) batimeout %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0], args[1], args[2],
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang MS(args[2], IEEE80211_BAPS_TID), args[3]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = args[0]; /* dialog token */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[1]); /* statuscode */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[2]); /* baparamset */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[3]); /* batimeout */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_BA_DELBA:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(args[1], IEEE80211_DELBAPS_INIT);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, baparamset);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, args[2]); /* reason code */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "send DELBA action: tid %d, initiator %d "
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "reason %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang args[0], args[1], args[2]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang default:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto badaction;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_CAT_HT:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang switch (action) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang case IEEE80211_ACTION_HT_TXCHWIDTH:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "send HT txchwidth: width %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ? 40 : 20);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ?
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_A_HT_TXCHWIDTH_2040 :
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang IEEE80211_A_HT_TXCHWIDTH_20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang default:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang goto badaction;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang break;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang default:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang badaction:
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang "unsupported category %d action %d\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang category, action);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (EINVAL);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NOTREACHED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang m->b_wptr = frm;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ret = ieee80211_mgmt_output(ic, in, m, IEEE80211_FC0_SUBTYPE_ACTION, 0);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ret);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef ADDSHORT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Construct the MCS bit mask for inclusion
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * in an HT information element.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rs->rs_nrates; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (r < IEEE80211_HTRATE_MAXSIZE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: this assumes a particular implementation */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_setbit(frm, r);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add body of an HTCAP information element.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic uint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define ADDSHORT(frm, v) do { \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = (v) & 0xff; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = (v) >> 8; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += 2; \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang _NOTE(CONSTCOND) \
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang} while (0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang uint16_t caps;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int rxmax, density;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* HT capabilities */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps = ic->ic_htcaps & 0xffff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Note channel width depends on whether we are operating as
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * a sta or not. When operating as a sta we are generating
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * a request based on our desired configuration. Otherwise
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * we are operational and the channel attributes identify
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * how we've been setup (which might be different if a fixed
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * channel is specified).
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_opmode == IEEE80211_M_STA) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* override 20/40 use based on config */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps |= IEEE80211_HTCAP_CHWIDTH40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps &= ~IEEE80211_HTCAP_CHWIDTH40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* use advertised setting (locally constraint) */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxmax = MS(in->in_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang density = MS(in->in_htparam, IEEE80211_HTCAP_MPDUDENSITY);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang } else {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* override 20/40 use based on current channel */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps |= IEEE80211_HTCAP_CHWIDTH40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps &= ~IEEE80211_HTCAP_CHWIDTH40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rxmax = ic->ic_ampdu_rxmax;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang density = ic->ic_ampdu_density;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* adjust short GI based on channel and config */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps &= ~IEEE80211_HTCAP_SHORTGI20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang caps &= ~IEEE80211_HTCAP_SHORTGI40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ADDSHORT(frm, caps);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* HT parameters */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang | SM(density, IEEE80211_HTCAP_MPDUDENSITY);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm++;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* pre-zero remainder of ie */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memset(frm, 0, sizeof (struct ieee80211_ie_htcap) -
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang offsetof(struct ieee80211_ie_htcap, hc_mcsset));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* supported MCS set */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * it would better to get the rate set from in_htrates
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * so we can restrict it but for sta mode in_htrates isn't
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * setup when we're called to form an AssocReq frame so for
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * now we're restricted to the default HT rate set.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_set_htrates(frm, &ieee80211_rateset_11n);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += sizeof (struct ieee80211_ie_htcap) -
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang offsetof(struct ieee80211_ie_htcap, hc_mcsset);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (frm);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef ADDSHORT
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add 802.11n HT capabilities information element
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanguint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = IEEE80211_ELEMID_HTCAP;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = sizeof (struct ieee80211_ie_htcap) - 2;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ieee80211_add_htcap_body(frm + 2, in));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add Broadcom OUI wrapped standard HTCAP ie; this is
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * used for compatibility w/ pre-draft implementations.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanguint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = IEEE80211_ELEMID_VENDOR;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = 4 + sizeof (struct ieee80211_ie_htcap) - 2;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[2] = (BCM_OUI >> 0) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[3] = (BCM_OUI >> 8) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[4] = (BCM_OUI >> 16) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[5] = BCM_OUI_HTCAP;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ieee80211_add_htcap_body(frm + 6, in));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Construct the MCS bit mask of basic rates
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * for inclusion in an HT information element.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rs->rs_nrates; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang r < IEEE80211_HTRATE_MAXSIZE) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: this assumes a particular implementation */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_setbit(frm, r);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Update the HTINFO ie for a beacon frame.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_update_beacon(struct ieee80211com *ic,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_beacon_offsets *bo)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211_ie_htinfo *ht =
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (struct ieee80211_ie_htinfo *)bo->bo_htinfo;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* only update on channel change */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_curchan);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else /* LINTED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* protection mode */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* propagate to vendor ie's */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef PROTMODE
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add body of an HTINFO information element.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * NB: We don't use struct ieee80211_ie_htinfo because we can
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * be called to fillin both a standard ie and a compat ie that
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * has a vendor OUI at the front.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic uint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang struct ieee80211com *ic = in->in_ic;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* pre-zero remainder of ie */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (void) memset(frm, 0, sizeof (struct ieee80211_ie_htinfo) - 2);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* primary/control channel center */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang else /* LINTED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = ic->ic_curhtprotmode;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += 5;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* basic MCS set */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_set_basic_htrates(frm, &in->in_htrates);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm += sizeof (struct ieee80211_ie_htinfo) -
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (frm);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add 802.11n HT information information element.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanguint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = IEEE80211_ELEMID_HTINFO;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = sizeof (struct ieee80211_ie_htinfo) - 2;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ieee80211_add_htinfo_body(frm + 2, in));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Add Broadcom OUI wrapped standard HTINFO ie; this is
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * used for compatibility w/ pre-draft implementations.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanguint8_t *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *in)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[0] = IEEE80211_ELEMID_VENDOR;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[1] = 4 + sizeof (struct ieee80211_ie_htinfo) - 2;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[2] = (BCM_OUI >> 0) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[3] = (BCM_OUI >> 8) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[4] = (BCM_OUI >> 16) & 0xff;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang frm[5] = BCM_OUI_HTINFO;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (ieee80211_add_htinfo_body(frm + 6, in));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_attach(struct ieee80211com *ic)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* setup default aggregation policy */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_recv_action = ieee80211_aggr_recv_action;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_send_action = ieee80211_send_action;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_request = ieee80211_addba_request;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_response = ieee80211_addba_response;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_addba_stop = ieee80211_addba_stop;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* get from driver */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTC_HT) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /*
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * Device is HT capable; enable all HT-related
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * facilities by default.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang * these choices may be too aggressive.
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_HT | IEEE80211_FEXT_HTCOMPAT;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* infer from channel list? */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#define ieee80211_isset16(a, i) ((a) & (1 << (i)))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang /* fill default rate sets for 11NA/11NG if driver has no specified */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NA) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11NA].ir_nrates == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11NA] =
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11A];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NG) &&
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11NG].ir_nrates == 0) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11NG] =
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ic->ic_sup_rates[IEEE80211_MODE_11G];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang#undef ieee80211_isset16
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_detach(struct ieee80211com *ic)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangstatic void
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fanght_announce(struct ieee80211com *ic, int mode,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_htrateset *rs)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang int i, rate;
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT, "%s MCS: \n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_phymode_name[mode]);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang for (i = 0; i < rs->rs_nrates; i++) {
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rate = ieee80211_htrates[rs->rs_rates[i]];
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ieee80211_dbg(IEEE80211_MSG_HT, "%s%d%sMbps\n",
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang (i != 0 ? " " : ""),
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang }
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangvoid
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_ht_announce(struct ieee80211com *ic)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NA))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NG))
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang/* ARGSUSED */
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangconst struct ieee80211_htrateset *
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fangieee80211_get_suphtrates(struct ieee80211com *ic,
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang const struct ieee80211_channel *c)
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang{
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang return (&ieee80211_rateset_11n);
e2cf88ac9d753a00c17aa235f6afdc76574fe3a6Quaker Fang}