2022-09-13 05:16:34

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 00/15] wifi: ath11k: add support for 6 GHz station for various modes : LPI, SP and VLP

v3:
1. added "ath11k: fix a possible dead lock caused by ab->base_lock".
3. deleted "ath11k: add support for extended wmi service bit" which is alreay upstream.

v2:
1. change some minor comments by Kalle.
2. rebased to ath.git ath-202112220603

Depends on one patch:
[v5] cfg80211: save power spectral density(psd) of regulatory rule
https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/

It introduced some new concept:
power type of AP(STANDARD_POWER_AP, INDOOR_AP, VERY_LOW_POWER_AP)
power type of STATION(DEFAULT_CLIENT, SUBORDINATE_CLIENT)
power spectral density(psd)

This patchset is to implement the new rules for 6 GHz band in
ath11k.

ath11k parsed the reg rules from new wmi event
WMI_REG_CHAN_LIST_CC_EXT_EVENTID and parse the
transmit power envelope element in beacon of AP
and then set new wmi cmd WMI_VDEV_SET_TPC_POWER_CMDID
to firmware when connect to 6G AP, also support backward
compatibility with firmware which not support new wmi
cmd WMI_VDEV_SET_TPC_POWER_CMDID.

Baochen Qiang (1):
wifi: ath11k: fix a possible dead lock caused by ab->base_lock

Wen Gong (14):
wifi: ath11k: Add support to parse new wmi event for 6 GHz regulatory
wifi: ath11k: add support to select 6 GHz Regulatory type
wifi: ath11k: allow only one interface up simultaneously for WCN6855
wifi: ath11k: store cur_regulatory_info for each radio
wifi: ath11k: update regulatory rules when interface added
wifi: ath11k: update regulatory rules when connect to AP on 6 GHz band
for station
wifi: ath11k: save power spectral density(psd) of regulatory rule
wifi: ath11k: add parse of transmit power envelope element
wifi: ath11k: save max tx power in vdev start response event from
firmware
wifi: ath11k: fill parameters for vdev_set_tpc_power wmi command
wifi: ath11k: add WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT service bit
wifi: ath11k: discard BSS_CHANGED_TXPOWER when EXT_TPC_REG_SUPPORT for
6 GHz
wifi: ath11k: add handler for WMI_VDEV_SET_TPC_POWER_CMDID
wifi: ath11k: send TPC power to firmware for 6 GHz station

drivers/net/wireless/ath/ath11k/core.c | 6 +
drivers/net/wireless/ath/ath11k/core.h | 40 ++
drivers/net/wireless/ath/ath11k/hw.c | 17 +
drivers/net/wireless/ath/ath11k/hw.h | 5 +
drivers/net/wireless/ath/ath11k/mac.c | 539 +++++++++++++++++-
drivers/net/wireless/ath/ath11k/mac.h | 5 +-
drivers/net/wireless/ath/ath11k/reg.c | 105 +++-
drivers/net/wireless/ath/ath11k/reg.h | 6 +-
drivers/net/wireless/ath/ath11k/wmi.c | 728 +++++++++++++++++++++++--
drivers/net/wireless/ath/ath11k/wmi.h | 210 +++++++
10 files changed, 1608 insertions(+), 53 deletions(-)


base-commit: e5da5e8c54e27d8fa86765cd733c1a05aee53ae9
prerequisite-patch-id: d0941cb1e08f82e9bd5feaf01b160807b4b0faa9
prerequisite-patch-id: 8ed14a45e891683ebc28d3fe1c9405430561b09f
--
2.31.1


2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 03/15] wifi: ath11k: allow only one interface up simultaneously for WCN6855

Currently ath11k support both station/AP mode for WCN6855, and it is
configured with single_pdev_only, it means it has only one ath11k
and one ieee80211_hw which registered in mac80211 and one wiphy
registered in cfg80211. Now it does not have requirement to start
up both station and AP interface simultaneously for WCN6855, this
is to disable station and AP concurrency mode.

After this patch, when station interface is up, then AP interface
can not start up. AP interface can start up after station interface
down. Also when AP interface is up, station interface can not start
up. station interface can start up after AP interface down.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/hw.c | 5 +++++
drivers/net/wireless/ath/ath11k/hw.h | 1 +
drivers/net/wireless/ath/ath11k/mac.c | 17 ++++++++++++++++-
3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index d503e8a73d32..712818edb4ca 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -807,6 +807,11 @@ bool ath11k_hw_supports_cc_ext(struct ath11k_base *ab)
test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, ab->wmi_ab.svc_map);
}

+bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar)
+{
+ return ath11k_hw_supports_cc_ext(ar->ab) && ar->supports_6ghz;
+}
+
const struct ath11k_hw_ops ipq8074_ops = {
.get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id,
.wmi_init_config = ath11k_init_wmi_config_ipq8074,
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index b57cab2b0c7b..969d29aaee6a 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -278,6 +278,7 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,
}

bool ath11k_hw_supports_cc_ext(struct ath11k_base *ab);
+bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar);

struct ath11k_fw_ie {
__le32 id;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 07f499d5ec92..7180ac37b327 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -8218,7 +8218,22 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)

combinations[0].limits = limits;
combinations[0].n_limits = n_limits;
- combinations[0].max_interfaces = 16;
+
+ /* When single pdev is set, there is only one ieee80211_hw/wiphy
+ * of mac80211/cfg80211, and it has only one reg rules stored
+ * The reg rules of 6 GHz is different for station and AP, please
+ * refer WMI_REG_CHAN_LIST_CC_EXT_EVENTID handler.
+ * When start station/AP simultaneously, there is not more
+ * struct to store the second reg rules in cfg80211.
+ * Also it does not have requirement for station/AP concurrency
+ * for WCN6855, so disable it currently.
+ */
+ if (ab->hw_params.single_pdev_only &&
+ ath11k_hw_supports_6g_cc_ext(ar))
+ combinations[0].max_interfaces = 1;
+ else
+ combinations[0].max_interfaces = 16;
+
combinations[0].num_different_channels = 1;
combinations[0].beacon_int_infra_match = true;
combinations[0].beacon_int_min_gcd = 100;
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 10/15] wifi: ath11k: save max tx power in vdev start response event from firmware

Save the max tx power received in the vdev start response event from
firmware. A subsequent patch will use this to calculate the final power
value for WMI_VDEV_SET_TPC_POWER_CMDID.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.h | 1 +
drivers/net/wireless/ath/ath11k/wmi.c | 3 ++-
drivers/net/wireless/ath/ath11k/wmi.h | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 506829febad7..01909035017d 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -641,6 +641,7 @@ struct ath11k {
struct completion finish_11d_ch_list;
bool pending_11d;
bool regdom_set_by_user;
+ s8 max_allowed_tx_power;
};

struct ath11k_band_cap {
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 8187b644078b..7d7751fd6a8e 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -4688,6 +4688,7 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf
vdev_rsp->mac_id = ev->mac_id;
vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams;
vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams;
+ vdev_rsp->max_allowed_tx_power = ev->max_allowed_tx_power;

kfree(tb);
return 0;
@@ -7115,7 +7116,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff
}

ar->last_wmi_vdev_start_status = 0;
-
+ ar->max_allowed_tx_power = vdev_start_resp.max_allowed_tx_power;
status = vdev_start_resp.status;

if (WARN_ON_ONCE(status)) {
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 754fef933cc7..2c50dc8c3834 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -4076,6 +4076,7 @@ struct wmi_vdev_start_resp_event {
};
u32 cfgd_tx_streams;
u32 cfgd_rx_streams;
+ s32 max_allowed_tx_power;
} __packed;

/* VDEV start response status codes */
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 06/15] wifi: ath11k: update regulatory rules when interface added

When wlan boot up, WMI_REG_CHAN_LIST_CC_EXT_EVENTID is sent from
firmware at an early stage, the interface mode is not decided at
this point, then ath11k select reg rules of AP mode as default.

After interface is created, it is exactly decided AP/station mode,
then ath11k need to update reg rules to the exact one.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/mac.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index ae7e40332cef..fc9556a1327e 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -6352,6 +6352,14 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
}
}

+ if (ath11k_hw_supports_6g_cc_ext(ar)) {
+ struct cur_regulatory_info *reg_info;
+
+ reg_info = &ab->reg_info_store[ar->pdev_idx];
+ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac interface added to change reg rules\n");
+ ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP);
+ }
+
mutex_unlock(&ar->conf_mutex);

return 0;
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 07/15] wifi: ath11k: update regulatory rules when connect to AP on 6 GHz band for station

When station connect to AP on 6 GHz band, it need switch the regulatory
rules according to the regulatory info subfield in HE operation element.
Swith to the power type of AP for station mode.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/mac.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index fc9556a1327e..a18b12abb63d 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -6998,6 +6998,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ath11k_vif *arvif = (void *)vif->drv_priv;
int ret;
struct peer_create_params param;
+ struct cur_regulatory_info *reg_info;
+ enum ieee80211_ap_reg_power power_type;

mutex_lock(&ar->conf_mutex);

@@ -7005,6 +7007,17 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
"mac chanctx assign ptr %pK vdev_id %i\n",
ctx, arvif->vdev_id);

+ if (ar->supports_6ghz && ctx->def.chan->band == NL80211_BAND_6GHZ &&
+ arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+ reg_info = &ab->reg_info_store[ar->pdev_idx];
+ power_type = vif->bss_conf.power_type;
+ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac chanctx power type %d\n",
+ power_type);
+ if (power_type == IEEE80211_REG_UNSET_AP)
+ power_type = IEEE80211_REG_LPI_AP;
+ ath11k_reg_handle_chan_list(ab, reg_info, power_type);
+ }
+
/* for QCA6390 bss peer must be created before vdev_start */
if (ab->hw_params.vdev_start_delay &&
arvif->vdev_type != WMI_VDEV_TYPE_AP &&
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 02/15] wifi: ath11k: add support to select 6 GHz Regulatory type

There are 3 types of regulatory rules for AP mode and 6 type for
station mode. Add wmi_vdev_type and ieee80211_ap_reg_power to
select the exact reg rules.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/reg.c | 61 ++++++--
drivers/net/wireless/ath/ath11k/reg.h | 6 +-
drivers/net/wireless/ath/ath11k/wmi.c | 192 +++++++++++++++++++++++++-
drivers/net/wireless/ath/ath11k/wmi.h | 25 +++-
4 files changed, 268 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 09825a41a284..5870cf57813c 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -583,16 +583,33 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
*rule_idx = i;
}

+enum wmi_reg_6g_ap_type
+ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type)
+{
+ switch (power_type) {
+ case IEEE80211_REG_LPI_AP:
+ return WMI_REG_INDOOR_AP;
+ case IEEE80211_REG_SP_AP:
+ return WMI_REG_STANDARD_POWER_AP;
+ case IEEE80211_REG_VLP_AP:
+ return WMI_REG_VERY_LOW_POWER_AP;
+ default:
+ return WMI_REG_MAX_AP_TYPE;
+ }
+}
+
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
- struct cur_regulatory_info *reg_info, bool intersect)
+ struct cur_regulatory_info *reg_info, bool intersect,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type)
{
struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
- struct cur_reg_rule *reg_rule;
+ struct cur_reg_rule *reg_rule, *reg_rule_6g;
u8 i = 0, j = 0, k = 0;
u8 num_rules;
u16 max_bw;
- u32 flags;
+ u32 flags, reg_6g_number, max_bw_6g;
char alpha2[3];

num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;
@@ -601,8 +618,32 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
* This can be updated to choose the combination dynamically based on AP
* type and client type, after complete 6 GHz regulatory support is added.
*/
- if (reg_info->is_ext_reg_event)
- num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP];
+ if (reg_info->is_ext_reg_event) {
+ if (vdev_type == WMI_VDEV_TYPE_STA) {
+ enum wmi_reg_6g_ap_type ap_type;
+
+ ap_type = ath11k_ieee80211_ap_pwr_type_convert(power_type);
+
+ if (ap_type == WMI_REG_MAX_AP_TYPE)
+ ap_type = WMI_REG_INDOOR_AP;
+ reg_6g_number = reg_info->num_6g_reg_rules_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ if (reg_6g_number == 0) {
+ ap_type = WMI_REG_INDOOR_AP;
+ reg_6g_number = reg_info->num_6g_reg_rules_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ }
+ reg_rule_6g = reg_info->reg_rules_6g_client_ptr
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ max_bw_6g = reg_info->max_bw_6g_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ } else {
+ reg_6g_number = reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP];
+ reg_rule_6g = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP];
+ max_bw_6g = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP];
+ }
+ num_rules += reg_6g_number;
+ }

