#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_ath.h"
#include "opt_ah.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/ktr.h>
#include <sys/smp.h>
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/ath_hal/ah_devid.h>
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
#include <dev/ath/if_ath_misc.h>
#include <dev/ath/if_ath_tsf.h>
#include <dev/ath/if_ath_tx.h>
#include <dev/ath/if_ath_sysctl.h>
#include <dev/ath/if_ath_led.h>
#include <dev/ath/if_ath_keycache.h>
#include <dev/ath/if_ath_rx.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_athdfs.h>
#include <dev/ath/if_ath_descdma.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
#ifdef ATH_DEBUG_ALQ
#include <dev/ath/if_ath_alq.h>
#endif
#include <dev/ath/if_ath_lna_div.h>
u_int32_t
ath_calcrxfilter(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
u_int32_t rfilt;
rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
if (!sc->sc_needmib && !sc->sc_scanning)
rfilt |= HAL_RX_FILTER_PHYERR;
if (ic->ic_opmode != IEEE80211_M_STA)
rfilt |= HAL_RX_FILTER_PROBEREQ;
if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_promisc > 0)
rfilt |= HAL_RX_FILTER_PROM;
if (ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) {
rfilt |= HAL_RX_FILTER_BEACON;
} else if (ic->ic_opmode == IEEE80211_M_STA) {
if (sc->sc_do_mybeacon && ! sc->sc_scanning) {
rfilt |= HAL_RX_FILTER_MYBEACON;
} else {
rfilt |= HAL_RX_FILTER_BEACON;
}
}
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
rfilt |= HAL_RX_FILTER_BEACON;
if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL,
0, NULL) == HAL_OK &&
ic->ic_opmode == IEEE80211_M_HOSTAP)
rfilt |= HAL_RX_FILTER_PSPOLL;
if (sc->sc_nmeshvaps) {
rfilt |= HAL_RX_FILTER_BEACON;
if (sc->sc_hasbmatch)
rfilt |= HAL_RX_FILTER_BSSID;
else
rfilt |= HAL_RX_FILTER_PROM;
}
if (ic->ic_opmode == IEEE80211_M_MONITOR)
rfilt |= HAL_RX_FILTER_CONTROL;
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan))
rfilt |= HAL_RX_FILTER_COMPBAR;
if (sc->sc_dodfs)
rfilt |= HAL_RX_FILTER_PHYRADAR;
if (sc->sc_dospectral)
rfilt |= HAL_RX_FILTER_PHYRADAR;
DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s\n",
__func__, rfilt, ieee80211_opmode_name[ic->ic_opmode]);
return rfilt;
}
static int
ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
int error;
struct mbuf *m;
struct ath_desc *ds;
m = bf->bf_m;
if (m == NULL) {
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m == NULL) {
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: no mbuf/cluster\n", __func__);
sc->sc_stats.ast_rx_nombuf++;
return ENOMEM;
}
m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
error = bus_dmamap_load_mbuf_sg(sc->sc_dmat,
bf->bf_dmamap, m,
bf->bf_segs, &bf->bf_nseg,
BUS_DMA_NOWAIT);
if (error != 0) {
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: bus_dmamap_load_mbuf_sg failed; error %d\n",
__func__, error);
sc->sc_stats.ast_rx_busdma++;
m_freem(m);
return error;
}
KASSERT(bf->bf_nseg == 1,
("multi-segment packet; nseg %u", bf->bf_nseg));
bf->bf_m = m;
}
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
ds = bf->bf_desc;
if (sc->sc_rxslink)
ds->ds_link = bf->bf_daddr;
else
ds->ds_link = 0;
ds->ds_data = bf->bf_segs[0].ds_addr;
ath_hal_setuprxdesc(ah, ds
, m->m_len
, 0
);
if (sc->sc_rxlink != NULL)
*sc->sc_rxlink = bf->bf_daddr;
sc->sc_rxlink = &ds->ds_link;
return 0;
}
void
ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ath_softc *sc = vap->iv_ic->ic_softc;
uint64_t tsf_beacon_old, tsf_beacon;
uint64_t nexttbtt;
int64_t tsf_delta;
int32_t tsf_delta_bmiss;
int32_t tsf_remainder;
uint64_t tsf_beacon_target;
int tsf_intval;
tsf_beacon_old = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32;
tsf_beacon_old |= le32dec(ni->ni_tstamp.data);
#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10)
tsf_intval = 1;
if (ni->ni_intval > 0) {
tsf_intval = TU_TO_TSF(ni->ni_intval);
}
#undef TU_TO_TSF
ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
ATH_RSSI_LPF(ATH_NODE(ni)->an_node_stats.ns_avgbrssi, rssi);
if ((vap->iv_opmode != IEEE80211_M_HOSTAP) &&
IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32;
tsf_beacon |= le32dec(ni->ni_tstamp.data);
nexttbtt = ath_hal_getnexttbtt(sc->sc_ah);
tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old;
tsf_delta_bmiss = tsf_delta / tsf_intval;
if (tsf_delta % tsf_intval > (tsf_intval / 2))
tsf_delta_bmiss ++;
tsf_beacon_target = tsf_beacon_old +
(((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval);
if (tsf_beacon < tsf_beacon_target) {
tsf_remainder =
-(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval));
} else {
tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval;
}
DPRINTF(sc, ATH_DEBUG_BEACON, "%s: %s: old_tsf=%llu (%u), new_tsf=%llu (%u), target_tsf=%llu (%u), delta=%lld, bmiss=%d, remainder=%d\n",
__func__,
ieee80211_get_vap_ifname(vap),
(unsigned long long) tsf_beacon_old,
(unsigned int) (tsf_beacon_old >> 10),
(unsigned long long) tsf_beacon,
(unsigned int ) (tsf_beacon >> 10),
(unsigned long long) tsf_beacon_target,
(unsigned int) (tsf_beacon_target >> 10),
(long long) tsf_delta,
tsf_delta_bmiss,
tsf_remainder);
DPRINTF(sc, ATH_DEBUG_BEACON, "%s: %s: ni=%6D bssid=%6D tsf=%llu (%u), nexttbtt=%llu (%u), delta=%d\n",
__func__,
ieee80211_get_vap_ifname(vap),
ni->ni_bssid, ":",
vap->iv_bss->ni_bssid, ":",
(unsigned long long) tsf_beacon,
(unsigned int) (tsf_beacon >> 10),
(unsigned long long) nexttbtt,
(unsigned int) (nexttbtt >> 10),
(int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval);
if (vap->iv_opmode == IEEE80211_M_STA &&
sc->sc_syncbeacon &&
(!sc->sc_swbmiss) &&
ni == vap->iv_bss &&
((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) &&
(vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) {
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: syncbeacon=1; syncing\n",
__func__);
ath_beacon_config(sc, vap);
sc->sc_syncbeacon = 0;
}
}
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
if (vap->iv_opmode == IEEE80211_M_IBSS &&
vap->iv_state == IEEE80211_S_RUN &&
ieee80211_ibss_merge_check(ni)) {
uint32_t rstamp = sc->sc_lastrs->rs_tstamp;
uint64_t tsf = ath_extend_tsf(sc, rstamp,
ath_hal_gettsf64(sc->sc_ah));
if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
DPRINTF(sc, ATH_DEBUG_STATE,
"ibss merge, rstamp %u tsf %ju "
"tstamp %ju\n", rstamp, (uintmax_t)tsf,
(uintmax_t)ni->ni_tstamp.tsf);
(void) ieee80211_ibss_merge(ni);
}
}
break;
}
}
#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT
static void
ath_rx_tap_vendor(struct ath_softc *sc, struct mbuf *m,
const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
{
sc->sc_rx_th.wr_ext_bitmap = htole32(1 << ATH_RADIOTAP_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;
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));
sc->sc_rx_th.wr_v.vh_version = 1;
sc->sc_rx_th.wr_v.vh_rx_chainmask = sc->sc_rxchainmask;
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];
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;
sc->sc_rx_th.wr_v.evm[3] = rs->rs_evm3;
sc->sc_rx_th.wr_v.evm[4] = rs->rs_evm4;
sc->sc_rx_th.wr_v.vh_flags = ATH_VENDOR_PKT_RX;
sc->sc_rx_th.wr_v.vh_rx_hwrate = rs->rs_rate;
sc->sc_rx_th.wr_v.vh_rs_flags = rs->rs_flags;
if (rs->rs_isaggr)
sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_ISAGGR;
if (rs->rs_moreaggr)
sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_MOREAGGR;
if (rs->rs_status & HAL_RXERR_PHY) {
sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr;
sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_RXPHYERR;
} 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
static void
ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
{
#define CHAN_HT20 htole32(IEEE80211_CHAN_HT20)
#define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U)
#define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D)
#define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D)
const HAL_RATE_TABLE *rt;
uint8_t rix;
rt = sc->sc_currates;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
rix = rt->rateCodeToIndex[rs->rs_rate];
sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT;
if (rs->rs_status & HAL_RXERR_PHY) {
if (IEEE80211_IS_CHAN_HT40U(sc->sc_curchan))
sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U;
else if (IEEE80211_IS_CHAN_HT40D(sc->sc_curchan))
sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D;
else if (IEEE80211_IS_CHAN_HT20(sc->sc_curchan))
sc->sc_rx_th.wr_chan_flags |= CHAN_HT20;
} else if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) {
struct ieee80211com *ic = &sc->sc_ic;
if ((rs->rs_flags & HAL_RX_2040) == 0)
sc->sc_rx_th.wr_chan_flags |= CHAN_HT20;
else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U;
else
sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D;
if (rs->rs_flags & HAL_RX_GI)
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
}
sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf));
if (rs->rs_status & HAL_RXERR_CRC)
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
sc->sc_rx_th.wr_antnoise = nf;
sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi;
sc->sc_rx_th.wr_antenna = rs->rs_antenna;
#undef CHAN_HT
#undef CHAN_HT20
#undef CHAN_HT40U
#undef CHAN_HT40D
}
static void
ath_handle_micerror(struct ieee80211com *ic,
struct ieee80211_frame *wh, int keyix)
{
struct ieee80211_node *ni;
ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
if (ni != NULL) {
ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix);
ieee80211_free_node(ni);
}
}
int
ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
uint64_t tsf, int nf, HAL_RX_QUEUE qtype, struct ath_buf *bf,
struct mbuf *m)
{
uint64_t rstamp;
struct ieee80211_rx_stats rxs;
int len, type, i;
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni;
int is_good = 0;
struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf);
if (rs->rs_flags & HAL_RX_GI)
sc->sc_stats.ast_rx_halfgi++;
if (rs->rs_flags & HAL_RX_2040)
sc->sc_stats.ast_rx_2040++;
if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE)
sc->sc_stats.ast_rx_pre_crc_err++;
if (rs->rs_flags & HAL_RX_DELIM_CRC_POST)
sc->sc_stats.ast_rx_post_crc_err++;
if (rs->rs_flags & HAL_RX_DECRYPT_BUSY)
sc->sc_stats.ast_rx_decrypt_busy_err++;
if (rs->rs_flags & HAL_RX_HI_RX_CHAIN)
sc->sc_stats.ast_rx_hi_rx_chain++;
if (rs->rs_flags & HAL_RX_STBC)
sc->sc_stats.ast_rx_stbc++;
if (rs->rs_status != 0) {
if (rs->rs_status & HAL_RXERR_CRC)
sc->sc_stats.ast_rx_crcerr++;
if (rs->rs_status & HAL_RXERR_FIFO)
sc->sc_stats.ast_rx_fifoerr++;
if (rs->rs_status & HAL_RXERR_PHY) {
sc->sc_stats.ast_rx_phyerr++;
if ((rs->rs_phyerr == HAL_PHYERR_RADAR) ||
(rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) {
ath_dfs_process_phy_err(sc, m, rstamp, rs);
}
if (rs->rs_phyerr < ATH_IOCTL_STATS_NUM_RX_PHYERR)
sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++;
goto rx_error;
}
if (rs->rs_status & HAL_RXERR_DECRYPT) {
if (rs->rs_keyix == HAL_RXKEYIX_INVALID)
goto rx_accept;
sc->sc_stats.ast_rx_badcrypt++;
}
if (rs->rs_status & HAL_RXERR_KEYMISS) {
sc->sc_stats.ast_rx_keymiss++;
goto rx_accept;
}
if (rs->rs_status & HAL_RXERR_MIC) {
sc->sc_stats.ast_rx_badmic++;
len = rs->rs_datalen;
if (len >= sizeof (struct ieee80211_frame)) {
ath_handle_micerror(ic,
mtod(m, struct ieee80211_frame *),
sc->sc_splitmic ?
rs->rs_keyix-32 : rs->rs_keyix);
}
}
counter_u64_add(ic->ic_ierrors, 1);
rx_error:
if (re->m_rxpending != NULL) {
m_freem(re->m_rxpending);
re->m_rxpending = NULL;
}
if (ieee80211_radiotap_active(ic) &&
(rs->rs_status & sc->sc_monpass)) {
len = rs->rs_datalen;
m->m_pkthdr.len = m->m_len = len;
ath_rx_tap(sc, m, rs, rstamp, nf);
#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT
ath_rx_tap_vendor(sc, m, rs, rstamp, nf);
#endif
ieee80211_radiotap_rx_all(ic, m);
}
m_freem(m); m = NULL;
goto rx_next;
}
rx_accept:
len = rs->rs_datalen;
m->m_len = len;
if (rs->rs_more) {
if (re->m_rxpending != NULL) {
sc->sc_stats.ast_rx_toobig++;
m_freem(re->m_rxpending);
}
m->m_pkthdr.len = len;
re->m_rxpending = m;
m = NULL;
goto rx_next;
} else if (re->m_rxpending != NULL) {
re->m_rxpending->m_next = m;
re->m_rxpending->m_pkthdr.len += len;
m = re->m_rxpending;
re->m_rxpending = NULL;
} else {
m->m_pkthdr.len = len;
}
if (rs->rs_antenna >= ATH_IOCTL_STATS_NUM_RX_ANTENNA) {
device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n",
__func__, rs->rs_antenna);
#ifdef ATH_DEBUG
ath_printrxbuf(sc, bf, 0, status == HAL_OK);
#endif
rs->rs_antenna = 0;
}
if (sc->sc_rx_lnamixer) {
rs->rs_antenna = 0;
rs->rs_antenna |=
((rs->rs_rssi_ctl[2] & HAL_RX_LNA_CFG_USED)
>> HAL_RX_LNA_CFG_USED_S);
if (rs->rs_rssi_ctl[2] & HAL_RX_LNA_EXTCFG)
rs->rs_antenna |= 0x4;
}
sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
if (ieee80211_radiotap_active(ic)) {
ath_rx_tap(sc, m, rs, rstamp, nf);
#ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT
ath_rx_tap_vendor(sc, m, rs, rstamp, nf);
#endif
}
if (len < IEEE80211_MIN_LEN) {
if (!ieee80211_radiotap_active(ic)) {
DPRINTF(sc, ATH_DEBUG_RECV,
"%s: short packet %d\n", __func__, len);
sc->sc_stats.ast_rx_tooshort++;
} else {
ieee80211_radiotap_rx_all(ic, m);
}
m_freem(m); m = NULL;
goto rx_next;
}
if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
const HAL_RATE_TABLE *rt = sc->sc_currates;
uint8_t rix = rt->rateCodeToIndex[rs->rs_rate];
ieee80211_dump_pkt(ic, mtod(m, caddr_t), len,
sc->sc_hwmap[rix].ieeerate, rs->rs_rssi);
}
m_adj(m, -IEEE80211_CRC_LEN);
ni = ieee80211_find_rxnode_withkey(ic,
mtod(m, const struct ieee80211_frame_min *),
rs->rs_keyix == HAL_RXKEYIX_INVALID ?
IEEE80211_KEYIX_NONE : rs->rs_keyix);
sc->sc_lastrs = rs;
if (rs->rs_isaggr)
sc->sc_stats.ast_rx_agg++;
bzero(&rxs, sizeof(rxs));
rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI |
IEEE80211_R_C_CHAIN |
IEEE80211_R_C_NF |
IEEE80211_R_C_RSSI |
IEEE80211_R_TSF64 |
IEEE80211_R_TSF_START;
rxs.c_rssi = rs->rs_rssi;
rxs.c_nf = nf;
rxs.c_chain = 3;
rxs.c_rx_tsf = rstamp;
for (i = 0; i < 3; i++) {
rxs.c_rssi_ctl[i] = rs->rs_rssi_ctl[i];
rxs.c_rssi_ext[i] = rs->rs_rssi_ext[i];
rxs.c_nf_ctl[i] = nf;
rxs.c_nf_ext[i] = nf;
}
if (ni != NULL) {
if (ni->ni_flags & IEEE80211_NODE_HT)
m->m_flags |= M_AMPDU;
ATH_RSSI_LPF(ATH_NODE(ni)->an_node_stats.ns_avgrssi,
rs->rs_rssi);
ath_rate_update_rx_rssi(sc, ATH_NODE(ni),
ATH_RSSI(ATH_NODE(ni)->an_node_stats.ns_avgrssi));
(void) ieee80211_add_rx_params(m, &rxs);
type = ieee80211_input_mimo(ni, m);
ieee80211_free_node(ni);
m = NULL;
if (ic->ic_opmode == IEEE80211_M_STA &&
rs->rs_keyix != HAL_RXKEYIX_INVALID)
is_good = 1;
} else {
(void) ieee80211_add_rx_params(m, &rxs);
type = ieee80211_input_mimo_all(ic, m);
m = NULL;
}
ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
if (sc->sc_diversity) {
if (sc->sc_defant != rs->rs_antenna) {
if (++sc->sc_rxotherant >= 3)
ath_setdefantenna(sc, rs->rs_antenna);
} else
sc->sc_rxotherant = 0;
}
if (sc->sc_dolnadiv) {
ath_lna_rx_comb_scan(sc, rs, ticks, hz);
}
if (sc->sc_softled) {
if (type == IEEE80211_FC0_TYPE_DATA) {
const HAL_RATE_TABLE *rt = sc->sc_currates;
ath_led_event(sc,
rt->rateCodeToIndex[rs->rs_rate]);
} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
ath_led_event(sc, 0);
}
rx_next:
if (m != NULL) {
device_printf(sc->sc_dev,
"%s: mbuf %p should've been freed!\n",
__func__,
m);
}
return (is_good);
}
#define ATH_RX_MAX 128
static void
ath_rx_proc(struct ath_softc *sc, int resched)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_buf *bf;
struct ath_hal *ah = sc->sc_ah;
#ifdef IEEE80211_SUPPORT_SUPERG
struct ieee80211com *ic = &sc->sc_ic;
#endif
struct ath_desc *ds;
struct ath_rx_status *rs;
struct mbuf *m;
int ngood;
HAL_STATUS status;
int16_t nf;
u_int64_t tsf;
int npkts = 0;
int kickpcu = 0;
int ret;
ATH_UNLOCK_ASSERT(sc);
ATH_PCU_UNLOCK_ASSERT(sc);
ATH_PCU_LOCK(sc);
sc->sc_rxproc_cnt++;
kickpcu = sc->sc_kickpcu;
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__);
ngood = 0;
nf = ath_hal_getchannoise(ah, sc->sc_curchan);
sc->sc_stats.ast_rx_noise = nf;
tsf = ath_hal_gettsf64(ah);
do {
if (!kickpcu && npkts >= ATH_RX_MAX)
break;
bf = TAILQ_FIRST(&sc->sc_rxbuf);
if (sc->sc_rxslink && bf == NULL) {
device_printf(sc->sc_dev, "%s: no buffer!\n", __func__);
break;
} else if (bf == NULL) {
sc->sc_stats.ast_rx_hitqueueend++;
break;
}
m = bf->bf_m;
if (m == NULL) {
device_printf(sc->sc_dev, "%s: no mbuf!\n", __func__);
TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
goto rx_proc_next;
}
ds = bf->bf_desc;
if (ds->ds_link == bf->bf_daddr) {
sc->sc_stats.ast_rx_hitqueueend++;
break;
}
rs = &bf->bf_status.ds_rxstat;
status = ath_hal_rxprocdesc(ah, ds,
bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
ath_printrxbuf(sc, bf, 0, status == HAL_OK);
#endif
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS))
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS,
sc->sc_rx_statuslen, (char *) ds);
#endif
if (status == HAL_EINPROGRESS)
break;
TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
npkts++;
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
bf->bf_m = NULL;
if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m))
ngood++;
rx_proc_next:
ret = 0;
if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) {
TAILQ_INSERT_TAIL(&sc->sc_rxbuf,
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf,
bf_list);
ret = ath_rxbuf_init(sc,
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf);
}
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf;
} while (ret == 0);
ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
if (ngood)
sc->sc_lastrx = tsf;
ATH_KTR(sc, ATH_KTR_RXPROC, 2, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood);
if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan))
taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask);
if (resched && kickpcu) {
ATH_PCU_LOCK(sc);
ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_rx_proc: kickpcu");
device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n",
__func__, npkts);
#if 1
ath_startrecv(sc);
#else
bf = TAILQ_FIRST(&sc->sc_rxbuf);
ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP);
ath_hal_rxena(ah);
ath_mode_init(sc);
ath_hal_startpcurecv(ah, (!! sc->sc_scanning));
#endif
ath_hal_intrset(ah, sc->sc_imask);
sc->sc_kickpcu = 0;
ATH_PCU_UNLOCK(sc);
}
#ifdef IEEE80211_SUPPORT_SUPERG
if (resched)
ieee80211_ff_age_all(ic, 100);
#endif
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
if (npkts >= ATH_RX_MAX)
sc->sc_rx.recv_sched(sc, resched);
ATH_PCU_LOCK(sc);
sc->sc_rxproc_cnt--;
ATH_PCU_UNLOCK(sc);
}
#undef PA2DESC
#undef ATH_RX_MAX
static void
ath_legacy_rx_tasklet(void *arg, int npending)
{
struct ath_softc *sc = arg;
ATH_KTR(sc, ATH_KTR_RXPROC, 1, "ath_rx_proc: pending=%d", npending);
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
ATH_PCU_LOCK(sc);
if (sc->sc_inreset_cnt > 0) {
device_printf(sc->sc_dev,
"%s: sc_inreset_cnt > 0; skipping\n", __func__);
ATH_PCU_UNLOCK(sc);
return;
}
ATH_PCU_UNLOCK(sc);
ath_rx_proc(sc, 1);
}
static void
ath_legacy_flushrecv(struct ath_softc *sc)
{
ath_rx_proc(sc, 0);
}
static void
ath_legacy_flush_rxpending(struct ath_softc *sc)
{
if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
}
if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
}
}
static int
ath_legacy_flush_rxholdbf(struct ath_softc *sc)
{
struct ath_buf *bf;
bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf;
if (bf != NULL) {
if (bf->bf_m != NULL)
m_freem(bf->bf_m);
bf->bf_m = NULL;
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
(void) ath_rxbuf_init(sc, bf);
}
sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL;
bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf;
if (bf != NULL) {
if (bf->bf_m != NULL)
m_freem(bf->bf_m);
bf->bf_m = NULL;
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
(void) ath_rxbuf_init(sc, bf);
}
sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL;
return (0);
}
static void
ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_hal *ah = sc->sc_ah;
ATH_RX_LOCK(sc);
ath_hal_stoppcurecv(ah);
ath_hal_setrxfilter(ah, 0);
ath_hal_stopdmarecv(ah);
#if 0
if (dodelay)
#endif
DELAY(3000);
#ifdef ATH_DEBUG
if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
struct ath_buf *bf;
u_int ix;
device_printf(sc->sc_dev,
"%s: rx queue %p, link %p\n",
__func__,
(caddr_t)(uintptr_t) ath_hal_getrxbuf(ah, HAL_RX_QUEUE_HP),
sc->sc_rxlink);
ix = 0;
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
struct ath_desc *ds = bf->bf_desc;
struct ath_rx_status *rs = &bf->bf_status.ds_rxstat;
HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
ath_printrxbuf(sc, bf, ix, status == HAL_OK);
ix++;
}
}
#endif
(void) ath_legacy_flush_rxpending(sc);
(void) ath_legacy_flush_rxholdbf(sc);
sc->sc_rxlink = NULL;
ATH_RX_UNLOCK(sc);
#undef PA2DESC
}
static int
ath_legacy_startrecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
ATH_RX_LOCK(sc);
sc->sc_rxlink = NULL;
(void) ath_legacy_flush_rxpending(sc);
(void) ath_legacy_flush_rxholdbf(sc);
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
int error = ath_rxbuf_init(sc, bf);
if (error != 0) {
DPRINTF(sc, ATH_DEBUG_RECV,
"%s: ath_rxbuf_init failed %d\n",
__func__, error);
return error;
}
}
bf = TAILQ_FIRST(&sc->sc_rxbuf);
ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP);
ath_hal_rxena(ah);
ath_mode_init(sc);
ath_hal_startpcurecv(ah, (!! sc->sc_scanning));
ATH_RX_UNLOCK(sc);
return 0;
}
static int
ath_legacy_dma_rxsetup(struct ath_softc *sc)
{
int error;
error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
"rx", sizeof(struct ath_desc), ath_rxbuf, 1);
if (error != 0)
return (error);
return (0);
}
static int
ath_legacy_dma_rxteardown(struct ath_softc *sc)
{
if (sc->sc_rxdma.dd_desc_len != 0)
ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
return (0);
}
static void
ath_legacy_recv_sched(struct ath_softc *sc, int dosched)
{
taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
}
static void
ath_legacy_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE q,
int dosched)
{
taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
}
void
ath_recv_setup_legacy(struct ath_softc *sc)
{
sc->sc_rx_statuslen = sizeof(struct ath_desc);
sc->sc_rx.recv_start = ath_legacy_startrecv;
sc->sc_rx.recv_stop = ath_legacy_stoprecv;
sc->sc_rx.recv_flush = ath_legacy_flushrecv;
sc->sc_rx.recv_tasklet = ath_legacy_rx_tasklet;
sc->sc_rx.recv_rxbuf_init = ath_legacy_rxbuf_init;
sc->sc_rx.recv_setup = ath_legacy_dma_rxsetup;
sc->sc_rx.recv_teardown = ath_legacy_dma_rxteardown;
sc->sc_rx.recv_sched = ath_legacy_recv_sched;
sc->sc_rx.recv_sched_queue = ath_legacy_recv_sched_queue;
}