Commit e04e4b6e authored by Jonas Jelonek's avatar Jonas Jelonek Committed by Kalle Valo

wifi: ath9k: fix per-packet TX-power cap for TPC

Fix incorrect usage of plain rate_idx as index into the max (power) per
rate lookup table.

For transmit power control (TPC), the ath9k driver maintains internal
tables (in struct ath_hw) to store the max allowed power level per rate.
They are used to limit a given TX-power according to regulatory and user
limits in the TX-path per packet. The tables are filled in a predefined
order, starting with values for CCK + OFDM rates and followed by the
values for MCS rates. Thus, the maximum power levels for MCS do not
start at index 0 in the table but are shifted by a fixed value.

The TX-power limiting in ath_get_rate_txpower currently does not apply
this shift, thus retrieves the incorrect maximum power level for a given
rate. In particular for MCS rates, the maximum power levels for CCK/OFDM
rates were used, e.g. maximum power for OFDM 0 was used for MCS 0. If
STBC is used, the power is mostly limited to 0 because the STBC table
is zeroed for legacy CCK/OFDM rates. Encountered this during testing of
our work-in-progress TPC per packet for ath9k.
This only has an effect when TPC is enabled in ath9k (tpc_enabled in
struct ath_hw) which defaults to false. In this case it has a
significant impact on the used TX-power, throughput + RSSI. Otherwise
the affected code is just skipped and TX-power is limited with the
hardware registers only. This patch fixes this table lookup.

Tested on OpenWrt (kernel 5.15.98, but backported ath9k driver) with
small desk setup using ath9k chips AR9280 and AR9580. Cap of TX-power is
working properly for all rates now, throughput and RSSI as expected,
equal to as if TPC was disabled.
Compile-tested with latest 6.3 kernel + allyesconfig.
Signed-off-by: default avatarJonas Jelonek <jelonek.jonas@gmail.com>
Acked-by: default avatarToke Høiland-Jørgensen <toke@toke.dk>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20230330132159.758088-1-jelonek.jonas@gmail.com
parent 41e02bf4
......@@ -34,6 +34,12 @@
#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2)
#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18)
/* Shifts in ar5008_phy.c and ar9003_phy.c are equal for all revisions */
#define ATH9K_PWRTBL_11NA_OFDM_SHIFT 0
#define ATH9K_PWRTBL_11NG_OFDM_SHIFT 4
#define ATH9K_PWRTBL_11NA_HT_SHIFT 8
#define ATH9K_PWRTBL_11NG_HT_SHIFT 12
static u16 bits_per_symbol[][2] = {
/* 20MHz 40MHz */
......@@ -1169,13 +1175,14 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
}
static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
u8 rateidx, bool is_40, bool is_cck)
u8 rateidx, bool is_40, bool is_cck, bool is_mcs)
{
u8 max_power;
struct sk_buff *skb;
struct ath_frame_info *fi;
struct ieee80211_tx_info *info;
struct ath_hw *ah = sc->sc_ah;
bool is_2ghz, is_5ghz, use_stbc;
if (sc->tx99_state || !ah->tpc_enabled)
return MAX_RATE_POWER;
......@@ -1184,6 +1191,19 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
fi = get_frame_info(skb);
info = IEEE80211_SKB_CB(skb);
is_2ghz = info->band == NL80211_BAND_2GHZ;
is_5ghz = info->band == NL80211_BAND_5GHZ;
use_stbc = is_mcs && rateidx < 8 && (info->flags &
IEEE80211_TX_CTL_STBC);
if (is_mcs)
rateidx += is_5ghz ? ATH9K_PWRTBL_11NA_HT_SHIFT
: ATH9K_PWRTBL_11NG_HT_SHIFT;
else if (is_2ghz && !is_cck)
rateidx += ATH9K_PWRTBL_11NG_OFDM_SHIFT;
else
rateidx += ATH9K_PWRTBL_11NA_OFDM_SHIFT;
if (!AR_SREV_9300_20_OR_LATER(ah)) {
int txpower = fi->tx_power;
......@@ -1193,10 +1213,8 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
u16 eeprom_rev = ah->eep_ops->get_eeprom_rev(ah);
if (eeprom_rev >= AR5416_EEP_MINOR_VER_2) {
bool is_2ghz;
struct modal_eep_header *pmodal;
is_2ghz = info->band == NL80211_BAND_2GHZ;
pmodal = &eep->modalHeader[is_2ghz];
power_ht40delta = pmodal->ht40PowerIncForPdadc;
} else {
......@@ -1229,7 +1247,7 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
if (!max_power && !AR_SREV_9280_20_OR_LATER(ah))
max_power = 1;
} else if (!bf->bf_state.bfs_paprd) {
if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
if (use_stbc)
max_power = min_t(u8, ah->tx_power_stbc[rateidx],
fi->tx_power);
else
......@@ -1319,7 +1337,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
}
info->txpower[i] = ath_get_rate_txpower(sc, bf, rix,
is_40, false);
is_40, false, true);
continue;
}
......@@ -1350,7 +1368,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
is_cck = IS_CCK_RATE(info->rates[i].Rate);
info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, false,
is_cck);
is_cck, false);
}
/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
......
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