diff --git a/sys/conf/options b/sys/conf/options index 18e6732f834..259f2d322bf 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -783,6 +783,7 @@ ATH_TX99_DIAG opt_ath.h ATH_ENABLE_11N opt_ath.h ATH_ENABLE_DFS opt_ath.h ATH_EEPROM_FIRMWARE opt_ath.h +ATH_ENABLE_RADIOTAP_VENDOR_EXT opt_ath.h # options for the Atheros hal AH_SUPPORT_AR5416 opt_ah.h diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 5d7396c4a8f..968b424b03d 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -799,11 +799,26 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ic->ic_update_chw = ath_update_chw; #endif /* ATH_ENABLE_11N */ +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* + * There's one vendor bitmap entry in the RX radiotap + * header; make sure that's taken into account. + */ + ieee80211_radiotap_attachv(ic, + &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 0, + ATH_TX_RADIOTAP_PRESENT, + &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 1, + ATH_RX_RADIOTAP_PRESENT); +#else + /* + * No vendor bitmap/extensions are present. + */ ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), ATH_RX_RADIOTAP_PRESENT); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ /* * Setup dynamic sysctl's now that country code and diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c index 49901b4b9c2..c1bc25cb059 100644 --- a/sys/dev/ath/if_ath_rx.c +++ b/sys/dev/ath/if_ath_rx.c @@ -354,6 +354,55 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, } } +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +static void +ath_rx_tap_vendor(struct ifnet *ifp, struct mbuf *m, + const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) +{ + struct ath_softc *sc = ifp->if_softc; + + /* Fill in the extension bitmap */ + sc->sc_rx_th.wr_ext_bitmap = htole32(1 << ATH_RADIOTAP_VENDOR_HEADER); + + /* Fill in the vendor header */ + sc->sc_rx_th.wr_vh.vh_oui[0] = 0x7f; + sc->sc_rx_th.wr_vh.vh_oui[1] = 0x03; + sc->sc_rx_th.wr_vh.vh_oui[2] = 0x00; + + /* XXX what should this be? */ + sc->sc_rx_th.wr_vh.vh_sub_ns = 0; + sc->sc_rx_th.wr_vh.vh_skip_len = + htole16(sizeof(struct ath_radiotap_vendor_hdr)); + + /* General version info */ + sc->sc_rx_th.wr_v.vh_version = 1; + + sc->sc_rx_th.wr_v.vh_rx_chainmask = sc->sc_rxchainmask; + + /* rssi */ + sc->sc_rx_th.wr_v.rssi_ctl[0] = rs->rs_rssi_ctl[0]; + sc->sc_rx_th.wr_v.rssi_ctl[1] = rs->rs_rssi_ctl[1]; + sc->sc_rx_th.wr_v.rssi_ctl[2] = rs->rs_rssi_ctl[2]; + sc->sc_rx_th.wr_v.rssi_ext[0] = rs->rs_rssi_ext[0]; + sc->sc_rx_th.wr_v.rssi_ext[1] = rs->rs_rssi_ext[1]; + sc->sc_rx_th.wr_v.rssi_ext[2] = rs->rs_rssi_ext[2]; + + /* evm */ + sc->sc_rx_th.wr_v.evm[0] = rs->rs_evm0; + sc->sc_rx_th.wr_v.evm[1] = rs->rs_evm1; + sc->sc_rx_th.wr_v.evm[2] = rs->rs_evm2; + /* XXX TODO: extend this to include 3-stream EVM */ + + /* phyerr info */ + if (rs->rs_status & HAL_RXERR_PHY) + sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr; + else + sc->sc_rx_th.wr_v.vh_phyerr_code = 0xff; + sc->sc_rx_th.wr_v.vh_rs_status = rs->rs_status; + sc->sc_rx_th.wr_v.vh_rssi = rs->rs_rssi; +} +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + static void ath_rx_tap(struct ifnet *ifp, struct mbuf *m, const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) @@ -552,6 +601,9 @@ rx_error: m->m_pkthdr.len = m->m_len = len; bf->bf_m = NULL; ath_rx_tap(ifp, m, rs, rstamp, nf); +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + ath_rx_tap_vendor(ifp, m, rs, rstamp, nf); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ ieee80211_radiotap_rx_all(ic, m); m_freem(m); } @@ -646,8 +698,12 @@ rx_accept: * material required by ieee80211_input. Note that * noise setting is filled in above. */ - if (ieee80211_radiotap_active(ic)) + if (ieee80211_radiotap_active(ic)) { ath_rx_tap(ifp, m, rs, rstamp, nf); +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + ath_rx_tap_vendor(ifp, m, rs, rstamp, nf); +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + } /* * From this point on we assume the frame is at least diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h index 0f04b8c1010..71813ae1835 100644 --- a/sys/dev/ath/if_athioctl.h +++ b/sys/dev/ath/if_athioctl.h @@ -187,7 +187,7 @@ struct ath_diag { /* * Radio capture format. */ -#define ATH_RX_RADIOTAP_PRESENT ( \ +#define ATH_RX_RADIOTAP_PRESENT_BASE ( \ (1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ @@ -197,8 +197,80 @@ struct ath_diag { (1 << IEEE80211_RADIOTAP_XCHANNEL) | \ 0) +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +#define ATH_RX_RADIOTAP_PRESENT \ + (ATH_RX_RADIOTAP_PRESENT_BASE | \ + (1 << IEEE80211_RADIOTAP_VENDOREXT) | \ + (1 << IEEE80211_RADIOTAP_EXT) | \ + 0) +#else +#define ATH_RX_RADIOTAP_PRESENT ATH_RX_RADIOTAP_PRESENT_BASE +#endif /* ATH_ENABLE_RADIOTAP_PRESENT */ + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT +/* + * This is higher than the vendor bitmap used inside + * the Atheros reference codebase. + */ + +/* Bit 8 */ +#define ATH_RADIOTAP_VENDOR_HEADER 8 + +/* + * Using four chains makes all the fields in the + * per-chain info header be 4-byte aligned. + */ +#define ATH_RADIOTAP_MAX_CHAINS 4 + +/* + * The vendor radiotap header data needs to be: + * + * + Aligned to a 4 byte address + * + .. so all internal fields are 4 bytes aligned; + * + .. and no 64 bit fields are allowed. + * + * So padding is required to ensure this is the case. + * + * Note that because of the lack of alignment with the + * vendor header (6 bytes), the first field must be + * two bytes so it can be accessed by alignment-strict + * platform (eg MIPS.) + */ +struct ath_radiotap_vendor_hdr { /* 30 bytes */ + uint8_t vh_version; /* 1 */ + uint8_t vh_rx_chainmask; /* 1 */ + + /* At this point it should be 4 byte aligned */ + uint32_t evm[ATH_RADIOTAP_MAX_CHAINS]; /* 4 * 4 = 16 */ + + uint8_t rssi_ctl[ATH_RADIOTAP_MAX_CHAINS]; /* 4 */ + uint8_t rssi_ext[ATH_RADIOTAP_MAX_CHAINS]; /* 4 */ + + uint8_t vh_phyerr_code; /* Phy error code, or 0xff */ + uint8_t vh_rs_status; /* RX status */ + uint8_t vh_rssi; /* Raw RSSI */ + uint8_t vh_pad1[1]; /* Pad to 4 byte boundary */ +} __packed; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + struct ath_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* Vendor extension header bitmap */ + uint32_t wr_ext_bitmap; /* 4 */ + + /* + * This padding is needed because: + * + the radiotap header is 8 bytes; + * + the extension bitmap is 4 bytes; + * + the tsf is 8 bytes, so it must start on an 8 byte + * boundary. + */ + uint32_t wr_pad1; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ + + /* Normal radiotap fields */ u_int64_t wr_tsf; u_int8_t wr_flags; u_int8_t wr_rate; @@ -210,6 +282,26 @@ struct ath_rx_radiotap_header { u_int16_t wr_chan_freq; u_int8_t wr_chan_ieee; int8_t wr_chan_maxpow; + +#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT + /* + * Vendor header section, as required by the + * presence of the vendor extension bit and bitmap + * entry. + * + * XXX This must be aligned to a 4 byte address? + * XXX or 8 byte address? + */ + struct ieee80211_radiotap_vendor_header wr_vh; /* 6 bytes */ + + /* + * Because of the lack of alignment enforced by the above + * header, this vendor section won't be aligned in any + * useful way. So, this will include a two-byte version + * value which will force the structure to be 4-byte aligned. + */ + struct ath_radiotap_vendor_hdr wr_v; +#endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ } __packed; #define ATH_TX_RADIOTAP_PRESENT ( \