X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fath%2Fath9k%2Fmain.c;h=3ff0e476c2b3d7ad4cc209360042c6c5097d87a8;hb=4e9900180eb72b3b22fc9742999045b11607eb24;hp=4c0831ff6e921f3f00594223287cfa1596bf6459;hpb=11fe883936980fe242869d671092a466cf1db3e3;p=mv-sheeva.git diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4c0831ff6e9..3ff0e476c2b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -18,36 +18,6 @@ #include "ath9k.h" #include "btcoex.h" -static void ath_cache_conf_rate(struct ath_softc *sc, - struct ieee80211_conf *conf) -{ - switch (conf->channel->band) { - case IEEE80211_BAND_2GHZ: - if (conf_is_ht20(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT20; - else if (conf_is_ht40_minus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS; - else if (conf_is_ht40_plus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS; - else - sc->cur_rate_mode = ATH9K_MODE_11G; - break; - case IEEE80211_BAND_5GHZ: - if (conf_is_ht20(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT20; - else if (conf_is_ht40_minus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS; - else if (conf_is_ht40_plus(conf)) - sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS; - else - sc->cur_rate_mode = ATH9K_MODE_11A; - break; - default: - BUG_ON(1); - break; - } -} - static void ath_update_txpow(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -121,6 +91,7 @@ bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) void ath9k_ps_wakeup(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); @@ -129,18 +100,33 @@ void ath9k_ps_wakeup(struct ath_softc *sc) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + /* + * While the hardware is asleep, the cycle counters contain no + * useful data. Better clear them now so that they don't mess up + * survey data results. + */ + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + memset(&common->cc_survey, 0, sizeof(common->cc_survey)); + spin_unlock(&common->cc_lock); + unlock: spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } void ath9k_ps_restore(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + spin_unlock(&common->cc_lock); + if (sc->ps_idle) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); else if (sc->ps_enabled && @@ -154,6 +140,66 @@ void ath9k_ps_restore(struct ath_softc *sc) spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } +static void ath_start_ani(struct ath_common *common) +{ + struct ath_hw *ah = common->ah; + unsigned long timestamp = jiffies_to_msecs(jiffies); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + if (!(sc->sc_flags & SC_OP_ANI_RUN)) + return; + + if (sc->sc_flags & SC_OP_OFFCHANNEL) + return; + + common->ani.longcal_timer = timestamp; + common->ani.shortcal_timer = timestamp; + common->ani.checkani_timer = timestamp; + + mod_timer(&common->ani.timer, + jiffies + + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); +} + +static void ath_update_survey_nf(struct ath_softc *sc, int channel) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_channel *chan = &ah->channels[channel]; + struct survey_info *survey = &sc->survey[channel]; + + if (chan->noisefloor) { + survey->filled |= SURVEY_INFO_NOISE_DBM; + survey->noise = chan->noisefloor; + } +} + +static void ath_update_survey_stats(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + int pos = ah->curchan - &ah->channels[0]; + struct survey_info *survey = &sc->survey[pos]; + struct ath_cycle_counters *cc = &common->cc_survey; + unsigned int div = common->clockrate * 1000; + + if (ah->power_mode == ATH9K_PM_AWAKE) + ath_hw_cycle_counters_update(common); + + if (cc->cycles > 0) { + survey->filled |= SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_RX | + SURVEY_INFO_CHANNEL_TIME_TX; + survey->channel_time += cc->cycles / div; + survey->channel_time_busy += cc->rx_busy / div; + survey->channel_time_rx += cc->rx_frame / div; + survey->channel_time_tx += cc->tx_frame / div; + } + memset(cc, 0, sizeof(*cc)); + + ath_update_survey_nf(sc, pos); +} + /* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending @@ -162,16 +208,23 @@ void ath9k_ps_restore(struct ath_softc *sc) int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { + struct ath_wiphy *aphy = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc = true, stopped; struct ieee80211_channel *channel = hw->conf.channel; + struct ath9k_hw_cal_data *caldata = NULL; int r; if (sc->sc_flags & SC_OP_INVALID) return -EIO; + del_timer_sync(&common->ani.timer); + cancel_work_sync(&sc->paprd_work); + cancel_work_sync(&sc->hw_check_work); + cancel_delayed_work_sync(&sc->tx_complete_work); + ath9k_ps_wakeup(sc); /* @@ -191,17 +244,21 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, * to flush data frames already in queue because of * changing channel. */ - if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET)) + if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) fastcc = false; + if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) + caldata = &aphy->caldata; + ath_print(common, ATH_DBG_CONFIG, - "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n", + "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", sc->sc_ah->curchan->channel, - channel->center_freq, conf_is_ht40(conf)); + channel->center_freq, conf_is_ht40(conf), + fastcc); spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(ah, hchan, fastcc); + r = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (r) { ath_print(common, ATH_DBG_FATAL, "Unable to reset channel (%u MHz), " @@ -212,8 +269,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, } spin_unlock_bh(&sc->sc_resetlock); - sc->sc_flags &= ~SC_OP_FULL_RESET; - if (ath_startrecv(sc) != 0) { ath_print(common, ATH_DBG_FATAL, "Unable to restart recv logic\n"); @@ -221,10 +276,15 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, goto ps_restore; } - ath_cache_conf_rate(sc, &hw->conf); ath_update_txpow(sc); ath9k_hw_set_interrupts(ah, ah->imask); + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { + ath_beacon_config(sc, NULL); + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + ath_start_ani(common); + } + ps_restore: ath9k_ps_restore(sc); return r; @@ -233,17 +293,20 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); int chain; - if (!ah->curchan->paprd_done) + if (!caldata || !caldata->paprd_done) return; ath9k_ps_wakeup(sc); + ar9003_paprd_enable(ah, false); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->caps.tx_chainmask & BIT(chain))) + if (!(common->tx_chainmask & BIT(chain))) continue; - ar9003_paprd_populate_single_table(ah, ah->curchan, chain); + ar9003_paprd_populate_single_table(ah, caldata, chain); } ar9003_paprd_enable(ah, true); @@ -261,6 +324,8 @@ void ath_paprd_calibrate(struct work_struct *work) int band = hw->conf.channel->band; struct ieee80211_supported_band *sband = &sc->sbands[band]; struct ath_tx_control txctl; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); int qnum, ftype; int chain_ok = 0; int chain; @@ -268,6 +333,9 @@ void ath_paprd_calibrate(struct work_struct *work) int time_left; int i; + if (!caldata) + return; + skb = alloc_skb(len, GFP_KERNEL); if (!skb) return; @@ -279,7 +347,7 @@ void ath_paprd_calibrate(struct work_struct *work) hdr = (struct ieee80211_hdr *)skb->data; ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; hdr->frame_control = cpu_to_le16(ftype); - hdr->duration_id = 10; + hdr->duration_id = cpu_to_le16(10); memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); @@ -291,7 +359,7 @@ void ath_paprd_calibrate(struct work_struct *work) ath9k_ps_wakeup(sc); ar9003_paprd_init_table(ah); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->caps.tx_chainmask & BIT(chain))) + if (!(common->tx_chainmask & BIT(chain))) continue; chain_ok = 0; @@ -322,7 +390,7 @@ void ath_paprd_calibrate(struct work_struct *work) if (!ar9003_paprd_is_done(ah)) break; - if (ar9003_paprd_create_curve(ah, ah->curchan, chain) != 0) + if (ar9003_paprd_create_curve(ah, caldata, chain) != 0) break; chain_ok = 1; @@ -330,7 +398,7 @@ void ath_paprd_calibrate(struct work_struct *work) kfree_skb(skb); if (chain_ok) { - ah->curchan->paprd_done = true; + caldata->paprd_done = true; ath_paprd_activate(sc); } @@ -354,7 +422,13 @@ void ath_ani_calibrate(unsigned long data) bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); - u32 cal_interval, short_cal_interval; + u32 cal_interval, short_cal_interval, long_cal_interval; + unsigned long flags; + + if (ah->caldata && ah->caldata->nfcal_interference) + long_cal_interval = ATH_LONG_CALINTERVAL_INT; + else + long_cal_interval = ATH_LONG_CALINTERVAL; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; @@ -366,7 +440,7 @@ void ath_ani_calibrate(unsigned long data) ath9k_ps_wakeup(sc); /* Long calibration runs independently of short calibration. */ - if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { longcal = true; ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; @@ -400,8 +474,12 @@ void ath_ani_calibrate(unsigned long data) /* Skip all processing if there's nothing to do. */ if (longcal || shortcal || aniflag) { /* Call ANI routine if necessary */ - if (aniflag) + if (aniflag) { + spin_lock_irqsave(&common->cc_lock, flags); ath9k_hw_ani_monitor(ah, ah->curchan); + ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + } /* Perform calibration if necessary */ if (longcal || shortcal) { @@ -410,16 +488,6 @@ void ath_ani_calibrate(unsigned long data) ah->curchan, common->rx_chainmask, longcal); - - if (longcal) - common->ani.noise_floor = ath9k_hw_getchan_noise(ah, - ah->curchan); - - ath_print(common, ATH_DBG_ANI, - " calibrate chan %u/%x nf: %d\n", - ah->curchan->channel, - ah->curchan->channelFlags, - common->ani.noise_floor); } } @@ -439,33 +507,14 @@ set_timer: cal_interval = min(cal_interval, (u32)short_cal_interval); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && - !(sc->sc_flags & SC_OP_SCANNING)) { - if (!sc->sc_ah->curchan->paprd_done) + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) { + if (!ah->caldata->paprd_done) ieee80211_queue_work(sc->hw, &sc->paprd_work); else ath_paprd_activate(sc); } } -static void ath_start_ani(struct ath_common *common) -{ - struct ath_hw *ah = common->ah; - unsigned long timestamp = jiffies_to_msecs(jiffies); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - if (!(sc->sc_flags & SC_OP_ANI_RUN)) - return; - - common->ani.longcal_timer = timestamp; - common->ani.shortcal_timer = timestamp; - common->ani.checkani_timer = timestamp; - - mod_timer(&common->ani.timer, - jiffies + - msecs_to_jiffies((u32)ah->config.ani_poll_interval)); -} - /* * Update tx/rx chainmask. For legacy association, * hard code chainmask to 1x1, for 11n association, use @@ -477,7 +526,7 @@ void ath_update_chainmask(struct ath_softc *sc, int is_ht) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - if ((sc->sc_flags & SC_OP_SCANNING) || is_ht || + if ((sc->sc_flags & SC_OP_OFFCHANNEL) || is_ht || (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) { common->tx_chainmask = ah->caps.tx_chainmask; common->rx_chainmask = ah->caps.rx_chainmask; @@ -615,6 +664,7 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; bool sched = false; @@ -664,7 +714,12 @@ irqreturn_t ath_isr(int irq, void *dev) if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_BB_WATCHDOG)) { + + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); ar9003_hw_bb_watchdog_dbg_info(ah); + spin_unlock(&common->cc_lock); + goto chip_reset; } @@ -693,7 +748,9 @@ irqreturn_t ath_isr(int irq, void *dev) * it will clear whatever condition caused * the interrupt. */ - ath9k_hw_procmibevent(ah); + spin_lock(&common->cc_lock); + ath9k_hw_proc_mib_event(ah); + spin_unlock(&common->cc_lock); ath9k_hw_set_interrupts(ah, ah->imask); } @@ -817,7 +874,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) ah->curchan = ath_get_curchannel(sc, sc->hw); spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(ah, ah->curchan, false); + r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (r) { ath_print(common, ATH_DBG_FATAL, "Unable to reset channel (%u MHz), " @@ -857,9 +914,14 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); ieee80211_stop_queues(hw); - /* Disable LED */ - ath9k_hw_set_gpio(ah, ah->led_pin, 1); - ath9k_hw_cfg_gpio_input(ah, ah->led_pin); + /* + * Keep the LED on when the radio is disabled + * during idle unassociated state. + */ + if (!sc->ps_idle) { + ath9k_hw_set_gpio(ah, ah->led_pin, 1); + ath9k_hw_cfg_gpio_input(ah, ah->led_pin); + } /* Disable interrupts */ ath9k_hw_set_interrupts(ah, 0); @@ -872,7 +934,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) ah->curchan = ath_get_curchannel(sc, hw); spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(ah, ah->curchan, false); + r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (r) { ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, "Unable to reset channel (%u MHz), " @@ -905,7 +967,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) ath_flushrecv(sc); spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false); + r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); if (r) ath_print(common, ATH_DBG_FATAL, "Unable to reset hardware; reset status %d\n", r); @@ -920,11 +982,9 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) * that changes the channel so update any state that * might change as a result. */ - ath_cache_conf_rate(sc, &hw->conf); - ath_update_txpow(sc); - if (sc->sc_flags & SC_OP_BEACONS) + if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL))) ath_beacon_config(sc, NULL); /* restart beacons */ ath9k_hw_set_interrupts(ah, ah->imask); @@ -1080,7 +1140,7 @@ static int ath9k_start(struct ieee80211_hw *hw) * and then setup of the interrupt mask. */ spin_lock_bh(&sc->sc_resetlock); - r = ath9k_hw_reset(ah, init_channel, false); + r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (r) { ath_print(common, ATH_DBG_FATAL, "Unable to reset hardware; reset status %d " @@ -1123,14 +1183,11 @@ static int ath9k_start(struct ieee80211_hw *hw) else ah->imask |= ATH9K_INT_RX; - if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT) - ah->imask |= ATH9K_INT_GTT; + ah->imask |= ATH9K_INT_GTT; if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; - ath_cache_conf_rate(sc, &hw->conf); - sc->sc_flags &= ~SC_OP_INVALID; /* Disable BMISS interrupt when we're not associated */ @@ -1264,6 +1321,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_softc *sc = aphy->sc; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + int i; mutex_lock(&sc->mutex); @@ -1276,7 +1334,12 @@ static void ath9k_stop(struct ieee80211_hw *hw) cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->hw_check_work); - if (!sc->num_sec_wiphy) { + for (i = 0; i < sc->num_sec_wiphy; i++) { + if (sc->sec_wiphy[i]) + break; + } + + if (i == sc->num_sec_wiphy) { cancel_delayed_work_sync(&sc->wiphy_work); cancel_work_sync(&sc->chan_work); } @@ -1340,16 +1403,13 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); - if (!(ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) && - sc->nvifs > 0) { - ret = -ENOBUFS; - goto out; - } - switch (vif->type) { case NL80211_IFTYPE_STATION: ic_opmode = NL80211_IFTYPE_STATION; break; + case NL80211_IFTYPE_WDS: + ic_opmode = NL80211_IFTYPE_WDS; + break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: @@ -1375,8 +1435,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, sc->nvifs++; - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - ath9k_set_bssid_mask(hw); + ath9k_set_bssid_mask(hw, vif); if (sc->nvifs > 1) goto out; /* skip global settings for secondary vif */ @@ -1458,7 +1517,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&sc->mutex); } -void ath9k_enable_ps(struct ath_softc *sc) +static void ath9k_enable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -1472,13 +1531,33 @@ void ath9k_enable_ps(struct ath_softc *sc) } } +static void ath9k_disable_ps(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + sc->ps_enabled = false; + ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + ath9k_hw_setrxabort(ah, 0); + sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK); + if (ah->imask & ATH9K_INT_TIM_TIMER) { + ah->imask &= ~ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(ah, ah->imask); + } + } + +} + static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ieee80211_conf *conf = &hw->conf; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &hw->conf; bool disable_radio; mutex_lock(&sc->mutex); @@ -1523,35 +1602,13 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. */ if (changed & IEEE80211_CONF_CHANGE_PS) { - if (conf->flags & IEEE80211_CONF_PS) { - sc->ps_flags |= PS_ENABLED; - /* - * At this point we know hardware has received an ACK - * of a previously sent null data frame. - */ - if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) { - sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; - ath9k_enable_ps(sc); - } - } else { - sc->ps_enabled = false; - sc->ps_flags &= ~(PS_ENABLED | - PS_NULLFUNC_COMPLETED); - ath9k_setpower(sc, ATH9K_PM_AWAKE); - if (!(ah->caps.hw_caps & - ATH9K_HW_CAP_AUTOSLEEP)) { - ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | - PS_WAIT_FOR_CAB | - PS_WAIT_FOR_PSPOLL_DATA | - PS_WAIT_FOR_TX_ACK); - if (ah->imask & ATH9K_INT_TIM_TIMER) { - ah->imask &= ~ATH9K_INT_TIM_TIMER; - ath9k_hw_set_interrupts(sc->sc_ah, - ah->imask); - } - } - } + unsigned long flags; + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (conf->flags & IEEE80211_CONF_PS) + ath9k_enable_ps(sc); + else + ath9k_disable_ps(sc); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { @@ -1565,9 +1622,18 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; + int old_pos = -1; + unsigned long flags; + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; aphy->chan_idx = pos; aphy->chan_is_ht = conf_is_ht(conf); + if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + sc->sc_flags |= SC_OP_OFFCHANNEL; + else + sc->sc_flags &= ~SC_OP_OFFCHANNEL; if (aphy->state == ATH_WIPHY_SCAN || aphy->state == ATH_WIPHY_ACTIVE) @@ -1588,12 +1654,45 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_chainmask(sc, conf_is_ht(conf)); + /* update survey stats for the old channel before switching */ + spin_lock_irqsave(&common->cc_lock, flags); + ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + + /* + * If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && + sc->cur_survey != &sc->survey[pos]) { + + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ath_print(common, ATH_DBG_FATAL, "Unable to set channel\n"); mutex_unlock(&sc->mutex); return -EINVAL; } + + /* + * The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); } skip_chan_change: @@ -1624,6 +1723,7 @@ skip_chan_change: FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ FIF_FCSFAIL) /* FIXME: sc->sc_full_reset ? */ @@ -1734,20 +1834,21 @@ static int ath9k_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: - ret = ath9k_cmn_key_config(common, vif, sta, key); + ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - if (key->alg == ALG_TKIP) + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + if (sc->sc_ah->sw_mgmt_crypto && + key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } break; case DISABLE_KEY: - ath9k_cmn_key_delete(common, key); + ath_key_delete(common, key); break; default: ret = -EINVAL; @@ -1931,8 +2032,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, break; case IEEE80211_AMPDU_TX_START: ath9k_ps_wakeup(sc); - ath_tx_aggr_start(sc, sta, tid, ssn); - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + ret = ath_tx_aggr_start(sc, sta, tid, ssn); + if (!ret) + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; case IEEE80211_AMPDU_TX_STOP: @@ -1961,16 +2063,35 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &hw->conf; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + unsigned long flags; + int pos; - if (idx != 0) + spin_lock_irqsave(&common->cc_lock, flags); + if (idx == 0) + ath_update_survey_stats(sc); + + sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) { + spin_unlock_irqrestore(&common->cc_lock, flags); return -ENOENT; + } - survey->channel = conf->channel; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = common->ani.noise_floor; + chan = &sband->channels[idx]; + pos = chan->hw_value; + memcpy(survey, &sc->survey[pos], sizeof(*survey)); + survey->channel = chan; + spin_unlock_irqrestore(&common->cc_lock, flags); return 0; } @@ -1979,15 +2100,15 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); if (ath9k_wiphy_scanning(sc)) { - printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the " - "same time\n"); /* - * Do not allow the concurrent scanning state for now. This - * could be improved with scanning control moved into ath9k. + * There is a race here in mac80211 but fixing it requires + * we revisit how we handle the scan complete callback. + * After mac80211 fixes we will not have configured hardware + * to the home channel nor would we have configured the RX + * filter yet. */ mutex_unlock(&sc->mutex); return; @@ -1995,27 +2116,20 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) aphy->state = ATH_WIPHY_SCAN; ath9k_wiphy_pause_all_forced(sc, aphy); - sc->sc_flags |= SC_OP_SCANNING; - del_timer_sync(&common->ani.timer); - cancel_work_sync(&sc->paprd_work); - cancel_work_sync(&sc->hw_check_work); - cancel_delayed_work_sync(&sc->tx_complete_work); mutex_unlock(&sc->mutex); } +/* + * XXX: this requires a revisit after the driver + * scan_complete gets moved to another place/removed in mac80211. + */ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); aphy->state = ATH_WIPHY_ACTIVE; - sc->sc_flags &= ~SC_OP_SCANNING; - sc->sc_flags |= SC_OP_FULL_RESET; - ath_start_ani(common); - ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - ath_beacon_config(sc, NULL); mutex_unlock(&sc->mutex); }