if (!num_rules)
goto ret;
@@ -649,12 +690,10 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
* per other BW rule flags we pass from here
*/
flags = NL80211_RRF_AUTO_BW;
- } else if (reg_info->is_ext_reg_event &&
- reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] &&
- (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) {
- reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++;
- max_bw = min_t(u16, reg_rule->max_bw,
- reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]);
+ } else if (reg_info->is_ext_reg_event && reg_6g_number &&
+ (k < reg_6g_number)) {
+ reg_rule = reg_rule_6g + k++;
+ max_bw = min_t(u16, reg_rule->max_bw, max_bw_6g);
flags = NL80211_RRF_AUTO_BW;
} else {
break;
diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
index 5fb9dc03a74e..a7b9dd281598 100644
--- a/drivers/net/wireless/ath/ath11k/reg.h
+++ b/drivers/net/wireless/ath/ath11k/reg.h
@@ -30,7 +30,11 @@ void ath11k_reg_free(struct ath11k_base *ab);
void ath11k_regd_update_work(struct work_struct *work);
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
- struct cur_regulatory_info *reg_info, bool intersect);
+ struct cur_regulatory_info *reg_info, bool intersect,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type);
+enum wmi_reg_6g_ap_type
+ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type);
int ath11k_regd_update(struct ath11k *ar);
int ath11k_reg_update_chan_list(struct ath11k *ar);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 5f389aa18ebc..fa87cf0dc390 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -4725,6 +4725,26 @@ static struct cur_reg_rule
return reg_rule_ptr;
}

+static char *ath11k_cc_status_to_str(enum cc_setting_code code)
+{
+ switch (code) {
+ case REG_SET_CC_STATUS_PASS:
+ return "REG_SET_CC_STATUS_PASS";
+ case REG_CURRENT_ALPHA2_NOT_FOUND:
+ return "REG_CURRENT_ALPHA2_NOT_FOUND";
+ case REG_INIT_ALPHA2_NOT_FOUND:
+ return "REG_INIT_ALPHA2_NOT_FOUND";
+ case REG_SET_CC_CHANGE_NOT_ALLOWED:
+ return "REG_SET_CC_CHANGE_NOT_ALLOWED";
+ case REG_SET_CC_STATUS_NO_MEMORY:
+ return "REG_SET_CC_STATUS_NO_MEMORY";
+ case REG_SET_CC_STATUS_FAIL:
+ return "REG_SET_CC_STATUS_FAIL";
+ default:
+ return "unknown cc status";
+ }
+};
+
static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
struct sk_buff *skb,
struct cur_regulatory_info *reg_info)
@@ -4791,6 +4811,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
num_2g_reg_rules = reg_info->num_2g_reg_rules;
num_5g_reg_rules = reg_info->num_5g_reg_rules;

+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "%s: status_code %s", __func__,
+ ath11k_cc_status_to_str(reg_info->status_code));
+
ath11k_dbg(ab, ATH11K_DBG_WMI,
"%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d",
__func__, reg_info->alpha2, reg_info->dfs_region,
@@ -4833,6 +4857,99 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
return 0;
}

+static char *ath11k_super_reg_6g_to_str(enum reg_super_domain_6g domain_id)
+{
+ switch (domain_id) {
+ case FCC1_6G:
+ return "FCC1_6G";
+ case ETSI1_6G:
+ return "ETSI1_6G";
+ case ETSI2_6G:
+ return "ETSI2_6G";
+ case APL1_6G:
+ return "APL1_6G";
+ case FCC1_6G_CL:
+ return "FCC1_6G_CL";
+ default:
+ return "unknown domain id";
+ }
+};
+
+static char *ath11k_6g_client_type_to_str(enum wmi_reg_6g_client_type type)
+{
+ switch (type) {
+ case WMI_REG_DEFAULT_CLIENT:
+ return "DEFAULT CLIENT";
+ case WMI_REG_SUBORDINATE_CLIENT:
+ return "SUBORDINATE CLIENT";
+ default:
+ return "unknown client type";
+ }
+};
+
+static char *ath11k_6g_ap_type_to_str(enum wmi_reg_6g_ap_type type)
+{
+ switch (type) {
+ case WMI_REG_INDOOR_AP:
+ return "INDOOR AP";
+ case WMI_REG_STANDARD_POWER_AP:
+ return "STANDARD POWER AP";
+ case WMI_REG_VERY_LOW_POWER_AP:
+ return "VERY LOW POWER AP";
+ default:
+ return "unknown AP type";
+ }
+};
+
+static char *ath11k_sub_reg_6g_to_str(enum reg_subdomains_6g sub_id)
+{
+ switch (sub_id) {
+ case FCC1_CLIENT_LPI_REGULAR_6G:
+ return "FCC1_CLIENT_LPI_REGULAR_6G";
+ case FCC1_CLIENT_SP_6G:
+ return "FCC1_CLIENT_SP_6G";
+ case FCC1_AP_LPI_6G:
+ return "FCC1_AP_LPI_6G/FCC1_CLIENT_LPI_SUBORDINATE";
+ case FCC1_AP_SP_6G:
+ return "FCC1_AP_SP_6G";
+ case ETSI1_LPI_6G:
+ return "ETSI1_LPI_6G";
+ case ETSI1_VLP_6G:
+ return "ETSI1_VLP_6G";
+ case ETSI2_LPI_6G:
+ return "ETSI2_LPI_6G";
+ case ETSI2_VLP_6G:
+ return "ETSI2_VLP_6G";
+ case APL1_LPI_6G:
+ return "APL1_LPI_6G";
+ case APL1_VLP_6G:
+ return "APL1_VLP_6G";
+ case EMPTY_6G:
+ return "N/A";
+ default:
+ return "unknown sub reg id";
+ }
+};
+
+static void ath11k_print_reg_rule(struct ath11k_base *ab, char *prev,
+ u32 num_reg_rules,
+ struct cur_reg_rule *reg_rule_ptr)
+{
+ struct cur_reg_rule *reg_rule = reg_rule_ptr;
+ u32 count;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "%s reg rules number %d\n", prev, num_reg_rules);
+
+ for (count = 0; count < num_reg_rules; count++) {
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "reg rule %d: (%d - %d @ %d) (%d, %d) (FLAGS %d) (psd flag %d EIRP %d dB/MHz)\n",
+ count + 1, reg_rule->start_freq, reg_rule->end_freq,
+ reg_rule->max_bw, reg_rule->ant_gain, reg_rule->reg_power,
+ reg_rule->flags, reg_rule->psd_flag, reg_rule->psd_eirp);
+ reg_rule++;
+ }
+}
+
static struct cur_reg_rule
*create_ext_reg_rules_from_wmi(u32 num_reg_rules,
const struct wmi_regulatory_ext_rule *wmi_reg_rule)
@@ -4990,6 +5107,10 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
* from 5 GHz rules list.
*/
if (memcmp(reg_info->alpha2, "US", 2) == 0) {
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "US 5g reg rules number %d from firmware",
+ reg_info->num_5g_reg_rules);
+
reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES;
num_5g_reg_rules = reg_info->num_5g_reg_rules;
}
@@ -5022,6 +5143,10 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
break;
}

+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "%s: status_code %s", __func__,
+ ath11k_cc_status_to_str(reg_info->status_code));
+
reg_info->is_ext_reg_event = true;

reg_info->min_bw_2g = ext_chan_list_event_hdr->min_bw_2g;
@@ -5041,6 +5166,15 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
reg_info->max_bw_6g_ap[WMI_REG_VERY_LOW_POWER_AP] =
ext_chan_list_event_hdr->max_bw_6g_ap_vlp;

+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz AP BW: lpi %d - %d sp %d - %d vlp %d - %d\n",
+ ext_chan_list_event_hdr->min_bw_6g_ap_lpi,
+ ext_chan_list_event_hdr->max_bw_6g_ap_lpi,
+ ext_chan_list_event_hdr->min_bw_6g_ap_sp,
+ ext_chan_list_event_hdr->max_bw_6g_ap_sp,
+ ext_chan_list_event_hdr->min_bw_6g_ap_vlp,
+ ext_chan_list_event_hdr->max_bw_6g_ap_vlp);
+
for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
reg_info->min_bw_6g_client[WMI_REG_INDOOR_AP][i] =
ext_chan_list_event_hdr->min_bw_6g_client_lpi[i];
@@ -5054,6 +5188,17 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
ext_chan_list_event_hdr->min_bw_6g_client_vlp[i];
reg_info->max_bw_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
ext_chan_list_event_hdr->max_bw_6g_client_vlp[i];
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz %s BW: lpi (%d - %d) sp (%d - %d) vlp (%d - %d)\n",
+ ath11k_6g_client_type_to_str(i),
+ ext_chan_list_event_hdr->min_bw_6g_client_lpi[i],
+ ext_chan_list_event_hdr->max_bw_6g_client_lpi[i],
+ ext_chan_list_event_hdr->min_bw_6g_client_sp[i],
+ ext_chan_list_event_hdr->max_bw_6g_client_sp[i],
+ ext_chan_list_event_hdr->min_bw_6g_client_vlp[i],
+ ext_chan_list_event_hdr->max_bw_6g_client_vlp[i]);
+
}

ath11k_dbg(ab, ATH11K_DBG_WMI,
@@ -5096,6 +5241,9 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n");
return -ENOMEM;
}
+ ath11k_print_reg_rule(ab, "2g",
+ num_2g_reg_rules,
+ reg_info->reg_rules_2g_ptr);
}

if (num_5g_reg_rules) {
@@ -5109,6 +5257,9 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n");
return -ENOMEM;
}
+ ath11k_print_reg_rule(ab, "5g",
+ num_5g_reg_rules,
+ reg_info->reg_rules_5g_ptr);
}

ext_wmi_reg_rule += num_5g_reg_rules;
@@ -5124,10 +5275,17 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
return -ENOMEM;
}

+ ath11k_print_reg_rule(ab, ath11k_6g_ap_type_to_str(i),
+ num_6g_reg_rules_ap[i],
+ reg_info->reg_rules_6g_ap_ptr[i]);
+
ext_wmi_reg_rule += num_6g_reg_rules_ap[i];
}

for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) {
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "AP type %s", ath11k_6g_ap_type_to_str(j));
+
for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
reg_info->reg_rules_6g_client_ptr[j][i] =
create_ext_reg_rules_from_wmi(num_6g_client[j][i],
@@ -5139,6 +5297,10 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
return -ENOMEM;
}

+ ath11k_print_reg_rule(ab, ath11k_6g_client_type_to_str(i),
+ num_6g_client[j][i],
+ reg_info->reg_rules_6g_client_ptr[j][i]);
+
ext_wmi_reg_rule += num_6g_client[j][i];
}
}
@@ -5154,6 +5316,18 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
reg_info->domain_code_6g_ap[WMI_REG_VERY_LOW_POWER_AP] =
ext_chan_list_event_hdr->domain_code_6g_ap_vlp;

+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz reg info client type %s rnr_tpe_usable %d unspecified_ap_usable %d AP sub domain: lpi %s , sp %s , vlp %s\n",
+ ath11k_6g_client_type_to_str(reg_info->client_type),
+ reg_info->rnr_tpe_usable,
+ reg_info->unspecified_ap_usable,
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_ap_lpi),
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_ap_sp),
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_ap_vlp));
+
for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
reg_info->domain_code_6g_client[WMI_REG_INDOOR_AP][i] =
ext_chan_list_event_hdr->domain_code_6g_client_lpi[i];
@@ -5161,13 +5335,24 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
ext_chan_list_event_hdr->domain_code_6g_client_sp[i];
reg_info->domain_code_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
ext_chan_list_event_hdr->domain_code_6g_client_vlp[i];
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz client type %s client sub domain: lpi %s , sp %s , vlp %s\n",
+ ath11k_6g_client_type_to_str(i),
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_client_lpi[i]),
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_client_sp[i]),
+ ath11k_sub_reg_6g_to_str
+ (ext_chan_list_event_hdr->domain_code_6g_client_vlp[i]));
}

reg_info->domain_code_6g_super_id =
ext_chan_list_event_hdr->domain_code_6g_super_id;

