* [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for
@ 2026-09-17 14:19 Mehmet Fide
2026-09-22 1:56 ` Ping-Ke Shih
0 siblings, 1 reply; 5+ messages in thread
From: Mehmet Fide @ 2026-09-17 14:19 UTC (permalink / raw)
To: Ping-Ke Shih; +Cc: Bitterblue Smith, linux-wireless, linux-kernel, mehmet.fide
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
The per-rate power index is the by-rate value capped by the regulatory
limit and the SAR limit; the level mac80211 hands over in conf.power_level
is never looked at, so "iw phy <phy> set txpower fixed <mBm>" is accepted
and silently ignored. Devices that share a small enclosure with their
client, or that must back off for coexistence, have no way to run below
the regulatory maximum.
Treat the requested level like the SAR limit: convert the dBm value to
the by-rate offset domain with the chip's gain index granularity and use
it as one more ceiling on the offset, then program the indices again
whenever the level changes. The level is the total the device may
radiate, so with two, three or four transmit paths each path gets 3, 5
or 6 dB less, the way ath9k and the vendor driver share it. mac80211
hands over the minimum of the regulatory maximum and the user's request,
so a channel's maximum is respected as well; before the first
configuration there is no request and nothing is capped, which keeps the
behaviour of a driver that does not honour the level at all.
Tested on RTL8822BU (2T) and RTL8821CU (1T) in AP mode: "fixed 1000"
moves every rate that sat above 10 dBm down to the 10 dBm index, less
the 3 dB path share on the 8822BU, while the rates already below stay,
"auto" restores the tables and the client stays associated through the
changes; a second radio saw the beacons drop by the requested amount.
Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
---
drivers/net/wireless/realtek/rtw88/mac80211.c | 3 +++
drivers/net/wireless/realtek/rtw88/phy.c | 26 +++++++++++++++++++
drivers/net/wireless/realtek/rtw88/phy.h | 1 +
3 files changed, 30 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 2a9b09fa76e7..e0462e934438 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -3,6 +3,7 @@
*/
#include "main.h"
+#include "phy.h"
#include "sec.h"
#include "tx.h"
#include "fw.h"
@@ -94,6 +95,8 @@ static int rtw_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
rtw_set_channel(rtwdev);
+ else if (changed & IEEE80211_CONF_CHANGE_POWER)
+ rtw_phy_set_tx_power_level(rtwdev, rtwdev->hal.current_channel);
if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
(hw->conf.flags & IEEE80211_CONF_IDLE) &&
diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index e2ac5c6fd500..0dfcd0739423 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -2236,6 +2236,29 @@ static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band,
return (s8)rtwdev->chip->max_power_index;
}
+static s8 rtw_phy_get_tx_power_user(struct rtw_dev *rtwdev, u8 band, u8 path,
+ u8 rate)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ const struct rtw_chip_info *chip = rtwdev->chip;
+ static const u8 path_share_dbm[] = { 0, 0, 3, 5, 6 };
+ int power_level = rtwdev->hw->conf.power_level;
+ u8 rs = rtw_phy_rate_to_rate_section(rate);
+ u8 paths = clamp_t(u8, hweight8(hal->antenna_tx), 1, 4);
+ s32 idx;
+ s8 base;
+
+ if (power_level <= 0 || rs == RTW_RATE_SECTION_NUM)
+ return (s8)chip->max_power_index;
+
+ idx = (power_level - path_share_dbm[paths]) << chip->txgi_factor;
+ base = band == PHY_BAND_2G ? hal->tx_pwr_by_rate_base_2g[path][rs] :
+ hal->tx_pwr_by_rate_base_5g[path][rs];
+
+ return (s8)clamp_t(s32, idx, -chip->max_power_index - 1,
+ chip->max_power_index) - base;
+}
+
void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
u8 ch, u8 regd, struct rtw_power_params *pwr_param)
{
@@ -2248,6 +2271,7 @@ void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
s8 *limit = &pwr_param->pwr_limit;
s8 *remnant = &pwr_param->pwr_remnant;
s8 *sar = &pwr_param->pwr_sar;
+ s8 *user = &pwr_param->pwr_user;
pwr_idx = &rtwdev->efuse.txpwr_idx_table[path];
group = rtw_get_channel_group(ch, rate);
@@ -2272,6 +2296,7 @@ void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
*remnant = rate <= DESC_RATE11M ? dm_info->txagc_remnant_cck :
dm_info->txagc_remnant_ofdm[path];
*sar = rtw_phy_get_tx_power_sar(rtwdev, hal->sar_band, path, rate);
+ *user = rtw_phy_get_tx_power_user(rtwdev, band, path, rate);
}
u8
@@ -2289,6 +2314,7 @@ rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate,
offset = min3(pwr_param.pwr_offset,
pwr_param.pwr_limit,
pwr_param.pwr_sar);
+ offset = min(offset, pwr_param.pwr_user);
if (rtwdev->chip->en_dis_dpd)
offset += rtw_phy_get_dis_dpd_by_rate_diff(rtwdev, rate);
diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index 8449936497bb..c26f18348709 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -155,6 +155,7 @@ struct rtw_power_params {
s8 pwr_limit;
s8 pwr_remnant;
s8 pwr_sar;
+ s8 pwr_user;
};
void
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for
2026-09-17 14:19 [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for Mehmet Fide
@ 2026-09-22 1:56 ` Ping-Ke Shih
2026-09-22 6:26 ` Mehmet Fide
0 siblings, 1 reply; 5+ messages in thread
From: Ping-Ke Shih @ 2026-09-22 1:56 UTC (permalink / raw)
To: Mehmet Fide; +Cc: Bitterblue Smith, linux-wireless, linux-kernel, mehmet.fide
Mehmet Fide <mehmet.fide@gmail.com> wrote:
> From: Mehmet Fide <mehmet.fide@screeningeagle.com>
>
> The per-rate power index is the by-rate value capped by the regulatory
> limit and the SAR limit; the level mac80211 hands over in conf.power_level
> is never looked at, so "iw phy <phy> set txpower fixed <mBm>" is accepted
> and silently ignored. Devices that share a small enclosure with their
> client, or that must back off for coexistence, have no way to run below
> the regulatory maximum.
>
> Treat the requested level like the SAR limit: convert the dBm value to
> the by-rate offset domain with the chip's gain index granularity and use
> it as one more ceiling on the offset, then program the indices again
> whenever the level changes. The level is the total the device may
> radiate, so with two, three or four transmit paths each path gets 3, 5
> or 6 dB less, the way ath9k and the vendor driver share it. mac80211
> hands over the minimum of the regulatory maximum and the user's request,
> so a channel's maximum is respected as well; before the first
> configuration there is no request and nothing is capped, which keeps the
> behaviour of a driver that does not honour the level at all.
>
> Tested on RTL8822BU (2T) and RTL8821CU (1T) in AP mode: "fixed 1000"
> moves every rate that sat above 10 dBm down to the 10 dBm index, less
> the 3 dB path share on the 8822BU, while the rates already below stay,
> "auto" restores the tables and the client stays associated through the
> changes; a second radio saw the beacons drop by the requested amount.
How did you measure the TX power decreasing as your expectation?
And, curiously, what is the purpose you added this? which application?
>
> Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
> ---
> drivers/net/wireless/realtek/rtw88/mac80211.c | 3 +++
> drivers/net/wireless/realtek/rtw88/phy.c | 26 +++++++++++++++++++
> drivers/net/wireless/realtek/rtw88/phy.h | 1 +
Miss to add pwr_param->pwr_user to rtw_debugfs_get_tx_pwr_tbl() to
show the correct final TX power and pwr_user factor.
> 3 files changed, 30 insertions(+)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c
> b/drivers/net/wireless/realtek/rtw88/mac80211.c
> index 2a9b09fa76e7..e0462e934438 100644
> --- a/drivers/net/wireless/realtek/rtw88/mac80211.c
> +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
> @@ -3,6 +3,7 @@
> */
>
> #include "main.h"
> +#include "phy.h"
> #include "sec.h"
> #include "tx.h"
> #include "fw.h"
> @@ -94,6 +95,8 @@ static int rtw_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
>
> if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
> rtw_set_channel(rtwdev);
> + else if (changed & IEEE80211_CONF_CHANGE_POWER)
Should it be 'if' instead of 'else if'?
> + rtw_phy_set_tx_power_level(rtwdev, rtwdev->hal.current_channel);
>
> if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
> (hw->conf.flags & IEEE80211_CONF_IDLE) &&
> diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
> index e2ac5c6fd500..0dfcd0739423 100644
> --- a/drivers/net/wireless/realtek/rtw88/phy.c
> +++ b/drivers/net/wireless/realtek/rtw88/phy.c
> @@ -2236,6 +2236,29 @@ static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band,
> return (s8)rtwdev->chip->max_power_index;
> }
>
> +static s8 rtw_phy_get_tx_power_user(struct rtw_dev *rtwdev, u8 band, u8 path,
> + u8 rate)
> +{
> + struct rtw_hal *hal = &rtwdev->hal;
> + const struct rtw_chip_info *chip = rtwdev->chip;
> + static const u8 path_share_dbm[] = { 0, 0, 3, 5, 6 };
> + int power_level = rtwdev->hw->conf.power_level;
> + u8 rs = rtw_phy_rate_to_rate_section(rate);
> + u8 paths = clamp_t(u8, hweight8(hal->antenna_tx), 1, 4);
In reverse X'mas tree order.
> + s32 idx;
> + s8 base;
> +
> + if (power_level <= 0 || rs == RTW_RATE_SECTION_NUM)
> + return (s8)chip->max_power_index;
> +
> + idx = (power_level - path_share_dbm[paths]) << chip->txgi_factor;
> + base = band == PHY_BAND_2G ? hal->tx_pwr_by_rate_base_2g[path][rs] :
> + hal->tx_pwr_by_rate_base_5g[path][rs];
> +
> + return (s8)clamp_t(s32, idx, -chip->max_power_index - 1,
> + chip->max_power_index) - base;
Should it clamp after 'idx - base' ?
> +}
> +
> void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
> u8 ch, u8 regd, struct rtw_power_params *pwr_param)
> {
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for
2026-09-22 1:56 ` Ping-Ke Shih
@ 2026-09-22 6:26 ` Mehmet Fide
2026-09-22 6:48 ` Ping-Ke Shih
0 siblings, 1 reply; 5+ messages in thread
From: Mehmet Fide @ 2026-09-22 6:26 UTC (permalink / raw)
To: Ping-Ke Shih; +Cc: Bitterblue Smith, linux-wireless, linux-kernel, mehmet.fide
On Tue, 22 Sep 2026, Ping-Ke Shih wrote:
> How did you measure the TX power decreasing as your expectation?
Two ways. The exact check is the tx_pwr_tbl debugfs table on the AP
before and after "iw phy phy0 set txpower fixed <mBm>": with fixed 1000
on the RTL8822BU every rate that sat above 10 dBm moved to the 7 dBm
index (10 dBm less the 3 dB two-path share) and the rates already below
it kept their index; fixed 100 gave index 0 instead of the wrap that
patch 1 of v2 fixes; fixed 2000 and "auto" left every index as the
stock driver programs it.
Over the air, a second radio (an RTL8821CU in station mode on a second
unit, at a fixed distance) read the AP's beacons at about -52 dBm with
"auto", -59 with fixed 1000, -62 with fixed 500 and -66 with fixed 100,
and about -52 again after "auto". The drop is smaller than the request
because the beacon goes out at the 1 Mbit/s CCK rate, whose calibrated
index already sits below the channel maximum, so the table is the exact
check and the second radio confirms direction and order.
> And, curiously, what is the purpose you added this? which application?
A handheld measuring instrument that carries the module as its access
point. The hardware team wanted to be able to run the module a fixed
number of dB below the regulatory maximum for an RF exposure
assessment. On the other products of the family the module is an
ath9k_htc one, where "iw set txpower" is the knob we use; on this one
the same command was accepted and mac80211 reported it as in force
while the chip kept transmitting at the maximum.
When I wrote v1 I did not know that rtw88 already exposes
set_sar_specs. I have since verified it on the RTL8822BU: it caps the
per-path index from the given dBm and covers our exposure case, so we
will use it. That leaves the patch as a consistency question rather
than a need of ours: should rtw88 honour the level mac80211 hands over,
as ath9k and most other drivers do, instead of accepting it and letting
mac80211 report it as applied? If you want that, a v2 with your review
points addressed is ready and I will send it. If you prefer SAR to be
the only way to lower the power on these chips, I will drop it.
> Miss to add pwr_param->pwr_user to rtw_debugfs_get_tx_pwr_tbl() to
> show the correct final TX power and pwr_user factor.
Done in v2: a "usr" column, and the effective ceiling column now takes
it into account.
> > + if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
> > + rtw_set_channel(rtwdev);
> > + else if (changed & IEEE80211_CONF_CHANGE_POWER)
>
> Should it be 'if' instead of 'else if'?
rtw_set_channel() ends with rtw_phy_set_tx_power_level(), so when the
channel changed the tables are already programmed with the level kept
in rtw_hal, and a second pass would program the same values again. The
explicit call is only needed when the level changed on its own, so I
kept the else. If you prefer the plain if for readability I will change
it.
> In reverse X'mas tree order.
Fixed in v2.
> > + return (s8)clamp_t(s32, idx, -chip->max_power_index - 1,
> > + chip->max_power_index) - base;
>
> Should it clamp after 'idx - base' ?
The function returns an offset relative to the by-rate base, like the
limit and SAR values it is compared with, and that offset has to go
negative to pull a rate below its base; clamping idx - base would forbid
exactly that. What has to stay inside the chip's index range is the
requested absolute index, so that is what is clamped, and the final sum
is clamped in patch 1 of v2. In v2 the clamp is on its own line before
the subtraction so the order is visible.
Mehmet
^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for
2026-09-22 6:26 ` Mehmet Fide
@ 2026-09-22 6:48 ` Ping-Ke Shih
2026-09-22 6:59 ` Mehmet Fide
0 siblings, 1 reply; 5+ messages in thread
From: Ping-Ke Shih @ 2026-09-22 6:48 UTC (permalink / raw)
To: Mehmet Fide; +Cc: Bitterblue Smith, linux-wireless, linux-kernel, mehmet.fide
Mehmet Fide <mehmet.fide@gmail.com> wrote:
> When I wrote v1 I did not know that rtw88 already exposes
> set_sar_specs. I have since verified it on the RTL8822BU: it caps the
> per-path index from the given dBm and covers our exposure case, so we
> will use it. That leaves the patch as a consistency question rather
> than a need of ours: should rtw88 honour the level mac80211 hands over,
> as ath9k and most other drivers do, instead of accepting it and letting
> mac80211 report it as applied? If you want that, a v2 with your review
> points addressed is ready and I will send it. If you prefer SAR to be
> the only way to lower the power on these chips, I will drop it.
I don't lean to either, but ...
> > > + if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
> > > + rtw_set_channel(rtwdev);
> > > + else if (changed & IEEE80211_CONF_CHANGE_POWER)
> >
> > Should it be 'if' instead of 'else if'?
>
> rtw_set_channel() ends with rtw_phy_set_tx_power_level(), so when the
> channel changed the tables are already programmed with the level kept
> in rtw_hal, and a second pass would program the same values again. The
> explicit call is only needed when the level changed on its own, so I
> kept the else. If you prefer the plain if for readability I will change
> it.
Here should add a comment to be clear. It might become not clean...
Then I'd prefer SAR.
Ping-Ke
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for
2026-09-22 6:48 ` Ping-Ke Shih
@ 2026-09-22 6:59 ` Mehmet Fide
0 siblings, 0 replies; 5+ messages in thread
From: Mehmet Fide @ 2026-09-22 6:59 UTC (permalink / raw)
To: Ping-Ke Shih; +Cc: Bitterblue Smith, linux-wireless, linux-kernel, mehmet.fide
On Tue, 22 Sep 2026, Ping-Ke Shih wrote:
> I don't lean to either, but ...
[...]
> Then I'd prefer SAR.
Understood. We will use the SAR interface, and I withdraw the patch;
please mark it as such. Thanks for the review.
Mehmet
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-22 6:59 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17 14:19 [PATCH rtw-next] wifi: rtw88: honour the transmit power mac80211 asks for Mehmet Fide
2026-09-22 1:56 ` Ping-Ke Shih
2026-09-22 6:26 ` Mehmet Fide
2026-09-22 6:48 ` Ping-Ke Shih
2026-09-22 6:59 ` Mehmet Fide
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®