diff --git a/sys/dev/rtwn/rtl8188e/pci/r88ee_attach.c b/sys/dev/rtwn/rtl8188e/pci/r88ee_attach.c --- a/sys/dev/rtwn/rtl8188e/pci/r88ee_attach.c +++ b/sys/dev/rtwn/rtl8188e/pci/r88ee_attach.c @@ -191,7 +191,7 @@ 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); diff --git a/sys/dev/rtwn/rtl8188e/r88e_chan.c b/sys/dev/rtwn/rtl8188e/r88e_chan.c --- a/sys/dev/rtwn/rtl8188e/r88e_chan.c +++ b/sys/dev/rtwn/rtl8188e/r88e_chan.c @@ -84,6 +84,7 @@ 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 @@ 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 @@ /* 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; diff --git a/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c b/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c --- a/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c +++ b/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c @@ -184,7 +184,7 @@ 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); diff --git a/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c b/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c --- a/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c +++ b/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c @@ -221,7 +221,7 @@ 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); diff --git a/sys/dev/rtwn/rtl8192c/r92c.h b/sys/dev/rtwn/rtl8192c/r92c.h --- a/sys/dev/rtwn/rtl8192c/r92c.h +++ b/sys/dev/rtwn/rtl8192c/r92c.h @@ -59,6 +59,7 @@ 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); diff --git a/sys/dev/rtwn/rtl8192c/r92c_chan.c b/sys/dev/rtwn/rtl8192c/r92c_chan.c --- a/sys/dev/rtwn/rtl8192c/r92c_chan.c +++ b/sys/dev/rtwn/rtl8192c/r92c_chan.c @@ -131,6 +131,7 @@ 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 @@ 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 @@ 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 @@ } } +/* + * 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) { diff --git a/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c b/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c --- a/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c +++ b/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c @@ -213,7 +213,7 @@ 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); diff --git a/sys/dev/rtwn/rtl8192e/r92e.h b/sys/dev/rtwn/rtl8192e/r92e.h --- a/sys/dev/rtwn/rtl8192e/r92e.h +++ b/sys/dev/rtwn/rtl8192e/r92e.h @@ -46,6 +46,7 @@ /* 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 diff --git a/sys/dev/rtwn/rtl8192e/r92e_chan.c b/sys/dev/rtwn/rtl8192e/r92e_chan.c --- a/sys/dev/rtwn/rtl8192e/r92e_chan.c +++ b/sys/dev/rtwn/rtl8192e/r92e_chan.c @@ -90,6 +90,7 @@ 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,34 @@ 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++) { + /* Ensure we don't underflow if the power delta is -ve */ + int8_t pwr_diff = rs->ofdm_tx_pwr_diff_2g[chain][0]; + int8_t pwr; - for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) - power[ridx] += rs->ofdm_tx_pwr_diff_2g[chain][0]; + pwr = power[ridx] + pwr_diff; + + 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 +139,13 @@ 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 +172,19 @@ } } +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) { diff --git a/sys/dev/rtwn/rtl8192e/usb/r92eu_attach.c b/sys/dev/rtwn/rtl8192e/usb/r92eu_attach.c --- a/sys/dev/rtwn/rtl8192e/usb/r92eu_attach.c +++ b/sys/dev/rtwn/rtl8192e/usb/r92eu_attach.c @@ -164,6 +164,7 @@ 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 = r92e_set_tx_power; sc->mac_prog = &rtl8192eu_mac[0]; sc->mac_size = nitems(rtl8192eu_mac);