- ath11k_dbg(ab, ATH11K_DBG_WMI, "6 GHz client_type: %d domain_code_6g_super_id: %d",
- reg_info->client_type, reg_info->domain_code_6g_super_id);
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "6 GHz client type %s 6 GHz super domain %s",
+ ath11k_6g_client_type_to_str(reg_info->client_type),
+ ath11k_super_reg_6g_to_str(reg_info->domain_code_6g_super_id));

ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n");

@@ -6680,7 +6865,8 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
!ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
intersect = true;

- regd = ath11k_reg_build_regd(ab, reg_info, intersect);
+ regd = ath11k_reg_build_regd(ab, reg_info, intersect,
+ WMI_VDEV_TYPE_AP, IEEE80211_REG_UNSET_AP);
if (!regd) {
ath11k_warn(ab, "failed to build regd from reg_info\n");
goto fallback;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 02c518e95121..9d36f5774fa9 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -4142,7 +4142,30 @@ struct cur_reg_rule {
u8 ant_gain;
u16 flags;
bool psd_flag;
- u16 psd_eirp;
+ s8 psd_eirp;
+};
+
+enum reg_subdomains_6g {
+ EMPTY_6G = 0x0,
+ FCC1_CLIENT_LPI_REGULAR_6G = 0x01,
+ FCC1_CLIENT_SP_6G = 0x02,
+ FCC1_AP_LPI_6G = 0x03,
+ FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6G,
+ FCC1_AP_SP_6G = 0x04,
+ ETSI1_LPI_6G = 0x10,
+ ETSI1_VLP_6G = 0x11,
+ ETSI2_LPI_6G = 0x12,
+ ETSI2_VLP_6G = 0x13,
+ APL1_LPI_6G = 0x20,
+ APL1_VLP_6G = 0x21,
+};
+
+enum reg_super_domain_6g {
+ FCC1_6G = 0x01,
+ ETSI1_6G = 0x02,
+ ETSI2_6G = 0x03,
+ APL1_6G = 0x04,
+ FCC1_6G_CL = 0x05,
};

struct cur_regulatory_info {
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 09/15] wifi: ath11k: add parse of transmit power envelope element

The transmit power envelope element has some fields for power, ath11k
should parse it according to IEEE Std 802.11ax™‐2021.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.h | 38 +++++
drivers/net/wireless/ath/ath11k/mac.c | 185 +++++++++++++++++++++++++
2 files changed, 223 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 681a58aaa26b..506829febad7 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -212,6 +212,43 @@ enum ath11k_monitor_flags {
ATH11K_FLAG_MONITOR_VDEV_CREATED,
};

+/**
+ * struct chan_power_info - TPE containing power info per channel chunk
+ * @chan_cfreq: channel center freq (MHz)
+ * e.g.
+ * channel 37/20 MHz, it is 6135
+ * channel 37/40 MHz, it is 6125
+ * channel 37/80 MHz, it is 6145
+ * channel 37/160 MHz, it is 6185
+ * @tx_power: transmit power (dBm)
+ */
+struct chan_power_info {
+ u16 chan_cfreq;
+ s8 tx_power;
+};
+
+/**
+ * struct reg_tpc_power_info - regulatory TPC power info
+ * @is_psd_power: is PSD power or not
+ * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD
+ * @power_type_6g: type of power (SP/LPI/VLP)
+ * @num_pwr_levels: number of power levels
+ * @reg_max: Array of maximum TX power (dBm) per PSD value
+ * @ap_constraint_power: AP constraint power (dBm)
+ * @tpe: TPE values processed from TPE IE
+ * @chan_power_info: power info to send to firmware
+ */
+struct ath11k_reg_tpc_power_info {
+ bool is_psd_power;
+ u8 eirp_power;
+ enum wmi_reg_6g_ap_type power_type_6g;
+ u8 num_pwr_levels;
+ u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL];
+ u8 ap_constraint_power;
+ s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL];
+ struct chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL];
+};
+
struct ath11k_vif {
u32 vdev_id;
enum wmi_vdev_type vdev_type;
@@ -263,6 +300,7 @@ struct ath11k_vif {
bool bcca_zero_sent;
bool do_not_send_tmpl;
struct ieee80211_chanctx_conf chanctx;
+ struct ath11k_reg_tpc_power_info reg_tpc_info;
};

struct ath11k_vif_iter {
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index a18b12abb63d..6eb707473ed3 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -6988,6 +6988,189 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
return 0;
}

+static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
+{
+ switch (txpwr_intrprt) {
+ /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield
+ * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of
+ * "IEEE Std 802.11ax 2021".
+ */
+ case IEEE80211_TPE_LOCAL_EIRP:
+ case IEEE80211_TPE_REG_CLIENT_EIRP:
+ txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3;
+ txpwr_cnt = txpwr_cnt + 1;
+ break;
+ /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield
+ * if Maximum Transmit Power Interpretation subfield is 1 or 3" of
+ * "IEEE Std 802.11ax 2021".
+ */
+ case IEEE80211_TPE_LOCAL_EIRP_PSD:
+ case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
+ txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4;
+ txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1;
+ break;
+ }
+
+ return txpwr_cnt;
+}
+
+static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
+{
+ if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_20:
+ return 1;
+ case NL80211_CHAN_WIDTH_40:
+ return 2;
+ case NL80211_CHAN_WIDTH_80:
+ return 4;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ return 8;
+ default:
+ return 1;
+ }
+ } else {
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_20:
+ return 1;
+ case NL80211_CHAN_WIDTH_40:
+ return 2;
+ case NL80211_CHAN_WIDTH_80:
+ return 3;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ return 4;
+ default:
+ return 1;
+ }
+ }
+}
+
+static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ struct ieee80211_tx_pwr_env *single_tpe;
+ enum wmi_reg_6g_client_type client_type;
+ struct cur_regulatory_info *reg_info;
+ int i;
+ u8 pwr_count, pwr_interpret, pwr_category;
+ u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0;
+ bool use_local_tpe, non_psd_set = false, psd_set = false;
+
+ reg_info = &ab->reg_info_store[ar->pdev_idx];
+ client_type = reg_info->client_type;
+
+ for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+ single_tpe = &bss_conf->tx_pwr_env[i];
+ pwr_category = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+
+ if (pwr_category == client_type) {
+ if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP ||
+ pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD)
+ local_tpe_count++;
+ else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP ||
+ pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD)
+ reg_tpe_count++;
+ }
+ }
+
+ if (!reg_tpe_count && !local_tpe_count) {
+ ath11k_warn(ab,
+ "no transmit power envelope match client power type %d\n",
+ client_type);
+ return;
+ } else if (!reg_tpe_count) {
+ use_local_tpe = true;
+ } else {
+ use_local_tpe = false;
+ }
+
+ for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+ single_tpe = &bss_conf->tx_pwr_env[i];
+ pwr_category = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+
+ if (pwr_category != client_type)
+ continue;
+
+ /* get local transmit power envelope */
+ if (use_local_tpe) {
+ if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) {
+ non_psd_index = i;
+ non_psd_set = true;
+ } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) {
+ psd_index = i;
+ psd_set = true;
+ }
+ /* get regulatory transmit power envelope */
+ } else {
+ if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) {
+ non_psd_index = i;
+ non_psd_set = true;
+ } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) {
+ psd_index = i;
+ psd_set = true;
+ }
+ }
+ }
+
+ if (non_psd_set && !psd_set) {
+ single_tpe = &bss_conf->tx_pwr_env[non_psd_index];
+ pwr_count = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_COUNT);
+ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+ arvif->reg_tpc_info.is_psd_power = false;
+ arvif->reg_tpc_info.eirp_power = 0;
+
+ arvif->reg_tpc_info.num_pwr_levels =
+ ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+ ath11k_dbg(ab, ATH11K_DBG_MAC,
+ "non PSD power[%d] : %d\n",
+ i, single_tpe->tx_power[i]);
+ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+ }
+ }
+
+ if (psd_set) {
+ single_tpe = &bss_conf->tx_pwr_env[psd_index];
+ pwr_count = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_COUNT);
+ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+ arvif->reg_tpc_info.is_psd_power = true;
+
+ if (pwr_count == 0) {
+ ath11k_dbg(ab, ATH11K_DBG_MAC,
+ "TPE PSD power : %d\n", single_tpe->tx_power[0]);
+ arvif->reg_tpc_info.num_pwr_levels =
+ ath11k_mac_get_num_pwr_levels(&ctx->def);
+ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++)
+ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2;
+ } else {
+ arvif->reg_tpc_info.num_pwr_levels =
+ ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+ ath11k_dbg(ab, ATH11K_DBG_MAC,
+ "TPE PSD power[%d] : %d\n",
+ i, single_tpe->tx_power[i]);
+ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+ }
+ }
+ }
+}
+
static int
ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -7016,6 +7199,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
if (power_type == IEEE80211_REG_UNSET_AP)
power_type = IEEE80211_REG_LPI_AP;
ath11k_reg_handle_chan_list(ab, reg_info, power_type);
+
+ ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
}

/* for QCA6390 bss peer must be created before vdev_start */
--
2.31.1

2022-09-13 05:16:59

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 08/15] wifi: ath11k: save power spectral density(psd) of regulatory rule

Save the power spectral density(psd) report from firmware to struct
ieee80211_reg_rule.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/reg.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index f23a351d3f1e..20aaa1ea3d2a 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -389,6 +389,10 @@ static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,

/* Use the flags of both the rules */
new_rule->flags = rule1->flags | rule2->flags;
+ if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD))
+ new_rule->psd = min_t(s8, rule1->psd, rule2->psd);
+ else
+ new_rule->flags &= ~NL80211_RRF_PSD;

/* To be safe, lts use the max cac timeout of both rules */
new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
@@ -492,13 +496,14 @@ ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
static void
ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
- u32 reg_flags)
+ s8 psd, u32 reg_flags)
{
reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
+ reg_rule->psd = psd;
reg_rule->flags = reg_flags;
}

@@ -528,7 +533,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
reg_rule->start_freq,
ETSI_WEATHER_RADAR_BAND_LOW, bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);

ath11k_dbg(ab, ATH11K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -549,7 +554,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,

ath11k_reg_update_rule(regd->reg_rules + i, start_freq,
end_freq, bw, reg_rule->ant_gain,
- reg_rule->reg_power, flags);
+ reg_rule->reg_power, reg_rule->psd_eirp, flags);

regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;

@@ -570,7 +575,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
ETSI_WEATHER_RADAR_BAND_HIGH,
reg_rule->end_freq, bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);

ath11k_dbg(ab, ATH11K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
@@ -695,6 +700,8 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
reg_rule = reg_rule_6g + k++;
max_bw = min_t(u16, reg_rule->max_bw, max_bw_6g);
flags = NL80211_RRF_AUTO_BW;
+ if (reg_rule->psd_flag)
+ flags |= NL80211_RRF_PSD;
} else {
break;
}
@@ -705,7 +712,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
reg_rule->start_freq,
reg_rule->end_freq, max_bw,
reg_rule->ant_gain, reg_rule->reg_power,
- flags);
+ reg_rule->psd_eirp, flags);

/* Update dfs cac timeout if the dfs domain is ETSI and the
* new rule covers weather radar band.
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 04/15] wifi: ath11k: store cur_regulatory_info for each radio

The regulatory info of WMI_REG_CHAN_LIST_CC_EXT_EVENTID is not saved
in ath11k now, the info should be saved in ath11k. Save the info for
each radio and support switch regulatory rules dynamically.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.h | 1 +
drivers/net/wireless/ath/ath11k/mac.c | 11 +++
drivers/net/wireless/ath/ath11k/mac.h | 2 +-
drivers/net/wireless/ath/ath11k/reg.c | 6 ++
drivers/net/wireless/ath/ath11k/wmi.c | 120 +++++++++++++++++--------
drivers/net/wireless/ath/ath11k/wmi.h | 5 ++
6 files changed, 105 insertions(+), 40 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 9e88ccca5ca7..681a58aaa26b 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -763,6 +763,7 @@ struct ath11k_base {
* This may or may not be used during the runtime
*/
struct ieee80211_regdomain *new_regd[MAX_RADIOS];
+ struct cur_regulatory_info *reg_info_store;

/* Current DFS Regulatory */
enum ath11k_dfs_region dfs_region;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 7180ac37b327..ae7e40332cef 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -618,6 +618,17 @@ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id)
return NULL;
}

+enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ return arvif->vdev_type;
+ }
+
+ return WMI_VDEV_TYPE_UNSPEC;
+}
+
struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id)
{
int i;
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 0e6c870b09c8..eb8273ffc2a7 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -159,7 +159,7 @@ struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab);

struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id);
struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id);
-
+enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar);
void ath11k_mac_drain_tx(struct ath11k *ar);
void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 5870cf57813c..f23a351d3f1e 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -788,6 +788,12 @@ void ath11k_reg_free(struct ath11k_base *ab)
{
int i;

+ for (i = 0; i < ab->num_radios; i++)
+ ath11k_reg_reset_info(&ab->reg_info_store[i]);
+
+ kfree(ab->reg_info_store);
+ ab->reg_info_store = NULL;
+
for (i = 0; i < ab->hw_params.max_radios; i++) {
kfree(ab->default_regd[i]);
kfree(ab->new_regd[i]);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index fa87cf0dc390..d2d0873fda83 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -4408,6 +4408,11 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
soc->pdevs[0].pdev_id = 0;
}

+ if (!soc->reg_info_store)
+ soc->reg_info_store = kcalloc(soc->num_radios,
+ sizeof(*soc->reg_info_store),
+ GFP_ATOMIC);
+
return 0;
}

@@ -6787,31 +6792,34 @@ static bool ath11k_reg_is_world_alpha(char *alpha)
return false;
}

-static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
- struct sk_buff *skb,
- enum wmi_reg_chan_list_cmd_type id)
+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info)
{
- struct cur_regulatory_info *reg_info = NULL;
- struct ieee80211_regdomain *regd = NULL;
- bool intersect = false;
- int ret = 0, pdev_idx, i, j;
- struct ath11k *ar;
+ int i, j;

- reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
- if (!reg_info) {
- ret = -ENOMEM;
- goto fallback;
- }
+ if (reg_info) {
+ kfree(reg_info->reg_rules_2g_ptr);

- if (id == WMI_REG_CHAN_LIST_CC_ID)
- ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
- else
- ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+ kfree(reg_info->reg_rules_5g_ptr);

- if (ret) {
- ath11k_warn(ab, "failed to extract regulatory info from received event\n");
- goto fallback;
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+ kfree(reg_info->reg_rules_6g_ap_ptr[i]);
+ for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++)
+ kfree(reg_info->reg_rules_6g_client_ptr[i][j]);
+ }
+
+ memset(reg_info, 0, sizeof(*reg_info));
}
+}
+
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+ struct cur_regulatory_info *reg_info,
+ enum ieee80211_ap_reg_power power_type)
+{
+ struct ieee80211_regdomain *regd;
+ bool intersect = false;
+ int pdev_idx;
+ struct ath11k *ar;
+ enum wmi_vdev_type vdev_type;

if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
/* In case of failure to set the requested ctry,
@@ -6819,7 +6827,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
* and return from here.
*/
ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n");
- goto mem_free;
+ return -EINVAL;
}

pdev_idx = reg_info->phy_id;
@@ -6831,7 +6839,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
ab->default_regd[pdev_idx]) {
spin_unlock(&ab->base_lock);
- goto mem_free;
+ goto retfail;
}
spin_unlock(&ab->base_lock);

@@ -6842,7 +6850,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
*/
if (ab->hw_params.single_pdev_only &&
pdev_idx < ab->hw_params.num_rxmda_per_pdev)
- goto mem_free;
+ goto retfail;
else
goto fallback;
}
@@ -6853,7 +6861,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
!memcmp((char *)ab->default_regd[pdev_idx]->alpha2,
(char *)reg_info->alpha2, 2))
- goto mem_free;
+ goto retfail;

/* Intersect new rules with default regd if a new country setting was
* requested, i.e a default regd was already set during initialization
@@ -6865,13 +6873,24 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
!ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
intersect = true;

- regd = ath11k_reg_build_regd(ab, reg_info, intersect,
- WMI_VDEV_TYPE_AP, IEEE80211_REG_UNSET_AP);
+ ar = ab->pdevs[pdev_idx].ar;
+ vdev_type = ath11k_mac_get_ar_vdev_type(ar);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi handle chan list power type %d vdev type %d intersect %d\n",
+ power_type, vdev_type, intersect);
+
+ regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type);
if (!regd) {
ath11k_warn(ab, "failed to build regd from reg_info\n");
goto fallback;
}

+ if (power_type == IEEE80211_REG_UNSET_AP) {
+ ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]);
+ ab->reg_info_store[pdev_idx] = *reg_info;
+ }
+
spin_lock(&ab->base_lock);
if (ab->default_regd[pdev_idx]) {
/* The initial rules from FW after WMI Init is to build
@@ -6894,7 +6913,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
ab->dfs_region = reg_info->dfs_region;
spin_unlock(&ab->base_lock);

- goto mem_free;
+ return 0;

fallback:
/* Fallback to older reg (by sending previous country setting
@@ -6906,20 +6925,43 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
*/
/* TODO: This is rare, but still should also be handled */
WARN_ON(1);
-mem_free:
- if (reg_info) {
- kfree(reg_info->reg_rules_2g_ptr);
- kfree(reg_info->reg_rules_5g_ptr);
- if (reg_info->is_ext_reg_event) {
- for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++)
- kfree(reg_info->reg_rules_6g_ap_ptr[i]);
+retfail:

- for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++)
- for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++)
- kfree(reg_info->reg_rules_6g_client_ptr[j][i]);
- }
- kfree(reg_info);
+ return -EINVAL;
+}
+
+static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb,
+ enum wmi_reg_chan_list_cmd_type id)
+{
+ struct cur_regulatory_info *reg_info;
+ int ret;
+
+ reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
+ if (!reg_info)
+ return -ENOMEM;
+
+ if (id == WMI_REG_CHAN_LIST_CC_ID)
+ ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+ else
+ ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+
+ if (ret) {
+ ath11k_warn(ab, "failed to extract regulatory info from received event\n");
+ goto mem_free;
}
+
+ ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP);
+ if (ret) {
+ ath11k_warn(ab, "failed to process regulatory info from received event\n");
+ goto mem_free;
+ }
+
+ kfree(reg_info);
+ return 0;
+
+mem_free:
+ ath11k_reg_reset_info(reg_info);
+ kfree(reg_info);
return ret;
}

diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 9d36f5774fa9..754fef933cc7 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -4761,6 +4761,7 @@ struct ath11k_targ_cap {
};

enum wmi_vdev_type {
+ WMI_VDEV_TYPE_UNSPEC = 0,
WMI_VDEV_TYPE_AP = 1,
WMI_VDEV_TYPE_STA = 2,
WMI_VDEV_TYPE_IBSS = 3,
@@ -5728,4 +5729,8 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar);
int ath11k_wmi_wow_enable(struct ath11k *ar);
int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
const u8 mac_addr[ETH_ALEN]);
+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+ struct cur_regulatory_info *reg_info,
+ enum ieee80211_ap_reg_power power_type);
#endif
--
2.31.1

2022-09-13 05:16:58

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 01/15] wifi: ath11k: Add support to parse new wmi event for 6 GHz regulatory

In order to support different power levels of 6 GHz AP and client, new
wmi event for regulatory was added in firmware(WMI_REG_CHAN_LIST_CC_EXT_EVENTID)
to provide new parameters required for 6 GHz regulatory rules.

firmware advertises its capability of handling new event in wmi service ready
event. Based on that, host needs to set host_service_flags in wmi init
command to indicate that host supports processing of new wmi event.
Based on advertised host capability, firmware decides to send old event
(WMI_REG_CHAN_LIST_CC_EVENTID) or new event(WMI_REG_CHAN_LIST_CC_EXT_EVENTID).

Add support for parsing 2.4 GHz/5 GHz/6 GHz reg rules and other parameters from
WMI_REG_CHAN_LIST_CC_EXT_EVENTID, to populate the channel lists.
Since 6 GHz requires additional power value fields(PSD info), update
reg rule parsing function.

Signed-off-by: Lavanya Suresh <[email protected]>
Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.c | 6 +
drivers/net/wireless/ath/ath11k/hw.c | 6 +
drivers/net/wireless/ath/ath11k/hw.h | 3 +
drivers/net/wireless/ath/ath11k/reg.c | 37 ++-
drivers/net/wireless/ath/ath11k/wmi.c | 378 ++++++++++++++++++++++++-
drivers/net/wireless/ath/ath11k/wmi.h | 118 ++++++++
6 files changed, 537 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 293563b3f784..cbfae45e600d 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -88,6 +88,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = false,
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
.supports_regdb = false,
+ .supports_cc_ext = false,
.fix_l1ss = true,
.credit_flow = false,
.max_tx_ring = DP_TCL_NUM_RING_MAX,
@@ -152,6 +153,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = false,
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
.supports_regdb = false,
+ .supports_cc_ext = false,
.fix_l1ss = true,
.credit_flow = false,
.max_tx_ring = DP_TCL_NUM_RING_MAX,
@@ -215,6 +217,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = true,
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
.supports_regdb = true,
+ .supports_cc_ext = false,
.fix_l1ss = true,
.credit_flow = true,
.max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390,
@@ -278,6 +281,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = false,
.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
.supports_regdb = false,
+ .supports_cc_ext = false,
.fix_l1ss = true,
.credit_flow = false,
.max_tx_ring = DP_TCL_NUM_RING_MAX,
@@ -341,6 +345,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = true,
.hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855),
.supports_regdb = true,
+ .supports_cc_ext = true,
.fix_l1ss = false,
.credit_flow = true,
.max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390,
@@ -403,6 +408,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_suspend = true,
.hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855),
.supports_regdb = true,
+ .supports_cc_ext = true,
.fix_l1ss = false,
.credit_flow = true,
.max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390,
diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index 3b0fdc1a6b3f..d503e8a73d32 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -801,6 +801,12 @@ static u16 ath11k_hw_wcn6855_mpdu_info_get_peerid(u8 *tlv_data)
return peer_id;
}

+bool ath11k_hw_supports_cc_ext(struct ath11k_base *ab)
+{
+ return ab->hw_params.supports_cc_ext &&
+ test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, ab->wmi_ab.svc_map);
+}
+
const struct ath11k_hw_ops ipq8074_ops = {
.get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id,
.wmi_init_config = ath11k_init_wmi_config_ipq8074,
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 29934b36c14e..b57cab2b0c7b 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -183,6 +183,7 @@ struct ath11k_hw_params {
bool supports_suspend;
u32 hal_desc_sz;
bool supports_regdb;
+ bool supports_cc_ext;
bool fix_l1ss;
bool credit_flow;
u8 max_tx_ring;
@@ -276,6 +277,8 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,
return 0;
}

+bool ath11k_hw_supports_cc_ext(struct ath11k_base *ab);
+
struct ath11k_fw_ie {
__le32 id;
__le32 len;
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 1f8a81987187..09825a41a284 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -589,7 +589,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
{
struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
struct cur_reg_rule *reg_rule;
- u8 i = 0, j = 0;
+ u8 i = 0, j = 0, k = 0;
u8 num_rules;
u16 max_bw;
u32 flags;
@@ -597,6 +597,13 @@ ath11k_reg_build_regd(struct ath11k_base *ab,

num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;

+ /* FIXME: Currently taking reg rules for 6 GHz only from Indoor AP mode list.
+ * This can be updated to choose the combination dynamically based on AP
+ * type and client type, after complete 6 GHz regulatory support is added.
+ */
+ if (reg_info->is_ext_reg_event)
+ num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP];
+
if (!num_rules)
goto ret;

@@ -642,6 +649,13 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
* per other BW rule flags we pass from here
*/
flags = NL80211_RRF_AUTO_BW;
+ } else if (reg_info->is_ext_reg_event &&
+ reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] &&
+ (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) {
+ reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++;
+ max_bw = min_t(u16, reg_rule->max_bw,
+ reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]);
+ flags = NL80211_RRF_AUTO_BW;
} else {
break;
}
@@ -669,12 +683,21 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
continue;
}

