/*- * Copyright (c) 2002-2009 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD: head/sys/dev/ath/if_ath.c 301181 2016-06-02 00:51:36Z adrian $"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" /* * This is needed for register operations which are performed * by the driver - eg, calls to ath_hal_gettsf32(). * * It's also required for any AH_DEBUG checks in here, eg the * module dependencies. */ #include "opt_ah.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for mp_ncpus */ #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATH_TX99_DIAG #include #endif #ifdef ATH_DEBUG_ALQ #include #endif #include /* XXX total hack */ struct ath_location_info { struct ath_rx_status last_rx; uint64_t last_tsf; int is_valid; }; static struct ath_location_info ath_loc_cur = { .is_valid = 0 }; void ath_location_process_rx_complete(struct ath_softc *sc, struct ath_rx_status *rs, uint64_t tsf, struct mbuf *m, struct ath_buf *bf) { struct ieee80211_frame *wh; ATH_PCU_UNLOCK_ASSERT(sc); /* * Don't bother processing non-upload, non-location frames. */ wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { case IEEE80211_FC0_SUBTYPE_CTS: case IEEE80211_FC0_SUBTYPE_ACK: device_printf(sc->sc_dev, "%s: ACK; len=%d; fc=0x%x 0x%x; flags=0x%08x\n", __func__, rs->rs_datalen, wh->i_fc[0] & 0xff, wh->i_fc[1] & 0xff, rs->rs_flags); break; default: break; } } if (! (rs->rs_flags & HAL_RX_LOC_INFO)) return; if (! (rs->rs_flags & HAL_RX_HW_UPLOAD_DATA)) return; if (! (rs->rs_flags & HAL_RX_UPLOAD_VALID)) return; ATH_PCU_LOCK(sc); ath_loc_cur.last_rx = *rs; ath_loc_cur.last_tsf = tsf; ath_loc_cur.is_valid = 1; ATH_PCU_UNLOCK(sc); /* Process RX location events */ device_printf(sc->sc_dev, "%s: RX location info: tsf=%llu, ts=%u, flags=0x%08x, ness=%d, upload_type=%d, rate=0x%02x\n", __func__, (unsigned long long) tsf, (unsigned int) rs->rs_tstamp, rs->rs_flags, rs->rs_ness, rs->rs_hw_upload_data_type, rs->rs_rate); #if 0 if (rs->rs_hw_upload_data_type == 1) { ath_printrxbuf(sc, bf, 0, 1); m_print(m, rs->rs_datalen); } #endif } extern u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs); /* * Notes: * * + re-review this again, osprey/peacock extra timing for 1/2/3 stream rates * may be different * + */ static int ath_location_txtime_adjust(struct ath_softc *sc, uint8_t rc) { #if 0 /* HT 3x3 - always 12uS (3 symbols) off */ if ((rc & 0x90) == 0x90) return 12; /* HT - always 8uS (2 symbols) off */ if ((rc & 0x90) == 0x80) return 8; #else if ((rc & 0x90) == 0x90) { return 6; } if ((rc & 0x90) == 0x80) { return 2; } #endif /* 2G ERP OFDM - always 2uS off */ if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan) && rc & 0x0f) return 2; /* XXX 5G OFDM? */ /* * XXX always off by 2.4uS on CCK long-preamble in * other tests.. */ return 0; } static int ath_location_sifs_adjust(struct ath_softc *sc) { /* XXX 10uS for DHSS, 802.11b; 11g, 11ng - but extra 6uS signal ext */ return 16; } void ath_location_process_tx_complete(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf) { struct ath_rx_status rs; /* XXX should be malloc'ed, etc */ struct ath_rc_series *rc; const HAL_RATE_TABLE *rt = sc->sc_currates; uint64_t rx_tsf; int delta_ps, diff_ps; int delta; int rx_rix, tx_rix; int rx_dur, tx_dur; int rx_dur_add, tx_dur_add; int sifs_dur; ATH_PCU_UNLOCK_ASSERT(sc); if ((ts->ts_flags & HAL_TX_FAST_TS) == 0) return; /* Fetch relevant RX side */ ATH_PCU_LOCK(sc); /* XXX should be per-NIC */ if (ath_loc_cur.is_valid == 0) { ATH_PCU_UNLOCK(sc); return; } rs = ath_loc_cur.last_rx; rx_tsf = ath_loc_cur.last_tsf; ath_loc_cur.is_valid = 0; ATH_PCU_UNLOCK(sc); rc = &bf->bf_state.bfs_rc[ts->ts_finaltsi]; /* Calculate RX duration - ACK, no SIFS */ rx_rix = rt->rateCodeToIndex[rs.rs_rate]; rx_dur = ath_hal_pkt_txtime(sc->sc_ah, rt, 14, rx_rix, !! (rs.rs_flags & HAL_RX_2040), (rs.rs_rate & 0x80) ? (!! (rs.rs_flags & HAL_RX_GI)) : rt->info[rx_rix].shortPreamble, AH_FALSE); rx_dur_add = ath_location_txtime_adjust(sc, rs.rs_rate); /* Calculate TX duration - NULL frame; we'll do SIFS */ tx_rix = rt->rateCodeToIndex[ts->ts_rate]; tx_dur = ath_hal_pkt_txtime(sc->sc_ah, rt, bf->bf_state.bfs_pktlen, tx_rix, !! (rc->flags & ATH_RC_CW40_FLAG), (ts->ts_rate & 0x80) ? (!! (rc->flags & ATH_RC_SGI_FLAG)) : rt->info[tx_rix].shortPreamble, AH_FALSE); tx_dur_add = ath_location_txtime_adjust(sc, ts->ts_rate); sifs_dur = ath_location_sifs_adjust(sc); //sifs_dur = 0; /* XXX TODO: actually print out the tx/rx status info */ device_printf(sc->sc_dev, "%s: RX ts=%u rssi=%d rate=0x%x, shortgi=%d, stbc=%d, 2040=%d\n", __func__, rs.rs_tstamp, rs.rs_rssi, rs.rs_rate, !! (rs.rs_flags & HAL_RX_STBC), !! (rs.rs_flags & HAL_RX_GI), !! (rs.rs_flags & HAL_RX_2040)); device_printf(sc->sc_dev, "%s; TX ts=%u, len=%d, rate=0x%x, shortgi=%d, stbc=%d, ldpc=%d, 2040=%d\n", __func__, ts->ts_tstamp, bf->bf_state.bfs_pktlen, ts->ts_rate, !! (rc->flags & ATH_RC_SGI_FLAG), !! (rc->flags & ATH_RC_STBC_FLAG), !! (bf->bf_state.bfs_txflags & HAL_TXDESC_LDPC), !! (rc->flags & ATH_RC_CW40_FLAG)); delta = rs.rs_tstamp - ts->ts_tstamp; delta_ps = ath_hal_mac_psec(sc->sc_ah, delta); diff_ps = delta_ps - ((tx_dur + tx_dur_add) * 1000000) - ((rx_dur + rx_dur_add) * 1000000) - (sifs_dur * 1000000); device_printf(sc->sc_dev, "%s: rxtsf=%llu, tx=%u, rx=%u, delta=%d, delta_ns=%d, rxrix=%d, txrix=%d, rxdur=%d, rxduradd=%d, txdur=%d, txduradd=%d, sifs_dur=%d, diff_ns=%d", __func__, (unsigned long long) rx_tsf, ts->ts_tstamp, rs.rs_tstamp, delta, delta_ps / 1000, rx_rix, tx_rix, rx_dur, rx_dur_add, tx_dur, tx_dur_add, sifs_dur, diff_ps / 1000); }