Message ID | 20230919071724.15505-2-quic_aisr@quicinc.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kalle Valo |
Headers | show |
Series | wifi: ath12k: add support for 6 GHz AP for various power modes | expand |
On 9/19/2023 12:17 AM, Aishwarya R wrote: > There are 3 types of regulatory rules for AP mode and 6 types for > STATION mode. This is to add wmi_vdev_type and ieee80211_ap_reg_power > to select the exact reg rules. > > Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Wen, Can you provide a "Tested-on: WCN7850" tag for this series? > > Signed-off-by: Aishwarya R <quic_aisr@quicinc.com> > --- > drivers/net/wireless/ath/ath12k/reg.c | 62 +++++++-- > drivers/net/wireless/ath/ath12k/reg.h | 6 +- > drivers/net/wireless/ath/ath12k/wmi.c | 182 +++++++++++++++++++++++++- > drivers/net/wireless/ath/ath12k/wmi.h | 27 +++- > 4 files changed, 257 insertions(+), 20 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c > index 6ede91ebc8e1..8501f77eee55 100644 > --- a/drivers/net/wireless/ath/ath12k/reg.c > +++ b/drivers/net/wireless/ath/ath12k/reg.c > @@ -28,6 +28,21 @@ static const struct ieee80211_regdomain ath12k_world_regd = { > } > }; > > +enum wmi_reg_6g_ap_type > +ath12k_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_STD_POWER_AP; > + case IEEE80211_REG_VLP_AP: > + return WMI_REG_VLP_AP; > + default: > + return WMI_REG_MAX_AP_TYPE; > + } > +} > + > static bool ath12k_regdom_changes(struct ath12k *ar, char *alpha2) > { > const struct ieee80211_regdomain *regd; > @@ -562,14 +577,16 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, > > struct ieee80211_regdomain * > ath12k_reg_build_regd(struct ath12k_base *ab, > - struct ath12k_reg_info *reg_info, bool intersect) > + struct ath12k_reg_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 ath12k_reg_rule *reg_rule; > + struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz; > u8 i = 0, j = 0, k = 0; > u8 num_rules; > u16 max_bw; > - u32 flags; > + u32 flags, reg_6ghz_number, max_bw_6ghz; > char alpha2[3]; > > num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; > @@ -578,8 +595,33 @@ ath12k_reg_build_regd(struct ath12k_base *ab, > * This can be updated to choose the combination dynamically based on AP > * type and client type, after complete 6G 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 = ath12k_ieee80211_ap_pwr_type_convert(power_type); > + > + if (ap_type == WMI_REG_MAX_AP_TYPE) > + ap_type = WMI_REG_INDOOR_AP; where is power_type coming from and can it be tainted? if we always expect a valid value then why not just have the conversion function set the default to WMI_REG_INDOOR_AP? Are there places in upcoming patches that actually perform error handling if the conversion function returns MAX? > + reg_6ghz_number = reg_info->num_6g_reg_rules_cl > + [ap_type][WMI_REG_DEFAULT_CLIENT]; please avoid splitting lines in the middle of a variable reference; that decreases the readability of the code. It is better to exceed some arbitrary line length guideline. you can use a client_type = WMI_REG_DEFAULT_CLIENT assignment to help reduce the length. if the line is still exceedingly long, split the lines after = repeat for all cases that follow > + if (reg_6ghz_number == 0) { > + ap_type = WMI_REG_INDOOR_AP; > + reg_6ghz_number = reg_info->num_6g_reg_rules_cl > + [ap_type][WMI_REG_DEFAULT_CLIENT]; > + } > + reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr > + [ap_type][WMI_REG_DEFAULT_CLIENT]; > + max_bw_6ghz = reg_info->max_bw_6g_client > + [ap_type][WMI_REG_DEFAULT_CLIENT]; > + } else { > + reg_6ghz_number = reg_info->num_6g_reg_rules_ap > + [WMI_REG_INDOOR_AP]; > + reg_rule_6ghz = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP]; > + max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]; > + } > + num_rules += reg_6ghz_number; > + } > > if (!num_rules) > goto ret; > @@ -626,12 +668,10 @@ ath12k_reg_build_regd(struct ath12k_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_6ghz_number && > + (k < reg_6ghz_number)) { > + reg_rule = reg_rule_6ghz + k++; > + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); > flags = NL80211_RRF_AUTO_BW; > } else { > break; > diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h > index 56d009a47234..56324e30a358 100644 > --- a/drivers/net/wireless/ath/ath12k/reg.h > +++ b/drivers/net/wireless/ath/ath12k/reg.h > @@ -88,7 +88,11 @@ void ath12k_reg_free(struct ath12k_base *ab); > void ath12k_regd_update_work(struct work_struct *work); > struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab, > struct ath12k_reg_info *reg_info, > - bool intersect); > + bool intersect, > + enum wmi_vdev_type vdev_type, > + enum ieee80211_ap_reg_power power_type); > +enum wmi_reg_6g_ap_type > +ath12k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type); > int ath12k_regd_update(struct ath12k *ar, bool init); > int ath12k_reg_update_chan_list(struct ath12k *ar); > > diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c > index af910296c41e..1b9ce9a2ae96 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.c > +++ b/drivers/net/wireless/ath/ath12k/wmi.c > @@ -4152,6 +4152,119 @@ static struct ath12k_reg_rule > return reg_rule_ptr; > } > > +static char *ath12k_cc_status_to_str(enum ath12k_reg_cc_code code) all of these *_str() functions should return const char * > +{ > + 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 char *ath12k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id) > +{ > + switch (domain_id) { > + case FCC1_6GHZ: > + return "FCC1_6GHZ"; > + case ETSI1_6GHZ: > + return "ETSI1_6GHZ"; > + case ETSI2_6GHZ: > + return "ETSI2_6GHZ"; > + case APL1_6GHZ: > + return "APL1_6GHZ"; > + case FCC1_6GHZ_CL: > + return "FCC1_6GHZ_CL"; > + default: > + return "unknown domain id"; > + } > +} > + > +static char *ath12k_6ghz_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 *ath12k_6ghz_ap_type_to_str(enum wmi_reg_6g_ap_type type) > +{ > + switch (type) { > + case WMI_REG_INDOOR_AP: > + return "INDOOR AP"; > + case WMI_REG_STD_POWER_AP: > + return "STANDARD POWER AP"; > + case WMI_REG_VLP_AP: > + return "VERY LOW POWER AP"; > + default: > + return "unknown AP type"; > + } > +} > + > +static char *ath12k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id) > +{ > + switch (sub_id) { > + case FCC1_CLIENT_LPI_REGULAR_6GHZ: > + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; > + case FCC1_CLIENT_SP_6GHZ: > + return "FCC1_CLIENT_SP_6GHZ"; > + case FCC1_AP_LPI_6GHZ: > + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; > + case FCC1_AP_SP_6GHZ: > + return "FCC1_AP_SP_6GHZ"; > + case ETSI1_LPI_6GHZ: > + return "ETSI1_LPI_6GHZ"; > + case ETSI1_VLP_6GHZ: > + return "ETSI1_VLP_6GHZ"; > + case ETSI2_LPI_6GHZ: > + return "ETSI2_LPI_6GHZ"; > + case ETSI2_VLP_6GHZ: > + return "ETSI2_VLP_6GHZ"; > + case APL1_LPI_6GHZ: > + return "APL1_LPI_6GHZ"; > + case APL1_VLP_6GHZ: > + return "APL1_VLP_6GHZ"; > + case EMPTY_6GHZ: > + return "N/A"; > + default: > + return "unknown sub reg id"; > + } > +} > + > +static void ath12k_print_reg_rule(struct ath12k_base *ab, char *prev, > + u32 num_reg_rules, > + struct ath12k_reg_rule *reg_rule_ptr) > +{ > + struct ath12k_reg_rule *reg_rule = reg_rule_ptr; > + u32 count; > + > + ath12k_dbg(ab, ATH12K_DBG_WMI, "%s reg rules number %d\n", prev, num_reg_rules); > + > + for (count = 0; count < num_reg_rules; count++) { > + ath12k_dbg(ab, ATH12K_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 int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > struct sk_buff *skb, > struct ath12k_reg_info *reg_info) > @@ -4163,7 +4276,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; > u32 num_6g_reg_rules_cl[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; > u32 total_reg_rules = 0; > - int ret, i, j; > + int ret, i, j, skip_6ghz_rules_in_5ghz_rules = 0; > > ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); > > @@ -4265,6 +4378,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > * from 5G rules list. > */ > if (memcmp(reg_info->alpha2, "US", 2) == 0) { > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "US 5 GHz reg rules number %d from fw", > + reg_info->num_5g_reg_rules); > + > + if (reg_info->num_5g_reg_rules > REG_US_5G_NUM_REG_RULES) > + skip_6ghz_rules_in_5ghz_rules = reg_info->num_5g_reg_rules - > + REG_US_5G_NUM_REG_RULES; I'd split this line after = instead of in the middle of the expression > reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES; > num_5g_reg_rules = reg_info->num_5g_reg_rules; > } > @@ -4297,6 +4417,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > break; > } > > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "%s: status_code %s", __func__, > + ath12k_cc_status_to_str(reg_info->status_code)); > + > reg_info->is_ext_reg_event = true; > > reg_info->min_bw_2g = le32_to_cpu(ev->min_bw_2g); > @@ -4325,6 +4449,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > le32_to_cpu(ev->max_bw_6g_client_vlp[i]); > } > > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "%s: status_code %s", __func__, > + ath12k_cc_status_to_str(reg_info->status_code)); > + > ath12k_dbg(ab, ATH12K_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, > @@ -4368,10 +4496,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); > return -ENOMEM; > } > + ath12k_print_reg_rule(ab, "2 GHz", > + num_2g_reg_rules, > + reg_info->reg_rules_2g_ptr); > } > + ext_wmi_reg_rule += num_2g_reg_rules; > > 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); > @@ -4381,9 +4512,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); > return -ENOMEM; > } > + ath12k_print_reg_rule(ab, "5 GHz", > + num_5g_reg_rules, > + reg_info->reg_rules_5g_ptr); > } > > - ext_wmi_reg_rule += num_5g_reg_rules; > + /* We have adjusted the number of 5 GHz reg rules via the hack above. > + * Here, we adjust that many extra rules which came with 5g reg rules > + * (for cc: US) > + * > + * NOTE: skip_6ghz_rules_in_5ghz_rules will be 0 for rest other cases. > + */ > + ext_wmi_reg_rule += num_5g_reg_rules + skip_6ghz_rules_in_5ghz_rules; > > for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { > reg_info->reg_rules_6g_ap_ptr[i] = > @@ -4396,10 +4536,17 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > return -ENOMEM; > } > > + ath12k_print_reg_rule(ab, ath12k_6ghz_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++) { > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "AP type %s", ath12k_6ghz_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_reg_rules_cl[j][i], > @@ -4411,6 +4558,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > return -ENOMEM; > } > > + ath12k_print_reg_rule(ab, ath12k_6ghz_client_type_to_str(i), > + num_6g_reg_rules_cl[j][i], > + reg_info->reg_rules_6g_client_ptr[j][i]); > + > ext_wmi_reg_rule += num_6g_reg_rules_cl[j][i]; > } > } > @@ -4425,6 +4576,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > reg_info->domain_code_6g_ap[WMI_REG_VLP_AP] = > le32_to_cpu(ev->domain_code_6g_ap_vlp); > > + ath12k_dbg(ab, ATH12K_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", > + ath12k_6ghz_client_type_to_str(reg_info->client_type), > + reg_info->rnr_tpe_usable, > + reg_info->unspecified_ap_usable, > + ath12k_sub_reg_6ghz_to_str > + (le32_to_cpu(ev->domain_code_6g_ap_lpi)), > + ath12k_sub_reg_6ghz_to_str > + (le32_to_cpu(ev->domain_code_6g_ap_sp)), > + ath12k_sub_reg_6ghz_to_str > + (le32_to_cpu(ev->domain_code_6g_ap_vlp))); avoid splitting the line between a function and it's parameters. it is ok to split in the middle of a parameter list, but the first parameter should almost always be on the same line as the function > + > for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { > reg_info->domain_code_6g_client[WMI_REG_INDOOR_AP][i] = > le32_to_cpu(ev->domain_code_6g_client_lpi[i]); > @@ -4432,12 +4595,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, > le32_to_cpu(ev->domain_code_6g_client_sp[i]); > reg_info->domain_code_6g_client[WMI_REG_VLP_AP][i] = > le32_to_cpu(ev->domain_code_6g_client_vlp[i]); > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "6 GHz AP BW: lpi %d - %d sp %d - %d vlp %d - %d\n", > + ev->min_bw_6g_ap_lpi, ev->max_bw_6g_ap_lpi, > + ev->min_bw_6g_ap_sp, ev->max_bw_6g_ap_sp, > + ev->min_bw_6g_ap_vlp, ev->max_bw_6g_ap_vlp); > } > > reg_info->domain_code_6g_super_id = le32_to_cpu(ev->domain_code_6g_super_id); > > - ath12k_dbg(ab, ATH12K_DBG_WMI, "6g client_type: %d domain_code_6g_super_id: %d", > - reg_info->client_type, reg_info->domain_code_6g_super_id); > + ath12k_dbg(ab, ATH12K_DBG_WMI, "6 GHz client_type: %s 6 GHz super domain %s", > + ath12k_6ghz_client_type_to_str(reg_info->client_type), > + ath12k_super_reg_6ghz_to_str(reg_info->domain_code_6g_super_id)); > > ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n"); > > @@ -5192,7 +5361,8 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk > !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) > intersect = true; > > - regd = ath12k_reg_build_regd(ab, reg_info, intersect); > + regd = ath12k_reg_build_regd(ab, reg_info, intersect, > + WMI_VDEV_TYPE_AP, IEEE80211_REG_UNSET_AP); why is this forced to AP? where is logic for client? > if (!regd) { > ath12k_warn(ab, "failed to build regd from reg_info\n"); > goto fallback; > diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h > index 08a8c9e0f59f..966e6ba4e162 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.h > +++ b/drivers/net/wireless/ath/ath12k/wmi.h > @@ -2832,8 +2832,8 @@ struct ath12k_wmi_rx_reorder_queue_remove_arg { > #define REG_RULE_MAX_BW 0x0000ffff > #define REG_RULE_REG_PWR 0x00ff0000 > #define REG_RULE_ANT_GAIN 0xff000000 > -#define REG_RULE_PSD_INFO BIT(2) > -#define REG_RULE_PSD_EIRP 0xffff0000 > +#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) > @@ -3844,6 +3844,29 @@ enum { > WMI_REG_SET_CC_STATUS_FAIL = 5, > }; > > +enum reg_subdomains_6ghz { > + EMPTY_6GHZ = 0x0, > + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, > + FCC1_CLIENT_SP_6GHZ = 0x02, > + FCC1_AP_LPI_6GHZ = 0x03, > + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, > + FCC1_AP_SP_6GHZ = 0x04, > + ETSI1_LPI_6GHZ = 0x10, > + ETSI1_VLP_6GHZ = 0x11, > + ETSI2_LPI_6GHZ = 0x12, > + ETSI2_VLP_6GHZ = 0x13, > + APL1_LPI_6GHZ = 0x20, > + APL1_VLP_6GHZ = 0x21, > +}; > + > +enum reg_super_domain_6ghz { > + FCC1_6GHZ = 0x01, > + ETSI1_6GHZ = 0x02, > + ETSI2_6GHZ = 0x03, > + APL1_6GHZ = 0x04, > + FCC1_6GHZ_CL = 0x05, > +}; > + > #define WMI_REG_CLIENT_MAX 4 > > struct wmi_reg_chan_list_cc_ext_event {
On 9/20/2023 1:47 AM, Jeff Johnson wrote: > On 9/19/2023 12:17 AM, Aishwarya R wrote: >> There are 3 types of regulatory rules for AP mode and 6 types for >> STATION mode. This is to add wmi_vdev_type and ieee80211_ap_reg_power >> to select the exact reg rules. >> >> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 > > Wen, > Can you provide a "Tested-on: WCN7850" tag for this series? > Jeff, it is this tag below. I have not tested this serials on WCN7850. Should I test this serials on WCN7850? Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 [...]
On 9/19/2023 12:17 AM, Aishwarya R wrote: >> There are 3 types of regulatory rules for AP mode and 6 types for >> STATION mode. This is to add wmi_vdev_type and ieee80211_ap_reg_power >> to select the exact reg rules. >> >> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 > Wen, > Can you provide a "Tested-on: WCN7850" tag for this series? >> >> Signed-off-by: Aishwarya R <quic_aisr@quicinc.com> >> --- >> drivers/net/wireless/ath/ath12k/reg.c | 62 +++++++-- >> drivers/net/wireless/ath/ath12k/reg.h | 6 +- >> drivers/net/wireless/ath/ath12k/wmi.c | 182 +++++++++++++++++++++++++- >> drivers/net/wireless/ath/ath12k/wmi.h | 27 +++- >> 4 files changed, 257 insertions(+), 20 deletions(-) >> >> diff --git a/drivers/net/wireless/ath/ath12k/reg.c >> b/drivers/net/wireless/ath/ath12k/reg.c >> index 6ede91ebc8e1..8501f77eee55 100644 >> --- a/drivers/net/wireless/ath/ath12k/reg.c >> +++ b/drivers/net/wireless/ath/ath12k/reg.c >> @@ -28,6 +28,21 @@ static const struct ieee80211_regdomain ath12k_world_regd = { >> } >> }; >> >> +enum wmi_reg_6g_ap_type >> +ath12k_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_STD_POWER_AP; >> + case IEEE80211_REG_VLP_AP: >> + return WMI_REG_VLP_AP; >> + default: >> + return WMI_REG_MAX_AP_TYPE; >> + } >> +} >> + >> static bool ath12k_regdom_changes(struct ath12k *ar, char *alpha2) >> { >> const struct ieee80211_regdomain *regd; @@ -562,14 +577,16 @@ >> ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, >> >> struct ieee80211_regdomain * >> ath12k_reg_build_regd(struct ath12k_base *ab, >> - struct ath12k_reg_info *reg_info, bool intersect) >> + struct ath12k_reg_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 ath12k_reg_rule *reg_rule; >> + struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz; >> u8 i = 0, j = 0, k = 0; >> u8 num_rules; >> u16 max_bw; >> - u32 flags; >> + u32 flags, reg_6ghz_number, max_bw_6ghz; >> char alpha2[3]; >> >> num_rules = reg_info->num_5g_reg_rules + >> reg_info->num_2g_reg_rules; @@ -578,8 +595,33 @@ ath12k_reg_build_regd(struct ath12k_base *ab, >> * This can be updated to choose the combination dynamically based on AP >> * type and client type, after complete 6G 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 = ath12k_ieee80211_ap_pwr_type_convert(power_type); >> + >> + if (ap_type == WMI_REG_MAX_AP_TYPE) >> + ap_type = WMI_REG_INDOOR_AP; > where is power_type coming from and can it be tainted? > if we always expect a valid value then why not just have the conversion function set the default to WMI_REG_INDOOR_AP? > Are there places in upcoming patches that actually perform error handling if the conversion function returns MAX? >> + reg_6ghz_number = reg_info->num_6g_reg_rules_cl >> + [ap_type][WMI_REG_DEFAULT_CLIENT]; > please avoid splitting lines in the middle of a variable reference; that decreases the readability of the code. It is better to exceed some arbitrary line length guideline. you can use a client_type = WMI_REG_DEFAULT_CLIENT assignment to help reduce the length. > if the line is still exceedingly long, split the lines after = > repeat for all cases that follow >> + if (reg_6ghz_number == 0) { >> + ap_type = WMI_REG_INDOOR_AP; >> + reg_6ghz_number = reg_info->num_6g_reg_rules_cl >> + [ap_type][WMI_REG_DEFAULT_CLIENT]; >> + } >> + reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr >> + [ap_type][WMI_REG_DEFAULT_CLIENT]; >> + max_bw_6ghz = reg_info->max_bw_6g_client >> + [ap_type][WMI_REG_DEFAULT_CLIENT]; >> + } else { >> + reg_6ghz_number = reg_info->num_6g_reg_rules_ap >> + [WMI_REG_INDOOR_AP]; >> + reg_rule_6ghz = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP]; >> + max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]; >> + } >> + num_rules += reg_6ghz_number; >> + } >> >> if (!num_rules) >> goto ret; >> @@ -626,12 +668,10 @@ ath12k_reg_build_regd(struct ath12k_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_6ghz_number && >> + (k < reg_6ghz_number)) { >> + reg_rule = reg_rule_6ghz + k++; >> + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); >> flags = NL80211_RRF_AUTO_BW; >> } else { >> break; >> diff --git a/drivers/net/wireless/ath/ath12k/reg.h >> b/drivers/net/wireless/ath/ath12k/reg.h >> index 56d009a47234..56324e30a358 100644 >> --- a/drivers/net/wireless/ath/ath12k/reg.h >> +++ b/drivers/net/wireless/ath/ath12k/reg.h >> @@ -88,7 +88,11 @@ void ath12k_reg_free(struct ath12k_base *ab); >> void ath12k_regd_update_work(struct work_struct *work); >> struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab, >> struct ath12k_reg_info *reg_info, >> - bool intersect); >> + bool intersect, >> + enum wmi_vdev_type vdev_type, >> + enum ieee80211_ap_reg_power power_type); enum >> +wmi_reg_6g_ap_type ath12k_ieee80211_ap_pwr_type_convert(enum >> +ieee80211_ap_reg_power power_type); >> int ath12k_regd_update(struct ath12k *ar, bool init); >> int ath12k_reg_update_chan_list(struct ath12k *ar); >> >> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c >> b/drivers/net/wireless/ath/ath12k/wmi.c >> index af910296c41e..1b9ce9a2ae96 100644 >> --- a/drivers/net/wireless/ath/ath12k/wmi.c >> +++ b/drivers/net/wireless/ath/ath12k/wmi.c >> @@ -4152,6 +4152,119 @@ static struct ath12k_reg_rule >> return reg_rule_ptr; >> } >> >> +static char *ath12k_cc_status_to_str(enum ath12k_reg_cc_code code) > all of these *_str() functions should return const char * >> +{ >> + 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 char *ath12k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz >> +domain_id) { >> + switch (domain_id) { >> + case FCC1_6GHZ: >> + return "FCC1_6GHZ"; >> + case ETSI1_6GHZ: >> + return "ETSI1_6GHZ"; >> + case ETSI2_6GHZ: >> + return "ETSI2_6GHZ"; >> + case APL1_6GHZ: >> + return "APL1_6GHZ"; >> + case FCC1_6GHZ_CL: >> + return "FCC1_6GHZ_CL"; >> + default: >> + return "unknown domain id"; >> + } >> +} >> + >> +static char *ath12k_6ghz_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 *ath12k_6ghz_ap_type_to_str(enum wmi_reg_6g_ap_type type) >> +{ >> + switch (type) { >> + case WMI_REG_INDOOR_AP: >> + return "INDOOR AP"; >> + case WMI_REG_STD_POWER_AP: >> + return "STANDARD POWER AP"; >> + case WMI_REG_VLP_AP: >> + return "VERY LOW POWER AP"; >> + default: >> + return "unknown AP type"; >> + } >> +} >> + >> +static char *ath12k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz >> +sub_id) { >> + switch (sub_id) { >> + case FCC1_CLIENT_LPI_REGULAR_6GHZ: >> + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; >> + case FCC1_CLIENT_SP_6GHZ: >> + return "FCC1_CLIENT_SP_6GHZ"; >> + case FCC1_AP_LPI_6GHZ: >> + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; >> + case FCC1_AP_SP_6GHZ: >> + return "FCC1_AP_SP_6GHZ"; >> + case ETSI1_LPI_6GHZ: >> + return "ETSI1_LPI_6GHZ"; >> + case ETSI1_VLP_6GHZ: >> + return "ETSI1_VLP_6GHZ"; >> + case ETSI2_LPI_6GHZ: >> + return "ETSI2_LPI_6GHZ"; >> + case ETSI2_VLP_6GHZ: >> + return "ETSI2_VLP_6GHZ"; >> + case APL1_LPI_6GHZ: >> + return "APL1_LPI_6GHZ"; >> + case APL1_VLP_6GHZ: >> + return "APL1_VLP_6GHZ"; >> + case EMPTY_6GHZ: >> + return "N/A"; >> + default: >> + return "unknown sub reg id"; >> + } >> +} >> + >> +static void ath12k_print_reg_rule(struct ath12k_base *ab, char *prev, >> + u32 num_reg_rules, >> + struct ath12k_reg_rule *reg_rule_ptr) { >> + struct ath12k_reg_rule *reg_rule = reg_rule_ptr; >> + u32 count; >> + >> + ath12k_dbg(ab, ATH12K_DBG_WMI, "%s reg rules number %d\n", prev, >> +num_reg_rules); >> + >> + for (count = 0; count < num_reg_rules; count++) { >> + ath12k_dbg(ab, ATH12K_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 int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> struct sk_buff *skb, >> struct ath12k_reg_info *reg_info) @@ -4163,7 +4276,7 @@ >> static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; >> u32 num_6g_reg_rules_cl[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; >> u32 total_reg_rules = 0; >> - int ret, i, j; >> + int ret, i, j, skip_6ghz_rules_in_5ghz_rules = 0; >> >> ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel >> list\n"); >> >> @@ -4265,6 +4378,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> * from 5G rules list. >> */ >> if (memcmp(reg_info->alpha2, "US", 2) == 0) { >> + ath12k_dbg(ab, ATH12K_DBG_WMI, >> + "US 5 GHz reg rules number %d from fw", >> + reg_info->num_5g_reg_rules); >> + >> + if (reg_info->num_5g_reg_rules >> REG_US_5G_NUM_REG_RULES) >> + skip_6ghz_rules_in_5ghz_rules = reg_info->num_5g_reg_rules - >> + REG_US_5G_NUM_REG_RULES; > I'd split this line after = instead of in the middle of the expression >> reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES; >> num_5g_reg_rules = reg_info->num_5g_reg_rules; >> } >> @@ -4297,6 +4417,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> break; >> } >> >> + ath12k_dbg(ab, ATH12K_DBG_WMI, >> + "%s: status_code %s", __func__, >> + ath12k_cc_status_to_str(reg_info->status_code)); >> + >> reg_info->is_ext_reg_event = true; >> >> reg_info->min_bw_2g = le32_to_cpu(ev->min_bw_2g); @@ -4325,6 >> +4449,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> le32_to_cpu(ev->max_bw_6g_client_vlp[i]); >> } >> >> + ath12k_dbg(ab, ATH12K_DBG_WMI, >> + "%s: status_code %s", __func__, >> + ath12k_cc_status_to_str(reg_info->status_code)); >> + >> ath12k_dbg(ab, ATH12K_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, @@ -4368,10 >> +4496,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); >> return -ENOMEM; >> } >> + ath12k_print_reg_rule(ab, "2 GHz", >> + num_2g_reg_rules, >> + reg_info->reg_rules_2g_ptr); >> } >> + ext_wmi_reg_rule += num_2g_reg_rules; >> >> 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); >> @@ -4381,9 +4512,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); >> return -ENOMEM; >> } >> + ath12k_print_reg_rule(ab, "5 GHz", >> + num_5g_reg_rules, >> + reg_info->reg_rules_5g_ptr); >> } >> >> - ext_wmi_reg_rule += num_5g_reg_rules; >> + /* We have adjusted the number of 5 GHz reg rules via the hack above. >> + * Here, we adjust that many extra rules which came with 5g reg rules >> + * (for cc: US) >> + * >> + * NOTE: skip_6ghz_rules_in_5ghz_rules will be 0 for rest other cases. >> + */ >> + ext_wmi_reg_rule += num_5g_reg_rules + >> +skip_6ghz_rules_in_5ghz_rules; >> >> for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { >> reg_info->reg_rules_6g_ap_ptr[i] = @@ -4396,10 +4536,17 @@ static >> int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> return -ENOMEM; >> } >> >> + ath12k_print_reg_rule(ab, ath12k_6ghz_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++) { >> + ath12k_dbg(ab, ATH12K_DBG_WMI, >> + "AP type %s", ath12k_6ghz_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_reg_rules_cl[j][i], >> @@ -4411,6 +4558,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> return -ENOMEM; >> } >> >> + ath12k_print_reg_rule(ab, ath12k_6ghz_client_type_to_str(i), >> + num_6g_reg_rules_cl[j][i], >> + reg_info->reg_rules_6g_client_ptr[j][i]); >> + >> ext_wmi_reg_rule += num_6g_reg_rules_cl[j][i]; >> } >> } >> @@ -4425,6 +4576,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> reg_info->domain_code_6g_ap[WMI_REG_VLP_AP] = >> le32_to_cpu(ev->domain_code_6g_ap_vlp); >> >> + ath12k_dbg(ab, ATH12K_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", >> + ath12k_6ghz_client_type_to_str(reg_info->client_type), >> + reg_info->rnr_tpe_usable, >> + reg_info->unspecified_ap_usable, >> + ath12k_sub_reg_6ghz_to_str >> + (le32_to_cpu(ev->domain_code_6g_ap_lpi)), >> + ath12k_sub_reg_6ghz_to_str >> + (le32_to_cpu(ev->domain_code_6g_ap_sp)), >> + ath12k_sub_reg_6ghz_to_str >> + (le32_to_cpu(ev->domain_code_6g_ap_vlp))); > avoid splitting the line between a function and it's parameters. > it is ok to split in the middle of a parameter list, but the first parameter should almost always be on the same line as the function >> + >> for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { >> reg_info->domain_code_6g_client[WMI_REG_INDOOR_AP][i] = >> le32_to_cpu(ev->domain_code_6g_client_lpi[i]); >> @@ -4432,12 +4595,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, >> le32_to_cpu(ev->domain_code_6g_client_sp[i]); >> reg_info->domain_code_6g_client[WMI_REG_VLP_AP][i] = >> le32_to_cpu(ev->domain_code_6g_client_vlp[i]); >> + ath12k_dbg(ab, ATH12K_DBG_WMI, >> + "6 GHz AP BW: lpi %d - %d sp %d - %d vlp %d - %d\n", >> + ev->min_bw_6g_ap_lpi, ev->max_bw_6g_ap_lpi, >> + ev->min_bw_6g_ap_sp, ev->max_bw_6g_ap_sp, >> + ev->min_bw_6g_ap_vlp, ev->max_bw_6g_ap_vlp); >> } >> >> reg_info->domain_code_6g_super_id = >> le32_to_cpu(ev->domain_code_6g_super_id); >> >> - ath12k_dbg(ab, ATH12K_DBG_WMI, "6g client_type: %d domain_code_6g_super_id: %d", >> - reg_info->client_type, reg_info->domain_code_6g_super_id); >> + ath12k_dbg(ab, ATH12K_DBG_WMI, "6 GHz client_type: %s 6 GHz super domain %s", >> + ath12k_6ghz_client_type_to_str(reg_info->client_type), >> + >> +ath12k_super_reg_6ghz_to_str(reg_info->domain_code_6g_super_id)); >> >> ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel >> list\n"); >> >> @@ -5192,7 +5361,8 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk >> !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) >> intersect = true; >> >> - regd = ath12k_reg_build_regd(ab, reg_info, intersect); >> + regd = ath12k_reg_build_regd(ab, reg_info, intersect, >> + WMI_VDEV_TYPE_AP, IEEE80211_REG_UNSET_AP); > why is this forced to AP? > where is logic for client? >> if (!regd) { >> ath12k_warn(ab, "failed to build regd from reg_info\n"); >> goto fallback; >> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h >> b/drivers/net/wireless/ath/ath12k/wmi.h >> index 08a8c9e0f59f..966e6ba4e162 100644 >> --- a/drivers/net/wireless/ath/ath12k/wmi.h >> +++ b/drivers/net/wireless/ath/ath12k/wmi.h >> @@ -2832,8 +2832,8 @@ struct ath12k_wmi_rx_reorder_queue_remove_arg { >> #define REG_RULE_MAX_BW 0x0000ffff >> #define REG_RULE_REG_PWR 0x00ff0000 >> #define REG_RULE_ANT_GAIN 0xff000000 >> -#define REG_RULE_PSD_INFO BIT(2) >> -#define REG_RULE_PSD_EIRP 0xffff0000 >> +#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) @@ -3844,6 +3844,29 @@ >> enum { >> WMI_REG_SET_CC_STATUS_FAIL = 5, >> }; >> >> +enum reg_subdomains_6ghz { >> + EMPTY_6GHZ = 0x0, >> + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, >> + FCC1_CLIENT_SP_6GHZ = 0x02, >> + FCC1_AP_LPI_6GHZ = 0x03, >> + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, >> + FCC1_AP_SP_6GHZ = 0x04, >> + ETSI1_LPI_6GHZ = 0x10, >> + ETSI1_VLP_6GHZ = 0x11, >> + ETSI2_LPI_6GHZ = 0x12, >> + ETSI2_VLP_6GHZ = 0x13, >> + APL1_LPI_6GHZ = 0x20, >> + APL1_VLP_6GHZ = 0x21, >> +}; >> + >> +enum reg_super_domain_6ghz { >> + FCC1_6GHZ = 0x01, >> + ETSI1_6GHZ = 0x02, >> + ETSI2_6GHZ = 0x03, >> + APL1_6GHZ = 0x04, >> + FCC1_6GHZ_CL = 0x05, >> +}; >> + >> #define WMI_REG_CLIENT_MAX 4 >> >> struct wmi_reg_chan_list_cc_ext_event { Thanks for your review. I will address all your comments in next revision. Aishwarya
"Aishwarya R (QUIC)" <quic_aisr@quicinc.com> writes:
[deleting ~500 lines of unnecessary quotes]
> Thanks for your review. I will address all your comments in next revision.
Please edit your quotes and only include the necessary information in
your reply. These kind of huge mails make use of patchwork very
difficult:
https://patchwork.kernel.org/project/linux-wireless/patch/20230919071724.15505-2-quic_aisr@quicinc.com/
On 9/19/2023 7:10 PM, Wen Gong wrote: > On 9/20/2023 1:47 AM, Jeff Johnson wrote: >> On 9/19/2023 12:17 AM, Aishwarya R wrote: >>> There are 3 types of regulatory rules for AP mode and 6 types for >>> STATION mode. This is to add wmi_vdev_type and ieee80211_ap_reg_power >>> to select the exact reg rules. >>> >>> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 >> >> Wen, >> Can you provide a "Tested-on: WCN7850" tag for this series? >> > Jeff, it is this tag below. > > I have not tested this serials on WCN7850. > > Should I test this serials on WCN7850? For a large, non-trivial patchset such as this I'd like to be sure that both AP-focused and STA-focused products are tested before merge. /jeff
diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 6ede91ebc8e1..8501f77eee55 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -28,6 +28,21 @@ static const struct ieee80211_regdomain ath12k_world_regd = { } }; +enum wmi_reg_6g_ap_type +ath12k_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_STD_POWER_AP; + case IEEE80211_REG_VLP_AP: + return WMI_REG_VLP_AP; + default: + return WMI_REG_MAX_AP_TYPE; + } +} + static bool ath12k_regdom_changes(struct ath12k *ar, char *alpha2) { const struct ieee80211_regdomain *regd; @@ -562,14 +577,16 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, struct ieee80211_regdomain * ath12k_reg_build_regd(struct ath12k_base *ab, - struct ath12k_reg_info *reg_info, bool intersect) + struct ath12k_reg_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 ath12k_reg_rule *reg_rule; + struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz; u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; - u32 flags; + u32 flags, reg_6ghz_number, max_bw_6ghz; char alpha2[3]; num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; @@ -578,8 +595,33 @@ ath12k_reg_build_regd(struct ath12k_base *ab, * This can be updated to choose the combination dynamically based on AP * type and client type, after complete 6G 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 = ath12k_ieee80211_ap_pwr_type_convert(power_type); + + if (ap_type == WMI_REG_MAX_AP_TYPE) + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + if (reg_6ghz_number == 0) { + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } + reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr + [ap_type][WMI_REG_DEFAULT_CLIENT]; + max_bw_6ghz = reg_info->max_bw_6g_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } else { + reg_6ghz_number = reg_info->num_6g_reg_rules_ap + [WMI_REG_INDOOR_AP]; + reg_rule_6ghz = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP]; + max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]; + } + num_rules += reg_6ghz_number; + } if (!num_rules) goto ret; @@ -626,12 +668,10 @@ ath12k_reg_build_regd(struct ath12k_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_6ghz_number && + (k < reg_6ghz_number)) { + reg_rule = reg_rule_6ghz + k++; + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); flags = NL80211_RRF_AUTO_BW; } else { break; diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index 56d009a47234..56324e30a358 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -88,7 +88,11 @@ void ath12k_reg_free(struct ath12k_base *ab); void ath12k_regd_update_work(struct work_struct *work); struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab, struct ath12k_reg_info *reg_info, - bool intersect); + bool intersect, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); +enum wmi_reg_6g_ap_type +ath12k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type); int ath12k_regd_update(struct ath12k *ar, bool init); int ath12k_reg_update_chan_list(struct ath12k *ar); diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index af910296c41e..1b9ce9a2ae96 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4152,6 +4152,119 @@ static struct ath12k_reg_rule return reg_rule_ptr; } +static char *ath12k_cc_status_to_str(enum ath12k_reg_cc_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 char *ath12k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id) +{ + switch (domain_id) { + case FCC1_6GHZ: + return "FCC1_6GHZ"; + case ETSI1_6GHZ: + return "ETSI1_6GHZ"; + case ETSI2_6GHZ: + return "ETSI2_6GHZ"; + case APL1_6GHZ: + return "APL1_6GHZ"; + case FCC1_6GHZ_CL: + return "FCC1_6GHZ_CL"; + default: + return "unknown domain id"; + } +} + +static char *ath12k_6ghz_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 *ath12k_6ghz_ap_type_to_str(enum wmi_reg_6g_ap_type type) +{ + switch (type) { + case WMI_REG_INDOOR_AP: + return "INDOOR AP"; + case WMI_REG_STD_POWER_AP: + return "STANDARD POWER AP"; + case WMI_REG_VLP_AP: + return "VERY LOW POWER AP"; + default: + return "unknown AP type"; + } +} + +static char *ath12k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id) +{ + switch (sub_id) { + case FCC1_CLIENT_LPI_REGULAR_6GHZ: + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; + case FCC1_CLIENT_SP_6GHZ: + return "FCC1_CLIENT_SP_6GHZ"; + case FCC1_AP_LPI_6GHZ: + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; + case FCC1_AP_SP_6GHZ: + return "FCC1_AP_SP_6GHZ"; + case ETSI1_LPI_6GHZ: + return "ETSI1_LPI_6GHZ"; + case ETSI1_VLP_6GHZ: + return "ETSI1_VLP_6GHZ"; + case ETSI2_LPI_6GHZ: + return "ETSI2_LPI_6GHZ"; + case ETSI2_VLP_6GHZ: + return "ETSI2_VLP_6GHZ"; + case APL1_LPI_6GHZ: + return "APL1_LPI_6GHZ"; + case APL1_VLP_6GHZ: + return "APL1_VLP_6GHZ"; + case EMPTY_6GHZ: + return "N/A"; + default: + return "unknown sub reg id"; + } +} + +static void ath12k_print_reg_rule(struct ath12k_base *ab, char *prev, + u32 num_reg_rules, + struct ath12k_reg_rule *reg_rule_ptr) +{ + struct ath12k_reg_rule *reg_rule = reg_rule_ptr; + u32 count; + + ath12k_dbg(ab, ATH12K_DBG_WMI, "%s reg rules number %d\n", prev, num_reg_rules); + + for (count = 0; count < num_reg_rules; count++) { + ath12k_dbg(ab, ATH12K_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 int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, struct sk_buff *skb, struct ath12k_reg_info *reg_info) @@ -4163,7 +4276,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; u32 num_6g_reg_rules_cl[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; u32 total_reg_rules = 0; - int ret, i, j; + int ret, i, j, skip_6ghz_rules_in_5ghz_rules = 0; ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); @@ -4265,6 +4378,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, * from 5G rules list. */ if (memcmp(reg_info->alpha2, "US", 2) == 0) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "US 5 GHz reg rules number %d from fw", + reg_info->num_5g_reg_rules); + + if (reg_info->num_5g_reg_rules > REG_US_5G_NUM_REG_RULES) + skip_6ghz_rules_in_5ghz_rules = reg_info->num_5g_reg_rules - + REG_US_5G_NUM_REG_RULES; reg_info->num_5g_reg_rules = REG_US_5G_NUM_REG_RULES; num_5g_reg_rules = reg_info->num_5g_reg_rules; } @@ -4297,6 +4417,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, break; } + ath12k_dbg(ab, ATH12K_DBG_WMI, + "%s: status_code %s", __func__, + ath12k_cc_status_to_str(reg_info->status_code)); + reg_info->is_ext_reg_event = true; reg_info->min_bw_2g = le32_to_cpu(ev->min_bw_2g); @@ -4325,6 +4449,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, le32_to_cpu(ev->max_bw_6g_client_vlp[i]); } + ath12k_dbg(ab, ATH12K_DBG_WMI, + "%s: status_code %s", __func__, + ath12k_cc_status_to_str(reg_info->status_code)); + ath12k_dbg(ab, ATH12K_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, @@ -4368,10 +4496,13 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); return -ENOMEM; } + ath12k_print_reg_rule(ab, "2 GHz", + num_2g_reg_rules, + reg_info->reg_rules_2g_ptr); } + ext_wmi_reg_rule += num_2g_reg_rules; 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); @@ -4381,9 +4512,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); return -ENOMEM; } + ath12k_print_reg_rule(ab, "5 GHz", + num_5g_reg_rules, + reg_info->reg_rules_5g_ptr); } - ext_wmi_reg_rule += num_5g_reg_rules; + /* We have adjusted the number of 5 GHz reg rules via the hack above. + * Here, we adjust that many extra rules which came with 5g reg rules + * (for cc: US) + * + * NOTE: skip_6ghz_rules_in_5ghz_rules will be 0 for rest other cases. + */ + ext_wmi_reg_rule += num_5g_reg_rules + skip_6ghz_rules_in_5ghz_rules; for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { reg_info->reg_rules_6g_ap_ptr[i] = @@ -4396,10 +4536,17 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, return -ENOMEM; } + ath12k_print_reg_rule(ab, ath12k_6ghz_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++) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "AP type %s", ath12k_6ghz_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_reg_rules_cl[j][i], @@ -4411,6 +4558,10 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, return -ENOMEM; } + ath12k_print_reg_rule(ab, ath12k_6ghz_client_type_to_str(i), + num_6g_reg_rules_cl[j][i], + reg_info->reg_rules_6g_client_ptr[j][i]); + ext_wmi_reg_rule += num_6g_reg_rules_cl[j][i]; } } @@ -4425,6 +4576,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, reg_info->domain_code_6g_ap[WMI_REG_VLP_AP] = le32_to_cpu(ev->domain_code_6g_ap_vlp); + ath12k_dbg(ab, ATH12K_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", + ath12k_6ghz_client_type_to_str(reg_info->client_type), + reg_info->rnr_tpe_usable, + reg_info->unspecified_ap_usable, + ath12k_sub_reg_6ghz_to_str + (le32_to_cpu(ev->domain_code_6g_ap_lpi)), + ath12k_sub_reg_6ghz_to_str + (le32_to_cpu(ev->domain_code_6g_ap_sp)), + ath12k_sub_reg_6ghz_to_str + (le32_to_cpu(ev->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] = le32_to_cpu(ev->domain_code_6g_client_lpi[i]); @@ -4432,12 +4595,18 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, le32_to_cpu(ev->domain_code_6g_client_sp[i]); reg_info->domain_code_6g_client[WMI_REG_VLP_AP][i] = le32_to_cpu(ev->domain_code_6g_client_vlp[i]); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "6 GHz AP BW: lpi %d - %d sp %d - %d vlp %d - %d\n", + ev->min_bw_6g_ap_lpi, ev->max_bw_6g_ap_lpi, + ev->min_bw_6g_ap_sp, ev->max_bw_6g_ap_sp, + ev->min_bw_6g_ap_vlp, ev->max_bw_6g_ap_vlp); } reg_info->domain_code_6g_super_id = le32_to_cpu(ev->domain_code_6g_super_id); - ath12k_dbg(ab, ATH12K_DBG_WMI, "6g client_type: %d domain_code_6g_super_id: %d", - reg_info->client_type, reg_info->domain_code_6g_super_id); + ath12k_dbg(ab, ATH12K_DBG_WMI, "6 GHz client_type: %s 6 GHz super domain %s", + ath12k_6ghz_client_type_to_str(reg_info->client_type), + ath12k_super_reg_6ghz_to_str(reg_info->domain_code_6g_super_id)); ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n"); @@ -5192,7 +5361,8 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) intersect = true; - regd = ath12k_reg_build_regd(ab, reg_info, intersect); + regd = ath12k_reg_build_regd(ab, reg_info, intersect, + WMI_VDEV_TYPE_AP, IEEE80211_REG_UNSET_AP); if (!regd) { ath12k_warn(ab, "failed to build regd from reg_info\n"); goto fallback; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 08a8c9e0f59f..966e6ba4e162 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2832,8 +2832,8 @@ struct ath12k_wmi_rx_reorder_queue_remove_arg { #define REG_RULE_MAX_BW 0x0000ffff #define REG_RULE_REG_PWR 0x00ff0000 #define REG_RULE_ANT_GAIN 0xff000000 -#define REG_RULE_PSD_INFO BIT(2) -#define REG_RULE_PSD_EIRP 0xffff0000 +#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) @@ -3844,6 +3844,29 @@ enum { WMI_REG_SET_CC_STATUS_FAIL = 5, }; +enum reg_subdomains_6ghz { + EMPTY_6GHZ = 0x0, + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, + FCC1_CLIENT_SP_6GHZ = 0x02, + FCC1_AP_LPI_6GHZ = 0x03, + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, + FCC1_AP_SP_6GHZ = 0x04, + ETSI1_LPI_6GHZ = 0x10, + ETSI1_VLP_6GHZ = 0x11, + ETSI2_LPI_6GHZ = 0x12, + ETSI2_VLP_6GHZ = 0x13, + APL1_LPI_6GHZ = 0x20, + APL1_VLP_6GHZ = 0x21, +}; + +enum reg_super_domain_6ghz { + FCC1_6GHZ = 0x01, + ETSI1_6GHZ = 0x02, + ETSI2_6GHZ = 0x03, + APL1_6GHZ = 0x04, + FCC1_6GHZ_CL = 0x05, +}; + #define WMI_REG_CLIENT_MAX 4 struct wmi_reg_chan_list_cc_ext_event {
There are 3 types of regulatory rules for AP mode and 6 types for STATION mode. This is to add wmi_vdev_type and ieee80211_ap_reg_power to select the exact reg rules. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aishwarya R <quic_aisr@quicinc.com> --- drivers/net/wireless/ath/ath12k/reg.c | 62 +++++++-- drivers/net/wireless/ath/ath12k/reg.h | 6 +- drivers/net/wireless/ath/ath12k/wmi.c | 182 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath12k/wmi.h | 27 +++- 4 files changed, 257 insertions(+), 20 deletions(-)