diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-09-02 19:40:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-09-14 13:56:22 -0400 |
commit | 43c3528430bd29f5e52438cad7cf7c0c62bf4583 (patch) | |
tree | b84845c758b2a4dba66403f6e6a6801d5e2574af | |
parent | 9adcf440dcc886a950a6049f928ab679912d99f4 (diff) |
ath9k: implement .get_antenna and .set_antenna
On MIMO chips this can be used to enable/disable hardware chains, ensuring
that the MCS information is updated accordingly.
On non-MIMO chips with rx diversity (e.g. 9285), this configures the rx
input antenna.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/init.c | 32 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 71 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/recv.c | 2 |
4 files changed, 99 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b2992d4097c3..0fb4a26f8979 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | |||
@@ -649,6 +649,7 @@ struct ath_softc { | |||
649 | struct ath_descdma txsdma; | 649 | struct ath_descdma txsdma; |
650 | 650 | ||
651 | struct ath_ant_comb ant_comb; | 651 | struct ath_ant_comb ant_comb; |
652 | u8 ant_tx, ant_rx; | ||
652 | }; | 653 | }; |
653 | 654 | ||
654 | void ath9k_tasklet(unsigned long data); | 655 | void ath9k_tasklet(unsigned long data); |
@@ -669,6 +670,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, | |||
669 | const struct ath_bus_ops *bus_ops); | 670 | const struct ath_bus_ops *bus_ops); |
670 | void ath9k_deinit_device(struct ath_softc *sc); | 671 | void ath9k_deinit_device(struct ath_softc *sc); |
671 | void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); | 672 | void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); |
673 | void ath9k_reload_chainmask_settings(struct ath_softc *sc); | ||
672 | 674 | ||
673 | void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); | 675 | void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); |
674 | bool ath9k_uses_beacons(int type); | 676 | bool ath9k_uses_beacons(int type); |
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index be302fbdc3dc..9b34c4bab937 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c | |||
@@ -653,9 +653,22 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc) | |||
653 | ah->curchan = curchan; | 653 | ah->curchan = curchan; |
654 | } | 654 | } |
655 | 655 | ||
656 | void ath9k_reload_chainmask_settings(struct ath_softc *sc) | ||
657 | { | ||
658 | if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)) | ||
659 | return; | ||
660 | |||
661 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) | ||
662 | setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); | ||
663 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) | ||
664 | setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); | ||
665 | } | ||
666 | |||
667 | |||
656 | void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) | 668 | void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) |
657 | { | 669 | { |
658 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 670 | struct ath_hw *ah = sc->sc_ah; |
671 | struct ath_common *common = ath9k_hw_common(ah); | ||
659 | 672 | ||
660 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | | 673 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | |
661 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | | 674 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | |
@@ -693,6 +706,16 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
693 | hw->sta_data_size = sizeof(struct ath_node); | 706 | hw->sta_data_size = sizeof(struct ath_node); |
694 | hw->vif_data_size = sizeof(struct ath_vif); | 707 | hw->vif_data_size = sizeof(struct ath_vif); |
695 | 708 | ||
709 | hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; | ||
710 | hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; | ||
711 | |||
712 | /* single chain devices with rx diversity */ | ||
713 | if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) | ||
714 | hw->wiphy->available_antennas_rx = BIT(0) | BIT(1); | ||
715 | |||
716 | sc->ant_rx = hw->wiphy->available_antennas_rx; | ||
717 | sc->ant_tx = hw->wiphy->available_antennas_tx; | ||
718 | |||
696 | #ifdef CONFIG_ATH9K_RATE_CONTROL | 719 | #ifdef CONFIG_ATH9K_RATE_CONTROL |
697 | hw->rate_control_algorithm = "ath9k_rate_control"; | 720 | hw->rate_control_algorithm = "ath9k_rate_control"; |
698 | #endif | 721 | #endif |
@@ -704,12 +727,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
704 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | 727 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = |
705 | &sc->sbands[IEEE80211_BAND_5GHZ]; | 728 | &sc->sbands[IEEE80211_BAND_5GHZ]; |
706 | 729 | ||
707 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { | 730 | ath9k_reload_chainmask_settings(sc); |
708 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) | ||
709 | setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); | ||
710 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) | ||
711 | setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); | ||
712 | } | ||
713 | 731 | ||
714 | SET_IEEE80211_PERM_ADDR(hw, common->macaddr); | 732 | SET_IEEE80211_PERM_ADDR(hw, common->macaddr); |
715 | } | 733 | } |
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 19c78be3dc41..c8ac2ce61ffe 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -281,6 +281,22 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) | |||
281 | ath_start_ani(common); | 281 | ath_start_ani(common); |
282 | } | 282 | } |
283 | 283 | ||
284 | if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) { | ||
285 | struct ath_hw_antcomb_conf div_ant_conf; | ||
286 | u8 lna_conf; | ||
287 | |||
288 | ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); | ||
289 | |||
290 | if (sc->ant_rx == 1) | ||
291 | lna_conf = ATH_ANT_DIV_COMB_LNA1; | ||
292 | else | ||
293 | lna_conf = ATH_ANT_DIV_COMB_LNA2; | ||
294 | div_ant_conf.main_lna_conf = lna_conf; | ||
295 | div_ant_conf.alt_lna_conf = lna_conf; | ||
296 | |||
297 | ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); | ||
298 | } | ||
299 | |||
284 | ieee80211_wake_queues(sc->hw); | 300 | ieee80211_wake_queues(sc->hw); |
285 | 301 | ||
286 | return true; | 302 | return true; |
@@ -2379,6 +2395,59 @@ static int ath9k_get_stats(struct ieee80211_hw *hw, | |||
2379 | return 0; | 2395 | return 0; |
2380 | } | 2396 | } |
2381 | 2397 | ||
2398 | static u32 fill_chainmask(u32 cap, u32 new) | ||
2399 | { | ||
2400 | u32 filled = 0; | ||
2401 | int i; | ||
2402 | |||
2403 | for (i = 0; cap && new; i++, cap >>= 1) { | ||
2404 | if (!(cap & BIT(0))) | ||
2405 | continue; | ||
2406 | |||
2407 | if (new & BIT(0)) | ||
2408 | filled |= BIT(i); | ||
2409 | |||
2410 | new >>= 1; | ||
2411 | } | ||
2412 | |||
2413 | return filled; | ||
2414 | } | ||
2415 | |||
2416 | static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) | ||
2417 | { | ||
2418 | struct ath_softc *sc = hw->priv; | ||
2419 | struct ath_hw *ah = sc->sc_ah; | ||
2420 | |||
2421 | if (!rx_ant || !tx_ant) | ||
2422 | return -EINVAL; | ||
2423 | |||
2424 | sc->ant_rx = rx_ant; | ||
2425 | sc->ant_tx = tx_ant; | ||
2426 | |||
2427 | if (ah->caps.rx_chainmask == 1) | ||
2428 | return 0; | ||
2429 | |||
2430 | /* AR9100 runs into calibration issues if not all rx chains are enabled */ | ||
2431 | if (AR_SREV_9100(ah)) | ||
2432 | ah->rxchainmask = 0x7; | ||
2433 | else | ||
2434 | ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); | ||
2435 | |||
2436 | ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); | ||
2437 | ath9k_reload_chainmask_settings(sc); | ||
2438 | |||
2439 | return 0; | ||
2440 | } | ||
2441 | |||
2442 | static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) | ||
2443 | { | ||
2444 | struct ath_softc *sc = hw->priv; | ||
2445 | |||
2446 | *tx_ant = sc->ant_tx; | ||
2447 | *rx_ant = sc->ant_rx; | ||
2448 | return 0; | ||
2449 | } | ||
2450 | |||
2382 | struct ieee80211_ops ath9k_ops = { | 2451 | struct ieee80211_ops ath9k_ops = { |
2383 | .tx = ath9k_tx, | 2452 | .tx = ath9k_tx, |
2384 | .start = ath9k_start, | 2453 | .start = ath9k_start, |
@@ -2405,4 +2474,6 @@ struct ieee80211_ops ath9k_ops = { | |||
2405 | .tx_frames_pending = ath9k_tx_frames_pending, | 2474 | .tx_frames_pending = ath9k_tx_frames_pending, |
2406 | .tx_last_beacon = ath9k_tx_last_beacon, | 2475 | .tx_last_beacon = ath9k_tx_last_beacon, |
2407 | .get_stats = ath9k_get_stats, | 2476 | .get_stats = ath9k_get_stats, |
2477 | .set_antenna = ath9k_set_antenna, | ||
2478 | .get_antenna = ath9k_get_antenna, | ||
2408 | }; | 2479 | }; |
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 9c7f905f3871..8d3e19dfe7db 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c | |||
@@ -1950,7 +1950,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) | |||
1950 | ath_rx_ps(sc, skb); | 1950 | ath_rx_ps(sc, skb); |
1951 | spin_unlock_irqrestore(&sc->sc_pm_lock, flags); | 1951 | spin_unlock_irqrestore(&sc->sc_pm_lock, flags); |
1952 | 1952 | ||
1953 | if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) | 1953 | if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) |
1954 | ath_ant_comb_scan(sc, &rs); | 1954 | ath_ant_comb_scan(sc, &rs); |
1955 | 1955 | ||
1956 | ieee80211_rx(hw, skb); | 1956 | ieee80211_rx(hw, skb); |