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 @@ -54,10 +54,12 @@ uint8_t r92c_temp_read(struct rtwn_softc *); /* r92c_chan.c */ +void r92c_dump_txpower(struct rtwn_softc *, int, uint8_t[RTWN_RIDX_COUNT]); 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); 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 @@ -53,6 +53,56 @@ #include #include +void +r92c_dump_txpower(struct rtwn_softc *sc, int chain, + uint8_t power[RTWN_RIDX_COUNT]) +{ + +#ifdef RTWN_DEBUG + if (sc->sc_debug & RTWN_DEBUG_TXPWR) { + int i; + + /* Print CCK */ + RTWN_DPRINTF(sc, RTWN_DEBUG_TXPWR, + "TX [%d]: CCK: 1M: %d 2M: %d 5.5M: %d 11M: %d\n", + chain, + power[RTWN_RIDX_CCK1], + power[RTWN_RIDX_CCK2], + power[RTWN_RIDX_CCK55], + power[RTWN_RIDX_CCK11]); + /* Print OFDM */ + RTWN_DPRINTF(sc, RTWN_DEBUG_TXPWR, + "TX [%d]: OFDM: 6M: %d 9M: %d 12M: %d 18M: %d 24M: %d " + "36M: %d 48M: %d 54M: %d\n", + chain, + power[RTWN_RIDX_OFDM6], + power[RTWN_RIDX_OFDM9], + power[RTWN_RIDX_OFDM12], + power[RTWN_RIDX_OFDM18], + power[RTWN_RIDX_OFDM24], + power[RTWN_RIDX_OFDM36], + power[RTWN_RIDX_OFDM48], + power[RTWN_RIDX_OFDM54]); + /* Print HT, 1 and 2 stream */ + for (i = 0; i < sc->ntxchains; i++) { + RTWN_DPRINTF(sc, RTWN_DEBUG_TXPWR, + "TX [%d]: MCS%d-%d: %d %d %d %d %d %d %d %d\n", + chain, + i * 8, + i * 8 + 7, + power[RTWN_RIDX_HT_MCS(i * 8 + 0)], + power[RTWN_RIDX_HT_MCS(i * 8 + 1)], + power[RTWN_RIDX_HT_MCS(i * 8 + 2)], + power[RTWN_RIDX_HT_MCS(i * 8 + 3)], + power[RTWN_RIDX_HT_MCS(i * 8 + 4)], + power[RTWN_RIDX_HT_MCS(i * 8 + 5)], + power[RTWN_RIDX_HT_MCS(i * 8 + 6)], + power[RTWN_RIDX_HT_MCS(i * 8 + 7)]); + } + } +#endif +} + static int r92c_get_power_group(struct rtwn_softc *sc, struct ieee80211_channel *c) { @@ -81,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; @@ -94,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")); @@ -149,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; + } } @@ -224,23 +284,32 @@ memset(power, 0, sizeof(power)); /* Compute per-rate Tx power values. */ rtwn_r92c_get_txpower(sc, i, c, power); -#ifdef RTWN_DEBUG - if (sc->sc_debug & RTWN_DEBUG_TXPWR) { - int max_mcs, ridx; - - max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1); - - /* Dump per-rate Tx power values. */ - printf("Tx power for chain %d:\n", i); - for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) - printf("Rate %d = %u\n", ridx, power[ridx]); - } -#endif + /* Optionally print out the power table */ + r92c_dump_txpower(sc, i, power); /* Write per-rate Tx power values to hardware. */ r92c_write_txpower(sc, i, power); } } +/* + * 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; + + pwr = power[ridx] + pwr_diff; - for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) - 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 +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. */ @@ -132,15 +153,6 @@ if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } - -#ifdef RTWN_DEBUG - if (sc->sc_debug & RTWN_DEBUG_TXPWR) { - /* Dump per-rate Tx power values. */ - printf("Tx power for chain %d:\n", chain); - for (ridx = RTWN_RIDX_CCK1; ridx < RTWN_RIDX_LEGACY_HT_COUNT; ridx++) - printf("Rate %d = %u\n", ridx, power[ridx]); - } -#endif } static void @@ -153,11 +165,26 @@ memset(power, 0, sizeof(power)); /* Compute per-rate Tx power values. */ r92e_get_txpower(sc, i, c, power); + /* Optionally print out the power table */ + r92c_dump_txpower(sc, i, power); /* Write per-rate Tx power values to hardware. */ r92c_write_txpower(sc, i, power); } } +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,7 +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 = 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);