diff options
Diffstat (limited to 'package/mac80211/patches/582-ath9k_antenna_control.patch')
-rw-r--r-- | package/mac80211/patches/582-ath9k_antenna_control.patch | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/package/mac80211/patches/582-ath9k_antenna_control.patch b/package/mac80211/patches/582-ath9k_antenna_control.patch new file mode 100644 index 0000000..ecd8680 --- /dev/null +++ b/package/mac80211/patches/582-ath9k_antenna_control.patch @@ -0,0 +1,178 @@ +--- 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,16 @@ 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.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) ++ hw->wiphy->available_antennas_rx = BIT(0) | BIT(1); ++ ++ 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 +726,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; ++ u8 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); |