- ath11k_dbg(ab, ATH11K_DBG_REG,
- "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
- i + 1, reg_rule->start_freq, reg_rule->end_freq,
- max_bw, reg_rule->ant_gain, reg_rule->reg_power,
- tmp_regd->reg_rules[i].dfs_cac_ms,
- flags);
+ if (reg_info->is_ext_reg_event) {
+ ath11k_dbg(ab, ATH11K_DBG_REG,
+ "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n",
+ i + 1, reg_rule->start_freq, reg_rule->end_freq,
+ max_bw, reg_rule->ant_gain, reg_rule->reg_power,
+ tmp_regd->reg_rules[i].dfs_cac_ms, flags,
+ reg_rule->psd_flag, reg_rule->psd_eirp);
+ } else {
+ ath11k_dbg(ab, ATH11K_DBG_REG,
+ "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
+ i + 1, reg_rule->start_freq, reg_rule->end_freq,
+ max_bw, reg_rule->ant_gain, reg_rule->reg_power,
+ tmp_regd->reg_rules[i].dfs_cac_ms,
+ flags);
+ }
}

tmp_regd->n_reg_rules = i;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index bfb3effc161d..5f389aa18ebc 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -104,6 +104,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
= { .min_len = sizeof(struct wmi_vdev_stopped_event) },
[WMI_TAG_REG_CHAN_LIST_CC_EVENT]
= { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) },
+ [WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]
+ = { .min_len = sizeof(struct wmi_reg_chan_list_cc_ext_event) },
[WMI_TAG_MGMT_RX_HDR]
= { .min_len = sizeof(struct wmi_mgmt_rx_hdr) },
[WMI_TAG_MGMT_TX_COMPL_EVENT]
@@ -3738,6 +3740,10 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg,
wmi_cfg->sched_params = tg_cfg->sched_params;
wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count;
wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count;
+ wmi_cfg->host_service_flags &=
+ ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
+ wmi_cfg->host_service_flags |= tg_cfg->is_reg_cc_ext_event_supported <<
+ WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT;
}

static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi,
@@ -3956,6 +3962,9 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab)

ab->hw_params.hw_ops->wmi_init_config(ab, &config);

+ if (ath11k_hw_supports_cc_ext(ab))
+ config.is_reg_cc_ext_event_supported = 1;
+
memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config));

init_param.res_cfg = &wmi_sc->wlan_resource_config;
@@ -4772,6 +4781,8 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL)
reg_info->status_code = REG_SET_CC_STATUS_FAIL;

+ reg_info->is_ext_reg_event = false;
+
reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g;
reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g;
reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g;
@@ -4822,6 +4833,348 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab,
return 0;
}

+static struct cur_reg_rule
+*create_ext_reg_rules_from_wmi(u32 num_reg_rules,
+ const struct wmi_regulatory_ext_rule *wmi_reg_rule)
+{
+ struct cur_reg_rule *reg_rule_ptr;
+ u32 count;
+
+ reg_rule_ptr = kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), GFP_ATOMIC);
+
+ if (!reg_rule_ptr)
+ return NULL;
+
+ for (count = 0; count < num_reg_rules; count++) {
+ reg_rule_ptr[count].start_freq =
+ FIELD_GET(REG_RULE_START_FREQ,
+ wmi_reg_rule[count].freq_info);
+ reg_rule_ptr[count].end_freq =
+ FIELD_GET(REG_RULE_END_FREQ,
+ wmi_reg_rule[count].freq_info);
+ reg_rule_ptr[count].max_bw =
+ FIELD_GET(REG_RULE_MAX_BW,
+ wmi_reg_rule[count].bw_pwr_info);
+ reg_rule_ptr[count].reg_power =
+ FIELD_GET(REG_RULE_REG_PWR,
+ wmi_reg_rule[count].bw_pwr_info);
+ reg_rule_ptr[count].ant_gain =
+ FIELD_GET(REG_RULE_ANT_GAIN,
+ wmi_reg_rule[count].bw_pwr_info);
+ reg_rule_ptr[count].flags =
+ FIELD_GET(REG_RULE_FLAGS,
+ wmi_reg_rule[count].flag_info);
+ reg_rule_ptr[count].psd_flag =
+ FIELD_GET(REG_RULE_PSD_INFO,
+ wmi_reg_rule[count].psd_power_info);
+ reg_rule_ptr[count].psd_eirp =
+ FIELD_GET(REG_RULE_PSD_EIRP,
+ wmi_reg_rule[count].psd_power_info);
+ }
+
+ return reg_rule_ptr;
+}
+
+static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab,
+ struct sk_buff *skb,
+ struct cur_regulatory_info *reg_info)
+{
+ const void **tb;
+ const struct wmi_reg_chan_list_cc_ext_event *ext_chan_list_event_hdr;
+ const struct wmi_regulatory_ext_rule *ext_wmi_reg_rule;
+ u32 num_2g_reg_rules, num_5g_reg_rules;
+ u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+ u32 num_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+ u32 total_reg_rules = 0;
+ int ret, i, j;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n");
+
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ret = PTR_ERR(tb);
+ ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+ return ret;
+ }
+
+ ext_chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT];
+ if (!ext_chan_list_event_hdr) {
+ ath11k_warn(ab, "failed to fetch reg chan list ext update ev\n");
+ kfree(tb);
+ return -EPROTO;
+ }
+
+ reg_info->num_2g_reg_rules = ext_chan_list_event_hdr->num_2g_reg_rules;
+ reg_info->num_5g_reg_rules = ext_chan_list_event_hdr->num_5g_reg_rules;
+ reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_ap_lpi;
+ reg_info->num_6g_reg_rules_ap[WMI_REG_STANDARD_POWER_AP] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_ap_sp;
+ reg_info->num_6g_reg_rules_ap[WMI_REG_VERY_LOW_POWER_AP] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_ap_vlp;
+
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+ reg_info->num_6g_reg_rules_client[WMI_REG_INDOOR_AP][i] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_client_lpi[i];
+ reg_info->num_6g_reg_rules_client[WMI_REG_STANDARD_POWER_AP][i] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_client_sp[i];
+ reg_info->num_6g_reg_rules_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+ ext_chan_list_event_hdr->num_6g_reg_rules_client_vlp[i];
+ }
+
+ num_2g_reg_rules = reg_info->num_2g_reg_rules;
+ total_reg_rules += num_2g_reg_rules;
+ num_5g_reg_rules = reg_info->num_5g_reg_rules;
+ total_reg_rules += num_5g_reg_rules;
+
+ if ((num_2g_reg_rules > MAX_REG_RULES) || (num_5g_reg_rules > MAX_REG_RULES)) {
+ ath11k_warn(ab, "Num reg rules for 2.4 GHz/5 GHz exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n",
+ num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES);
+ kfree(tb);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+ num_6g_reg_rules_ap[i] = reg_info->num_6g_reg_rules_ap[i];
+
+ if (num_6g_reg_rules_ap[i] > MAX_6G_REG_RULES) {
+ ath11k_warn(ab, "Num 6 GHz reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n",
+ i, num_6g_reg_rules_ap[i], MAX_6G_REG_RULES);
+ kfree(tb);
+ return -EINVAL;
+ }
+
+ total_reg_rules += num_6g_reg_rules_ap[i];
+ }
+
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+ num_6g_client[WMI_REG_INDOOR_AP][i] =
+ reg_info->num_6g_reg_rules_client[WMI_REG_INDOOR_AP][i];
+ total_reg_rules += num_6g_client[WMI_REG_INDOOR_AP][i];
+
+ num_6g_client[WMI_REG_STANDARD_POWER_AP][i] =
+ reg_info->num_6g_reg_rules_client[WMI_REG_STANDARD_POWER_AP][i];
+ total_reg_rules += num_6g_client[WMI_REG_STANDARD_POWER_AP][i];
+
+ num_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+ reg_info->num_6g_reg_rules_client[WMI_REG_VERY_LOW_POWER_AP][i];
+ total_reg_rules += num_6g_client[WMI_REG_VERY_LOW_POWER_AP][i];
+
+ if ((num_6g_client[WMI_REG_INDOOR_AP][i] > MAX_6G_REG_RULES) ||
+ (num_6g_client[WMI_REG_STANDARD_POWER_AP][i] > MAX_6G_REG_RULES) ||
+ (num_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] > MAX_6G_REG_RULES)) {
+ ath11k_warn(ab,
+ "Num 6 GHz client reg rules exceeds max limit, for client(type: %d)\n",
+ i);
+ kfree(tb);
+ return -EINVAL;
+ }
+ }
+
+ if (!total_reg_rules) {
+ ath11k_warn(ab, "No reg rules available\n");
+ kfree(tb);
+ return -EINVAL;
+ }
+
+ memcpy(reg_info->alpha2, &ext_chan_list_event_hdr->alpha2,
+ REG_ALPHA2_LEN);
+
+ /* FIXME: Currently firmware includes 6 GHz reg rule also in 5 GHz rule
+ * list for country US.
+ * Having same 6 GHz reg rule in 5 GHz and 6 GHz rules list causes
+ * intersect check to be true, and same rules will be shown
+ * multiple times in iw cmd. So added hack below to avoid
+ * parsing 6 GHz rule from 5 GHz reg rule list, and this can be
+ * removed later, after firmware updates to remove 6 GHz reg rule
+ * from 5 GHz rules list.
+ */
+ if (memcmp(reg_info->alpha2, "US", 2) == 0) {
+ reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES;
+ num_5g_reg_rules = reg_info->num_5g_reg_rules;
+ }
+
+ reg_info->dfs_region = ext_chan_list_event_hdr->dfs_region;
+ reg_info->phybitmap = ext_chan_list_event_hdr->phybitmap;
+ reg_info->num_phy = ext_chan_list_event_hdr->num_phy;
+ reg_info->phy_id = ext_chan_list_event_hdr->phy_id;
+ reg_info->ctry_code = ext_chan_list_event_hdr->country_id;
+ reg_info->reg_dmn_pair = ext_chan_list_event_hdr->domain_code;
+
+ switch (ext_chan_list_event_hdr->status_code) {
+ case WMI_REG_SET_CC_STATUS_PASS:
+ reg_info->status_code = REG_SET_CC_STATUS_PASS;
+ break;
+ case WMI_REG_CURRENT_ALPHA2_NOT_FOUND:
+ reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND;
+ break;
+ case WMI_REG_INIT_ALPHA2_NOT_FOUND:
+ reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND;
+ break;
+ case WMI_REG_SET_CC_CHANGE_NOT_ALLOWED:
+ reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED;
+ break;
+ case WMI_REG_SET_CC_STATUS_NO_MEMORY:
+ reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY;
+ break;
+ case WMI_REG_SET_CC_STATUS_FAIL:
+ reg_info->status_code = REG_SET_CC_STATUS_FAIL;
+ break;
+ }
+
+ reg_info->is_ext_reg_event = true;
+
+ reg_info->min_bw_2g = ext_chan_list_event_hdr->min_bw_2g;
+ reg_info->max_bw_2g = ext_chan_list_event_hdr->max_bw_2g;
+ reg_info->min_bw_5g = ext_chan_list_event_hdr->min_bw_5g;
+ reg_info->max_bw_5g = ext_chan_list_event_hdr->max_bw_5g;
+ reg_info->min_bw_6g_ap[WMI_REG_INDOOR_AP] =
+ ext_chan_list_event_hdr->min_bw_6g_ap_lpi;
+ reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP] =
+ ext_chan_list_event_hdr->max_bw_6g_ap_lpi;
+ reg_info->min_bw_6g_ap[WMI_REG_STANDARD_POWER_AP] =
+ ext_chan_list_event_hdr->min_bw_6g_ap_sp;
+ reg_info->max_bw_6g_ap[WMI_REG_STANDARD_POWER_AP] =
+ ext_chan_list_event_hdr->max_bw_6g_ap_sp;
+ reg_info->min_bw_6g_ap[WMI_REG_VERY_LOW_POWER_AP] =
+ ext_chan_list_event_hdr->min_bw_6g_ap_vlp;
+ reg_info->max_bw_6g_ap[WMI_REG_VERY_LOW_POWER_AP] =
+ ext_chan_list_event_hdr->max_bw_6g_ap_vlp;
+
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+ reg_info->min_bw_6g_client[WMI_REG_INDOOR_AP][i] =
+ ext_chan_list_event_hdr->min_bw_6g_client_lpi[i];
+ reg_info->max_bw_6g_client[WMI_REG_INDOOR_AP][i] =
+ ext_chan_list_event_hdr->max_bw_6g_client_lpi[i];
+ reg_info->min_bw_6g_client[WMI_REG_STANDARD_POWER_AP][i] =
+ ext_chan_list_event_hdr->min_bw_6g_client_sp[i];
+ reg_info->max_bw_6g_client[WMI_REG_STANDARD_POWER_AP][i] =
+ ext_chan_list_event_hdr->max_bw_6g_client_sp[i];
+ reg_info->min_bw_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+ ext_chan_list_event_hdr->min_bw_6g_client_vlp[i];
+ reg_info->max_bw_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+ ext_chan_list_event_hdr->max_bw_6g_client_vlp[i];
+ }
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "%s:cc_ext %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d",
+ __func__, reg_info->alpha2, reg_info->dfs_region,
+ reg_info->min_bw_2g, reg_info->max_bw_2g,
+ reg_info->min_bw_5g, reg_info->max_bw_5g);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "num_2g_reg_rules %d num_5g_reg_rules %d",
+ num_2g_reg_rules, num_5g_reg_rules);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "num_6g_reg_rules_ap_lpi: %d num_6g_reg_rules_ap_sp: %d num_6g_reg_rules_ap_vlp: %d",
+ num_6g_reg_rules_ap[WMI_REG_INDOOR_AP],
+ num_6g_reg_rules_ap[WMI_REG_STANDARD_POWER_AP],
+ num_6g_reg_rules_ap[WMI_REG_VERY_LOW_POWER_AP]);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz Regular client: num_6g_reg_rules_lpi: %d num_6g_reg_rules_sp: %d num_6g_reg_rules_vlp: %d",
+ num_6g_client[WMI_REG_INDOOR_AP][WMI_REG_DEFAULT_CLIENT],
+ num_6g_client[WMI_REG_STANDARD_POWER_AP][WMI_REG_DEFAULT_CLIENT],
+ num_6g_client[WMI_REG_VERY_LOW_POWER_AP][WMI_REG_DEFAULT_CLIENT]);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "6 GHz Subordinate client: num_6g_reg_rules_lpi: %d num_6g_reg_rules_sp: %d num_6g_reg_rules_vlp: %d",
+ num_6g_client[WMI_REG_INDOOR_AP][WMI_REG_SUBORDINATE_CLIENT],
+ num_6g_client[WMI_REG_STANDARD_POWER_AP][WMI_REG_SUBORDINATE_CLIENT],
+ num_6g_client[WMI_REG_VERY_LOW_POWER_AP][WMI_REG_SUBORDINATE_CLIENT]);
+
+ ext_wmi_reg_rule = tb[WMI_TAG_ARRAY_STRUCT];
+
+ if (num_2g_reg_rules) {
+ reg_info->reg_rules_2g_ptr =
+ create_ext_reg_rules_from_wmi(num_2g_reg_rules,
+ ext_wmi_reg_rule);
+
+ if (!reg_info->reg_rules_2g_ptr) {
+ kfree(tb);
+ ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (num_5g_reg_rules) {
+ ext_wmi_reg_rule += num_2g_reg_rules;
+ reg_info->reg_rules_5g_ptr =
+ create_ext_reg_rules_from_wmi(num_5g_reg_rules,
+ ext_wmi_reg_rule);
+
+ if (!reg_info->reg_rules_5g_ptr) {
+ kfree(tb);
+ ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n");
+ return -ENOMEM;
+ }
+ }
+
+ ext_wmi_reg_rule += num_5g_reg_rules;
+
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+ reg_info->reg_rules_6g_ap_ptr[i] =
+ create_ext_reg_rules_from_wmi(num_6g_reg_rules_ap[i],
+ ext_wmi_reg_rule);
+
+ if (!reg_info->reg_rules_6g_ap_ptr[i]) {
+ kfree(tb);
+ ath11k_warn(ab, "Unable to Allocate memory for 6 GHz ap rules\n");
+ return -ENOMEM;
+ }
+
+ ext_wmi_reg_rule += num_6g_reg_rules_ap[i];
+ }
+
+ for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) {
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+ reg_info->reg_rules_6g_client_ptr[j][i] =
+ create_ext_reg_rules_from_wmi(num_6g_client[j][i],
+ ext_wmi_reg_rule);
+
+ if (!reg_info->reg_rules_6g_client_ptr[j][i]) {
+ kfree(tb);
+ ath11k_warn(ab, "Unable to Allocate memory for 6 GHz client rules\n");
+ return -ENOMEM;
+ }
+
+ ext_wmi_reg_rule += num_6g_client[j][i];
+ }
+ }
+
+ reg_info->client_type = ext_chan_list_event_hdr->client_type;
+ reg_info->rnr_tpe_usable = ext_chan_list_event_hdr->rnr_tpe_usable;
+ reg_info->unspecified_ap_usable =
+ ext_chan_list_event_hdr->unspecified_ap_usable;
+ reg_info->domain_code_6g_ap[WMI_REG_INDOOR_AP] =
+ ext_chan_list_event_hdr->domain_code_6g_ap_lpi;
+ reg_info->domain_code_6g_ap[WMI_REG_STANDARD_POWER_AP] =
+ ext_chan_list_event_hdr->domain_code_6g_ap_sp;
+ reg_info->domain_code_6g_ap[WMI_REG_VERY_LOW_POWER_AP] =
+ ext_chan_list_event_hdr->domain_code_6g_ap_vlp;
+
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) {
+ reg_info->domain_code_6g_client[WMI_REG_INDOOR_AP][i] =
+ ext_chan_list_event_hdr->domain_code_6g_client_lpi[i];
+ reg_info->domain_code_6g_client[WMI_REG_STANDARD_POWER_AP][i] =
+ ext_chan_list_event_hdr->domain_code_6g_client_sp[i];
+ reg_info->domain_code_6g_client[WMI_REG_VERY_LOW_POWER_AP][i] =
+ ext_chan_list_event_hdr->domain_code_6g_client_vlp[i];
+ }
+
+ reg_info->domain_code_6g_super_id =
+ ext_chan_list_event_hdr->domain_code_6g_super_id;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "6 GHz client_type: %d domain_code_6g_super_id: %d",
+ reg_info->client_type, reg_info->domain_code_6g_super_id);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n");
+
+ kfree(tb);
+ return 0;
+}
+
static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb,
struct wmi_peer_delete_resp_event *peer_del_resp)
{
@@ -6249,12 +6602,14 @@ static bool ath11k_reg_is_world_alpha(char *alpha)
return false;
}

