Index | Thread | Search

From:
Marcus Glocker <marcus@nazgul.ch>
Subject:
Re: sys/qwz: implement regulatory domain for 2.4Ghz and 5Ghz
To:
"Kirill A. Korinsky" <kirill@korins.ky>
Cc:
tech@openbsd.org
Date:
Tue, 19 May 2026 07:21:54 +0200

Download raw body.

Thread
On Tue, May 19, 2026 at 03:20:48AM +0200, Kirill A. Korinsky wrote:

> tech@,
> 
> I'd like to add support of regulatory domain to qwz.
> 
> Without regulatory domain support it uses world, and some chanells not
> available from firmware point of view when AP can actually use it.
> 
> With this patch my honor can connect to 112 and similar channels.
> 
> Ok?

Looks like ported from the Linux ath12k driver, which is good.
Works on the Samsung, can't spot any new regressions.
One nit pick inline.

Otherwise ok mglocker@
 
> Index: sys/dev/ic/qwz.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v
> diff -u -p -r1.29 qwz.c
> --- sys/dev/ic/qwz.c	18 May 2026 13:47:32 -0000	1.29
> +++ sys/dev/ic/qwz.c	19 May 2026 01:16:14 -0000
> @@ -10523,6 +10523,57 @@ qwz_create_reg_rules_from_wmi(uint32_t n
>  	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

I don't think this function needs to be static.

> +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)
> @@ -10639,8 +10690,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
> 
> -- 
> wbr, Kirill
>