Commit 58296567 authored by Christian Lamparter's avatar Christian Lamparter Committed by John W. Linville

carl9170: set beacon xmit power to the max

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=131067225105801Reported-by: default avatarHarshal Chhaya <harshal@gmail.com>
Signed-off-by: default avatarChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 943c3399
...@@ -661,11 +661,67 @@ void carl9170_tx_process_status(struct ar9170 *ar, ...@@ -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, static __le32 carl9170_tx_physet(struct ar9170 *ar,
struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate) struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate)
{ {
struct ieee80211_rate *rate = NULL; unsigned int power = 0, chains = 0, phyrate = 0;
u32 power, chains;
__le32 tmp; __le32 tmp;
tmp = cpu_to_le32(0); tmp = cpu_to_le32(0);
...@@ -682,35 +738,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, ...@@ -682,35 +738,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
if (txrate->flags & IEEE80211_TX_RC_MCS) { if (txrate->flags & IEEE80211_TX_RC_MCS) {
u32 r = txrate->idx; SET_VAL(AR9170_TX_PHY_MCS, phyrate, txrate->idx);
u8 *txpower;
/* heavy clip control */ /* heavy clip control */
tmp |= cpu_to_le32((r & 0x7) << tmp |= cpu_to_le32((txrate->idx & 0x7) <<
AR9170_TX_PHY_TX_HEAVY_CLIP_S); 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); tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
/* /*
...@@ -720,34 +753,15 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, ...@@ -720,34 +753,15 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
* tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
*/ */
} else { } else {
u8 *txpower; if (info->band == IEEE80211_BAND_2GHZ) {
u32 mod; if (txrate->idx <= AR9170_TX_PHY_RATE_CCK_11M)
u32 phyrate; tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_CCK);
u8 idx = txrate->idx; else
tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
if (info->band != IEEE80211_BAND_2GHZ) {
idx += 4;
txpower = ar->power_5G_leg;
mod = AR9170_TX_PHY_MOD_OFDM;
} else { } else {
if (idx < 4) { tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM);
txpower = ar->power_2G_cck;
mod = AR9170_TX_PHY_MOD_CCK;
} else {
mod = AR9170_TX_PHY_MOD_OFDM;
txpower = ar->power_2G_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. * short preamble seems to be broken too.
* *
...@@ -755,23 +769,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, ...@@ -755,23 +769,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar,
* tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); * tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
*/ */
} }
power <<= AR9170_TX_PHY_TX_PWR_S; carl9170_tx_rate_tpc_chains(ar, info, txrate,
power &= AR9170_TX_PHY_TX_PWR; &phyrate, &power, &chains);
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);
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; return tmp;
} }
...@@ -1444,8 +1447,10 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit) ...@@ -1444,8 +1447,10 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
struct carl9170_vif_info *cvif; struct carl9170_vif_info *cvif;
struct ieee80211_tx_info *txinfo; struct ieee80211_tx_info *txinfo;
struct ieee80211_tx_rate *rate;
__le32 *data, *old = NULL; __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; int i = 0, err = 0;
rcu_read_lock(); rcu_read_lock();
...@@ -1476,11 +1481,6 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit) ...@@ -1476,11 +1481,6 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
} }
txinfo = IEEE80211_SKB_CB(skb); 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); spin_lock_bh(&ar->beacon_lock);
data = (__le32 *)skb->data; data = (__le32 *)skb->data;
if (cvif->beacon) if (cvif->beacon)
...@@ -1510,18 +1510,43 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit) ...@@ -1510,18 +1510,43 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
goto err_unlock; goto err_unlock;
} }
i = txinfo->control.rates[0].idx; ht1 = AR9170_MAC_BCN_HT1_TX_ANT0;
if (txinfo->band != IEEE80211_BAND_2GHZ) rate = &txinfo->control.rates[0];
i += 4; 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 (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
if (i < 4) ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED;
word |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; plcp |= AR9170_MAC_BCN_HT2_BW40;
else }
word |= ((skb->len + FCS_LEN) << 16) + 0x0010; 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_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++) { for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment