rtwn: try enforcing net80211 regulatory / txpower limits for 11n chips

This is an attempt to reverse engineer what the actual transmit power
calculations are doing and apply net80211 limits on them.  It doesn't
look as simple as just applying the check at the end - there are plenty
of places where offsets are calculated between different PHY modes and
1 / 2 antenna MCS transmit rates.

There are also some places where the offset being added is negative,
so handle the potential underflow so when things hit 0, they don't
just wrap and cause the maximum transmit power into the registers.

This is being done to aide in power/performance debugging - if there
are issues with the transmit power being wrongly calculated and are too
high, the output waveform will be distorted and it will effect performance.
Being able to drop the transmit power by a few dB here and there can
quickly identify if this is happening (because suddenly higher MCS
rates / OFDM rates suddenly work better!)

I've tested each NIC through the transmit power values from 0 dBm
to 30dBm via ifconfig (and they're all capped far before that,
normally around 20-25dBm) and they're not underflowing.

Locally tested:

* RTL8192CU, STA
* RTL8192EU, STA
* RTL8188EU, STA

Differential Revision:	https://reviews.freebsd.org/D47987
Reviewed by:	bz, imp
This commit is contained in:
Adrian Chadd 2024-12-08 07:51:42 -08:00
parent 6858c6b1e1
commit 0ea7f8ca66
10 changed files with 107 additions and 18 deletions

View file

@ -191,7 +191,7 @@ r88ee_attach(struct rtwn_pci_softc *pc)
sc->sc_init_antsel = rtwn_nop_softc;
sc->sc_post_init = r88ee_post_init;
sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
sc->sc_set_tx_power = rtwn_nop_int_softc_vap;
sc->sc_set_tx_power = r92c_set_tx_power;
sc->mac_prog = &rtl8188e_mac[0];
sc->mac_size = nitems(rtl8188e_mac);

View file

