From: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
To: Gaole Zhang <gaole.zhang@oss.qualcomm.com>,
ath12k@lists.infradead.org, linux-wireless@vger.kernel.org,
baochen.qiang@oss.qualcomm.com,
rameshkumar.sundaram@oss.qualcomm.com
Cc: gaolez@qti.qualcomm.com, miaoqing.pan@oss.qualcomm.com,
hangtian.zhu@oss.qualcomm.com
Subject: Re: [PATCH v2 ath-next] wifi: ath12k: Add EHT capabilities to scan probe frame
Date: Sun, 4 Oct 2026 11:50:51 -0700 [thread overview]
Message-ID: <93d5fc5e-8174-40ea-9fb0-7beca2f37aa9@oss.qualcomm.com> (raw)
In-Reply-To: <20261001083940.3051840-1-gaole.zhang@oss.qualcomm.com>
On 10/1/2026 1:39 AM, Gaole Zhang wrote:
> ath12k hardware scan Probe Request frames do not include the EHT
> Capabilities element. Without this element, an AP cannot identify the
> STA as EHT-capable during active scanning and may omit EHT-related
> information from its Probe Response. This can result in incomplete scan
> information and prevent userspace from correctly determining the AP's
> EHT capabilities.
>
> For firmware to include the EHT Capabilities element in scan Probe
> Request frames, two conditions are required:
>
> - The scan channel must have WMI_CHAN_INFO_ALLOW_EHT set, which allows
> firmware to add the EHT Capabilities element to Probe Request frames.
> - The EHT Capabilities element must be configured on the vdev through
> WMI_VDEV_SET_IE_CMDID, so firmware can use it when building Probe
> Request frames.
>
> Set WMI_CHAN_INFO_ALLOW_EHT for channels when 11be is supported by
> firmware and not disabled by ACPI. Also configure the EHT Capabilities
> element before each hardware scan when the element is present in the
> mac80211 scan IE buffer.
>
> Firmware retains the configured element in the vdev context until it is
> replaced or the vdev is deleted. However, ath12k scans may either reuse
> an already started vdev or create a temporary scan vdev that is cleaned
> up after scan completion. Sending WMI_VDEV_SET_IE_CMDID for each scan
> request ensures correct behavior in both cases.
>
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
>
> Signed-off-by: Gaole Zhang <gaole.zhang@oss.qualcomm.com>
> ---
> v2:
> - Clarify the firmware requirements for adding the EHT Capabilities
> element to scan Probe Request frames.
> - Explain why the EHT Capabilities element is configured before each
> hardware scan, covering both reused vdevs and temporary scan vdevs.
> - No code changes.
>
> drivers/net/wireless/ath/ath12k/core.h | 6 ++++
> drivers/net/wireless/ath/ath12k/mac.c | 29 +++++++++++++--
> drivers/net/wireless/ath/ath12k/reg.c | 4 +++
> drivers/net/wireless/ath/ath12k/wmi.c | 50 ++++++++++++++++++++++++++
> drivers/net/wireless/ath/ath12k/wmi.h | 23 +++++++++++-
> 5 files changed, 109 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index a98fc6e0699d..e7373a523dc3 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -1394,6 +1394,12 @@ static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 radio_idx)
> return &ah->radio[radio_idx];
> }
>
> +static inline bool ath12k_is_11be_enabled(struct ath12k_base *ab)
> +{
> + return test_bit(WMI_TLV_SERVICE_11BE, ab->wmi_ab.svc_map) &&
> + !ath12k_acpi_get_disable_11be(ab);
> +}
> +
> static inline struct ath12k_hw *ath12k_ar_to_ah(struct ath12k *ar)
> {
> return ar->ah;
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index d4116ba0da08..f353af63c5eb 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -5602,6 +5602,25 @@ ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar)
> return ATH12K_FIRST_SCAN_LINK;
> }
>
> +static int ath12k_mac_set_scan_eht_cap_ie(struct ath12k *ar,
> + struct ath12k_link_vif *arvif,
> + const u8 *ies, size_t ies_len)
> +{
> + const struct element *eht_cap;
> +
> + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
> +
> + eht_cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_CAPABILITY, ies,
> + ies_len);
> + if (!eht_cap || eht_cap->datalen <= 1)
> + return 0;
> +
> + return ath12k_wmi_vdev_set_ie(ar, arvif->vdev_id,
> + WLAN_EID_EXTENSION,
> + eht_cap->data, eht_cap->datalen,
> + WMI_SET_VDEV_IE_BAND_ALL);
> +}
> +
> static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
> struct ieee80211_vif *vif,
> struct ieee80211_scan_request *hw_req,
> @@ -5718,6 +5737,13 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
> goto exit;
> }
> arg->extraie.len = req->ie_len;
> + ret = ath12k_mac_set_scan_eht_cap_ie(ar, arvif, req->ie,
> + req->ie_len);
> + if (ret) {
> + ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
> + "failed to set eht cap ie, ret %d\n", ret);
> + goto exit;
do we really want to bypass scanning in this case?
would it be better to still scan, just without added capability?
> + }
> }
>
> if (req->n_ssids) {
> @@ -8983,8 +9009,7 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar,
>
> memset(eht_cap, 0, sizeof(struct ieee80211_sta_eht_cap));
>
> - if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)) ||
> - ath12k_acpi_get_disable_11be(ar->ab))
> + if (!ath12k_is_11be_enabled(ar->ab))
> return;
>
> eht_cap->has_eht = true;
> diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
> index 89abf2e87ad1..0aba1e74a3e9 100644
> --- a/drivers/net/wireless/ath/ath12k/reg.c
> +++ b/drivers/net/wireless/ath/ath12k/reg.c
> @@ -140,6 +140,7 @@ int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
> enum nl80211_band band;
> int num_channels = 0;
> int i, ret = 0;
> + bool has_eht;
>
> if (ar->ah->state == ATH12K_HW_STATE_RESTARTING)
> return 0;
> @@ -180,6 +181,8 @@ int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
>
> ch = arg->channel;
>
> + has_eht = ath12k_is_11be_enabled(ar->ab);
> +
> for (band = 0; band < NUM_NL80211_BANDS; band++) {
> if (!(ar->mac.sbands[band].channels && bands[band]))
> continue;
> @@ -201,6 +204,7 @@ int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
> ch->allow_ht = true;
> ch->allow_vht = true;
> ch->allow_he = true;
> + ch->allow_eht = has_eht;
>
> ch->dfs_set =
> !!(channel->flags & IEEE80211_CHAN_RADAR);
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index a63bbda0219c..5fe7af731a4e 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -2005,6 +2005,54 @@ int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id,
> return ret;
> }
>
> +int ath12k_wmi_vdev_set_ie(struct ath12k *ar, u32 vdev_id, u32 ie_id,
> + const u8 *ie, size_t ie_len, u32 band)
> +{
> + struct ath12k_wmi_pdev *wmi = ar->wmi;
> + struct wmi_vdev_set_ie_cmd *cmd;
> + struct sk_buff *skb;
> + struct wmi_tlv *tlv;
> + size_t aligned_len;
> + int ret, len;
> + void *ptr;
> +
> + aligned_len = roundup(ie_len, sizeof(u32));
> + len = sizeof(*cmd) + TLV_HDR_SIZE + aligned_len;
> +
> + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
> + if (!skb)
> + return -ENOMEM;
> +
> + ptr = skb->data;
> + cmd = ptr;
> + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_SET_IE_CMD,
> + sizeof(*cmd));
> + cmd->vdev_id = cpu_to_le32(vdev_id);
> + cmd->ie_id = cpu_to_le32(ie_id);
> + cmd->ie_len = cpu_to_le32(ie_len);
> + cmd->ie_source = cpu_to_le32(WMI_SET_VDEV_IE_SOURCE_HOST);
> + cmd->band = cpu_to_le32(band);
> +
> + ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
> + "WMI set ie vdev_id %u ie_id %u ie_len %zu band %u\n",
> + vdev_id, ie_id, ie_len, band);
> +
> + ptr += sizeof(*cmd);
> + tlv = ptr;
> + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len);
> + memcpy(tlv->value, ie, ie_len);
> +
> + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_IE_CMDID);
> + if (ret) {
> + ath12k_warn(ar->ab,
> + "failed to send WMI_VDEV_SET_IE_CMDID for vdev %u ie %u: %d\n",
> + vdev_id, ie_id, ret);
> + dev_kfree_skb(skb);
> + }
> +
> + return ret;
> +}
> +
> int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif,
> struct ieee80211_mutable_offsets *offs,
> struct sk_buff *bcn,
> @@ -2946,6 +2994,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar,
> chan_info->info |= cpu_to_le32(WMI_CHAN_INFO_ALLOW_VHT);
> else if (channel_arg->allow_ht)
> chan_info->info |= cpu_to_le32(WMI_CHAN_INFO_ALLOW_HT);
> + if (channel_arg->allow_eht)
> + chan_info->info |= cpu_to_le32(WMI_CHAN_INFO_ALLOW_EHT);
> if (channel_arg->half_rate)
> chan_info->info |= cpu_to_le32(WMI_CHAN_INFO_HALF_RATE);
> if (channel_arg->quarter_rate)
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index b508aa759bd8..e51b3bdab77b 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -3075,7 +3075,8 @@ struct ath12k_wmi_channel_arg {
> allow_vht:1,
> allow_he:1,
> set_agile:1,
> - psc_channel:1;
> + psc_channel:1,
> + allow_eht:1;
add after allow_he to keep in a logical order since this isn't ABI?
> u32 phy_mode;
> u32 cfreq1;
> u32 cfreq2;
> @@ -3694,6 +3695,7 @@ struct ath12k_wmi_scan_cancel_arg {
> #define WMI_CHAN_INFO_DFS_FREQ2 BIT(16)
> #define WMI_CHAN_INFO_ALLOW_HE BIT(17)
> #define WMI_CHAN_INFO_PSC BIT(18)
> +#define WMI_CHAN_INFO_ALLOW_EHT BIT(21)
>
> #define WMI_CHAN_REG_INFO1_MIN_PWR GENMASK(7, 0)
> #define WMI_CHAN_REG_INFO1_MAX_PWR GENMASK(15, 8)
> @@ -3845,6 +3847,23 @@ struct wmi_p2p_go_set_beacon_ie_cmd {
> __le32 ie_buf_len;
> } __packed;
>
> +#define WMI_SET_VDEV_IE_SOURCE_HOST 0
> +
> +enum wmi_set_vdev_ie_band {
> + WMI_SET_VDEV_IE_BAND_ALL,
> + WMI_SET_VDEV_IE_BAND_2_4GHZ,
> + WMI_SET_VDEV_IE_BAND_5GHZ,
> +};
> +
> +struct wmi_vdev_set_ie_cmd {
> + __le32 tlv_header;
> + __le32 vdev_id;
> + __le32 ie_id;
> + __le32 ie_len;
> + __le32 ie_source;
> + __le32 band;
> +} __packed;
> +
> struct wmi_vdev_install_key_cmd {
> __le32 tlv_header;
> __le32 vdev_id;
> @@ -6583,6 +6602,8 @@ int ath12k_wmi_pdev_resume(struct ath12k *ar, u32 pdev_id);
>
> int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
> struct ath12k_wmi_peer_assoc_arg *arg);
> +int ath12k_wmi_vdev_set_ie(struct ath12k *ar, u32 vdev_id, u32 ie_id,
> + const u8 *ie, size_t ie_len, u32 band);
> int ath12k_wmi_vdev_install_key(struct ath12k *ar,
> struct wmi_vdev_install_key_arg *arg);
> int ath12k_wmi_pdev_bss_chan_info_request(struct ath12k *ar,
>
> base-commit: c3bace8584ca707e34ba837f65c2e9d566af4c2c
next prev parent reply other threads:[~2026-10-04 18:50 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 8:39 [PATCH v2 ath-next] wifi: ath12k: Add EHT capabilities to scan probe frame Gaole Zhang
2026-10-04 18:50 ` Jeff Johnson [this message]
2026-10-09 12:47 ` Gaole Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=93d5fc5e-8174-40ea-9fb0-7beca2f37aa9@oss.qualcomm.com \
--to=jeff.johnson@oss.qualcomm.com \
--cc=ath12k@lists.infradead.org \
--cc=baochen.qiang@oss.qualcomm.com \
--cc=gaole.zhang@oss.qualcomm.com \
--cc=gaolez@qti.qualcomm.com \
--cc=hangtian.zhu@oss.qualcomm.com \
--cc=linux-wireless@vger.kernel.org \
--cc=miaoqing.pan@oss.qualcomm.com \
--cc=rameshkumar.sundaram@oss.qualcomm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox