[PATCH v2 ath-next] wifi: ath12k: Add EHT capabilities to scan probe frame
Gaole Zhang
gaole.zhang at oss.qualcomm.com
Fri Oct 9 05:47:39 PDT 2026
On 10/5/2026 2:50 AM, Jeff Johnson wrote:
> 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 at 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?
done on v3 patch
>
>> + }
>> }
>>
>> 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?
done on v3 patch
>
>> 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
More information about the ath12k
mailing list