blob: 3f83e91afc9e11861dbc8ae842d34b02934b8f91 [file] [log] [blame]
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2005 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
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* 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.
*
* $Id: ieee80211_output.c 2606 2007-07-25 15:14:52Z mrenzmann $
*/
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
/*
* IEEE 802.11 output handling.
*/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h> /* for ARP proxy */
#include <linux/ip.h> /* XXX for TOS */
#include <net/iw_handler.h> /* wireless_send_event(..) */
#include "qtn/qtn_global.h"
#include "qtn/shared_params.h"
#include "qtn/hardware_revision.h"
#include "net80211/if_llc.h"
#include "net80211/if_ethersubr.h"
#include "net80211/if_media.h"
#include "net80211/ieee80211.h"
#include "net80211/ieee80211_var.h"
#include "net80211/ieee80211_dot11_msg.h"
#include "net80211/ieee80211_monitor.h"
#include "net80211/ieee80211_tdls.h"
#if defined(CONFIG_QTN_BSA_SUPPORT)
#include "net80211/ieee80211_bsa.h"
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include "asm-generic/unaligned.h"
#endif
#include "qtn_logging.h"
#ifdef IEEE80211_DEBUG
/*
* Decide if an outbound management frame should be
* printed when debugging is enabled. This filters some
* of the less interesting frames that come frequently
* (e.g. beacons).
*/
static __inline int
doprint(struct ieee80211vap *vap, int subtype)
{
if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
return (vap->iv_opmode == IEEE80211_M_IBSS);
return 1;
}
#endif
#define senderr(_x, _v) do { vap->iv_stats._v++; ret = (_x); goto bad; } while (0)
/*
* Add the Quantenna OUI to a frame
*/
uint8_t
ieee80211_oui_add_qtn(uint8_t *oui)
{
oui[0] = QTN_OUI & 0xff;
oui[1] = (QTN_OUI >> 8) & 0xff;
oui[2] = (QTN_OUI >> 16) & 0xff;
return IEEE80211_OUI_LEN;
}
EXPORT_SYMBOL(ieee80211_oui_add_qtn);
void ieee80211_parent_queue_xmit(struct sk_buff *skb)
{
struct ieee80211vap *vap = netdev_priv(skb->dev);
skb->dev = vap->iv_dev;
dev_queue_xmit(skb);
}
/*
* Initialise an 802.11 header.
* This should be called early on in constructing a frame as it sets i_fc[1]. Other bits can then be
* OR'd in.
*/
static void
ieee80211_send_setup(struct ieee80211vap *vap, struct ieee80211_node *ni,
struct ieee80211_frame *wh,
const uint8_t type, const uint8_t subtype,
const uint8_t *sa, const uint8_t *da, const uint8_t *bssid)
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type | subtype;
if (type == IEEE80211_FC0_TYPE_DATA) {
switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
IEEE80211_ADDR_COPY(wh->i_addr2, sa);
IEEE80211_ADDR_COPY(wh->i_addr3, da);
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
IEEE80211_ADDR_COPY(wh->i_addr1, da);
IEEE80211_ADDR_COPY(wh->i_addr2, sa);
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
break;
case IEEE80211_M_HOSTAP:
wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
IEEE80211_ADDR_COPY(wh->i_addr1, da);
IEEE80211_ADDR_COPY(wh->i_addr2, bssid);
IEEE80211_ADDR_COPY(wh->i_addr3, sa);
break;
case IEEE80211_M_WDS:
wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
IEEE80211_ADDR_COPY(wh->i_addr1, bssid); /* bssid holds RA */
IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, da);
IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
break;
case IEEE80211_M_MONITOR:
break;
}
} else {
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
IEEE80211_ADDR_COPY(wh->i_addr1, da);
IEEE80211_ADDR_COPY(wh->i_addr2, sa);
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
wh->i_dur[0] = 0;
wh->i_dur[1] = 0;
if (!(subtype & IEEE80211_FC0_SUBTYPE_QOS)) {
*(__le16 *)&wh->i_seq[0] = htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
ni->ni_txseqs[0]++;
}
#undef WH4
}
/*
* Send an EAPOL frame to the specified node.
* Use the MGMT frame path to ensure that EAPOL frames are high priority.
*/
void
ieee80211_eap_output(struct net_device *dev, const void *const eap_msg, const int eap_msg_len)
{
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
struct ieee80211_frame *wh;
struct ieee80211_qosframe *qwh;
const struct ether_header *const eh = eap_msg;
uint8_t *frm;
struct sk_buff *skb;
struct llc *llc;
int headerlen;
uint8_t subtype;
if (eap_msg_len <= sizeof(*eh)) {
return;
}
if (vap->iv_opmode == IEEE80211_M_WDS) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DOT1X,
"[%pM] eap send failed - WDS not supported\n",
eh->ether_dhost);
return;
}
ni = ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
if (!ni) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DOT1X,
"[%pM] eap send failed - node %s not found\n",
eh->ether_dhost);
return;
}
if (ni->ni_flags & IEEE80211_NODE_QOS) {
headerlen = sizeof(*qwh);
subtype = IEEE80211_FC0_SUBTYPE_QOS;
} else {
headerlen = sizeof(*wh);
subtype = IEEE80211_FC0_SUBTYPE_DATA;
}
skb = ieee80211_getdataframe(vap, &frm, 1, eap_msg_len + LLC_SNAPFRAMELEN);
if (!skb) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DOT1X,
"[%s] eap send failed - sbk alloc\n",
ni->ni_macaddr);
ieee80211_free_node(ni);
return;
}
memcpy(frm, eap_msg, eap_msg_len);
/* Replace the ethernet header with SNAP and 802.11 headers */
skb_pull(skb, sizeof(*eh));
llc = (struct llc *) skb_push(skb, LLC_SNAPFRAMELEN);
llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
llc->llc_control = LLC_UI;
llc->llc_snap.org_code[0] = 0;
llc->llc_snap.org_code[1] = 0;
llc->llc_snap.org_code[2] = 0;
llc->llc_snap.ether_type = htons(ETH_P_PAE);
wh = (struct ieee80211_frame *) skb_push(skb, headerlen);
ieee80211_send_setup(vap, ni, wh,
IEEE80211_FC0_TYPE_DATA, subtype,
vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
skb_trim(skb, eap_msg_len - sizeof(*eh) + headerlen + LLC_SNAPFRAMELEN);
if (ni->ni_flags & IEEE80211_NODE_QOS) {
qwh = (struct ieee80211_qosframe *) wh;
qwh->i_qos[0] = QTN_TID_WLAN;
qwh->i_qos[1] = 0;
}
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DOT1X,
"[%pM] send eapol frame on channel %u\n",
ni->ni_macaddr, ieee80211_chan2ieee(ic, ic->ic_curchan));
IEEE80211_NODE_STAT(ni, tx_data);
ieee80211_off_channel_suspend(vap, IEEE80211_OFFCHAN_TIMEOUT_EAPOL);
ic->ic_send_80211(ic, ni, skb, WME_AC_VO, 0);
}
EXPORT_SYMBOL(ieee80211_eap_output);
/*
* Send a management frame to the specified node. The node pointer
* must have a reference as the pointer will be passed to the driver
* and potentially held for a long time. If the frame is successfully
* dispatched to the driver, then it is responsible for freeing the
* reference (and potentially freeing up any associated storage).
*/
void
ieee80211_mgmt_output(struct ieee80211_node *ni, struct sk_buff *skb, int subtype,
const u_int8_t da[IEEE80211_ADDR_LEN])
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
wh = (struct ieee80211_frame *) skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, wh, IEEE80211_FC0_TYPE_MGT, subtype,
vap->iv_myaddr, da, ni->ni_bssid);
/* FIXME power management */
if (M_FLAG_ISSET(skb, M_LINK0) && ni->ni_challenge != NULL) {
M_FLAG_CLR(skb, M_LINK0);
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
"encrypting frame (%s)", __func__);
wh->i_fc[1] |= IEEE80211_FC1_PROT;
}
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
#ifdef IEEE80211_DEBUG
if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
ieee80211_msg_dumppkts(vap)) {
printf("[%pM] send %s on channel %u\n",
wh->i_addr1,
ieee80211_mgt_subtype_name[
(subtype & IEEE80211_FC0_SUBTYPE_MASK) >>
IEEE80211_FC0_SUBTYPE_SHIFT],
ieee80211_chan2ieee(ic, ic->ic_curchan));
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
ic->ic_send_80211(ic, ni, skb, WME_AC_VO, 1);
}
EXPORT_SYMBOL(ieee80211_mgmt_output);
void
ieee80211_tdls_mgmt_output(struct ieee80211_node *ni,
struct sk_buff *skb, const uint8_t type,
const uint8_t subtype, const uint8_t *da,
const uint8_t *bssid)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
wh = (struct ieee80211_frame *)
skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, wh,
type, subtype, vap->iv_myaddr, da, bssid);
if (M_FLAG_ISSET(skb, M_LINK0) && ni->ni_challenge != NULL) {
M_FLAG_CLR(skb, M_LINK0);
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
"encrypting frame (%s)", __func__);
wh->i_fc[1] |= IEEE80211_FC1_PROT;
}
/* XXX power management */
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
#ifdef IEEE80211_DEBUG
if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
ieee80211_msg_dumppkts(vap)) {
printf("[%s] send %s on channel %u\n",
ether_sprintf(wh->i_addr1),
ieee80211_mgt_subtype_name[
(subtype & IEEE80211_FC0_SUBTYPE_MASK) >>
IEEE80211_FC0_SUBTYPE_SHIFT],
ieee80211_chan2ieee(ic, ic->ic_curchan));
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
ic->ic_send_80211(ic, ni, skb, WME_AC_VO, 1);
}
EXPORT_SYMBOL(ieee80211_tdls_mgmt_output);
/*
* Send a null data frame to the specified node.
*
* NB: the caller is assumed to have setup a node reference
* for use; this is necessary to deal with a race condition
* when probing for inactive stations.
*/
int
ieee80211_send_nulldata(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
struct ieee80211_frame *wh;
uint8_t *frm;
skb = ieee80211_getdataframe(vap, &frm, 0, 0);
if (skb == NULL) {
ieee80211_free_node(ni);
return -ENOMEM;
}
wh = (struct ieee80211_frame *) skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, wh,
IEEE80211_FC0_TYPE_DATA,
IEEE80211_FC0_SUBTYPE_NODATA,
vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
/* NB: power management bit is never sent by an AP */
if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
vap->iv_opmode != IEEE80211_M_HOSTAP &&
vap->iv_opmode != IEEE80211_M_WDS)
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
IEEE80211_NODE_STAT(ni, tx_data);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send null data frame on channel %u, pwr mgt %s\n",
ether_sprintf(ni->ni_macaddr),
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
ic->ic_send_80211(ic, ni, skb, WME_AC_VO, 0);
return 0;
}
/*
* Send some tuning data packets for low level to do
* power adjustment.
*/
int
ieee80211_send_tuning_data(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
struct sk_buff *skb;
struct ieee80211_frame *wh;
uint8_t *frm;
const uint8_t da[IEEE80211_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
skb = ieee80211_getdataframe(vap, &frm, 0, 0);
if (skb == NULL)
return -ENOMEM;
/* Fill up the frame header */
wh = (struct ieee80211_frame *)skb_push(skb, sizeof(struct ieee80211_frame));
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA;
wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
IEEE80211_ADDR_COPY(wh->i_addr1, da);
IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_macaddr);
wh->i_dur[0] = 0;
wh->i_dur[1] = 0;
ieee80211_ref_node(ni);
/* No need to send these at high priority */
(void)ic->ic_send_80211(ic, ni, skb, WME_AC_BE, 0);
return 0;
}
EXPORT_SYMBOL(ieee80211_send_nulldata);
/*
* Get null data on a particular AC to a node.
*
* The caller is assumed to have taken a node reference.
*/
struct sk_buff *
ieee80211_get_nulldata(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct sk_buff *skb;
struct ieee80211_frame *wh;
uint8_t *frm;
skb = ieee80211_getdataframe(vap, &frm, 0, 0);
if (skb == NULL)
return NULL;
wh = (struct ieee80211_frame *)skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)wh,
IEEE80211_FC0_TYPE_DATA,
IEEE80211_FC0_SUBTYPE_NODATA,
vap->iv_myaddr,
ni->ni_macaddr,
ni->ni_bssid);
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
IEEE80211_NODE_STAT(ni, tx_data);
return skb;
}
EXPORT_SYMBOL(ieee80211_get_nulldata);
/*
* Get QoS null data on a particular AC to a node.
*
* The caller is assumed to have taken a node reference.
*/
struct sk_buff *
ieee80211_get_qosnulldata(struct ieee80211_node *ni, int ac)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
struct ieee80211_qosframe *qwh;
uint8_t *frm;
int tid;
skb = ieee80211_getdataframe(vap, &frm, 1, 0);
if (skb == NULL)
return NULL;
skb->priority = ac;
qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe));
ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
IEEE80211_FC0_TYPE_DATA,
IEEE80211_FC0_SUBTYPE_QOS_NULL,
vap->iv_myaddr,
ni->ni_macaddr,
ni->ni_bssid);
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
qwh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
tid = QTN_TID_WLAN;
qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmm_noackPolicy)
qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY;
qwh->i_qos[1] = 0;
IEEE80211_NODE_STAT(ni, tx_data);
if (WME_UAPSD_AC_CAN_TRIGGER(skb->priority, ni)) {
/* U-APSD power save queue */
/* XXXAPSD: assuming triggerable means deliverable */
M_FLAG_SET(skb, M_UAPSD);
}
return skb;
}
EXPORT_SYMBOL(ieee80211_get_qosnulldata);
/*
* Send QoS null data on a particular AC to a node.
*
* The caller is assumed to have taken a node reference.
*/
int
ieee80211_send_qosnulldata(struct ieee80211_node *ni, int ac)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
skb = ieee80211_get_qosnulldata(ni, ac);
if (skb == NULL) {
ieee80211_free_node(ni);
return -ENOMEM;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send qos null data frame on channel %u\n",
ether_sprintf(ni->ni_macaddr),
ieee80211_chan2ieee(ic, ic->ic_curchan));
ic->ic_send_80211(ic, ni, skb, ac, 0);
return 0;
}
EXPORT_SYMBOL(ieee80211_send_qosnulldata);
int
ieee80211_send_qosnulldata_ext(struct ieee80211com *ic, uint8_t *mac_addr, int pwr_mgt)
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
struct ieee80211vap *vap;
struct ieee80211_node *ni;
struct sk_buff *skb;
uint8_t *frm;
struct ieee80211_qosframe *qwh;
int ac;
int tid;
ni = ieee80211_find_node(&ic->ic_sta, mac_addr);
if (!ni)
return -EINVAL;
ac = WMM_AC_BK;
vap = ni->ni_vap;
skb = ieee80211_getdataframe(vap, &frm, 1, 0);
if (skb == NULL) {
ieee80211_free_node(ni);
return -ENOMEM;
}
skb->priority = ac;
qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe_addr4));
ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
IEEE80211_FC0_TYPE_MGT,
IEEE80211_FC0_SUBTYPE_QOS_NULL,
vap->iv_myaddr,
ni->ni_macaddr,
ni->ni_bssid);
qwh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS_NULL;
if (pwr_mgt)
qwh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
tid = QTN_TID_WLAN;
qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmm_noackPolicy)
qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY;
qwh->i_qos[1] = 0;
IEEE80211_NODE_STAT(ni, tx_data);
if (WME_UAPSD_AC_CAN_TRIGGER(skb->priority, ni)) {
/* U-APSD power save queue */
/* XXXAPSD: assuming triggerable means deliverable */
M_FLAG_SET(skb, M_UAPSD);
}
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send pwr_mgt(%d) data frame on channel %u\n",
ether_sprintf(ni->ni_macaddr), pwr_mgt,
ieee80211_chan2ieee(ic, ic->ic_curchan));
ic->ic_send_80211(ic, ni, skb, ac, 0);
return 0;
}
EXPORT_SYMBOL(ieee80211_send_qosnulldata_ext);
/*
* Add transmit power envelope information element
*/
u_int8_t *
ieee80211_add_vhttxpwr_envelope(u_int8_t *frm, struct ieee80211com *ic)
{
u_int32_t bw = ieee80211_get_bw(ic);
struct ieee80211_ie_vtxpwren *ie = (struct ieee80211_ie_vtxpwren *)frm;
u_int8_t local_max_tx_pwrcnt = 0;
struct ieee80211_channel *des_chan = ic->ic_des_chan;
if (des_chan == IEEE80211_CHAN_ANYC)
return frm;
switch (bw) {
case BW_HT20:
local_max_tx_pwrcnt = IEEE80211_TX_POW_FOR_20MHZ;
break;
case BW_HT40:
local_max_tx_pwrcnt = IEEE80211_TX_POW_FOR_40MHZ;
break;
case BW_HT80:
local_max_tx_pwrcnt = IEEE80211_TX_POW_FOR_80MHZ;
break;
default:
local_max_tx_pwrcnt = IEEE80211_TX_POW_FOR_80MHZ;
}
ie->vtxpwren_id = IEEE80211_ELEMID_VHTXMTPWRENVLP;
ie->vtxpwren_len = sizeof(struct ieee80211_ie_vtxpwren) - 2;
ie->vtxpwren_txpwr_info = local_max_tx_pwrcnt;
ie->vtxpwren_tp20 = des_chan->ic_maxregpower - ic->ic_pwr_constraint;
ie->vtxpwren_tp40 = des_chan->ic_maxregpower - ic->ic_pwr_constraint;
ie->vtxpwren_tp80 = des_chan->ic_maxregpower - ic->ic_pwr_constraint;
ie->vtxpwren_tp160 = 0;
frm += sizeof(struct ieee80211_ie_vtxpwren);
return frm;
}
/*
* Add wide-bandwidth Channel switch wrapper information element
*/
u_int8_t *
ieee80211_add_wband_chanswitch(u_int8_t *frm, struct ieee80211com *ic)
{
u_int32_t bw = ieee80211_get_bw(ic);
struct ieee80211_ie_wbchansw *ie = (struct ieee80211_ie_wbchansw *)frm;
struct ieee80211_channel *des_chan = ic->ic_csa_chan;
u_int32_t chwidth = 0;
if (!des_chan || (des_chan == IEEE80211_CHAN_ANYC))
return frm;
ie->wbcs_id = IEEE80211_ELEMID_WBWCHANSWITCH;
ie->wbcs_len = sizeof(struct ieee80211_ie_wbchansw) - 2;
switch (bw) {
case BW_HT20:
case BW_HT40:
chwidth = IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ;
break;
case BW_HT80:
chwidth = IEEE80211_VHTOP_CHAN_WIDTH_80MHZ;
break;
default:
chwidth = IEEE80211_VHTOP_CHAN_WIDTH_80MHZ;
}
ie->wbcs_newchanw = chwidth;
if (bw == BW_HT40) {
ie->wbcs_newchancf0 = des_chan->ic_center_f_40MHz;
ie->wbcs_newchancf1 = 0;
} else if (bw == BW_HT80) {
ie->wbcs_newchancf0 = des_chan->ic_center_f_80MHz;
ie->wbcs_newchancf1 = 0;
} else {
ie->wbcs_newchancf0 = 0;
ie->wbcs_newchancf1 = 0;
}
frm += sizeof(struct ieee80211_ie_wbchansw);
return frm;
}
/*
* Add Channel switch wrapper information element
*/
u_int8_t *
ieee80211_add_chansw_wrap(u_int8_t *frm, struct ieee80211com *ic)
{
struct ieee80211_ie_chsw_wrapper *ie = (struct ieee80211_ie_chsw_wrapper *) frm;
u_int32_t bw = ieee80211_get_bw(ic);
ie->chsw_id = IEEE80211_ELEMID_CHANSWITCHWRP;
ie->chsw_len = 0;
frm += sizeof(struct ieee80211_ie_chsw_wrapper);
/* Wide bandwidth channel switch element */
if (bw > BW_HT20) {
ie->chsw_len += sizeof(struct ieee80211_ie_wbchansw);
frm = ieee80211_add_wband_chanswitch(frm, ic);
}
/* VHT transmit power envelope */
if ((ic->ic_flags & IEEE80211_F_DOTH) &&
(ic->ic_flags_ext & IEEE80211_FEXT_TPC)) {
ie->chsw_len += sizeof(struct ieee80211_ie_vtxpwren);
frm = ieee80211_add_vhttxpwr_envelope(frm, ic);
}
return frm;
}
/*
* Add a supported rates element id to a frame.
*/
u_int8_t *
ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
{
int nrates;
*frm++ = IEEE80211_ELEMID_RATES;
nrates = rs->rs_nrates;
if (nrates > IEEE80211_RATE_SIZE)
nrates = IEEE80211_RATE_SIZE;
*frm++ = nrates;
memcpy(frm, rs->rs_rates, nrates);
return frm + nrates;
}
/*
* Add a Supported Channels element id to a frame.
*/
uint8_t*
ieee80211_add_supported_chans(uint8_t *frm, struct ieee80211com *ic)
{
int band_idx;
int first_chan;
int temp_chan;
int chan_cnt;
int active_chan_cnt;
uint8_t *ie_len;
int cur_bw;
uint8_t *chan_active;
struct ieee80211_band_info *band;
*frm++ = IEEE80211_ELEMID_SUPPCHAN;
ie_len = frm++;
*ie_len = 0;
cur_bw = ieee80211_get_bw(ic);
if (cur_bw == BW_HT20)
chan_active = &ic->ic_chan_active_20[0];
else if (cur_bw == BW_HT40)
chan_active = &ic->ic_chan_active_40[0];
else if (cur_bw == BW_HT80)
chan_active = &ic->ic_chan_active_80[0];
else
chan_active = &ic->ic_chan_active[0];
for (band_idx = 0; band_idx < IEEE80211_BAND_IDX_MAX; band_idx++) {
band = ieee80211_get_band_info(band_idx);
if (band == NULL)
continue;
first_chan = band->band_first_chan;
chan_cnt = band->band_chan_cnt;
active_chan_cnt = 0;
for (temp_chan = first_chan; chan_cnt >= 0; chan_cnt--){
if (isset(chan_active, temp_chan) && chan_cnt > 0) {
active_chan_cnt++;
} else if (active_chan_cnt) {
*frm++ = first_chan;
*frm++ = active_chan_cnt;
*ie_len += 2;
active_chan_cnt = 0;
}
if (active_chan_cnt == 1)
first_chan = temp_chan;
temp_chan += band->band_chan_step;
}
}
return frm;
}
/*
* Add an extended capabilities element id to a frame
*/
u_int8_t *
ieee80211_add_extcap(u_int8_t *frm)
{
struct ieee80211_extcap_param *ie = (struct ieee80211_extcap_param *)frm;
memset(ie, 0, sizeof(struct ieee80211_extcap_param));
ie->param_id = IEEE80211_ELEMID_EXTCAP;
ie->param_len = sizeof(struct ieee80211_extcap_param) - 2;
/* max msdu in amsdu 0 = unlimited */
ie->ext_cap[7] = IEEE80211_EXTCAP_OPMODE_NOTIFICATION;
return frm + sizeof(struct ieee80211_extcap_param);
}
void
ieee80211_update_bss_tm(uint8_t *appie, int len, struct ieee80211com *ic, struct ieee80211vap *vap)
{
size_t left = len;
uint8_t *pos = appie;
while (left >= 2) {
uint8_t id;
uint8_t elen;
id = *pos++;
elen = *pos++;
left -= 2;
if (elen > left) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ELEMID,
"IEEE 802.11 element parse failed (id=%u elen=%u left=%u)",
id, elen, left);
return;
}
if (id == IEEE80211_ELEMID_EXTCAP) {
if (IEEE80211_COM_BTM_ENABLED(ic)) {
*(pos + 2) |= IEEE80211_EXTCAP_BTM;
} else {
*(pos + 2) &= ~IEEE80211_EXTCAP_BTM;
}
return;
} else {
left -= elen;
pos += elen;
}
}
}
/*
* Add an extended supported rates element id to a frame.
*/
u_int8_t *
ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
{
//FIXME
#if 1
/*
* Add an extended supported rates element if operating in 11g/n mode.
* Only 11g rates are added. 11n Rates are published via ht cap */
if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
int nrates = rs->rs_legacy_nrates - IEEE80211_RATE_SIZE;
if(nrates)
{
*frm++ = IEEE80211_ELEMID_XRATES;
*frm++ = nrates;
memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
frm += nrates;
}
}
#else
/* Add BSS membership selector (HT == 0x7F)*/
*frm++ = IEEE80211_ELEMID_XRATES;
*frm++ = 1;
*frm++ = 0x7F;
#endif
return frm;
}
/*
* Add an ssid elemet to a frame.
*/
static u_int8_t *
ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
{
*frm++ = IEEE80211_ELEMID_SSID;
*frm++ = len;
memcpy(frm, ssid, len);
return frm + len;
}
/*
* Add an csa element to a frame.
*/
u_int8_t *
ieee80211_add_csa(u_int8_t *frm,
u_int8_t csa_mode,
u_int8_t csa_chan,
u_int8_t csa_count)
{
*frm++ = IEEE80211_ELEMID_CHANSWITCHANN;
*frm++ = 3;
*frm++ = csa_mode;
*frm++ = csa_chan;
*frm++ = csa_count;
return frm;
}
/*
* Add secondary channel offset element to a frame.
*/
void ieee80211_add_sec_chan_off(u_int8_t **frm,
struct ieee80211com *ic,
uint8_t csa_chan)
{
struct ieee80211_channel *chan = NULL;
uint8_t sec_position = IEEE80211_HTINFO_EXTOFFSET_NA;
struct ieee80211_ie_sec_chan_off *sco = (struct ieee80211_ie_sec_chan_off *)(*frm);
uint32_t curr_bw = ieee80211_get_bw(ic);
chan = ieee80211_find_channel_by_ieee(ic, csa_chan);
if (chan && (curr_bw >= BW_HT40)) {
if (chan->ic_flags & IEEE80211_CHAN_HT40D) {
sec_position = IEEE80211_HTINFO_EXTOFFSET_BELOW;
} else if (chan->ic_flags & IEEE80211_CHAN_HT40U) {
sec_position = IEEE80211_HTINFO_EXTOFFSET_ABOVE;
}
}
sco->sco_id = IEEE80211_ELEMID_SEC_CHAN_OFF;
sco->sco_len = 1;
sco->sco_off = sec_position;
*frm += sizeof(struct ieee80211_ie_sec_chan_off);
return;
}
/*
* Add an erp element to a frame.
*/
u_int8_t *
ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
{
u_int8_t erp;
*frm++ = IEEE80211_ELEMID_ERP;
*frm++ = 1;
erp = 0;
if (ic->ic_nonerpsta != 0)
erp |= IEEE80211_ERP_NON_ERP_PRESENT;
if (ic->ic_flags & IEEE80211_F_USEPROT)
erp |= IEEE80211_ERP_USE_PROTECTION;
if (ic->ic_flags & IEEE80211_F_USEBARKER)
erp |= IEEE80211_ERP_LONG_PREAMBLE;
*frm++ = erp;
return frm;
}
/*
* Add a country information element to a frame.
*/
u_int8_t *
ieee80211_add_country(u_int8_t *frm, struct ieee80211com *ic)
{
/* add country code */
memcpy(frm, (u_int8_t *)&ic->ic_country_ie,
ic->ic_country_ie.country_len + 2);
frm += ic->ic_country_ie.country_len + 2;
return frm;
}
/*
* Add BSS load element to frame
*/
u_int8_t *
ieee80211_add_bss_load(u_int8_t *frm, struct ieee80211vap *vap)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
*frm++ = IEEE80211_ELEMID_BSS_LOAD;
*frm++ = 5;
ADDINT16LE(frm, vap->iv_sta_assoc);
*frm++ = sp->chan_util;
/* TODO: Available Admission Capacity
* parameters need to be updated with correct values */
*(__le16 *)frm = 0xffff;
frm += 2;
return frm;
}
/*
* Add RRM Enabled capabilities information element to frame
*/
u_int8_t *
ieee80211_add_rrm_enabled(u_int8_t *frm, struct ieee80211vap *vap)
{
struct ieee80211_ie_rrm *ie = (struct ieee80211_ie_rrm *)frm;
memset(ie, 0, sizeof(*ie));
ie->id = IEEE80211_ELEMID_RRM_ENABLED;
ie->len = (uint8_t)sizeof(*ie) - IEEE80211_IE_ID_LEN_SIZE;
/* set neigbor report capable bit */
if (IEEE80211_COM_NEIGHREPORT_ENABLED(vap->iv_ic)) {
ie->cap[0] |= IEEE80211_RM_NEIGH_REPORT_CAP;
} else {
ie->cap[0] = 0;
}
return frm + sizeof(*ie);
}
static u_int8_t *
ieee80211_setup_wpa_ie(struct ieee80211vap *vap, u_int8_t *ie)
{
#define WPA_OUI_BYTES 0x00, 0x50, 0xf2
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
#define ADDSELECTOR(frm, sel) do { \
memcpy(frm, sel, 4); \
frm += 4; \
} while (0)
static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_RSN_OUI_TYPE };
static const u_int8_t cipher_suite[][4] = {
{ WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */
{ WPA_OUI_BYTES, WPA_CSE_TKIP },
{ 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */
{ WPA_OUI_BYTES, WPA_CSE_CCMP },
{ 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
{ WPA_OUI_BYTES, WPA_CSE_NULL },
};
static const u_int8_t wep104_suite[4] =
{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
static const u_int8_t key_mgt_unspec[4] =
{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
static const u_int8_t key_mgt_psk[4] =
{ WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
const struct ieee80211_rsnparms *rsn = &vap->iv_bss->ni_rsn;
u_int8_t *frm = ie;
u_int8_t *selcnt;
struct ieee80211com *ic = vap->iv_ic;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* length filled in below */
memcpy(frm, oui, sizeof(oui)); /* WPA OUI */
frm += sizeof(oui);
ADDSHORT(frm, WPA_VERSION);
/* XXX filter out CKIP */
/* multicast cipher */
if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
rsn->rsn_mcastkeylen >= 13)
ADDSELECTOR(frm, wep104_suite);
else if (!(IEEE80211_IS_TKIP_ALLOWED(ic)) &&
(rsn->rsn_mcastcipher == IEEE80211_CIPHER_TKIP)) /* remove TKIP functionality */
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
else
ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
/* unicast cipher list */
selcnt = frm;
ADDSHORT(frm, 0); /* selector count */
if (rsn->rsn_ucastcipherset & (1 << IEEE80211_CIPHER_AES_CCM)) {
selcnt[0]++;
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
}
if (IEEE80211_IS_TKIP_ALLOWED(ic)) {
if (rsn->rsn_ucastcipherset & (1 << IEEE80211_CIPHER_TKIP)) {
selcnt[0]++;
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
}
}
/* authenticator selector list */
selcnt = frm;
ADDSHORT(frm, 0); /* selector count */
if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_unspec);
}
if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_psk);
}
/* optional capabilities */
if ((rsn->rsn_caps != 0) && (rsn->rsn_caps != RSN_CAP_PREAUTH))
ADDSHORT(frm, rsn->rsn_caps);
/* calculate element length */
ie[1] = frm - ie - 2;
KASSERT(ie[1] + 2 <= sizeof(struct ieee80211_ie_wpa),
("WPA IE too big, %u > %u",
ie[1] + 2, (int)sizeof(struct ieee80211_ie_wpa)));
return frm;
#undef ADDSHORT
#undef ADDSELECTOR
#undef WPA_OUI_BYTES
}
static u_int8_t *
ieee80211_setup_rsn_ie(struct ieee80211vap *vap, u_int8_t *ie)
{
#define RSN_OUI_BYTES 0x00, 0x0f, 0xac
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
#define ADDSELECTOR(frm, sel) do { \
memcpy(frm, sel, 4); \
frm += 4; \
} while (0)
static const u_int8_t cipher_suite[][4] = {
{ RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */
{ RSN_OUI_BYTES, RSN_CSE_TKIP },
{ RSN_OUI_BYTES, RSN_CSE_WRAP },
{ RSN_OUI_BYTES, RSN_CSE_CCMP },
{ 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
{ RSN_OUI_BYTES, RSN_CSE_NULL },
};
static const u_int8_t wep104_suite[4] =
{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
static const u_int8_t key_mgt_unspec[4] =
{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
static const u_int8_t key_mgt_psk[4] =
{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
static const u_int8_t key_mgt_dot1x_sha256[4] =
{ RSN_OUI_BYTES, RSN_ASE_8021X_SHA256 };
static const u_int8_t key_mgt_psk_sha256[4] =
{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK_SHA256 };
static const u_int8_t key_mgt_bip[4] =
{ RSN_OUI_BYTES, RSN_CSE_BIP };
static const u_int8_t key_mgt_ft_8021x[4] =
{ RSN_OUI_BYTES, RSN_ASE_FT_8021X };
static const u_int8_t key_mgt_ft_psk[4] =
{ RSN_OUI_BYTES, RSN_ASE_FT_PSK };
const struct ieee80211_rsnparms *rsn = &vap->iv_bss->ni_rsn;
u_int8_t *frm = ie;
u_int8_t *selcnt;
struct ieee80211com *ic = vap->iv_ic;
*frm++ = IEEE80211_ELEMID_RSN;
*frm++ = 0; /* length filled in below */
ADDSHORT(frm, RSN_VERSION);
/* XXX filter out CKIP */
/* multicast cipher */
if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
rsn->rsn_mcastkeylen >= 13)
ADDSELECTOR(frm, wep104_suite);
else if (!(IEEE80211_IS_TKIP_ALLOWED(ic)) &&
(rsn->rsn_mcastcipher == IEEE80211_CIPHER_TKIP)) /* remove TKIP functionality */
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
else
ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
/* unicast cipher list */
selcnt = frm;
ADDSHORT(frm, 0); /* selector count */
if (rsn->rsn_ucastcipherset & (1 << IEEE80211_CIPHER_AES_CCM)) {
selcnt[0]++;
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
}
if (IEEE80211_IS_TKIP_ALLOWED(ic)) {
if (rsn->rsn_ucastcipherset & (1 << IEEE80211_CIPHER_TKIP)) {
selcnt[0]++;
ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
}
}
/* authenticator selector list */
selcnt = frm;
ADDSHORT(frm, 0); /* selector count */
if (rsn->rsn_keymgmtset == RSN_ASE_8021X_UNSPEC) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_unspec);
}
if (rsn->rsn_keymgmtset == RSN_ASE_8021X_PSK) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_psk);
}
if (rsn->rsn_keymgmtset == (RSN_ASE_8021X_PSK | WPA_KEY_MGMT_FT_PSK)) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_psk);
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_ft_psk);
}
if (rsn->rsn_keymgmtset == (RSN_ASE_8021X_UNSPEC | WPA_KEY_MGMT_FT_IEEE8021X)) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_unspec);
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_ft_8021x);
}
if (vap->iv_pmf) {
if ((rsn->rsn_keymgmtset == RSN_ASE_8021X_PSK_SHA256)) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_psk_sha256);
} else if ((rsn->rsn_keymgmtset == RSN_ASE_8021X_SHA256)) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_dot1x_sha256);
}
}
/* capabilities */
ADDSHORT(frm, (rsn->rsn_caps | (vap->iv_pmf << 6)));
/* XXX PMKID */
if (vap->iv_pmf) {
/* PMKID here: We dont support PMKID list */
ADDSHORT(frm, 0);
/* 802.11w Group Management Cipher suite */
selcnt = frm;
if (rsn->rsn_ucastcipherset & (1 << IEEE80211_CIPHER_AES_CCM)) {
selcnt[0]++;
ADDSELECTOR(frm, key_mgt_bip);
}
}
/* calculate element length */
ie[1] = frm - ie - 2;
KASSERT(ie[1] + 2 <= sizeof(struct ieee80211_ie_wpa),
("RSN IE too big, %u > %u",
ie[1] + 2, (int)sizeof(struct ieee80211_ie_wpa)));
return frm;
#undef ADDSELECTOR
#undef ADDSHORT
#undef RSN_OUI_BYTES
}
/*
* Add a WPA/RSN element to a frame.
*/
u_int8_t *
ieee80211_add_wpa(u_int8_t *frm, struct ieee80211vap *vap)
{
KASSERT(vap->iv_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
if (vap->iv_flags & IEEE80211_F_WPA2)
frm = ieee80211_setup_rsn_ie(vap, frm);
if (vap->iv_flags & IEEE80211_F_WPA1)
frm = ieee80211_setup_wpa_ie(vap, frm);
return frm;
}
/*
* Add a mobility domain element to a frame.
*/
uint8_t *
ieee80211_add_mdie(uint8_t *frm, struct ieee80211vap *vap)
{
struct ieee80211_md_ie *mdie = (struct ieee80211_md_ie *) frm;
mdie->md_id = IEEE80211_ELEMID_MOBILITY_DOMAIN;
mdie->md_len = IEEE80211_MDIE_LEN;
mdie->md_info = vap->iv_mdid;
mdie->md_cap = vap->iv_ft_over_ds ? 1 : 0;
frm += sizeof(struct ieee80211_md_ie);
return frm;
}
#define WME_OUI_BYTES 0x00, 0x50, 0xf2
/*
* Add a WME Info element to a frame.
*/
static u_int8_t *
ieee80211_add_wme(u_int8_t *frm, struct ieee80211_node *ni)
{
static const u_int8_t oui[4] = { WME_OUI_BYTES, WME_OUI_TYPE };
struct ieee80211_ie_wme *ie = (struct ieee80211_ie_wme *) frm;
struct ieee80211_wme_state *wme = &ni->ni_ic->ic_wme;
struct ieee80211vap *vap = ni->ni_vap;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* length filled in below */
memcpy(frm, oui, sizeof(oui)); /* WME OUI */
frm += sizeof(oui);
*frm++ = WME_INFO_OUI_SUBTYPE; /* OUI subtype */
*frm++ = WME_VERSION; /* protocol version */
/* QoS Info field depends on operating mode */
switch (vap->iv_opmode) {
case IEEE80211_M_HOSTAP:
*frm = wme->wme_bssChanParams.cap_info_count;
if (IEEE80211_VAP_UAPSD_ENABLED(vap))
*frm |= WME_CAPINFO_UAPSD_EN;
frm++;
break;
case IEEE80211_M_STA:
*frm++ = vap->iv_uapsdinfo;
break;
default:
*frm++ = 0;
}
ie->wme_len = frm - &ie->wme_oui[0];
return frm;
}
/*
* Add a WME Parameter element to a frame.
*/
u_int8_t *
ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme,
int uapsd_enable, int is_qtn_wme)
{
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
static const u_int8_t oui[4] = { WME_OUI_BYTES, WME_OUI_TYPE };
struct ieee80211_wme_param *ie = (struct ieee80211_wme_param *) frm;
int i;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* length filled in below */
memcpy(frm, oui, sizeof(oui)); /* WME OUI */
frm += sizeof(oui);
*frm++ = WME_PARAM_OUI_SUBTYPE; /* OUI subtype */
*frm++ = WME_VERSION; /* protocol version */
*frm = wme->wme_bssChanParams.cap_info_count;
if (uapsd_enable)
*frm |= WME_CAPINFO_UAPSD_EN;
frm++;
*frm++ = 0; /* reserved field */
for (i = 0; i < WME_NUM_AC; i++) {
const struct wmm_params *ac;
#ifdef CONFIG_QVSP
if (!is_qtn_wme && (wme->wme_throt_bm & BIT(i))) {
ac = &wme->wme_throt_bssChanParams.cap_wmeParams[i];
} else
#endif
{
ac = &wme->wme_bssChanParams.cap_wmeParams[i];
}
*frm++ = SM(i, WME_PARAM_ACI) |
SM(ac->wmm_acm, WME_PARAM_ACM) |
SM(ac->wmm_aifsn, WME_PARAM_AIFSN);
*frm++ = SM(ac->wmm_logcwmax, WME_PARAM_LOGCWMAX) |
SM(ac->wmm_logcwmin, WME_PARAM_LOGCWMIN);
ADDSHORT(frm, ac->wmm_txopLimit);
}
ie->param_len = frm - &ie->param_oui[0];
return frm;
#undef ADDSHORT
}
#undef WME_OUI_BYTES
/*
* Add an Atheros Advanaced Capability element to a frame
*/
u_int8_t *
ieee80211_add_athAdvCap(u_int8_t *frm, u_int8_t capability, u_int16_t defaultKey)
{
static const u_int8_t oui[6] = {(ATH_OUI & 0xff), ((ATH_OUI >>8) & 0xff),
((ATH_OUI >> 16) & 0xff), ATH_OUI_TYPE,
ATH_OUI_SUBTYPE, ATH_OUI_VERSION};
struct ieee80211_ie_athAdvCap *ie = (struct ieee80211_ie_athAdvCap *) frm;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* Length filled in below */
memcpy(frm, oui, sizeof(oui)); /* Atheros OUI, type, subtype, and version for adv capabilities */
frm += sizeof(oui);
*frm++ = capability;
/* Setup default key index in little endian byte order */
*frm++ = (defaultKey & 0xff);
*frm++ = ((defaultKey >> 8) & 0xff);
ie->athAdvCap_len = frm - &ie->athAdvCap_oui[0];
return frm;
}
/*
* Add the Quantenna IE to a frame
* - all existing fields must be backwards compatible with previous verions.
*/
uint8_t *
ieee80211_add_qtn_ie(uint8_t *frm, struct ieee80211com *ic, uint8_t flags, uint8_t my_flags,
uint8_t implicit_ba, uint16_t implicit_ba_size, uint32_t rate_train)
{
struct ieee80211_ie_qtn *ie = (struct ieee80211_ie_qtn *)frm;
ie->qtn_ie_id = IEEE80211_ELEMID_VENDOR;
ie->qtn_ie_len = (uint8_t)sizeof(*ie) - IEEE80211_IE_ID_LEN_SIZE;
ieee80211_oui_add_qtn(ie->qtn_ie_oui);
ie->qtn_ie_type = QTN_OUI_CFG;
ie->qtn_ie_flags = (flags | IEEE80211_QTN_FLAGS_ENVY_DFLT) & IEEE80211_QTN_FLAGS_ENVY;
ie->qtn_ie_implicit_ba_tid = implicit_ba;
ie->qtn_ie_my_flags = (my_flags | IEEE80211_QTN_CAPS_DFLT) & ~IEEE80211_QTN_BF_VER1;
ie->qtn_ie_implicit_ba_tid_h = implicit_ba;
ie->qtn_ie_implicit_ba_size = (implicit_ba_size >> IEEE80211_QTN_IE_BA_SIZE_SH);
ie->qtn_ie_vsp_version = IEEE80211_QTN_VSP_VERSION;
put_unaligned(htonl(ic->ic_ver_sw), &ie->qtn_ie_ver_sw);
put_unaligned(htons(ic->ic_ver_hw), &ie->qtn_ie_ver_hw);
put_unaligned(htons(ic->ic_ver_platform_id), &ie->qtn_ie_ver_platform_id);
put_unaligned(htonl(ic->ic_ver_timestamp), &ie->qtn_ie_ver_timestamp);
put_unaligned(htonl(rate_train), &ie->qtn_ie_rate_train);
put_unaligned(htonl(ic->ic_ver_flags), &ie->qtn_ie_ver_flags);
return frm + sizeof(*ie);
}
#ifdef CONFIG_QVSP
static __inline int
ieee80211_vsp_ie_max_len(struct ieee80211com *ic)
{
return sizeof(struct ieee80211_ie_vsp) +
(sizeof(struct ieee80211_ie_vsp_item) * ARRAY_SIZE(ic->vsp_cfg));
}
/*
* Add the Quantenna VSP configuration IE to a frame
*/
static uint8_t *
ieee80211_add_vsp_ie(struct ieee80211vap *vap, void *start, void *end)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_vsp *vsp_ie = start;
struct ieee80211_ie_vsp_item *item_p = &vsp_ie->item[0];
int i;
vsp_ie->id = IEEE80211_ELEMID_VENDOR;
ieee80211_oui_add_qtn(vsp_ie->oui);
vsp_ie->type = QTN_OUI_VSP_CTRL;
vsp_ie->item_cnt = 0;
for (i = 0; i < ARRAY_SIZE(ic->vsp_cfg); i++) {
if (ic->vsp_cfg[i].set != 0) {
item_p->index = i;
put_unaligned(htonl(ic->vsp_cfg[i].value), &item_p->value);
item_p++;
vsp_ie->item_cnt++;
if ((void *)item_p > end) {
printk(KERN_INFO "VSP: not adding IE to assoc resp - too long\n");
return start;
}
}
}
vsp_ie->len = (uint8_t *)item_p - &vsp_ie->oui[0];
return (uint8_t *)item_p;
}
static uint8_t *
ieee80211_add_timeout_ie(u_int8_t *frm)
{
struct ieee80211_timout_int_ie *tie = (struct ieee80211_timout_int_ie *) frm;
tie->timout_int_ie = IEEE80211_ELEMID_TIMEOUT_INT;
tie->timout_int_len = 5;
tie->timout_int_type = IEEE80211_TIMEOUT_ASSOC_COMEBACK; /* timeout value type */
tie->timout_int_value = htole32(IEEE80211_W_ASSOC_COMEBACK_TO); /* default value is 1000tus */
frm += sizeof(struct ieee80211_timout_int_ie);
return frm;
}
uint8_t *
ieee80211_add_qtn_wme_param(struct ieee80211vap *vap, u_int8_t *frm)
{
struct ieee80211_ie_qtn_wme *qwme_ie = (struct ieee80211_ie_qtn_wme *)frm;
struct ieee80211_wme_state *wme = ieee80211_vap_get_wmestate(vap);
qwme_ie->qtn_ie_id = IEEE80211_ELEMID_VENDOR;
qwme_ie->qtn_ie_len = sizeof(struct ieee80211_ie_qtn_wme) - 2;
ieee80211_oui_add_qtn(qwme_ie->qtn_ie_oui);
qwme_ie->qtn_ie_type = QTN_OUI_QWME;
qwme_ie->qtn_wme_ie_version = QTN_QWME_IE_VERSION;
return ieee80211_add_wme_param((uint8_t*)&qwme_ie->qtn_wme_ie, wme, IEEE80211_VAP_UAPSD_ENABLED(vap), 1);
}
#endif
/*
* Add Quantenna pairing hash to a frame
*/
u_int8_t *
ieee80211_add_qtn_pairing_ie(u_int8_t *frm, struct ieee80211_app_ie_t *pairing_ie)
{
struct ieee80211_ie_qtn_pairing_tlv tlv_ie;
struct ieee80211_ie_qtn_pairing *ie = (struct ieee80211_ie_qtn_pairing *) frm;
tlv_ie.qtn_pairing_tlv_type = QTN_OUI_PAIRING;
memcpy(tlv_ie.qtn_pairing_tlv_hash, pairing_ie->ie, QTN_PAIRING_TLV_HASH_LEN);
tlv_ie.qtn_pairing_tlv_len = htole16(sizeof(struct ieee80211_ie_qtn_pairing_tlv));
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0;
frm += ieee80211_oui_add_qtn(frm);
memcpy(frm, &tlv_ie, sizeof(struct ieee80211_ie_qtn_pairing_tlv));
frm += sizeof(struct ieee80211_ie_qtn_pairing_tlv);
ie->qtn_pairing_ie_len = frm - &ie->qtn_pairing_ie_oui[0];
return frm;
}
/*
* Add Quantenna specific 802.11h information elements to a frame.
*/
u_int8_t *
ieee80211_add_qtn_csatsf_ie(u_int8_t *frm, u_int64_t tsf)
{
struct ieee80211_ie_qtn_csa_tsf *ie = (struct ieee80211_ie_qtn_csa_tsf *)frm;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* Length is filled in below */
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_CFG;
ie->tsf = htonll(tsf);
frm += sizeof(tsf);
ie->len = frm - &ie->qtn_ie_oui[0];
return frm;
}
/*
* Add 802.11h information elements to a frame.
*/
static u_int8_t *
ieee80211_add_doth(u_int8_t *frm, struct ieee80211com *ic)
{
/* XXX ie structures */
/*
* Power Capability IE
*/
if (ic->ic_flags_ext & IEEE80211_FEXT_TPC) {
*frm++ = IEEE80211_ELEMID_PWRCAP;
*frm++ = 2;
*frm++ = ic->ic_bsschan->ic_minpower;
*frm++ = ic->ic_bsschan->ic_maxpower;
}
return frm;
}
/*
* Add 802.11n HT MCS
*/
static void
ieee80211_mcs_populate(struct ieee80211_node *ni, struct ieee80211_ie_htcap *ie, struct ieee80211_htcap *htcap, int subtype) {
/* Update the supported MCS on Assoc response based on intersection of AP and client capability */
if ((ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP) &&
(subtype == IEEE80211_FC0_SUBTYPE_ASSOC_RESP || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)) {
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS1,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS1] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS1]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS2,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS2] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS2]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS3,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS3] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS3]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS4,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS4] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_NSS4]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM1,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM2,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM2] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM2]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM3,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM3] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM3]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM4,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM4] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM4]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM5,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM5] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM5]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM6,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM6] & ni->ni_htcap.mcsset[IEEE80211_HT_MCSSET_20_40_UEQM6]);
} else {
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS1,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS1]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS2,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS2]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS3,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS3]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_NSS4,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_NSS4]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM1,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM1]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM2,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM2]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM3,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM3]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM4,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM4]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM5,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM5]);
IEEE80211_HTCAP_SET_MCS_VALUE(ie, IEEE80211_HT_MCSSET_20_40_UEQM6,
htcap->mcsset[IEEE80211_HT_MCSSET_20_40_UEQM6]);
}
}
/*
* Add 802.11n HT Capabilities IE
*/
u_int8_t *
ieee80211_add_htcap(struct ieee80211_node *ni, u_int8_t *frm, struct ieee80211_htcap *htcap, int subtype)
{
struct ieee80211_ie_htcap *ie = (struct ieee80211_ie_htcap *)(void*) frm;
memset(ie, 0, sizeof(struct ieee80211_ie_htcap));
ie->hc_id = IEEE80211_ELEMID_HTCAP;
ie->hc_len = sizeof(struct ieee80211_ie_htcap) - 2;
/* Update the LDPC capability based on the setting */
if (ni->ni_vap->iv_ht_flags & IEEE80211_HTF_LDPC_ENABLED) {
htcap->cap |= IEEE80211_HTCAP_C_LDPCCODING;
} else {
htcap->cap &= ~IEEE80211_HTCAP_C_LDPCCODING;
}
/* Update the STBC capability based on the setting */
if (ni->ni_vap->iv_ht_flags & IEEE80211_HTF_STBC_ENABLED) {
htcap->cap |= (IEEE80211_HTCAP_C_TXSTBC | IEEE80211_HTCAP_C_RXSTBC);
} else {
htcap->cap &= ~(IEEE80211_HTCAP_C_TXSTBC | IEEE80211_HTCAP_C_RXSTBC);
}
IEEE80211_HTCAP_SET_CAPABILITIES(ie,htcap->cap);
if (ni->ni_vap->iv_smps_force & 0x8000) {
IEEE80211_HTCAP_SET_PWRSAVE_MODE(ie, ni->ni_vap->iv_smps_force & 0xF);
} else {
IEEE80211_HTCAP_SET_PWRSAVE_MODE(ie,htcap->pwrsave);
}
IEEE80211_HTCAP_SET_AMPDU_LEN(ie,htcap->maxampdu);
IEEE80211_HTCAP_SET_AMPDU_SPACING(ie,htcap->mpduspacing);
ieee80211_mcs_populate(ni, ie, htcap, subtype);
IEEE80211_HTCAP_SET_HIGHEST_DATA_RATE(ie,htcap->maxdatarate);
IEEE80211_HTCAP_SET_MCS_PARAMS(ie,htcap->mcsparams);
IEEE80211_HTCAP_SET_MCS_STREAMS(ie,htcap->numtxspstr);
ie->hc_txbf[0] = htcap->hc_txbf[0];
ie->hc_txbf[1] = htcap->hc_txbf[1];
ie->hc_txbf[2] = htcap->hc_txbf[2];
ie->hc_txbf[3] = htcap->hc_txbf[3];
return frm + sizeof(struct ieee80211_ie_htcap);
}
/*
* Add 802.11n HT Information IE
*/
u_int8_t *
ieee80211_add_htinfo(struct ieee80211_node *ni, u_int8_t *frm, struct ieee80211_htinfo *htinfo)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = vap->iv_ic; /* back ptr to common state */
u_int8_t byteval = 0;
struct ieee80211_ie_htinfo *ie = (struct ieee80211_ie_htinfo *)(void*) frm;
memset(ie, 0, sizeof(struct ieee80211_ie_htinfo));
ie->hi_id = IEEE80211_ELEMID_HTINFO;
ie->hi_len = sizeof(struct ieee80211_ie_htinfo) - 2;
IEEE80211_HTINFO_SET_PRIMARY_CHANNEL(ie,htinfo->ctrlchannel);
/* set byte 1 */
byteval = 0;
/* set channel width */
byteval |= (htinfo->byte1 & IEEE80211_HTINFO_B1_REC_TXCHWIDTH_40);
/*
* Std 802.11ac-2013, 10.39.1 'Basic VHT BSS functionality': A VHT
* AP shall set the RIFS Mode field in the HT Operation element to 0.
*/
if (!IS_IEEE80211_VHT_ENABLED(ic) ||
(vap->iv_opmode != IEEE80211_M_HOSTAP)) {
/* Rx RIFS is supported */
byteval |= IEEE80211_HTINFO_B1_RIFS_MODE;
}
/* set S-PSMP support */
/* Deprecated in current draft 11.0 */
// byteval |= (htinfo->byte1 & IEEE80211_HTINFO_B1_CONTROLLED_ACCESS);
IEEE80211_HTINFO_SET_BYTE_ONE(ie,byteval);
/* set service level granularity and secondary channel offset */
/* Deprecated in current draft 11.0 */
//IEEE80211_HTINFO_B1_SET_SIGRANULARITY(ie,htinfo->sigranularity);
//
IEEE80211_HTINFO_B1_SET_EXT_CHOFFSET(ie,htinfo->choffset);
/* set byte 2 */
byteval = 0;
/* set op mode */
if (IEEE80211_11N_PROTECT_ENABLED(ic) &&
(vap->iv_opmode != IEEE80211_M_IBSS)) {
if (vap->iv_non_gf_sta_present)
byteval |= IEEE80211_HTINFO_B2_NON_GF_PRESENT;
if (ic->ic_non_ht_non_member || ic->ic_non_ht_sta)
byteval |= IEEE80211_HTINFO_B2_OBSS_PROT;
}
/* set OBSS */
IEEE80211_HTINFO_SET_BYTE_TWO(ie,byteval);
if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
u_int8_t opmode = 0;
if (ic->ic_non_ht_sta != 0) {
opmode = IEEE80211_HTINFO_OPMODE_HT_PROT_MIXED;
} else {
if (ic->ic_non_ht_non_member != 0) {
opmode = IEEE80211_HTINFO_OPMODE_HT_PROT_NON_MEM;
} else {
if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { /* 20/40 MHZ mode */
if (ic->ic_ht_20mhz_only_sta != 0) /* 20 MHZ only HT STA is present */
opmode = IEEE80211_HTINFO_OPMODE_HT_PROT_20_ONLY;
else
opmode = IEEE80211_HTINFO_OPMODE_NO_PROT;
} else {
opmode = IEEE80211_HTINFO_OPMODE_NO_PROT;
}
}
}
/*
* If nonHT, 20MHz, 'nonHT in other BSS' stations counts are all 0, then we have
* a QTN specific usage of HT protection field. If any of those counters are non-zero,
* then ht protection field is set as per standard. Otherwise WFA test cases fail.
*/
if (!ic->ic_non_ht_sta && !ic->ic_ht_20mhz_only_sta && !ic->ic_non_ht_non_member) {
/* QTN specific settings */
if ((!IEEE80211_COM_WDS_IS_RBS(ic) || !ic->ic_extender_mbs_ocac) && !ic->ic_peer_rts) {
opmode = IEEE80211_HTINFO_OPMODE_NO_PROT;
} else {
opmode = IEEE80211_HTINFO_OPMODE_HT_PROT_NON_MEM;
}
}
if (!IEEE80211_11N_PROTECT_ENABLED(ic))
opmode = IEEE80211_HTINFO_OPMODE_NO_PROT;
htinfo->opmode = opmode;
IEEE80211_HTINFO_B2_SET_OP_MODE(ie, htinfo->opmode);
}
/* set byte 3 */
IEEE80211_HTINFO_SET_BYTE_THREE(ie,0);
/* set byte 4 */
byteval = 0;
if (vap->iv_opmode != IEEE80211_M_IBSS)
{
/* set dual beacon */
byteval |= (htinfo->byte4 & IEEE80211_HTINFO_B4_DUAL_BEACON);
/* set DUAL CTS requirement */
if (vap->iv_dual_cts_required)
byteval |= (IEEE80211_HTINFO_B4_DUAL_CTS);
}
IEEE80211_HTINFO_SET_BYTE_FOUR(ie,byteval);
/* set byte 5 */
byteval = 0;
if (vap->iv_opmode != IEEE80211_M_IBSS) {
/* set STBC beacon */
if (vap->iv_stbc_beacon)
byteval |= (IEEE80211_HTINFO_B5_STBC_BEACON);
/* set LSIG TXOP support */
if (vap->iv_lsig_txop_ok)
byteval |= (IEEE80211_HTINFO_B5_LSIGTXOPPROT);
}
IEEE80211_HTINFO_SET_BYTE_FIVE(ie,byteval);
IEEE80211_HTINFO_SET_BASIC_MCS_VALUE(ie,IEEE80211_HT_MCSSET_20_40_NSS1,htinfo->basicmcsset[0]);
IEEE80211_HTINFO_SET_BASIC_MCS_VALUE(ie,IEEE80211_HT_MCSSET_20_40_NSS2,htinfo->basicmcsset[1]);
return frm + sizeof(struct ieee80211_ie_htinfo);
}
/*
* Add 802.11ac VHT Capabilities IE
*/
u_int8_t *
ieee80211_add_vhtcap(struct ieee80211_node *ni, u_int8_t *frm, struct ieee80211_vhtcap *vhtcap, uint8_t subtype)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_vhtcap *ie = (struct ieee80211_ie_vhtcap *)(void *) frm;
u_int32_t vhtcap_flags = vhtcap->cap_flags;
uint32_t bfstscap;
memset(ie, 0, sizeof(struct ieee80211_ie_vhtcap));
ie->vht_id = IEEE80211_ELEMID_VHTCAP;
ie->vht_len = sizeof(struct ieee80211_ie_vhtcap) - 2;
if (vap->iv_vht_flags & IEEE80211_VHTCAP_C_RX_LDPC) {
vhtcap_flags |= IEEE80211_VHTCAP_C_RX_LDPC;
} else {
vhtcap_flags &= ~IEEE80211_VHTCAP_C_RX_LDPC;
}
if (vap->iv_vht_flags & IEEE80211_VHTCAP_C_TX_STBC) {
vhtcap_flags |= IEEE80211_VHTCAP_C_TX_STBC;
} else {
vhtcap_flags &= ~IEEE80211_VHTCAP_C_TX_STBC;
}
if (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_MU_BEAM_FORMER_CAP) {
vhtcap_flags |= IEEE80211_VHTCAP_C_MU_BEAM_FORMER_CAP;
} else {
vhtcap_flags &= ~IEEE80211_VHTCAP_C_MU_BEAM_FORMER_CAP;
}
if (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_MU_BEAM_FORMEE_CAP) {
vhtcap_flags |= IEEE80211_VHTCAP_C_MU_BEAM_FORMEE_CAP;
} else {
vhtcap_flags &= ~IEEE80211_VHTCAP_C_MU_BEAM_FORMEE_CAP;
}
IEEE80211_VHTCAP_SET_CAPFLAGS(ie, vhtcap_flags);
IEEE80211_VHTCAP_SET_MAXMPDU(ie, vhtcap->maxmpdu);
IEEE80211_VHTCAP_SET_CHANWIDTH(ie, vhtcap->chanwidth);
if (ni->ni_vap->iv_vht_flags & IEEE80211_VHTCAP_C_TX_STBC) {
IEEE80211_VHTCAP_SET_RXSTBC(ie, vhtcap->rxstbc);
}
/* FIXME: IOT Workaround for BRCM to set beamformee STS to 3 in beacons
* BRCM sound with us assuming 2 antenna when STS is set to our max value which is 4
* However when beacons advertise it as 3, the sounding is done in 3 antenna.
* Intel, QCA, MRVL takes the STS value from probe resp / assoc resp.
* So having STS 4 in those frames can make use of 4 antenna in V matrix.
*/
bfstscap = ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) && (vap->enable_iot_sts_war))
? MIN(vhtcap->bfstscap, IEEE80211_VHTCAP_RX_STS_3)
: vhtcap->bfstscap;
IEEE80211_VHTCAP_SET_BFSTSCAP(ie, bfstscap);
IEEE80211_VHTCAP_SET_NUMSOUND(ie, vhtcap->numsounding);
IEEE80211_VHTCAP_SET_MAXAMPDUEXP(ie, vhtcap->maxampduexp);
IEEE80211_VHTCAP_SET_LNKADPTCAP(ie, vhtcap->lnkadptcap);
IEEE80211_VHTCAP_SET_RX_MCS_NSS(ie, vhtcap->rxmcsmap);
IEEE80211_VHTCAP_SET_TX_MCS_NSS(ie, vhtcap->txmcsmap);
IEEE80211_VHTCAP_SET_RX_LGIMAXRATE(ie, vhtcap->rxlgimaxrate);
IEEE80211_VHTCAP_SET_TX_LGIMAXRATE(ie, vhtcap->txlgimaxrate);
return frm + sizeof(struct ieee80211_ie_vhtcap);
}
/*
* 802.11ac VHT Operation IE
*/
uint8_t *ieee80211_add_vhtop(struct ieee80211_node *ni, uint8_t *frm, struct ieee80211_vhtop *vhtop)
{
struct ieee80211_ie_vhtop *ie = (struct ieee80211_ie_vhtop *)frm;
memset (ie, 0, sizeof(struct ieee80211_ie_vhtop));
ie->vhtop_id = IEEE80211_ELEMID_VHTOP;
ie->vhtop_len = sizeof(struct ieee80211_ie_vhtop) - 2;
IEEE80211_VHTOP_SET_CHANWIDTH(ie, vhtop->chanwidth);
IEEE80211_VHTOP_SET_CENTERFREQ0(ie, vhtop->centerfreq0);
IEEE80211_VHTOP_SET_CENTERFREQ1(ie, vhtop->centerfreq1);
IEEE80211_VHTOP_SET_BASIC_MCS_NSS(ie, vhtop->basicvhtmcsnssset);
return frm + sizeof(struct ieee80211_ie_vhtop);
}
u_int8_t ieee80211_get_peer_nss(struct ieee80211_node *ni)
{
u_int8_t nss = 0;
if (IEEE80211_VHT_HAS_4SS(ni->ni_vhtcap.txmcsmap)) {
nss = 3;
} else if (IEEE80211_VHT_HAS_3SS(ni->ni_vhtcap.txmcsmap)) {
nss = 2;
} else if (IEEE80211_VHT_HAS_2SS(ni->ni_vhtcap.txmcsmap)) {
nss = 1;
}
return nss;
}
/*
* Add 802.11ac VHT Operating Mode Notification IE
*/
uint8_t *ieee80211_add_vhtop_notif(struct ieee80211_node *ni, uint8_t *frm, struct ieee80211com *ic, int band_24g)
{
struct ieee80211_ie_vhtop_notif *ie = (struct ieee80211_ie_vhtop_notif *)frm;
uint8_t chwidth;
uint8_t vht_nss_cap = (band_24g ? ic->ic_vht_nss_cap_24g : ic->ic_vht_nss_cap);
uint8_t rxnss = min(vht_nss_cap - 1, QTN_GLOBAL_RATE_NSS_MAX - 1);
uint8_t rxnss_type = 0;
struct ieee80211vap *vap = ni->ni_vap;
switch (ic->ic_max_system_bw) {
case BW_HT40:
chwidth = IEEE80211_CWM_WIDTH40;
break;
case BW_HT20:
chwidth = IEEE80211_CWM_WIDTH20;
break;
case BW_HT80:
default:
chwidth = IEEE80211_CWM_WIDTH80;
break;
}
if (band_24g && chwidth == IEEE80211_CWM_WIDTH80)
chwidth = IEEE80211_CWM_WIDTH40;
memset(ie, 0, sizeof(struct ieee80211_ie_vhtop_notif));
ie->id = IEEE80211_ELEMID_OPMOD_NOTIF;
ie->len = sizeof(*ie) - 2;
if (vap->iv_opmode == IEEE80211_M_STA) {
rxnss = min((uint8_t)(vht_nss_cap - 1), (uint8_t)ieee80211_get_peer_nss(ni));
}
if (ic->ic_vht_opmode_notif == IEEE80211_VHT_OPMODE_NOTIF_DEFAULT) {
/* by default, use current configured channel width and nss */
ie->vhtop_notif_mode = SM(chwidth, IEEE80211_VHT_OPMODE_CHWIDTH) |
SM(rxnss, IEEE80211_VHT_OPMODE_RXNSS) |
SM(rxnss_type, IEEE80211_VHT_OPMODE_RXNSS_TYPE);
} else {
ie->vhtop_notif_mode = (uint8_t) (ic->ic_vht_opmode_notif & 0x00ff);
}
return frm + sizeof(*ie);
}
/*
* Add 20/40 coexistence IE
*/
u_int8_t *
ieee80211_add_20_40_bss_coex_ie(u_int8_t *frm, u_int8_t coex)
{
*frm++ = IEEE80211_ELEMID_20_40_BSS_COEX;
*frm++ = 1;
*frm++ = coex;
return frm;
}
void
ieee80211_get_20_40_bss_into_chan_list(struct ieee80211com *ic,
struct ieee80211vap *vap, u_int16_t *pp_ch_list)
{
struct sta_table *st = ic->ic_scan->ss_priv;
struct sta_entry *se, *next;
uint16_t ch_list = 0;
uint8_t se_pri_chan = 0;
uint8_t se_sec_chan = 0;
uint8_t unallowed = 0;
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
if (!IEEE80211_ADDR_EQ(se->base.se_macaddr, vap->iv_bss->ni_macaddr) &&
(se->base.se_chan->ic_ieee <= QTN_2G_LAST_OPERATING_CHAN)) {
ieee80211_find_ht_pri_sec_chan(vap, &se->base,
&se_pri_chan, &se_sec_chan);
unallowed = !ieee80211_20_40_operation_permitted(ic,
ic->ic_curchan, se_pri_chan, se_sec_chan);
} else {
unallowed = 0;
}
if (unallowed)
ch_list |= 1 << se->base.se_chan->ic_ieee;
}
*pp_ch_list = ch_list;
}
static uint8_t
ieee80211_count_channels(uint16_t ch_list)
{
uint8_t chan_count = 0;
for ( ; ch_list; ch_list &= (ch_list - 1))
chan_count++;
return chan_count;
}
/*
* Add 20/40 BSS channel report
*/
u_int8_t *
ieee80211_add_20_40_bss_into_ch_rep(u_int8_t *frm, struct ieee80211com *ic, u_int16_t ch_list)
{
#define IEEE80211_24GHZ_BAND 25
#define IEEE80211_GLOBAL_24GHZ_OPER_CLASS 81
#define bitsz_var(var) (sizeof(var) * 8)
int i;
uint8_t cur_reg_class = 0;
for (i = 1; i < bitsz_var(ch_list); i++) {
if (ch_list & (1 << i)) {
cur_reg_class = ieee80211_get_current_operating_class(ic->ic_country_code,
ic->ic_bsschan->ic_ieee,
IEEE80211_24GHZ_BAND);
if (!cur_reg_class)
cur_reg_class = IEEE80211_GLOBAL_24GHZ_OPER_CLASS;
break;
}
}
*frm++ = IEEE80211_ELEMID_20_40_IT_CH_REP;
*frm++ = ieee80211_count_channels(ch_list) + 1;
*frm++ = cur_reg_class;
for (i = 1; i < bitsz_var(ch_list); i++) {
if (ch_list & (1 << i)) {
*frm++ = i;
}
}
return frm;
}
u_int8_t *
ieee80211_add_obss_scan_ie(u_int8_t *frm, struct ieee80211_obss_scan_ie *obss_ie)
{
*frm++ = IEEE80211_ELEMID_OBSS_SCAN;
*frm++ = sizeof(struct ieee80211_obss_scan_ie) - 2;
memcpy(frm, &obss_ie->obss_passive_dwell, sizeof(struct ieee80211_obss_scan_ie) - 2);
return (frm + sizeof(struct ieee80211_obss_scan_ie) - 2);
}
/*
* Add Extender Role IE
*/
u_int8_t *
ieee80211_add_qtn_extender_role_ie(uint8_t *frm, uint8_t role)
{
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = sizeof(struct ieee80211_qtn_ext_role) - 2;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_EXTENDER_ROLE;
*frm++ = role;
return frm;
}
u_int8_t *
ieee80211_add_qtn_extender_bssid_ie(struct ieee80211vap *vap, uint8_t *frm)
{
struct ieee80211com *ic = vap->iv_ic;
int i;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = sizeof(struct ieee80211_qtn_ext_bssid) - 2;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_EXTENDER_BSSID;
memcpy(frm, ic->ic_extender_mbs_bssid, IEEE80211_ADDR_LEN);
frm = frm + IEEE80211_ADDR_LEN;
*frm++ = ic->ic_extender_rbs_num;
for (i = 0; i < QTN_MAX_RBS_NUM; i++) {
memcpy(frm, ic->ic_extender_rbs_bssid[i], IEEE80211_ADDR_LEN);
frm = frm + IEEE80211_ADDR_LEN;
}
return frm;
}
u_int8_t *
ieee80211_add_qtn_extender_state_ie(uint8_t *frm, uint8_t ocac)
{
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = sizeof(struct ieee80211_qtn_ext_state) - 2;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_EXTENDER_STATE;
*frm++ = (ocac ? QTN_EXT_MBS_OCAC : 0);
*frm++ = 0;
*frm++ = 0;
*frm++ = 0;
return frm;
}
u_int8_t *
ieee80211_add_qtn_ocac_state_ie(uint8_t *frm)
{
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = sizeof(struct ieee80211_ie_qtn_ocac_state) - 2;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_OCAC_STATE;
*frm++ = OCAC_STATE_NONE;
*frm++ = 0;
return frm;
}
/**
* Find the next IE in the buffer, return pointer to the next IE,
* and the length of the current IE.
*/
static void *
ieee80211_smash_ie(const void *p_buf, int buf_len, int *p_this_ie_len)
{
struct ieee80211_ie *p_ie = (struct ieee80211_ie *)p_buf;
struct ieee80211_ie *p_next_ie;
if (p_buf == NULL || buf_len < IEEE80211_IE_ID_LEN_SIZE) {
return NULL;
}
if ((p_ie->len + IEEE80211_IE_ID_LEN_SIZE) > buf_len) {
return NULL;
}
*p_this_ie_len = p_ie->len;
p_next_ie = (struct ieee80211_ie *)(p_buf + p_ie->len + IEEE80211_IE_ID_LEN_SIZE);
if ((u32)p_next_ie < (u32)(p_buf + buf_len)) {
return p_next_ie;
}
return NULL;
}
/*
* Create a probe request frame with the specified ssid
* and any optional information element data.
*/
struct sk_buff *
ieee80211_get_probereq(struct ieee80211_node *ni,
const u_int8_t sa[IEEE80211_ADDR_LEN],
const u_int8_t da[IEEE80211_ADDR_LEN],
const u_int8_t bssid[IEEE80211_ADDR_LEN],
const u_int8_t *ssid, size_t ssidlen,
const void *optie, size_t optielen)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
enum ieee80211_phymode mode;
struct ieee80211_frame *wh;
struct sk_buff *skb;
u_int8_t *frm;
mode = ic->ic_curmode;
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] HT capabilities
* [tlv] user-specified ie's
*/
skb = ieee80211_getmgtframe(&frm, 2 + IEEE80211_NWID_LEN +
2 + IEEE80211_RATE_SIZE +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) +
((ic->ic_curmode >= IEEE80211_MODE_11NA) ?
(sizeof(struct ieee80211_ie_htcap) +
sizeof(struct ieee80211_extcap_param)) : 0) +
(optie != NULL ? optielen : 0) +
vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_REQ].length +
(IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) ? sizeof(struct ieee80211_ie_vhtcap): 0)
);
if (skb == NULL) {
vap->iv_stats.is_tx_nobuf++;
return NULL;
}
frm = ieee80211_add_ssid(frm, ssid, ssidlen);
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
if (ic->ic_curmode >= IEEE80211_MODE_11NA) {
frm = ieee80211_add_htcap(ni, frm, &ic->ic_htcap, IEEE80211_FC0_SUBTYPE_PROBE_REQ);
/* Ext. Capabilities - For AP mode hostapd adds the extended cap */
if (vap->iv_opmode == IEEE80211_M_STA)
frm = ieee80211_add_extcap(frm);
}
frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
if (IS_IEEE80211_VHT_ENABLED(ic)) {
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap, IEEE80211_FC0_SUBTYPE_PROBE_REQ);
} else if (IS_IEEE80211_11NG_VHT_ENABLED(ic)) {
/* QTN 2.4G VHT IE */
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap_24g, IEEE80211_FC0_SUBTYPE_PROBE_REQ);
}
/* Only add in vendor IEs to the probe request frame. */
if (optie != NULL) {
struct ieee80211_ie *p_ie = (struct ieee80211_ie *)optie;
struct ieee80211_ie *p_next_ie;
int this_len = 0;
do {
p_next_ie = ieee80211_smash_ie(p_ie, optielen, &this_len);
if (p_ie && this_len) {
/**
* Rules for probe request is that vendor elements can be appended,
* and only a few of the standard IEs. optie as passed in can
* contain any one of a number of elements.
*/
if (p_ie->id == IEEE80211_ELEMID_VENDOR) {
if (((struct ieee80211_ie_wme *)p_ie)->wme_type == WPA_RSN_OUI_TYPE) {
continue;
}
memcpy(frm, p_ie, this_len + IEEE80211_IE_ID_LEN_SIZE);
frm += this_len + IEEE80211_IE_ID_LEN_SIZE;
}
p_ie = p_next_ie;
optielen -= this_len + IEEE80211_IE_ID_LEN_SIZE;
}
} while (p_next_ie != NULL);
}
if (vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_REQ].ie) {
memcpy(frm, vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_REQ].ie,
vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_REQ].length);
frm += vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_REQ].length;
}
skb_trim(skb, frm - skb->data);
wh = (struct ieee80211_frame *)
skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, wh,
IEEE80211_FC0_TYPE_MGT,
IEEE80211_FC0_SUBTYPE_PROBE_REQ,
sa, da, bssid);
/* FIXME power management? */
IEEE80211_NODE_STAT(ni, tx_probereq);
IEEE80211_NODE_STAT(ni, tx_mgmt);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send probe req on channel %u\n",
ether_sprintf(wh->i_addr1),
ieee80211_chan2ieee(ic, ic->ic_curchan));
return skb;
}
EXPORT_SYMBOL(ieee80211_get_probereq);
/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
int
ieee80211_send_probereq(struct ieee80211_node *ni,
const u_int8_t sa[IEEE80211_ADDR_LEN],
const u_int8_t da[IEEE80211_ADDR_LEN],
const u_int8_t bssid[IEEE80211_ADDR_LEN],
const u_int8_t *ssid, size_t ssidlen,
const void *optie, size_t optielen)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
ieee80211_ref_node(ni);
skb = ieee80211_get_probereq(ni, sa, da, bssid,
ssid, ssidlen, optie, optielen);
if (skb == NULL) {
ieee80211_free_node(ni);
return -ENOMEM;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send probe req frame on channel %u\n",
ether_sprintf(ni->ni_macaddr),
ieee80211_chan2ieee(ic, ic->ic_curchan));
ic->ic_send_80211(ic, ni, skb, WME_AC_BE, 0);
return 0;
}
/* Send a broadcast CSA frame, announcing the new channel. References are from
* IEEE 802.11h-2003. CSA frame format is an "Action" frame (Type: 00, Subtype:
* 1101, see 7.1.3.1.2)
*
* [1] Category : 0, Spectrum Management, 7.3.1.11
* [1] Action : 4, Channel Switch Announcement, 7.4.1 and 7.4.1.5
* [1] Element ID : 37, Channel Switch Announcement, 7.3.2
* [1] Length : 3, 7.3.2.20
* [1] Channel Switch Mode : 1, stop transmission immediately
* [1] New Channel Number
* [1] Channel Switch Count in TBTT : 0, immediate channel switch
*
* csa_mode : IEEE80211_CSA_CAN_STOP_TX / IEEE80211_CSA_MUST_STOP_TX
* csa_chan : new IEEE channel number
* csa_tbtt : TBTT until Channel Switch happens
*/
void
ieee80211_send_csa_frame(struct ieee80211vap *vap,
u_int8_t csa_mode,
u_int8_t csa_chan,
u_int8_t csa_count,
u_int64_t tsf)
{
struct ieee80211_node *ni = vap->iv_bss;
struct ieee80211com *ic = ni->ni_ic;
uint32_t bw = ieee80211_get_bw(ic);
uint8_t wband_chanswitch_ie_len = ieee80211_wband_chanswitch_ie_len(bw);
struct sk_buff *skb;
int frm_len;
u_int8_t *frm;
frm_len = IEEE80211_CSA_LEN + ieee80211_sec_chan_off_ie_len() +
wband_chanswitch_ie_len;
if (tsf != 0) {
frm_len += sizeof(struct ieee80211_ie_qtn_csa_tsf);
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Sending action frame with CSA IE: %u/%u/%u\n",
__func__, csa_mode, csa_chan, csa_count);
skb = ieee80211_getmgtframe(&frm, frm_len);
if (skb == NULL) {
IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
"%s: cannot get buf; size %u", __func__, frm_len);
vap->iv_stats.is_tx_nobuf++;
return;
}
*frm++ = IEEE80211_ACTION_CAT_SPEC_MGMT; /* Category */
*frm++ = IEEE80211_ACTION_S_CHANSWITCHANN; /* Spectrum Management */
frm = ieee80211_add_csa(frm, csa_mode, csa_chan, csa_count);
ieee80211_add_sec_chan_off(&frm, ic, csa_chan);
if (wband_chanswitch_ie_len) {
frm = ieee80211_add_wband_chanswitch(frm, ic);
}
if (tsf != 0) {
ieee80211_add_qtn_csatsf_ie(frm, tsf);
}
if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
ieee80211_ref_node(ni);
ieee80211_mgmt_output(ni, skb, IEEE80211_FC0_SUBTYPE_ACTION,
vap->iv_dev->broadcast);
} else {
/* STA mode - tell AP to change channel */
ieee80211_ref_node(ni);
ieee80211_mgmt_output(ni, skb, IEEE80211_FC0_SUBTYPE_ACTION,
ni->ni_bssid);
}
}
EXPORT_SYMBOL(ieee80211_send_csa_frame);
#ifdef CONFIG_QVSP
static int
ieee80211_compile_action_qvsp_frame(struct ieee80211vap *vap, struct ieee80211_qvsp_act *qvsp_a,
struct sk_buff **pp_skb)
{
struct sk_buff *skb = NULL;
switch (qvsp_a->type) {
case QVSP_ACTION_STRM_CTRL: {
struct ieee80211_qvsp_act_strm_ctrl *qvsp_asc =
(struct ieee80211_qvsp_act_strm_ctrl *)qvsp_a;
int total_len;
struct ieee80211_qvsp_act_strm_ctrl_s *qa;
struct ieee80211_qvsp_strm_id *qai;
struct ieee80211_qvsp_strm_id *qvsp_asc_i = &qvsp_asc->strm_items[0];
int i;
u_int8_t *frm;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACTION,
"VSP: constructing stream ctrl frame\n", 0);
if (qvsp_asc->count > IEEE8021_QVSP_MAX_ACT_ITEMS) {
printk(KERN_INFO "VSP: truncating strm ctrl frame - too long\n");
qvsp_asc->count = IEEE8021_QVSP_MAX_ACT_ITEMS;
}
total_len = sizeof(*qa) + (qvsp_asc->count * sizeof(*qai));
KASSERT(total_len <= IEEE80211_MTU_MAX, ("VSP: strm ctrl frame is too large"));
skb = ieee80211_getmgtframe(&frm, total_len);
if (!skb) {
return -ENOMEM;
}
/* Common header */
qa = (struct ieee80211_qvsp_act_strm_ctrl_s *)frm;
qai = &qa->strm_items[0];
qa->header.category = IEEE80211_ACTION_CAT_VENDOR;
ieee80211_oui_add_qtn(qa->header.oui);
qa->header.type = QVSP_ACTION_TYPE_VSP;
qa->header.action = qvsp_a->type;
qa->strm_state = qvsp_asc->strm_state;
qa->dis_attr.throt_policy = qvsp_asc->dis_attr.throt_policy;
qa->dis_attr.throt_rate = qvsp_asc->dis_attr.throt_rate;
qa->dis_attr.demote_rule = qvsp_asc->dis_attr.demote_rule;
qa->dis_attr.demote_state = qvsp_asc->dis_attr.demote_state;
qa->count = qvsp_asc->count;
/* Set state for one or more streams */
for (i = 0; i < qa->count; i++) {
*qai++ = *qvsp_asc_i++;
}
break;
}
case QVSP_ACTION_VSP_CTRL: {
struct ieee80211_qvsp_act_cfg *qvsp_ac = (struct ieee80211_qvsp_act_cfg *)qvsp_a;
int total_len;
struct ieee80211_qvsp_act_vsp_ctrl_s *qa;
struct ieee80211_qvsp_act_vsp_ctrl_item_s *qai;
struct ieee80211_qvsp_act_cfg_item *qvsp_ac_i = &qvsp_ac->cfg_items[0];
int i;
u_int8_t *frm;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACTION,
"VSP: constructing cfg frame\n", 0);
if (qvsp_ac->count > IEEE8021_QVSP_MAX_ACT_ITEMS) {
printk(KERN_INFO "VSP: truncating cfg frame - too long\n");
qvsp_ac->count = IEEE8021_QVSP_MAX_ACT_ITEMS;
}
total_len = sizeof(*qa) + (qvsp_ac->count * sizeof(*qai));
KASSERT(total_len <= IEEE80211_MTU_MAX, ("VSP: cfg frame is too large"));
skb = ieee80211_getmgtframe(&frm, total_len);
if (!skb) {
return -ENOMEM;
}
/* Common header */
qa = (struct ieee80211_qvsp_act_vsp_ctrl_s *)frm;
qai = &qa->ctrl_items[0];
qa->header.category = IEEE80211_ACTION_CAT_VENDOR;
ieee80211_oui_add_qtn(qa->header.oui);
qa->header.type = QVSP_ACTION_TYPE_VSP;
qa->header.action = qvsp_a->type;
qa->count = qvsp_ac->count;
/* Zero or more config index/value pairs. */
for (i = 0; i < qa->count; i++) {
qai->index = htonl(qvsp_ac_i->index);
qai->value = htonl(qvsp_ac_i->value);
qai++;
qvsp_ac_i++;
}
break;
}
default:
break;
}
if (skb) {
*pp_skb = skb;
return 0;
}
return -EINVAL;
}
#endif
static int
ieee80211_compile_action_20_40_coex_frame(struct ieee80211vap *vap, struct ieee80211_action_data *action_data,
struct sk_buff **pp_skb, struct ieee80211_node *ni)
{
struct sk_buff *skb;
int32_t frame_len = 0;
struct ieee80211com *ic = vap->iv_ic;
uint8_t *frm;
uint8_t *coex_value = (uint8_t *)action_data->params;
uint8_t coex = vap->iv_coex;
uint16_t ch_list = 0;
if (coex_value)
coex = *coex_value;
frame_len = sizeof(struct ieee80211_action) + sizeof(struct ieee80211_20_40_coex_param);
if (ic->ic_opmode == IEEE80211_M_STA) {
uint8_t chan_count;
ieee80211_get_20_40_bss_into_chan_list(ic, vap, &ch_list);
chan_count = ieee80211_count_channels(ch_list);
if (chan_count) {
frame_len += sizeof(struct ieee80211_20_40_in_ch_rep) + chan_count;
coex |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
}
}
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = action_data->cat;
*frm++ = action_data->action;
frm = ieee80211_add_20_40_bss_coex_ie(frm, coex);
if (ic->ic_opmode == IEEE80211_M_STA && ch_list) {
frm = ieee80211_add_20_40_bss_into_ch_rep(frm, ic, ch_list);
}
ni->ni_coex = 0;
skb_trim(skb, frm - skb->data);
if (skb) {
*pp_skb = skb;
}
return 0;
}
static int
ieee80211_compile_action_sa_query_frame(struct ieee80211vap *vap, struct ieee80211_action_data *action_data,
struct sk_buff **pp_skb){
struct sk_buff *skb;
int32_t frame_len = 0;
u_int8_t *frm;
uint16_t *tid = (uint16_t *)action_data->params;
frame_len = sizeof(struct ieee80211_action_sa_query);
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_SA_QUERY;
*frm++ = action_data->action;
ADDINT16(frm, *tid);
skb_trim(skb, frm - skb->data);
*pp_skb = skb;
return 0;
}
int32_t ieee80211_measure_request_ie_len(struct ieee80211_meas_request_ctrl *mrequest_ctrl)
{
int32_t meas_ie_len;
switch (mrequest_ctrl->meas_type) {
case IEEE80211_CCA_MEASTYPE_BASIC:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq);
break;
case IEEE80211_CCA_MEASTYPE_CCA:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq);
break;
case IEEE80211_CCA_MEASTYPE_RPI:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq);
break;
case IEEE80211_RM_MEASTYPE_STA:
{
int32_t cnt;
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_sta_stat);
if (mrequest_ctrl->u.sta_stats.sub_item == NULL) {
break;
}
se_head = (ieee80211_11k_sub_element_head *)mrequest_ctrl->u.sta_stats.sub_item;
SLIST_FOREACH(p_se, se_head, next) {
switch (p_se->sub_id) {
case IEEE80211_ELEMID_VENDOR:
{
struct stastats_subele_vendor *vendor;
u_int32_t flags;
meas_ie_len += sizeof(struct ieee80211_ie_qtn_rm_measure_sta);
vendor = (struct stastats_subele_vendor *)p_se->data;
flags = vendor->flags;
if (!IEEE80211_IS_ALL_SET(flags, RM_QTN_MAX)) {
meas_ie_len += 1;
for (cnt = RM_QTN_TX_STATS; cnt <= RM_QTN_MAX; cnt++) {
if (flags & (BIT(cnt)))
meas_ie_len += 2;
}
for (cnt = RM_QTN_CTRL_START; cnt <= RM_QTN_CTRL_END; cnt++) {
if (flags & (BIT(cnt)))
meas_ie_len += 2;
}
}
break;
}
default:
break;
}
}
break;
}
case IEEE80211_RM_MEASTYPE_QTN_CCA:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq);
break;
case IEEE80211_RM_MEASTYPE_CH_LOAD:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_chan_load);
break;
case IEEE80211_RM_MEASTYPE_NOISE:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_noise_his);
break;
case IEEE80211_RM_MEASTYPE_BEACON:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_beacon);
break;