示例#1
0
/*
 * 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;
	}
}
示例#2
0
/*
 * 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;
	}
}
示例#3
0
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;
}
示例#4
0
/*
 * 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;
}