This adds support for adjusting the Txpower level while pushing
traffic to an associated station. The allowed range is from 0 to
the maximum power of channel.
Signed-off-by: Ryder Lee <[email protected]>
---
.../net/wireless/mediatek/mt76/mt7915/init.c | 3 +
.../net/wireless/mediatek/mt76/mt7915/main.c | 33 +++++++
.../net/wireless/mediatek/mt76/mt7915/mcu.c | 92 +++++++++++++++++--
.../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 ++
.../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +
5 files changed, 134 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index b48c2ba9273d..79bd7bf93f33 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -355,6 +355,9 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ if (!is_mt7915(&dev->mt76))
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
+
if (!mdev->dev->of_node ||
!of_property_read_bool(mdev->dev->of_node,
"mediatek,disable-radar-background"))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index a29cbdb47801..9792831090c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -1126,6 +1126,38 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw,
mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta);
}
+static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7915_phy *phy = mt7915_hw_phy(hw);
+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+ s16 txpower = sta->deflink.txpwr.power;
+ int ret;
+
+ if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC)
+ txpower = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ /* NOTE: temporarily use 0 as minimum limit, which is a
+ * global setting and will be applied to all stations.
+ */
+ ret = mt7915_mcu_set_txpower_frame_min(phy, 0);
+ if (ret)
+ return ret;
+
+ /* This only applies to data frames while pushing traffic,
+ * whereas the management frames or other packets that are
+ * using fixed rate can be configured via TxD.
+ */
+ ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = {
"tx_ampdu_cnt",
"tx_stop_q_empty_cnt",
@@ -1491,6 +1523,7 @@ const struct ieee80211_ops mt7915_ops = {
.set_bitrate_mask = mt7915_set_bitrate_mask,
.set_coverage_class = mt7915_set_coverage_class,
.sta_statistics = mt7915_sta_statistics,
+ .sta_set_txpwr = mt7915_sta_set_txpwr,
.sta_set_4addr = mt7915_sta_set_4addr,
.sta_set_decap_offload = mt7915_sta_set_decap_offload,
.add_twt_setup = mt7915_mac_add_twt_setup,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 3a0d97dad96f..d911512f31a6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -3083,6 +3083,86 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state)
&req, sizeof(req), false);
}
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower)
+{
+ struct mt7915_dev *dev = phy->dev;
+ struct {
+ u8 format_id;
+ u8 rsv;
+ u8 band_idx;
+ s8 txpower_min;
+ } __packed req = {
+ .format_id = TX_POWER_LIMIT_FRAME_MIN,
+ .band_idx = phy != &dev->phy,
+ .txpower_min = txpower * 2, /* 0.5db */
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+ sizeof(req), true);
+}
+
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, s8 txpower)
+{
+ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+ struct mt7915_dev *dev = phy->dev;
+ struct {
+ u8 format_id;
+ u8 rsv[3];
+ u8 band_idx;
+ s8 txpower_max;
+ __le16 wcid;
+ s8 txpower_offs[48];
+ } __packed req = {
+ .format_id = TX_POWER_LIMIT_FRAME,
+ .band_idx = phy != &dev->phy,
+ .txpower_max = DIV_ROUND_UP(phy->mt76->txpower_cur, 2),
+ .wcid = cpu_to_le16(msta->wcid.idx),
+ };
+ int ret;
+ s8 txpower_sku[MT7915_SKU_RATE_NUM];
+
+ ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku));
+ if (ret)
+ return ret;
+
+ if (txpower > req.txpower_max || txpower < 0)
+ return -EINVAL;
+
+ if (txpower) {
+ u32 offs, len, i;
+
+ if (sta->deflink.ht_cap.ht_supported) {
+ const u8 *sku_len = mt7915_sku_group_len;
+
+ offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM];
+ len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40];
+
+ if (sta->deflink.vht_cap.vht_supported) {
+ offs += len;
+ len = sku_len[SKU_VHT_BW20] * 4;
+
+ if (sta->deflink.he_cap.has_he) {
+ offs += len + sku_len[SKU_HE_RU26] * 3;
+ len = sku_len[SKU_HE_RU242] * 4;
+ }
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++, offs++)
+ req.txpower_offs[i] =
+ txpower - DIV_ROUND_UP(txpower_sku[offs], 2);
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+ sizeof(req), true);
+}
+
int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
@@ -3094,7 +3174,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
u8 dbdc_idx;
s8 val[MT7915_SKU_RATE_NUM];
} __packed req = {
- .format_id = 4,
+ .format_id = TX_POWER_LIMIT_TABLE,
.dbdc_idx = phy != &dev->phy,
};
struct mt76_power_limits limits_array;
@@ -3144,11 +3224,11 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
u8 band;
u8 _rsv;
} __packed req = {
- .format_id = 7,
+ .format_id = TX_POWER_LIMIT_INFO,
.category = RATE_POWER_INFO,
.band = phy != &dev->phy,
};
- s8 res[MT7915_SKU_RATE_NUM][2];
+ s8 txpower_sku[MT7915_SKU_RATE_NUM][2];
struct sk_buff *skb;
int ret, i;
@@ -3158,9 +3238,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
if (ret)
return ret;
- memcpy(res, skb->data + 4, sizeof(res));
+ memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku));
for (i = 0; i < len; i++)
- txpower[i] = res[i][req.band];
+ txpower[i] = txpower_sku[i][req.band_idx];
dev_kfree_skb(skb);
@@ -3198,7 +3278,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
u8 dbdc_idx;
u8 rsv;
} __packed req = {
- .format_id = 0,
+ .format_id = TX_POWER_LIMIT_ENABLE,
.dbdc_idx = phy != &dev->phy,
.sku_enable = enable,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 2fc09fd53777..46c517e50ae4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -419,6 +419,14 @@ enum {
#define RATE_CFG_PHY_TYPE GENMASK(27, 24)
#define RATE_CFG_HE_LTF GENMASK(31, 28)
+enum {
+ TX_POWER_LIMIT_ENABLE,
+ TX_POWER_LIMIT_TABLE = 0x4,
+ TX_POWER_LIMIT_INFO = 0x7,
+ TX_POWER_LIMIT_FRAME = 0x11,
+ TX_POWER_LIMIT_FRAME_MIN = 0x12,
+};
+
enum {
SPR_ENABLE = 0x1,
SPR_ENABLE_SD = 0x3,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 0bad78cf32c7..015c7190d79f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -526,6 +526,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower);
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, s8 txpower);
int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action);
int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val);
int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
--
2.36.1
> This adds support for adjusting the Txpower level while pushing
> traffic to an associated station. The allowed range is from 0 to
> the maximum power of channel.
Very cool, iiuc the hw/fw is capable of adjusting tx power according to the trasmitted
frame, right? Is it possible to specify it on per-packet basis or just
per-station?
>
> Signed-off-by: Ryder Lee <[email protected]>
> ---
> .../net/wireless/mediatek/mt76/mt7915/init.c | 3 +
> .../net/wireless/mediatek/mt76/mt7915/main.c | 33 +++++++
> .../net/wireless/mediatek/mt76/mt7915/mcu.c | 92 +++++++++++++++++--
> .../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 ++
> .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +
> 5 files changed, 134 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
> index b48c2ba9273d..79bd7bf93f33 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
> @@ -355,6 +355,9 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
> wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
> wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
>
> + if (!is_mt7915(&dev->mt76))
> + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
> +
> if (!mdev->dev->of_node ||
> !of_property_read_bool(mdev->dev->of_node,
> "mediatek,disable-radar-background"))
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
> index a29cbdb47801..9792831090c7 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
> @@ -1126,6 +1126,38 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw,
> mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta);
> }
>
> +static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct mt7915_phy *phy = mt7915_hw_phy(hw);
> + struct mt7915_dev *dev = mt7915_hw_dev(hw);
> + s16 txpower = sta->deflink.txpwr.power;
> + int ret;
> +
> + if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC)
> + txpower = 0;
> +
> + mutex_lock(&dev->mt76.mutex);
> +
> + /* NOTE: temporarily use 0 as minimum limit, which is a
> + * global setting and will be applied to all stations.
> + */
> + ret = mt7915_mcu_set_txpower_frame_min(phy, 0);
> + if (ret)
we need to relase the lock here
Regards,
Lorenzo
> + return ret;
> +
> + /* This only applies to data frames while pushing traffic,
> + * whereas the management frames or other packets that are
> + * using fixed rate can be configured via TxD.
> + */
> + ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower);
> +
> + mutex_unlock(&dev->mt76.mutex);
> +
> + return ret;
> +}
> +
> static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = {
> "tx_ampdu_cnt",
> "tx_stop_q_empty_cnt",
> @@ -1491,6 +1523,7 @@ const struct ieee80211_ops mt7915_ops = {
> .set_bitrate_mask = mt7915_set_bitrate_mask,
> .set_coverage_class = mt7915_set_coverage_class,
> .sta_statistics = mt7915_sta_statistics,
> + .sta_set_txpwr = mt7915_sta_set_txpwr,
> .sta_set_4addr = mt7915_sta_set_4addr,
> .sta_set_decap_offload = mt7915_sta_set_decap_offload,
> .add_twt_setup = mt7915_mac_add_twt_setup,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
> index 3a0d97dad96f..d911512f31a6 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
> @@ -3083,6 +3083,86 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state)
> &req, sizeof(req), false);
> }
>
> +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower)
> +{
> + struct mt7915_dev *dev = phy->dev;
> + struct {
> + u8 format_id;
> + u8 rsv;
> + u8 band_idx;
> + s8 txpower_min;
> + } __packed req = {
> + .format_id = TX_POWER_LIMIT_FRAME_MIN,
> + .band_idx = phy != &dev->phy,
> + .txpower_min = txpower * 2, /* 0.5db */
> + };
> +
> + return mt76_mcu_send_msg(&dev->mt76,
> + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
> + sizeof(req), true);
> +}
> +
> +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta, s8 txpower)
> +{
> + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
> + struct mt7915_dev *dev = phy->dev;
> + struct {
> + u8 format_id;
> + u8 rsv[3];
> + u8 band_idx;
> + s8 txpower_max;
> + __le16 wcid;
> + s8 txpower_offs[48];
> + } __packed req = {
> + .format_id = TX_POWER_LIMIT_FRAME,
> + .band_idx = phy != &dev->phy,
> + .txpower_max = DIV_ROUND_UP(phy->mt76->txpower_cur, 2),
> + .wcid = cpu_to_le16(msta->wcid.idx),
> + };
> + int ret;
> + s8 txpower_sku[MT7915_SKU_RATE_NUM];
> +
> + ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku));
> + if (ret)
> + return ret;
> +
> + if (txpower > req.txpower_max || txpower < 0)
> + return -EINVAL;
> +
> + if (txpower) {
> + u32 offs, len, i;
> +
> + if (sta->deflink.ht_cap.ht_supported) {
> + const u8 *sku_len = mt7915_sku_group_len;
> +
> + offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM];
> + len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40];
> +
> + if (sta->deflink.vht_cap.vht_supported) {
> + offs += len;
> + len = sku_len[SKU_VHT_BW20] * 4;
> +
> + if (sta->deflink.he_cap.has_he) {
> + offs += len + sku_len[SKU_HE_RU26] * 3;
> + len = sku_len[SKU_HE_RU242] * 4;
> + }
> + }
> + } else {
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < len; i++, offs++)
> + req.txpower_offs[i] =
> + txpower - DIV_ROUND_UP(txpower_sku[offs], 2);
> + }
> +
> + return mt76_mcu_send_msg(&dev->mt76,
> + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
> + sizeof(req), true);
> +}
> +
> int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
> {
> struct mt7915_dev *dev = phy->dev;
> @@ -3094,7 +3174,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
> u8 dbdc_idx;
> s8 val[MT7915_SKU_RATE_NUM];
> } __packed req = {
> - .format_id = 4,
> + .format_id = TX_POWER_LIMIT_TABLE,
> .dbdc_idx = phy != &dev->phy,
> };
> struct mt76_power_limits limits_array;
> @@ -3144,11 +3224,11 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
> u8 band;
> u8 _rsv;
> } __packed req = {
> - .format_id = 7,
> + .format_id = TX_POWER_LIMIT_INFO,
> .category = RATE_POWER_INFO,
> .band = phy != &dev->phy,
> };
> - s8 res[MT7915_SKU_RATE_NUM][2];
> + s8 txpower_sku[MT7915_SKU_RATE_NUM][2];
> struct sk_buff *skb;
> int ret, i;
>
> @@ -3158,9 +3238,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
> if (ret)
> return ret;
>
> - memcpy(res, skb->data + 4, sizeof(res));
> + memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku));
> for (i = 0; i < len; i++)
> - txpower[i] = res[i][req.band];
> + txpower[i] = txpower_sku[i][req.band_idx];
>
> dev_kfree_skb(skb);
>
> @@ -3198,7 +3278,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
> u8 dbdc_idx;
> u8 rsv;
> } __packed req = {
> - .format_id = 0,
> + .format_id = TX_POWER_LIMIT_ENABLE,
> .dbdc_idx = phy != &dev->phy,
> .sku_enable = enable,
> };
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
> index 2fc09fd53777..46c517e50ae4 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
> @@ -419,6 +419,14 @@ enum {
> #define RATE_CFG_PHY_TYPE GENMASK(27, 24)
> #define RATE_CFG_HE_LTF GENMASK(31, 28)
>
> +enum {
> + TX_POWER_LIMIT_ENABLE,
> + TX_POWER_LIMIT_TABLE = 0x4,
> + TX_POWER_LIMIT_INFO = 0x7,
> + TX_POWER_LIMIT_FRAME = 0x11,
> + TX_POWER_LIMIT_FRAME_MIN = 0x12,
> +};
> +
> enum {
> SPR_ENABLE = 0x1,
> SPR_ENABLE_SD = 0x3,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
> index 0bad78cf32c7..015c7190d79f 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
> @@ -526,6 +526,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
> int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
> int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
> int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
> +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower);
> +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta, s8 txpower);
> int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action);
> int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val);
> int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
> --
> 2.36.1
>
On Sun, 2022-11-20 at 22:21 +0100, Lorenzo Bianconi wrote:
> > This adds support for adjusting the Txpower level while pushing
> > traffic to an associated station. The allowed range is from 0 to
> > the maximum power of channel.
>
> Very cool, iiuc the hw/fw is capable of adjusting tx power according
> to the trasmitted
> frame, right? Is it possible to specify it on per-packet basis or
> just
> per-station?
Only for those fixed rate packets we can appy the offset through TxD,
whereas other data frames are hard to scale to per-packet basis but you
can continusly apply offsets for batch adjustment.
Ryder