blob: 7832981c0d1742d07411c13c65356c144f9cf583 [file] [log] [blame]
/*
* RadioTap utility routines for WL
* This file housing the functions use by
* wl driver.
*
* Copyright (C) 2020, Broadcom.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
*
* <<Broadcom-WL-IPTag/Dual:>>
*/
#include <bcmutils.h>
#include <bcmendian.h>
#include <bcmwifi_channels.h>
#include <hndd11.h>
#include <bcmwifi_radiotap.h>
const struct rtap_field rtap_parse_info[] = {
{8, 8}, /* 0: IEEE80211_RADIOTAP_TSFT */
{1, 1}, /* 1: IEEE80211_RADIOTAP_FLAGS */
{1, 1}, /* 2: IEEE80211_RADIOTAP_RATE */
{4, 2}, /* 3: IEEE80211_RADIOTAP_CHANNEL */
{2, 2}, /* 4: IEEE80211_RADIOTAP_FHSS */
{1, 1}, /* 5: IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
{1, 1}, /* 6: IEEE80211_RADIOTAP_DBM_ANTNOISE */
{2, 2}, /* 7: IEEE80211_RADIOTAP_LOCK_QUALITY */
{2, 2}, /* 8: IEEE80211_RADIOTAP_TX_ATTENUATION */
{2, 2}, /* 9: IEEE80211_RADIOTAP_DB_TX_ATTENUATION */
{1, 1}, /* 10: IEEE80211_RADIOTAP_DBM_TX_POWER */
{1, 1}, /* 11: IEEE80211_RADIOTAP_ANTENNA */
{1, 1}, /* 12: IEEE80211_RADIOTAP_DB_ANTSIGNAL */
{1, 1}, /* 13: IEEE80211_RADIOTAP_DB_ANTNOISE */
{0, 0}, /* 14: netbsd */
{2, 2}, /* 15: IEEE80211_RADIOTAP_TXFLAGS */
{0, 0}, /* 16: missing */
{1, 1}, /* 17: IEEE80211_RADIOTAP_RETRIES */
{8, 4}, /* 18: IEEE80211_RADIOTAP_XCHANNEL */
{3, 1}, /* 19: IEEE80211_RADIOTAP_MCS */
{8, 4}, /* 20: IEEE80211_RADIOTAP_AMPDU_STATUS */
{12, 2}, /* 21: IEEE80211_RADIOTAP_VHT */
{0, 0}, /* 22: */
{0, 0}, /* 23: */
{0, 0}, /* 24: */
{0, 0}, /* 25: */
{0, 0}, /* 26: */
{0, 0}, /* 27: */
{0, 0}, /* 28: */
{0, 0}, /* 29: IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE */
{6, 2}, /* 30: IEEE80211_RADIOTAP_VENDOR_NAMESPACE */
{0, 0} /* 31: IEEE80211_RADIOTAP_EXT */
};
static int bitmap = 0;
void
radiotap_add_vendor_ns(ieee80211_radiotap_header_t *hdr);
void
radiotap_encode_multi_rssi(monitor_pkt_rxsts_t* rxsts, ieee80211_radiotap_header_t *hdr);
void
radiotap_encode_bw_signaling(uint16 mask, struct wl_rxsts* rxsts, ieee80211_radiotap_header_t *hdr);
#ifdef MONITOR_DNGL_CONV
void radiotap_encode_alignpad(ieee80211_radiotap_header_t *hdr, uint16 pad_req);
#endif
static const uint8 brcm_oui[] = {0x00, 0x10, 0x18};
static void
wl_rtapParseReset(radiotap_parse_t *rtap)
{
rtap->idx = 0; /* reset parse index */
rtap->offset = 0; /* reset current field pointer */
}
static void*
wl_rtapParseFindField(radiotap_parse_t *rtap, uint search_idx)
{
uint idx; /* first bit index to parse */
uint32 btmap; /* presence bitmap */
uint offset, field_offset;
uint align, len;
void *ptr = NULL;
if (search_idx > IEEE80211_RADIOTAP_EXT)
return ptr;
if (search_idx < rtap->idx)
wl_rtapParseReset(rtap);
btmap = rtap->hdr->it_present;
idx = rtap->idx;
offset = rtap->offset;
/* loop through each field index until we get to the target idx */
while (idx <= search_idx) {
/* if field 'idx' is present, update the offset and check for a match */
if ((1 << idx) & btmap) {
/* if we hit a field for which we have no parse info
* we need to just bail out
*/
if (rtap_parse_info[idx].align == 0)
break;
/* step past any alignment padding */
align = rtap_parse_info[idx].align;
len = rtap_parse_info[idx].len;
/* ROUNDUP */
field_offset = ((offset + (align - 1)) / align) * align;
/* if this field is not in the boulds of the header
* just bail out
*/
if (field_offset + len > rtap->fields_len)
break;
/* did we find the field? */
if (idx == search_idx)
ptr = (uint8*)rtap->fields + field_offset;
/* step past this field */
offset = field_offset + len;
}
idx++;
}
rtap->idx = idx;
rtap->offset = offset;
return ptr;
}
ratespec_t
wl_calcRspecFromRTap(uint8 *rtap_header)
{
ratespec_t rspec = 0;
radiotap_parse_t rtap;
uint8 rate = 0;
uint8 flags = 0;
int flags_present = FALSE;
uint8 mcs = 0;
uint8 mcs_flags = 0;
uint8 mcs_known = 0;
int mcs_present = FALSE;
void *p;
wl_rtapParseInit(&rtap, rtap_header);
p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_FLAGS);
if (p != NULL) {
flags_present = TRUE;
flags = ((uint8*)p)[0];
}
p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_RATE);
if (p != NULL)
rate = ((uint8*)p)[0];
p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_MCS);
if (p != NULL) {
mcs_present = TRUE;
mcs_known = ((uint8*)p)[0];
mcs_flags = ((uint8*)p)[1];
mcs = ((uint8*)p)[2];
}
if (rate != 0) {
/* validate the DSSS rates 1,2,5.5,11 */
if (rate == 2 || rate == 4 || rate == 11 || rate == 22) {
rspec = LEGACY_RSPEC(rate) | WL_RSPEC_OVERRIDE_RATE;
if (flags_present && (flags & IEEE80211_RADIOTAP_F_SHORTPRE)) {
rspec |= WL_RSPEC_OVERRIDE_MODE | WL_RSPEC_SHORT_PREAMBLE;
}
}
} else if (mcs_present) {
/* validate the MCS value */
if (mcs <= 23 || mcs == 32) {
uint32 override = 0;
if (mcs_known &
(IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_FMT |
IEEE80211_RADIOTAP_MCS_HAVE_FEC)) {
override = WL_RSPEC_OVERRIDE_MODE;
}
rspec = HT_RSPEC(mcs) | WL_RSPEC_OVERRIDE_RATE;
if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI) &&
(mcs_flags & IEEE80211_RADIOTAP_MCS_SGI))
rspec |= WL_RSPEC_SGI;
if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_FMT) &&
(mcs_flags & IEEE80211_RADIOTAP_MCS_FMT_GF))
rspec |= WL_RSPEC_SHORT_PREAMBLE;
if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_FEC) &&
(mcs_flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC))
rspec |= WL_RSPEC_LDPC;
rspec |= override;
}
}
return rspec;
}
bool
wl_rtapFlags(uint8 *rtap_header, uint8* flags)
{
radiotap_parse_t rtap;
void *p;
wl_rtapParseInit(&rtap, rtap_header);
p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_FLAGS);
if (p != NULL) {
*flags = ((uint8*)p)[0];
}
return (p != NULL);
}
void
wl_rtapParseInit(radiotap_parse_t *rtap, uint8 *rtap_header)
{
uint rlen;
uint32 *present_word;
struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header*)rtap_header;
bzero(rtap, sizeof(radiotap_parse_t));
rlen = hdr->it_len; /* total space in rtap_header */
/* If a precence word has the IEEE80211_RADIOTAP_EXT bit set it indicates
* that there is another precence word.
* Step over the presence words until we find the end of the list
*/
present_word = &hdr->it_present;
/* remaining length in header past it_present */
rlen -= sizeof(struct ieee80211_radiotap_header);
while ((*present_word & (1<<IEEE80211_RADIOTAP_EXT)) && rlen >= 4) {
present_word++;
rlen -= 4; /* account for 4 bytes of present_word */
}
rtap->hdr = hdr;
rtap->fields = (uint8*)(present_word + 1);
rtap->fields_len = rlen;
wl_rtapParseReset(rtap);
}
uint
wl_radiotap_rx(struct dot11_header *mac_header, wl_rxsts_t *rxsts, bsd_header_rx_t *bsd_header)
{
int channel_frequency;
uint32 channel_flags;
uint8 flags;
uint8 *cp;
uint pad_len;
uint32 field_map;
uint16 fc;
uint bsd_header_len;
uint16 ampdu_flags = 0;
fc = LTOH16(mac_header->fc);
pad_len = 3;
field_map = WL_RADIOTAP_PRESENT_BASIC;
if (CHSPEC_IS2G(rxsts->chanspec)) {
channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN;
channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_2_4_G);
} else if (CHSPEC_IS5G(rxsts->chanspec)) {
channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_5_G);
} else {
channel_flags = IEEE80211_CHAN_OFDM;
channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_6_G);
}
if ((rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) ||
(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) {
ampdu_flags = IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
}
flags = IEEE80211_RADIOTAP_F_FCS;
if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT)
flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
if ((fc & FC_WEP) == FC_WEP)
flags |= IEEE80211_RADIOTAP_F_WEP;
if ((fc & FC_MOREFRAG) == FC_MOREFRAG)
flags |= IEEE80211_RADIOTAP_F_FRAG;
if (rxsts->pkterror & WL_RXS_CRC_ERROR)
flags |= IEEE80211_RADIOTAP_F_BADFCS;
if (rxsts->encoding == WL_RXS_ENCODING_HT)
field_map = WL_RADIOTAP_PRESENT_HT;
else if (rxsts->encoding == WL_RXS_ENCODING_VHT)
field_map = WL_RADIOTAP_PRESENT_VHT;
bsd_header_len = sizeof(struct wl_radiotap_sna); /* start with sna size */
/* Test for signal/noise values and update length and field bitmap */
if (rxsts->signal == 0) {
field_map &= ~(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
pad_len = (pad_len - 1);
bsd_header_len--;
}
if (rxsts->noise == 0) {
field_map &= ~(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
pad_len = (pad_len - 1);
bsd_header_len--;
}
if (rxsts->encoding == WL_RXS_ENCODING_HT ||
rxsts->encoding == WL_RXS_ENCODING_VHT) {
struct wl_radiotap_hdr *rtht = &bsd_header->hdr;
struct wl_radiotap_ht_tail *tail;
/*
* Header length is complicated due to dynamic
* presence of signal and noise fields
* and padding for xchannel following
* signal/noise/ant.
* Start with length of wl_radiotap_ht plus
* signal/noise/ant
*/
bsd_header_len += sizeof(struct wl_radiotap_hdr) + pad_len;
bsd_header_len += sizeof(struct wl_radiotap_xchan);
if (rxsts->nfrmtype == WL_RXS_NFRM_AMPDU_FIRST ||
rxsts->nfrmtype == WL_RXS_NFRM_AMPDU_SUB) {
bsd_header_len += sizeof(struct wl_radiotap_ampdu);
}
/* add the length of the tail end of the structure */
if (rxsts->encoding == WL_RXS_ENCODING_HT)
bsd_header_len += sizeof(struct wl_htmcs);
else if (rxsts->encoding == WL_RXS_ENCODING_VHT)
bsd_header_len += sizeof(struct wl_vhtmcs);
bzero((uint8 *)rtht, sizeof(*rtht));
rtht->ieee_radiotap.it_version = 0;
rtht->ieee_radiotap.it_pad = 0;
rtht->ieee_radiotap.it_len = (uint16)HTOL16(bsd_header_len);
rtht->ieee_radiotap.it_present = HTOL32(field_map);
rtht->tsft = HTOL64((uint64)rxsts->mactime);
rtht->flags = flags;
rtht->channel_freq = (uint16)HTOL16(channel_frequency);
rtht->channel_flags = (uint16)HTOL16(channel_flags);
cp = bsd_header->pad;
/* add in signal/noise/ant */
if (rxsts->signal != 0) {
*cp++ = (int8)rxsts->signal;
pad_len--;
}
if (rxsts->noise != 0) {
*cp++ = (int8)rxsts->noise;
pad_len--;
}
*cp++ = (int8)rxsts->antenna;
pad_len--;
tail = (struct wl_radiotap_ht_tail *)(bsd_header->ht);
/* Fill in XCHANNEL */
if (CHSPEC_IS40(rxsts->chanspec)) {
if (CHSPEC_SB_UPPER(rxsts->chanspec))
channel_flags |= IEEE80211_CHAN_HT40D;
else
channel_flags |= IEEE80211_CHAN_HT40U;
} else
channel_flags |= IEEE80211_CHAN_HT20;
tail->xc.xchannel_flags = HTOL32(channel_flags);
tail->xc.xchannel_freq = (uint16)HTOL16(channel_frequency);
tail->xc.xchannel_channel = wf_chspec_ctlchan(rxsts->chanspec);
tail->xc.xchannel_maxpower = (17*2);
/* fill in A-mpdu Status */
tail->ampdu.ref_num = mac_header->seq;
tail->ampdu.flags = ampdu_flags;
tail->ampdu.delimiter_crc = 0;
tail->ampdu.reserved = 0;
if (rxsts->encoding == WL_RXS_ENCODING_HT) {
tail->u.ht.mcs_index = rxsts->mcs;
tail->u.ht.mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW |
IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_FEC |
IEEE80211_RADIOTAP_MCS_HAVE_FMT);
tail->u.ht.mcs_flags = 0;
switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) {
case WL_RXS_HTF_20L:
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L;
break;
case WL_RXS_HTF_20U:
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U;
break;
case WL_RXS_HTF_40:
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40;
break;
default:
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20;
break;
}
if (rxsts->htflags & WL_RXS_HTF_SGI)
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI;
if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF)
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF;
if (rxsts->htflags & WL_RXS_HTF_LDPC)
tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
} else if (rxsts->encoding == WL_RXS_ENCODING_VHT) {
tail->u.vht.vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC |
IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS |
IEEE80211_RADIOTAP_VHT_HAVE_GI |
IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA |
IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA |
IEEE80211_RADIOTAP_VHT_HAVE_BF |
IEEE80211_RADIOTAP_VHT_HAVE_BW |
IEEE80211_RADIOTAP_VHT_HAVE_GID |
IEEE80211_RADIOTAP_VHT_HAVE_PAID);
tail->u.vht.vht_flags = (uint8)HTOL16(rxsts->vhtflags);
switch (rxsts->bw) {
case WL_RXS_VHT_BW_20:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20;
break;
case WL_RXS_VHT_BW_40:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40;
break;
case WL_RXS_VHT_BW_20L:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20L;
break;
case WL_RXS_VHT_BW_20U:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20U;
break;
case WL_RXS_VHT_BW_80:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_80;
break;
case WL_RXS_VHT_BW_40L:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40L;
break;
case WL_RXS_VHT_BW_40U:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40U;
break;
case WL_RXS_VHT_BW_20LL:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20LL;
break;
case WL_RXS_VHT_BW_20LU:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20LU;
break;
case WL_RXS_VHT_BW_20UL:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20UL;
break;
case WL_RXS_VHT_BW_20UU:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20UU;
break;
default:
tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20;
break;
}
tail->u.vht.vht_mcs_nss[0] = (rxsts->mcs << 4) |
(rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS);
tail->u.vht.vht_mcs_nss[1] = 0;
tail->u.vht.vht_mcs_nss[2] = 0;
tail->u.vht.vht_mcs_nss[3] = 0;
tail->u.vht.vht_coding = rxsts->coding;
tail->u.vht.vht_group_id = rxsts->gid;
tail->u.vht.vht_partial_aid = HTOL16(rxsts->aid);
}
} else {
struct wl_radiotap_hdr *rtl = &bsd_header->hdr;
/*
* Header length is complicated due to dynamic presence of signal and noise fields
* Start with length of wl_radiotap_legacy plus signal/noise/ant
*/
bsd_header_len = sizeof(struct wl_radiotap_hdr) + pad_len;
bzero((uint8 *)rtl, sizeof(*rtl));
rtl->ieee_radiotap.it_version = 0;
rtl->ieee_radiotap.it_pad = 0;
rtl->ieee_radiotap.it_len = (uint16)HTOL16(bsd_header_len);
rtl->ieee_radiotap.it_present = HTOL32(field_map);
rtl->tsft = HTOL64((uint64)rxsts->mactime);
rtl->flags = flags;
rtl->u.rate = (uint8)rxsts->datarate;
rtl->channel_freq = (uint16)HTOL16(channel_frequency);
rtl->channel_flags = (uint16)HTOL16(channel_flags);
/* add in signal/noise/ant */
cp = bsd_header->pad;
if (rxsts->signal != 0)
*cp++ = (int8)rxsts->signal;
if (rxsts->noise != 0)
*cp++ = (int8)rxsts->noise;
*cp++ = (int8)rxsts->antenna;
}
return bsd_header_len;
}
static int
wl_radiotap_rx_channel_frequency(wl_rxsts_t *rxsts)
{
if (CHSPEC_IS2G(rxsts->chanspec)) {
return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_2_4_G);
} else if (CHSPEC_IS5G(rxsts->chanspec)) {
return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_5_G);
} else {
return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
WF_CHAN_FACTOR_6_G);
}
}
static uint16
wl_radiotap_rx_channel_flags(wl_rxsts_t *rxsts)
{
if (CHSPEC_IS2G(rxsts->chanspec)) {
return (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN);
} else if (CHSPEC_IS5G(rxsts->chanspec)) {
return (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM);
} else {
return (IEEE80211_CHAN_OFDM);
}
}
static uint8
wl_radiotap_rx_flags(struct dot11_header *mac_header, wl_rxsts_t *rxsts)
{
uint8 flags;
uint16 fc;
fc = ltoh16(mac_header->fc);
flags = IEEE80211_RADIOTAP_F_FCS;
if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT)
flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
if (fc & FC_WEP)
flags |= IEEE80211_RADIOTAP_F_WEP;
if (fc & FC_MOREFRAG)
flags |= IEEE80211_RADIOTAP_F_FRAG;
return flags;
}
uint
wl_radiotap_rx_legacy(struct dot11_header *mac_header,
wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr)
{
int channel_frequency;
uint16 channel_flags;
uint8 flags;
uint16 rtap_len = LTOH16(rtap_hdr->it_len);
wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)((uint8*)rtap_hdr + rtap_len);
rtap_len += sizeof(wl_radiotap_legacy_t);
rtap_hdr->it_len = HTOL16(rtap_len);
rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_LEGACY);
channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts);
channel_flags = wl_radiotap_rx_channel_flags(rxsts);
flags = wl_radiotap_rx_flags(mac_header, rxsts);
rtl->basic.tsft_l = HTOL32(rxsts->mactime);
rtl->basic.tsft_h = 0;
rtl->basic.flags = flags;
rtl->basic.rate = (uint8)rxsts->datarate;
rtl->basic.channel_freq = (uint16)HTOL16(channel_frequency);
rtl->basic.channel_flags = HTOL16(channel_flags);
rtl->basic.signal = (int8)rxsts->signal;
rtl->basic.noise = (int8)rxsts->noise;
rtl->basic.antenna = (int8)rxsts->antenna;
return 0;
}
uint
wl_radiotap_rx_ht(struct dot11_header *mac_header,
wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr)
{
int channel_frequency;
uint16 channel_flags;
uint32 xchannel_flags;
uint8 flags;
uint16 rtap_len = LTOH16(rtap_hdr->it_len);
wl_radiotap_ht_t *rtht = (wl_radiotap_ht_t *)((uint8*)rtap_hdr + rtap_len);
rtap_len += sizeof(wl_radiotap_ht_t);
rtap_hdr->it_len = HTOL16(rtap_len);
rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_HT);
channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts);
channel_flags = wl_radiotap_rx_channel_flags(rxsts);
flags = wl_radiotap_rx_flags(mac_header, rxsts);
rtht->basic.tsft_l = HTOL32(rxsts->mactime);
rtht->basic.tsft_h = 0;
rtht->basic.flags = flags;
rtht->basic.channel_freq = (uint16)HTOL16(channel_frequency);
rtht->basic.channel_flags = HTOL16(channel_flags);
rtht->basic.signal = (int8)rxsts->signal;
rtht->basic.noise = (int8)rxsts->noise;
rtht->basic.antenna = (uint8)rxsts->antenna;
/* xchannel */
xchannel_flags = (uint32)channel_flags;
if (CHSPEC_IS40(rxsts->chanspec)) {
if (CHSPEC_SB_UPPER(rxsts->chanspec))
xchannel_flags |= IEEE80211_CHAN_HT40D;
else {
xchannel_flags |= IEEE80211_CHAN_HT40U;
}
} else {
xchannel_flags |= IEEE80211_CHAN_HT20;
}
rtht->xchannel_flags = HTOL32(xchannel_flags);
rtht->xchannel_freq = (uint16)HTOL16(channel_frequency);
rtht->xchannel_channel = wf_chspec_ctlchan(rxsts->chanspec);
rtht->xchannel_maxpower = (17*2);
/* add standard MCS */
rtht->mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW |
IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_FEC |
IEEE80211_RADIOTAP_MCS_HAVE_FMT);
rtht->mcs_flags = 0;
switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) {
case WL_RXS_HTF_20L:
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L;
break;
case WL_RXS_HTF_20U:
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U;
break;
case WL_RXS_HTF_40:
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40;
break;
default:
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20;
}
if (rxsts->htflags & WL_RXS_HTF_SGI) {
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI;
}
if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) {
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF;
}
if (rxsts->htflags & WL_RXS_HTF_LDPC) {
rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
}
rtht->mcs_index = rxsts->mcs;
rtht->ampdu_flags = 0;
rtht->ampdu_delim_crc = 0;
rtht->ampdu_ref_num = rxsts->ampdu_counter;
if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) &&
!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) {
rtht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST;
} else {
rtht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
}
return 0;
}
uint
wl_radiotap_rx_vht(struct dot11_header *mac_header,
wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr)
{
int channel_frequency;
uint16 channel_flags;
uint8 flags;
uint16 rtap_len = LTOH16(rtap_hdr->it_len);
wl_radiotap_vht_t *rtvht = (wl_radiotap_vht_t *)((uint8*)rtap_hdr + rtap_len);
rtap_len += sizeof(wl_radiotap_vht_t);
rtap_hdr->it_len = HTOL16(rtap_len);
rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_VHT);
channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts);
channel_flags = wl_radiotap_rx_channel_flags(rxsts);
flags = wl_radiotap_rx_flags(mac_header, rxsts);
rtvht->basic.tsft_l = HTOL32(rxsts->mactime);
rtvht->basic.tsft_h = 0;
rtvht->basic.flags = flags;
rtvht->basic.channel_freq = (uint16)HTOL16(channel_frequency);
rtvht->basic.channel_flags = HTOL16(channel_flags);
rtvht->basic.signal = (int8)rxsts->signal;
rtvht->basic.noise = (int8)rxsts->noise;
rtvht->basic.antenna = (uint8)rxsts->antenna;
rtvht->vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC |
IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS |
IEEE80211_RADIOTAP_VHT_HAVE_GI |
IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA |
IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA |
IEEE80211_RADIOTAP_VHT_HAVE_BF |
IEEE80211_RADIOTAP_VHT_HAVE_BW |
IEEE80211_RADIOTAP_VHT_HAVE_GID |
IEEE80211_RADIOTAP_VHT_HAVE_PAID);
STATIC_ASSERT(WL_RXS_VHTF_STBC ==
IEEE80211_RADIOTAP_VHT_STBC);
STATIC_ASSERT(WL_RXS_VHTF_TXOP_PS ==
IEEE80211_RADIOTAP_VHT_TXOP_PS);
STATIC_ASSERT(WL_RXS_VHTF_SGI ==
IEEE80211_RADIOTAP_VHT_SGI);
STATIC_ASSERT(WL_RXS_VHTF_SGI_NSYM_DA ==
IEEE80211_RADIOTAP_VHT_SGI_NSYM_DA);
STATIC_ASSERT(WL_RXS_VHTF_LDPC_EXTRA ==
IEEE80211_RADIOTAP_VHT_LDPC_EXTRA);
STATIC_ASSERT(WL_RXS_VHTF_BF ==
IEEE80211_RADIOTAP_VHT_BF);
rtvht->vht_flags = (uint8)HTOL16(rxsts->vhtflags);
STATIC_ASSERT(WL_RXS_VHT_BW_20 ==
IEEE80211_RADIOTAP_VHT_BW_20);
STATIC_ASSERT(WL_RXS_VHT_BW_40 ==
IEEE80211_RADIOTAP_VHT_BW_40);
STATIC_ASSERT(WL_RXS_VHT_BW_20L ==
IEEE80211_RADIOTAP_VHT_BW_20L);
STATIC_ASSERT(WL_RXS_VHT_BW_20U ==
IEEE80211_RADIOTAP_VHT_BW_20U);
STATIC_ASSERT(WL_RXS_VHT_BW_80 ==
IEEE80211_RADIOTAP_VHT_BW_80);
STATIC_ASSERT(WL_RXS_VHT_BW_40L ==
IEEE80211_RADIOTAP_VHT_BW_40L);
STATIC_ASSERT(WL_RXS_VHT_BW_40U ==
IEEE80211_RADIOTAP_VHT_BW_40U);
STATIC_ASSERT(WL_RXS_VHT_BW_20LL ==
IEEE80211_RADIOTAP_VHT_BW_20LL);
STATIC_ASSERT(WL_RXS_VHT_BW_20LU ==
IEEE80211_RADIOTAP_VHT_BW_20LU);
STATIC_ASSERT(WL_RXS_VHT_BW_20UL ==
IEEE80211_RADIOTAP_VHT_BW_20UL);
STATIC_ASSERT(WL_RXS_VHT_BW_20UU ==
IEEE80211_RADIOTAP_VHT_BW_20UU);
rtvht->vht_bw = rxsts->bw;
rtvht->vht_mcs_nss[0] = (rxsts->mcs << 4) |
(rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS);
rtvht->vht_mcs_nss[1] = 0;
rtvht->vht_mcs_nss[2] = 0;
rtvht->vht_mcs_nss[3] = 0;
STATIC_ASSERT(WL_RXS_VHTF_CODING_LDCP ==
IEEE80211_RADIOTAP_VHT_CODING_LDPC);
rtvht->vht_coding = rxsts->coding;
rtvht->vht_group_id = rxsts->gid;
rtvht->vht_partial_aid = HTOL16(rxsts->aid);
rtvht->ampdu_flags = 0;
rtvht->ampdu_delim_crc = 0;
rtvht->ampdu_ref_num = HTOL32(rxsts->ampdu_counter);
if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) &&
!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) {
rtvht->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_IS_LAST);
} else {
rtvht->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN);
}
return 0;
}
/* Rx status to radiotap conversion of HE type */
uint
wl_radiotap_rx_he(struct dot11_header *mac_header, wl_rxsts_t *rxsts,
ieee80211_radiotap_header_t *rtap_hdr)
{
int channel_frequency;
uint16 channel_flags;
uint8 flags;
uint16 rtap_len = LTOH16(rtap_hdr->it_len);
wl_radiotap_he_t *rthe = (wl_radiotap_he_t *)((uint8*)rtap_hdr + rtap_len);
rtap_len += sizeof(wl_radiotap_he_t);
rtap_hdr->it_len = HTOL16(rtap_len);
rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_HE);
channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts);
channel_flags = wl_radiotap_rx_channel_flags(rxsts);
flags = wl_radiotap_rx_flags(mac_header, rxsts);
rthe->basic.tsft_l = HTOL32(rxsts->mactime);
rthe->basic.tsft_h = 0;
rthe->basic.flags = flags;
rthe->basic.channel_freq = (uint16)HTOL16(channel_frequency);
rthe->basic.channel_flags = HTOL16(channel_flags);
rthe->basic.signal = (int8)rxsts->signal;
rthe->basic.noise = (int8)rxsts->noise;
rthe->basic.antenna = (uint8)rxsts->antenna;
rthe->ampdu_flags = 0;
rthe->ampdu_delim_crc = 0;
rthe->ampdu_ref_num = HTOL32(rxsts->ampdu_counter);
if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) &&
!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) {
rthe->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_IS_LAST);
} else {
rthe->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN);
}
rthe->data1 = HTOL16(rxsts->data1);
rthe->data2 = HTOL16(rxsts->data2);
rthe->data3 = HTOL16(rxsts->data3);
rthe->data4 = HTOL16(rxsts->data4);
rthe->data5 = HTOL16(rxsts->data5);
rthe->data6 = HTOL16(rxsts->data6);
return 0;
}
/* Rx status to radiotap conversion of EHT type */
uint
wl_radiotap_rx_eht(struct dot11_header *mac_header, wl_rxsts_t *rxsts,
ieee80211_radiotap_header_t *rtap_hdr)
{
ASSERT(!"wl_radiotap_rx_eht: not implemented!");
return 0;
}
uint16
wl_rxsts_to_rtap(monitor_pkt_rxsts_t *pkt_rxsts, void *payload,
uint16 len, void* pout, uint16 pad_req)
{
uint16 rtap_len = 0;
struct dot11_header* mac_header;
uint8* p = payload;
ieee80211_radiotap_header_t* rtap_hdr = (ieee80211_radiotap_header_t*)pout;
wl_rxsts_t* rxsts;
ASSERT(p && pkt_rxsts);
rxsts = pkt_rxsts->rxsts;
rtap_hdr->it_version = 0;
rtap_hdr->it_pad = 0;
rtap_hdr->it_len = HTOL16(sizeof(*rtap_hdr));
rtap_hdr->it_present = 0;
bitmap = 0;
#ifdef MONITOR_DNGL_CONV
if (pad_req) {
radiotap_add_vendor_ns(rtap_hdr);
}
#endif
#ifdef BCM_MON_QDBM_RSSI
/* if per-core RSSI is present, add vendor element */
if (pkt_rxsts->corenum != 0) {
radiotap_add_vendor_ns(rtap_hdr);
}
#endif
mac_header = (struct dot11_header *)(p);
if (rxsts->encoding == WL_RXS_ENCODING_EHT) {
wl_radiotap_rx_eht(mac_header, rxsts, rtap_hdr);
} else if (rxsts->encoding == WL_RXS_ENCODING_HE) {
wl_radiotap_rx_he(mac_header, rxsts, rtap_hdr);
} else if (rxsts->encoding == WL_RXS_ENCODING_VHT) {
wl_radiotap_rx_vht(mac_header, rxsts, rtap_hdr);
} else if (rxsts->encoding == WL_RXS_ENCODING_HT) {
wl_radiotap_rx_ht(mac_header, rxsts, rtap_hdr);
} else {
uint16 mask = ltoh16(mac_header->fc) & FC_KIND_MASK;
if (mask == FC_RTS || mask == FC_CTS) {
radiotap_add_vendor_ns(rtap_hdr);
}
wl_radiotap_rx_legacy(mac_header, rxsts, rtap_hdr);
if (mask == FC_RTS || mask == FC_CTS) {
radiotap_encode_bw_signaling(mask, rxsts, rtap_hdr);
}
}
#ifdef BCM_MON_QDBM_RSSI
/* if per-core RSSI is present, add vendor element */
if (pkt_rxsts->corenum != 0) {
radiotap_encode_multi_rssi(pkt_rxsts, rtap_hdr);
}
#endif
#ifdef MONITOR_DNGL_CONV
if (pad_req) {
radiotap_encode_alignpad(rtap_hdr, pad_req);
}
#endif
rtap_len = LTOH16(rtap_hdr->it_len);
len += rtap_len;
#ifndef MONITOR_DNGL_CONV
if (len > MAX_MON_PKT_SIZE) {
return 0;
}
/* copy payload */
if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMSDU_FIRST) &&
!(rxsts->nfrmtype & WL_RXS_NFRM_AMSDU_SUB)) {
memcpy((uint8*)pout + rtap_len, (uint8*)p, len - rtap_len);
}
#endif
#ifdef MONITOR_DNGL_CONV
return rtap_len;
#else
return len;
#endif
}
#ifdef BCM_MON_QDBM_RSSI
void
radiotap_encode_multi_rssi(monitor_pkt_rxsts_t* rxsts, ieee80211_radiotap_header_t *hdr)
{
uint16 cur_len = LTOH16(hdr->it_len);
uint16 len = ROUNDUP(1 + rxsts->corenum * sizeof(monitor_pkt_rssi_t), 4);
int i = 0;
uint8 *vend_p = (uint8 *)hdr + cur_len;
radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t*)vend_p;
memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui));
vendor_ns->sns = 1;
vendor_ns->skip_len = HTOL16(len);
vend_p += sizeof(*vendor_ns);
vend_p[0] = rxsts->corenum;
for (i = 0; i < rxsts->corenum; i++) {
vend_p[2*i + 1] = rxsts->rxpwr[i].dBm;
vend_p[2*i + 2] = rxsts->rxpwr[i].decidBm;
}
hdr->it_len = HTOL16(cur_len + sizeof(radiotap_vendor_ns_t) + len);
}
#endif /* BCM_CORE_RSSI */
#ifdef MONITOR_DNGL_CONV
#define AILIGN_4BYTES (4u)
void
radiotap_encode_alignpad(ieee80211_radiotap_header_t *hdr, uint16 pad_req)
{
uint16 cur_len = LTOH16(hdr->it_len);
uint8 *vend_p = (uint8 *)hdr + cur_len;
radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t*)vend_p;
uint16 len;
uint16 align_pad = 0;
memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui));
vendor_ns->sns = WL_RADIOTAP_BRCM_PAD_SNS;
len = cur_len + sizeof(radiotap_vendor_ns_t);
if (len % AILIGN_4BYTES != 0) {
align_pad = (AILIGN_4BYTES - (len % AILIGN_4BYTES));
}
hdr->it_len = HTOL16(len + pad_req + align_pad);
vendor_ns->skip_len = HTOL16(pad_req + align_pad);
}
#endif /* MONITOR_DNGL_CONV */
void
radiotap_encode_bw_signaling(uint16 mask,
struct wl_rxsts* rxsts, ieee80211_radiotap_header_t *hdr)
{
uint16 cur_len = LTOH16(hdr->it_len);
uint8 *vend_p = (uint8 *)hdr + cur_len;
radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t *)vend_p;
wl_radiotap_nonht_vht_t* nonht_vht;
memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui));
vendor_ns->sns = 0;
vendor_ns->skip_len = sizeof(wl_radiotap_nonht_vht_t);
nonht_vht = (wl_radiotap_nonht_vht_t *)(vend_p + sizeof(*vendor_ns));
/* VHT b/w signalling */
bzero((uint8 *)nonht_vht, sizeof(wl_radiotap_nonht_vht_t));
nonht_vht->len = WL_RADIOTAP_NONHT_VHT_LEN;
nonht_vht->flags |= WL_RADIOTAP_F_NONHT_VHT_BW;
nonht_vht->bw = (uint8)rxsts->bw_nonht;
if (mask == FC_RTS) {
if (rxsts->vhtflags & WL_RXS_VHTF_DYN_BW_NONHT) {
nonht_vht->flags |= WL_RADIOTAP_F_NONHT_VHT_DYN_BW;
}
}
hdr->it_len = HTOL16(cur_len + sizeof(radiotap_vendor_ns_t) +
sizeof(wl_radiotap_nonht_vht_t));
}
void
radiotap_add_vendor_ns(ieee80211_radiotap_header_t *hdr)
{
uint32 * it_present = &hdr->it_present;
uint16 len = LTOH16(hdr->it_len);
/* if the last bitmap has a vendor ns, add a new one */
if (it_present[bitmap] & (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) {
it_present[bitmap] |= 1 << IEEE80211_RADIOTAP_EXT;
bitmap++;
/* align to 8 bytes */
if (bitmap%2) {
hdr->it_len = HTOL16(len + 8);
}
it_present[bitmap] = 1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
} else {
it_present[bitmap] |= 1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
}
}