From 02f5ba5bac8b374bc2126920c7204c63019f28ce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 30 Jul 2010 13:30:46 +0200 Subject: iwlwifi: fix possible recursive locking deadlock commit f84b29ec0a1ab767679d3f2428877b65f94bc3ff Author: Johannes Berg Date: Tue May 18 02:29:13 2010 -0700 iwlwifi: queue user-initiated scan when doing internal scan introduced a potential deadlock because it calls ieee80211_scan_completed() with the priv->mutex held, but mac80211 may call back into iwlwifi which would lead to recursive locking. Move this out from under the mutex. Cc: stable@kernel.org Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 8024d44ce4bb..8ccb6d205b6d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2000,6 +2000,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_priv *priv = hw->priv; + bool scan_completed = false; IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -2013,7 +2014,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, if (priv->vif == vif) { priv->vif = NULL; if (priv->scan_vif == vif) { - ieee80211_scan_completed(priv->hw, true); + scan_completed = true; priv->scan_vif = NULL; priv->scan_request = NULL; } @@ -2021,6 +2022,9 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, } mutex_unlock(&priv->mutex); + if (scan_completed) + ieee80211_scan_completed(priv->hw, true); + IWL_DEBUG_MAC80211(priv, "leave\n"); } -- cgit v1.2.3 From 00c86590e36bd42574821b43b5124d75f30df9dd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 30 Jul 2010 21:02:09 +0200 Subject: ath9k_hw: clean up and fix initial noise floor calibration On AR9003 the initial noise floor calibration is currently triggered at the end of the reset without allowing the hardware to update the baseband settings. This could potentially make scans in noisy environments a bit more unreliable, so use the same calibration sequence that is used on AR9002. Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 5 ++--- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 4 +++- drivers/net/wireless/ath/ath9k/calib.c | 10 ++++++++-- drivers/net/wireless/ath/ath9k/calib.h | 2 +- drivers/net/wireless/ath/ath9k/hw.c | 7 ++----- 5 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index dabafb874c36..2387ad1a23a0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -721,7 +721,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, */ ath9k_hw_loadnf(ah, ah->curchan); - ath9k_hw_start_nfcal(ah); + ath9k_hw_start_nfcal(ah, false); } return iscaldone; @@ -869,8 +869,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) ar9002_hw_pa_cal(ah, true); /* Do NF Calibration after DC offset and other calibrations */ - REG_WRITE(ah, AR_PHY_AGC_CONTROL, - REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); + ath9k_hw_start_nfcal(ah, true); ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 5a0650399136..938365eebae6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -156,7 +156,7 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah, ath9k_hw_loadnf(ah, ah->curchan); /* start NF calibration, without updating BB NF register */ - ath9k_hw_start_nfcal(ah); + ath9k_hw_start_nfcal(ah, false); } return iscaldone; @@ -762,6 +762,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, /* Revert chainmasks to their original values before NF cal */ ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); + ath9k_hw_start_nfcal(ah, true); + /* Initialize list pointers */ ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 139289e4e933..294016f9ce7d 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -158,12 +158,18 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_reset_calvalid); -void ath9k_hw_start_nfcal(struct ath_hw *ah) +void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + + if (update) + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + else + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); } diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index cd60d09cdda7..bf4474220d1d 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -108,7 +108,7 @@ struct ath9k_pacal_info{ }; bool ath9k_hw_reset_calvalid(struct ath_hw *ah); -void ath9k_hw_start_nfcal(struct ath_hw *ah); +void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); int16_t ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8d291ccf5c88..257b623185cd 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1256,7 +1256,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ath9k_hw_channel_change(ah, chan)) { ath9k_hw_loadnf(ah, ah->curchan); - ath9k_hw_start_nfcal(ah); + ath9k_hw_start_nfcal(ah, true); return 0; } } @@ -1461,11 +1461,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ah->btcoex_hw.enabled) ath9k_hw_btcoex_enable(ah); - if (AR_SREV_9300_20_OR_LATER(ah)) { - ath9k_hw_loadnf(ah, curchan); - ath9k_hw_start_nfcal(ah); + if (AR_SREV_9300_20_OR_LATER(ah)) ar9003_hw_bb_watchdog_config(ah); - } return 0; } -- cgit v1.2.3 From 9369746050e838c57e357f0caa552386ad65a82d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 30 Jul 2010 21:02:10 +0200 Subject: ath9k_hw: fix periodic noise floor calibration on AR9003 The periodic noise floor calibration is broken on this chip family, because it keeps triggering a software-filtered noise floor calibration, but never reads the result before uploading the history buffer value to the hardware. Fix this with a call to ath9k_hw_getnf(), just like on AR9002. Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 938365eebae6..f51ab89c989d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -148,6 +148,12 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah, /* Do NF cal only at longer intervals */ if (longcal) { + /* + * Get the value from the previous NF cal and update + * history buffer. + */ + ath9k_hw_getnf(ah, chan); + /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical -- cgit v1.2.3 From ddfef79257648e165ac49e6088d459819a02a882 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 30 Jul 2010 21:02:11 +0200 Subject: ath9k: fix a crash in the PA predistortion apply function When updating the PAPRD table in hardware, PAPRD itself needs to be disabled first, otherwise the hardware can throw a data bus error, which upsets at least some platforms. Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0429dda0961f..6c1d9ef550de 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -239,6 +239,7 @@ static void ath_paprd_activate(struct ath_softc *sc) 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))) continue; -- cgit v1.2.3 From b2ccc507b70be2815a25e3c5dc23c636e128222c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 30 Jul 2010 21:02:12 +0200 Subject: ath9k_hw: fix analog shift register writes on AR9003 Writes to the analog shift registers, which are issues by the initval programming function, require a 100 usec delay (similar to AR9002, but in a different register range). Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index a753a431bb13..a491854fa38a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -542,7 +542,11 @@ static void ar9003_hw_prog_ini(struct ath_hw *ah, u32 reg = INI_RA(iniArr, i, 0); u32 val = INI_RA(iniArr, i, column); - REG_WRITE(ah, reg, val); + if (reg >= 0x16000 && reg < 0x17000) + ath9k_hw_analog_shift_regwrite(ah, reg, val); + else + REG_WRITE(ah, reg, val); + DO_DELAY(regWrites); } } -- cgit v1.2.3 From 5ee0865615f65f84e6ee9174771a6716c29e08e1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 31 Jul 2010 00:11:59 +0200 Subject: ath9k: prevent calibration during off-channel activity Previously the software scan callback was used to indicate to the hardware, when it was safe to calibrate. This didn't really work properly, because it depends on a specific order of software scan callbacks vs. channel changes. Also, software scans are not the only thing that triggers off-channel activity, so it's better to use the newly added indication from mac80211 for this and not use the software scan callback for anything calibration related. This fixes at least some of the invalid noise floor readings that I've seen in AP mode on AR9160 Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/main.c | 73 ++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 35 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 998ae2c49ed2..11fc69bee58f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -510,7 +510,7 @@ void ath_deinit_leds(struct ath_softc *sc); #define SC_OP_BEACONS BIT(1) #define SC_OP_RXAGGR BIT(2) #define SC_OP_TXAGGR BIT(3) -#define SC_OP_FULL_RESET BIT(4) +#define SC_OP_OFFCHANNEL BIT(4) #define SC_OP_PREAMBLE_SHORT BIT(5) #define SC_OP_PROTECT_ENABLE BIT(6) #define SC_OP_RXFLUSH BIT(7) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6c1d9ef550de..8387ad5b4897 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -154,6 +154,27 @@ 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)); +} + /* * 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 @@ -172,6 +193,11 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, 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,7 +217,7 @@ 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; ath_print(common, ATH_DBG_CONFIG, @@ -212,8 +238,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"); @@ -225,6 +249,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, ath_update_txpow(sc); ath9k_hw_set_interrupts(ah, ah->imask); + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) { + ath_start_ani(common); + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + ath_beacon_config(sc, NULL); + } + ps_restore: ath9k_ps_restore(sc); return r; @@ -440,8 +470,7 @@ 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->caps.hw_caps & ATH9K_HW_CAP_PAPRD) { if (!sc->sc_ah->curchan->paprd_done) ieee80211_queue_work(sc->hw, &sc->paprd_work); else @@ -449,24 +478,6 @@ set_timer: } } -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 @@ -478,7 +489,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; @@ -1580,6 +1591,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) 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) @@ -1991,7 +2006,6 @@ 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)) { @@ -2009,10 +2023,6 @@ 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); } @@ -2024,15 +2034,10 @@ 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); } -- cgit v1.2.3 From 20bd2a0952d01ba82a99b3f22d46e3832c255529 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 31 Jul 2010 00:12:00 +0200 Subject: ath9k_hw: clean up per-channel calibration data The noise floor history buffer is currently not kept per channel, which can lead to problems when changing channels from a clean channel to a noisy one. Also when switching from HT20 to HT40, the noise floor history buffer is full of measurements, but none of them contain data for the extension channel, which it needs quite a bit of time to recover from. This patch puts all the per-channel calibration data into a single data structure, and gives the the driver control over whether that is used per-channel or even not used for some channels. For ath9k_htc, I decided to keep this per-channel in order to avoid creating regressions. For ath9k, the data is kept only for the operating channel, which saves some space. ath9k_hw takes care of wiping old data when the operating channel or its channel flags change. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 8 ++- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 8 ++- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 17 ++--- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/calib.c | 99 ++++++++++++++------------- drivers/net/wireless/ath/ath9k/calib.h | 3 +- drivers/net/wireless/ath/ath9k/htc.h | 2 + drivers/net/wireless/ath/ath9k/htc_drv_main.c | 10 +-- drivers/net/wireless/ath/ath9k/hw.c | 18 +++-- drivers/net/wireless/ath/ath9k/hw.h | 24 ++++--- drivers/net/wireless/ath/ath9k/main.c | 32 ++++++--- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 12 files changed, 130 insertions(+), 94 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 2387ad1a23a0..d28a8d37f01f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -63,6 +63,7 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah, u8 rxchainmask, struct ath9k_cal_list *currCal) { + struct ath9k_hw_cal_data *caldata = ah->caldata; bool iscaldone = false; if (currCal->calState == CAL_RUNNING) { @@ -81,14 +82,14 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah, } currCal->calData->calPostProc(ah, numChains); - ichan->CalValid |= currCal->calData->calType; + caldata->CalValid |= currCal->calData->calType; currCal->calState = CAL_DONE; iscaldone = true; } else { ar9002_hw_setup_calibration(ah, currCal); } } - } else if (!(ichan->CalValid & currCal->calData->calType)) { + } else if (!(caldata->CalValid & currCal->calData->calType)) { ath9k_hw_reset_calibration(ah, currCal); } @@ -900,7 +901,8 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) ath9k_hw_reset_calibration(ah, ah->cal_list_curr); } - chan->CalValid = 0; + if (ah->caldata) + ah->caldata->CalValid = 0; return true; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index f51ab89c989d..4674ea8c9c99 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -68,6 +68,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah, u8 rxchainmask, struct ath9k_cal_list *currCal) { + struct ath9k_hw_cal_data *caldata = ah->caldata; /* Cal is assumed not done until explicitly set below */ bool iscaldone = false; @@ -95,7 +96,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah, currCal->calData->calPostProc(ah, numChains); /* Calibration has finished. */ - ichan->CalValid |= currCal->calData->calType; + caldata->CalValid |= currCal->calData->calType; currCal->calState = CAL_DONE; iscaldone = true; } else { @@ -106,7 +107,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah, ar9003_hw_setup_calibration(ah, currCal); } } - } else if (!(ichan->CalValid & currCal->calData->calType)) { + } else if (!(caldata->CalValid & currCal->calData->calType)) { /* If current cal is marked invalid in channel, kick it off */ ath9k_hw_reset_calibration(ah, currCal); } @@ -793,7 +794,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (ah->cal_list_curr) ath9k_hw_reset_calibration(ah, ah->cal_list_curr); - chan->CalValid = 0; + if (ah->caldata) + ah->caldata->CalValid = 0; return true; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 49e0c865ce5c..7c38229ba670 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -577,10 +577,11 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) } void ar9003_paprd_populate_single_table(struct ath_hw *ah, - struct ath9k_channel *chan, int chain) + struct ath9k_hw_cal_data *caldata, + int chain) { - u32 *paprd_table_val = chan->pa_table[chain]; - u32 small_signal_gain = chan->small_signal_gain[chain]; + u32 *paprd_table_val = caldata->pa_table[chain]; + u32 small_signal_gain = caldata->small_signal_gain[chain]; u32 training_power; u32 reg = 0; int i; @@ -654,17 +655,17 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) } EXPORT_SYMBOL(ar9003_paprd_setup_gain_table); -int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan, - int chain) +int ar9003_paprd_create_curve(struct ath_hw *ah, + struct ath9k_hw_cal_data *caldata, int chain) { - u16 *small_signal_gain = &chan->small_signal_gain[chain]; - u32 *pa_table = chan->pa_table[chain]; + u16 *small_signal_gain = &caldata->small_signal_gain[chain]; + u32 *pa_table = caldata->pa_table[chain]; u32 *data_L, *data_U; int i, status = 0; u32 *buf; u32 reg; - memset(chan->pa_table[chain], 0, sizeof(chan->pa_table[chain])); + memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain])); buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC); if (!buf) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 11fc69bee58f..07f26ee7a723 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -609,6 +609,7 @@ struct ath_softc { struct ath_wiphy { struct ath_softc *sc; /* shared for all virtual wiphys */ struct ieee80211_hw *hw; + struct ath9k_hw_cal_data caldata; enum ath_wiphy_state { ATH_WIPHY_INACTIVE, ATH_WIPHY_ACTIVE, diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 294016f9ce7d..18b5c0dcc1fc 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -22,23 +22,6 @@ /* We can tune this as we go by monitoring really low values */ #define ATH9K_NF_TOO_LOW -60 -/* AR5416 may return very high value (like -31 dBm), in those cases the nf - * is incorrect and we should use the static NF value. Later we can try to - * find out why they are reporting these values */ - -static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf) -{ - if (nf > ATH9K_NF_TOO_LOW) { - ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, - "noise floor value detected (%d) is " - "lower than what we think is a " - "reasonable value (%d)\n", - nf, ATH9K_NF_TOO_LOW); - return false; - } - return true; -} - static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { int16_t nfval; @@ -121,6 +104,19 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, ah->cal_samples = 0; } +static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ath_nf_limits *limit; + + if (!chan || IS_CHAN_2GHZ(chan)) + limit = &ah->nf_2g; + else + limit = &ah->nf_5g; + + return limit->nominal; +} + /* This is done for the currently configured channel */ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { @@ -128,7 +124,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; - if (!ah->curchan) + if (!ah->caldata) return true; if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) @@ -151,7 +147,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) "Resetting Cal %d state for channel %u\n", currCal->calData->calType, conf->channel->center_freq); - ah->curchan->CalValid &= ~currCal->calData->calType; + ah->caldata->CalValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; return false; @@ -175,19 +171,28 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) { - struct ath9k_nfcal_hist *h; + struct ath9k_nfcal_hist *h = NULL; unsigned i, j; int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); + s16 default_nf = ath9k_hw_get_default_nf(ah, chan); - h = ah->nfCalHist; + if (ah->caldata) + h = ah->caldata->nfCalHist; for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { + s16 nfval; + + if (h) + nfval = h[i].privNF; + else + nfval = default_nf; + val = REG_READ(ah, ah->nf_regs[i]); val &= 0xFFFFFE00; - val |= (((u32) (h[i].privNF) << 1) & 0x1ff); + val |= (((u32) nfval << 1) & 0x1ff); REG_WRITE(ah, ah->nf_regs[i], val); } } @@ -291,14 +296,18 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS] = { 0 }; struct ath9k_nfcal_hist *h; struct ieee80211_channel *c = chan->chan; + struct ath9k_hw_cal_data *caldata = ah->caldata; + + if (!caldata) + return ath9k_hw_get_default_nf(ah, chan); chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { ath_print(common, ATH_DBG_CALIBRATE, "NF did not complete in calibration window\n"); nf = 0; - chan->rawNoiseFloor = nf; - return chan->rawNoiseFloor; + caldata->rawNoiseFloor = nf; + return caldata->rawNoiseFloor; } else { ath9k_hw_do_getnf(ah, nfarray); ath9k_hw_nf_sanitize(ah, nfarray); @@ -313,47 +322,41 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, } } - h = ah->nfCalHist; + h = caldata->nfCalHist; ath9k_hw_update_nfcal_hist_buffer(h, nfarray); - chan->rawNoiseFloor = h[0].privNF; + caldata->rawNoiseFloor = h[0].privNF; - return chan->rawNoiseFloor; + return ah->caldata->rawNoiseFloor; } -void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) +void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, + struct ath9k_channel *chan) { - struct ath_nf_limits *limit; + struct ath9k_nfcal_hist *h; + s16 default_nf; int i, j; - if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan)) - limit = &ah->nf_2g; - else - limit = &ah->nf_5g; + if (!ah->caldata) + return; + h = ah->caldata->nfCalHist; + default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { - ah->nfCalHist[i].currIndex = 0; - ah->nfCalHist[i].privNF = limit->nominal; - ah->nfCalHist[i].invalidNFcount = - AR_PHY_CCA_FILTERWINDOW_LENGTH; + h[i].currIndex = 0; + h[i].privNF = default_nf; + h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH; for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { - ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal; + h[i].nfCalBuffer[j] = default_nf; } } } s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) { - s16 nf; - - if (chan->rawNoiseFloor == 0) - nf = -96; - else - nf = chan->rawNoiseFloor; - - if (!ath9k_hw_nf_in_range(ah, nf)) - nf = ATH_DEFAULT_NOISE_FLOOR; + if (!ah->caldata || !ah->caldata->rawNoiseFloor) + return ath9k_hw_get_default_nf(ah, chan); - return nf; + return ah->caldata->rawNoiseFloor; } EXPORT_SYMBOL(ath9k_hw_getchan_noise); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index bf4474220d1d..ca2e6b39c1d4 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -112,7 +112,8 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); int16_t ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); -void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah); +void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, + struct ath9k_channel *chan); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 3756400e6bf9..43b9e21bc562 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -353,6 +353,8 @@ struct ath9k_htc_priv { u16 seq_no; u32 bmiss_cnt; + struct ath9k_hw_cal_data caldata[38]; + spinlock_t beacon_lock; bool tx_queues_stop; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index cf9bcc67ade2..ebed9d1691a5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -125,6 +125,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_conf *conf = &common->hw->conf; bool fastcc = true; struct ieee80211_channel *channel = hw->conf.channel; + struct ath9k_hw_cal_data *caldata; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; @@ -149,7 +150,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf)); - ret = ath9k_hw_reset(ah, hchan, fastcc); + caldata = &priv->caldata[channel->hw_value]; + ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_print(common, ATH_DBG_FATAL, "Unable to reset channel (%u Mhz) " @@ -1028,7 +1030,7 @@ static void ath9k_htc_radio_enable(struct ieee80211_hw *hw) ah->curchan = ath9k_cmn_get_curchannel(hw, ah); /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, false); + ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (ret) { ath_print(common, ATH_DBG_FATAL, "Unable to reset hardware; reset status %d " @@ -1091,7 +1093,7 @@ static void ath9k_htc_radio_disable(struct ieee80211_hw *hw) ah->curchan = ath9k_cmn_get_curchannel(hw, ah); /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, false); + ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (ret) { ath_print(common, ATH_DBG_FATAL, "Unable to reset hardware; reset status %d " @@ -1179,7 +1181,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ath9k_hw_configpcipowersave(ah, 0, 0); ath9k_hw_htc_resetinit(ah); - ret = ath9k_hw_reset(ah, init_channel, false); + ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { ath_print(common, ATH_DBG_FATAL, "Unable to reset hardware; reset status %d " diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 257b623185cd..3384ca164562 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -610,7 +610,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) else ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); - ath9k_init_nfcal_hist_buffer(ah); ah->bb_watchdog_timeout_ms = 25; common->state = ATH_HW_INITIALIZED; @@ -1183,9 +1182,6 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, ath9k_hw_spur_mitigate_freq(ah, chan); - if (!chan->oneTimeCalsDone) - chan->oneTimeCalsDone = true; - return true; } @@ -1218,7 +1214,7 @@ bool ath9k_hw_check_alive(struct ath_hw *ah) EXPORT_SYMBOL(ath9k_hw_check_alive); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, - bool bChannelChange) + struct ath9k_hw_cal_data *caldata, bool bChannelChange) { struct ath_common *common = ath9k_hw_common(ah); u32 saveLedState; @@ -1243,9 +1239,19 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; - if (curchan && !ah->chip_fullsleep) + if (curchan && !ah->chip_fullsleep && ah->caldata) ath9k_hw_getnf(ah, curchan); + ah->caldata = caldata; + if (caldata && + (chan->channel != caldata->channel || + (chan->channelFlags & ~CHANNEL_CW_INT) != + (caldata->channelFlags & ~CHANNEL_CW_INT))) { + /* Operating channel changed, reset channel calibration data */ + memset(caldata, 0, sizeof(*caldata)); + ath9k_init_nfcal_hist_buffer(ah, chan); + } + if (bChannelChange && (ah->chip_fullsleep != true) && (ah->curchan != NULL) && diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 2d30efc0b94f..c1b701119d88 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -346,19 +346,24 @@ enum ath9k_int { CHANNEL_HT40PLUS | \ CHANNEL_HT40MINUS) -struct ath9k_channel { - struct ieee80211_channel *chan; +struct ath9k_hw_cal_data { u16 channel; u32 channelFlags; - u32 chanmode; int32_t CalValid; - bool oneTimeCalsDone; int8_t iCoff; int8_t qCoff; int16_t rawNoiseFloor; bool paprd_done; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; +}; + +struct ath9k_channel { + struct ieee80211_channel *chan; + u16 channel; + u32 channelFlags; + u32 chanmode; }; #define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ @@ -669,7 +674,7 @@ struct ath_hw { enum nl80211_iftype opmode; enum ath9k_power_mode power_mode; - struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; + struct ath9k_hw_cal_data *caldata; struct ath9k_pacal_info pacal_info; struct ar5416Stats stats; struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; @@ -863,7 +868,7 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, - bool bChannelChange); + struct ath9k_hw_cal_data *caldata, bool bChannelChange); int ath9k_hw_fill_cap_info(struct ath_hw *ah); u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan); @@ -958,9 +963,10 @@ void ar9003_hw_bb_watchdog_read(struct ath_hw *ah); void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah); void ar9003_paprd_enable(struct ath_hw *ah, bool val); void ar9003_paprd_populate_single_table(struct ath_hw *ah, - struct ath9k_channel *chan, int chain); -int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan, - int chain); + struct ath9k_hw_cal_data *caldata, + int chain); +int ar9003_paprd_create_curve(struct ath_hw *ah, + struct ath9k_hw_cal_data *caldata, int chain); int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8387ad5b4897..3caa32316e7b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -183,11 +183,13 @@ static void ath_start_ani(struct ath_common *common) 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) @@ -220,6 +222,9 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, 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", sc->sc_ah->curchan->channel, @@ -227,7 +232,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, 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), " @@ -263,9 +268,10 @@ 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; int chain; - if (!ah->curchan->paprd_done) + if (!caldata || !caldata->paprd_done) return; ath9k_ps_wakeup(sc); @@ -274,7 +280,7 @@ static void ath_paprd_activate(struct ath_softc *sc) if (!(ah->caps.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); @@ -292,6 +298,7 @@ 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; int qnum, ftype; int chain_ok = 0; int chain; @@ -299,6 +306,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; @@ -353,7 +363,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; @@ -361,7 +371,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); } @@ -470,8 +480,8 @@ 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) { - 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); @@ -829,7 +839,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), " @@ -889,7 +899,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), " @@ -922,7 +932,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); @@ -1097,7 +1107,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 " diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 501b72821b4d..700ba8dee5a5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1181,7 +1181,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) "Failed to stop TX DMA. Resetting hardware!\n"); 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", -- cgit v1.2.3 From 4254bc1c4d7b53ac10e558dfe015725fdd693da4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 31 Jul 2010 00:12:01 +0200 Subject: ath9k_hw: fix a noise floor calibration related race condition On AR5008-AR9002, other forms of calibration must not be started while the noise floor calibration is running, as this can create invalid readings which were sometimes not even recoverable by any further calibration attempts. This patch also ensures that the result of noise floor measurements are processed faster and also allows the result of the initial calibration on reset to make it into the NF history buffer Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_calib.c | 32 ++++++++++++++++++--------- drivers/net/wireless/ath/ath9k/calib.c | 15 +++++++------ drivers/net/wireless/ath/ath9k/calib.h | 3 +-- drivers/net/wireless/ath/ath9k/hw.h | 1 + 4 files changed, 31 insertions(+), 20 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index d28a8d37f01f..fe7418aefc4a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -687,8 +687,13 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, { bool iscaldone = true; struct ath9k_cal_list *currCal = ah->cal_list_curr; + bool nfcal, nfcal_pending = false; - if (currCal && + nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); + if (ah->caldata) + nfcal_pending = ah->caldata->nfcal_pending; + + if (currCal && !nfcal && (currCal->calState == CAL_RUNNING || currCal->calState == CAL_WAITING)) { iscaldone = ar9002_hw_per_calibration(ah, chan, @@ -704,7 +709,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, } /* Do NF cal only at longer intervals */ - if (longcal) { + if (longcal || nfcal_pending) { /* Do periodic PAOffset Cal */ ar9002_hw_pa_cal(ah, false); ar9002_hw_olc_temp_compensation(ah); @@ -713,16 +718,18 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, * Get the value from the previous NF cal and update * history buffer. */ - ath9k_hw_getnf(ah, chan); - - /* - * Load the NF from history buffer of the current channel. - * NF is slow time-variant, so it is OK to use a historical - * value. - */ - ath9k_hw_loadnf(ah, ah->curchan); + if (ath9k_hw_getnf(ah, chan)) { + /* + * Load the NF from history buffer of the current + * channel. + * NF is slow time-variant, so it is OK to use a + * historical value. + */ + ath9k_hw_loadnf(ah, ah->curchan); + } - ath9k_hw_start_nfcal(ah, false); + if (longcal) + ath9k_hw_start_nfcal(ah, false); } return iscaldone; @@ -872,6 +879,9 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) /* Do NF Calibration after DC offset and other calibrations */ ath9k_hw_start_nfcal(ah, true); + if (ah->caldata) + ah->caldata->nfcal_pending = true; + ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; /* Enable IQ, ADC Gain and ADC DC offset CALs */ diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 18b5c0dcc1fc..45208690c0ec 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -156,6 +156,9 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { + if (ah->caldata) + ah->caldata->nfcal_pending = true; + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); @@ -288,8 +291,7 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) } } -int16_t ath9k_hw_getnf(struct ath_hw *ah, - struct ath9k_channel *chan) +bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); int16_t nf, nfThresh; @@ -299,7 +301,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata = ah->caldata; if (!caldata) - return ath9k_hw_get_default_nf(ah, chan); + return false; chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { @@ -307,7 +309,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, "NF did not complete in calibration window\n"); nf = 0; caldata->rawNoiseFloor = nf; - return caldata->rawNoiseFloor; + return false; } else { ath9k_hw_do_getnf(ah, nfarray); ath9k_hw_nf_sanitize(ah, nfarray); @@ -323,11 +325,10 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, } h = caldata->nfCalHist; - + caldata->nfcal_pending = false; ath9k_hw_update_nfcal_hist_buffer(h, nfarray); caldata->rawNoiseFloor = h[0].privNF; - - return ah->caldata->rawNoiseFloor; + return true; } void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index ca2e6b39c1d4..0a304b3eeeb6 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -110,8 +110,7 @@ struct ath9k_pacal_info{ bool ath9k_hw_reset_calvalid(struct ath_hw *ah); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); -int16_t ath9k_hw_getnf(struct ath_hw *ah, - struct ath9k_channel *chan); +bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index c1b701119d88..399f7c1283cd 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -354,6 +354,7 @@ struct ath9k_hw_cal_data { int8_t qCoff; int16_t rawNoiseFloor; bool paprd_done; + bool nfcal_pending; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; -- cgit v1.2.3 From 5fc97f7789e6ff3d1698a544f863b55dba9d9cd9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 31 Jul 2010 08:34:09 -0700 Subject: iwlwifi: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When iwlwifi is compiled w/o debug, we get this warning: iwl-agn.c: In function ‘iwlagn_load_firmware’: iwl-agn.c:2014: warning: passing argument 3 of ‘iwl_print_hex_dump’ discards qualifiers from pointer target type iwl-debug.h:73: note: expected ‘void *’ but argument is of type ‘const u8 *’ because the const qualifier is missing in the inline stub. Fix this. Signed-off-by: Johannes Berg Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 5c2bcef5df0c..0b961a353ff6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -71,7 +71,7 @@ do { \ #define IWL_DEBUG(__priv, level, fmt, args...) #define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level, - void *p, u32 len) + const void *p, u32 len) {} #endif /* CONFIG_IWLWIFI_DEBUG */ -- cgit v1.2.3 From 735df29a0641d9d8d65117c48ee460284ffcfc05 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Sat, 31 Jul 2010 08:34:11 -0700 Subject: iwlwifi: BA scd_flow not match condition detected It is a uCode bug which cause the tx queue id not match scd_flow in compressed block ack frame, and it need to be addressed in uCode. Currently, driver will log the information when it happen. Since it is possible happen very often and we do not want to fill the syslog, so don't enable the logging by default. Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 55a1b31fd09a..ecb953f28068 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -1331,7 +1331,14 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, tid = ba_resp->tid; agg = &priv->stations[sta_id].tid[tid].agg; if (unlikely(agg->txq_id != scd_flow)) { - IWL_ERR(priv, "BA scd_flow %d does not match txq_id %d\n", + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now! + * since it is possible happen very often and in order + * not to fill the syslog, don't enable the logging by default + */ + IWL_DEBUG_TX_REPLY(priv, + "BA scd_flow %d does not match txq_id %d\n", scd_flow, agg->txq_id); return; } -- cgit v1.2.3 From 74dee2c3e1a3dc5cd6100f110f7935dece7a3505 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Sat, 31 Jul 2010 08:34:12 -0700 Subject: iwlagn: fix typo in ucode_bt_stats_read debugfs small typo fix in ucode_bt_stats_read debugfs file Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c index f052c6d09b37..d706b8afbe5a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c @@ -980,7 +980,7 @@ ssize_t iwl_ucode_bt_stats_read(struct file *file, le32_to_cpu(bt->lo_priority_tx_req_cnt), accum_bt->lo_priority_tx_req_cnt); pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", + "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", le32_to_cpu(bt->lo_priority_tx_denied_cnt), accum_bt->lo_priority_tx_denied_cnt); pos += scnprintf(buf + pos, bufsz - pos, -- cgit v1.2.3 From bb1236115eb6fd9ab7563b6a8cfbcc6980eb3ff1 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Sat, 31 Jul 2010 20:33:59 +0400 Subject: net: wl12xx: do not use kfree'd memory wl1271_dump() uses cmd after kfree(cmd). Move kfree() just after wl1271_dump(). Signed-off-by: Kulikov Vasiliy Acked-by: Juuso Oikarinen Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 96d25fb50495..4cb99c541e2a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -160,9 +160,8 @@ static void wl1271_spi_init(struct wl1271 *wl) spi_message_add_tail(&t, &m); spi_sync(wl_to_spi(wl), &m); - kfree(cmd); - wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); + kfree(cmd); } #define WL1271_BUSY_WORD_TIMEOUT 1000 -- cgit v1.2.3 From 824b185adf86163e57892f22a878f97bc4bc69ab Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sun, 1 Aug 2010 02:25:16 -0400 Subject: ath9k_hw: Fix regulatory CTL index usage for AR9003 AR9003 was not relying on the CTL indexes from the EEPROM for capping the max output power. The CTL indexes from the EEPROM provide calibrated limits for output power for each tested and supported frequency. Without this the device operates at a power level which only conforms to the transmit spectrum mask as specified by IEEE Annex I.2.3. The regulatory limit by CRDA is always used but does not provide calibrated values for optimal performance, specially on band edges. Using the calibrated data from the EEPROM ensures the device operates at optimal output power while still ensuring proper regulatory compliance. The device uses the minimum of these tree values, the value from CRDA, the calibrated value from CTL indexex, and the value to conform to the IEEE transmit spectrum mask. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 388 ++++++++++++++++++++++++- 1 file changed, 381 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index ace8d2678b18..b883b174385b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -41,6 +41,20 @@ #define LE16(x) __constant_cpu_to_le16(x) #define LE32(x) __constant_cpu_to_le32(x) +/* Local defines to distinguish between extension and control CTL's */ +#define EXT_ADDITIVE (0x8000) +#define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) +#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) +#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) +#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ +#define PWRINCR_3_TO_1_CHAIN 9 /* 10*log(3)*2 */ +#define PWRINCR_3_TO_2_CHAIN 3 /* floor(10*log(3/2)*2) */ +#define PWRINCR_2_TO_1_CHAIN 6 /* 10*log(2)*2 */ + +#define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ +#define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ + static const struct ar9300_eeprom ar9300_default = { .eepromVersion = 2, .templateVersion = 2, @@ -609,6 +623,14 @@ static const struct ar9300_eeprom ar9300_default = { } }; +static u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) +{ + if (fbin == AR9300_BCHAN_UNUSED) + return fbin; + + return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); +} + static int ath9k_hw_ar9300_check_eeprom(struct ath_hw *ah) { return 0; @@ -1417,9 +1439,9 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray) #undef POW_SM } -static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq) +static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, + u8 *targetPowerValT2) { - u8 targetPowerValT2[ar9300RateSize]; /* XXX: hard code for now, need to get from eeprom struct */ u8 ht40PowerIncForPdadc = 0; bool is2GHz = false; @@ -1553,9 +1575,6 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq) "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); i++; } - - /* Write target power array to registers */ - ar9003_hw_tx_power_regwrite(ah, targetPowerValT2); } static int ar9003_hw_cal_pier_get(struct ath_hw *ah, @@ -1799,14 +1818,369 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency) return 0; } +static u16 ar9003_hw_get_direct_edge_power(struct ar9300_eeprom *eep, + int idx, + int edge, + bool is2GHz) +{ + struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G; + struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G; + + if (is2GHz) + return ctl_2g[idx].ctlEdges[edge].tPower; + else + return ctl_5g[idx].ctlEdges[edge].tPower; +} + +static u16 ar9003_hw_get_indirect_edge_power(struct ar9300_eeprom *eep, + int idx, + unsigned int edge, + u16 freq, + bool is2GHz) +{ + struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G; + struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G; + + u8 *ctl_freqbin = is2GHz ? + &eep->ctl_freqbin_2G[idx][0] : + &eep->ctl_freqbin_5G[idx][0]; + + if (is2GHz) { + if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 1) < freq && + ctl_2g[idx].ctlEdges[edge - 1].flag) + return ctl_2g[idx].ctlEdges[edge - 1].tPower; + } else { + if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 0) < freq && + ctl_5g[idx].ctlEdges[edge - 1].flag) + return ctl_5g[idx].ctlEdges[edge - 1].tPower; + } + + return AR9300_MAX_RATE_POWER; +} + +/* + * Find the maximum conformance test limit for the given channel and CTL info + */ +static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep, + u16 freq, int idx, bool is2GHz) +{ + u16 twiceMaxEdgePower = AR9300_MAX_RATE_POWER; + u8 *ctl_freqbin = is2GHz ? + &eep->ctl_freqbin_2G[idx][0] : + &eep->ctl_freqbin_5G[idx][0]; + u16 num_edges = is2GHz ? + AR9300_NUM_BAND_EDGES_2G : AR9300_NUM_BAND_EDGES_5G; + unsigned int edge; + + /* Get the edge power */ + for (edge = 0; + (edge < num_edges) && (ctl_freqbin[edge] != AR9300_BCHAN_UNUSED); + edge++) { + /* + * If there's an exact channel match or an inband flag set + * on the lower channel use the given rdEdgePower + */ + if (freq == ath9k_hw_fbin2freq(ctl_freqbin[edge], is2GHz)) { + twiceMaxEdgePower = + ar9003_hw_get_direct_edge_power(eep, idx, + edge, is2GHz); + break; + } else if ((edge > 0) && + (freq < ath9k_hw_fbin2freq(ctl_freqbin[edge], + is2GHz))) { + twiceMaxEdgePower = + ar9003_hw_get_indirect_edge_power(eep, idx, + edge, freq, + is2GHz); + /* + * Leave loop - no more affecting edges possible in + * this monotonic increasing list + */ + break; + } + } + return twiceMaxEdgePower; +} + +static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, + u8 *pPwrArray, u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u16 powerLimit) +{ + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ath_common *common = ath9k_hw_common(ah); + struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep; + u16 twiceMaxEdgePower = AR9300_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = { + 0, 3, 6, 9, AR9300_MAX_RATE_POWER + }; + int i; + int16_t twiceLargestAntenna; + u16 scaledPower = 0, minCtlPower, maxRegAllowedPower; + u16 ctlModesFor11a[] = { + CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 + }; + u16 ctlModesFor11g[] = { + CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, + CTL_11G_EXT, CTL_2GHT40 + }; + u16 numCtlModes, *pCtlMode, ctlMode, freq; + struct chan_centers centers; + u8 *ctlIndex; + u8 ctlNum; + u16 twiceMinEdgePower; + bool is2ghz = IS_CHAN_2GHZ(chan); + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + /* Compute TxPower reduction due to Antenna Gain */ + if (is2ghz) + twiceLargestAntenna = pEepData->modalHeader2G.antennaGain; + else + twiceLargestAntenna = pEepData->modalHeader5G.antennaGain; + + twiceLargestAntenna = (int16_t)min((twiceAntennaReduction) - + twiceLargestAntenna, 0); + + /* + * scaledPower is the minimum of the user input power level + * and the regulatory allowed power level + */ + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(regulatory->tp_scale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + + /* + * Reduce scaled Power by number of chains active to get + * to per chain tx power level + */ + switch (ar5416_get_ntxchains(ah->txchainmask)) { + case 1: + break; + case 2: + scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; + break; + case 3: + scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; + break; + } + + scaledPower = max((u16)0, scaledPower); + + /* + * Get target powers from EEPROM - our baseline for TX Power + */ + if (is2ghz) { + /* Setup for CTL modes */ + /* CTL_11B, CTL_11G, CTL_2GHT20 */ + numCtlModes = + ARRAY_SIZE(ctlModesFor11g) - + SUB_NUM_CTL_MODES_AT_2G_40; + pCtlMode = ctlModesFor11g; + if (IS_CHAN_HT40(chan)) + /* All 2G CTL's */ + numCtlModes = ARRAY_SIZE(ctlModesFor11g); + } else { + /* Setup for CTL modes */ + /* CTL_11A, CTL_5GHT20 */ + numCtlModes = ARRAY_SIZE(ctlModesFor11a) - + SUB_NUM_CTL_MODES_AT_5G_40; + pCtlMode = ctlModesFor11a; + if (IS_CHAN_HT40(chan)) + /* All 5G CTL's */ + numCtlModes = ARRAY_SIZE(ctlModesFor11a); + } + + /* + * For MIMO, need to apply regulatory caps individually across + * dynamically running modes: CCK, OFDM, HT20, HT40 + * + * The outer loop walks through each possible applicable runtime mode. + * The inner loop walks through each ctlIndex entry in EEPROM. + * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. + */ + for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { + bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || + (pCtlMode[ctlMode] == CTL_2GHT40); + if (isHt40CtlMode) + freq = centers.synth_center; + else if (pCtlMode[ctlMode] & EXT_ADDITIVE) + freq = centers.ext_center; + else + freq = centers.ctl_center; + + ath_print(common, ATH_DBG_REGULATORY, + "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, " + "EXT_ADDITIVE %d\n", + ctlMode, numCtlModes, isHt40CtlMode, + (pCtlMode[ctlMode] & EXT_ADDITIVE)); + + /* walk through each CTL index stored in EEPROM */ + if (is2ghz) { + ctlIndex = pEepData->ctlIndex_2G; + ctlNum = AR9300_NUM_CTLS_2G; + } else { + ctlIndex = pEepData->ctlIndex_5G; + ctlNum = AR9300_NUM_CTLS_5G; + } + + for (i = 0; (i < ctlNum) && ctlIndex[i]; i++) { + ath_print(common, ATH_DBG_REGULATORY, + "LOOP-Ctlidx %d: cfgCtl 0x%2.2x " + "pCtlMode 0x%2.2x ctlIndex 0x%2.2x " + "chan %dn", + i, cfgCtl, pCtlMode[ctlMode], ctlIndex[i], + chan->channel); + + /* + * compare test group from regulatory + * channel list with test mode from pCtlMode + * list + */ + if ((((cfgCtl & ~CTL_MODE_M) | + (pCtlMode[ctlMode] & CTL_MODE_M)) == + ctlIndex[i]) || + (((cfgCtl & ~CTL_MODE_M) | + (pCtlMode[ctlMode] & CTL_MODE_M)) == + ((ctlIndex[i] & CTL_MODE_M) | + SD_NO_CTL))) { + twiceMinEdgePower = + ar9003_hw_get_max_edge_power(pEepData, + freq, i, + is2ghz); + + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) + /* + * Find the minimum of all CTL + * edge powers that apply to + * this channel + */ + twiceMaxEdgePower = + min(twiceMaxEdgePower, + twiceMinEdgePower); + else { + /* specific */ + twiceMaxEdgePower = + twiceMinEdgePower; + break; + } + } + } + + minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); + + ath_print(common, ATH_DBG_REGULATORY, + "SEL-Min ctlMode %d pCtlMode %d 2xMaxEdge %d " + "sP %d minCtlPwr %d\n", + ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower, + scaledPower, minCtlPower); + + /* Apply ctl mode to correct target power set */ + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = ALL_TARGET_LEGACY_1L_5L; + i <= ALL_TARGET_LEGACY_11S; i++) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + minCtlPower); + break; + case CTL_11A: + case CTL_11G: + for (i = ALL_TARGET_LEGACY_6_24; + i <= ALL_TARGET_LEGACY_54; i++) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + minCtlPower); + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = ALL_TARGET_HT20_0_8_16; + i <= ALL_TARGET_HT20_21; i++) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + minCtlPower); + pPwrArray[ALL_TARGET_HT20_22] = + (u8)min((u16)pPwrArray[ALL_TARGET_HT20_22], + minCtlPower); + pPwrArray[ALL_TARGET_HT20_23] = + (u8)min((u16)pPwrArray[ALL_TARGET_HT20_23], + minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = ALL_TARGET_HT40_0_8_16; + i <= ALL_TARGET_HT40_23; i++) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + minCtlPower); + break; + default: + break; + } + } /* end ctl mode checking */ +} + static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 twiceMaxRegulatoryPower, u8 powerLimit) { - ah->txpower_limit = powerLimit; - ar9003_hw_set_target_power_eeprom(ah, chan->channel); + struct ath_common *common = ath9k_hw_common(ah); + u8 targetPowerValT2[ar9300RateSize]; + unsigned int i = 0; + + ar9003_hw_set_target_power_eeprom(ah, chan->channel, targetPowerValT2); + ar9003_hw_set_power_per_rate_table(ah, chan, + targetPowerValT2, cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + while (i < ar9300RateSize) { + ath_print(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]); + i++; + ath_print(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]); + i++; + ath_print(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]); + i++; + ath_print(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x\n\n", i, targetPowerValT2[i]); + i++; + } + + /* Write target power array to registers */ + ar9003_hw_tx_power_regwrite(ah, targetPowerValT2); + + /* + * This is the TX power we send back to driver core, + * and it can use to pass to userspace to display our + * currently configured TX power setting. + * + * Since power is rate dependent, use one of the indices + * from the AR9300_Rates enum to select an entry from + * targetPowerValT2[] to report. Currently returns the + * power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps + * as CCK power is less interesting (?). + */ + i = ALL_TARGET_LEGACY_6_24; /* legacy */ + if (IS_CHAN_HT40(chan)) + i = ALL_TARGET_HT40_0_8_16; /* ht40 */ + else if (IS_CHAN_HT20(chan)) + i = ALL_TARGET_HT20_0_8_16; /* ht20 */ + + ah->txpower_limit = targetPowerValT2[i]; + ar9003_hw_calibration_apply(ah, chan->channel); } -- cgit v1.2.3 From 754018494216e07f43c611d342d7d8bd25b22140 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 1 Aug 2010 15:47:32 +0200 Subject: ath9k: fix an issue in ath_atx_tid paused flag management I noticed a possible issue in the paused flag management of the ath_atx_tid data structure. In particular, in a noisy environment and under heavy load, I observed that the AGGR session establishment could fail several times consecutively causing values of the paused flag greater than one for this TID (ath_tx_pause_tid is called more than once from ath_tx_aggr_start). Considering that the session for this TID can not be established also after the mac80211 stack calls the ieee80211_agg_tx_operational() since the ath_tx_aggr_resume() lowers the paused flag only by one. This patch also replaces some BUG_ON calls with WARN_ON, as even if these unlikely conditions happen, it's not fatal enough to justify a BUG_ON. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 700ba8dee5a5..4dda14e36227 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -120,26 +120,14 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) list_add_tail(&ac->list, &txq->axq_acq); } -static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid) -{ - struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; - - spin_lock_bh(&txq->axq_lock); - tid->paused++; - spin_unlock_bh(&txq->axq_lock); -} - static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; - BUG_ON(tid->paused <= 0); - spin_lock_bh(&txq->axq_lock); - - tid->paused--; + WARN_ON(!tid->paused); - if (tid->paused > 0) - goto unlock; + spin_lock_bh(&txq->axq_lock); + tid->paused = false; if (list_empty(&tid->buf_q)) goto unlock; @@ -157,15 +145,10 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) struct list_head bf_head; INIT_LIST_HEAD(&bf_head); - BUG_ON(tid->paused <= 0); - spin_lock_bh(&txq->axq_lock); + WARN_ON(!tid->paused); - tid->paused--; - - if (tid->paused > 0) { - spin_unlock_bh(&txq->axq_lock); - return; - } + spin_lock_bh(&txq->axq_lock); + tid->paused = false; while (!list_empty(&tid->buf_q)) { bf = list_first_entry(&tid->buf_q, struct ath_buf, list); @@ -811,7 +794,7 @@ void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); txtid->state |= AGGR_ADDBA_PROGRESS; - ath_tx_pause_tid(sc, txtid); + txtid->paused = true; *ssn = txtid->seq_start; } @@ -835,10 +818,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) return; } - ath_tx_pause_tid(sc, txtid); - /* drop all software retried frames and mark this TID */ spin_lock_bh(&txq->axq_lock); + txtid->paused = true; while (!list_empty(&txtid->buf_q)) { bf = list_first_entry(&txtid->buf_q, struct ath_buf, list); if (!bf_isretried(bf)) { -- cgit v1.2.3 From 5f7aebd845a9d2ed42f36b7333579ec3534b4713 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 1 Aug 2010 15:40:04 -0500 Subject: p54pci: Add PCI ID for SMC2802W The SMC2802W appears to work with p54pci. Signed-off-by: Larry Finger Tested-by: David Cozatt Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 822f8dc26e9c..71a101fb2e4e 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -43,6 +43,8 @@ static DEFINE_PCI_DEVICE_TABLE(p54p_table) = { { PCI_DEVICE(0x1260, 0x3886) }, /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */ { PCI_DEVICE(0x1260, 0xffff) }, + /* Standard Microsystems Corp SMC2802W Wireless PCI */ + { PCI_DEVICE(0x10b8, 0x2802) }, { }, }; -- cgit v1.2.3 From c8f3b7213342d905cbb75d6c8820d9141205f3a0 Mon Sep 17 00:00:00 2001 From: Jan Friedrich Date: Mon, 2 Aug 2010 23:55:50 +0200 Subject: ath9k: fix erased ieee80211_rx_status.mactime ath9k_rx_skb_preprocess nulls rxs and the mactime is never set again - mactime is always 0. This causes problems in IBSS mode. ieee80211_rx_bss_info uses mactime to decide if an IBSS merge is needed. Without this patch the merge is triggered by each beacon received. This can be recognized by the "beacon TSF higher than local TSF - IBSS merge with BSSID" log message accompanying each beacon. This problem was not completely fixed in commit a6d2055b02dde1067075795274672720baadd3ca and is not a stable kernel fix. It is solely intended for wireless-testing. Signed-off-by: Jan Friedrich Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index da0cfe90c38a..a3fc987ebab0 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1140,6 +1140,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (flush) goto requeue; + retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, + rxs, &decrypt_error); + if (retval) + goto requeue; + rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; if (rs.rs_tstamp > tsf_lower && unlikely(rs.rs_tstamp - tsf_lower > 0x10000000)) @@ -1149,11 +1154,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) rxs->mactime += 0x100000000ULL; - retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, - rxs, &decrypt_error); - if (retval) - goto requeue; - /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); -- cgit v1.2.3 From 93c0584ca92a76bd458c7fd505b1747901315156 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Tue, 3 Aug 2010 12:54:20 +0200 Subject: wireless: ipw2100: check result of kzalloc() If kzalloc() fails return with -ENOMEM from ipw2100_net_init() which is called by register_netdev. CC: Dan Carpenter Signed-off-by: Christoph Fritz Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/ipw2100.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 5bbff4c5a489..a146240f7ddb 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -1924,6 +1924,10 @@ static int ipw2100_net_init(struct net_device *dev) bg_band->channels = kzalloc(geo->bg_channels * sizeof(struct ieee80211_channel), GFP_KERNEL); + if (!bg_band->channels) { + ipw2100_down(priv); + return -ENOMEM; + } /* translate geo->bg to bg_band.channels */ for (i = 0; i < geo->bg_channels; i++) { bg_band->channels[i].band = IEEE80211_BAND_2GHZ; -- cgit v1.2.3 From 47cb905d6b1f46bf403998f4838baa3f3b597391 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Tue, 3 Aug 2010 19:43:22 +0400 Subject: rt2x00: do not use PCI resources before pci_enable_device() IRQ and resource[] may not have correct values until after PCI hotplug setup occurs at pci_enable_device() time. The semantic match that finds this problem is as follows: // @@ identifier x; identifier request ~= "pci_request.*|pci_resource.*"; @@ ( * x->irq | * x->resource | * request(x, ...) ) ... *pci_enable_device(x) // Signed-off-by: Kulikov Vasiliy Acked-by: Gertjan van Wingerde Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00pci.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index 19b262e1ddbe..63c2cc408e15 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -240,16 +240,16 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) struct rt2x00_dev *rt2x00dev; int retval; - retval = pci_request_regions(pci_dev, pci_name(pci_dev)); + retval = pci_enable_device(pci_dev); if (retval) { - ERROR_PROBE("PCI request regions failed.\n"); + ERROR_PROBE("Enable device failed.\n"); return retval; } - retval = pci_enable_device(pci_dev); + retval = pci_request_regions(pci_dev, pci_name(pci_dev)); if (retval) { - ERROR_PROBE("Enable device failed.\n"); - goto exit_release_regions; + ERROR_PROBE("PCI request regions failed.\n"); + goto exit_disable_device; } pci_set_master(pci_dev); @@ -260,14 +260,14 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { ERROR_PROBE("PCI DMA not supported.\n"); retval = -EIO; - goto exit_disable_device; + goto exit_release_regions; } hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { ERROR_PROBE("Failed to allocate hardware.\n"); retval = -ENOMEM; - goto exit_disable_device; + goto exit_release_regions; } pci_set_drvdata(pci_dev, hw); @@ -300,13 +300,12 @@ exit_free_reg: exit_free_device: ieee80211_free_hw(hw); -exit_disable_device: - if (retval != -EBUSY) - pci_disable_device(pci_dev); - exit_release_regions: pci_release_regions(pci_dev); +exit_disable_device: + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, NULL); return retval; -- cgit v1.2.3 From 2411054ae9eb7e7ef488a39912abfc4ebadbe3ad Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sun, 25 Jul 2010 13:14:29 -0400 Subject: iwlagn: Improve aggregation failure error messages 82ca9341763107615a15da6e59b9535d49eb91c3 added scary looking but harmless error messages. Make them clearer and make the actual failure message show up with the same severity as the harmless one. Signed-off-by: Andy Lutomirski Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 35c86d22b14b..23e5c42e7d7e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -300,8 +300,9 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, struct ieee80211_sta *sta) { int ret = -EAGAIN; + u32 load = rs_tl_get_load(lq_data, tid); - if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { + if (load > IWL_AGG_LOAD_THRESHOLD) { IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); ret = ieee80211_start_tx_ba_session(sta, tid); @@ -311,12 +312,14 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, * this might be cause by reloading firmware * stop the tx ba session here */ - IWL_DEBUG_HT(priv, "Fail start Tx agg on tid: %d\n", + IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", tid); ieee80211_stop_tx_ba_session(sta, tid); } - } else - IWL_ERR(priv, "Fail finding valid aggregation tid: %d\n", tid); + } else { + IWL_ERR(priv, "Aggregation not enabled for tid %d " + "because load = %u\n", tid, load); + } return ret; } -- cgit v1.2.3 From aebb628f39ccf67fe9ed888c7f80926fc1070ce5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 29 Jul 2010 23:11:30 -0700 Subject: libertas: get the right # of scanned BSSes Let's actually check the right field in the command response; and if there aren't any reported BSSes, exit early with success. Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 25f902760980..b60f66115061 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -465,7 +465,15 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, lbs_deb_enter(LBS_DEB_CFG80211); bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); - nr_sets = le16_to_cpu(resp->size); + nr_sets = le16_to_cpu(scanresp->nr_sets); + + lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", + nr_sets, bsssize, le16_to_cpu(resp->size)); + + if (nr_sets == 0) { + ret = 0; + goto done; + } /* * The general layout of the scan response is described in chapter -- cgit v1.2.3 From 197575393713eba035925e4bfdee407f473e5057 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 29 Jul 2010 23:16:01 -0700 Subject: libertas: fix association with some APs by using extended rates Some APs get pissy if you don't send the firmware the extended rates in the association request's rates TLV. Found this on a Linksys WRT54G v2; it denies association with status code 18 unless you add the extended rates too. The old driver did this, but it got lost in the cfg80211 conversion. Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 56 ++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index b60f66115061..6b35d057426d 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -257,6 +257,29 @@ static int lbs_add_supported_rates_tlv(u8 *tlv) return sizeof(rate_tlv->header) + i; } +/* Add common rates from a TLV and return the new end of the TLV */ +static u8 * +add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) +{ + int hw, ap, ap_max = ie[1]; + u8 hw_rate; + + /* Advance past IE header */ + ie += 2; + + lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); + + for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { + hw_rate = lbs_rates[hw].bitrate / 5; + for (ap = 0; ap < ap_max; ap++) { + if (hw_rate == (ie[ap] & 0x7f)) { + *tlv++ = ie[ap]; + *nrates = *nrates + 1; + } + } + } + return tlv; +} /* * Adds a TLV with all rates the hardware *and* BSS supports. @@ -264,8 +287,11 @@ static int lbs_add_supported_rates_tlv(u8 *tlv) static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) { struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; - const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); - int n; + const u8 *rates_eid, *ext_rates_eid; + int n = 0; + + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); /* * 01 00 TLV_TYPE_RATES @@ -275,26 +301,21 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); tlv += sizeof(rate_tlv->header); - if (!rates_eid) { + /* Add basic rates */ + if (rates_eid) { + tlv = add_ie_rates(tlv, rates_eid, &n); + + /* Add extended rates, if any */ + if (ext_rates_eid) + tlv = add_ie_rates(tlv, ext_rates_eid, &n); + } else { + lbs_deb_assoc("assoc: bss had no basic rate IE\n"); /* Fallback: add basic 802.11b rates */ *tlv++ = 0x82; *tlv++ = 0x84; *tlv++ = 0x8b; *tlv++ = 0x96; n = 4; - } else { - int hw, ap; - u8 ap_max = rates_eid[1]; - n = 0; - for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { - u8 hw_rate = lbs_rates[hw].bitrate / 5; - for (ap = 0; ap < ap_max; ap++) { - if (hw_rate == (rates_eid[ap+2] & 0x7f)) { - *tlv++ = rates_eid[ap+2]; - n++; - } - } - } } rate_tlv->header.len = cpu_to_le16(n); @@ -1008,6 +1029,7 @@ static int lbs_associate(struct lbs_private *priv, int status; int ret; u8 *pos = &(cmd->iebuf[0]); + u8 *tmp; lbs_deb_enter(LBS_DEB_CFG80211); @@ -1052,7 +1074,9 @@ static int lbs_associate(struct lbs_private *priv, pos += lbs_add_cf_param_tlv(pos); /* add rates TLV */ + tmp = pos + 4; /* skip Marvell IE header */ pos += lbs_add_common_rates_tlv(pos, bss); + lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); /* add auth type TLV */ if (priv->fwrelease >= 0x09000000) -- cgit v1.2.3 From cc02681923ce09a7c8cfacc6855de259b9d4ef87 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Aug 2010 00:43:47 -0500 Subject: libertas: scan before assocation if no BSSID was given Fix this leftover TODO from the cfg80211 conversion by doing a scan if cfg80211 didn't pass in the BSSID for us. Since the scan code uses so much of the cfg80211_scan_request structure to build up the firmware command, we just fake one when the scan request is triggered internally. But we need to make sure that internal 'fake' cfg82011 scan request does not get back to cfg82011 via cfg80211_scan_done(). Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 148 +++++++++++++++++++++++++++-------- drivers/net/wireless/libertas/dev.h | 5 ++ drivers/net/wireless/libertas/main.c | 1 + 3 files changed, 121 insertions(+), 33 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 6b35d057426d..8e9fbfd804b6 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -699,8 +699,13 @@ static void lbs_scan_worker(struct work_struct *work) if (priv->scan_channel >= priv->scan_req->n_channels) { /* Mark scan done */ - cfg80211_scan_done(priv->scan_req, false); + if (priv->internal_scan) + kfree(priv->scan_req); + else + cfg80211_scan_done(priv->scan_req, false); + priv->scan_req = NULL; + priv->last_scan = jiffies; } /* Restart network */ @@ -711,10 +716,33 @@ static void lbs_scan_worker(struct work_struct *work) kfree(scan_cmd); + /* Wake up anything waiting on scan completion */ + if (priv->scan_req == NULL) { + lbs_deb_scan("scan: waking up waiters\n"); + wake_up_all(&priv->scan_q); + } + out_no_scan_cmd: lbs_deb_leave(LBS_DEB_SCAN); } +static void _internal_start_scan(struct lbs_private *priv, bool internal, + struct cfg80211_scan_request *request) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", + request->n_ssids, request->n_channels, request->ie_len); + + priv->scan_channel = 0; + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(50)); + + priv->scan_req = request; + priv->internal_scan = internal; + + lbs_deb_leave(LBS_DEB_CFG80211); +} static int lbs_cfg_scan(struct wiphy *wiphy, struct net_device *dev, @@ -731,18 +759,11 @@ static int lbs_cfg_scan(struct wiphy *wiphy, goto out; } - lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", - request->n_ssids, request->n_channels, request->ie_len); - - priv->scan_channel = 0; - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(50)); + _internal_start_scan(priv, false, request); if (priv->surpriseremoved) ret = -EIO; - priv->scan_req = request; - out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; @@ -1156,7 +1177,62 @@ done: return ret; } +static struct cfg80211_scan_request * +_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) +{ + struct cfg80211_scan_request *creq = NULL; + int i, n_channels = 0; + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) + return NULL; + + /* SSIDs come after channels */ + creq->ssids = (void *)&creq->channels[n_channels]; + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* Scan all available channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + + if (!wiphy->bands[band]) + continue; + + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + /* ignore disabled channels */ + if (wiphy->bands[band]->channels[j].flags & + IEEE80211_CHAN_DISABLED) + continue; + + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + if (i) { + /* Set real number of channels specified in creq->channels[] */ + creq->n_channels = i; + + /* Scan for the SSID we're going to connect to */ + memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); + creq->ssids[0].ssid_len = sme->ssid_len; + } else { + /* No channels found... */ + kfree(creq); + creq = NULL; + } + return creq; +} static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) @@ -1168,37 +1244,43 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); - if (sme->bssid) { - bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, - sme->ssid, sme->ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - } else { - /* - * Here we have an impedance mismatch. The firmware command - * CMD_802_11_ASSOCIATE always needs a BSSID, it cannot - * connect otherwise. However, for the connect-API of - * cfg80211 the bssid is purely optional. We don't get one, - * except the user specifies one on the "iw" command line. - * - * If we don't got one, we could initiate a scan and look - * for the best matching cfg80211_bss entry. - * - * Or, better yet, net/wireless/sme.c get's rewritten into - * something more generally useful. + if (!sme->bssid) { + /* Run a scan if one isn't in-progress already and if the last + * scan was done more than 2 seconds ago. */ - lbs_pr_err("TODO: no BSS specified\n"); - ret = -ENOTSUPP; - goto done; - } + if (priv->scan_req == NULL && + time_after(jiffies, priv->last_scan + (2 * HZ))) { + struct cfg80211_scan_request *creq; + creq = _new_connect_scan_req(wiphy, sme); + if (!creq) { + ret = -EINVAL; + goto done; + } + + lbs_deb_assoc("assoc: scanning for compatible AP\n"); + _internal_start_scan(priv, true, creq); + } + + /* Wait for any in-progress scan to complete */ + lbs_deb_assoc("assoc: waiting for scan to complete\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); + lbs_deb_assoc("assoc: scanning competed\n"); + } + /* Find the BSS we want using available scan results */ + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!bss) { - lbs_pr_err("assicate: bss %pM not in scan results\n", + lbs_pr_err("assoc: bss %pM not in scan results\n", sme->bssid); ret = -ENOENT; goto done; } - lbs_deb_assoc("trying %pM", sme->bssid); + lbs_deb_assoc("trying %pM\n", bss->bssid); lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", sme->crypto.cipher_group, sme->key_idx, sme->key_len); @@ -1261,7 +1343,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, lbs_set_radio(priv, preamble, 1); /* Do the actual association */ - lbs_associate(priv, bss, sme); + ret = lbs_associate(priv, bss, sme); done: if (bss) diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 3c7e255e18c7..f062ed583901 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -161,6 +161,11 @@ struct lbs_private { /** Scanning */ struct delayed_work scan_work; int scan_channel; + /* Queue of things waiting for scan completion */ + wait_queue_head_t scan_q; + /* Whether the scan was initiated internally and not by cfg80211 */ + bool internal_scan; + unsigned long last_scan; }; extern struct cmd_confirm_sleep confirm_sleep; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 258967144b96..24958a86747b 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -719,6 +719,7 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->deep_sleep_required = 0; priv->wakeup_dev_required = 0; init_waitqueue_head(&priv->ds_awake_q); + init_waitqueue_head(&priv->scan_q); priv->authtype_auto = 1; priv->is_host_sleep_configured = 0; priv->is_host_sleep_activated = 0; -- cgit v1.2.3 From 8f1d2d2be73a98c21e68fe2a26f633892d4abdd1 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Thu, 5 Aug 2010 13:46:27 -0400 Subject: rtl8180: avoid potential NULL deref in rtl8180_beacon_work ieee80211_beacon_get can return NULL... Signed-off-by: John W. Linville Cc: stable@kernel.org --- drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 1d8178563d76..b50c39aaec05 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -695,6 +695,8 @@ static void rtl8180_beacon_work(struct work_struct *work) /* grab a fresh beacon */ skb = ieee80211_beacon_get(dev, vif); + if (!skb) + goto resched; /* * update beacon timestamp w/ TSF value -- cgit v1.2.3 From e95b743536937a72e1560c85696b425c5d1a1c18 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Aug 2010 16:00:48 +0200 Subject: iwlwifi: fix TX tracer The TX tracing code copies with the wrong length, which will typically copy too little data. Fix this by using the correct length variable. Cc: stable@kernel.org [2.6.32+] Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index ae7319bb3a99..4cf864c664ee 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -193,7 +193,7 @@ TRACE_EVENT(iwlwifi_dev_tx, __entry->framelen = buf0_len + buf1_len; memcpy(__get_dynamic_array(tfd), tfd, tfdlen); memcpy(__get_dynamic_array(buf0), buf0, buf0_len); - memcpy(__get_dynamic_array(buf1), buf1, buf0_len); + memcpy(__get_dynamic_array(buf1), buf1, buf1_len); ), TP_printk("[%p] TX %.2x (%zu bytes)", __entry->priv, -- cgit v1.2.3 From a24d52f390a77ce90dc8cf0cd75d27ec0a6ea3f8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Aug 2010 16:17:53 +0200 Subject: iwlwifi: fix locking assertions spin_is_locked() can return zero on some (UP?) configurations because locks don't exist, and that causes an endless amount of warnings. Use lockdep_assert_held() instead, which has two advantages: 1) it verifies the current task is holding the lock or mutex 2) it compiles away completely when lockdep is not enabled Cc: stable@kernel.org [2.6.34+, maybe only parts of patch] Reported-by: Thomas Meyer Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl-scan.c | 2 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index a1b6d202d57c..9dd9e64c2b0b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -1429,7 +1429,7 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv, void iwl_free_tfds_in_queue(struct iwl_priv *priv, int sta_id, int tid, int freed) { - WARN_ON(!spin_is_locked(&priv->sta_lock)); + lockdep_assert_held(&priv->sta_lock); if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed) priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index ecb953f28068..d04502d54df3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -1117,7 +1117,7 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv, u8 *addr = priv->stations[sta_id].sta.sta.addr; struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; - WARN_ON(!spin_is_locked(&priv->sta_lock)); + lockdep_assert_held(&priv->sta_lock); switch (priv->stations[sta_id].tid[tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_DELBA: diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index b0c6b0473901..a4b3663a262f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -298,7 +298,7 @@ EXPORT_SYMBOL(iwl_init_scan_params); static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif) { - WARN_ON(!mutex_is_locked(&priv->mutex)); + lockdep_assert_held(&priv->mutex); IWL_DEBUG_INFO(priv, "Starting scan...\n"); set_bit(STATUS_SCANNING, &priv->status); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 9511f03f07e0..7e0829be5e78 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -773,7 +773,7 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) int iwl_restore_default_wep_keys(struct iwl_priv *priv) { - WARN_ON(!mutex_is_locked(&priv->mutex)); + lockdep_assert_held(&priv->mutex); return iwl_send_static_wepkey_cmd(priv, 0); } @@ -784,7 +784,7 @@ int iwl_remove_default_wep_key(struct iwl_priv *priv, { int ret; - WARN_ON(!mutex_is_locked(&priv->mutex)); + lockdep_assert_held(&priv->mutex); IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", keyconf->keyidx); @@ -808,7 +808,7 @@ int iwl_set_default_wep_key(struct iwl_priv *priv, { int ret; - WARN_ON(!mutex_is_locked(&priv->mutex)); + lockdep_assert_held(&priv->mutex); if (keyconf->keylen != WEP_KEY_LEN_128 && keyconf->keylen != WEP_KEY_LEN_64) { -- cgit v1.2.3