/* * Intercept management frames to collect beacon rssi data * and to do ibss merges. */ void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; /* * Call up first so subsequent work can use information * potentially stored in the node (e.g. for ibss merge). */ ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: /* update rssi statistics for use by the hal */ /* XXX unlocked check against vap->iv_bss? */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); if (sc->sc_syncbeacon && ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { /* * Resync beacon timers using the tsf of the beacon * frame we just received. */ ath_beacon_config(sc, vap); } /* fall thru... */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS && vap->iv_state == IEEE80211_S_RUN) { uint32_t rstamp = sc->sc_lastrs->rs_tstamp; uint64_t tsf = ath_extend_tsf(sc, rstamp, ath_hal_gettsf64(sc->sc_ah)); /* * Handle ibss merge as needed; check the tsf on the * frame before attempting the merge. The 802.11 spec * says the station should change it's bssid to match * the oldest station with the same ssid, where oldest * is determined by the tsf. Note that hardware * reconfiguration happens through callback to * ath_newstate as the state machine will go from * RUN -> RUN when this happens. */ 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; } }
/* * Intercept management frames to collect beacon rssi data * and to do ibss merges. */ 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 /* * Call up first so subsequent work can use information * potentially stored in the node (e.g. for ibss merge). */ ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rxs, rssi, nf); switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: /* * Only do the following processing if it's for * the current BSS. * * In scan and IBSS mode we receive all beacons, * which means we need to filter out stuff * that isn't for us or we'll end up constantly * trying to sync / merge to BSSes that aren't * actually us. */ if (IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { /* update rssi statistics for use by the hal */ /* XXX unlocked check against vap->iv_bss? */ 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); /* * Let's calculate the delta and remainder, so we can see * if the beacon timer from the AP is varying by more than * a few TU. (Which would be a huge, huge problem.) */ tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old; tsf_delta_bmiss = tsf_delta / tsf_intval; /* * If our delta is greater than half the beacon interval, * let's round the bmiss value up to the next beacon * interval. Ie, we're running really, really early * on the next beacon. */ 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); /* * The remainder using '%' is between 0 .. intval-1. * If we're actually running too fast, then the remainder * will be some large number just under intval-1. * So we need to look at whether we're running * before or after the target beacon interval * and if we are, modify how we do the remainder * calculation. */ 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: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n", __func__, (unsigned long long) tsf_beacon_old, (unsigned long long) tsf_beacon, (unsigned long long) tsf_beacon_target, (long long) tsf_delta, tsf_delta_bmiss, tsf_remainder); DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n", __func__, (unsigned long long) tsf_beacon, (unsigned long long) nexttbtt, (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval); /* We only do syncbeacon on STA VAPs; not on IBSS */ if (vap->iv_opmode == IEEE80211_M_STA && sc->sc_syncbeacon && ni == vap->iv_bss && (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=1; syncing\n", __func__); /* * Resync beacon timers using the tsf of the beacon * frame we just received. */ ath_beacon_config(sc, vap); sc->sc_syncbeacon = 0; } } /* fall thru... */ 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)); /* * Handle ibss merge as needed; check the tsf on the * frame before attempting the merge. The 802.11 spec * says the station should change it's bssid to match * the oldest station with the same ssid, where oldest * is determined by the tsf. Note that hardware * reconfiguration happens through callback to * ath_newstate as the state machine will go from * RUN -> RUN when this happens. */ 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; } }
static void ath_pwrsave_set_state_sync(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; ATH_PWRSAVE_STATE newstate = (sc)->sc_pwrsave.ps_set_state; if(sc->sc_pwrsave.ps_pwrsave_state == newstate) return; if (sc->sc_removed) return; switch (sc->sc_pwrsave.ps_pwrsave_state) { case ATH_PWRSAVE_NETWORK_SLEEP: switch (newstate) { case ATH_PWRSAVE_AWAKE: if (!ATH_PS_ALWAYS_AWAKE(sc)){ ath_hal_setpower(ah, HAL_PM_AWAKE); /* * Must clear RxAbort bit manually if hardware does not support * automatic sleep after waking up for TIM. */ if (! sc->sc_hasautosleep) { u_int32_t imask; ath_hal_setrxabort(ah, 0); /* Disable TIM_TIMER interrupt */ imask = ath_hal_intrget(ah); if ((imask & HAL_INT_TIM_TIMER) || ((imask & HAL_INT_TSFOOR) == 0)) { sc->sc_imask &= ~HAL_INT_TIM_TIMER; imask &= ~HAL_INT_TIM_TIMER; imask |= HAL_INT_TSFOOR; sc->sc_imask |= HAL_INT_TSFOOR; ath_hal_intrset(ah, imask); } } } #if ATH_TX_DUTY_CYCLE if (sc->sc_tx_dc_enable) { u_int32_t duration; /* re-arm tx duty cycle */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d=>%d: re-enable quiet time: %u%% active\n", __func__, sc->sc_pwrsave.ps_pwrsave_state, newstate, sc->sc_tx_dc_active_pct); if (sc->sc_opmode == HAL_M_HOSTAP && sc->sc_nbcnvaps != 0) { ath_beacon_config(sc, ATH_BEACON_CONFIG_REASON_RESET, ATH_IF_ID_ANY); }else if (sc->sc_nvaps){ duration = ((100-sc->sc_tx_dc_active_pct)*sc->sc_tx_dc_period)/100; ath_hal_setQuiet(sc->sc_ah, sc->sc_tx_dc_period, duration, 0, AH_TRUE); } } #endif break; case ATH_PWRSAVE_FULL_SLEEP: /* * Stop both receive PCU and DMA before going full sleep to prevent deaf mute. */ ath_hal_setrxabort(ah, 1); ath_hal_stopdmarecv(ah, 0); ath_hal_setpower(ah, HAL_PM_FULL_SLEEP); break; default: break; } break; case ATH_PWRSAVE_AWAKE: switch (newstate) { case ATH_PWRSAVE_NETWORK_SLEEP: if (!ATH_PS_ALWAYS_AWAKE(sc)){ /* * Chips that do not support automatic sleep after waking up to * receive TIM must make sure at least one beacon is received * before reentering network sleep. */ #if ATH_TX_DUTY_CYCLE if (sc->sc_tx_dc_enable) { /* disarm tx duty cycle */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d=>%d: disable quiet time: %u%% active\n", __func__, sc->sc_pwrsave.ps_pwrsave_state, newstate, sc->sc_tx_dc_active_pct); ath_hal_setQuiet(sc->sc_ah, 0, 0, 0, AH_FALSE); } #endif if (! sc->sc_hasautosleep) { /* * Do not enter network sleep if no beacon received */ if (! sc->sc_waitbeacon) { u_int32_t imask; /* Enable TIM_TIMER interrupt */ imask = ath_hal_intrget(ah); if (((imask & HAL_INT_TIM_TIMER) == 0) || (imask & HAL_INT_TSFOOR)){ sc->sc_imask |= HAL_INT_TIM_TIMER; imask |= HAL_INT_TIM_TIMER; imask &= ~HAL_INT_TSFOOR; sc->sc_imask &= ~HAL_INT_TSFOOR; ath_hal_intrset(ah, imask); } /* Stop RX state machine */ if (ath_hal_setrxabort(ah, 1)) { ath_hal_setpower(ah, HAL_PM_NETWORK_SLEEP); } } } else { ath_hal_setpower(ah, HAL_PM_NETWORK_SLEEP); } } break; case ATH_PWRSAVE_FULL_SLEEP: /* * Must abort rx prior to full sleep for 2 reasons: * * 1. Hardware does not support automatic sleep (sc_hasautosleep=0) * after waking up for TIM. * 2. WAR for EV68448 - card disappearance. * Ideally, rx would be stopped at a higher level * but the code was removed because it broke RFKILL (see EV66300). * 3. Stop both receive PCU and DMA before going full sleep to prevent deaf mute. */ #if ATH_TX_DUTY_CYCLE if (sc->sc_tx_dc_enable) { /* disarm tx duty cycle */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d=>%d: disable quiet time: %u%% active\n", __func__, sc->sc_pwrsave.ps_pwrsave_state, newstate, sc->sc_tx_dc_active_pct); ath_hal_setQuiet(sc->sc_ah, 0, 0, 0, AH_FALSE); } #endif ath_hal_setrxabort(ah, 1); ath_hal_stopdmarecv(ah, 0); ath_hal_setpower(ah, HAL_PM_FULL_SLEEP); break; default: break; } break; case ATH_PWRSAVE_FULL_SLEEP: switch (newstate) { case ATH_PWRSAVE_AWAKE: ath_hal_setpower(ah, HAL_PM_AWAKE); /* * Must clear RxAbort bit manually if hardware does not support * automatic sleep after waking up for TIM. */ if (! sc->sc_hasautosleep) { u_int32_t imask; ath_hal_setrxabort(ah, 0); /* Disable TIM_TIMER interrupt */ imask = ath_hal_intrget(ah); if (imask & HAL_INT_TIM_TIMER) { sc->sc_imask &= ~HAL_INT_TIM_TIMER; ath_hal_intrset(ah, imask & ~HAL_INT_TIM_TIMER); } } #if ATH_TX_DUTY_CYCLE if (sc->sc_tx_dc_enable) { u_int32_t duration; /* re-arm tx duty cycle */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d=>%d: re-enable quiet time: %u%% active\n", __func__, sc->sc_pwrsave.ps_pwrsave_state, newstate, sc->sc_tx_dc_active_pct); if (sc->sc_opmode == HAL_M_HOSTAP && sc->sc_nbcnvaps != 0) { ath_beacon_config(sc, ATH_BEACON_CONFIG_REASON_RESET, ATH_IF_ID_ANY); }else if (sc->sc_nvaps){ duration = ((100-sc->sc_tx_dc_active_pct)*sc->sc_tx_dc_period)/100; ath_hal_setQuiet(sc->sc_ah, sc->sc_tx_dc_period, duration, 0, AH_TRUE); } } #endif break; default: break; } default: break; } sc->sc_pwrsave.ps_pwrsave_state = newstate; /* If chip has been put in full sleep, make sure full reset is called */ if (sc->sc_pwrsave.ps_pwrsave_state == ATH_PWRSAVE_FULL_SLEEP) sc->sc_full_reset = 1; }
/* * To enable PHY (radio on) */ int ath_radio_enable(ath_dev_t dev) { struct ath_softc *sc = ATH_DEV_TO_SC(dev); struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; HAL_HT_MACMODE ht_macmode = sc->sc_ieee_ops->cwm_macmode(sc->sc_ieee); if (sc->sc_invalid) return -EIO; #if ATH_RESET_SERIAL ATH_RESET_ACQUIRE_MUTEX(sc); #endif ATH_PS_WAKEUP(sc); ath_pwrsave_awake(sc); /* Turn off PCIE ASPM when card is active */ ath_pcie_pwrsave_enable_on_phystate_change(sc, 0); ATH_USB_TX_STOP(sc->sc_osdev); #if ATH_C3_WAR STOP_C3_WAR_TIMER(sc); #endif #if !ATH_RESET_SERIAL ATH_LOCK_PCI_IRQ(sc); #endif if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, ht_macmode, sc->sc_tx_chainmask, sc->sc_rx_chainmask, sc->sc_ht_extprotspacing, AH_FALSE, &status, sc->sc_scanning)) { printk("%s: unable to reset hardware; hal status %u\n", __func__, status); } #if !ATH_RESET_SERIAL ATH_UNLOCK_PCI_IRQ(sc); #endif ath_update_txpow(sc, sc->tx_power); /* update tx power state */ ath_beacon_config(sc, ATH_BEACON_CONFIG_REASON_RESET, ATH_IF_ID_ANY); /* restart beacons */ ath_hal_intrset(ah, sc->sc_imask); ath_wmi_start_recv(sc); if (ATH_STARTRECV(sc) != 0) { /* restart recv */ printk("%s: unable to start recv logic\n", __func__); } ATH_USB_TX_START(sc->sc_osdev); /* * notify LED module radio has been turned on * This function will access the hw, so we must call it after * the power save function. */ ath_led_enable(&sc->sc_led_control); ATH_PS_SLEEP(sc); #if ATH_RESET_SERIAL ATH_RESET_RELEASE_MUTEX(sc); #endif return 0; }