carl9170: set beacon xmit power to the max
authorChristian Lamparter <chunkeey@googlemail.com>
Sat, 16 Jul 2011 15:30:19 +0000 (17:30 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 18 Jul 2011 18:29:44 +0000 (14:29 -0400)
Harshal Chhaya discovered during network tests, that
several of his clients dropped-off the network. The
captured packets shows that the beacons sent by the
AP are at a much lower power than the other data
packets.

The reason for this mishap: The driver never updated
the beacon phy register, so all beacons were always
sent at the lowest power.

Reference: http://marc.info/?l=linux-wireless&m=131067225105801
Reported-by: Harshal Chhaya <harshal@gmail.com>
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/carl9170/tx.c

index 0f8cdeffd64586f7d9a05214b6d1ffc1a178700e..d20946939cd8cae0968abfb096dbe3321c0e63eb 100644 (file)
@@ -661,11 +661,67 @@ void carl9170_tx_process_status(struct ar9170 *ar,
        }
 }
 
+static void carl9170_tx_rate_tpc_chains(struct ar9170 *ar,
+       struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate,
+       unsigned int *phyrate, unsigned int *tpc, unsigned int *chains)
+{
+       struct ieee80211_rate *rate = NULL;
+       u8 *txpower;
+       unsigned int idx;
+
+       idx = txrate->idx;
+       *tpc = 0;
+       *phyrate = 0;
+
+       if (txrate->flags & IEEE80211_TX_RC_MCS) {
+               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                       /* +1 dBm for HT40 */
+                       *tpc += 2;
+
+                       if (info->band == IEEE80211_BAND_2GHZ)
+                               txpower = ar->power_2G_ht40;
+                       else
+                               txpower = ar->power_5G_ht40;
+               } else {
+                       if (info->band == IEEE80211_BAND_2GHZ)
+                               txpower = ar->power_2G_ht20;
+                       else
+                               txpower = ar->power_5G_ht20;
+               }
+
+               *phyrate = txrate->idx;
+               *tpc += txpower[idx & 7];
+       } else {
+               if (info->band == IEEE80211_BAND_2GHZ) {
+                       if (idx < 4)
+                               txpower = ar->power_2G_cck;
+                       else
+                               txpower = ar->power_2G_ofdm;
+               } else {
+                       txpower = ar->power_5G_leg;
+                       idx += 4;
+               }
+
+               rate = &__carl9170_ratetable[idx];
+               *tpc += txpower[(rate->hw_value & 0x30) >> 4];
+               *phyrate = rate->hw_value & 0xf;
+       }
+
+       if (ar->eeprom.tx_mask == 1) {
+               *chains = AR9170_TX_PHY_TXCHAIN_1;
+       } else {
+               if (!(txrate->flags & IEEE80211_TX_RC_MCS) &&
+                   rate && rate->bitrate >= 360)
+                       *chains = AR9170_TX_PHY_TXCHAIN_1;
+               else
+                       *chains = AR9170_TX_PHY_TXCHAIN_2;
+       }
+}
+
 static __le32 carl9170_tx_physet(struct ar9170 *ar,
        struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate)
 {
-       struct ieee80211_rate *rate = NULL;
-       u32 power, chains;
+       unsigned int power = 0, chains = 0, phyrate = 0;
        __le32 tmp;
 
        tmp = cpu_to_le32(0);
@@ -682,35 +738,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
                tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
 
        if (txrate->flags & IEEE80211_TX_RC_MCS) {
-               u32 r = txrate->idx;
-               u8 *txpower;
+               SET_VAL(AR9170_TX_PHY_MCS, phyrate, txrate->idx);
 
                /* heavy clip control */
-               tmp |= cpu_to_le32((r & 0x7) <<
+               tmp |= cpu_to_le32((txrate->idx & 0x7) <<
                        AR9170_TX_PHY_TX_HEAVY_CLIP_S);
 
-               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
-                       if (info->band == IEEE80211_BAND_5GHZ)
-                               txpower = ar->power_5G_ht40;
-                       else
-                               txpower = ar->power_2G_ht40;
-               } else {
-                       if (info->band == IEEE80211_BAND_5GHZ)
-                               txpower = ar->power_5G_ht20;
-                       else
-                               txpower = ar->power_2G_ht20;
-               }
-
-               power = txpower[r & 7];
-
-               /* +1 dBm for HT40 */
-               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
-                       power += 2;
-
-               r <<= AR9170_TX_PHY_MCS_S;
-               BUG_ON(r & ~AR9170_TX_PHY_MCS);
-
-               tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS);
                tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
 
                /*
@@ -720,34 +753,15 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
                 * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
                 */
        } else {
-               u8 *txpower;
-               u32 mod;
-               u32 phyrate;
-               u8 idx = txrate->idx;
-
-               if (info->band != IEEE80211_BAND_2GHZ) {
-                       idx += 4;
-                       txpower = ar->power_5G_leg;
-                       mod = AR9170_TX_PHY_MOD_OFDM;
+               if (info->band == IEEE80211_BAND_2GHZ) {
+                       if (txrate->idx <= AR9170_TX_PHY_RATE_CCK_11M)
+                               tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_CCK);
+                       else
+                               tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
                } else {
-                       if (idx < 4) {
-                               txpower = ar->power_2G_cck;
-                               mod = AR9170_TX_PHY_MOD_CCK;
-                       } else {
-                               mod = AR9170_TX_PHY_MOD_OFDM;
-                               txpower = ar->power_2G_ofdm;
-                       }
+                       tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
                }
 
-               rate = &__carl9170_ratetable[idx];
-
-               phyrate = rate->hw_value & 0xF;
-               power = txpower[(rate->hw_value & 0x30) >> 4];
-               phyrate <<= AR9170_TX_PHY_MCS_S;
-
-               tmp |= cpu_to_le32(mod);
-               tmp |= cpu_to_le32(phyrate);
-
                /*
                 * short preamble seems to be broken too.
                 *
@@ -755,23 +769,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
                 *      tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
                 */
        }
