The branch main has been updated by bz:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=6c61f58562b932eb46b2e05b2f5a82d34250435a

commit 6c61f58562b932eb46b2e05b2f5a82d34250435a
Author:     Bjoern A. Zeeb <[email protected]>
AuthorDate: 2026-01-20 10:55:32 +0000
Commit:     Bjoern A. Zeeb <[email protected]>
CommitDate: 2026-01-21 19:57:42 +0000

    ath10k: update Atheros/QCA's ath10k driver
    
    This version is based on
    git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7 ( tag: v6.19-rc6 ).
    
    Sponsored by:   The FreeBSD Foundation
---
 sys/contrib/dev/athk/ath10k/core.c       |  28 ++--
 sys/contrib/dev/athk/ath10k/core.h       |   6 +-
 sys/contrib/dev/athk/ath10k/mac.c        |   2 +-
 sys/contrib/dev/athk/ath10k/qmi.c        |   2 +-
 sys/contrib/dev/athk/ath10k/testmode.c   | 253 +++++++++++++++++++++++++++----
 sys/contrib/dev/athk/ath10k/testmode_i.h |  15 ++
 sys/contrib/dev/athk/ath10k/wmi.h        |  19 ++-
 sys/modules/ath10k/Makefile              |   1 +
 8 files changed, 275 insertions(+), 51 deletions(-)

diff --git a/sys/contrib/dev/athk/ath10k/core.c 
b/sys/contrib/dev/athk/ath10k/core.c
index a0407f693659..9ec08b402fd2 100644
--- a/sys/contrib/dev/athk/ath10k/core.c
+++ b/sys/contrib/dev/athk/ath10k/core.c
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -1212,7 +1211,7 @@ static int ath10k_download_fw(struct ath10k *ar)
        u32 address, data_len;
        const void *data;
        int ret;
-       struct pm_qos_request latency_qos;
+       struct pm_qos_request latency_qos = {};
 
        address = ar->hw_params.patch_load_addr;
 
@@ -1246,7 +1245,6 @@ static int ath10k_download_fw(struct ath10k *ar)
                            ret);
        }
 
-       memset(&latency_qos, 0, sizeof(latency_qos));
        cpu_latency_qos_add_request(&latency_qos, 0);
 
        ret = ath10k_bmi_fast_download(ar, address, data, data_len);
@@ -2570,8 +2568,9 @@ static int ath10k_init_hw_params(struct ath10k *ar)
        return 0;
 }
 
-static bool ath10k_core_needs_recovery(struct ath10k *ar)
+static void ath10k_core_recovery_check_work(struct work_struct *work)
 {
+       struct ath10k *ar = container_of(work, struct ath10k, 
recovery_check_work);
        long time_left;
 
        /* Sometimes the recovery will fail and then the next all recovery fail,
@@ -2581,7 +2580,7 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
                ath10k_err(ar, "consecutive fail %d times, will shutdown 
driver!",
                           atomic_read(&ar->fail_cont_count));
                ar->state = ATH10K_STATE_WEDGED;
-               return false;
+               return;
        }
 
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "total recovery count: %d", 
++ar->recovery_count);
@@ -2595,27 +2594,24 @@ static bool ath10k_core_needs_recovery(struct ath10k 
*ar)
                                                        
ATH10K_RECOVERY_TIMEOUT_HZ);
                if (time_left) {
                        ath10k_warn(ar, "previous recovery succeeded, skip 
this!\n");
-                       return false;
+                       return;
                }
 
                /* Record the continuous recovery fail count when recovery 
failed. */
                atomic_inc(&ar->fail_cont_count);
 
                /* Avoid having multiple recoveries at the same time. */
-               return false;
+               return;
        }
 
        atomic_inc(&ar->pending_recovery);
-
-       return true;
+       queue_work(ar->workqueue, &ar->restart_work);
 }
 
 void ath10k_core_start_recovery(struct ath10k *ar)
 {
-       if (!ath10k_core_needs_recovery(ar))
-               return;
-
-       queue_work(ar->workqueue, &ar->restart_work);
+       /* Use workqueue_aux to avoid blocking recovery tracking */
+       queue_work(ar->workqueue_aux, &ar->recovery_check_work);
 }
 EXPORT_SYMBOL(ath10k_core_start_recovery);
 
