diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-08-28 18:38:24 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2011-08-28 18:38:24 +0000 |
commit | 65fbf0f9f04e7773edc8f273ac24afa232bf01d8 (patch) | |
tree | e021e922696045c1b0670b2813193c97c89ee969 | |
parent | 293d665351aec1046ecb731795e1777a9d26cdda (diff) | |
download | upstream-65fbf0f9f04e7773edc8f273ac24afa232bf01d8.tar.gz upstream-65fbf0f9f04e7773edc8f273ac24afa232bf01d8.tar.bz2 upstream-65fbf0f9f04e7773edc8f273ac24afa232bf01d8.zip |
ath9k: implement rx/tx antenna control
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@28107 3c298f89-4303-0410-b956-a3cf2f4a3e73
6 files changed, 893 insertions, 31 deletions
diff --git a/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch b/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch index 6ae1323d2b..fedd17f51d 100644 --- a/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch +++ b/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch @@ -22,36 +22,53 @@ } --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -226,6 +226,7 @@ static int ath_set_channel(struct ath_so - bool fastcc = true, stopped; - struct ieee80211_channel *channel = hw->conf.channel; - struct ath9k_hw_cal_data *caldata = NULL; -+ u32 oldflags; - int r; +@@ -1654,9 +1654,10 @@ static int ath9k_config(struct ieee80211 - if (sc->sc_flags & SC_OP_INVALID) -@@ -268,6 +269,24 @@ static int ath_set_channel(struct ath_so - if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) - fastcc = false; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.channel; +- struct ath9k_channel old_chan; ++ struct ath9k_channel old_chan, *hchan; + int pos = curchan->hw_value; + int old_pos = -1; ++ u32 oldflags; + unsigned long flags; -+ oldflags = hchan->channelFlags; -+ switch (sc->chan_bw) { -+ case 5: -+ hchan->channelFlags &= ~CHANNEL_HALF; -+ hchan->channelFlags |= CHANNEL_QUARTER; -+ break; -+ case 10: -+ hchan->channelFlags &= ~CHANNEL_QUARTER; -+ hchan->channelFlags |= CHANNEL_HALF; -+ break; -+ default: -+ hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER); -+ break; -+ } -+ -+ if (oldflags != hchan->channelFlags) -+ fastcc = false; + if (ah->curchan) +@@ -1709,7 +1710,23 @@ static int ath9k_config(struct ieee80211 + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + +- if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ++ hchan = &sc->sc_ah->channels[pos]; ++ oldflags = hchan->channelFlags; ++ switch (sc->chan_bw) { ++ case 5: ++ hchan->channelFlags &= ~CHANNEL_HALF; ++ hchan->channelFlags |= CHANNEL_QUARTER; ++ break; ++ case 10: ++ hchan->channelFlags &= ~CHANNEL_QUARTER; ++ hchan->channelFlags |= CHANNEL_HALF; ++ break; ++ default: ++ hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER); ++ break; ++ } + - if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) - caldata = &sc->caldata; ++ if (ath_set_channel(sc, hw, hchan) < 0) { + ath_err(common, "Unable to set channel\n"); + mutex_unlock(&sc->mutex); + return -EINVAL; +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -1504,6 +1504,10 @@ int ath9k_hw_reset(struct ath_hw *ah, st + } + ah->noise = ath9k_hw_getchan_noise(ah, chan); ++ if (!ah->curchan || ((ah->curchan->channelFlags ^ chan->channelFlags) & ++ (CHANNEL_HALF | CHANNEL_QUARTER))) ++ bChannelChange = false; ++ + if (bChannelChange && + (ah->chip_fullsleep != true) && + (ah->curchan != NULL) && diff --git a/package/mac80211/patches/531-ath9k_cur_txpower.patch b/package/mac80211/patches/531-ath9k_cur_txpower.patch index f0de43eb47..8e104d9fee 100644 --- a/package/mac80211/patches/531-ath9k_cur_txpower.patch +++ b/package/mac80211/patches/531-ath9k_cur_txpower.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -1734,6 +1734,8 @@ static int ath9k_config(struct ieee80211 +@@ -1732,6 +1732,8 @@ static int ath9k_config(struct ieee80211 return -EINVAL; } @@ -9,7 +9,7 @@ /* * The most recent snapshot of channel->noisefloor for the old * channel is only available after the hardware reset. Copy it to -@@ -1751,6 +1753,7 @@ static int ath9k_config(struct ieee80211 +@@ -1749,6 +1751,7 @@ static int ath9k_config(struct ieee80211 ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); ath9k_ps_restore(sc); diff --git a/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch b/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch new file mode 100644 index 0000000000..1164fc2acc --- /dev/null +++ b/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch @@ -0,0 +1,273 @@ +--- a/drivers/net/wireless/ath/ath.h ++++ b/drivers/net/wireless/ath/ath.h +@@ -140,9 +140,6 @@ struct ath_common { + u8 curbssid[ETH_ALEN]; + u8 bssidmask[ETH_ALEN]; + +- u8 tx_chainmask; +- u8 rx_chainmask; +- + u32 rx_bufsize; + + u32 keymax; +--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +@@ -113,7 +113,7 @@ static int ar9003_get_training_power_5g( + if (delta > scale) + return -1; + +- switch (get_streams(common->tx_chainmask)) { ++ switch (get_streams(ah->txchainmask)) { + case 1: + delta = 6; + break; +@@ -126,7 +126,7 @@ static int ar9003_get_training_power_5g( + default: + delta = 0; + ath_dbg(common, ATH_DBG_CALIBRATE, +- "Invalid tx-chainmask: %u\n", common->tx_chainmask); ++ "Invalid tx-chainmask: %u\n", ah->txchainmask); + } + + power += delta; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -318,7 +318,6 @@ static void ath_paprd_activate(struct at + { + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_cal_data *caldata = ah->caldata; +- struct ath_common *common = ath9k_hw_common(ah); + int chain; + + if (!caldata || !caldata->paprd_done) +@@ -327,7 +326,7 @@ static void ath_paprd_activate(struct at + ath9k_ps_wakeup(sc); + ar9003_paprd_enable(ah, false); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { +- if (!(common->tx_chainmask & BIT(chain))) ++ if (!(ah->txchainmask & BIT(chain))) + continue; + + ar9003_paprd_populate_single_table(ah, caldata, chain); +@@ -414,7 +413,7 @@ void ath_paprd_calibrate(struct work_str + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { +- if (!(common->tx_chainmask & BIT(chain))) ++ if (!(ah->txchainmask & BIT(chain))) + continue; + + chain_ok = 0; +@@ -535,7 +534,7 @@ void ath_ani_calibrate(unsigned long dat + if (longcal || shortcal) { + common->ani.caldone = + ath9k_hw_calibrate(ah, ah->curchan, +- common->rx_chainmask, longcal); ++ ah->rxchainmask, longcal); + } + + ath9k_ps_restore(sc); +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -1485,9 +1485,6 @@ int ath9k_hw_reset(struct ath_hw *ah, st + u64 tsf = 0; + int i, r; + +- ah->txchainmask = common->tx_chainmask; +- ah->rxchainmask = common->rx_chainmask; +- + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return -EIO; + +@@ -2105,6 +2102,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw + + pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask); + pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask); ++ ah->txchainmask = pCap->tx_chainmask; ++ ah->rxchainmask = pCap->rx_chainmask; + + ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; + +--- a/drivers/net/wireless/ath/ath9k/xmit.c ++++ b/drivers/net/wireless/ath/ath9k/xmit.c +@@ -1655,7 +1655,7 @@ u8 ath_txchainmask_reduction(struct ath_ + + static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len) + { +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; + struct ath9k_11n_rate_series series[4]; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; +@@ -1715,7 +1715,7 @@ static void ath_buf_set_rate(struct ath_ + /* MCS rates */ + series[i].Rate = rix | 0x80; + series[i].ChSel = ath_txchainmask_reduction(sc, +- common->tx_chainmask, series[i].Rate); ++ ah->txchainmask, series[i].Rate); + series[i].PktDuration = ath_pkt_duration(sc, rix, len, + is_40, is_sgi, is_sp); + if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) +@@ -1740,10 +1740,10 @@ static void ath_buf_set_rate(struct ath_ + } + + if (bf->bf_state.bfs_paprd) +- series[i].ChSel = common->tx_chainmask; ++ series[i].ChSel = ah->txchainmask; + else + series[i].ChSel = ath_txchainmask_reduction(sc, +- common->tx_chainmask, series[i].Rate); ++ ah->txchainmask, series[i].Rate); + + series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah, + phy, rate->bitrate * 100, len, rix, is_sp); +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -107,7 +107,7 @@ static void ath_beacon_setup(struct ath_ + series[0].Tries = 1; + series[0].Rate = rate; + series[0].ChSel = ath_txchainmask_reduction(sc, +- common->tx_chainmask, series[0].Rate); ++ ah->txchainmask, series[0].Rate); + series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; + ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, + series, 4, 0); +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -95,11 +95,11 @@ static ssize_t read_file_tx_chainmask(st + size_t count, loff_t *ppos) + { + struct ath_softc *sc = file->private_data; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + +- len = sprintf(buf, "0x%08x\n", common->tx_chainmask); ++ len = sprintf(buf, "0x%08x\n", ah->txchainmask); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); + } + +@@ -107,7 +107,7 @@ static ssize_t write_file_tx_chainmask(s + size_t count, loff_t *ppos) + { + struct ath_softc *sc = file->private_data; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; + unsigned long mask; + char buf[32]; + ssize_t len; +@@ -120,8 +120,8 @@ static ssize_t write_file_tx_chainmask(s + if (strict_strtoul(buf, 0, &mask)) + return -EINVAL; + +- common->tx_chainmask = mask; +- sc->sc_ah->caps.tx_chainmask = mask; ++ ah->txchainmask = mask; ++ ah->caps.tx_chainmask = mask; + return count; + } + +@@ -138,11 +138,11 @@ static ssize_t read_file_rx_chainmask(st + size_t count, loff_t *ppos) + { + struct ath_softc *sc = file->private_data; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + +- len = sprintf(buf, "0x%08x\n", common->rx_chainmask); ++ len = sprintf(buf, "0x%08x\n", ah->rxchainmask); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); + } + +@@ -150,7 +150,7 @@ static ssize_t write_file_rx_chainmask(s + size_t count, loff_t *ppos) + { + struct ath_softc *sc = file->private_data; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; + unsigned long mask; + char buf[32]; + ssize_t len; +@@ -163,8 +163,8 @@ static ssize_t write_file_rx_chainmask(s + if (strict_strtoul(buf, 0, &mask)) + return -EINVAL; + +- common->rx_chainmask = mask; +- sc->sc_ah->caps.rx_chainmask = mask; ++ ah->rxchainmask = mask; ++ ah->caps.rx_chainmask = mask; + return count; + } + +--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c +@@ -509,8 +509,8 @@ static void setup_ht_cap(struct ath9k_ht + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + /* ath9k_htc supports only 1 or 2 stream devices */ +- tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, 2); +- rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, 2); ++ tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2); ++ rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2); + + ath_dbg(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", +@@ -601,9 +601,6 @@ static void ath9k_init_misc(struct ath9k + { + struct ath_common *common = ath9k_hw_common(priv->ah); + +- common->tx_chainmask = priv->ah->caps.tx_chainmask; +- common->rx_chainmask = priv->ah->caps.rx_chainmask; +- + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + + priv->ah->opmode = NL80211_IFTYPE_STATION; +--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c +@@ -826,8 +826,7 @@ void ath9k_htc_ani_work(struct work_stru + if (longcal || shortcal) + common->ani.caldone = + ath9k_hw_calibrate(ah, ah->curchan, +- common->rx_chainmask, +- longcal); ++ ah->rxchainmask, longcal); + + ath9k_htc_ps_restore(priv); + } +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -270,8 +270,8 @@ static void setup_ht_cap(struct ath_soft + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); +- tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, max_streams); +- rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, max_streams); ++ tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); ++ rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); + + ath_dbg(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", +@@ -506,9 +506,6 @@ static void ath9k_init_misc(struct ath_s + sc->sc_flags |= SC_OP_RXAGGR; + } + +- common->tx_chainmask = sc->sc_ah->caps.tx_chainmask; +- common->rx_chainmask = sc->sc_ah->caps.rx_chainmask; +- + ath9k_hw_set_diversity(sc->sc_ah, true); + sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); + +@@ -645,10 +642,8 @@ static void ath9k_init_band_txpower(stru + static void ath9k_init_txpower_limits(struct ath_softc *sc) + { + struct ath_hw *ah = sc->sc_ah; +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath9k_channel *curchan = ah->curchan; + +- ah->txchainmask = common->tx_chainmask; + if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) + ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ); + if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) diff --git a/package/mac80211/patches/581-ath9k_merge_reset_functions.patch b/package/mac80211/patches/581-ath9k_merge_reset_functions.patch new file mode 100644 index 0000000000..201994db49 --- /dev/null +++ b/package/mac80211/patches/581-ath9k_merge_reset_functions.patch @@ -0,0 +1,343 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -212,83 +212,39 @@ static int ath_update_survey_stats(struc + return ret; + } + +-/* +- * 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 +- * DMA, then restart stuff. +-*/ +-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, +- struct ath9k_channel *hchan) ++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) + { + 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; ++ bool ret; + +- if (sc->sc_flags & SC_OP_INVALID) +- return -EIO; ++ ieee80211_stop_queues(sc->hw); + + sc->hw_busy_count = 0; +- +- 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); + cancel_delayed_work_sync(&sc->hw_pll_work); + +- ath9k_ps_wakeup(sc); +- +- spin_lock_bh(&sc->sc_pcu_lock); +- +- /* +- * This is only performed if the channel settings have +- * actually changed. +- * +- * To switch channels clear any pending DMA operations; +- * wait long enough for the RX fifo to drain, reset the +- * hardware at the new frequency, and then re-enable +- * the relevant bits of the h/w. +- */ + ath9k_hw_disable_interrupts(ah); +- stopped = ath_drain_all_txq(sc, false); + +- if (!ath_stoprecv(sc)) +- stopped = false; +- +- if (!ath9k_hw_check_alive(ah)) +- stopped = false; +- +- /* XXX: do not flush receive queue here. We don't want +- * to flush data frames already in queue because of +- * changing channel. */ ++ ret = ath_drain_all_txq(sc, retry_tx); + +- if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) +- fastcc = false; ++ if (!ath_stoprecv(sc)) ++ ret = false; + +- if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) +- caldata = &sc->caldata; ++ ath_flushrecv(sc); + +- ath_dbg(common, ATH_DBG_CONFIG, +- "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", +- sc->sc_ah->curchan->channel, +- channel->center_freq, conf_is_ht40(conf), +- fastcc); ++ return ret; ++} + +- r = ath9k_hw_reset(ah, hchan, caldata, fastcc); +- if (r) { +- ath_err(common, +- "Unable to reset channel (%u MHz), reset status %d\n", +- channel->center_freq, r); +- goto ps_restore; +- } ++static bool ath_complete_reset(struct ath_softc *sc) ++{ ++ struct ath_hw *ah = sc->sc_ah; ++ struct ath_common *common = ath9k_hw_common(ah); + + if (ath_startrecv(sc) != 0) { + ath_err(common, "Unable to restart recv logic\n"); +- r = -EIO; +- goto ps_restore; ++ return false; + } + + ath9k_cmn_update_txpow(ah, sc->curtxpow, +@@ -299,18 +255,84 @@ static int ath_set_channel(struct ath_so + if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { + if (sc->sc_flags & SC_OP_BEACONS) + ath_set_beacon(sc); ++ + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); + if (!common->disable_ani) + ath_start_ani(common); + } + +- ps_restore: +- ieee80211_wake_queues(hw); ++ ieee80211_wake_queues(sc->hw); ++ ++ return true; ++} ++ ++static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, ++ bool retry_tx) ++{ ++ struct ath_hw *ah = sc->sc_ah; ++ struct ath_common *common = ath9k_hw_common(ah); ++ struct ath9k_hw_cal_data *caldata = NULL; ++ bool fastcc = true; ++ int r; ++ ++ if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) { ++ fastcc = false; ++ caldata = &sc->caldata; ++ } + ++ if (!hchan) { ++ fastcc = false; ++ hchan = ah->curchan; ++ } ++ ++ if (fastcc && !ath9k_hw_check_alive(ah)) ++ fastcc = false; ++ ++ if (!ath_prepare_reset(sc, retry_tx)) ++ fastcc = false; ++ ++ ath_dbg(common, ATH_DBG_CONFIG, ++ "Reset to %u MHz, HT40: %d fastcc: %d\n", ++ hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS | ++ CHANNEL_HT40PLUS)), ++ fastcc); ++ ++ r = ath9k_hw_reset(ah, hchan, caldata, fastcc); ++ if (r) { ++ ath_err(common, ++ "Unable to reset channel, reset status %d\n", r); ++ return r; ++ } ++ ++ if (!ath_complete_reset(sc)) ++ return -EIO; ++ ++ return 0; ++} ++ ++ ++/* ++ * 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 ++ * DMA, then restart stuff. ++*/ ++static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, ++ struct ath9k_channel *hchan) ++{ ++ int r; ++ ++ if (sc->sc_flags & SC_OP_INVALID) ++ return -EIO; ++ ++ ath9k_ps_wakeup(sc); ++ ++ spin_lock_bh(&sc->sc_pcu_lock); ++ r = ath_reset_internal(sc, hchan, false); + spin_unlock_bh(&sc->sc_pcu_lock); + + ath9k_ps_restore(sc); ++ + return r; + } + +@@ -893,28 +915,13 @@ static void ath_radio_enable(struct ath_ + channel->center_freq, r); + } + +- ath9k_cmn_update_txpow(ah, sc->curtxpow, +- sc->config.txpowlimit, &sc->curtxpow); +- if (ath_startrecv(sc) != 0) { +- ath_err(common, "Unable to restart recv logic\n"); +- goto out; +- } +- if (sc->sc_flags & SC_OP_BEACONS) +- ath_set_beacon(sc); /* restart beacons */ +- +- /* Re-Enable interrupts */ +- ath9k_hw_set_interrupts(ah, ah->imask); +- ath9k_hw_enable_interrupts(ah); ++ ath_complete_reset(sc); + + /* Enable LED */ + ath9k_hw_cfg_output(ah, ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(ah, ah->led_pin, 0); + +- ieee80211_wake_queues(hw); +- ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2); +- +-out: + spin_unlock_bh(&sc->sc_pcu_lock); + + ath9k_ps_restore(sc); +@@ -942,13 +949,7 @@ void ath_radio_disable(struct ath_softc + ath9k_hw_cfg_gpio_input(ah, ah->led_pin); + } + +- /* Disable interrupts */ +- ath9k_hw_disable_interrupts(ah); +- +- ath_drain_all_txq(sc, false); /* clear pending tx frames */ +- +- ath_stoprecv(sc); /* turn off frame recv */ +- ath_flushrecv(sc); /* flush recv queue */ ++ ath_prepare_reset(sc, false); + + if (!ah->curchan) + ah->curchan = ath9k_cmn_get_curchannel(hw, ah); +@@ -970,48 +971,11 @@ void ath_radio_disable(struct ath_softc + + int ath_reset(struct ath_softc *sc, bool retry_tx) + { +- struct ath_hw *ah = sc->sc_ah; +- struct ath_common *common = ath9k_hw_common(ah); +- struct ieee80211_hw *hw = sc->hw; + int r; + +- sc->hw_busy_count = 0; +- +- /* Stop ANI */ +- +- del_timer_sync(&common->ani.timer); +- + ath9k_ps_wakeup(sc); + +- ieee80211_stop_queues(hw); +- +- ath9k_hw_disable_interrupts(ah); +- ath_drain_all_txq(sc, retry_tx); +- +- ath_stoprecv(sc); +- ath_flushrecv(sc); +- +- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); +- if (r) +- ath_err(common, +- "Unable to reset hardware; reset status %d\n", r); +- +- if (ath_startrecv(sc) != 0) +- ath_err(common, "Unable to start recv logic\n"); +- +- /* +- * We may be doing a reset in response to a request +- * that changes the channel so update any state that +- * might change as a result. +- */ +- ath9k_cmn_update_txpow(ah, sc->curtxpow, +- sc->config.txpowlimit, &sc->curtxpow); +- +- if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL))) +- ath_set_beacon(sc); /* restart beacons */ +- +- ath9k_hw_set_interrupts(ah, ah->imask); +- ath9k_hw_enable_interrupts(ah); ++ r = ath_reset_internal(sc, NULL, retry_tx); + + if (retry_tx) { + int i; +@@ -1024,12 +988,6 @@ int ath_reset(struct ath_softc *sc, bool + } + } + +- ieee80211_wake_queues(hw); +- +- /* Start ANI */ +- if (!common->disable_ani) +- ath_start_ani(common); +- + ath9k_ps_restore(sc); + + return r; +@@ -1081,28 +1039,6 @@ static int ath9k_start(struct ieee80211_ + goto mutex_unlock; + } + +- /* +- * This is needed only to setup initial state +- * but it's best done after a reset. +- */ +- ath9k_cmn_update_txpow(ah, sc->curtxpow, +- sc->config.txpowlimit, &sc->curtxpow); +- +- /* +- * Setup the hardware after reset: +- * The receive engine is set going. +- * Frame transmit is handled entirely +- * in the frame output path; there's nothing to do +- * here except setup the interrupt mask. +- */ +- if (ath_startrecv(sc) != 0) { +- ath_err(common, "Unable to start recv logic\n"); +- r = -EIO; +- spin_unlock_bh(&sc->sc_pcu_lock); +- goto mutex_unlock; +- } +- spin_unlock_bh(&sc->sc_pcu_lock); +- + /* Setup our intr mask. */ + ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL | + ATH9K_INT_RXORN | ATH9K_INT_FATAL | +@@ -1125,12 +1061,14 @@ static int ath9k_start(struct ieee80211_ + + /* Disable BMISS interrupt when we're not associated */ + ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); +- ath9k_hw_set_interrupts(ah, ah->imask); +- ath9k_hw_enable_interrupts(ah); + +- ieee80211_wake_queues(hw); ++ if (!ath_complete_reset(sc)) { ++ r = -EIO; ++ spin_unlock_bh(&sc->sc_pcu_lock); ++ goto mutex_unlock; ++ } + +- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ++ spin_unlock_bh(&sc->sc_pcu_lock); + + if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && + !ah->btcoex_hw.enabled) { diff --git a/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch b/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch new file mode 100644 index 0000000000..92c8ea4b8b --- /dev/null +++ b/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch @@ -0,0 +1,50 @@ +--- a/drivers/net/wireless/ath/ath9k/hw-ops.h ++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h +@@ -121,13 +121,15 @@ static inline void ath9k_hw_set_clrdmask + static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf) + { +- ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf); ++ if (ath9k_hw_ops(ah)->antdiv_comb_conf_get) ++ ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf); + } + + static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, + struct ath_hw_antcomb_conf *antconf) + { +- ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); ++ if (ath9k_hw_ops(ah)->antdiv_comb_conf_set) ++ ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); + } + + /* Private hardware call ops */ +--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c +@@ -570,8 +570,10 @@ void ar9002_hw_attach_phy_ops(struct ath + priv_ops->compute_pll_control = ar9002_hw_compute_pll_control; + priv_ops->do_getnf = ar9002_hw_do_getnf; + +- ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; +- ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; ++ if (AR_SREV_9285(ah)) { ++ ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; ++ ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; ++ } + + ar9002_hw_set_nf_limits(ah); + } +--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c +@@ -1283,8 +1283,10 @@ void ar9003_hw_attach_phy_ops(struct ath + priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs; + priv_ops->set_radar_params = ar9003_hw_set_radar_params; + +- ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; +- ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ++ if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { ++ ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ++ ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ++ } + + ar9003_hw_set_nf_limits(ah); + ar9003_hw_set_radar_conf(ah); diff --git a/package/mac80211/patches/583-ath9k_antenna_control.patch b/package/mac80211/patches/583-ath9k_antenna_control.patch new file mode 100644 index 0000000000..a1caa8aaa9 --- /dev/null +++ b/package/mac80211/patches/583-ath9k_antenna_control.patch @@ -0,0 +1,179 @@ +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -652,9 +652,22 @@ static void ath9k_init_txpower_limits(st + ah->curchan = curchan; + } + ++void ath9k_reload_chainmask_settings(struct ath_softc *sc) ++{ ++ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)) ++ return; ++ ++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) ++ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); ++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) ++ setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); ++} ++ ++ + void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) + { +- struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ath_hw *ah = sc->sc_ah; ++ struct ath_common *common = ath9k_hw_common(ah); + + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | +@@ -692,6 +705,17 @@ void ath9k_set_hw_capab(struct ath_softc + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + ++ hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; ++ hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; ++ ++ /* single chain devices with rx diversity */ ++ if (ah->caps.max_rxchains == 1 && ++ ath9k_hw_ops(ah)->antdiv_comb_conf_get) ++ hw->wiphy->available_antennas_rx = 3; ++ ++ sc->ant_rx = hw->wiphy->available_antennas_rx; ++ sc->ant_tx = hw->wiphy->available_antennas_tx; ++ + #ifdef CONFIG_ATH9K_RATE_CONTROL + hw->rate_control_algorithm = "ath9k_rate_control"; + #endif +@@ -703,12 +727,7 @@ void ath9k_set_hw_capab(struct ath_softc + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; + +- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { +- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) +- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); +- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) +- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); +- } ++ ath9k_reload_chainmask_settings(sc); + + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + } +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -652,6 +652,7 @@ struct ath_softc { + struct ath_descdma txsdma; + + struct ath_ant_comb ant_comb; ++ u32 ant_tx, ant_rx; + }; + + void ath9k_tasklet(unsigned long data); +@@ -673,6 +674,7 @@ int ath9k_init_device(u16 devid, struct + const struct ath_bus_ops *bus_ops); + void ath9k_deinit_device(struct ath_softc *sc); + void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); ++void ath9k_reload_chainmask_settings(struct ath_softc *sc); + + void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); + bool ath9k_uses_beacons(int type); +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -262,6 +262,22 @@ static bool ath_complete_reset(struct at + ath_start_ani(common); + } + ++ if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) { ++ struct ath_hw_antcomb_conf div_ant_conf; ++ u8 lna_conf; ++ ++ ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); ++ ++ if (sc->ant_rx == 1) ++ lna_conf = ATH_ANT_DIV_COMB_LNA1; ++ else ++ lna_conf = ATH_ANT_DIV_COMB_LNA2; ++ div_ant_conf.main_lna_conf = lna_conf; ++ div_ant_conf.alt_lna_conf = lna_conf; ++ ++ ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); ++ } ++ + ieee80211_wake_queues(sc->hw); + + return true; +@@ -2360,6 +2376,59 @@ static int ath9k_get_stats(struct ieee80 + return 0; + } + ++static u32 fill_chainmask(u32 cap, u32 new) ++{ ++ u32 filled = 0; ++ int i; ++ ++ for (i = 0; cap && new; i++, cap >>= 1) { ++ if (!(cap & BIT(0))) ++ continue; ++ ++ if (new & BIT(0)) ++ filled |= BIT(i); ++ ++ new >>= 1; ++ } ++ ++ return filled; ++} ++ ++static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) ++{ ++ struct ath_softc *sc = hw->priv; ++ struct ath_hw *ah = sc->sc_ah; ++ ++ if (!rx_ant || !tx_ant) ++ return -EINVAL; ++ ++ sc->ant_rx = rx_ant; ++ sc->ant_tx = tx_ant; ++ ++ if (ah->caps.rx_chainmask == 1) ++ return 0; ++ ++ /* AR9100 runs into calibration issues if not all rx chains are enabled */ ++ if (AR_SREV_9100(ah)) ++ ah->rxchainmask = 0x7; ++ else ++ ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); ++ ++ ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); ++ ath9k_reload_chainmask_settings(sc); ++ ++ return 0; ++} ++ ++static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) ++{ ++ struct ath_softc *sc = hw->priv; ++ ++ *tx_ant = sc->ant_tx; ++ *rx_ant = sc->ant_rx; ++ return 0; ++} ++ + struct ieee80211_ops ath9k_ops = { + .tx = ath9k_tx, + .start = ath9k_start, +@@ -2386,4 +2455,6 @@ struct ieee80211_ops ath9k_ops = { + .tx_frames_pending = ath9k_tx_frames_pending, + .tx_last_beacon = ath9k_tx_last_beacon, + .get_stats = ath9k_get_stats, ++ .set_antenna = ath9k_set_antenna, ++ .get_antenna = ath9k_get_antenna, + }; +--- a/drivers/net/wireless/ath/ath9k/recv.c ++++ b/drivers/net/wireless/ath/ath9k/recv.c +@@ -1956,7 +1956,7 @@ int ath_rx_tasklet(struct ath_softc *sc, + ath_rx_ps(sc, skb); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + +- if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) ++ if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) + ath_ant_comb_scan(sc, &rs); + + ieee80211_rx(hw, skb); |