-       power <<= AR9170_TX_PHY_TX_PWR_S;
-       power &= AR9170_TX_PHY_TX_PWR;
-       tmp |= cpu_to_le32(power);
-
-       /* set TX chains */
-       if (ar->eeprom.tx_mask == 1) {
-               chains = AR9170_TX_PHY_TXCHAIN_1;
-       } else {
-               chains = AR9170_TX_PHY_TXCHAIN_2;
-
-               /* >= 36M legacy OFDM - use only one chain */
-               if (rate && rate->bitrate >= 360 &&
-                   !(txrate->flags & IEEE80211_TX_RC_MCS))
-                       chains = AR9170_TX_PHY_TXCHAIN_1;
-       }
-       tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S);
+       carl9170_tx_rate_tpc_chains(ar, info, txrate,
+                                   &phyrate, &power, &chains);
 
+       tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_MCS, phyrate));
+       tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TX_PWR, power));
+       tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TXCHAIN, chains));
        return tmp;
 }
 
@@ -1444,8 +1447,10 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
        struct sk_buff *skb = NULL;
        struct carl9170_vif_info *cvif;
        struct ieee80211_tx_info *txinfo;
+       struct ieee80211_tx_rate *rate;
        __le32 *data, *old = NULL;
-       u32 word, off, addr, len;
+       unsigned int plcp, power, chains;
+       u32 word, ht1, off, addr, len;
        int i = 0, err = 0;
 
        rcu_read_lock();
@@ -1476,11 +1481,6 @@ found:
        }
 
        txinfo = IEEE80211_SKB_CB(skb);
-       if (txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS) {
-               err = -EINVAL;
-               goto err_free;
-       }
-
        spin_lock_bh(&ar->beacon_lock);
        data = (__le32 *)skb->data;
        if (cvif->beacon)
@@ -1510,18 +1510,43 @@ found:
                goto err_unlock;
        }
 
-       i = txinfo->control.rates[0].idx;
-       if (txinfo->band != IEEE80211_BAND_2GHZ)
-               i += 4;
+       ht1 = AR9170_MAC_BCN_HT1_TX_ANT0;
+       rate = &txinfo->control.rates[0];
+       carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains);
+       if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+               if (plcp <= AR9170_TX_PHY_RATE_CCK_11M)
+                       plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
+               else
+                       plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010;
+       } else {
+               ht1 |= AR9170_MAC_BCN_HT1_HT_EN;
+               if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+                       plcp |= AR9170_MAC_BCN_HT2_SGI;
 
-       word = __carl9170_ratetable[i].hw_value & 0xf;
-       if (i < 4)
-               word |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
-       else
-               word |= ((skb->len + FCS_LEN) << 16) + 0x0010;
+               if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                       ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED;
+                       plcp |= AR9170_MAC_BCN_HT2_BW40;
+               }
+               if (rate->flags & IEEE80211_TX_RC_DUP_DATA) {
+                       ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP;
+                       plcp |= AR9170_MAC_BCN_HT2_BW40;
+               }
+
+               SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN);
+       }
+
+       SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7);
+       SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power);
+       SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains);
+       if (chains == AR9170_TX_PHY_TXCHAIN_2)
+               ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1;
 
        carl9170_async_regwrite_begin(ar);
-       carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, word);
+       carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1);
+       if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS))
+               carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp);
+       else
+               carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp);
 
        for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
                /*