@@ -3440,7 +3436,7 @@ EXPORT_SYMBOL(ath10k_core_stop);
  */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-       struct bmi_target_info target_info;
+       struct bmi_target_info target_info = {};
        int ret = 0;
 
        ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -3451,7 +3447,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 
        switch (ar->hif.bus) {
        case ATH10K_BUS_SDIO:
-               memset(&target_info, 0, sizeof(target_info));
                ret = ath10k_bmi_get_target_info_sdio(ar, &target_info);
                if (ret) {
                        ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3463,7 +3458,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
        case ATH10K_BUS_PCI:
        case ATH10K_BUS_AHB:
        case ATH10K_BUS_USB:
-               memset(&target_info, 0, sizeof(target_info));
                ret = ath10k_bmi_get_target_info(ar, &target_info);
                if (ret) {
                        ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3473,7 +3467,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                ar->hw->wiphy->hw_version = target_info.version;
                break;
        case ATH10K_BUS_SNOC:
-               memset(&target_info, 0, sizeof(target_info));
                ret = ath10k_hif_get_target_info(ar, &target_info);
                if (ret) {
                        ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3824,6 +3817,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, 
struct device *dev,
 
        INIT_WORK(&ar->register_work, ath10k_core_register_work);
        INIT_WORK(&ar->restart_work, ath10k_core_restart);
+       INIT_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
        INIT_WORK(&ar->set_coverage_class_work,
                  ath10k_core_set_coverage_class_work);
 
diff --git a/sys/contrib/dev/athk/ath10k/core.h 
b/sys/contrib/dev/athk/ath10k/core.h
index cb250ca6991d..eaf122d4b112 100644
--- a/sys/contrib/dev/athk/ath10k/core.h
+++ b/sys/contrib/dev/athk/ath10k/core.h
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -1216,6 +1215,7 @@ struct ath10k {
 
        struct work_struct register_work;
        struct work_struct restart_work;
+       struct work_struct recovery_check_work;
        struct work_struct bundle_tx_work;
        struct work_struct tx_complete_work;
 
@@ -1267,9 +1267,13 @@ struct ath10k {
        struct {
                /* protected by conf_mutex */
                struct ath10k_fw_components utf_mode_fw;
+               u8 ftm_msgref;
 
                /* protected by data_lock */
                bool utf_monitor;
+               u32 data_pos;
+               u32 expected_seq;
+               u8 *eventdata;
        } testmode;
 
        struct {
diff --git a/sys/contrib/dev/athk/ath10k/mac.c 
b/sys/contrib/dev/athk/ath10k/mac.c
index 6725c2c742bd..e2bda3c0d925 100644
--- a/sys/contrib/dev/athk/ath10k/mac.c
+++ b/sys/contrib/dev/athk/ath10k/mac.c
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -5442,6 +5441,7 @@ static void ath10k_stop(struct ieee80211_hw *hw, bool 
suspend)
        cancel_work_sync(&ar->set_coverage_class_work);
        cancel_delayed_work_sync(&ar->scan.timeout);
        cancel_work_sync(&ar->restart_work);
+       cancel_work_sync(&ar->recovery_check_work);
 }
 
 static int ath10k_config_ps(struct ath10k *ar)
diff --git a/sys/contrib/dev/athk/ath10k/qmi.c 
b/sys/contrib/dev/athk/ath10k/qmi.c
index f1f33af0170a..8275345631a0 100644
--- a/sys/contrib/dev/athk/ath10k/qmi.c
+++ b/sys/contrib/dev/athk/ath10k/qmi.c
@@ -986,7 +986,7 @@ static int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,
 
        ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");
 
-       ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq,
+       ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)&qmi->sq,
                             sizeof(qmi->sq), 0);
        if (ret) {
                ath10k_err(ar, "failed to connect to a remote QMI service 
port\n");
diff --git a/sys/contrib/dev/athk/ath10k/testmode.c 
b/sys/contrib/dev/athk/ath10k/testmode.c
index 3fcefc55b74f..d3bd385694d6 100644
--- a/sys/contrib/dev/athk/ath10k/testmode.c
+++ b/sys/contrib/dev/athk/ath10k/testmode.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: ISC
 /*
  * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #include "testmode.h"
@@ -10,12 +11,17 @@
 
 #include "debug.h"
 #include "wmi.h"
+#include "wmi-tlv.h"
 #include "hif.h"
 #include "hw.h"
 #include "core.h"
 
 #include "testmode_i.h"
 
+#define ATH10K_FTM_SEG_NONE                    ((u32)-1)
+#define ATH10K_FTM_SEGHDR_CURRENT_SEQ          GENMASK(3, 0)
+#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS       GENMASK(7, 4)
+
 static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
        [ATH10K_TM_ATTR_CMD]            = { .type = NLA_U32 },
        [ATH10K_TM_ATTR_DATA]           = { .type = NLA_BINARY,
@@ -25,41 +31,19 @@ static const struct nla_policy 
ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
        [ATH10K_TM_ATTR_VERSION_MINOR]  = { .type = NLA_U32 },
 };
 
-/* Returns true if callee consumes the skb and the skb should be discarded.
- * Returns false if skb is not used. Does not sleep.
- */
-bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
+                                       struct sk_buff *skb)
 {
        struct sk_buff *nl_skb;
-       bool consumed;
        int ret;
 
-       ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
-                  "testmode event wmi cmd_id %d skb %p skb->len %d\n",
-                  cmd_id, skb, skb->len);
-
-       ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
-
-       spin_lock_bh(&ar->data_lock);
-
-       if (!ar->testmode.utf_monitor) {
-               consumed = false;
-               goto out;
-       }
-
-       /* Only testmode.c should be handling events from utf firmware,
-        * otherwise all sort of problems will arise as mac80211 operations
-        * are not initialised.
-        */
-       consumed = true;
-
        nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
                                                   2 * sizeof(u32) + skb->len,
                                                   GFP_ATOMIC);
        if (!nl_skb) {
                ath10k_warn(ar,
                            "failed to allocate skb for testmode wmi event\n");
-               goto out;
+               return;
        }
 
        ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
@@ -68,7 +52,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, 
struct sk_buff *skb)
                            "failed to put testmode wmi event cmd attribute: 
%d\n",
                            ret);
                kfree_skb(nl_skb);
-               goto out;
+               return;
        }
 
        ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
@@ -77,7 +61,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, 
struct sk_buff *skb)
                            "failed to put testmode wmi event cmd_id: %d\n",
                            ret);
                kfree_skb(nl_skb);
-               goto out;
+               return;
        }
 
        ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
@@ -86,10 +70,122 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, 
struct sk_buff *skb)
                            "failed to copy skb to testmode wmi event: %d\n",
                            ret);
                kfree_skb(nl_skb);
-               goto out;
+               return;
+       }
+
+       cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct 
sk_buff *skb)
+{
+       struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;
+       u8 total_segments, current_seq;
+       struct sk_buff *nl_skb;
+       u8 const *buf_pos;
+       u16 datalen;
+       u32 data_pos;
+       int ret;
+
+       if (skb->len < sizeof(*ftm)) {
+               ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);
+               return;
+       }
+
+       current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,
+                               __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+       total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,
+                                  __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+       datalen = skb->len - sizeof(*ftm);
+       buf_pos = ftm->data;
+
+       if (current_seq == 0) {
+               ar->testmode.expected_seq = 0;
+               ar->testmode.data_pos = 0;
+       }
+
+       data_pos = ar->testmode.data_pos;
+
+       if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
+               ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",
+                           data_pos, datalen);
+               ret = -EINVAL;
+               return;
+       }
+
+       memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);
+       data_pos += datalen;
+
+       if (++ar->testmode.expected_seq != total_segments) {
+               ar->testmode.data_pos = data_pos;
+               ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received 
%u/%u\n",
+                          current_seq + 1, total_segments);
+               return;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);
+
+       nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
+                                                  2 * sizeof(u32) + data_pos,
+                                                  GFP_ATOMIC);
+       if (!nl_skb) {
+               ath10k_warn(ar, "failed to allocate skb for testmode wmi 
event\n");
+               return;
+       }
+
+       ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);
+       if (ret) {
+               ath10k_warn(ar, "failed to put testmode wmi event attribute: 
%d\n", ret);
+               kfree_skb(nl_skb);
+               return;
+       }
+
+       ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
+       if (ret) {
+               ath10k_warn(ar, "failed to put testmode wmi event cmd_id: 
%d\n", ret);
+               kfree_skb(nl_skb);
+               return;
+       }
+
+       ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, 
&ar->testmode.eventdata[0]);
+       if (ret) {
+               ath10k_warn(ar, "failed to copy skb to testmode wmi event: 
%d\n", ret);
+               kfree_skb(nl_skb);
+               return;
        }
 
        cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+/* Returns true if callee consumes the skb and the skb should be discarded.
+ * Returns false if skb is not used. Does not sleep.
+ */
+bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+{
+       bool consumed;
+
+       ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+                  "testmode event wmi cmd_id %d skb %p skb->len %d\n",
+                  cmd_id, skb, skb->len);
+
+       ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+
+       spin_lock_bh(&ar->data_lock);
+
+       if (!ar->testmode.utf_monitor) {
+               consumed = false;
+               goto out;
+       }
+
+       /* Only testmode.c should be handling events from utf firmware,
+        * otherwise all sort of problems will arise as mac80211 operations
+        * are not initialised.
+        */
+       consumed = true;
+
+       if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
+               ath10k_tm_event_segmented(ar, cmd_id, skb);
+       else
+               ath10k_tm_event_unsegmented(ar, cmd_id, skb);
 
 out:
        spin_unlock_bh(&ar->data_lock);
@@ -281,12 +377,18 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, 
struct nlattr *tb[])
                goto err_release_utf_mode_fw;
        }
 
