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;
case IEEE80211_RM_MEASTYPE_FRAME:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_frame);
break;
case IEEE80211_RM_MEASTYPE_CATEGORY:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_trans_stream_cat);
break;
case IEEE80211_RM_MEASTYPE_MUL_DIAG:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ sizeof(struct ieee80211_ie_measreq_multicast_diag);
break;
default:
meas_ie_len = -1;
break;
}
return meas_ie_len;
}
int32_t ieee80211_measure_report_ie_len(struct ieee80211_meas_report_ctrl *mreport_ctrl)
{
int32_t meas_ie_len;
/* measurement report filed would not exist if any bit of measurement report is set */
switch (mreport_ctrl->meas_type) {
case IEEE80211_CCA_MEASTYPE_BASIC:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? sizeof(struct ieee80211_ie_measrep_basic) : 0);
break;
case IEEE80211_CCA_MEASTYPE_CCA:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? sizeof(struct ieee80211_ie_measrep_cca) : 0);
break;
case IEEE80211_CCA_MEASTYPE_RPI:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? sizeof(struct ieee80211_ie_measrep_rpi) : 0);
break;
case IEEE80211_RM_MEASTYPE_STA:
{
int32_t cnt;
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm);
if (mreport_ctrl->report_mode == 0) {
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
meas_ie_len += sizeof(struct ieee80211_ie_measrep_sta_stat);
if (0 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group0);
else if (1 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group1);
else if (1 < mreport_ctrl->u.sta_stats.group_id && mreport_ctrl->u.sta_stats.group_id < 10)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group2to9);
else if (10 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group10);
else if (11 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group11);
else if (12 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group12);
else if (13 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group13);
else if (14 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group14);
else if (15 == mreport_ctrl->u.sta_stats.group_id)
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group15);
else
meas_ie_len += sizeof(struct ieee80211_rm_sta_stats_group16);
if (mreport_ctrl->u.sta_stats.sub_item == NULL) {
break;
}
se_head = (ieee80211_11k_sub_element_head *)mreport_ctrl->u.sta_stats.sub_item;
/* optional sub element length */
SLIST_FOREACH(p_se, se_head, next) {
switch (p_se->sub_id) {
case IEEE80211_ELEMID_VENDOR:
{
struct stastats_subele_vendor *vendor = (struct stastats_subele_vendor *)p_se->data;
u_int32_t vendor_flags = vendor->flags;
meas_ie_len += sizeof(struct ieee80211_ie_qtn_rm_measure_sta);
if (IEEE80211_IS_ALL_SET(vendor_flags, RM_QTN_MAX)) {
meas_ie_len += sizeof(struct ieee80211_ie_qtn_rm_sta_all);
} else {
meas_ie_len++;
for (cnt = RM_QTN_TX_STATS; cnt <= RM_QTN_MAX; cnt++) {
if (vendor_flags & (BIT(cnt))) {
meas_ie_len += 2;
meas_ie_len += ieee80211_meas_sta_qtn_report_subtype_len[cnt];
}
}
for (cnt = RM_QTN_CTRL_START; cnt <= RM_QTN_CTRL_END; cnt++) {
if (vendor_flags & (BIT(cnt))) {
meas_ie_len += 2;
meas_ie_len += ieee80211_meas_sta_qtn_report_subtype_len[cnt];
}
}
}
break;
}
default:
break;
}
}
}
break;
}
case IEEE80211_RM_MEASTYPE_QTN_CCA:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ?
sizeof(struct cca_rm_rep_data) + sizeof(struct ieee80211_ie_qtn_scs) : 0) +
mreport_ctrl->u.qtn_cca.extra_ie_len;
break;
case IEEE80211_RM_MEASTYPE_CH_LOAD:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? (sizeof(struct ieee80211_ie_measrep_chan_load)) : (0));
break;
case IEEE80211_RM_MEASTYPE_NOISE:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? (sizeof(struct ieee80211_ie_measrep_noise_his)) : (0));
break;
case IEEE80211_RM_MEASTYPE_BEACON:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? (sizeof(struct ieee80211_ie_measrep_beacon)) : (0));
break;
case IEEE80211_RM_MEASTYPE_FRAME:
{
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm);
if (mreport_ctrl->report_mode == 0) {
meas_ie_len += sizeof(struct ieee80211_ie_measrep_frame);
se_head = (ieee80211_11k_sub_element_head *)mreport_ctrl->u.frame.sub_item;
SLIST_FOREACH(p_se, se_head, next) {
switch (p_se->sub_id) {
case IEEE80211_FRAME_REPORT_SUBELE_FRAME_COUNT_REPORT:
meas_ie_len += sizeof(struct ieee80211_subie_section_frame_entry);
break;
default:
break;
}
}
}
break;
}
case IEEE80211_RM_MEASTYPE_CATEGORY:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? (sizeof(struct ieee80211_ie_measrep_trans_stream_cat)) : (0));
break;
case IEEE80211_RM_MEASTYPE_MUL_DIAG:
meas_ie_len = sizeof(struct ieee80211_ie_measure_comm)
+ ((mreport_ctrl->report_mode == 0) ? (sizeof(struct ieee80211_ie_measrep_multicast_diag)) : (0));
break;
default:
meas_ie_len = -1;
break;
}
return meas_ie_len;
}
u_int8_t *ieee80211_measure_request_ie_generate(struct ieee80211_node *ni,
u_int8_t *frm,
struct ieee80211_meas_request_ctrl *mrequest_ctrl)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = vap->iv_ic;
u_int8_t *ele_len;
*frm++ = IEEE80211_ELEMID_MEASREQ;
ele_len = frm;
*frm++ = 0; /* will be filled when finished */
*frm++ = 1; /* measurement token */
*frm++ = 0; /* mode */
*frm++ = mrequest_ctrl->meas_type;
switch (mrequest_ctrl->meas_type) {
case IEEE80211_CCA_MEASTYPE_BASIC:
{
*frm++ = mrequest_ctrl->u.basic.channel;
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.basic.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.basic.start_tsf + 1));
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.basic.duration_ms));
break;
}
case IEEE80211_CCA_MEASTYPE_CCA:
{
*frm++ = mrequest_ctrl->u.cca.channel;
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.cca.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.cca.start_tsf + 1));
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.cca.duration_ms));
break;
}
case IEEE80211_CCA_MEASTYPE_RPI:
{
*frm++ = mrequest_ctrl->u.rpi.channel;
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.rpi.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mrequest_ctrl->u.cca.start_tsf + 1));
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.rpi.duration_ms));
break;
}
case IEEE80211_RM_MEASTYPE_STA:
{
u_int16_t random_interval;
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
memcpy(frm, ni->ni_macaddr, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
get_random_bytes(&random_interval, 1);
ADDINT16(frm, random_interval);
ADDINT16(frm, mrequest_ctrl->u.sta_stats.duration_tu);
*frm++ = mrequest_ctrl->u.sta_stats.group_id;
/* optional sub element */
if (mrequest_ctrl->u.sta_stats.sub_item != NULL) {
se_head = (ieee80211_11k_sub_element_head *)mrequest_ctrl->u.sta_stats.sub_item;
while (!SLIST_EMPTY(se_head)) {
p_se = SLIST_FIRST(se_head);
switch (p_se->sub_id) {
case IEEE80211_ELEMID_VENDOR:
{
struct stastats_subele_vendor *vendor = (struct stastats_subele_vendor *)p_se->data;
u_int8_t *vendor_ie_len;
if (vendor->flags & RM_QTN_MEASURE_MASK) {
*frm++ = IEEE80211_ELEMID_VENDOR;
vendor_ie_len = frm;
*frm++ = 0;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = ni->ni_rm_sta_seq++;
if (IEEE80211_IS_ALL_SET(vendor->flags, RM_QTN_MAX)) {
*frm++ = QTN_OUI_RM_ALL;
} else {
u_int8_t cnt, *p_tlv_cnt;
*frm++ = QTN_OUI_RM_SPCIAL;
p_tlv_cnt = frm;
*frm++ = 0;
for (cnt = RM_QTN_TX_STATS; cnt <= RM_QTN_MAX; cnt++) {
if (vendor->flags & (BIT(cnt))) {
*frm++ = cnt;
*frm++ = 0;
*p_tlv_cnt += 1;
}
}
for (cnt = RM_QTN_CTRL_START; cnt <= RM_QTN_CTRL_END; cnt++) {
if (vendor->flags & (BIT(cnt))) {
*frm++ = cnt;
*frm++ = 0;
*p_tlv_cnt += 1;
}
}
}
*vendor_ie_len = frm - vendor_ie_len - 1;
}
break;
}
default:
break;
}
SLIST_REMOVE_HEAD(se_head, next);
kfree(p_se);
}
}
break;
}
case IEEE80211_RM_MEASTYPE_QTN_CCA:
{
/* replace with real type */
*(frm - 1) = IEEE80211_CCA_MEASTYPE_CCA;
*frm++ = ic->ic_curchan->ic_ieee;
ADDINT32(frm, 0);
ADDINT32(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.qtn_cca.duration_tu));
break;
}
case IEEE80211_RM_MEASTYPE_CH_LOAD:
{
*frm++ = 0; /* TODO: operating class, figure out a correct mapping */
*frm++ = mrequest_ctrl->u.chan_load.channel;
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.chan_load.duration_ms));
break;
}
case IEEE80211_RM_MEASTYPE_NOISE:
{
*frm++ = 0; /* TODO: operating class, figure out a correct mapping */
*frm++ = mrequest_ctrl->u.noise_his.channel;
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.noise_his.duration_ms));
break;
}
case IEEE80211_RM_MEASTYPE_BEACON:
{
*frm++ = mrequest_ctrl->u.beacon.op_class;
*frm++ = mrequest_ctrl->u.beacon.channel;
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.beacon.duration_ms));
*frm++ = mrequest_ctrl->u.beacon.mode;
memcpy(frm, mrequest_ctrl->u.beacon.bssid, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
if (mrequest_ctrl->u.beacon.ssid_len) {
*frm++ = IEEE80211_ELEMID_SSID;
*frm++ = mrequest_ctrl->u.beacon.ssid_len;
memcpy(frm, mrequest_ctrl->u.beacon.ssid, mrequest_ctrl->u.beacon.ssid_len);
frm += mrequest_ctrl->u.beacon.ssid_len;
}
break;
}
case IEEE80211_RM_MEASTYPE_FRAME:
{
*frm++ = mrequest_ctrl->u.frame.op_class;
*frm++ = mrequest_ctrl->u.frame.channel;
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.frame.duration_ms));
*frm++ = mrequest_ctrl->u.frame.type;
memcpy(frm, mrequest_ctrl->u.frame.mac_address, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
break;
}
case IEEE80211_RM_MEASTYPE_CATEGORY:
{
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.tran_stream_cat.duration_ms));
memcpy(frm, mrequest_ctrl->u.tran_stream_cat.peer_sta, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
*frm++ = mrequest_ctrl->u.tran_stream_cat.tid;
*frm++ = mrequest_ctrl->u.tran_stream_cat.bin0;
break;
}
case IEEE80211_RM_MEASTYPE_MUL_DIAG:
ADDINT16(frm, 0);
ADDINT16(frm, IEEE80211_MS_TO_TU(mrequest_ctrl->u.multicast_diag.duration_ms));
memcpy(frm, mrequest_ctrl->u.multicast_diag.group_mac, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
break;
default:
break;
}
*ele_len = (frm - ele_len) - 1;
return frm;
}
u_int8_t *ieee80211_measure_report_ie_generate(struct ieee80211_node *ni,
u_int8_t *frm,
struct ieee80211_meas_report_ctrl *mreport_ctrl)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = vap->iv_ic;
u_int8_t *ele_len;
/* common part */
*frm++ = IEEE80211_ELEMID_MEASREP;
ele_len = frm;
*frm++ = 0;
if (mreport_ctrl->autonomous)
*frm++ = 0;
else
*frm++ = mreport_ctrl->meas_token;
*frm++ = mreport_ctrl->report_mode;
*frm++ = mreport_ctrl->meas_type;
if (mreport_ctrl->report_mode == 0) {
switch (mreport_ctrl->meas_type) {
case IEEE80211_CCA_MEASTYPE_BASIC:
{
*frm++ = mreport_ctrl->u.basic.channel;
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.basic.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.basic.start_tsf) + 1);
ADDINT16(frm, mreport_ctrl->u.basic.duration_tu);
*frm++ = mreport_ctrl->u.basic.basic_report;
break;
}
case IEEE80211_CCA_MEASTYPE_CCA:
{
*frm++ = mreport_ctrl->u.cca.channel;
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.cca.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.cca.start_tsf) + 1);
ADDINT16(frm, mreport_ctrl->u.cca.duration_tu);
*frm++ = mreport_ctrl->u.cca.cca_report;
break;
}
case IEEE80211_CCA_MEASTYPE_RPI:
{
*frm++ = mreport_ctrl->u.rpi.channel;
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.rpi.start_tsf));
ADDINT32(frm, *((u_int32_t *)&mreport_ctrl->u.rpi.start_tsf) + 1);
ADDINT16(frm, mreport_ctrl->u.rpi.duration_tu);
memcpy(frm, mreport_ctrl->u.rpi.rpi_report, sizeof(mreport_ctrl->u.rpi.rpi_report));
frm += sizeof(mreport_ctrl->u.rpi.rpi_report);
break;
}
case IEEE80211_RM_MEASTYPE_STA:
{
u_int8_t group_len;
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
struct ieee80211_nodestats *stats;
u_int8_t assoc_bw;
ADDINT16(frm, mreport_ctrl->u.sta_stats.duration_tu);
*frm++ = mreport_ctrl->u.sta_stats.group_id;
switch (mreport_ctrl->u.sta_stats.group_id) {
case 0:
group_len = sizeof(struct ieee80211_rm_sta_stats_group0);
break;
case 1:
group_len = sizeof(struct ieee80211_rm_sta_stats_group0);
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
group_len = sizeof(struct ieee80211_rm_sta_stats_group2to9);
break;
case 10:
group_len = sizeof(struct ieee80211_rm_sta_stats_group10);
break;
case 11:
group_len = sizeof(struct ieee80211_rm_sta_stats_group11);
break;
case 12:
group_len = sizeof(struct ieee80211_rm_sta_stats_group12);
break;
case 13:
group_len = sizeof(struct ieee80211_rm_sta_stats_group13);
break;
case 14:
group_len = sizeof(struct ieee80211_rm_sta_stats_group14);
break;
case 15:
group_len = sizeof(struct ieee80211_rm_sta_stats_group15);
break;
case 16:
group_len = sizeof(struct ieee80211_rm_sta_stats_group16);
break;
default:
group_len = sizeof(struct ieee80211_ie_qtn_rm_sta_all);
break;
}
frm += group_len;
if (mreport_ctrl->u.sta_stats.sub_item != NULL) {
se_head = (ieee80211_11k_sub_element_head *)mreport_ctrl->u.sta_stats.sub_item;
while (!SLIST_EMPTY(se_head)) {
p_se = SLIST_FIRST(se_head);
switch (p_se->sub_id) {
case IEEE80211_ELEMID_VENDOR:
{
struct stastats_subele_vendor *vendor = (struct stastats_subele_vendor *)p_se->data;
u_int32_t vendor_flags = vendor->flags;
u_int8_t sequence = vendor->sequence;
if (vendor_flags & RM_QTN_MEASURE_MASK) {
u_int8_t *vendor_ie_len;
*frm++ = IEEE80211_ELEMID_VENDOR;
vendor_ie_len = frm;
*frm++ = 0;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = sequence;
stats = &ni->ni_stats;
if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) {
switch (ni->ni_vhtcap.chanwidth) {
case IEEE80211_VHTCAP_CW_160M:
case IEEE80211_VHTCAP_CW_160_AND_80P80M:
assoc_bw = 160;
break;
case IEEE80211_VHTCAP_CW_80M_ONLY:
default:
assoc_bw = 80;
}
} else {
if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40 &&
ni->ni_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) {
assoc_bw = 40;
} else {
assoc_bw = 20;
}
}
ic->ic_iterate_nodes(&ic->ic_sta, get_node_info, (void *)NULL, 1);
if (IEEE80211_IS_ALL_SET(vendor_flags, RM_QTN_MAX)) {
*frm++ = QTN_OUI_RM_ALL;
/* fill the content here */
/* fill the sta tx statistics */
ADDINT32TO64(frm, stats->ns_tx_bytes);
ADDINT32(frm, stats->ns_tx_data);
ADDINT32(frm, stats->ns_tx_dropped);
ADDINT32(frm, stats->ns_tx_errors);
ADDINT32(frm, stats->ns_tx_ucast);
ADDINT32(frm, stats->ns_tx_mcast);
ADDINT32(frm, stats->ns_tx_bcast);
/* fill the sta rx statistics */
ADDINT32TO64(frm, stats->ns_rx_bytes);
ADDINT32(frm, stats->ns_rx_data);
ADDINT32(frm, stats->ns_rx_dropped);
ADDINT32(frm, stats->ns_rx_errors);
ADDINT32(frm, stats->ns_rx_ucast);
ADDINT32(frm, stats->ns_rx_mcast);
ADDINT32(frm, stats->ns_rx_bcast);
/* fill the sta parameters */
ADDINT32(frm, ni->ni_max_queue);
ADDINT16TO32(frm, ni->ni_linkqual);
ADDINT32(frm, ni->ni_smthd_rssi);
ADDINT8TO32(frm, assoc_bw);
ADDINT32(frm, ni->ni_snr);
ADDINT8TO32(frm, ni->ni_rates.rs_rates[ni->ni_txrate]);
ADDINT16TO32(frm, ni->ni_rx_phy_rate);
} else {
u_int8_t *p_tlv_cnt;
u_int8_t i;
*frm++ = QTN_OUI_RM_SPCIAL;
p_tlv_cnt = frm;
*frm++ = 0;
for (i = 0; i <= RM_QTN_MAX; i++) {
if (vendor_flags & (BIT(i))) {
*p_tlv_cnt += 1;
*frm++ = i;
switch (i) {
case RM_QTN_TX_STATS:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32TO64(frm, stats->ns_tx_bytes);
ADDINT32(frm, stats->ns_tx_data);
ADDINT32(frm, stats->ns_tx_dropped);
ADDINT32(frm, stats->ns_tx_errors);
ADDINT32(frm, stats->ns_tx_ucast);
ADDINT32(frm, stats->ns_tx_mcast);
ADDINT32(frm, stats->ns_tx_bcast);
break;
}
case RM_QTN_RX_STATS:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32TO64(frm, stats->ns_rx_bytes);
ADDINT32(frm, stats->ns_rx_data);
ADDINT32(frm, stats->ns_rx_dropped);
ADDINT32(frm, stats->ns_rx_errors);
ADDINT32(frm, stats->ns_rx_ucast);
ADDINT32(frm, stats->ns_rx_mcast);
ADDINT32(frm, stats->ns_rx_bcast);
break;
}
case RM_QTN_MAX_QUEUED:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_max_queue);
break;
}
case RM_QTN_LINK_QUALITY:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_linkqual);
break;
}
case RM_QTN_RSSI_DBM:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_smthd_rssi);
break;
}
case RM_QTN_BANDWIDTH:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, assoc_bw);
break;
}
case RM_QTN_SNR:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_snr);
break;
}
case RM_QTN_TX_PHY_RATE:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_linkqual);
break;
}
case RM_QTN_RX_PHY_RATE:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ni->ni_rx_phy_rate);
break;
}
case RM_QTN_CCA:
{
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
/* Reserved for cca */
ADDINT32(frm, 0);
break;
}
case RM_QTN_BR_IP:
{
/* Vendor specific content: tlv length field */
__be32 br_ip = 0;
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
if (ic->ic_getparam != NULL) {
(*ic->ic_getparam)(ni, IEEE80211_PARAM_BR_IP_ADDR,
(int *)&br_ip, NULL, NULL);
}
ADDINT32(frm, br_ip);
break;
}
case RM_QTN_RSSI:
{
int32_t local_rssi = 0;
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
if (ic->ic_rssi) {
local_rssi = ic->ic_rssi(ni);
}
if (local_rssi < -1 && local_rssi > -1200) {
local_rssi += 900;
}
if (local_rssi < 0) {
local_rssi = 0;
}
ADDINT32(frm, local_rssi);
break;
}
case RM_QTN_HW_NOISE:
{
int32_t local_noise = 0;
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
local_noise = ic->ic_hw_noise(ni);
ADDINT32(frm, local_noise);
break;
}
case RM_QTN_SOC_MACADDR:
{
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
memcpy(frm, ic->soc_addr, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
break;
}
case RM_QTN_SOC_IPADDR:
{
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
ADDINT32(frm, ic->ic_soc_ipaddr);
break;
}
default:
/* Vendor specific content: tlv length field */
*frm++ = sizeof(u_int32_t);
/* unkown type report 0 */
ADDINT32(frm, 0);
break;
}
}
}
for (i = RM_QTN_CTRL_START; i <= RM_QTN_CTRL_END; i++) {
if (vendor_flags & (BIT(i))) {
*frm++ = i;
switch(i) {
case RM_QTN_RESET_CNTS:
{
int32_t ret;
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
/* reset all counter */
ret = ieee80211_rst_dev_stats(vap);
ADDINT32(frm, ret);
break;
}
case RM_QTN_RESET_QUEUED:
/* Vendor specific content: tlv length field */
*frm++ = ieee80211_meas_sta_qtn_report_subtype_len[i];
/* reset all counter */
ic->ic_queue_reset(ni);
ADDINT32(frm, 0);
break;
default:
/* Vendor specific content: tlv length field */
*frm++ = sizeof(int32_t);
ADDINT32(frm, -1);
break;
}
}
}
}
*vendor_ie_len = frm - vendor_ie_len - 1;
}
break;
}
default:
printk("unknown STA Statistics sub element, ID = %d\n", p_se->sub_id);
break;
}
SLIST_REMOVE_HEAD(se_head, next);
kfree(p_se);
}
}
break;
}
case IEEE80211_RM_MEASTYPE_QTN_CCA:
{
u_int8_t *vendor_ie_len;
/* replace with real type */
*(frm - 1) = IEEE80211_CCA_MEASTYPE_CCA;
*frm++ = mreport_ctrl->u.qtn_cca.channel;
ADDINT32LE(frm, *((u_int32_t *)&mreport_ctrl->u.qtn_cca.start_tsf));
ADDINT32LE(frm, *((u_int32_t *)&mreport_ctrl->u.qtn_cca.start_tsf) + 1);
ADDINT16LE(frm, IEEE80211_MS_TO_TU(mreport_ctrl->u.qtn_cca.duration_ms));
*frm++ = mreport_ctrl->u.qtn_cca.qtn_cca_report;
/* qtn SCS IE */
*frm++ = IEEE80211_ELEMID_VENDOR;
vendor_ie_len = frm;
*frm++ = 0;
frm += ieee80211_oui_add_qtn(frm);
*frm++ = QTN_OUI_SCS;
*frm++ = mreport_ctrl->u.qtn_cca.type;
if (QTN_SCS_IE_TYPE_STA_INTF_RPT == mreport_ctrl->u.qtn_cca.type) {
ADDINT32LE(frm, mreport_ctrl->u.qtn_cca.u.qtn_cca_info.sp_fail);
ADDINT32LE(frm, mreport_ctrl->u.qtn_cca.u.qtn_cca_info.lp_fail);
ADDINT16LE(frm, mreport_ctrl->u.qtn_cca.u.qtn_cca_info.others_time);
} else if (QTN_SCS_IE_TYPE_STA_DFS_RPT == mreport_ctrl->u.qtn_cca.type) {
ADDINT16LE(frm, mreport_ctrl->u.qtn_cca.u.qtn_dfs_info.dfs_enabled);
*frm++ = mreport_ctrl->u.qtn_cca.u.qtn_dfs_info.max_txpower;
} else if (QTN_SCS_IE_TYPE_STA_FAT_RPT == mreport_ctrl->u.qtn_cca.type) {
ADDINT16LE(frm, mreport_ctrl->u.qtn_cca.u.qtn_fat_info.free_airtime);
}
ADDINT16LE(frm, mreport_ctrl->u.qtn_cca.extra_ie_len);
memcpy(frm, mreport_ctrl->u.qtn_cca.extra_ie,
mreport_ctrl->u.qtn_cca.extra_ie_len);
frm += mreport_ctrl->u.qtn_cca.extra_ie_len;
*vendor_ie_len = frm - vendor_ie_len - 1;
break;
}
case IEEE80211_RM_MEASTYPE_CH_LOAD:
{
u_int64_t tsf;
ic->ic_get_tsf(&tsf);
*frm++ = mreport_ctrl->u.chan_load.op_class;
*frm++ = mreport_ctrl->u.chan_load.channel;
ADDINT32(frm, *((u_int32_t *)&tsf));
ADDINT32(frm, *((u_int32_t *)&tsf + 1));
ADDINT16(frm, mreport_ctrl->u.chan_load.duration_tu);
*frm++ = mreport_ctrl->u.chan_load.channel_load;
break;
}
case IEEE80211_RM_MEASTYPE_NOISE:
{
u_int64_t tsf;
ic->ic_get_tsf(&tsf);
*frm++ = mreport_ctrl->u.noise_his.op_class;
*frm++ = mreport_ctrl->u.noise_his.channel;
ADDINT32(frm, *((u_int32_t *)&tsf));
ADDINT32(frm, *((u_int32_t *)&tsf + 1));
ADDINT16(frm, mreport_ctrl->u.noise_his.duration_tu);
*frm++ = mreport_ctrl->u.noise_his.antenna_id;
*frm++ = mreport_ctrl->u.noise_his.anpi;
memcpy(frm, mreport_ctrl->u.noise_his.ipi, sizeof(mreport_ctrl->u.noise_his.ipi));
frm += sizeof(mreport_ctrl->u.noise_his.ipi);
break;
}
case IEEE80211_RM_MEASTYPE_BEACON:
{
u_int64_t tsf;
ic->ic_get_tsf(&tsf);
*frm++ = mreport_ctrl->u.beacon.op_class;
*frm++ = mreport_ctrl->u.beacon.channel;
ADDINT32(frm, *((u_int32_t *)&tsf));
ADDINT32(frm, *((u_int32_t *)&tsf + 1));
ADDINT16(frm, mreport_ctrl->u.beacon.duration_tu);
*frm++ = mreport_ctrl->u.beacon.reported_frame_info;
*frm++ = mreport_ctrl->u.beacon.rcpi;
*frm++ = mreport_ctrl->u.beacon.rsni;
memcpy(frm, mreport_ctrl->u.beacon.bssid, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
*frm++ = mreport_ctrl->u.beacon.antenna_id;
memcpy(frm, mreport_ctrl->u.beacon.parent_tsf, 4);
frm += sizeof(mreport_ctrl->u.beacon.parent_tsf);
break;
}
case IEEE80211_RM_MEASTYPE_FRAME:
{
u_int64_t tsf;
ieee80211_11k_sub_element *p_se;
ieee80211_11k_sub_element_head *se_head;
ic->ic_get_tsf(&tsf);
*frm++ = mreport_ctrl->u.frame.op_class;
*frm++ = mreport_ctrl->u.frame.channel;
ADDINT32(frm, *((u_int32_t *)&tsf));
ADDINT32(frm, *((u_int32_t *)&tsf + 1));
ADDINT16(frm, mreport_ctrl->u.frame.duration_tu);
se_head = (ieee80211_11k_sub_element_head *)mreport_ctrl->u.frame.sub_item;
while (!SLIST_EMPTY(se_head)) {
p_se = SLIST_FIRST(se_head);
switch (p_se->sub_id) {
case IEEE80211_FRAME_REPORT_SUBELE_FRAME_COUNT_REPORT:
{
u_int8_t *sub_ele_len;
struct frame_report_subele_frame_count *sub_ele;
sub_ele = (struct frame_report_subele_frame_count *)p_se->data;
*frm++ = p_se->sub_id;
sub_ele_len = frm;
*frm++ = 0;
memcpy(frm, sub_ele->ta, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
memcpy(frm, sub_ele->bssid, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
*frm++ = sub_ele->phy_type;
*frm++ = sub_ele->avg_rcpi;
*frm++ = sub_ele->last_rsni;
*frm++ = sub_ele->last_rcpi;
*frm++ = sub_ele->antenna_id;
ADDINT16(frm, sub_ele->frame_count);
*sub_ele_len = frm - sub_ele_len - 1;
break;
}
default:
break;
}
SLIST_REMOVE_HEAD(se_head, next);
kfree(p_se);
}
break;
}
case IEEE80211_RM_MEASTYPE_CATEGORY:
{
u_int64_t tsf;
ic->ic_get_tsf(&tsf);
ADDINT32(frm, *((u_int32_t *)&tsf));
ADDINT32(frm, *((u_int32_t *)&tsf + 1));
ADDINT16(frm, mreport_ctrl->u.tran_stream_cat.duration_tu);
memcpy(frm, mreport_ctrl->u.tran_stream_cat.peer_sta, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
*frm++ = mreport_ctrl->u.tran_stream_cat.tid;
*frm++ = mreport_ctrl->u.tran_stream_cat.reason;
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.tran_msdu_cnt);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.msdu_discard_cnt);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.msdu_fail_cnt);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.msdu_mul_retry_cnt);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.qos_lost_cnt);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.avg_queue_delay);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.avg_tran_delay);
*frm++ = mreport_ctrl->u.tran_stream_cat.bin0_range;
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[0]);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[1]);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[2]);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[3]);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[4]);
ADDINT32(frm, mreport_ctrl->u.tran_stream_cat.bins[5]);
break;
}
case IEEE80211_RM_MEASTYPE_MUL_DIAG:
ADDINT32(frm, 0);
ADDINT32(frm, 0);
ADDINT16(frm, mreport_ctrl->u.multicast_diag.duration_tu);
memcpy(frm, mreport_ctrl->u.multicast_diag.group_mac, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
*frm++ = mreport_ctrl->u.multicast_diag.reason;
ADDINT32(frm, mreport_ctrl->u.multicast_diag.mul_rec_msdu_cnt);
ADDINT16(frm, mreport_ctrl->u.multicast_diag.first_seq_num);
ADDINT16(frm, mreport_ctrl->u.multicast_diag.last_seq_num);
ADDINT16(frm, mreport_ctrl->u.multicast_diag.mul_rate);
break;
default:
break;
}
}
*ele_len = frm - ele_len - 1;
return frm;
}
int32_t ieee80211_compile_action_measurement_11h(struct ieee80211_node *ni,
void *ctrl,
u_int8_t action,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
u_int8_t *frm;
int32_t meas_frame_len = 0;
int32_t meas_ie_len = 0;
struct ieee80211_meas_request_ctrl *mrequest_ctrl = NULL;
struct ieee80211_meas_report_ctrl *mreport_ctrl = NULL;
u_int8_t tx_token;
if ((action != IEEE80211_ACTION_S_MEASUREMENT_REQUEST) && (action != IEEE80211_ACTION_S_MEASUREMENT_REPORT))
return -1;
if (action == IEEE80211_ACTION_S_MEASUREMENT_REQUEST) {
mrequest_ctrl = (struct ieee80211_meas_request_ctrl *)ctrl;
meas_frame_len = sizeof(struct ieee80211_action_sm_measurement_header);
meas_ie_len = ieee80211_measure_request_ie_len(mrequest_ctrl);
if (meas_ie_len <= 0)
return -1;
meas_frame_len += meas_ie_len;
skb = ieee80211_getmgtframe(&frm, meas_frame_len);
if (NULL == skb)
return -1;
*frm++ = IEEE80211_ACTION_CAT_SPEC_MGMT;
*frm++ = action;
if (ni->ni_action_token == 0)
ni->ni_action_token++;
tx_token = ni->ni_action_token++;
*frm++ = tx_token;
frm = ieee80211_measure_request_ie_generate(ni, frm, mrequest_ctrl);
if (mrequest_ctrl->expire != 0) {
skb = ieee80211_ppqueue_pre_tx(ni,
skb,
IEEE80211_ACTION_CAT_SPEC_MGMT,
IEEE80211_ACTION_S_MEASUREMENT_REPORT,
tx_token,
mrequest_ctrl->expire,
mrequest_ctrl->fn_success,
mrequest_ctrl->fn_fail);
if (skb == NULL)
return -1;
}
} else {
mreport_ctrl = (struct ieee80211_meas_report_ctrl *)ctrl;
meas_frame_len = sizeof(struct ieee80211_action_sm_measurement_header);
meas_ie_len = ieee80211_measure_report_ie_len(mreport_ctrl);
if (meas_ie_len <= 0)
return -1;
meas_frame_len += meas_ie_len;
skb = ieee80211_getmgtframe(&frm, meas_frame_len);
if (NULL == skb)
return -1;
memset(frm, 0, meas_frame_len);
*frm++ = IEEE80211_ACTION_CAT_SPEC_MGMT;
*frm++ = action;
if (mreport_ctrl->autonomous)
*frm++ = 0;
else
*frm++ = mreport_ctrl->token;
frm = ieee80211_measure_report_ie_generate(ni, frm, mreport_ctrl);
}
KASSERT(((frm - skb->data) <= meas_frame_len),
("ERROR: 11h measure frame gen fail\n"
"expected len = %d\n"
"start address(0x%x), end address(0x%x), len = %d\n",
meas_frame_len,
(uint32_t)skb->data,
(uint32_t)frm,
(uint32_t)(frm - skb->data)));
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
int32_t ieee80211_compile_action_measurement_11k(struct ieee80211_node *ni,
void *ctrl,
u_int8_t action,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
u_int8_t *frm;
int32_t meas_frame_len = 0;
int32_t meas_ie_len = 0;
struct ieee80211_meas_request_ctrl *mrequest_ctrl = NULL;
struct ieee80211_meas_report_ctrl *mreport_ctrl = NULL;
u_int8_t tx_token;
if ((action != IEEE80211_ACTION_R_MEASUREMENT_REQUEST) && (action != IEEE80211_ACTION_R_MEASUREMENT_REPORT))
return -1;
if (action == IEEE80211_ACTION_R_MEASUREMENT_REQUEST) {
mrequest_ctrl = (struct ieee80211_meas_request_ctrl *)ctrl;
meas_frame_len = sizeof(struct ieee80211_action_radio_measure_request);
meas_ie_len = ieee80211_measure_request_ie_len(mrequest_ctrl);
if (meas_ie_len <= 0)
return -1;
meas_frame_len += meas_ie_len;
if ((mrequest_ctrl->meas_type == IEEE80211_RM_MEASTYPE_BEACON)
&& (mrequest_ctrl->u.beacon.ssid_len)) {
meas_frame_len += mrequest_ctrl->u.beacon.ssid_len + 2;
}
skb = ieee80211_getmgtframe(&frm, meas_frame_len);
if (NULL == skb)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = action;
if (ni->ni_action_token == 0)
ni->ni_action_token++;
tx_token = ni->ni_action_token++;
*frm++ = tx_token;
ADDINT16(frm, 0); /* set number of repetitions to 0 */
frm = ieee80211_measure_request_ie_generate(ni, frm, mrequest_ctrl);
if (mrequest_ctrl->expire != 0) {
skb = ieee80211_ppqueue_pre_tx(ni,
skb,
IEEE80211_ACTION_CAT_RM,
IEEE80211_ACTION_R_MEASUREMENT_REPORT,
tx_token,
mrequest_ctrl->expire,
mrequest_ctrl->fn_success,
mrequest_ctrl->fn_fail);
if (skb == NULL)
return -1;
}
} else {
mreport_ctrl = (struct ieee80211_meas_report_ctrl *)ctrl;
meas_frame_len = sizeof(struct ieee80211_action_radio_measure_report);
meas_ie_len = ieee80211_measure_report_ie_len(mreport_ctrl);
if (meas_ie_len <= 0)
return -1;
meas_frame_len += meas_ie_len;
skb = ieee80211_getmgtframe(&frm, meas_frame_len);
if (NULL == skb)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = action;
if (mreport_ctrl->autonomous)
*frm++ = 0;
else
*frm++ = mreport_ctrl->token;
frm = ieee80211_measure_report_ie_generate(ni, frm, mreport_ctrl);
}
KASSERT(((frm - skb->data) <= meas_frame_len),
("ERROR: 11k measure frame gen fail\n"
"expected len = %d\n"
"start address(0x%x), end address(0x%x), len = %d\n",
meas_frame_len,
(uint32_t)skb->data,
(uint32_t)frm,
(uint32_t)(frm - skb->data)));
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
#if defined(CONFIG_QTN_80211K_SUPPORT)
void ieee80211_send_action_cca_report(struct ieee80211_node *ni, uint8_t token,
uint16_t cca_intf, uint64_t tsf, uint16_t duration, uint32_t sp_fail,
uint32_t lp_fail, uint16_t others_time, uint8_t *extra_ie, uint16_t ie_len)
{
struct ieee80211_meas_report_ctrl mreport_ctrl;
struct ieee80211_action_data action_data;
u_int16_t frac_busy;
frac_busy = cca_intf * IEEE80211_11K_CCA_INTF_SCALE / IEEE80211_SCS_CCA_INTF_SCALE;
mreport_ctrl.meas_type = IEEE80211_RM_MEASTYPE_QTN_CCA;
mreport_ctrl.report_mode = 0;
mreport_ctrl.autonomous = 1;
mreport_ctrl.u.qtn_cca.type = QTN_SCS_IE_TYPE_STA_INTF_RPT;
mreport_ctrl.u.qtn_cca.channel = ni->ni_chan->ic_ieee;
mreport_ctrl.u.qtn_cca.start_tsf = tsf;
mreport_ctrl.u.qtn_cca.duration_ms = duration;
mreport_ctrl.u.qtn_cca.qtn_cca_report = (u_int8_t)frac_busy;
mreport_ctrl.u.qtn_cca.u.qtn_cca_info.sp_fail = sp_fail;
mreport_ctrl.u.qtn_cca.u.qtn_cca_info.lp_fail = lp_fail;
mreport_ctrl.u.qtn_cca.u.qtn_cca_info.others_time = others_time;
mreport_ctrl.u.qtn_cca.extra_ie = extra_ie;
mreport_ctrl.u.qtn_cca.extra_ie_len = ie_len;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &mreport_ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
void ieee80211_send_action_fat_report(struct ieee80211_node *ni, uint8_t token,
uint16_t cca_intf, uint64_t tsf, uint16_t duration, uint16_t idle_time)
{
struct ieee80211_meas_report_ctrl mreport_ctrl;
struct ieee80211_action_data action_data;
u_int16_t frac_busy;
frac_busy = cca_intf * IEEE80211_11K_CCA_INTF_SCALE / IEEE80211_SCS_CCA_INTF_SCALE;
mreport_ctrl.meas_type = IEEE80211_RM_MEASTYPE_QTN_CCA;
mreport_ctrl.report_mode = 0;
mreport_ctrl.autonomous = 1;
mreport_ctrl.u.qtn_cca.type = QTN_SCS_IE_TYPE_STA_FAT_RPT;
mreport_ctrl.u.qtn_cca.channel = ni->ni_chan->ic_ieee;
mreport_ctrl.u.qtn_cca.start_tsf = tsf;
mreport_ctrl.u.qtn_cca.duration_ms = duration;
mreport_ctrl.u.qtn_cca.qtn_cca_report = (u_int8_t)frac_busy;
mreport_ctrl.u.qtn_cca.u.qtn_fat_info.free_airtime = idle_time;
mreport_ctrl.u.qtn_cca.extra_ie_len = 0;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &mreport_ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
void ieee80211_send_action_dfs_report(struct ieee80211_node *ni)
{
struct ieee80211_meas_report_ctrl mreport_ctrl;
struct ieee80211_action_data action_data;
struct ieee80211com *ic = ni->ni_ic;
mreport_ctrl.meas_type = IEEE80211_RM_MEASTYPE_QTN_CCA;
mreport_ctrl.report_mode = 0;
mreport_ctrl.autonomous = 1;
mreport_ctrl.u.qtn_cca.type = QTN_SCS_IE_TYPE_STA_DFS_RPT;
mreport_ctrl.u.qtn_cca.channel = ni->ni_chan->ic_ieee;
mreport_ctrl.u.qtn_cca.start_tsf = 0;
mreport_ctrl.u.qtn_cca.duration_ms = 0;
mreport_ctrl.u.qtn_cca.qtn_cca_report = 0;
mreport_ctrl.u.qtn_cca.u.qtn_dfs_info.dfs_enabled = !!(ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS);
mreport_ctrl.u.qtn_cca.u.qtn_dfs_info.max_txpower = ic->ic_curchan->ic_maxpower;
mreport_ctrl.u.qtn_cca.extra_ie_len = 0;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &mreport_ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_action_dfs_report);
#endif
__inline void ieee80211_ppqueue_release_entry(struct ieee80211_pairing_pending_entry *entry)
{
if (entry != NULL) {
dev_kfree_skb_any(entry->skb);
kfree(entry);
}
}
void ieee80211_ppqueue_insert_entry(struct ieee80211_pairing_pending_queue *queue,
struct ieee80211_pairing_pending_entry *entry)
{
int32_t timer_refresh = 0;
unsigned long flags;
struct ieee80211_pairing_pending_entry *prev, *cur;
spin_lock_irqsave(&queue->lock, flags);
if (queue->next == NULL) {
queue->next = entry;
queue->next_expire_jiffies = entry->next_expire_jiffies;
timer_refresh = 1;
} else {
if (time_before(entry->next_expire_jiffies, queue->next->next_expire_jiffies)) {
entry->next = queue->next;
queue->next = entry;
queue->next_expire_jiffies = entry->next_expire_jiffies;
timer_refresh = 1;
} else {
prev = queue->next;
cur = prev->next;
while (cur != NULL) {
if (time_before(entry->next_expire_jiffies, cur->next_expire_jiffies)) {
entry->next = cur;
prev->next = entry;
break;
}
prev = cur;
cur = prev->next;
}
}
}
spin_unlock_irqrestore(&queue->lock, flags);
if (timer_refresh) {
mod_timer(&queue->timer, queue->next_expire_jiffies);
}
}
void ieee80211_ppqueue_remove_with_response(struct ieee80211_pairing_pending_queue *queue,
struct ieee80211_node *ni,
u_int8_t category,
u_int8_t action,
u_int8_t token)
{
struct ieee80211_pairing_pending_entry **prev, *cur, *to_free;
unsigned long flags;
prev = &queue->next;
cur = queue->next;
to_free = NULL;
spin_lock_irqsave(&queue->lock, flags);
while (cur != NULL) {
if ((cur->ni == ni) &&
(cur->expected_category == category) &&
(cur->expected_action == action) &&
(cur->expected_token == token)) {
to_free = cur;
*prev = cur->next;
break;
}
prev = &cur->next;
cur = cur->next;
}
spin_unlock_irqrestore(&queue->lock, flags);
if (to_free != NULL) {
if (to_free->fn_success != NULL)
to_free->fn_success(to_free->ni);
ieee80211_ppqueue_release_entry(to_free);
}
}
void ieee80211_ppqueue_remove_node_leave(struct ieee80211_pairing_pending_queue *queue,
struct ieee80211_node *ni)
{
struct ieee80211_pairing_pending_entry **prev, *cur;
struct ieee80211_pairing_pending_entry *ni_drop_list, *to_free;
unsigned long flags;
prev = &queue->next;
cur = queue->next;
ni_drop_list = NULL;
to_free = NULL;
spin_lock_irqsave(&queue->lock, flags);
while (cur != NULL) {
if (cur->ni == ni) {
to_free = cur;
*prev = cur->next;
cur = cur->next;
REPLACE_PPQ_ENTRY_HEAD(ni_drop_list, to_free);
continue;
}
prev = &cur->next;
cur = cur->next;
}
spin_unlock_irqrestore(&queue->lock, flags);
while (ni_drop_list != NULL) {
to_free = ni_drop_list;
ni_drop_list = ni_drop_list->next;
if (to_free->fn_fail)
to_free->fn_fail(to_free->ni, PPQ_FAIL_NODELEAVE);
ieee80211_ppqueue_release_entry(to_free);
}
}
void ieee80211_ppqueue_remove_with_cat_action(struct ieee80211_pairing_pending_queue *queue,
u_int8_t category,
u_int8_t action)
{
struct ieee80211_pairing_pending_entry **prev, *cur;
struct ieee80211_pairing_pending_entry *ni_drop_list, *to_free;
unsigned long flags;
prev = &queue->next;
cur = queue->next;
ni_drop_list = NULL;
to_free = NULL;
spin_lock_irqsave(&queue->lock, flags);
while (cur != NULL) {
if (cur->expected_category == category &&
cur->expected_action == action) {
to_free = cur;
*prev = cur->next;
cur = cur->next;
REPLACE_PPQ_ENTRY_HEAD(ni_drop_list, to_free);
continue;
}
prev = &cur->next;
cur = cur->next;
}
spin_unlock_irqrestore(&queue->lock, flags);
while (ni_drop_list != NULL) {
to_free = ni_drop_list;
ni_drop_list = ni_drop_list->next;
if (to_free->fn_fail)
to_free->fn_fail(to_free->ni, PPQ_FAIL_STOP);
ieee80211_ppqueue_release_entry(to_free);
}
}
void ieee80211_ppqueue_flush(struct ieee80211_pairing_pending_queue *queue)
{
struct ieee80211_pairing_pending_entry *flush_list, *to_free;
unsigned long flags;
spin_lock_irqsave(&queue->lock, flags);
flush_list = queue->next;
queue->next = NULL;
spin_unlock_irqrestore(&queue->lock, flags);
while (flush_list != NULL) {
to_free = flush_list;
flush_list = flush_list->next;
if (to_free->fn_fail)
to_free->fn_fail(to_free->ni, PPQ_FAIL_STOP);
ieee80211_ppqueue_release_entry(to_free);
}
}
void ieee80211_ppqueue_timeout(unsigned long ctx)
{
struct ieee80211_pairing_pending_queue *queue = (struct ieee80211_pairing_pending_queue *)ctx;
struct ieee80211_pairing_pending_entry **prev, *cur, *to_do, *timeout_retry, *timeout_fail;
struct sk_buff *skb;
unsigned long flags;
prev = &queue->next;
cur = queue->next;
to_do = NULL;
timeout_retry = NULL;
timeout_fail = NULL;
spin_lock_irqsave(&queue->lock, flags);
while (cur != NULL) {
if (time_before_eq(cur->next_expire_jiffies, jiffies)) {
to_do = cur;
*prev = cur->next;
cur = cur->next;
if (to_do->retry_cnt < to_do->max_retry)
REPLACE_PPQ_ENTRY_HEAD(timeout_retry, to_do);
else
REPLACE_PPQ_ENTRY_HEAD(timeout_fail, to_do);
continue;
}
prev = &cur->next;
cur = cur->next;
}
spin_unlock_irqrestore(&queue->lock, flags);
while (timeout_retry != NULL) {
to_do = timeout_retry;
timeout_retry = timeout_retry->next;
to_do->retry_cnt++;
to_do->next_expire_jiffies = jiffies + to_do->expire;
skb = skb_clone(to_do->skb, GFP_ATOMIC);
if (skb) {
ieee80211_ref_node(to_do->ni);
ieee80211_mgmt_output(to_do->ni, skb, IEEE80211_FC0_SUBTYPE_ACTION, to_do->ni->ni_macaddr);
}
ieee80211_ppqueue_insert_entry(queue, to_do);
}
while (timeout_fail != NULL) {
to_do = timeout_fail;
timeout_fail = timeout_fail->next;
if (to_do->fn_fail)
to_do->fn_fail(to_do->ni, PPQ_FAIL_TIMEOUT);
ieee80211_ppqueue_release_entry(to_do);
}
}
void ieee80211_ppqueue_init(struct ieee80211vap *vap)
{
struct ieee80211_pairing_pending_queue *queue = (struct ieee80211_pairing_pending_queue *)&vap->iv_ppqueue;
spin_lock_init(&queue->lock);
init_timer(&queue->timer);
queue->timer.data = (unsigned long)queue;
queue->timer.function = ieee80211_ppqueue_timeout;
queue->next = NULL;
queue->next_expire_jiffies = 0;
}
void ieee80211_ppqueue_deinit(struct ieee80211vap *vap)
{
struct ieee80211_pairing_pending_queue *queue = (struct ieee80211_pairing_pending_queue *)&vap->iv_ppqueue;
del_timer(&queue->timer);
ieee80211_ppqueue_flush(queue);
}
struct sk_buff *ieee80211_ppqueue_pre_tx(struct ieee80211_node *ni,
struct sk_buff *skb,
u_int8_t category,
u_int8_t action,
u_int8_t token,
unsigned long expire,
ppq_callback_success fn_success,
ppq_callback_fail fn_fail)
{
struct sk_buff *cloned_skb = NULL;
struct ieee80211_pairing_pending_queue *queue = &ni->ni_vap->iv_ppqueue;
struct ieee80211_pairing_pending_entry *entry = NULL;
entry = (struct ieee80211_pairing_pending_entry *)kmalloc(sizeof(*entry), GFP_ATOMIC);
if (NULL == entry) {
dev_kfree_skb_any(skb);
return NULL;
}
cloned_skb = skb_clone(skb, GFP_ATOMIC);
if (cloned_skb == NULL) {
dev_kfree_skb_any(skb);
kfree(entry);
return NULL;
}
memset(entry, 0, sizeof(*entry));
entry->skb = skb;
entry->ni = ni;
entry->expected_category = category;
entry->expected_action = action;
entry->expected_token = token;
entry->expire = expire;
entry->next_expire_jiffies = jiffies + expire;
entry->max_retry = IEEE80211_PPQ_DEF_MAX_RETRY;
entry->retry_cnt = 0;
entry->fn_success = fn_success;
entry->fn_fail = fn_fail;
ieee80211_ppqueue_insert_entry(queue, entry);
return cloned_skb;
}
int32_t ieee80211_compile_action_link_measure_request(struct ieee80211_node *ni,
void *ctrl,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
int32_t frame_len = 0;
struct ieee80211_link_measure_request *request;
u_int8_t *frm;
u_int8_t tx_token;
request = (struct ieee80211_link_measure_request *)ctrl;
frame_len = sizeof(struct ieee80211_action_rm_link_measure_request);
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = IEEE80211_ACTION_R_LINKMEASURE_REQUEST;
if (ni->ni_action_token == 0)
ni->ni_action_token++;
tx_token = ni->ni_action_token++;
*frm++ = tx_token;
*frm++ = ni->ni_ic->ic_get_local_txpow(ni->ni_ic);
*frm++ = ni->ni_ic->ic_curchan->ic_maxpower_normal + 6; /* 4 anntenna, add 6 db */
if (request->ppq.expire != 0) {
skb = ieee80211_ppqueue_pre_tx(ni, skb, IEEE80211_ACTION_CAT_RM,
IEEE80211_ACTION_R_LINKMEASURE_REPORT,
tx_token, request->ppq.expire,
request->ppq.fn_success, request->ppq.fn_fail);
if (skb == NULL)
return -1;
}
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
int32_t ieee80211_compile_action_link_measure_report(struct ieee80211_node *ni,
void *ctrl,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
int32_t frame_len = 0;
struct ieee80211_link_measure_report *report;
u_int8_t *frm;
report = (struct ieee80211_link_measure_report *)ctrl;
frame_len = sizeof(struct ieee80211_action_rm_link_measure_report);
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = IEEE80211_ACTION_R_LINKMEASURE_REPORT;
*frm++ = report->token;
*frm++ = IEEE80211_ELEMID_TPCREP;
*frm++ = 2;
*frm++ = report->tpc_report.tx_power;
*frm++ = report->tpc_report.link_margin;
*frm++ = report->recv_antenna_id;
*frm++ = report->tran_antenna_id;
*frm++ = report->rcpi;
*frm++ = report->rsni;
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
int32_t ieee80211_compile_action_neighbor_report_request(struct ieee80211_node *ni,
void *ctrl,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
int32_t frame_len = 0;
u_int8_t *frm;
u_int8_t tx_token;
struct ieee80211_neighbor_report_request *request;
request = (struct ieee80211_neighbor_report_request *)ctrl;
frame_len = sizeof(struct ieee80211_action_rm_neighbor_report_request);
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = IEEE80211_ACTION_R_NEIGHBOR_REQUEST;
if (ni->ni_action_token == 0)
ni->ni_action_token++;
tx_token = ni->ni_action_token++;
*frm++ = tx_token;
if (request->ppq.expire != 0) {
skb = ieee80211_ppqueue_pre_tx(ni, skb, IEEE80211_ACTION_CAT_RM,
IEEE80211_ACTION_R_NEIGHBOR_REPORT,
tx_token, request->ppq.expire,
request->ppq.fn_success, request->ppq.fn_fail);
if (skb == NULL)
return -1;
}
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
int32_t ieee80211_compile_action_neighbor_report_response(struct ieee80211_node *ni,
void *ctrl,
struct sk_buff **p_skb)
{
struct sk_buff *skb;
int32_t frame_len = 0;
u_int8_t *frm;
struct ieee80211_neighbor_report_response *response;
u_int8_t i;
u_int8_t bss_num = 0;
response = (struct ieee80211_neighbor_report_response *)ctrl;
frame_len = sizeof(struct ieee80211_action_rm_neighbor_report_response);
if (response->bss_num > 0) {
bss_num = (response->bss_num > 32 ? 32 : response->bss_num);
frame_len += sizeof(struct ieee80211_ie_neighbor_report) * bss_num;
}
skb = ieee80211_getmgtframe(&frm, frame_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_RM;
*frm++ = IEEE80211_ACTION_R_NEIGHBOR_REPORT;
*frm++ = response->token;
for (i = 0; i < bss_num; i++) {
*frm++ = IEEE80211_ELEMID_NEIGHBOR_REP;
*frm++ = sizeof(struct ieee80211_ie_neighbor_report) - 2;
memcpy(frm, response->neighbor_report_ptr[i]->bssid, IEEE80211_ADDR_LEN);
frm += IEEE80211_ADDR_LEN;
ADDINT32(frm, response->neighbor_report_ptr[i]->bssid_info);
*frm++ = response->neighbor_report_ptr[i]->operating_class;
*frm++ = response->neighbor_report_ptr[i]->channel;
*frm++ = response->neighbor_report_ptr[i]->phy_type;
}
skb_trim(skb, frm - skb->data);
*p_skb = skb;
return 0;
}
/*
* To check whether to enable RX AMSDU or not.
* Return 1: RX AMSDU can be enabled, 0: should be disabled
*/
static int ieee80211_rx_amsdu_allowed(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
if (vap->iv_rx_amsdu_enable == QTN_RX_AMSDU_DISABLE) {
return 0;
}
if ((vap->iv_rx_amsdu_enable == QTN_RX_AMSDU_DYNAMIC) && !ieee80211_node_is_qtn(ni)) {
if (ic->ic_scs.scs_stats_on) {
if (vap->iv_rx_amsdu_threshold_cca && (ic->ic_opmode == IEEE80211_M_HOSTAP)) {
struct ap_state *as = ic->ic_scan->ss_scs_priv;
uint32_t cca_intf = as->as_cca_intf[ic->ic_curchan->ic_ieee];
if ((cca_intf != SCS_CCA_INTF_INVALID) &&
(cca_intf > vap->iv_rx_amsdu_threshold_cca)) {
return 0;
}
}
if (vap->iv_rx_amsdu_threshold_pmbl) {
uint32_t pmbl_err = (vap->iv_rx_amsdu_pmbl_wf_sp * ic->ic_scs.scs_sp_err_smthed +
vap->iv_rx_amsdu_pmbl_wf_lp * ic->ic_scs.scs_lp_err_smthed) / 100;
if (pmbl_err > vap->iv_rx_amsdu_threshold_pmbl) {
return 0;
}
}
}
}
return 1;
}
void
ieee80211_get_channel_bw_offset(struct ieee80211com *ic, int16_t *is_40, int16_t *offset)
{
*is_40 = 0;
*offset = IEEE80211_HTINFO_CHOFF_SCN;
if ((ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) &&
(ic->ic_bsschan->ic_flags & IEEE80211_CHAN_HT40_DUAL_EXT)) {
*offset = (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_HT40U) ?
IEEE80211_HTINFO_CHOFF_SCA : IEEE80211_HTINFO_CHOFF_SCB;
*is_40 = 1;
}
}
static int ieee80211_check_11b_ap(const struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_rateset *b_rates = &ic->ic_sup_rates[IEEE80211_MODE_11B];
int i, j;
for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
for (j = 0; j < b_rates->rs_nrates; j++) {
if ((ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) ==
(b_rates->rs_rates[j] & IEEE80211_RATE_VAL)) {
break;
}
}
if (j == b_rates->rs_nrates)
return 0;
}
return 1;
}
static int
ieee80211_compile_action_btm_req(struct ieee80211_node *ni,
struct btm_request_params *ctrl,
struct sk_buff **pp_skb)
{
struct sk_buff *skb = NULL;
uint8_t *frm = NULL;
size_t url_len = 0;
uint16_t frm_len = 0;
uint8_t tx_token = 0;
url_len = ctrl->url ? strlen(ctrl->url) + 1 : 0;
frm_len = (sizeof(struct ieee80211_action_btm_req)
+ (ctrl->bss_term_dur ? sizeof(struct ieee80211_ie_btm_bss_termdur) : 0)
+ url_len
+ ctrl->neigh_reports_length);
skb = ieee80211_getmgtframe(&frm, frm_len);
if (skb == NULL)
return -1;
*frm++ = IEEE80211_ACTION_CAT_WNM;
*frm++ = IEEE80211_WNM_BSS_TRANS_MGMT_REQ;
if (ctrl->dialog_token != 0) {
*frm++ = ctrl->dialog_token;
tx_token = ctrl->dialog_token;
} else {
if (ni->ni_action_token == 0)
ni->ni_action_token++;
*frm++ = ni->ni_action_token;
tx_token = ni->ni_action_token++;
}
*frm++ = ctrl->request_mode;
ADDINT16LE(frm, ctrl->disassoc_timer);
*frm++ = ctrl->validity_interval;
ni->ni_btm_req = tx_token;
if ((ctrl->request_mode & BTM_REQ_BSS_TERMINATION_INCLUDED) && ctrl->bss_term_dur) {
memcpy(frm, ctrl->bss_term_dur, sizeof(struct ieee80211_ie_btm_bss_termdur));
frm += sizeof(struct ieee80211_ie_btm_bss_termdur);
}
if (ctrl->url) {
*frm++ = url_len;
memcpy(frm, ctrl->url, url_len);
frm += url_len;
}
if (ctrl->neigh_reports) {
memcpy(frm, ctrl->neigh_reports, ctrl->neigh_reports_length);
frm += ctrl->neigh_reports_length;
}
skb_trim(skb, frm - skb->data);
*pp_skb = skb;
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ACTION, "Sending BTM request to %pM token %u\n",
ni->ni_macaddr, tx_token);
return 0;
}
/*
* Send a management frame. The node is for the destination (or ic_bss
* when in station mode). Nodes other than ic_bss have their reference
* count bumped to reflect our use for an indeterminate time.
*/
int
ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb = NULL;
u_int8_t *frm;
int16_t htinfo_channel_width = 0;
int16_t htinfo_2nd_channel_offset = 0;
u_int16_t capinfo, def_keyindex;
int has_challenge, is_shared_key, ret, timer, status, is_bcast_probe, expired_timer;
enum ieee80211_phymode mode;
struct ieee80211_wme_state *wme = ieee80211_vap_get_wmestate(vap);
int ap_pure_tkip = 0;
int sta_pure_tkip = 0;
int is_11b_ap;
KASSERT(ni != NULL, ("null node"));
if (vap->iv_opmode == IEEE80211_M_STA && ni != vap->iv_bss) {
/*
* In Roaming cases, STA may receive null data frames from old AP
* if it's not disassociated properly.
* We add a exception here so that STA can send De-auth to old AP.
*/
if (type != IEEE80211_FC0_SUBTYPE_ACTION && type != IEEE80211_FC0_SUBTYPE_DEAUTH)
return 0;
}
ieee80211_ref_node(ni);
mode = ic->ic_curmode;
timer = 0;
if (vap->iv_bss && !vap->allow_tkip_for_vht) {
ap_pure_tkip = (vap->iv_bss->ni_rsn.rsn_ucastcipherset == IEEE80211_C_TKIP);
sta_pure_tkip = (vap->iv_bss->ni_rsn.rsn_ucastcipher == IEEE80211_CIPHER_TKIP);
}
switch (type) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
/*
* probe response frame format
* [8] time stamp
* [2] beacon interval
* [2] capability information
* [tlv] ssid
* [tlv] supported rates
* [7] FH/DS parameter set
* [tlv] IBSS parameter set
* [tlv] country code
* [3] power constraint
* [4] tpc report
* [tlv] Channel Switch Announcement
* [3] extended rate phy (ERP)
* [tlv] extended supported rates
* [tlv] WME parameters
* [tlv] WPA/RSN parameters
* [tlv] Atheros Advanced Capabilities
* [tlv] AtherosXR parameters
* [tlv] Quantenna parameters (probe resp)
* [tlv] QTN IE
*/
skb = ieee80211_getmgtframe(&frm,
8 /* time stamp */
+ sizeof(u_int16_t) /* beacon interval */
+ sizeof(u_int16_t) /* capability information */
+ 2 + IEEE80211_NWID_LEN /* ssid */
+ 2 + IEEE80211_RATE_SIZE /* supported rates */
+ 7 /* FH/DS parameters max(7,3) */
/* XXX allocate max size */
+ 4 /* IBSS parameter set*/
+ 2 + ic->ic_country_ie.country_len /* country code */
+ ((vap->interworking) ? 7 : 0) /* BSS load */
+ 3 /* power constraint */
+ 4 /* tpc report */
+ IEEE80211_CHANSWITCHANN_BYTES /* CSA */
+ 3 /* ERP */
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ sizeof(struct ieee80211_wme_param)
/* XXX !WPA1+WPA2 fits w/o a cluster */
+ (vap->iv_flags & IEEE80211_F_WPA ?
2 * sizeof(struct ieee80211_ie_wpa) : 0)
+ ((ic->ic_curmode >= IEEE80211_MODE_11NA) ?
(sizeof(struct ieee80211_ie_htcap) +
sizeof(struct ieee80211_ie_htinfo)) : 0)
+ sizeof(struct ieee80211_ie_athAdvCap)
+ vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].length
+ sizeof(struct ieee80211_ie_qtn)
+ sizeof(struct ieee80211_qtn_ext_role)
+ sizeof(struct ieee80211_qtn_ext_bssid)
+ (vap->qtn_pairing_ie.ie ? sizeof(struct ieee80211_ie_qtn_pairing) : 0)
+ (IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) ?
(sizeof(struct ieee80211_ie_vhtcap) +
sizeof(struct ieee80211_ie_vhtop) +
sizeof(struct ieee80211_ie_vtxpwren)) : 0)
+ ((IS_IEEE80211_11NG(ic)) ?
(sizeof(struct ieee80211_20_40_coex_param) +
sizeof(struct ieee80211_obss_scan_ie)) : 0)
+ (IEEE80211_COM_NEIGHREPORT_ENABLED(ic) ? sizeof(struct ieee80211_ie_rrm) : 0)
);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
/* timestamp should be filled later */
memset(frm, 0, 8);
frm += 8;
/* beacon interval */
*(__le16 *)frm = htole16(vap->iv_bss ? vap->iv_bss->ni_intval : IEEE80211_BINTVAL_DEFAULT);
frm += 2;
/* cap. info */
if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = IEEE80211_CAPINFO_ESS;
if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if (ic->ic_flags & IEEE80211_F_DOTH)
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
*(__le16 *)frm = htole16(capinfo);
frm += 2;
/* ssid */
is_bcast_probe = arg;
if ((!vap->iv_bss) || ((vap->iv_flags & IEEE80211_F_HIDESSID)
&& is_bcast_probe)) {
frm = ieee80211_add_ssid(frm, (u_int8_t *)"", 0);
} else {
frm = ieee80211_add_ssid(frm, vap->iv_bss->ni_essid,
vap->iv_bss->ni_esslen);
}
/* supported rates */
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
/* XXX: FH/DS parameter set, correct ? */
if (ic->ic_phytype == IEEE80211_T_FH) {
*frm++ = IEEE80211_ELEMID_FHPARMS;
*frm++ = 5;
*frm++ = ni->ni_fhdwell & 0x00ff;
*frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
*frm++ = IEEE80211_FH_CHANSET(
ieee80211_chan2ieee(ic, ic->ic_curchan));
*frm++ = IEEE80211_FH_CHANPAT(
ieee80211_chan2ieee(ic, ic->ic_curchan));
*frm++ = ni->ni_fhindex;
} else {
*frm++ = IEEE80211_ELEMID_DSPARMS;
*frm++ = 1;
*frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
}
if (vap->iv_opmode == IEEE80211_M_IBSS) {
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0;
*frm++ = 0; /* TODO: ATIM window */
}
frm = ieee80211_add_bss_load(frm, vap);
/*
* Tight coupling between Country IE and Power Constraint IE
* Both using IEEE80211_FEXT_COUNTRYIE to optional enable them.
*/
/* country code */
if ((ic->ic_flags_ext & IEEE80211_FEXT_COUNTRYIE) ||
((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)))
frm = ieee80211_add_country(frm, ic);
/* power constraint */
if (((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_COUNTRYIE)) ||
((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext && IEEE80211_FEXT_TPC))) {
*frm++ = IEEE80211_ELEMID_PWRCNSTR;
*frm++ = 1;
*frm++ = IEEE80211_PWRCONSTRAINT_VAL(ic);
}
if (IS_IEEE80211_11NG(ic)) {
frm = ieee80211_add_20_40_bss_coex_ie(frm, vap->iv_coex);
frm = ieee80211_add_obss_scan_ie(frm, &ic->ic_obss_ie);
}
/* Transmit power envelope */
if (IS_IEEE80211_VHT_ENABLED(ic) && (ic->ic_flags & IEEE80211_F_DOTH)) {
frm = ieee80211_add_vhttxpwr_envelope(frm, ic);
}
/*TPC Report*/
if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) {
*frm++ = IEEE80211_ELEMID_TPCREP;
*frm++ = 2;
*frm++ = ic->ic_get_local_txpow(ic); /* tx power would be updated in macfw */
*frm++ = 0; /* link margin is 0 */
}
/* CSA */
if (ic->ic_csa_count) {
frm = ieee80211_add_csa(frm, ic->ic_csa_mode,
ic->ic_csa_chan->ic_ieee, ic->ic_csa_count);
ieee80211_add_sec_chan_off(&frm, ic, ic->ic_csa_chan->ic_ieee);
}
/* ERP */
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
((ic->ic_curmode == IEEE80211_MODE_11A) ||
(ic->ic_curmode == IEEE80211_MODE_11B))) {
frm = ieee80211_add_erp(frm, ic);
}
ieee80211_get_channel_bw_offset(ic, &htinfo_channel_width, &htinfo_2nd_channel_offset);
/* 802.11n specific IEs */
if (IEEE80211_IS_CHAN_ANYN(ic->ic_bsschan) &&
(ic->ic_curmode >= IEEE80211_MODE_11NA) && !ap_pure_tkip) {
frm = ieee80211_add_htcap(ni, frm, &ic->ic_htcap, type);
ic->ic_htinfo.ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
ic->ic_htinfo.byte1 |= (htinfo_channel_width ? IEEE80211_HTINFO_B1_REC_TXCHWIDTH_40 : 0x0);
ic->ic_htinfo.choffset = htinfo_2nd_channel_offset;
frm = ieee80211_add_htinfo(ni, frm, &ic->ic_htinfo);
}
/* Ext. Supp. Rates */
frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
/* WME */
if (vap->iv_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, wme,
IEEE80211_VAP_UAPSD_ENABLED(vap), 0);
/* WPA */
if (!vap->iv_osen && vap->iv_bss && (vap->iv_flags & IEEE80211_F_WPA))
frm = ieee80211_add_wpa(frm, vap);
/* RRM enabled IE */
if (IEEE80211_COM_NEIGHREPORT_ENABLED(ic))
frm = ieee80211_add_rrm_enabled(frm, vap);
/* Mobility Domain IE */
if (vap->iv_mdid) {
frm = ieee80211_add_mdie(frm, vap);
}
/* AthAdvCaps */
if (vap->iv_bss && vap->iv_bss->ni_ath_flags)
frm = ieee80211_add_athAdvCap(frm, vap->iv_bss->ni_ath_flags,
vap->iv_bss->ni_ath_defkeyindex);
if (vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].ie) {
memcpy(frm, vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].ie,
vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].length);
ieee80211_update_bss_tm(frm,
vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].length, ic, vap);
frm += vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].length;
}
if (vap->qtn_pairing_ie.ie) {
frm = ieee80211_add_qtn_pairing_ie(frm, &vap->qtn_pairing_ie);
}
if (IS_IEEE80211_VHT_ENABLED(ic) && !ap_pure_tkip) {
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap, type);
/* VHT Operation element */
if ((IEEE80211_IS_VHT_40(ic)) || (IEEE80211_IS_VHT_20(ic))) {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ;
ic->ic_vhtop.centerfreq0 = 0;
} else if (IEEE80211_IS_VHT_80(ic)) {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_80MHZ;
ic->ic_vhtop.centerfreq0 = ic->ic_bsschan->ic_center_f_80MHz;
} else {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_160MHZ;
ic->ic_vhtop.centerfreq0 = ic->ic_bsschan->ic_center_f_160MHz;
}
frm = ieee80211_add_vhtop(ni, frm, &ic->ic_vhtop);
} else if (IS_IEEE80211_11NG_VHT_ENABLED(ic) && !ap_pure_tkip) {
/* QTN 2.4G VHT IE */
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap_24g, type);
frm = ieee80211_add_vhtop(ni, frm, &ic->ic_vhtop_24g);
}
frm = ieee80211_add_qtn_ie(frm, ic,
(vap->iv_flags_ext & IEEE80211_FEXT_WDS ? (IEEE80211_QTN_BRIDGEMODE) : 0),
(vap->iv_flags_ext & IEEE80211_FEXT_WDS ?
(IEEE80211_QTN_BRIDGEMODE | IEEE80211_QTN_LNCB) : 0),
0, 0, 0);
/* Add Quantenna extender IE */
if (!IEEE80211_COM_WDS_IS_NONE(ic) && (vap == TAILQ_FIRST(&ic->ic_vaps))) {
frm = ieee80211_add_qtn_extender_role_ie(frm, ic->ic_extender_role);
frm = ieee80211_add_qtn_extender_bssid_ie(vap, frm);
frm = ieee80211_add_qtn_extender_state_ie(frm, !!ic->ic_ocac.ocac_cfg.ocac_enable);
}
#ifdef CONFIG_QVSP
/* QTN WME IE */
if (ic->ic_wme.wme_throt_bm && ic->ic_wme.wme_throt_add_qwme_ie &&
(vap->iv_flags & IEEE80211_F_WME)) {
frm = ieee80211_add_qtn_wme_param(vap, frm);
}
#endif
skb_trim(skb, frm - skb->data);
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
status = arg >> 16;
arg &= 0xffff;
has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
ni->ni_challenge != NULL);
uint8_t len = 0;
uint8_t *data = NULL;
/*
* Deduce whether we're doing open authentication or
* shared key authentication. We do the latter if
* we're in the middle of a shared key authentication
* handshake or if we're initiating an authentication
* request and configured to use shared key.
*/
is_shared_key = has_challenge ||
arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
(arg == IEEE80211_AUTH_SHARED_REQUEST &&
vap->iv_bss &&
vap->iv_bss->ni_authmode == IEEE80211_AUTH_SHARED);
len = 3 * sizeof(u_int16_t)
+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0);
if (ni->ni_tx_md_ie && ni->ni_tx_md_ie[1] > 0)
len += ni->ni_tx_md_ie[1] + 2;
if (ni->ni_tx_ft_ie && ni->ni_tx_ft_ie[1] > 0)
len += ni->ni_tx_ft_ie[1] + 2;
if (ni->ni_tx_rsn_ie && ni->ni_tx_rsn_ie[1] > 0)
len += ni->ni_tx_rsn_ie[1] + 2;
skb = ieee80211_getmgtframe(&frm, len);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
((__le16 *)frm)[0] =
(is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
: htole16(IEEE80211_AUTH_ALG_OPEN);
((__le16 *)frm)[1] = htole16(arg); /* sequence number */
((__le16 *)frm)[2] = htole16(status); /* status */
if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
((__le16 *)frm)[3] =
htole16((IEEE80211_CHALLENGE_LEN << 8) |
IEEE80211_ELEMID_CHALLENGE);
memcpy(&((__le16 *)frm)[4], ni->ni_challenge,
IEEE80211_CHALLENGE_LEN);
if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
"request encrypt frame (%s)", __func__);
M_FLAG_SET(skb, M_LINK0);
}
}
if ( arg == IEEE80211_AUTH_FT) {
((__le16 *)frm)[0] = htole16(IEEE80211_AUTH_ALG_FT);
((__le16 *)frm)[1] = htole16(2); /* sequence number */
if (status == IEEE80211_STATUS_SUCCESS) {
data = &frm[6];
if (ni->ni_tx_md_ie) {
memcpy(data, ni->ni_tx_md_ie, ni->ni_tx_md_ie[1] + 2);
data += ni->ni_tx_md_ie[1] + 2;
}
if (ni->ni_tx_ft_ie) {
memcpy(data, ni->ni_tx_ft_ie, ni->ni_tx_ft_ie[1] + 2);
data += ni->ni_tx_ft_ie[1] + 2;
}
if (ni->ni_tx_rsn_ie) {
memcpy(data, ni->ni_tx_rsn_ie, ni->ni_tx_rsn_ie[1] + 2);
data += ni->ni_tx_rsn_ie[1] + 2;
}
}
}
/* XXX not right for shared key */
if (status == IEEE80211_STATUS_SUCCESS) {
IEEE80211_NODE_STAT(ni, tx_auth);
if (arg == IEEE80211_AUTH_OPEN_RESPONSE && vap->iv_opmode == IEEE80211_M_HOSTAP) {
char event_string[IW_CUSTOM_MAX]; /* Buffer for IWEVENT message */
union iwreq_data wreq;
memset(&wreq, 0, sizeof(wreq));
snprintf(event_string,IW_CUSTOM_MAX,"%sClient authenticated [%pM]",
QEVT_COMMON_PREFIX, ni->ni_macaddr);
wreq.data.length = strlen(event_string);
wireless_send_event(vap->iv_dev, IWEVCUSTOM, &wreq, event_string);
}
} else {
IEEE80211_NODE_STAT(ni, tx_auth_fail);
if (arg == IEEE80211_AUTH_OPEN_RESPONSE && vap->iv_opmode == IEEE80211_M_HOSTAP) {
char event_string[IW_CUSTOM_MAX]; /* Buffer for IWEVENT message */
union iwreq_data wreq;
memset(&wreq, 0, sizeof(wreq));
snprintf(event_string,IW_CUSTOM_MAX,"%sClient failed to authenticate [%pM]",
QEVT_COMMON_PREFIX, ni->ni_macaddr);
wreq.data.length = strlen(event_string);
wireless_send_event(vap->iv_dev, IWEVCUSTOM, &wreq, event_string);
}
}
if (vap->iv_opmode == IEEE80211_M_STA)
timer = IEEE80211_TRANS_WAIT;
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
"send station deauthenticate (reason %d)", arg);
skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*(__le16 *)frm = htole16(arg); /* reason */
IEEE80211_NODE_STAT(ni, tx_deauth);
IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
{
int msg = IEEE80211_DOT11_MSG_CLIENT_REMOVED;
if (vap->iv_opmode == IEEE80211_M_STA) {
msg = IEEE80211_DOT11_MSG_AP_DISCONNECTED;
}
if (arg == IEEE80211_REASON_AUTH_EXPIRE) {
ieee80211_eventf(vap->iv_dev,"%s[WLAN access rejected: incorrect "
"security] from MAC address %pM", QEVT_ACL_PREFIX,
ni->ni_macaddr);
}
#if defined(CONFIG_QTN_BSA_SUPPORT)
if ((vap->bsa_status == BSA_STATUS_ACTIVE) &&
(vap->iv_opmode == IEEE80211_M_HOSTAP))
ieee80211_bsa_disconnect_event_send(vap, ni, arg,
IEEE80211_FC0_SUBTYPE_DEAUTH,
BSA_DISCONNECT_SELF_GENERATED);
#endif
ieee80211_dot11_msg_send(ni->ni_vap,
(char *)ni->ni_macaddr,
d11_m[msg],
d11_c[IEEE80211_DOT11_MSG_REASON_DEAUTHENTICATED],
arg,
(arg < DOT11_MAX_REASON_CODE) ? d11_r[arg] : "Reserved",
NULL,
NULL);
}
ieee80211_node_unauthorize(ni); /* port closed */
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
/*
* asreq frame format
* [2] capability information
* [2] listen interval
* [6*] current AP address (reassoc only)
* [tlv] ssid
* [tlv] supported rates
* [4] power capability (802.11h)
* [54] supported channels element (802.11h)
* [tlv] extended supported rates
* [tlv] WME [if enabled and AP capable]
* [tlv] Atheros advanced capabilities
* [tlv] user-specified ie's
* [tlv] QTN IE
*/
skb = ieee80211_getmgtframe(&frm,
sizeof(u_int16_t) +
sizeof(u_int16_t) +
IEEE80211_ADDR_LEN +
2 + IEEE80211_NWID_LEN +
2 + IEEE80211_RATE_SIZE +
4 + (2 + IEEE80211_SUPPCHAN_LEN) +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) +
((ic->ic_curmode >= IEEE80211_MODE_11NA) ?
(sizeof(struct ieee80211_ie_htcap) +
sizeof(struct ieee80211_extcap_param)) : 0) +
sizeof(struct ieee80211_ie_wme) +
sizeof(struct ieee80211_ie_athAdvCap) +
(vap->iv_opt_ie != NULL ? vap->iv_opt_ie_len : 0) +
vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].length +
sizeof(struct ieee80211_ie_qtn) +
(vap->qtn_pairing_ie.ie ? sizeof(struct ieee80211_ie_qtn_pairing) : 0) +
(IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) ?
sizeof(struct ieee80211_ie_vhtcap) +
sizeof(struct ieee80211_ie_vhtop_notif) : 0)
);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
capinfo = 0;
if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo |= IEEE80211_CAPINFO_IBSS;
else /* IEEE80211_M_STA */
capinfo |= IEEE80211_CAPINFO_ESS;
if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
/*
* NB: Some 11a AP's reject the request when
* short premable is set.
*/
/* Capability information */
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if ((ic->ic_flags & IEEE80211_F_DOTH) && vap->iv_bss &&
(vap->iv_bss->ni_flags & IEEE80211_NODE_HT)) {
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
}
*(__le16 *)frm = htole16(capinfo);
frm += 2;
/* listen interval */
*(__le16 *)frm = htole16(ic->ic_lintval / ni->ni_intval);
frm += 2;
/* Current AP address */
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
if (vap->iv_bss) {
IEEE80211_ADDR_COPY(frm, vap->iv_bss->ni_bssid);
} else {
printk(KERN_ERR "Sending Reassoc Req frame"
" with NULL BSSID \n");
memset(frm, 0, IEEE80211_ADDR_LEN);
}
frm += IEEE80211_ADDR_LEN;
}
/* ssid */
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
is_11b_ap = ieee80211_check_11b_ap(ni);
/* supported rates */
if (!is_11b_ap) {
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
} else {
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[IEEE80211_MODE_11B]);
}
if ((ic->ic_curmode >= IEEE80211_MODE_11NA) && vap->iv_bss &&
(vap->iv_bss->ni_flags & IEEE80211_NODE_HT) && !sta_pure_tkip) {
frm = ieee80211_add_htcap(ni, frm, &ic->ic_htcap, type);
/* Ext. Capabilities - For AP mode hostapd adds the extended cap */
if (vap->iv_opmode == IEEE80211_M_STA)
frm = ieee80211_add_extcap(frm);
}
/* ext. supp. rates */
if (!is_11b_ap) {
frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
}
/* power capability/supported channels
* in chapter 8.3.3.5, power capability IE is right after extended supported rates
* and before supported channels
* */
if (ic->ic_flags & IEEE80211_F_DOTH)
frm = ieee80211_add_doth(frm, ic);
/* Supported Channels */
frm = ieee80211_add_supported_chans(frm, ic);
/* WME */
if ((vap->iv_flags & IEEE80211_F_WME) && (ni->ni_wme_ie != NULL))
frm = ieee80211_add_wme(frm, ni);
/* ath adv. cap */
if (ni->ni_ath_flags & vap->iv_ath_cap) {
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
"Adding ath adv cap ie: ni_ath_flags = %02x, "
"iv_ath_cap = %02x", ni->ni_ath_flags,
vap->iv_ath_cap);
/* Setup default key index for static wep case */
def_keyindex = IEEE80211_INVAL_DEFKEY;
if (((vap->iv_flags & IEEE80211_F_WPA) == 0) &&
(ni->ni_authmode != IEEE80211_AUTH_8021X) &&
(vap->iv_def_txkey != IEEE80211_KEYIX_NONE))
def_keyindex = vap->iv_def_txkey;
frm = ieee80211_add_athAdvCap(frm,
ni->ni_ath_flags & vap->iv_ath_cap,
def_keyindex);
}
/* 802.11ac vht capability */
if (IS_IEEE80211_VHT_ENABLED(ic) && !sta_pure_tkip) {
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap, type);
frm = ieee80211_add_vhtop_notif(ni, frm, ic, 0);
} else if (IS_IEEE80211_11NG_VHT_ENABLED(ic) && !sta_pure_tkip) {
/* QTN 2.4G VHT IE */
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap_24g, type);
frm = ieee80211_add_vhtop_notif(ni, frm, ic, 1);
}
/* User-spec */
if (vap->iv_opt_ie != NULL) {
memcpy(frm, vap->iv_opt_ie, vap->iv_opt_ie_len);
frm += vap->iv_opt_ie_len;
}
if (vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].ie) {
memcpy(frm, vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].ie,
vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].length);
frm += vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].length;
}
frm = ieee80211_add_qtn_ie(frm, ic,
(vap->iv_flags_ext & IEEE80211_FEXT_WDS ? (IEEE80211_QTN_BRIDGEMODE) : 0),
(vap->iv_flags_ext & IEEE80211_FEXT_WDS ?
(IEEE80211_QTN_BRIDGEMODE | IEEE80211_QTN_LNCB) : 0),
vap->iv_implicit_ba, IEEE80211_DEFAULT_BA_WINSIZE_H,
ni->ni_rate_train);
/* Add QTN Pairing IE */
if (vap->qtn_pairing_ie.ie) {
frm = ieee80211_add_qtn_pairing_ie(frm, &vap->qtn_pairing_ie);
}
skb_trim(skb, frm - skb->data);
timer = IEEE80211_TRANS_WAIT;
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
"send station %s",
(type == IEEE80211_FC0_SUBTYPE_ASSOC_REQ) ? "assoc_req" : "reassoc_req");
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
/*
* asreq frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME (if enabled and STA enabled)
* [tlv] Atheros Advanced Capabilities
* [tlv] QTN IE
* [tlv] VSP IE
*/
skb = ieee80211_getmgtframe(&frm,
3 * sizeof(u_int16_t) +
2 + IEEE80211_RATE_SIZE +
2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) +
((IEEE80211_IS_CHAN_ANYN(ic->ic_bsschan) &&
(ic->ic_curmode >= IEEE80211_MODE_11NA)) ?
(sizeof(struct ieee80211_ie_htcap) +
sizeof(struct ieee80211_ie_htinfo)) : 0) +
sizeof(struct ieee80211_wme_param) +
(vap->iv_ath_cap ? sizeof(struct ieee80211_ie_athAdvCap) : 0) +
vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_RESP].length +
sizeof(struct ieee80211_ie_qtn) +
(vap->qtn_pairing_ie.ie ? sizeof(struct ieee80211_ie_qtn_pairing) : 0)
#ifdef CONFIG_QVSP
+ ieee80211_vsp_ie_max_len(ic)
#endif
+ (IS_IEEE80211_DUALBAND_VHT_ENABLED(ic) ?
(sizeof(struct ieee80211_ie_vhtcap) +
sizeof(struct ieee80211_ie_vhtop)) : 0)
+ ((IS_IEEE80211_11NG(ic)) ?
(sizeof(struct ieee80211_20_40_coex_param) +
sizeof(struct ieee80211_obss_scan_ie)) : 0)
+ (IEEE80211_COM_NEIGHREPORT_ENABLED(ic) ? sizeof(struct ieee80211_ie_rrm) : 0)
+ (ni->ni_tx_md_ie ? IEEE80211_MDIE_LEN + 2 : 0)
+ (ni->ni_tx_ft_ie ? ni->ni_tx_ft_ie[1] + 2: 0)
+ (ni->ni_rsn_ie ? ni->ni_rsn_ie[1] + 2: 0)
);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
/* Capability Information */
capinfo = IEEE80211_CAPINFO_ESS;
if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if (ic->ic_flags & IEEE80211_F_DOTH)
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
if (IEEE80211_COM_NEIGHREPORT_ENABLED(ic))
capinfo |= IEEE80211_CAPINFO_RM;
*(__le16 *)frm = htole16(capinfo);
frm += 2;
/* status */
*(__le16 *)frm = htole16(arg);
frm += 2;
/* Assoc ID */
if (arg == IEEE80211_STATUS_SUCCESS) {
*(__le16 *)frm = htole16(ni->ni_associd);
IEEE80211_NODE_STAT(ni, tx_assoc);
} else
IEEE80211_NODE_STAT(ni, tx_assoc_fail);
frm += 2;
/* supported rates */
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
if (IS_IEEE80211_11NG(ic)) {
frm = ieee80211_add_20_40_bss_coex_ie(frm, vap->iv_coex);
frm = ieee80211_add_obss_scan_ie(frm, &ic->ic_obss_ie);
}
/* 802.11w / PMF Timeout element */
if(vap->iv_pmf && arg == IEEE80211_STATUS_PMF_REJECT_RETRY) {
frm = ieee80211_add_timeout_ie(frm);
}
if (IEEE80211_IS_CHAN_ANYN(ic->ic_bsschan) &&
(ic->ic_curmode >= IEEE80211_MODE_11NA)) {
if (arg == IEEE80211_STATUS_SUCCESS) {
if ((ni->ni_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) == 0) {
vap->iv_ht_anomaly_40MHz_present = 1;
}
if ((ni->ni_htcap.cap & IEEE80211_HTCAP_C_GREENFIELD) == 0)
vap->iv_non_gf_sta_present = 1;
if ((ni->ni_htcap.cap & IEEE80211_HTCAP_C_LSIGTXOPPROT) == 0)
vap->iv_lsig_txop_ok = 0;
vap->iv_ht_flags |= IEEE80211_HTF_HTINFOUPDATE;
}
if (!ap_pure_tkip &&
(ni->ni_rsn.rsn_ucastcipher != IEEE80211_CIPHER_TKIP) &&
(ni->ni_flags >= IEEE80211_NODE_HT)) {
frm = ieee80211_add_htcap(ni, frm, &ic->ic_htcap, type);
frm = ieee80211_add_htinfo(ni, frm, &ic->ic_htinfo);
}
}
/* ext. suppo. rates */
frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
/* WME */
if ((vap->iv_flags & IEEE80211_F_WME) && (ni->ni_wme_ie != NULL))
frm = ieee80211_add_wme_param(frm, wme,
IEEE80211_VAP_UAPSD_ENABLED(vap), 0);
if (IEEE80211_COM_NEIGHREPORT_ENABLED(ic))
frm = ieee80211_add_rrm_enabled(frm, vap);
/* athAdvCap */
if (vap->iv_ath_cap)
frm = ieee80211_add_athAdvCap(frm,
vap->iv_ath_cap & ni->ni_ath_flags,
ni->ni_ath_defkeyindex);
if (vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_RESP].ie) {
memcpy(frm, vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_RESP].ie,
vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_RESP].length);
ieee80211_update_bss_tm(frm,
vap->app_ie[IEEE80211_APPIE_FRAME_PROBE_RESP].length, ic, vap);
frm += vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_RESP].length;
}
if (ni->ni_qtn_assoc_ie) {
frm = ieee80211_add_qtn_ie(frm, ic,
((struct ieee80211_ie_qtn *)ni->ni_qtn_assoc_ie)->qtn_ie_flags,
(vap->iv_flags_ext & IEEE80211_FEXT_WDS ?
(IEEE80211_QTN_BRIDGEMODE | IEEE80211_QTN_LNCB) : 0),
vap->iv_implicit_ba, IEEE80211_DEFAULT_BA_WINSIZE_H,
ni->ni_rate_train);
#ifdef CONFIG_QVSP
frm = ieee80211_add_vsp_ie(vap, frm, skb->end);
/* QTN WME IE */
if (ic->ic_wme.wme_throt_bm && ic->ic_wme.wme_throt_add_qwme_ie &&
(vap->iv_flags & IEEE80211_F_WME)) {
frm = ieee80211_add_qtn_wme_param(vap, frm);
}
#endif
}
if (!ap_pure_tkip && (ni->ni_flags & IEEE80211_NODE_VHT)) {
if (IS_IEEE80211_VHT_ENABLED(ic)) {
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap, type);
/* VHT Operation element */
if ((IEEE80211_IS_VHT_40(ic)) || (IEEE80211_IS_VHT_20(ic))) {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ;
ic->ic_vhtop.centerfreq0 = 0;
} else if (IEEE80211_IS_VHT_80(ic)) {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_80MHZ;
ic->ic_vhtop.centerfreq0 = ic->ic_bsschan->ic_center_f_80MHz;
} else {
ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_160MHZ;
ic->ic_vhtop.centerfreq0 = ic->ic_bsschan->ic_center_f_160MHz;
}
frm = ieee80211_add_vhtop(ni, frm, &ic->ic_vhtop);
} else if (IS_IEEE80211_11NG_VHT_ENABLED(ic)) {
/* QTN 2.4G VHT IE */
frm = ieee80211_add_vhtcap(ni, frm, &ic->ic_vhtcap_24g, type);
frm = ieee80211_add_vhtop(ni, frm, &ic->ic_vhtop_24g);
}
}
/* Add QTN Pairing IE. */
if (vap->qtn_pairing_ie.ie) {
frm = ieee80211_add_qtn_pairing_ie(frm, &vap->qtn_pairing_ie);
}
if (ni->ni_tx_md_ie) {
memcpy(frm, ni->ni_tx_md_ie, ni->ni_tx_md_ie[1] + 2);
frm += 5;
}
if (ni->ni_tx_ft_ie) {
memcpy(frm, ni->ni_tx_ft_ie, ni->ni_tx_ft_ie[1] + 2);
frm += ni->ni_tx_ft_ie[1] + 2;
}
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) {
if (ni->ni_tx_rsn_ie) {
memcpy(frm, ni->ni_tx_rsn_ie, ni->ni_tx_rsn_ie[1] + 2);
frm += ni->ni_tx_rsn_ie[1] + 2;
}
}
skb_trim(skb, frm - skb->data);
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
"send station %s (reason %d)",
(type == IEEE80211_FC0_SUBTYPE_ASSOC_RESP) ? "assoc_resp" : "reassoc_resp", arg);
if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
ieee80211_eventf(vap->iv_dev, "%sClient associated [%pM]",
QEVT_COMMON_PREFIX, ni->ni_macaddr);
}
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
"send station disassociate (reason %d)", arg);
skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*(__le16 *)frm = htole16(arg); /* reason */
{
int msg = IEEE80211_DOT11_MSG_CLIENT_REMOVED;
if (vap->iv_opmode == IEEE80211_M_STA) {
msg = IEEE80211_DOT11_MSG_AP_DISCONNECTED;
}
#if defined(CONFIG_QTN_BSA_SUPPORT)
if ((vap->bsa_status == BSA_STATUS_ACTIVE) &&
(vap->iv_opmode == IEEE80211_M_HOSTAP))
ieee80211_bsa_disconnect_event_send(vap, ni, arg,
IEEE80211_FC0_SUBTYPE_DISASSOC,
BSA_DISCONNECT_SELF_GENERATED);
#endif
ieee80211_dot11_msg_send(ni->ni_vap,
(char *)ni->ni_macaddr,
d11_m[msg],
d11_c[IEEE80211_DOT11_MSG_REASON_DISASSOCIATED],
arg,
(arg < DOT11_MAX_REASON_CODE) ? d11_r[arg] : "Reserved",
NULL,
NULL);
}
if (ic->ic_opmode == IEEE80211_M_STA)
del_timer_sync(&ic->ic_obss_timer);
IEEE80211_NODE_STAT(ni, tx_disassoc);
IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
break;
case IEEE80211_FC0_SUBTYPE_ACTION: {
u_int16_t temp16;
struct ieee80211_action_data *action_data = (struct ieee80211_action_data *)arg;
u_int8_t cat = action_data->cat;
IEEE80211_NODE_STAT(ni, tx_action);
switch (cat) {
case IEEE80211_ACTION_CAT_SPEC_MGMT:
switch (action_data->action) {
case IEEE80211_ACTION_S_TPC_REQUEST:
{
struct ieee80211_action_tpc_request *request;
u_int8_t tx_token;
request = (struct ieee80211_action_tpc_request *)action_data->params;
skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t) + /* action header */
1 + /* dialog token */
2); /* tpc request ie */
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*frm++ = IEEE80211_ACTION_CAT_SPEC_MGMT;
*frm++ = IEEE80211_ACTION_S_TPC_REQUEST;
tx_token = ni->ni_action_token++;
*frm++ = tx_token;
*frm++ = IEEE80211_ELEMID_TPCREQ;
*frm++ = 0;
if (request->expire != 0) {
skb = ieee80211_ppqueue_pre_tx(ni,
skb,
IEEE80211_ACTION_CAT_SPEC_MGMT,
IEEE80211_ACTION_S_TPC_REPORT,
tx_token,
request->expire,
request->fn_success,
request->fn_fail);
if (skb == NULL) {
ret = -ENOMEM;
goto bad;
}
}
break;
}
case IEEE80211_ACTION_S_TPC_REPORT: {
struct ieee80211_action_tpc_report *tpc_report = (struct ieee80211_action_tpc_report *)action_data->params;
skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t) + /* action header */
1 + /* dialog token */
4); /* tpc report ie */
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*frm++ = IEEE80211_ACTION_CAT_SPEC_MGMT;
*frm++ = IEEE80211_ACTION_S_TPC_REPORT;
*frm++ = tpc_report->rx_token;
*frm++ = IEEE80211_ELEMID_TPCREP;
*frm++ = 2;
*frm++ = tpc_report->tx_power;
*frm++ = tpc_report->link_margin;
break;
}
case IEEE80211_ACTION_S_MEASUREMENT_REQUEST:
case IEEE80211_ACTION_S_MEASUREMENT_REPORT:
ret = ieee80211_compile_action_measurement_11h(ni, action_data->params, action_data->action, &skb);
if (ret != 0)
goto bad;
break;
default:
break;
}
break;
case IEEE80211_ACTION_CAT_HT: {
switch (action_data->action) {
case IEEE80211_ACTION_HT_NCBEAMFORMING:
IEEE80211_DPRINTF(vap,IEEE80211_MSG_OUTPUT,
"Err: Action frame construction not suppported\n",0);
break;
case IEEE80211_ACTION_HT_MIMOPWRSAVE:
{
/* Form the HT SM PS frame - change of mode for this client. */
/* Single byte argument, which is formatted as per 802.11n d11.0 section 7.3.1.22 */
u_int8_t *p_byte = (u_int8_t *)&action_data->params;
skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t) + /* action header */
1 /* SMPS state change */ );
if (skb == NULL) {
senderr(ENOMEM, is_tx_nobuf);
}
*(u_int8_t *)frm = IEEE80211_ACTION_CAT_HT;
frm += 1;
*(u_int8_t *)frm = IEEE80211_ACTION_HT_MIMOPWRSAVE;
frm += 1;
*(u_int8_t *)frm = *p_byte; /* New power save mode */
frm += 1;
}
break;
default:
break;
}
if(skb != NULL) {
skb_trim(skb, frm - skb->data);
}
break;
}
case IEEE80211_ACTION_CAT_BA: {
switch (action_data->action) {
case IEEE80211_ACTION_BA_ADDBA_REQ: {
struct ba_action_req *ba = (struct ba_action_req *)action_data->params;
skb = ieee80211_getmgtframe(&frm,
sizeof(u_int16_t) + /* action header */
sizeof(u_int8_t) + /* dialog */
sizeof(u_int16_t) + /* BA params */
sizeof(u_int16_t) + /* BA timeout */
sizeof(u_int16_t) /* BA sequence control */
);
if (skb == NULL) {
senderr(ENOMEM, is_tx_nobuf);
}
*(u_int8_t *)frm = IEEE80211_ACTION_CAT_BA;
frm += 1;
*(u_int8_t *)frm = IEEE80211_ACTION_BA_ADDBA_REQ;
frm += 1;
/* fill ba dialog */
ni->ni_ba_tx[ba->tid].dlg_out = (ni->ni_ba_tx[ba->tid].dlg_out + 1) % 0xFF;
*(u_int8_t *)frm = ni->ni_ba_tx[ba->tid].dlg_out;
frm += 1;
/* fill ba params (non half word aligned) */
temp16 = 0;
temp16 |= ((ba->type == IEEE80211_BA_DELAYED) ?
IEEE80211_A_BA_DELAYED : IEEE80211_A_BA_IMMEDIATE);
temp16 |= ((ba->tid) << IEEE80211_A_BA_TID_S);
temp16 |= ((ba->buff_size) << IEEE80211_A_BA_BUFF_SIZE_S);
if (!ieee80211_tx_amsdu_disabled(ni))
temp16 |= IEEE80211_A_BA_AMSDU_SUPPORTED;
*(u_int8_t *)frm = temp16 & 0xFF;
frm += 1;
*(u_int8_t *)frm = temp16 >> 8;
frm += 1;
/* fill ba timeout (non half word aligned) */
*(u_int8_t *)frm = ba->timeout & 0xFF;
frm += 1;
*(u_int8_t *)frm = ba->timeout >> 8;
frm += 1;
/* fill sequence control (non half word aligned) */
*(u_int8_t *)frm = ba->frag | ((ba->seq & 0xF) << 4);
frm += 1;
*(u_int8_t *)frm = (ba->seq & 0xFF0) >> 4;
frm += 1;
break;
}
case IEEE80211_ACTION_BA_ADDBA_RESP: {
struct ba_action_resp *ba = (struct ba_action_resp *)action_data->params;
skb = ieee80211_getmgtframe(&frm,
sizeof(u_int16_t) + /* action header */
sizeof(u_int8_t) + /* dialog */
sizeof(u_int16_t) + /* status */
sizeof(u_int16_t) + /* BA params */
sizeof(u_int16_t) /* BA timeout */
);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*(u_int8_t *)frm = IEEE80211_ACTION_CAT_BA;
frm += 1;
*(u_int8_t *)frm = IEEE80211_ACTION_BA_ADDBA_RESP;
frm += 1;
/* fill ba dialog */
*(u_int8_t *)frm = ni->ni_ba_rx[ba->tid].dlg_in;
frm += 1;
/* fill ba status (non half word aligned) */
*(u_int8_t *)frm = ba->reason & 0xFF;
frm += 1;
*(u_int8_t *)frm = ba->reason >> 8;
frm += 1;
/* fill ba params (non half word aligned) */
temp16 = 0;
temp16 |= ((ba->type == IEEE80211_BA_DELAYED) ?
IEEE80211_A_BA_DELAYED : IEEE80211_A_BA_IMMEDIATE);
temp16 |= ((ba->tid) << IEEE80211_A_BA_TID_S);
temp16 |= ((ba->buff_size) << IEEE80211_A_BA_BUFF_SIZE_S);
if (!ieee80211_rx_amsdu_allowed(ni)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACTION,
"receive AMSDU within AMPDU is not permitted from \
station %pM\n", ni->ni_macaddr);
temp16 &= ~IEEE80211_A_BA_AMSDU_SUPPORTED;
} else {
temp16 |= IEEE80211_A_BA_AMSDU_SUPPORTED;
}
*(u_int8_t *)frm = temp16 & 0xFF;
frm += 1;
*(u_int8_t *)frm = temp16 >> 8;
frm += 1;
/* fill ba timeout (non half word aligned) */
*(u_int8_t *)frm = ba->timeout & 0xFF;
frm += 1;
*(u_int8_t *)frm = ba->timeout >> 8;
frm += 1;
break;
}
case IEEE80211_ACTION_BA_DELBA: {
struct ba_action_del *ba = (struct ba_action_del *)action_data->params;
skb = ieee80211_getmgtframe(&frm,
sizeof(u_int16_t) + /* action header */
sizeof(u_int16_t) + /* DELBA params */
sizeof(u_int16_t) /* DELBA reason */
);
if (skb == NULL)
senderr(ENOMEM, is_tx_nobuf);
*(u_int8_t *)frm = IEEE80211_ACTION_CAT_BA;
frm += 1;
*(u_int8_t *)frm = IEEE80211_ACTION_BA_DELBA;
frm += 1;
/* fill ba params (non half word aligned) */
temp16 = 0;
temp16 |= SM(ba->tid, IEEE80211_A_BA_DELBA_TID);
temp16 |= SM(ba->initiator, IEEE80211_A_BA_INITIATOR);
*(u_int8_t *)frm = temp16 & 0xFF;
frm += 1;
*(u_int8_t *)frm = temp16 >> 8;
frm += 1;
/* fill ba reason (non half word aligned) */
*(u_int8_t *)frm = ba->reason & 0xFF;
frm += 1;
*(u_int8_t *)frm = ba->reason >> 8;
frm += 1;
break;
}
default:
break;
}
if(skb != NULL) {
skb_trim(skb, frm - skb->data);
}
break;
}
case IEEE80211_ACTION_CAT_RM:
{
switch (action_data->action) {
case IEEE80211_ACTION_R_MEASUREMENT_REQUEST:
case IEEE80211_ACTION_R_MEASUREMENT_REPORT:
ret = ieee80211_compile_action_measurement_11k(ni, action_data->params, action_data->action, &skb);
break;
case IEEE80211_ACTION_R_LINKMEASURE_REQUEST:
ret = ieee80211_compile_action_link_measure_request(ni, action_data->params, &skb);
break;
case IEEE80211_ACTION_R_LINKMEASURE_REPORT:
ret = ieee80211_compile_action_link_measure_report(ni, action_data->params, &skb);
break;
case IEEE80211_ACTION_R_NEIGHBOR_REQUEST:
ret = ieee80211_compile_action_neighbor_report_request(ni, action_data->params, &skb);
break;
case IEEE80211_ACTION_R_NEIGHBOR_REPORT:
ret = ieee80211_compile_action_neighbor_report_response(ni, action_data->params, &skb);
break;
default:
ret = -1;
break;
}
if (ret != 0)
goto bad;
break;
}
#ifdef CONFIG_QVSP
case IEEE80211_ACTION_CAT_VENDOR: {
/* FIXME: Work out which vendor specific type to output. */
struct ieee80211_qvsp_act *qvsp_a = (struct ieee80211_qvsp_act *)action_data->params;
ret = ieee80211_compile_action_qvsp_frame(vap, qvsp_a, &skb);
if (ret != 0)
goto bad;
break;
}
#endif
case IEEE80211_ACTION_CAT_SA_QUERY: {
ret = ieee80211_compile_action_sa_query_frame(vap, action_data, &skb);
if (ret != 0)
goto bad;
break;
}
case IEEE80211_ACTION_CAT_PUBLIC:
if (action_data->action == IEEE80211_ACTION_PUB_20_40_COEX) {
ret = ieee80211_compile_action_20_40_coex_frame(vap,
action_data, &skb, ni);
if (ret != 0)
goto bad;
break;
}
/* fall through if condition is not true */
case IEEE80211_ACTION_CAT_FBSS:
case IEEE80211_ACTION_CAT_QOS: {
struct action_frame_payload *frm_payload =
(struct action_frame_payload *)action_data->params;
skb = ieee80211_getmgtframe(&frm, frm_payload->length);
if (skb == NULL) {
senderr(ENOMEM, is_tx_nobuf);
} else {
memcpy(frm, frm_payload->data, frm_payload->length);
frm += frm_payload->length;
}
break;
}
case IEEE80211_ACTION_CAT_WNM: {
if (action_data->action == IEEE80211_WNM_BSS_TRANS_MGMT_REQ) {
struct btm_request_params *ctrl
= (struct btm_request_params *)action_data->params;
ret = ieee80211_compile_action_btm_req(ni, ctrl, &skb);
if (ret != 0)
goto bad;
} else {
struct action_frame_payload *frm_payload =
(struct action_frame_payload *)action_data->params;
skb = ieee80211_getmgtframe(&frm, frm_payload->length);
if (skb == NULL) {
senderr(ENOMEM, is_tx_nobuf);
} else {
memcpy(frm, frm_payload->data, frm_payload->length);
frm += frm_payload->length;
}
}
break;
}
default:
break;
}
break;
}
default:
IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
"invalid mgmt frame type %u", type);
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
if (skb != NULL) {
if (timer) {
if (ni != vap->iv_mgmt_retry_ni || type != vap->iv_mgmt_retry_type ||
arg != vap->iv_mgmt_retry_arg) {
vap->iv_mgmt_retry_ni = ni;
vap->iv_mgmt_retry_type = type;
vap->iv_mgmt_retry_arg = arg;
vap->iv_mgmt_retry_cnt = 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
expired_timer = (timer * HZ) + (random32() % HZ);
#else
expired_timer = (timer * HZ) + (prandom_u32() % HZ);
#endif
mod_timer(&vap->iv_mgtsend, jiffies + expired_timer);
}
ieee80211_mgmt_output(ni, skb, type, ni->ni_macaddr);
} else {
ieee80211_free_node(ni);
}
return 0;
bad:
ieee80211_free_node(ni);
return ret;
#undef senderr
}
/*
* Send PS-POLL from to bss. Should only be called when as STA.
*/
void
ieee80211_send_pspoll(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
struct ieee80211_ctlframe_addr2 *wh;
skb = dev_alloc_skb(sizeof(struct ieee80211_ctlframe_addr2));
if (skb == NULL) {
return;
}
ieee80211_ref_node(ni);
wh = (struct ieee80211_ctlframe_addr2 *) skb_put(skb, sizeof(struct ieee80211_ctlframe_addr2));
wh->i_aidordur = htole16(0xc000 | IEEE80211_NODE_AID(ni));
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
wh->i_fc[0] = 0;
wh->i_fc[1] = 0;
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
IEEE80211_FC0_SUBTYPE_PS_POLL;
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
"[%s] send ps poll 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_VO, 0);
}
EXPORT_SYMBOL(ieee80211_send_pspoll);
/*
* Send DELBA management frame.
*/
void ieee80211_send_delba(struct ieee80211_node *ni, int tid, int tx, int reason)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_action_data act;
struct ba_action_del ba_del;
memset(&ba_del, 0, sizeof(ba_del));
ba_del.tid = tid;
ba_del.reason = reason;
// ba_del.initiator = (tx) ? 1 : 0;
ba_del.initiator = !tx;
memset(&act, 0, sizeof(act));
act.cat = IEEE80211_ACTION_CAT_BA;
act.action = IEEE80211_ACTION_BA_DELBA;
act.params = (void *)&ba_del;
ic->ic_send_mgmt(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&act);
}
EXPORT_SYMBOL(ieee80211_send_delba);
void ieee80211_rm_req_callback_success(void *ctx)
{
struct ieee80211_node *ni = (struct ieee80211_node *)ctx;
if (ni->ni_dotk_meas_state.meas_state_sta.pending) {
ni->ni_dotk_meas_state.meas_state_sta.pending = 0;
wake_up_interruptible(&ni->ni_dotk_waitq);
}
}
void ieee80211_rm_req_callback_fail(void *ctx, int32_t reason)
{
struct ieee80211_node *ni = (struct ieee80211_node *)ctx;
if (ni->ni_dotk_meas_state.meas_state_sta.pending) {
ni->ni_dotk_meas_state.meas_state_sta.status = -ETIMEDOUT;
ni->ni_dotk_meas_state.meas_state_sta.pending = 0;
wake_up_interruptible(&ni->ni_dotk_waitq);
}
}
/*
* Send radio measurement request - STA Statistics to associated STA. Should only be called when as AP.
*/
void
ieee80211_send_rm_req_stastats(struct ieee80211_node *ni, u_int32_t flags)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
ieee80211_11k_sub_element_head se_head;
ieee80211_11k_sub_element *p_se;
struct stastats_subele_vendor *vendor = NULL;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_STA;
ctrl.u.sta_stats.duration_tu = 0;
if (flags & RM_QTN_MEASURE_MASK) {
ctrl.u.sta_stats.group_id = 0;
SLIST_INIT(&se_head);
p_se = (ieee80211_11k_sub_element *)kmalloc(sizeof(*p_se) + sizeof(struct stastats_subele_vendor), GFP_KERNEL);
if (p_se != NULL) {
p_se->sub_id = IEEE80211_ELEMID_VENDOR;
vendor = (struct stastats_subele_vendor *)p_se->data;
vendor->flags = flags;
SLIST_INSERT_HEAD(&se_head, p_se, next);
}
ctrl.u.sta_stats.sub_item = &se_head;
} else {
ctrl.u.sta_stats.group_id = 221;
}
ctrl.expire = IEEE80211K_RM_MEASURE_STA_TIMEOUT;
ctrl.fn_success = ieee80211_rm_req_callback_success;
ctrl.fn_fail = ieee80211_rm_req_callback_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_stastats);
int32_t
ieee80211_send_rm_rep_stastats(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t group_id,
u_int16_t duration_tu,
void *sub_item)
{
struct ieee80211_meas_report_ctrl mreport_ctrl;
struct ieee80211_action_data action_data;
mreport_ctrl.meas_type = IEEE80211_RM_MEASTYPE_STA;
mreport_ctrl.token = token;
mreport_ctrl.meas_token = meas_token;
mreport_ctrl.report_mode = report_mode;
mreport_ctrl.autonomous = 0;
if (report_mode == 0) {
mreport_ctrl.u.sta_stats.group_id = group_id;
mreport_ctrl.u.sta_stats.duration_tu = duration_tu;
mreport_ctrl.u.sta_stats.sub_item = sub_item;
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &mreport_ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_stastats);
/*
* Send radio measurement request - CCA. Should only be called when as AP.
*/
void
ieee80211_send_rm_req_cca(struct ieee80211_node *ni)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_QTN_CCA;
ctrl.u.qtn_cca.duration_tu = 1;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_cca);
void
ieee80211_send_rm_req_stastats_all(struct ieee80211com *ic)
{
struct ieee80211_node *ni;
struct ieee80211_node_table *nt = &ic->ic_sta;
/* Fixed structure STA statistics */
u_int32_t flags = BIT(RM_QTN_MAX + 1) - 1;
IEEE80211_NODE_LOCK_BH(nt);
TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
if (ni->ni_vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (ni->ni_associd == 0)
continue;
ieee80211_send_rm_req_stastats(ni, flags);
}
IEEE80211_NODE_UNLOCK_BH(nt);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_stastats_all);
void
ieee80211_send_rm_req_chan_load(struct ieee80211_node *ni,
u_int8_t channel,
u_int16_t duration_ms,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_CH_LOAD;
ctrl.u.chan_load.channel = (channel == 0 ? ni->ni_ic->ic_curchan->ic_ieee : channel);
ctrl.u.chan_load.duration_ms = duration_ms;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_chan_load);
void
ieee80211_send_rm_rep_chan_load(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_tu,
u_int8_t channel_load)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_CH_LOAD;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.chan_load.op_class = op_class;
ctrl.u.chan_load.channel = channel;
ctrl.u.chan_load.duration_tu = duration_tu;
ctrl.u.chan_load.channel_load = channel_load;
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_chan_load);
void
ieee80211_send_rm_req_noise_his(struct ieee80211_node *ni,
u_int8_t channel,
u_int16_t duration_ms,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_NOISE;
ctrl.u.noise_his.channel = (channel == 0 ? ni->ni_ic->ic_curchan->ic_ieee : channel);
ctrl.u.noise_his.duration_ms = duration_ms;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_noise_his);
void
ieee80211_send_rm_rep_noise_his(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_tu,
u_int8_t antenna_id,
u_int8_t anpi,
u_int8_t *ipi)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_NOISE;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.noise_his.op_class = op_class;
ctrl.u.noise_his.channel = channel;
ctrl.u.noise_his.duration_tu = duration_tu;
ctrl.u.noise_his.antenna_id = antenna_id;
ctrl.u.noise_his.anpi = anpi;
if (ipi != NULL)
memcpy(ctrl.u.noise_his.ipi, ipi, sizeof(ctrl.u.noise_his.ipi));
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_noise_his);
void
ieee80211_send_rm_req_beacon(struct ieee80211_node *ni,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_ms,
u_int8_t mode,
u_int8_t *bssid,
u_int8_t *ssid,
u_int8_t ssid_len,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_BEACON;
ctrl.u.beacon.op_class = op_class;
/* ctrl.u.beacon.channel = (channel == 0 ? ni->ni_ic->ic_curchan->ic_ieee : channel); */
ctrl.u.beacon.channel = channel;
ctrl.u.beacon.duration_ms = duration_ms;
ctrl.u.beacon.mode = mode;
ctrl.u.beacon.ssid = ssid;
ctrl.u.beacon.ssid_len = ssid_len;
if (bssid != NULL)
memcpy(ctrl.u.beacon.bssid, bssid, IEEE80211_ADDR_LEN);
else
memset(ctrl.u.beacon.bssid, 0xFF, IEEE80211_ADDR_LEN);
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_beacon);
void
ieee80211_send_rm_rep_beacon(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_tu,
u_int8_t reported_frame_info,
u_int8_t rcpi,
u_int8_t rsni,
u_int8_t *bssid,
u_int8_t antenna_id,
u_int8_t *parent_tsf)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_BEACON;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.beacon.op_class = op_class;
ctrl.u.beacon.channel = channel;
ctrl.u.beacon.duration_tu = duration_tu;
ctrl.u.beacon.reported_frame_info = reported_frame_info;
ctrl.u.beacon.rcpi = rcpi;
ctrl.u.beacon.rsni = rsni;
if (bssid != NULL)
memcpy(ctrl.u.beacon.bssid, bssid, IEEE80211_ADDR_LEN);
ctrl.u.beacon.antenna_id = antenna_id;
if (parent_tsf != NULL)
memcpy(ctrl.u.beacon.parent_tsf, parent_tsf, sizeof(ctrl.u.beacon.parent_tsf));
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_beacon);
void
ieee80211_send_rm_req_frame(struct ieee80211_node *ni,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_ms,
u_int8_t type,
u_int8_t *mac_address,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_FRAME;
ctrl.u.frame.op_class = op_class;
ctrl.u.frame.channel = (channel == 0 ? ni->ni_ic->ic_curchan->ic_ieee : channel);
ctrl.u.frame.duration_ms = duration_ms;
ctrl.u.frame.type = type;
if (mac_address != NULL)
memcpy(ctrl.u.frame.mac_address, mac_address, IEEE80211_ADDR_LEN);
else
memset(ctrl.u.frame.mac_address, 0xFF, IEEE80211_ADDR_LEN);
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_frame);
void
ieee80211_send_rm_rep_frame(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t op_class,
u_int8_t channel,
u_int16_t duration_tu,
void *sub_ele)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_FRAME;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.frame.op_class = op_class;
ctrl.u.frame.channel = channel;
ctrl.u.frame.duration_tu = duration_tu;
ctrl.u.frame.sub_item = sub_ele;
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_frame);
void
ieee80211_send_rm_req_tran_stream_cat(struct ieee80211_node *ni,
u_int16_t duration_ms,
u_int8_t *peer_sta,
u_int8_t tid,
u_int8_t bin0,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
u_int8_t null_mac[IEEE80211_ADDR_LEN] = {0};
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_CATEGORY;
ctrl.u.tran_stream_cat.duration_ms = duration_ms;
if (peer_sta != NULL && memcmp(null_mac, ctrl.u.tran_stream_cat.peer_sta, IEEE80211_ADDR_LEN) != 0)
memcpy(ctrl.u.tran_stream_cat.peer_sta, peer_sta, IEEE80211_ADDR_LEN);
else
memcpy(ctrl.u.tran_stream_cat.peer_sta, ni->ni_macaddr, IEEE80211_ADDR_LEN);
ctrl.u.tran_stream_cat.tid = tid;
ctrl.u.tran_stream_cat.bin0 = bin0;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_tran_stream_cat);
void
ieee80211_send_rm_req_multicast_diag(struct ieee80211_node *ni,
u_int16_t duration_ms,
u_int8_t *group_mac,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_MUL_DIAG;
ctrl.u.multicast_diag.duration_ms = duration_ms;
if (group_mac != NULL)
memcpy(ctrl.u.multicast_diag.group_mac, group_mac, IEEE80211_ADDR_LEN);
else
memset(ctrl.u.multicast_diag.group_mac, 0, IEEE80211_ADDR_LEN);
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_req_multicast_diag);
void
ieee80211_send_rm_rep_multicast_diag(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int16_t duration_tu,
u_int8_t *group_mac,
u_int8_t reason,
u_int32_t mul_rec_msdu_cnt,
u_int16_t first_seq_num,
u_int16_t last_seq_num,
u_int16_t mul_rate)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_RM_MEASTYPE_MUL_DIAG;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.multicast_diag.duration_tu = duration_tu;
if (group_mac != NULL)
memcpy(ctrl.u.multicast_diag.group_mac, group_mac, IEEE80211_ADDR_LEN);
else
memset(ctrl.u.multicast_diag.group_mac, 0, IEEE80211_ADDR_LEN);
ctrl.u.multicast_diag.reason = reason;
ctrl.u.multicast_diag.mul_rec_msdu_cnt = mul_rec_msdu_cnt;
ctrl.u.multicast_diag.first_seq_num = first_seq_num;
ctrl.u.multicast_diag.last_seq_num = last_seq_num;
ctrl.u.multicast_diag.mul_rate = mul_rate;
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_rm_rep_multicast_diag);
int32_t ieee80211_send_meas_request_basic(struct ieee80211_node *ni,
u_int8_t channel,
u_int16_t tsf_offset,
u_int16_t duration,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211com *ic = ni->ni_vap->iv_ic;
u_int64_t tsf;
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_BASIC;
if (channel == 0)
ctrl.u.basic.channel = ic->ic_curchan->ic_ieee;
else
ctrl.u.basic.channel = channel;
ic->ic_get_tsf(&tsf);
if (tsf_offset == 0)
ctrl.u.basic.start_tsf = 0;
else
ctrl.u.basic.start_tsf = tsf + tsf_offset * 1000;
ctrl.u.basic.duration_ms = duration;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_S_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_request_basic);
int32_t ieee80211_send_meas_request_cca(struct ieee80211_node *ni,
u_int8_t channel,
u_int16_t tsf_offset,
u_int16_t duration,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211com *ic = ni->ni_vap->iv_ic;
u_int64_t tsf;
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_CCA;
if (channel == 0)
ctrl.u.cca.channel = ic->ic_curchan->ic_ieee;
else
ctrl.u.cca.channel = channel;
ic->ic_get_tsf(&tsf);
if (tsf_offset == 0)
ctrl.u.cca.start_tsf = 0;
else
ctrl.u.cca.start_tsf = tsf + tsf_offset * 1000;
ctrl.u.cca.duration_ms = duration;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_S_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_request_cca);
int32_t ieee80211_send_meas_request_rpi(struct ieee80211_node *ni,
u_int8_t channel,
u_int16_t tsf_offset,
u_int16_t duration,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211com *ic = ni->ni_vap->iv_ic;
u_int64_t tsf;
struct ieee80211_meas_request_ctrl ctrl;
struct ieee80211_action_data action_data;
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_RPI;
if (channel == 0)
ctrl.u.rpi.channel = ic->ic_curchan->ic_ieee;
else
ctrl.u.rpi.channel = channel;
ic->ic_get_tsf(&tsf);
if (tsf_offset == 0)
ctrl.u.rpi.start_tsf = 0;
else
ctrl.u.rpi.start_tsf = tsf + tsf_offset * 1000;
ctrl.u.rpi.duration_ms = duration;
ctrl.expire = expire;
ctrl.fn_success = (ppq_callback_success)fn_success;
ctrl.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_S_MEASUREMENT_REQUEST;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_request_rpi);
int32_t ieee80211_send_meas_report_basic(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t channel,
u_int64_t start_tsf,
u_int16_t duration,
u_int8_t basic_report)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_BASIC;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.basic.channel = channel;
ctrl.u.basic.start_tsf = start_tsf;
ctrl.u.basic.duration_tu = duration;
ctrl.u.basic.basic_report = basic_report;
}
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_report_basic);
int32_t ieee80211_send_meas_report_cca(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t channel,
u_int64_t start_tsf,
u_int16_t duration,
u_int8_t cca_report)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_CCA;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.cca.channel = channel;
ctrl.u.cca.start_tsf = start_tsf;
ctrl.u.cca.duration_tu = duration;
ctrl.u.cca.cca_report = cca_report;
}
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_report_cca);
int32_t ieee80211_send_meas_report_rpi(struct ieee80211_node *ni,
u_int8_t report_mode,
u_int8_t token,
u_int8_t meas_token,
u_int8_t channel,
u_int64_t start_tsf,
u_int16_t duration,
u_int8_t *rpi_report)
{
struct ieee80211_meas_report_ctrl ctrl;
struct ieee80211_action_data action_data;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.meas_type = IEEE80211_CCA_MEASTYPE_RPI;
ctrl.report_mode = report_mode;
ctrl.token = token;
ctrl.meas_token = meas_token;
ctrl.autonomous = 0;
if (report_mode == 0) {
ctrl.u.rpi.channel = channel;
ctrl.u.rpi.start_tsf = start_tsf;
ctrl.u.rpi.duration_tu = duration;
memcpy(ctrl.u.rpi.rpi_report, rpi_report, sizeof(ctrl.u.rpi.rpi_report));
}
action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT;
action_data.action = IEEE80211_ACTION_R_MEASUREMENT_REPORT;
action_data.params = &ctrl;
return IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_meas_report_rpi);
void ieee80211_send_link_measure_request(struct ieee80211_node *ni,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_link_measure_request request;
struct ieee80211_action_data action_data;
memset(&request, 0, sizeof(request));
request.ppq.expire = expire;
request.ppq.fn_success = (ppq_callback_success)fn_success;
request.ppq.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_LINKMEASURE_REQUEST;
action_data.params = &request;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_link_measure_request);
void ieee80211_send_neighbor_report_request(struct ieee80211_node *ni,
unsigned long expire,
void *fn_success,
void *fn_fail)
{
struct ieee80211_neighbor_report_request request;
struct ieee80211_action_data action_data;
memset(&request, 0, sizeof(request));
request.ppq.expire = expire;
request.ppq.fn_success = (ppq_callback_success)fn_success;
request.ppq.fn_fail = (ppq_callback_fail)fn_fail;
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_NEIGHBOR_REQUEST;
action_data.params = &request;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
EXPORT_SYMBOL(ieee80211_send_neighbor_report_request);
void ieee80211_send_neighbor_report_response(struct ieee80211_node *ni,
u_int8_t token,
u_int8_t bss_num,
void *table)
{
struct ieee80211_neighbor_report_response response;
struct ieee80211_action_data action_data;
struct ieee80211_neighbor_report_request_item** bss_table;
u_int8_t i;
memset(&response, 0, sizeof(response));
response.token = token;
response.bss_num = bss_num;
bss_table = (struct ieee80211_neighbor_report_request_item**)table;
for (i = 0; i < bss_num; i++) {
response.neighbor_report_ptr[i] = bss_table[i];
}
action_data.cat = IEEE80211_ACTION_CAT_RM;
action_data.action = IEEE80211_ACTION_R_NEIGHBOR_REPORT;
action_data.params = &response;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
}
void
ieee80211_send_vht_opmode_action(struct ieee80211vap *vap,
struct ieee80211_node *ni,
uint8_t bw, uint8_t rx_nss)
{
struct sk_buff *skb;
int frm_len;
u_int8_t *frm;
frm_len = IEEE80211_NCW_ACT_LEN;
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_VHT;
*frm++ = IEEE80211_ACTION_VHT_OPMODE_NOTIFICATION;
*frm++ = bw | (rx_nss << 4);
ieee80211_ref_node(ni);
ieee80211_mgmt_output(ni, skb, IEEE80211_FC0_SUBTYPE_ACTION,
ni->ni_macaddr);
}
/* sending Notify Channel Width Action
* if the ni is NULL, then it sends it as broadcast
* otherwise, unicast it to the targeted node
* width == 0: use HT20
* width != 0 use any channel width the node support
*
* Notify Channel Width Action frame fromat:
* |category: 1byte, value(7): CAT_HT | action: 1byte, value(0): NCW | Channel_width: 1byte, value: 0/HT20, or 1/HT40 |
*/
void
ieee80211_send_notify_chan_width_action(struct ieee80211vap *vap,
struct ieee80211_node *ni,
u_int32_t width)
{
// struct ieee80211com *ic = vap->iv_ic;
struct sk_buff *skb;
int frm_len;
u_int8_t *frm;
int is_bcst = (ni == NULL) ? 1 : 0;
frm_len = IEEE80211_NCW_ACT_LEN;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: Sending Notify Chan Width %s action to %s\n",
__func__,
(width) ? "HT40/20" : "HT20",
(is_bcst) ? "bcast" : ether_sprintf(ni->ni_macaddr));
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_HT; /* Category */
*frm++ = IEEE80211_ACTION_HT_TXCHWIDTH; /* notify channel width action */
if (width) {
*frm++ = IEEE80211_CWM_WIDTH40;
} else {
*frm++ = IEEE80211_CWM_WIDTH20;
}
if (is_bcst) {
ieee80211_ref_node(vap->iv_bss);
ieee80211_mgmt_output(vap->iv_bss, skb, IEEE80211_FC0_SUBTYPE_ACTION,
vap->iv_dev->broadcast);
} else {
ieee80211_ref_node(ni);
ieee80211_mgmt_output(ni, skb, IEEE80211_FC0_SUBTYPE_ACTION,
ni->ni_macaddr);
}
}
EXPORT_SYMBOL(ieee80211_send_notify_chan_width_action);
/* sending 11ac(VHT) Group ID mgmt Action
* node group membership and the positions are stored inside the node struct
* VHT group ID mgmt action frame format:
* |category: 1byte, value(21): CAT_VHT | action: 1byte, value(1): GRP ID MGMT | membership status array: 8 bytes | user position array: 16 bytes|
*/
void
ieee80211_send_vht_grp_id_mgmt_action(struct ieee80211vap *vap,
struct ieee80211_node *ni)
{
struct sk_buff *skb;
int frm_len;
u_int8_t *frm;
frm_len = IEEE80211_MU_GRP_ID_ACT_LEN;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_VHT,
"%s: Sending MU GRP ID action to %s\n",
__func__, ether_sprintf(ni->ni_macaddr));
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;
}
/* Category VHT */
*frm++ = IEEE80211_ACTION_CAT_VHT;
/* MU GRP ID action */
*frm++ = IEEE80211_ACTION_VHT_MU_GRP_ID;
memcpy(frm, &ni->ni_mu_grp, sizeof(ni->ni_mu_grp));
frm += sizeof(ni->ni_mu_grp);
ieee80211_ref_node(ni);
ieee80211_mgmt_output(ni, skb, IEEE80211_FC0_SUBTYPE_ACTION,
ni->ni_macaddr);
}
EXPORT_SYMBOL(ieee80211_send_vht_grp_id_mgmt_action);
int
ieee80211_send_20_40_bss_coex(struct ieee80211vap *vap)
{
struct ieee80211_action_data action_data;
struct ieee80211_node *ni = vap->iv_bss;
uint8_t coex = vap->iv_coex;
if ((!ni) || (!IEEE80211_AID(ni->ni_associd)))
return -EFAULT;
action_data.cat = IEEE80211_ACTION_CAT_PUBLIC;
action_data.action = IEEE80211_ACTION_PUB_20_40_COEX;
action_data.params = &coex;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
return 0;
}
void ieee80211_send_sa_query (struct ieee80211_node *ni, u_int8_t action,
u_int16_t tid)
{
struct ieee80211_action_data action_data;
if(RSN_IS_MFP(ni->ni_rsn.rsn_caps)) {
action_data.cat = IEEE80211_ACTION_CAT_SA_QUERY;
action_data.action = action;
action_data.params = &tid;
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
ni->ni_sa_query_timeout = jiffies;
}
}
void ieee80211_btm_resp_timeout(unsigned long arg)
{
struct ieee80211_node *ni = (struct ieee80211_node *)arg;
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ACTION, \
"BSS Transition management response timeout token(%d)\n", ni->ni_btm_req);
ni->ni_btm_req = 0;
}
static int
ieee80211_send_wnm_bss_tm_req(struct ieee80211_node *ni,
uint8_t mode,
uint16_t disassoc_timer,
uint8_t valid_int,
const uint8_t *bss_term_dur,
const char *url,
const uint8_t *nei_rep,
size_t nei_rep_len,
uint8_t tx_token)
{
struct btm_request_params req;
struct ieee80211_action_data action_data;
int url_len = 0;
if (url) {
/* Session Information URL */
url_len = strlen(url);
if (url_len > 255) {
return -1;
}
}
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ACTION,
"WNM: Send BSS Transition Management Request to %s"
" req_mode=0x%x disassoc_timer=%u valid_int=0x%u\n",
ether_sprintf(ni->ni_macaddr), mode, disassoc_timer, valid_int);
memset(&req, 0, sizeof(req));
req.dialog_token = tx_token;
req.request_mode = mode;
req.disassoc_timer = disassoc_timer;
req.validity_interval = valid_int;
req.bss_term_dur = (uint8_t *)bss_term_dur;
req.url = (char *)url;
req.neigh_reports = (uint8_t *)nei_rep;
req.neigh_reports_length = nei_rep_len;
action_data.cat = IEEE80211_ACTION_CAT_WNM;
action_data.action = IEEE80211_WNM_BSS_TRANS_MGMT_REQ;
action_data.params = &req;
if (ni->ni_btm_req != 0) {
del_timer(&ni->ni_btm_resp_wait_timer);
ni->ni_btm_req = 0;
}
ni->ni_btm_req = tx_token;
ni->ni_btm_resp_wait_timer.function = ieee80211_btm_resp_timeout;
ni->ni_btm_resp_wait_timer.data = (unsigned long)ni;
mod_timer(&ni->ni_btm_resp_wait_timer, jiffies + ((disassoc_timer + 1) * ni->ni_intval));
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data);
return 0;
}
int
ieee80211_send_wnm_bss_tm_solicited_req(struct ieee80211_node *ni,
uint8_t mode,
uint16_t disassoc_timer,
uint8_t valid_int,
const uint8_t *bss_term_dur,
const char *url,
const uint8_t *nei_rep,
size_t nei_rep_len,
uint8_t tx_token)
{
return ieee80211_send_wnm_bss_tm_req(ni, mode, disassoc_timer, valid_int,
bss_term_dur, url, nei_rep, nei_rep_len, tx_token);
}
int
ieee80211_send_wnm_bss_tm_unsolicited_req(struct ieee80211_node *ni,
uint8_t mode,
uint16_t disassoc_timer,
uint8_t valid_int,
const uint8_t *bss_term_dur,
const char *url,
const uint8_t *nei_rep,
size_t nei_rep_len,
uint8_t tx_token)
{
if (!ni)
return -1;
if (valid_int == 0)
disassoc_timer = WNM_BTM_DEFAULT_VAL_INTVAL;
return ieee80211_send_wnm_bss_tm_req(ni, mode, disassoc_timer, valid_int,
bss_term_dur, url, nei_rep, nei_rep_len, tx_token);
}