net80211_ht.c revision e2cf88ac9d753a00c17aa235f6afdc76574fe3a6
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* IEEE 802.11n protocol support.
*/
#include <sys/mac_provider.h>
#include <sys/byteorder.h>
#include "net80211_impl.h"
/* define here, used throughout file */
/* need max array size */
/* NB: these are for HT20 w/ long GI */
const int ieee80211_htrates[16] = {
13, /* IFM_IEEE80211_MCS0 */
26, /* IFM_IEEE80211_MCS1 */
39, /* IFM_IEEE80211_MCS2 */
52, /* IFM_IEEE80211_MCS3 */
78, /* IFM_IEEE80211_MCS4 */
104, /* IFM_IEEE80211_MCS5 */
117, /* IFM_IEEE80211_MCS6 */
130, /* IFM_IEEE80211_MCS7 */
26, /* IFM_IEEE80211_MCS8 */
52, /* IFM_IEEE80211_MCS9 */
78, /* IFM_IEEE80211_MCS10 */
104, /* IFM_IEEE80211_MCS11 */
156, /* IFM_IEEE80211_MCS12 */
208, /* IFM_IEEE80211_MCS13 */
234, /* IFM_IEEE80211_MCS14 */
260, /* IFM_IEEE80211_MCS15 */
};
struct ieee80211_htrateset ieee80211_rateset_11n =
{ 16, {
/* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
/* 39 52 78 104 117, 130 */
10, 11, 12, 13, 14, 15 }
};
#define IEEE80211_AMPDU_AGE
#define IEEE80211_AGGR_MAXTRIES 3
/*
* Receive processing.
*/
/*
* Decap the encapsulated A-MSDU frames and dispatch all but
* the last for delivery. The last frame is returned for
* delivery via the normal path.
*/
#define FF_LLC_SIZE \
(sizeof (struct ether_header) + sizeof (struct ieee80211_llc))
mblk_t *
{
struct ether_header *eh;
struct ieee80211_frame *wh;
/* all msdu has same ieee80211_frame header */
for (;;) {
/*
* The frame has an 802.3 header followed by an 802.2
* LLC header. The encapsulated frame length is in the
* first header type field;
*/
ieee80211_err("too short, decap failed\n");
goto out;
}
/*
* Decap frames, encapsulate to 802.11 frame then deliver.
* 802.3 header is first (struct ether_header)
* 802.2 header follows (struct ieee80211_llc)
* data, msdu = llc + data
*/
/* 802.2 header follows */
ieee80211_err("decap_msdu(): can't alloc mblk\n");
goto out;
}
framelen += sizeof (struct ether_header);
goto out;
/*
* Remove frame contents; each intermediate frame
* is required to be aligned to a 4-byte boundary.
*/
}
out:
return (NULL); /* none delivered by caller */
}
/*
*/
static void
{
}
/*
* Purge all frames in the A-MPDU re-order queue.
*/
static void
{
mblk_t *m;
int i;
if (m != NULL) {
freemsg(m);
if (--rap->rxa_qframes == 0)
break;
}
}
}
/*
* Stop A-MPDU rx processing for the specified TID.
*/
static void
{
}
/*
* Dispatch a frame from the A-MPDU reorder queue. The
* frame is fed back into ieee80211_input marked with an
* M_AMPDU flag so it doesn't come back to us (it also
* permits ieee80211_input to optimize re-processing).
*/
static void
{
/* NB: rssi and rstamp are ignored w/ M_AMPDU set */
}
/*
* Dispatch as many frames as possible from the re-order queue.
* Frames will always be "at the front"; we process all frames
* up to the first empty slot in the window. On completion we
* cleanup state if there are still pending frames in the current
* BA window. We assume the frame at slot 0 is already handled
* by the caller; we always start at slot 1.
*/
static void
{
mblk_t *m;
int i;
/* flush run of frames */
if (m == NULL)
break;
rap->rxa_qframes--;
ampdu_dispatch(in, m);
}
/*
* If frames remain, copy the mbuf pointers down so
* they correspond to the offsets in the new window.
*/
if (rap->rxa_qframes != 0) {
int n = rap->rxa_qframes, j;
if (--n == 0)
break;
}
}
ASSERT(n == 0);
}
/*
* Adjust the start of the BA window to
* reflect the frames just dispatched.
*/
}
#ifdef IEEE80211_AMPDU_AGE
/*
* Dispatch all frames in the A-MPDU re-order queue.
*/
static void
{
mblk_t *m;
int i;
"ampdu_rx_flush(%d)\n",
if (m == NULL)
continue;
rap->rxa_qframes--;
ampdu_dispatch(in, m);
if (rap->rxa_qframes == 0)
break;
}
}
#endif /* IEEE80211_AMPDU_AGE */
/*
* Dispatch all frames in the A-MPDU re-order queue
* preceding the specified sequence number. This logic
* handles window moves due to a received MSDU or BAR.
*/
static void
{
mblk_t *m;
int i;
/*
* Flush any complete MSDU's with a sequence number lower
* than winstart. Gaps may exist. Note that we may actually
* dispatch frames past winstart if a run continues; this is
* an optimization that avoids having to do a separate pass
* to dispatch frames after moving the BA window start.
*/
if (m != NULL) {
rap->rxa_qframes--;
ampdu_dispatch(in, m);
} else {
break;
}
}
/*
* If frames remain, copy the mbuf pointers down so
* they correspond to the offsets in the new window.
*/
if (rap->rxa_qframes != 0) {
int n = rap->rxa_qframes, j;
if (--n == 0)
break;
}
}
if (n != 0) {
"ampdu_rx_flush_upto(): "
"lost %d frames, qframes %d off %d "
"BA win <%d:%d> winstart %d\n",
winstart);
}
}
/*
* Move the start of the BA window; we use the
* sequence number of the last MSDU that was
* passed up the stack+1 or winstart if stopped on
* a gap in the reorder buffer.
*/
}
/*
* Process a received QoS data frame for an HT station. Handle
* A-MPDU reordering: if this frame is received out of order
* and falls within the BA window hold onto it. Otherwise if
* this frame completes a run, flush any pending frames. We
* return 1 if the frame is consumed. A 0 is returned if
* the frame should be processed normally by the caller.
*/
int
{
#define IEEE80211_FC0_QOSDATA \
#define PROCESS 0 /* caller should process frame */
struct ieee80211_qosframe *wh;
struct ieee80211_rx_ampdu *rap;
int off;
/* NB: m_len known to be sufficient */
else
tid &= IEEE80211_QOS_TID;
/*
* No ADDBA request yet, don't touch.
*/
return (PROCESS);
}
rap->rxa_nframes++;
/*
* First frame in window.
*/
if (rap->rxa_qframes != 0) {
/*
* Dispatch as many packets as we can.
*/
ampdu_dispatch(in, m);
"ieee80211_ampdu_reorder(%u), CONSUMED ...\n",
rap->rxa_qframes);
return (CONSUMED);
} else {
/*
* In order; advance window and notify
* caller to dispatch directly.
*/
"ieee80211_ampdu_reorder(%u), PROCESS ...\n",
return (PROCESS);
}
}
"ieee80211_ampdu_reorder(%u, %u), out of order ...\n",
/*
* Frame is out of order; store if in the BA window.
*/
/* calculate offset in BA window */
#ifdef IEEE80211_AMPDU_AGE
/*
* Common case (hopefully): in the BA window.
* Sec 9.10.7.6 a) (D2.04 p.118 line 47)
* --
* Check for frames sitting too long in the reorder queue.
* This should only ever happen if frames are not delivered
* without the sender otherwise notifying us (e.g. with a
* BAR to move the window). Typically this happens because
* of vendor bugs that cause the sequence number to jump.
* When this happens we get a gap in the reorder queue that
* leaves frame sitting on the queue until they get pushed
* out due to window moves. When the vendor does not send
* BAR this move only happens due to explicit packet sends
*
* NB: we only track the time of the oldest frame in the
* reorder q; this means that if we flush we might push
* frames that still "new"; if this happens then subsequent
* frames will result in BA window moves which cost something
* but is still better than a big throughput dip.
*/
ticks = ddi_get_lbolt();
if (rap->rxa_qframes != 0) {
/* honor batimeout? */
/*
* Too long since we received the first
* frame; flush the reorder buffer.
*/
if (rap->rxa_qframes != 0) {
}
return (PROCESS);
}
} else {
/*
* First frame, start aging timer.
*/
}
#endif /* IEEE80211_AMPDU_AGE */
/* save packet */
rap->rxa_qframes++;
} else {
"a-mpdu duplicate "
"seqno %u tid %u BA win <%u:%u>\n",
freemsg(m);
}
return (CONSUMED);
}
if (off < IEEE80211_SEQ_BA_RANGE) {
/*
* Outside the BA window, but within range;
* flush the reorder q and move the window.
* Sec 9.10.7.6 b) (D2.04 p.118 line 60)
*/
"move BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
/*
* The spec says to flush frames up to but not including:
* WinStart_B = rxseq - rap->rxa_wnd + 1
* Then insert the frame or notify the caller to process
* it immediately. We can safely do this by just starting
* over again because we know the frame will now be within
* the BA window.
*/
/* NB: rxa_wnd known to be >0 */
goto again;
} else {
/*
* Outside the BA window and out of range; toss.
* Sec 9.10.7.6 c) (D2.04 p.119 line 16)
*/
"BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
freemsg(m);
return (CONSUMED);
}
}
/*
* Process a BAR ctl frame. Dispatch all frames up to
* the sequence number of the frame. If this frame is
* out of range it's discarded.
*/
void
{
struct ieee80211_frame_bar *wh;
struct ieee80211_rx_ampdu *rap;
/* check basic BAR */
/*
* No ADDBA request yet, don't touch.
*/
"BAR no BA stream, tid %u\n", tid);
return;
}
return;
/* calculate offset in BA window */
if (off < IEEE80211_SEQ_BA_RANGE) {
/*
* Flush the reorder q up to rxseq and move the window.
* Sec 9.10.7.6 a) (D2.04 p.119 line 22)
*/
"BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
/*
* BAR specifies a window start to the right of BA
* window; we must move it explicitly since
* ampdu_rx_flush_upto will not.
*/
}
} else {
/*
* Out of range; toss.
* Sec 9.10.7.6 b) (D2.04 p.119 line 41)
*/
"BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
}
}
/*
* Setup HT-specific state in a node. Called only
* when HT use is negotiated so we don't do extra
*/
void
{
struct ieee80211_tx_ampdu *tap;
int ac;
/*
* Clean AMPDU state on re-associate. This handles the case
* where a station leaves w/o notifying us and then returns
* before node is reaped for inactivity.
*/
}
/* NB: further initialization deferred */
}
}
/*
* Cleanup HT-specific state in a node. Called only
* when HT use has been marked.
*/
void
{
int i;
/* optimize this */
for (i = 0; i < WME_NUM_AC; i++) {
/*
* Stop BA stream if setup so driver has a chance
* to reclaim any resources it might have allocated.
*/
/* IEEE80211_TAPQ_DESTROY(tap); */
/* NB: clearing NAK means we may re-send ADDBA */
}
}
for (i = 0; i < WME_NUM_TID; i++)
}
static struct ieee80211_channel *
{
}
/*
*/
struct ieee80211_channel *
{
struct ieee80211_channel *c;
if (flags & IEEE80211_FEXT_HT) {
/* promote to HT if possible */
if (flags & IEEE80211_FEXT_USEHT40) {
if (!IEEE80211_IS_CHAN_HT40(chan)) {
/* NB: arbitrarily pick ht40+ over ht40- */
if (c == NULL)
if (c == NULL)
if (c != NULL)
chan = c;
}
} else if (!IEEE80211_IS_CHAN_HT20(chan)) {
if (c != NULL)
chan = c;
}
} else if (IEEE80211_IS_CHAN_HT(chan)) {
/* demote to legacy, HT use is disabled */
if (c != NULL)
chan = c;
}
return (chan);
}
/*
* Setup HT-specific state for a legacy WDS peer.
*/
void
{
struct ieee80211_tx_ampdu *tap;
int ac;
/* check scan cache in case peer has an ap and we have info */
/*
* If setup with a legacy channel; locate an HT channel.
* Otherwise if the inherited channel (from a companion
* AP) is suitable use it so we use the same location
* for the extension channel).
*/
ic->ic_flags_ext);
} else {
}
}
}
/*
* Notify hostap vaps of a change in the HTINFO ie.
*/
static void
{
return;
"HT bss occupancy change: %d sta, %d ht, "
"%d ht40%s, HT protmode now 0x%x\n",
", non-HT sta present" : "",
}
/*
* Calculate HT protection mode from current
* state and handle updates.
*/
static void
{
} else {
}
}
}
/*
* Handle an HT station joining a BSS.
*/
void
{
ic->ic_ht_sta_assoc++;
ic->ic_ht40_sta_assoc++;
}
}
/*
* Handle an HT station leaving a BSS.
*/
void
{
ic->ic_ht_sta_assoc--;
ic->ic_ht40_sta_assoc--;
}
}
/*
* Public version of htinfo_update; used for processing
* beacon frames from overlapping bss in hostap_recv_mgmt.
*/
void
{
}
}
/* unalligned little endian access */
#define LE_READ_2(p) \
((uint16_t) \
((((const uint8_t *)(p))[0]) | \
/*
* Process an 802.11n HT capabilities ie.
*/
void
{
if (ie[0] == IEEE80211_ELEMID_VENDOR) {
/*
* Station used Vendor OUI ie to associate;
* mark the node so when we respond we'll use
* the Vendor OUI's and not the standard ie's.
*/
ie += 4;
} else
/* needed or will ieee80211_parse_htinfo always be called? */
}
/*
* Process an 802.11n HT info ie and update the node state.
* Note that we handle use this information to identify the
* correct channel (HT20, HT40+, HT40-, legacy). The caller
* is responsible for insuring any required channel change is
* done (e.g. in sta mode when parsing the contents of a
* beacon frame).
*/
void
{
const struct ieee80211_ie_htinfo *htinfo;
struct ieee80211_channel *c;
uint16_t w;
if (ie[0] == IEEE80211_ELEMID_VENDOR)
ie += 4;
/*
* Handle 11n channel switch. Use the received HT ie's to
* identify the right channel to use. If we cannot locate it
* in the channel table then fallback to legacy operation.
*/
IEEE80211_CHAN_HT20 : 0;
/* NB: honor operating mode constraint */
}
c = ieee80211_find_channel(ic,
/*
* No HT40 channel entry in our table; fall back
* to HT20 operation. This should not happen.
*/
"no HT40 channel (freq %u), falling back to HT20\n",
/* stat */
}
"switch station to HT%d channel %u/0x%x\n",
}
/* NB: caller responsible for forcing any channel change */
}
/* update node's tx channel width */
}
/*
* Install received HT rate set by parsing the HT cap ie.
*/
int
{
const struct ieee80211_ie_htcap *htcap;
struct ieee80211_htrateset *rs;
int i;
if (ie[0] == IEEE80211_ELEMID_VENDOR)
ie += 4;
for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
continue;
"WARNING, HT rate set too large; only "
"using %u rates\n",
break;
}
}
}
}
/*
* Mark rates in a node's HT rate set as basic according
* to the information in the supplied HT info ie.
*/
void
{
const struct ieee80211_ie_htinfo *htinfo;
struct ieee80211_htrateset *rs;
int i, j;
if (ie[0] == IEEE80211_ELEMID_VENDOR)
ie += 4;
"WARNING, empty HT rate set\n");
return;
}
for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
continue;
}
}
static void
addba_timeout(void *arg)
{
tap->txa_attempts++;
}
static void
{
}
static void
{
}
}
}
/*
* Default method for requesting A-MPDU tx aggregation.
* We setup the specified state block and start a timer
* to wait for an ADDBA response frame.
*/
/* ARGSUSED */
static int
struct ieee80211_tx_ampdu *tap,
{
int bufsiz;
return (1);
}
/*
* Default method for processing an A-MPDU tx aggregation
* response. We shutdown any pending timer and update the
* state block according to the reply.
*/
/* ARGSUSED */
static int
struct ieee80211_tx_ampdu *tap,
{
int bufsiz;
if (status == IEEE80211_STATUS_SUCCESS) {
/* override our request? */
} else {
/* mark tid so we don't try again */
}
return (1);
}
/*
* Default method for stopping A-MPDU tx aggregation.
* Any timer is cleared and we drain any pending frames.
*/
/* ARGSUSED */
static void
{
/* clear aggregation queue */
}
tap->txa_attempts = 0;
}
/*
* Process a received action frame using the default aggregation
* policy. We intercept ADDBA-related frames and use them to
* update our aggregation state. All other frames are passed up
* for processing by ieee80211_recv_action.
*/
static void
{
const struct ieee80211_action *ia;
struct ieee80211_rx_ampdu *rap;
struct ieee80211_tx_ampdu *tap;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
"recv ADDBA request: dialogtoken %u "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
"baseqctl %d:%d\n",
/* Send ADDBA response */
args[0] = dialogtoken;
/*
* NB: We ack only if the sta associated with HT and
* the ap is configured to do AMPDU rx (the latter
* violates the 11n spec and is mostly for testing).
*/
} else {
"reject ADDBA request: %s\n",
"administratively disabled" :
"not negotiated for station");
}
/* honor rap flags? */
args[3] = 0;
return;
ieee80211_err("ADDBA response"
"no pending ADDBA, tid %d dialogtoken %u "
return;
}
ieee80211_err("ADDBA response"
"dialogtoken mismatch: waiting for %d, "
"received %d, tid %d code %d\n",
return;
}
"recv ADDBA response: dialogtoken %u code %d "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d\n",
return;
"recv DELBA: baparamset 0x%x (tid %d initiator %d) "
if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
} else {
}
return;
}
break;
}
}
/*
* Process a received 802.11n action frame.
* Aggregation-related frames are assumed to be handled
* already; we handle any other frames we can, otherwise
* complain about being unsupported (with debugging).
*/
/* ARGSUSED */
void
{
const struct ieee80211_action *ia;
int chw;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
"BA action %d not implemented\n",
break;
case IEEE80211_ACTION_CAT_HT:
}
"HT txchwidth, width %d (%s)\n",
chw,
"new" : "no change");
break;
"HT MIMO PS\n");
break;
default:
"HT action %d not implemented\n",
break;
}
break;
default:
"category %d not implemented\n",
ia->ia_category);
break;
}
}
/*
* Transmit processing.
*/
/*
* Request A-MPDU tx aggregation. Setup local state and
* issue an ADDBA request. BA use will only happen after
* the other end replies with ADDBA response.
*/
int
struct ieee80211_tx_ampdu *tap)
{
int tid, dialogtoken;
static int tokens = 0; /* tokens */
ticks = ddi_get_lbolt();
/* do deferred setup of state */
}
/*
* Don't retry too often; IEEE80211_AGGR_MINRETRY
* defines the minimum interval we'll retry after
* IEEE80211_AGGR_MAXTRIES failed attempts to
* negotiate use.
*/
return (0);
}
/* hack for not doing proper locking */
| SM(0, IEEE80211_BASEQ_FRAG);
/* NB: do first so there's no race against reply */
/* unable to setup state, don't make request */
"could not setup BA stream for AC %d\n",
/* defer next try so we don't slam the driver with requests */
return (0);
}
}
/*
* Terminate an AMPDU tx stream. State is reclaimed
* and the peer notified with a DelBA Action frame.
*/
void
{
if (IEEE80211_AMPDU_RUNNING(tap)) {
} else {
"BA stream for AC %d not running\n",
}
}
/*
* Transmit a BAR frame to the specified node. The
* BAR contents are drawn from the supplied aggregation
* state associated with the node.
*/
int
const struct ieee80211_tx_ampdu *tap)
{
frm[0] = (v) & 0xff; \
frm += 2; \
} while (0)
struct ieee80211_frame_min *wh;
mblk_t *m;
int tid;
if (m == NULL)
return (ENOMEM);
| SM(0, IEEE80211_BASEQ_FRAG);
"send bar frame (tid %u start %u) on channel %u\n",
return (0);
}
/*
* Send an action management frame. The arguments are stuff
* into a frame without inspection; the caller is assumed to
* prepare them carefully (e.g. based on the aggregation state).
*/
int
{
frm[0] = (v) & 0xff; \
frm += 2; \
} while (0)
mblk_t *m;
int ret;
m = ieee80211_getmgtframe(&frm,
sizeof (uint16_t) /* action+category */
/* may action payload */
+ sizeof (struct ieee80211_action_ba_addbaresponse));
if (m == NULL)
return (ENOMEM);
switch (category) {
case IEEE80211_ACTION_CAT_BA:
switch (action) {
"send ADDBA request: dialogtoken %d "
"baparamset 0x%x (tid %d) "
"batimeout 0x%x baseqctl 0x%x\n",
break;
"send ADDBA response: dialogtoken %d status %d "
"baparamset 0x%x (tid %d) batimeout %d\n",
break;
"send DELBA action: tid %d, initiator %d "
"reason %d\n",
break;
default:
goto badaction;
}
break;
case IEEE80211_ACTION_CAT_HT:
switch (action) {
"send HT txchwidth: width %d\n",
break;
default:
goto badaction;
}
break;
default:
"unsupported category %d action %d\n",
return (EINVAL);
/* NOTREACHED */
}
return (ret);
}
/*
* Construct the MCS bit mask for inclusion
* in an HT information element.
*/
static void
{
int i;
if (r < IEEE80211_HTRATE_MAXSIZE) {
/* NB: this assumes a particular implementation */
ieee80211_setbit(frm, r);
}
}
}
/*
* Add body of an HTCAP information element.
*/
static uint8_t *
{
frm[0] = (v) & 0xff; \
frm += 2; \
} while (0)
/* HT capabilities */
/*
* Note channel width depends on whether we are operating as
* a sta or not. When operating as a sta we are generating
* a request based on our desired configuration. Otherwise
* we are operational and the channel attributes identify
* how we've been setup (which might be different if a fixed
* channel is specified).
*/
/* override 20/40 use based on config */
else
/* use advertised setting (locally constraint) */
} else {
/* override 20/40 use based on current channel */
else
}
/* adjust short GI based on channel and config */
(caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
/* HT parameters */
frm++;
/* pre-zero remainder of ie */
/* supported MCS set */
/*
* it would better to get the rate set from in_htrates
* so we can restrict it but for sta mode in_htrates isn't
* setup when we're called to form an AssocReq frame so for
* now we're restricted to the default HT rate set.
*/
frm += sizeof (struct ieee80211_ie_htcap) -
return (frm);
}
/*
* Add 802.11n HT capabilities information element
*/
uint8_t *
{
frm[0] = IEEE80211_ELEMID_HTCAP;
}
/*
* Add Broadcom OUI wrapped standard HTCAP ie; this is
* used for compatibility w/ pre-draft implementations.
*/
uint8_t *
{
frm[0] = IEEE80211_ELEMID_VENDOR;
}
/*
* Construct the MCS bit mask of basic rates
* for inclusion in an HT information element.
*/
static void
{
int i;
r < IEEE80211_HTRATE_MAXSIZE) {
/* NB: this assumes a particular implementation */
ieee80211_setbit(frm, r);
}
}
}
/*
* Update the HTINFO ie for a beacon frame.
*/
void
struct ieee80211_beacon_offsets *bo)
{
struct ieee80211_ie_htinfo *ht =
/* only update on channel change */
else /* LINTED */
/* protection mode */
/* propagate to vendor ie's */
}
/*
* Add body of an HTINFO information element.
*
* NB: We don't use struct ieee80211_ie_htinfo because we can
* be called to fillin both a standard ie and a compat ie that
* has a vendor OUI at the front.
*/
static uint8_t *
{
/* pre-zero remainder of ie */
else /* LINTED */
frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
frm += 5;
/* basic MCS set */
frm += sizeof (struct ieee80211_ie_htinfo) -
return (frm);
}
/*
* Add 802.11n HT information information element.
*/
uint8_t *
{
frm[0] = IEEE80211_ELEMID_HTINFO;
}
/*
* Add Broadcom OUI wrapped standard HTINFO ie; this is
* used for compatibility w/ pre-draft implementations.
*/
uint8_t *
{
frm[0] = IEEE80211_ELEMID_VENDOR;
}
void
{
/* setup default aggregation policy */
/* get from driver */
/*
* Device is HT capable; enable all HT-related
* facilities by default.
* these choices may be too aggressive.
*/
/* infer from channel list? */
}
/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
}
#define ieee80211_isset16(a, i) ((a) & (1 << (i)))
/* fill default rate sets for 11NA/11NG if driver has no specified */
}
}
}
/* ARGSUSED */
void
{
}
/* ARGSUSED */
static void
const struct ieee80211_htrateset *rs)
{
int i, rate;
(i != 0 ? " " : ""),
}
}
void
{
}
/* ARGSUSED */
const struct ieee80211_htrateset *
const struct ieee80211_channel *c)
{
return (&ieee80211_rateset_11n);
}