+       ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, 
GFP_KERNEL);
+       if (!ar->testmode.eventdata) {
+               ret = -ENOMEM;
+               goto err_power_down;
+       }
+
        ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
                                &ar->testmode.utf_mode_fw);
        if (ret) {
                ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
                ar->state = ATH10K_STATE_OFF;
-               goto err_power_down;
+               goto err_release_eventdata;
        }
 
        ar->state = ATH10K_STATE_UTF;
@@ -302,6 +404,10 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, 
struct nlattr *tb[])
 
        return 0;
 
+err_release_eventdata:
+       kfree(ar->testmode.eventdata);
+       ar->testmode.eventdata = NULL;
+
 err_power_down:
        ath10k_hif_power_down(ar);
 
@@ -341,6 +447,9 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
        release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
        ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
 
+       kfree(ar->testmode.eventdata);
+       ar->testmode.eventdata = NULL;
+
        ar->state = ATH10K_STATE_OFF;
 }
 
@@ -424,6 +533,85 @@ out:
        return ret;
 }
 
+static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])
+{
+       u16 total_bytes, num_segments;
+       u32 cmd_id, buf_len;
+       u8 segnumber = 0;
+       u8 *bufpos;
+       void *buf;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_UTF) {
+               ret = -ENETDOWN;
+               goto out;
+       }
+
+       buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
+       buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
+       cmd_id = WMI_PDEV_UTF_CMDID;
+
+       ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+                  "cmd wmi ftm cmd_id %d buffer length %d\n",
+                  cmd_id, buf_len);
+       ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
+
+       bufpos = buf;
+       total_bytes = buf_len;
+       num_segments = total_bytes / MAX_WMI_UTF_LEN;
+       ar->testmode.expected_seq = 0;
+
+       if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+               num_segments++;
+
+       while (buf_len) {
+               u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
+               struct wmi_ftm_cmd *ftm_cmd;
+               struct sk_buff *skb;
+               u32 hdr_info;
+               u8 seginfo;
+
+               skb = ath10k_wmi_alloc_skb(ar, (chunk_len +
+                                          sizeof(struct wmi_ftm_cmd)));
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
+               hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |
+                          FIELD_PREP(WMI_TLV_LEN, (chunk_len +
+                                     sizeof(struct wmi_ftm_seg_hdr)));
+               ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);
+               ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);
+               ftm_cmd->seg_hdr.msgref = 
__cpu_to_le32(ar->testmode.ftm_msgref);
+               seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, 
num_segments) |
+                         FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
+               ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);
+               segnumber++;
+
+               memcpy(&ftm_cmd->data, bufpos, chunk_len);
+
+               ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
+               if (ret) {
+                       ath10k_warn(ar, "failed to send wmi ftm command: %d\n", 
ret);
+                       goto out;
+               }
+
+               buf_len -= chunk_len;
+               bufpos += chunk_len;
+       }
+
+       ar->testmode.ftm_msgref++;
+       ret = 0;
+
+out:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                  void *data, int len)
 {
@@ -439,9 +627,14 @@ int ath10k_tm_cmd(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif,
        if (!tb[ATH10K_TM_ATTR_CMD])
                return -EINVAL;
 
+       ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;
+
        switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
        case ATH10K_TM_CMD_GET_VERSION:
-               return ath10k_tm_cmd_get_version(ar, tb);
+               if (!tb[ATH10K_TM_ATTR_DATA])
+                       return ath10k_tm_cmd_get_version(ar, tb);
+               else /* ATH10K_TM_CMD_TLV */
+                       return ath10k_tm_cmd_tlv(ar, tb);
        case ATH10K_TM_CMD_UTF_START:
                return ath10k_tm_cmd_utf_start(ar, tb);
        case ATH10K_TM_CMD_UTF_STOP:
diff --git a/sys/contrib/dev/athk/ath10k/testmode_i.h 
b/sys/contrib/dev/athk/ath10k/testmode_i.h
index ee1cb27c1d60..1603f5276682 100644
--- a/sys/contrib/dev/athk/ath10k/testmode_i.h
+++ b/sys/contrib/dev/athk/ath10k/testmode_i.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: ISC */
 /*
  * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 /* "API" level of the ath10k testmode interface. Bump it after every
@@ -14,6 +15,7 @@
 #define ATH10K_TESTMODE_VERSION_MINOR 0
 
 #define ATH10K_TM_DATA_MAX_LEN         5000
+#define ATH_FTM_EVENT_MAX_BUF_LENGTH   2048
 
 enum ath10k_tm_attr {
        __ATH10K_TM_ATTR_INVALID        = 0,
@@ -57,4 +59,17 @@ enum ath10k_tm_cmd {
         * ATH10K_TM_ATTR_DATA.
         */
        ATH10K_TM_CMD_WMI = 3,
+
+       /* The command used to transmit a test command to the firmware
+        * and the event to receive test events from the firmware. The data
+        * received only contain the TLV payload, need to add the tlv header
+        * and send the cmd to firmware with command id WMI_PDEV_UTF_CMDID.
+        * The data payload size could be large and the driver needs to
+        * send segmented data to firmware.
+        *
+        * This legacy testmode command shares the same value as the get-version
+        * command. To distinguish between them, we check whether the data 
attribute
+        * is present.
+        */
+       ATH10K_TM_CMD_TLV = ATH10K_TM_CMD_GET_VERSION,
 };
diff --git a/sys/contrib/dev/athk/ath10k/wmi.h 
b/sys/contrib/dev/athk/ath10k/wmi.h
index 0faefc0a9a40..7f50a1de6b97 100644
--- a/sys/contrib/dev/athk/ath10k/wmi.h
+++ b/sys/contrib/dev/athk/ath10k/wmi.h
@@ -3,7 +3,7 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #ifndef _WMI_H_
@@ -7418,6 +7418,23 @@ struct wmi_pdev_bb_timing_cfg_cmd {
        __le32 bb_xpa_timing;
 } __packed;
 
+struct wmi_ftm_seg_hdr {
+       __le32 len;
+       __le32 msgref;
+       __le32 segmentinfo;
+       __le32 pdev_id;
+} __packed;
+
+struct wmi_ftm_cmd {
+       __le32 tlv_header;
+       struct wmi_ftm_seg_hdr seg_hdr;
+       u8 data[];
+} __packed;
+
+#define WMI_TLV_LEN      GENMASK(15, 0)
+#define WMI_TLV_TAG     GENMASK(31, 16)
+#define MAX_WMI_UTF_LEN  252
+
 struct ath10k;
 struct ath10k_vif;
 struct ath10k_fw_stats_pdev;
diff --git a/sys/modules/ath10k/Makefile b/sys/modules/ath10k/Makefile
index 98df270b6791..d8196854b681 100644
--- a/sys/modules/ath10k/Makefile
+++ b/sys/modules/ath10k/Makefile
@@ -29,6 +29,7 @@ SRCS+=                leds.c
 .endif
 
 CFLAGS+=       -DKBUILD_MODNAME='"ath10k"'
+CFLAGS+=       -DLINUXKPI_VERSION=61900
 
 CFLAGS+=       -I${DEVATH10KDIR}
 CFLAGS+=       -I${DEVATH10KDIR}/..

Reply via email to