@ -84,6 +84,7 @@ void
r88e_get_txpower(struct rtwn_softc *sc, int chain,
struct ieee80211_channel *c, uint8_t power[RTWN_RIDX_COUNT])
{
const struct ieee80211com *ic = &sc->sc_ic;
struct r92c_softc *rs = sc->sc_priv;
const struct rtwn_r88e_txpwr *rt = rs->rs_txpwr;
uint8_t cckpow, ofdmpow, bw20pow, htpow = 0;
@ -96,15 +97,36 @@ r88e_get_txpower(struct rtwn_softc *sc, int chain,
return;
}
/* XXX net80211 regulatory */
/*
* Treat the entries in 1/2 dBm resolution where 0 = 0dBm.
* Apply the adjustments afterwards; assume that the vendor
* driver is applying offsets to make up for the actual
* target power in dBm.
*/
max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1);
KASSERT(max_mcs <= RTWN_RIDX_LEGACY_HT_COUNT, ("increase ridx limit\n"));
/* Compute per-CCK rate Tx power. */
cckpow = rt->cck_tx_pwr[group];
for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) {
power[ridx] = (ridx == RTWN_RIDX_CCK2) ? cckpow - 9 : cckpow;
/*
* Note: the regulatory limit is applied to cckpow before
* it's subtracted for CCK2.
*/
cckpow = rt->cck_tx_pwr[group];
if (cckpow > ic->ic_txpowlimit)
cckpow = ic->ic_txpowlimit;
/*
* If it's CCK2 then we subtract the 9 (4.5dB?) offset
* and make sure we aren't going to underflow.
*/
if (ridx == RTWN_RIDX_CCK2 && cckpow < 9)
cckpow = 0;
else if (ridx == RTWN_RIDX_CCK2)
cckpow = cckpow - 9;
power[ridx] = cckpow;
}
if (group < 5)
@ -112,14 +134,18 @@ r88e_get_txpower(struct rtwn_softc *sc, int chain,
/* Compute per-OFDM rate Tx power. */
ofdmpow = htpow + rt->ofdm_tx_pwr_diff;
if (ofdmpow > ic->ic_txpowlimit)
ofdmpow = ic->ic_txpowlimit;
for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++)
power[ridx] = ofdmpow;
bw20pow = htpow + rt->bw20_tx_pwr_diff;
if (bw20pow > ic->ic_txpowlimit)
bw20pow = ic->ic_txpowlimit;
for (ridx = RTWN_RIDX_HT_MCS(0); ridx <= max_mcs; ridx++)
power[ridx] = bw20pow;
/* Apply max limit. */
/* Apply max limit */
for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) {
if (power[ridx] > R92C_MAX_TX_PWR)
power[ridx] = R92C_MAX_TX_PWR;

View file

@ -184,7 +184,7 @@ r88eu_attach(struct rtwn_usb_softc *uc)
sc->sc_init_antsel = rtwn_nop_softc;
sc->sc_post_init = r88eu_post_init;
sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
sc->sc_set_tx_power = rtwn_nop_int_softc_vap;
sc->sc_set_tx_power = r92c_set_tx_power;
sc->mac_prog = &rtl8188e_mac[0];
sc->mac_size = nitems(rtl8188e_mac);

View file

@ -221,7 +221,7 @@ r92ce_attach(struct rtwn_pci_softc *pc)
sc->sc_init_antsel = rtwn_nop_softc;
sc->sc_post_init = r92ce_post_init;
sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
sc->sc_set_tx_power = rtwn_nop_int_softc_vap;
sc->sc_set_tx_power = r92c_set_tx_power;
sc->mac_prog = &rtl8192ce_mac[0];
sc->mac_size = nitems(rtl8192ce_mac);

View file

@ -59,6 +59,7 @@ void r92c_get_txpower(struct rtwn_softc *, int,
struct ieee80211_channel *, uint8_t[RTWN_RIDX_COUNT]);
void r92c_write_txpower(struct rtwn_softc *, int,
uint8_t power[RTWN_RIDX_COUNT]);
int r92c_set_tx_power(struct rtwn_softc *, struct ieee80211vap *);
void r92c_set_bw20(struct rtwn_softc *, uint8_t);
void r92c_set_chan(struct rtwn_softc *, struct ieee80211_channel *);
void r92c_set_gain(struct rtwn_softc *, uint8_t);

View file

@ -131,6 +131,7 @@ void
r92c_get_txpower(struct rtwn_softc *sc, int chain,
struct ieee80211_channel *c, uint8_t power[RTWN_RIDX_COUNT])
{
const struct ieee80211com *ic = &sc->sc_ic;
struct r92c_softc *rs = sc->sc_priv;
struct rtwn_r92c_txpwr *rt = rs->rs_txpwr;
const struct rtwn_r92c_txagc *base = rs->rs_txagc;
@ -144,7 +145,12 @@ r92c_get_txpower(struct rtwn_softc *sc, int chain,
return;
}
/* XXX net80211 regulatory */
/*
* Treat the entries in 1/2 dBm resolution where 0 = 0dBm.
* Apply the adjustments afterwards; assume that the vendor
* driver is applying offsets to make up for the actual
* target power in dBm.
*/
max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1);
KASSERT(max_mcs <= RTWN_RIDX_LEGACY_HT_COUNT, ("increase ridx limit\n"));
@ -199,6 +205,10 @@ r92c_get_txpower(struct rtwn_softc *sc, int chain,
for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) {
if (power[ridx] > R92C_MAX_TX_PWR)
power[ridx] = R92C_MAX_TX_PWR;
/* Apply net80211 limits */
if (power[ridx] > ic->ic_txpowlimit)
power[ridx] = ic->ic_txpowlimit;
}
}
@ -281,6 +291,25 @@ r92c_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c)
}
}
/*
* Only reconfigure the transmit power if there's a valid BSS node and
* channel. Otherwise just let the next call to r92c_set_chan()
* configure the transmit power.
*/
int
r92c_set_tx_power(struct rtwn_softc *sc, struct ieee80211vap *vap)
{
if (vap->iv_bss == NULL)
return (EINVAL);
if (vap->iv_bss->ni_chan == IEEE80211_CHAN_ANYC)
return (EINVAL);
/* Set it for the current channel */
r92c_set_txpower(sc, vap->iv_bss->ni_chan);
return (0);
}
static void
r92c_set_bw40(struct rtwn_softc *sc, uint8_t chan, int prichlo)
{

View file

@ -213,7 +213,7 @@ r92cu_attach(struct rtwn_usb_softc *uc)
sc->sc_init_antsel = r92c_init_antsel;
sc->sc_post_init = r92cu_post_init;
sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
sc->sc_set_tx_power = rtwn_nop_int_softc_vap;
sc->sc_set_tx_power = r92c_set_tx_power;
sc->mac_prog = &rtl8192cu_mac[0];
sc->mac_size = nitems(rtl8192cu_mac);

View file

@ -46,6 +46,7 @@ void r92e_detach_private(struct rtwn_softc *);
/* r92e_chan.c */
void r92e_set_chan(struct rtwn_softc *, struct ieee80211_channel *);
int r92e_set_tx_power(struct rtwn_softc *sc, struct ieee80211vap *vap);
/* r92e_fw.c */
#ifndef RTWN_WITHOUT_UCODE

View file

@ -90,6 +90,7 @@ static void
r92e_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c,
uint8_t power[RTWN_RIDX_COUNT])
{
const struct ieee80211com *ic = &sc->sc_ic;
struct r92e_softc *rs = sc->sc_priv;
int i, ridx, group, max_mcs;
@ -103,19 +104,32 @@ r92e_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c,
max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1);
/* XXX regulatory */
/* XXX net80211 regulatory */
for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++)
for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) {
power[ridx] = rs->cck_tx_pwr[chain][group];
for (ridx = RTWN_RIDX_OFDM6; ridx <= max_mcs; ridx++)
if (power[ridx] > ic->ic_txpowlimit)
power[ridx] = ic->ic_txpowlimit;
}
for (ridx = RTWN_RIDX_OFDM6; ridx <= max_mcs; ridx++) {
power[ridx] = rs->ht40_tx_pwr_2g[chain][group];
if (power[ridx] > ic->ic_txpowlimit)
power[ridx] = ic->ic_txpowlimit;
}
for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++)
power[ridx] += rs->ofdm_tx_pwr_diff_2g[chain][0];
for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) {
/* Ensure we don't underflow if the power delta is -ve */
int8_t pwr;
pwr = power[ridx] + rs->ofdm_tx_pwr_diff_2g[chain][0];
if (pwr < 0)
pwr = 0;
power[ridx] = pwr;
}
for (i = 0; i < sc->ntxchains; i++) {
uint8_t min_mcs;
uint8_t pwr_diff;
int8_t pwr_diff, pwr;
if (IEEE80211_IS_CHAN_HT40(c))
pwr_diff = rs->bw40_tx_pwr_diff_2g[chain][i];
@ -123,8 +137,13 @@ r92e_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c,
pwr_diff = rs->bw20_tx_pwr_diff_2g[chain][i];
min_mcs = RTWN_RIDX_HT_MCS(i * 8);
for (ridx = min_mcs; ridx <= max_mcs; ridx++)
power[ridx] += pwr_diff;
for (ridx = min_mcs; ridx <= max_mcs; ridx++) {
/* Ensure we don't underflow */
pwr = power[ridx] + pwr_diff;
if (pwr < 0)
pwr = 0;
power[ridx] = pwr;
}
}
/* Apply max limit. */
@ -151,6 +170,19 @@ r92e_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c)
}
}
int
r92e_set_tx_power(struct rtwn_softc *sc, struct ieee80211vap *vap)
{
if (vap->iv_bss == NULL)
return (EINVAL);
if (vap->iv_bss->ni_chan == IEEE80211_CHAN_ANYC)
return (EINVAL);
r92e_set_txpower(sc, vap->iv_bss->ni_chan);
return (0);
}
static void
r92e_set_bw40(struct rtwn_softc *sc, uint8_t chan, int prichlo)
{

View file

@ -164,7 +164,7 @@ r92eu_attach(struct rtwn_usb_softc *uc)
sc->sc_init_antsel = rtwn_nop_softc;
sc->sc_post_init = r92eu_post_init;
sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
sc->sc_set_tx_power = rtwn_nop_int_softc_vap;
sc->sc_set_tx_power = r92e_set_tx_power;
sc->mac_prog = &rtl8192eu_mac[0];
sc->mac_size = nitems(rtl8192eu_mac);