-static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb)
+static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ struct sk_buff *skb,
+ enum wmi_reg_chan_list_cmd_type id)
{
struct cur_regulatory_info *reg_info = NULL;
struct ieee80211_regdomain *regd = NULL;
bool intersect = false;
- int ret = 0, pdev_idx;
+ int ret = 0, pdev_idx, i, j;
struct ath11k *ar;

reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
@@ -6263,7 +6618,11 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
goto fallback;
}

- ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+ if (id == WMI_REG_CHAN_LIST_CC_ID)
+ ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+ else
+ ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+
if (ret) {
ath11k_warn(ab, "failed to extract regulatory info from received event\n");
goto fallback;
@@ -6365,6 +6724,14 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk
if (reg_info) {
kfree(reg_info->reg_rules_2g_ptr);
kfree(reg_info->reg_rules_5g_ptr);
+ if (reg_info->is_ext_reg_event) {
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++)
+ kfree(reg_info->reg_rules_6g_ap_ptr[i]);
+
+ for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++)
+ for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++)
+ kfree(reg_info->reg_rules_6g_client_ptr[j][i]);
+ }
kfree(reg_info);
}
return ret;
@@ -7574,7 +7941,10 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
ath11k_service_ready_ext2_event(ab, skb);
break;
case WMI_REG_CHAN_LIST_CC_EVENTID:
- ath11k_reg_chan_list_event(ab, skb);
+ ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_ID);
+ break;
+ case WMI_REG_CHAN_LIST_CC_EXT_EVENTID:
+ ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_EXT_ID);
break;
case WMI_READY_EVENTID:
ath11k_ready_event(ab, skb);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 8609dcd0543b..02c518e95121 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -790,6 +790,7 @@ enum wmi_tlv_event_id {
WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC),
WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY),
WMI_11D_NEW_COUNTRY_EVENTID,
+ WMI_REG_CHAN_LIST_CC_EXT_EVENTID,
WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE),
WMI_NDP_INITIATOR_RSP_EVENTID,
WMI_NDP_RESPONDER_RSP_EVENTID,
@@ -1857,6 +1858,9 @@ enum wmi_tlv_tag {
WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD,
WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
+ WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9,
+ WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT,
+
WMI_TAG_MAX
};

