Search Linux Wireless

Re: [PATCH v2 4/4] ath9k: implement .get_antenna and .set_antenna

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Aug 29, 2011 at 07:54:22AM +0200, Felix Fietkau wrote:
> 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@xxxxxxxxxxx>
> ---
>  drivers/net/wireless/ath/ath9k/ath9k.h |    2 +
>  drivers/net/wireless/ath/ath9k/init.c  |   33 ++++++++++++---
>  drivers/net/wireless/ath/ath9k/main.c  |   71 ++++++++++++++++++++++++++++++++
>  drivers/net/wireless/ath/ath9k/recv.c  |    2 +-
>  4 files changed, 100 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 5d9a9aa..6c7b27c 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -647,6 +647,7 @@ struct ath_softc {
>  	struct ath_descdma txsdma;
>  
>  	struct ath_ant_comb ant_comb;
> +	u32 ant_tx, ant_rx;
u8 is sufficient.
>  };
>  
>  void ath9k_tasklet(unsigned long data);
> @@ -668,6 +669,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
>  		    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);
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index 31ef501..263bbe2 100644
> --- 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(struct ath_softc *sc)
>  	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 *sc, struct ieee80211_hw *hw)
>  	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;
> +
Why dont you use caps.tx/rx_chainmask instead?
> +	/* 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;
> +
Use hw caps to determine ant_divsersity.
use hex instead of decimal for available_antennas, ant_rx/tx
> +	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 *sc, struct ieee80211_hw *hw)
>  		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);
>  }
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index 4b1fe7c..1cbbaea 100644
> --- 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 ath_softc *sc, bool start)
>  			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);
Adjust the fast_div_bias based on main and alt lna conf.
> +	}
> +
>  	ieee80211_wake_queues(sc->hw);
>  
>  	return true;
> @@ -2358,6 +2374,59 @@ static int ath9k_get_stats(struct ieee80211_hw *hw,
>  	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);
Setting chainmask alone is not sufficient. dont you have to complete rx and
calibration before setting antenna?
> +
> +	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,
> @@ -2384,4 +2453,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,
>  };
> diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
> index ad5f9bd..7e1265c 100644
> --- 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, int flush, bool hp)
>  			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);
What about already received packets using combined antennas?
>  
>  		ieee80211_rx(hw, skb);

--
Rajkumar
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux