diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c index 927af5d6723..63399f4acb8 100644 --- a/sys/dev/ic/qwz.c +++ b/sys/dev/ic/qwz.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qwz.c,v 1.30 2026/05/19 04:17:51 mglocker Exp $ */ +/* $OpenBSD: qwz.c,v 1.31 2026/05/19 09:29:08 kirill Exp $ */ /* * Copyright 2023 Stefan Sperling @@ -10527,6 +10527,57 @@ qwz_create_reg_rules_from_wmi(uint32_t num_reg_rules, return reg_rule_ptr; } +struct cur_reg_rule * +qwz_create_ext_reg_rules_from_wmi(uint32_t num_reg_rules, + struct wmi_regulatory_ext_rule *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + uint32_t count; + + reg_rule_ptr = mallocarray(num_reg_rules, sizeof(*reg_rule_ptr), + M_DEVBUF, M_NOWAIT | M_ZERO); + 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 uint8_t +qwz_wmi_ignore_num_extra_rules(struct wmi_regulatory_ext_rule *wmi_reg_rule, + uint32_t num_reg_rules) +{ + uint8_t num_invalid_5ghz_rules = 0; + uint32_t count, start_freq; + + for (count = 0; count < num_reg_rules; count++) { + start_freq = FIELD_GET(REG_RULE_START_FREQ, + wmi_reg_rule[count].freq_info); + if (start_freq >= ATH12K_MIN_6G_FREQ) + num_invalid_5ghz_rules++; + } + + return num_invalid_5ghz_rules; +} + int qwz_pull_reg_chan_list_update_ev(struct qwz_softc *sc, struct mbuf *m, struct cur_regulatory_info *reg_info) @@ -10643,8 +10694,139 @@ int qwz_pull_reg_chan_list_ext_update_ev(struct qwz_softc *sc, struct mbuf *m, struct cur_regulatory_info *reg_info) { - printf("%s: not implemented\n", __func__); - return ENOTSUP; + const void **tb; + const struct wmi_reg_chan_list_cc_ext_event *chan_list_event_hdr; + struct wmi_regulatory_ext_rule *wmi_reg_rule; + uint32_t num_2ghz_reg_rules, num_5ghz_reg_rules; + uint8_t num_invalid_5ghz_ext_rules; + int ret; + + DNPRINTF(QWZ_D_WMI, "%s: processing regulatory ext channel list\n", + __func__); + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; /* XXX allocation failure or parsing failure? */ + printf("%s: failed to parse tlv: %d\n", __func__, ret); + return ENOMEM; + } + + chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; + if (!chan_list_event_hdr) { + printf("%s: failed to fetch reg chan list ext update ev\n", + __func__); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + reg_info->num_2ghz_reg_rules = chan_list_event_hdr->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = chan_list_event_hdr->num_5ghz_reg_rules; + + if (reg_info->num_2ghz_reg_rules > MAX_REG_RULES || + reg_info->num_5ghz_reg_rules > MAX_REG_RULES) { + printf("%s: Num reg rules for 2 GHz/5 GHz exceeds max " + "limit (num_2ghz_reg_rules: %d num_5ghz_reg_rules: %d " + "max_rules: %d)\n", __func__, reg_info->num_2ghz_reg_rules, + reg_info->num_5ghz_reg_rules, MAX_REG_RULES); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EINVAL; + } + + if (!(reg_info->num_2ghz_reg_rules + reg_info->num_5ghz_reg_rules)) { + printf("%s: No 2 GHz/5 GHz regulatory rules available in " + "the ext event info\n", __func__); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EINVAL; + } + + memcpy(reg_info->alpha2, &chan_list_event_hdr->alpha2, REG_ALPHA2_LEN); + reg_info->dfs_region = chan_list_event_hdr->dfs_region; + reg_info->phybitmap = chan_list_event_hdr->phybitmap; + reg_info->num_phy = chan_list_event_hdr->num_phy; + reg_info->phy_id = chan_list_event_hdr->phy_id; + reg_info->ctry_code = chan_list_event_hdr->country_id; + reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; + reg_info->status_code = qwz_wmi_cc_setting_code_to_reg( + chan_list_event_hdr->status_code); + reg_info->is_ext_reg_event = true; + + reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz; + reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz; + reg_info->min_bw_5ghz = chan_list_event_hdr->min_bw_5ghz; + reg_info->max_bw_5ghz = chan_list_event_hdr->max_bw_5ghz; + + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; + + DNPRINTF(QWZ_D_WMI, + "%s: cc_ext %s dfs %d BW: min_2ghz %d max_2ghz %d min_5ghz %d " + "max_5ghz %d phybitmap 0x%x\n", __func__, reg_info->alpha2, + reg_info->dfs_region, reg_info->min_bw_2ghz, + reg_info->max_bw_2ghz, reg_info->min_bw_5ghz, + reg_info->max_bw_5ghz, reg_info->phybitmap); + + DNPRINTF(QWZ_D_WMI, + "%s: num_2ghz_reg_rules %d num_5ghz_reg_rules %d\n", __func__, + num_2ghz_reg_rules, num_5ghz_reg_rules); + + wmi_reg_rule = (struct wmi_regulatory_ext_rule *) + ((uint8_t *)chan_list_event_hdr + sizeof(*chan_list_event_hdr) + + sizeof(struct wmi_tlv)); + + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = + qwz_create_ext_reg_rules_from_wmi(num_2ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_2ghz_ptr) { + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + printf("%s: Unable to allocate memory for " + "2 GHz rules\n", __func__); + return ENOMEM; + } + + qwz_print_reg_rule(sc, "2 GHz", num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); + } + + wmi_reg_rule += num_2ghz_reg_rules; + + /* + * Firmware may include 6 GHz rules in the 5 GHz ext rule list. + * Ignore them here until the stack grows real 6 GHz support. + * XXX implement the 6 GHz regulatory rule path as well. + */ + num_invalid_5ghz_ext_rules = qwz_wmi_ignore_num_extra_rules( + wmi_reg_rule, num_5ghz_reg_rules); + if (num_invalid_5ghz_ext_rules) { + DNPRINTF(QWZ_D_WMI, + "%s: cc %s 5 GHz reg rules %d from fw, %d invalid " + "5 GHz rules\n", __func__, reg_info->alpha2, + reg_info->num_5ghz_reg_rules, + num_invalid_5ghz_ext_rules); + num_5ghz_reg_rules -= num_invalid_5ghz_ext_rules; + reg_info->num_5ghz_reg_rules = num_5ghz_reg_rules; + } + + if (num_5ghz_reg_rules) { + reg_info->reg_rules_5ghz_ptr = + qwz_create_ext_reg_rules_from_wmi(num_5ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_5ghz_ptr) { + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + printf("%s: Unable to allocate memory for " + "5 GHz rules\n", __func__); + return ENOMEM; + } + + qwz_print_reg_rule(sc, "5 GHz", num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); + } + + DNPRINTF(QWZ_D_WMI, "%s: processed regulatory ext channel list\n", + __func__); + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; } void