@@ -2086,6 +2090,8 @@ enum wmi_tlv_service {
/* The second 128 bits */
WMI_MAX_EXT_SERVICE = 256,

+ WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
+
/* The third 128 bits */
WMI_MAX_EXT2_SERVICE = 384
};
@@ -2299,6 +2305,8 @@ struct wmi_init_cmd {

#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)

+#define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4
+
struct wmi_resource_config {
u32 tlv_header;
u32 num_vdevs;
@@ -2358,6 +2366,15 @@ struct wmi_resource_config {
u32 sched_params;
u32 twt_ap_pdev_count;
u32 twt_ap_sta_count;
+ u32 max_nlo_ssids;
+ u32 num_pkt_filters;
+ u32 num_max_sta_vdevs;
+ u32 max_bssid_indicator;
+ u32 ul_resp_config;
+ u32 msdu_flow_override_config0;
+ u32 msdu_flow_override_config1;
+ u32 flags2;
+ u32 host_service_flags;
} __packed;

struct wmi_service_ready_event {
@@ -2840,6 +2857,8 @@ struct rx_reorder_queue_remove_params {
#define REG_RULE_MAX_BW 0x0000ffff
#define REG_RULE_REG_PWR 0x00ff0000
#define REG_RULE_ANT_GAIN 0xff000000
+#define REG_RULE_PSD_INFO BIT(0)
+#define REG_RULE_PSD_EIRP 0xff0000

#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
@@ -4036,6 +4055,8 @@ struct wmi_he_rate_set {

#define MAX_REG_RULES 10
#define REG_ALPHA2_LEN 2
+#define MAX_6G_REG_RULES 5
+#define REG_US_5G_NUM_REG_RULES 4

enum wmi_start_event_param {
WMI_VDEV_START_RESP_EVENT = 0,
@@ -4098,6 +4119,21 @@ enum {
WMI_REG_SET_CC_STATUS_FAIL = 5,
};

+enum wmi_reg_6g_ap_type {
+ WMI_REG_INDOOR_AP = 0,
+ WMI_REG_STANDARD_POWER_AP = 1,
+ WMI_REG_VERY_LOW_POWER_AP = 2,
+ WMI_REG_CURRENT_MAX_AP_TYPE,
+ WMI_REG_MAX_SUPP_AP_TYPE = WMI_REG_VERY_LOW_POWER_AP,
+ WMI_REG_MAX_AP_TYPE = 7,
+};
+
+enum wmi_reg_6g_client_type {
+ WMI_REG_DEFAULT_CLIENT = 0,
+ WMI_REG_SUBORDINATE_CLIENT = 1,
+ WMI_REG_MAX_CLIENT_TYPE = 2,
+};
+
struct cur_reg_rule {
u16 start_freq;
u16 end_freq;
@@ -4105,6 +4141,8 @@ struct cur_reg_rule {
u8 reg_power;
u8 ant_gain;
u16 flags;
+ bool psd_flag;
+ u16 psd_eirp;
};

struct cur_regulatory_info {
@@ -4116,6 +4154,7 @@ struct cur_regulatory_info {
u8 alpha2[REG_ALPHA2_LEN + 1];
u32 dfs_region;
u32 phybitmap;
+ bool is_ext_reg_event;
u32 min_bw_2g;
u32 max_bw_2g;
u32 min_bw_5g;
@@ -4124,6 +4163,29 @@ struct cur_regulatory_info {
u32 num_5g_reg_rules;
struct cur_reg_rule *reg_rules_2g_ptr;
struct cur_reg_rule *reg_rules_5g_ptr;
+ enum wmi_reg_6g_client_type client_type;
+ bool rnr_tpe_usable;
+ bool unspecified_ap_usable;
+ /* TODO: All 6 GHz related info can be stored only for required
+ * combination instead of all types, to optimize memory usage.
+ */
+ u8 domain_code_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+ u8 domain_code_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+ u32 domain_code_6g_super_id;
+ u32 min_bw_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+ u32 max_bw_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+ u32 min_bw_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+ u32 max_bw_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+ u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
+ u32 num_6g_reg_rules_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+ struct cur_reg_rule *reg_rules_6g_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE];
+ struct cur_reg_rule *reg_rules_6g_client_ptr
+ [WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
+};
+
+enum wmi_reg_chan_list_cmd_type {
+ WMI_REG_CHAN_LIST_CC_ID = 0,
+ WMI_REG_CHAN_LIST_CC_EXT_ID = 1,
};

struct wmi_reg_chan_list_cc_event {
@@ -4154,6 +4216,61 @@ struct wmi_vdev_delete_resp_event {
u32 vdev_id;
} __packed;

+#define WMI_REG_CLIENT_MAX 4
+
+struct wmi_reg_chan_list_cc_ext_event {
+ u32 status_code;
+ u32 phy_id;
+ u32 alpha2;
+ u32 num_phy;
+ u32 country_id;
+ u32 domain_code;
+ u32 dfs_region;
+ u32 phybitmap;
+ u32 min_bw_2g;
+ u32 max_bw_2g;
+ u32 min_bw_5g;
+ u32 max_bw_5g;
+ u32 num_2g_reg_rules;
+ u32 num_5g_reg_rules;
+ u32 client_type;
+ u32 rnr_tpe_usable;
+ u32 unspecified_ap_usable;
+ u32 domain_code_6g_ap_lpi;
+ u32 domain_code_6g_ap_sp;
+ u32 domain_code_6g_ap_vlp;
+ u32 domain_code_6g_client_lpi[WMI_REG_CLIENT_MAX];
+ u32 domain_code_6g_client_sp[WMI_REG_CLIENT_MAX];
+ u32 domain_code_6g_client_vlp[WMI_REG_CLIENT_MAX];
+ u32 domain_code_6g_super_id;
+ u32 min_bw_6g_ap_sp;
+ u32 max_bw_6g_ap_sp;
+ u32 min_bw_6g_ap_lpi;
+ u32 max_bw_6g_ap_lpi;
+ u32 min_bw_6g_ap_vlp;
+ u32 max_bw_6g_ap_vlp;
+ u32 min_bw_6g_client_sp[WMI_REG_CLIENT_MAX];
+ u32 max_bw_6g_client_sp[WMI_REG_CLIENT_MAX];
+ u32 min_bw_6g_client_lpi[WMI_REG_CLIENT_MAX];
+ u32 max_bw_6g_client_lpi[WMI_REG_CLIENT_MAX];
+ u32 min_bw_6g_client_vlp[WMI_REG_CLIENT_MAX];
+ u32 max_bw_6g_client_vlp[WMI_REG_CLIENT_MAX];
+ u32 num_6g_reg_rules_ap_sp;
+ u32 num_6g_reg_rules_ap_lpi;
+ u32 num_6g_reg_rules_ap_vlp;
+ u32 num_6g_reg_rules_client_sp[WMI_REG_CLIENT_MAX];
+ u32 num_6g_reg_rules_client_lpi[WMI_REG_CLIENT_MAX];
+ u32 num_6g_reg_rules_client_vlp[WMI_REG_CLIENT_MAX];
+} __packed;
+
+struct wmi_regulatory_ext_rule {
+ u32 tlv_header;
+ u32 freq_info;
+ u32 bw_pwr_info;
+ u32 flag_info;
+ u32 psd_power_info;
+};
+
struct wmi_peer_delete_resp_event {
u32 vdev_id;
struct wmi_mac_addr peer_macaddr;
@@ -5218,6 +5335,7 @@ struct target_resource_config {
u32 sched_params;
u32 twt_ap_pdev_count;
u32 twt_ap_sta_count;
+ u8 is_reg_cc_ext_event_supported;
};

enum wmi_sys_cap_info_flags {
--
2.31.1

2022-09-13 05:17:00

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 05/15] wifi: ath11k: fix a possible dead lock caused by ab->base_lock

From: Baochen Qiang <[email protected]>

spin_lock/spin_unlock are used in ath11k_reg_chan_list_event to
acquire/release ab->base_lock, for now this is safe because that
function is only called in soft IRQ context. But in an upcoming
patch ath11k_reg_chan_list_event() will be called from process
context, and this can result in a deadlock if ab->base_lock is
acquired in process context and then soft IRQ occurs on the same
CPU and tries to acquire that lock.

Fix it by using spin_lock_bh and spin_unlock_bh instead.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3

Fixes: 69a0fcf8a9f2 ("ath11k: Avoid reg rules update during firmware recovery")
Signed-off-by: Baochen Qiang <[email protected]>
Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/wmi.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index d2d0873fda83..8187b644078b 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -6835,13 +6835,13 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
/* Avoid default reg rule updates sent during FW recovery if
* it is already available
*/
- spin_lock(&ab->base_lock);
+ spin_lock_bh(&ab->base_lock);
if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
ab->default_regd[pdev_idx]) {
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);
goto retfail;
}
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);

if (pdev_idx >= ab->num_radios) {
/* Process the event for phy0 only if single_pdev_only
@@ -6891,7 +6891,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
ab->reg_info_store[pdev_idx] = *reg_info;
}

- spin_lock(&ab->base_lock);
+ spin_lock_bh(&ab->base_lock);
if (ab->default_regd[pdev_idx]) {
/* The initial rules from FW after WMI Init is to build
* the default regd. From then on, any rules updated for
@@ -6911,7 +6911,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
ab->default_regd[pdev_idx] = regd;
}
ab->dfs_region = reg_info->dfs_region;
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);

return 0;

--
2.31.1

2022-09-13 05:17:06

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 11/15] wifi: ath11k: fill parameters for vdev_set_tpc_power wmi command

Prepare the parameters which is needed for wmi cmd vdev_set_tpc_power.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/mac.c | 280 ++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/mac.h | 3 +
2 files changed, 283 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 6eb707473ed3..e1bdfe466bc7 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -7047,6 +7047,286 @@ static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
}
}

+static u16 ath11k_mac_get_6g_start_frequency(struct cfg80211_chan_def *chan_def)
+{
+ u16 diff_seq;
+
+ /* It is to get the lowest channel number's center frequency of the chan.
+ * For example,
+ * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
+ * with center frequency 5955, its diff is 5965 - 5955 = 10.
+ * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
+ * with center frequency 5955, its diff is 5985 - 5955 = 30.
+ * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
+ * with center frequency 5955, its diff is 6025 - 5955 = 70.
+ */
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_160:
+ diff_seq = 70;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ diff_seq = 30;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ diff_seq = 10;
+ break;
+ default:
+ diff_seq = 0;
+ }
+
+ return chan_def->center_freq1 - diff_seq;
+}
+
+static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
+ u16 start_seq, u8 seq)
+{
+ u16 seg_seq;
+
+ /* It is to get the center frequency of the specific bandwidth.
+ * start_seq means the lowest channel number's center frequency.
+ * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80.
+ * For example,
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
+ * lowest channel is 1, its center frequency 5955,
+ * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
+ */
+ if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
+ return chan_def->center_freq2;
+
+ seg_seq = 10 * (BIT(seq) - 1);
+ return seg_seq + start_seq;
+}
+
+static void ath11k_mac_get_psd_channel(struct ath11k *ar,
+ u16 step_freq,
+ u16 *start_freq,
+ u16 *center_freq,
+ u8 i,
+ struct ieee80211_channel **temp_chan,
+ s8 *tx_power)
+{
+ /* It is to get the the center frequency for each 20 MHz.
+ * For example, if the chan is 160 MHz and center frequency is 6025,
+ * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
+ * channel number 1's center frequency is 5955, it is parameter start_freq.
+ * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
+ * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
+ * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
+ * the gap is 20 for each channel, parameter step_freq means the gap.
+ * after get the center frequency of each channel, it is easy to find the
+ * struct ieee80211_channel of it and get the max_reg_power.
+ */
+ *center_freq = *start_freq + i * step_freq;
+ *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+ *tx_power = (*temp_chan)->max_reg_power;
+}
+
+static void ath11k_mac_get_eirp_power(struct ath11k *ar,
+ u16 *start_freq,
+ u16 *center_freq,
+ u8 i,
+ struct ieee80211_channel **temp_chan,
+ struct cfg80211_chan_def *def,
+ s8 *tx_power)
+{
+ /* It is to get the the center frequency for 20 MHz/40 MHz/80 MHz/
+ * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency,
+ * it is the center frequency of a channel number.
+ * For example, when configured channel number is 1.
+ * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
+ * then it is channel number 5.
+ * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
+ * then it is channel number 9.
+ * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
+ * then it is channel number 17.
+ * after get the center frequency of each channel, it is easy to find the
+ * struct ieee80211_channel of it and get the max_reg_power.
+ */
+ *center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i);
+
+ /* For the 20 MHz, its center frequency is same with same channel */
+ if (i != 0)
+ *center_freq += 10;
+
+ *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+ *tx_power = (*temp_chan)->max_reg_power;
+}
+
+void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
+ struct ieee80211_channel *chan, *temp_chan;
+ u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
+ bool is_psd_power = false, is_tpe_present = false;
+ s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
+ psd_power, tx_power, eirp_power;
+ u16 oper_freq, start_freq, center_freq;
+
+ chan = ctx->def.chan;
+ oper_freq = ctx->def.chan->center_freq;
+ start_freq = ath11k_mac_get_6g_start_frequency(&ctx->def);
+ pwr_reduction = bss_conf->pwr_reduction;
+
+ if (arvif->reg_tpc_info.num_pwr_levels) {
+ is_tpe_present = true;
+ num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
+ } else {
+ num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def);
+ }
+
+ for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
+ /* STA received TPE IE*/
+ if (is_tpe_present) {
+ /* local power is PSD power*/
+ if (chan->flags & IEEE80211_CHAN_PSD) {
+ /* Connecting AP is psd power */
+ if (reg_tpc_info->is_psd_power) {
+ is_psd_power = true;
+ ath11k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ psd_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ /* Connecting AP is not psd power */
+ } else {
+ ath11k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ /* convert psd power to EIRP power based
+ * on channel width
+ */
+ tx_power =
+ min_t(s8, tx_power,
+ psd_power + 13 + pwr_lvl_idx * 3);
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ tx_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ }
+ /* local power is not PSD power */
+ } else {
+ /* Connecting AP is psd power */
+ if (reg_tpc_info->is_psd_power) {
+ is_psd_power = true;
+ ath11k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] =
+ reg_tpc_info->tpe[pwr_lvl_idx];
+ /* Connecting AP is not psd power */
+ } else {
+ ath11k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ tx_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ }
+ }
+ /* STA not received TPE IE */
+ } else {
+ /* local power is PSD power*/
+ if (chan->flags & IEEE80211_CHAN_PSD) {
+ is_psd_power = true;
+ ath11k_mac_get_psd_channel(ar, 20,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &tx_power);
+ psd_power = temp_chan->psd;
+ eirp_power = tx_power;
+ max_tx_power[pwr_lvl_idx] = psd_power;
+ } else {
+ ath11k_mac_get_eirp_power(ar,
+ &start_freq,
+ &center_freq,
+ pwr_lvl_idx,
+ &temp_chan,
+ &ctx->def,
+ &tx_power);
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ tx_power,
+ reg_tpc_info->tpe[pwr_lvl_idx]);
+ }
+ }
+
+ if (is_psd_power) {
+ /* If AP local power constraint is present */
+ if (pwr_reduction)
+ eirp_power = eirp_power - pwr_reduction;
+
+ /* If firmware updated max tx power is non zero, then take
+ * the min of firmware updated ap tx power
+ * and max power derived from above mentioned parameters.
+ */
+ ath11k_dbg(ab, ATH11K_DBG_MAC,
+ "eirp power : %d firmware report power : %d\n",
+ eirp_power, ar->max_allowed_tx_power);
+ if (ar->max_allowed_tx_power)
+ eirp_power = min_t(s8,
+ eirp_power,
+ ar->max_allowed_tx_power);
+ } else {
+ /* If AP local power constraint is present */
+ if (pwr_reduction)
+ max_tx_power[pwr_lvl_idx] =
+ max_tx_power[pwr_lvl_idx] - pwr_reduction;
+ /* If firmware updated max tx power is non zero, then take
+ * the min of firmware updated ap tx power
+ * and max power derived from above mentioned parameters.
+ */
+ if (ar->max_allowed_tx_power)
+ max_tx_power[pwr_lvl_idx] =
+ min_t(s8,
+ max_tx_power[pwr_lvl_idx],
+ ar->max_allowed_tx_power);
+ }
+ reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
+ reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
+ max_tx_power[pwr_lvl_idx];
+ }
+
+ reg_tpc_info->num_pwr_levels = num_pwr_levels;
+ reg_tpc_info->is_psd_power = is_psd_power;
+ reg_tpc_info->eirp_power = eirp_power;
+ reg_tpc_info->power_type_6g =
+ ath11k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type);
+}
+
static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index eb8273ffc2a7..13c0ef667777 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -172,4 +172,7 @@ enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher);
void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb);
void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif);
+void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx);
#endif
--
2.31.1

