| /* |
| * 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; |
| } |
| } |