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=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>
This commit is contained in:
Christian Lamparter 2011-07-16 17:30:19 +02:00 committed by John W. Linville
parent 943c33996f
commit 5829656728

View 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 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
}
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 @@ int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
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++) {
/*