2022-09-13 05:17:06

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 14/15] wifi: ath11k: add handler for WMI_VDEV_SET_TPC_POWER_CMDID

Add the handler for WMI_VDEV_SET_TPC_POWER_CMDID, it is for 6 GHz band.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/wmi.c | 63 +++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/wmi.h | 61 ++++++++++++++++++++++++++
2 files changed, 124 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 7d7751fd6a8e..89027d3cb5e9 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -2304,6 +2304,69 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
return ret;
}

+int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
+ u32 vdev_id,
+ struct ath11k_reg_tpc_power_info *param)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_vdev_set_tpc_power_cmd *cmd;
+ struct wmi_vdev_ch_power_info *ch;
+ struct sk_buff *skb;
+ struct wmi_tlv *tlv;
+ u8 *ptr;
+ int i, ret, len;
+
+ len = sizeof(*cmd) + TLV_HDR_SIZE;
+ len += (sizeof(struct wmi_vdev_ch_power_info) * param->num_pwr_levels);
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ ptr = skb->data;
+
+ cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_TPC_POWER_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+ cmd->vdev_id = vdev_id;
+ cmd->psd_power = param->is_psd_power;
+ cmd->eirp_power = param->eirp_power;
+ cmd->power_type_6ghz = param->power_type_6g;
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi tpc vdev_id %d is_psd_power %d eirp_power %d power_type_6g %d\n",
+ vdev_id, param->is_psd_power, param->eirp_power, param->power_type_6g);
+
+ ptr += sizeof(*cmd);
+ tlv = (struct wmi_tlv *)ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, param->num_pwr_levels * sizeof(*ch));
+
+ ptr += TLV_HDR_SIZE;
+ ch = (struct wmi_vdev_ch_power_info *)ptr;
+
+ for (i = 0; i < param->num_pwr_levels; i++, ch++) {
+ ch->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+ WMI_TAG_VDEV_CH_POWER_INFO) |
+ FIELD_PREP(WMI_TLV_LEN,
+ sizeof(*ch) - TLV_HDR_SIZE);
+
+ ch->chan_cfreq = param->chan_power_info[i].chan_cfreq;
+ ch->tx_power = param->chan_power_info[i].tx_power;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi tpc chan_cfreq %d tx_power %d\n",
+ ch->chan_cfreq, ch->tx_power);
+ }
+
+ ret = ath11k_wmi_cmd_send(wmi, skb,
+ WMI_VDEV_SET_TPC_POWER_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+ return ret;
+}
+
int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar,
struct scan_cancel_param *param)
{
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index d285d18e99a8..736b6f00ccb5 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -12,6 +12,7 @@
struct ath11k_base;
struct ath11k;
struct ath11k_fw_stats;
+struct ath11k_reg_tpc_power_info;

#define PSOC_HOST_MAX_NUM_SS (8)

@@ -310,6 +311,36 @@ enum wmi_tlv_cmd_id {
WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID,
WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID,
WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID,
+ /** WMI commands related to dbg arp stats */
+ WMI_VDEV_SET_ARP_STAT_CMDID,
+ WMI_VDEV_GET_ARP_STAT_CMDID,
+ /** get tx power for the current vdev */
+ WMI_VDEV_GET_TX_POWER_CMDID,
+ /* limit STA offchannel activity */
+ WMI_VDEV_LIMIT_OFFCHAN_CMDID,
+ /** To set custom software retries per-AC for vdev */
+ WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID,
+ /** To set chainmask configuration for vdev */
+ WMI_VDEV_CHAINMASK_CONFIG_CMDID,
+ WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID,
+ /* request LTE-Coex info */
+ WMI_VDEV_GET_MWS_COEX_INFO_CMDID,
+ /** delete all peer (excluding bss peer) */
+ WMI_VDEV_DELETE_ALL_PEER_CMDID,
+ /* To set bss max idle time related parameters */
+ WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID,
+ /** Indicates firmware to trigger Audio sync */
+ WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID,
+ /** Gives Qtimer value to firmware */
+ WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID,
+ /** Preferred channel list for each vdev */
+ WMI_VDEV_SET_PCL_CMDID,
+ /** VDEV_GET_BIG_DATA_CMD IS DEPRECATED - DO NOT USE */
+ WMI_VDEV_GET_BIG_DATA_CMDID,
+ /** Get per vdev BIG DATA stats phase 2 */
+ WMI_VDEV_GET_BIG_DATA_P2_CMDID,
+ /** set TPC PSD/non-PSD power */
+ WMI_VDEV_SET_TPC_POWER_CMDID,
WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER),
WMI_PEER_DELETE_CMDID,
WMI_PEER_FLUSH_TIDS_CMDID,
@@ -1860,6 +1891,8 @@ enum wmi_tlv_tag {
WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9,
WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT,
+ WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5,
+ WMI_TAG_VDEV_CH_POWER_INFO,

WMI_TAG_MAX
};
@@ -3128,6 +3161,31 @@ struct wlan_ssid {
u8 ssid[WLAN_SSID_MAX_LEN];
};

+struct wmi_vdev_ch_power_info {
+ u32 tlv_header;
+ u32 chan_cfreq; /* Channel center frequency (MHz) */
+ /* Unit: dBm, either PSD/EIRP power for this frequency or
+ * incremental for non-PSD BW
+ */
+ u32 tx_power;
+} __packed;
+
+struct wmi_vdev_set_tpc_power_cmd {
+ u32 tlv_header;
+ u32 vdev_id;
+ u32 psd_power; /* Value: 0 or 1, is PSD power or not */
+ u32 eirp_power; /* Maximum EIRP power (dBm units), valid only if power is PSD */
+ u32 power_type_6ghz; /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */
+ /* This fixed_param TLV is followed by the below TLVs:
+ * num_pwr_levels of wmi_vdev_ch_power_info
+ * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks).
+ * For non-PSD power, the power values are for 20, 40, and till
+ * BSS BW power levels.
+ * The num_pwr_levels will be checked by sw how many elements present
+ * in the variable-length array.
+ */
+} __packed;
+
#define WMI_IE_BITMAP_SIZE 8

#define WMI_SCAN_MAX_NUM_SSID 0x0A
@@ -5736,4 +5794,7 @@ void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
struct cur_regulatory_info *reg_info,
enum ieee80211_ap_reg_power power_type);
+int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
+ u32 vdev_id,
+ struct ath11k_reg_tpc_power_info *param);
#endif
--
2.31.1

2022-09-13 05:17:14

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 12/15] wifi: ath11k: add WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT service bit

Firmware advertises support for SERVICE_EXT_TPC_REG via a WMI service bit.
Add the definition of this service bit so that a subsequent patch can
check whether or not firmware supports this service.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/wmi.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 2c50dc8c3834..d285d18e99a8 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -2090,6 +2090,8 @@ enum wmi_tlv_service {
/* The second 128 bits */
WMI_MAX_EXT_SERVICE = 256,

+ WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280,
+
WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,

/* The third 128 bits */
--
2.31.1

2022-09-13 05:17:20

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 13/15] wifi: ath11k: discard BSS_CHANGED_TXPOWER when EXT_TPC_REG_SUPPORT for 6 GHz

When station is connected to a 6 GHz AP, it has 2 way to configure
the power limit to firmware. The first way is to send 2 wmi command
WMI_PDEV_PARAM_TXPOWER_LIMIT2G/WMI_PDEV_PARAM_TXPOWER_LIMIT5G to
firmware, the second way is to send WMI_VDEV_SET_TPC_POWER_CMDID to
firmware which include more parameters for power control.

When firmware support SERVICE_EXT_TPC_REG, it means firmware support
the second way for WMI_VDEV_SET_TPC_POWER_CMDID, then ath11k discard
BSS_CHANGED_TXPOWER flag from mac80211 which is used to the first way
for 6 GHz band in this patch and select the second way in the subsequent
patch.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/hw.c | 6 ++++++
drivers/net/wireless/ath/ath11k/hw.h | 1 +
drivers/net/wireless/ath/ath11k/mac.c | 19 +++++++++++++++++--
3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index 712818edb4ca..5734fbfc6db5 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -812,6 +812,12 @@ bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar)
return ath11k_hw_supports_cc_ext(ar->ab) && ar->supports_6ghz;
}

+bool ath11k_hw_supports_tpc_ext(struct ath11k *ar)
+{
+ return ath11k_hw_supports_6g_cc_ext(ar) &&
+ test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map);
+}
+
const struct ath11k_hw_ops ipq8074_ops = {
.get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id,
.wmi_init_config = ath11k_init_wmi_config_ipq8074,
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 969d29aaee6a..909ddd0fa187 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -279,6 +279,7 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,

bool ath11k_hw_supports_cc_ext(struct ath11k_base *ab);
bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar);
+bool ath11k_hw_supports_tpc_ext(struct ath11k *ar);

struct ath11k_fw_ie {
__le32 id;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index e1bdfe466bc7..54d7327265d6 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -3078,6 +3078,16 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar,
return 0;
}

+static bool ath11k_mac_supports_station_tpc(struct ath11k *ar,
+ struct ath11k_vif *arvif,
+ const struct cfg80211_chan_def *chandef)
+{
+ return ath11k_hw_supports_tpc_ext(ar) &&
+ arvif->vdev_type == WMI_VDEV_TYPE_STA &&
+ chandef->chan &&
+ chandef->chan->band == NL80211_BAND_6GHZ;
+}
+
static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
@@ -3268,8 +3278,13 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n",
arvif->vdev_id, info->txpower);

- arvif->txpower = info->txpower;
- ath11k_mac_txpower_recalc(ar);
+ if (ath11k_mac_supports_station_tpc(ar, arvif, &info->chandef)) {
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "discard tx power, change to set TPC power\n");
+ } else {
+ arvif->txpower = info->txpower;
+ ath11k_mac_txpower_recalc(ar);
+ }
}

if (changed & BSS_CHANGED_PS &&
--
2.31.1

2022-09-13 05:18:08

by Wen Gong

[permalink] [raw]
Subject: [PATCH v3 15/15] wifi: ath11k: send TPC power to firmware for 6 GHz station

When station is connected to a 6 GHz AP, it has 2 way to configure
the power limit to firmware. The first way is to send 2 wmi command
WMI_PDEV_PARAM_TXPOWER_LIMIT2G/WMI_PDEV_PARAM_TXPOWER_LIMIT5G to
firmware, the second way is to send WMI_VDEV_SET_TPC_POWER_CMDID to
firmware which include more parameters for power control.

The first way is disabled in previous patch
"ath11k: discard BSS_CHANGED_TXPOWER when EXT_TPC_REG_SUPPORT for 6 GHz".

Prepare the parameter for wmi command WMI_VDEV_SET_TPC_POWER_CMDID and
send the firmware after vdev start response success from firmware, it
is for the second way of power control.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/mac.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 54d7327265d6..1d9eebfe84fa 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -6724,6 +6724,12 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
return ret;
}

+ if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) {
+ ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
+ ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
+ &arvif->reg_tpc_info);
+ }
+
if (!restart)
ar->num_started_vdevs++;

@@ -7494,7 +7500,7 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
if (power_type == IEEE80211_REG_UNSET_AP)
power_type = IEEE80211_REG_LPI_AP;
ath11k_reg_handle_chan_list(ab, reg_info, power_type);
-
+ arvif->chanctx = *ctx;
ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
}

--
2.31.1

2022-09-13 07:17:11

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v3 00/15] wifi: ath11k: add support for 6 GHz station for various modes : LPI, SP and VLP

Wen Gong <[email protected]> writes:

> v3:
> 1. added "ath11k: fix a possible dead lock caused by ab->base_lock".
> 3. deleted "ath11k: add support for extended wmi service bit" which is alreay upstream.
>
> v2:
> 1. change some minor comments by Kalle.
> 2. rebased to ath.git ath-202112220603
>
> Depends on one patch:
> [v5] cfg80211: save power spectral density(psd) of regulatory rule
> https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/

The dependency is in "Changes Requested" state so I'll mark this the
same.

--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches